passagemath-schemes 10.8.1a4__cp314-cp314t-macosx_13_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (312) hide show
  1. passagemath_schemes/.dylibs/libflint.22.0.dylib +0 -0
  2. passagemath_schemes/.dylibs/libgmp.10.dylib +0 -0
  3. passagemath_schemes/.dylibs/libgmpxx.4.dylib +0 -0
  4. passagemath_schemes/.dylibs/libmpfr.6.dylib +0 -0
  5. passagemath_schemes/__init__.py +3 -0
  6. passagemath_schemes-10.8.1a4.dist-info/METADATA +203 -0
  7. passagemath_schemes-10.8.1a4.dist-info/METADATA.bak +204 -0
  8. passagemath_schemes-10.8.1a4.dist-info/RECORD +312 -0
  9. passagemath_schemes-10.8.1a4.dist-info/WHEEL +6 -0
  10. passagemath_schemes-10.8.1a4.dist-info/top_level.txt +3 -0
  11. sage/all__sagemath_schemes.py +23 -0
  12. sage/databases/all__sagemath_schemes.py +7 -0
  13. sage/databases/cremona.py +1723 -0
  14. sage/dynamics/all__sagemath_schemes.py +2 -0
  15. sage/dynamics/arithmetic_dynamics/affine_ds.py +1083 -0
  16. sage/dynamics/arithmetic_dynamics/all.py +14 -0
  17. sage/dynamics/arithmetic_dynamics/berkovich_ds.py +1101 -0
  18. sage/dynamics/arithmetic_dynamics/dynamical_semigroup.py +1543 -0
  19. sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py +2426 -0
  20. sage/dynamics/arithmetic_dynamics/endPN_minimal_model.py +1169 -0
  21. sage/dynamics/arithmetic_dynamics/generic_ds.py +663 -0
  22. sage/dynamics/arithmetic_dynamics/product_projective_ds.py +339 -0
  23. sage/dynamics/arithmetic_dynamics/projective_ds.py +9556 -0
  24. sage/dynamics/arithmetic_dynamics/projective_ds_helper.cpython-314t-darwin.so +0 -0
  25. sage/dynamics/arithmetic_dynamics/projective_ds_helper.pyx +301 -0
  26. sage/dynamics/arithmetic_dynamics/wehlerK3.py +2578 -0
  27. sage/lfunctions/all.py +18 -0
  28. sage/lfunctions/dokchitser.py +727 -0
  29. sage/lfunctions/pari.py +971 -0
  30. sage/lfunctions/zero_sums.cpython-314t-darwin.so +0 -0
  31. sage/lfunctions/zero_sums.pyx +1847 -0
  32. sage/modular/abvar/abvar.py +5132 -0
  33. sage/modular/abvar/abvar_ambient_jacobian.py +414 -0
  34. sage/modular/abvar/abvar_newform.py +246 -0
  35. sage/modular/abvar/all.py +8 -0
  36. sage/modular/abvar/constructor.py +187 -0
  37. sage/modular/abvar/cuspidal_subgroup.py +371 -0
  38. sage/modular/abvar/finite_subgroup.py +896 -0
  39. sage/modular/abvar/homology.py +721 -0
  40. sage/modular/abvar/homspace.py +989 -0
  41. sage/modular/abvar/lseries.py +415 -0
  42. sage/modular/abvar/morphism.py +935 -0
  43. sage/modular/abvar/torsion_point.py +274 -0
  44. sage/modular/abvar/torsion_subgroup.py +741 -0
  45. sage/modular/all.py +43 -0
  46. sage/modular/arithgroup/all.py +20 -0
  47. sage/modular/arithgroup/arithgroup_element.cpython-314t-darwin.so +0 -0
  48. sage/modular/arithgroup/arithgroup_element.pyx +474 -0
  49. sage/modular/arithgroup/arithgroup_generic.py +1406 -0
  50. sage/modular/arithgroup/arithgroup_perm.py +2692 -0
  51. sage/modular/arithgroup/congroup.cpython-314t-darwin.so +0 -0
  52. sage/modular/arithgroup/congroup.pyx +334 -0
  53. sage/modular/arithgroup/congroup_gamma.py +361 -0
  54. sage/modular/arithgroup/congroup_gamma0.py +692 -0
  55. sage/modular/arithgroup/congroup_gamma1.py +659 -0
  56. sage/modular/arithgroup/congroup_gammaH.py +1491 -0
  57. sage/modular/arithgroup/congroup_generic.py +630 -0
  58. sage/modular/arithgroup/congroup_sl2z.py +266 -0
  59. sage/modular/arithgroup/farey_symbol.cpython-314t-darwin.so +0 -0
  60. sage/modular/arithgroup/farey_symbol.pyx +1067 -0
  61. sage/modular/arithgroup/tests.py +425 -0
  62. sage/modular/btquotients/all.py +4 -0
  63. sage/modular/btquotients/btquotient.py +3736 -0
  64. sage/modular/btquotients/pautomorphicform.py +2564 -0
  65. sage/modular/buzzard.py +100 -0
  66. sage/modular/congroup.py +29 -0
  67. sage/modular/congroup_element.py +13 -0
  68. sage/modular/cusps.py +1107 -0
  69. sage/modular/cusps_nf.py +1270 -0
  70. sage/modular/dims.py +571 -0
  71. sage/modular/dirichlet.py +3310 -0
  72. sage/modular/drinfeld_modform/all.py +2 -0
  73. sage/modular/drinfeld_modform/element.py +446 -0
  74. sage/modular/drinfeld_modform/ring.py +773 -0
  75. sage/modular/drinfeld_modform/tutorial.py +236 -0
  76. sage/modular/etaproducts.py +1076 -0
  77. sage/modular/hecke/algebra.py +725 -0
  78. sage/modular/hecke/all.py +19 -0
  79. sage/modular/hecke/ambient_module.py +994 -0
  80. sage/modular/hecke/degenmap.py +119 -0
  81. sage/modular/hecke/element.py +302 -0
  82. sage/modular/hecke/hecke_operator.py +736 -0
  83. sage/modular/hecke/homspace.py +185 -0
  84. sage/modular/hecke/module.py +1744 -0
  85. sage/modular/hecke/morphism.py +139 -0
  86. sage/modular/hecke/submodule.py +970 -0
  87. sage/modular/hypergeometric_misc.cpython-314t-darwin.so +0 -0
  88. sage/modular/hypergeometric_misc.pxd +4 -0
  89. sage/modular/hypergeometric_misc.pyx +166 -0
  90. sage/modular/hypergeometric_motive.py +2020 -0
  91. sage/modular/local_comp/all.py +2 -0
  92. sage/modular/local_comp/liftings.py +292 -0
  93. sage/modular/local_comp/local_comp.py +1070 -0
  94. sage/modular/local_comp/smoothchar.py +1825 -0
  95. sage/modular/local_comp/type_space.py +748 -0
  96. sage/modular/modform/all.py +30 -0
  97. sage/modular/modform/ambient.py +817 -0
  98. sage/modular/modform/ambient_R.py +177 -0
  99. sage/modular/modform/ambient_eps.py +306 -0
  100. sage/modular/modform/ambient_g0.py +120 -0
  101. sage/modular/modform/ambient_g1.py +199 -0
  102. sage/modular/modform/constructor.py +545 -0
  103. sage/modular/modform/cuspidal_submodule.py +708 -0
  104. sage/modular/modform/defaults.py +14 -0
  105. sage/modular/modform/eis_series.py +487 -0
  106. sage/modular/modform/eisenstein_submodule.py +663 -0
  107. sage/modular/modform/element.py +4105 -0
  108. sage/modular/modform/half_integral.py +154 -0
  109. sage/modular/modform/hecke_operator_on_qexp.py +247 -0
  110. sage/modular/modform/j_invariant.py +47 -0
  111. sage/modular/modform/l_series_gross_zagier.py +127 -0
  112. sage/modular/modform/l_series_gross_zagier_coeffs.cpython-314t-darwin.so +0 -0
  113. sage/modular/modform/l_series_gross_zagier_coeffs.pyx +177 -0
  114. sage/modular/modform/notes.py +45 -0
  115. sage/modular/modform/numerical.py +514 -0
  116. sage/modular/modform/periods.py +14 -0
  117. sage/modular/modform/ring.py +1257 -0
  118. sage/modular/modform/space.py +1859 -0
  119. sage/modular/modform/submodule.py +118 -0
  120. sage/modular/modform/tests.py +64 -0
  121. sage/modular/modform/theta.py +110 -0
  122. sage/modular/modform/vm_basis.py +380 -0
  123. sage/modular/modform/weight1.py +221 -0
  124. sage/modular/modform_hecketriangle/abstract_ring.py +1932 -0
  125. sage/modular/modform_hecketriangle/abstract_space.py +2527 -0
  126. sage/modular/modform_hecketriangle/all.py +30 -0
  127. sage/modular/modform_hecketriangle/analytic_type.py +590 -0
  128. sage/modular/modform_hecketriangle/constructor.py +416 -0
  129. sage/modular/modform_hecketriangle/element.py +351 -0
  130. sage/modular/modform_hecketriangle/functors.py +752 -0
  131. sage/modular/modform_hecketriangle/graded_ring.py +541 -0
  132. sage/modular/modform_hecketriangle/graded_ring_element.py +2225 -0
  133. sage/modular/modform_hecketriangle/hecke_triangle_group_element.py +3349 -0
  134. sage/modular/modform_hecketriangle/hecke_triangle_groups.py +1426 -0
  135. sage/modular/modform_hecketriangle/readme.py +1214 -0
  136. sage/modular/modform_hecketriangle/series_constructor.py +580 -0
  137. sage/modular/modform_hecketriangle/space.py +1037 -0
  138. sage/modular/modform_hecketriangle/subspace.py +423 -0
  139. sage/modular/modsym/all.py +17 -0
  140. sage/modular/modsym/ambient.py +3844 -0
  141. sage/modular/modsym/boundary.py +1420 -0
  142. sage/modular/modsym/element.py +336 -0
  143. sage/modular/modsym/g1list.py +178 -0
  144. sage/modular/modsym/ghlist.py +182 -0
  145. sage/modular/modsym/hecke_operator.py +73 -0
  146. sage/modular/modsym/manin_symbol.cpython-314t-darwin.so +0 -0
  147. sage/modular/modsym/manin_symbol.pxd +5 -0
  148. sage/modular/modsym/manin_symbol.pyx +497 -0
  149. sage/modular/modsym/manin_symbol_list.py +1291 -0
  150. sage/modular/modsym/modsym.py +400 -0
  151. sage/modular/modsym/modular_symbols.py +384 -0
  152. sage/modular/modsym/p1list_nf.py +1241 -0
  153. sage/modular/modsym/relation_matrix.py +591 -0
  154. sage/modular/modsym/relation_matrix_pyx.cpython-314t-darwin.so +0 -0
  155. sage/modular/modsym/relation_matrix_pyx.pyx +108 -0
  156. sage/modular/modsym/space.py +2468 -0
  157. sage/modular/modsym/subspace.py +455 -0
  158. sage/modular/modsym/tests.py +376 -0
  159. sage/modular/multiple_zeta.py +2635 -0
  160. sage/modular/multiple_zeta_F_algebra.py +789 -0
  161. sage/modular/overconvergent/all.py +6 -0
  162. sage/modular/overconvergent/genus0.py +1879 -0
  163. sage/modular/overconvergent/hecke_series.py +1187 -0
  164. sage/modular/overconvergent/weightspace.py +776 -0
  165. sage/modular/pollack_stevens/all.py +4 -0
  166. sage/modular/pollack_stevens/distributions.py +874 -0
  167. sage/modular/pollack_stevens/fund_domain.py +1572 -0
  168. sage/modular/pollack_stevens/manin_map.py +856 -0
  169. sage/modular/pollack_stevens/modsym.py +1590 -0
  170. sage/modular/pollack_stevens/padic_lseries.py +417 -0
  171. sage/modular/pollack_stevens/sigma0.py +534 -0
  172. sage/modular/pollack_stevens/space.py +1078 -0
  173. sage/modular/quasimodform/all.py +3 -0
  174. sage/modular/quasimodform/element.py +846 -0
  175. sage/modular/quasimodform/ring.py +826 -0
  176. sage/modular/quatalg/all.py +3 -0
  177. sage/modular/quatalg/brandt.py +1642 -0
  178. sage/modular/ssmod/all.py +8 -0
  179. sage/modular/ssmod/ssmod.py +827 -0
  180. sage/rings/all__sagemath_schemes.py +1 -0
  181. sage/rings/polynomial/all__sagemath_schemes.py +1 -0
  182. sage/rings/polynomial/binary_form_reduce.py +585 -0
  183. sage/schemes/all.py +41 -0
  184. sage/schemes/berkovich/all.py +6 -0
  185. sage/schemes/berkovich/berkovich_cp_element.py +2582 -0
  186. sage/schemes/berkovich/berkovich_space.py +700 -0
  187. sage/schemes/curves/affine_curve.py +2924 -0
  188. sage/schemes/curves/all.py +33 -0
  189. sage/schemes/curves/closed_point.py +434 -0
  190. sage/schemes/curves/constructor.py +397 -0
  191. sage/schemes/curves/curve.py +542 -0
  192. sage/schemes/curves/plane_curve_arrangement.py +1283 -0
  193. sage/schemes/curves/point.py +463 -0
  194. sage/schemes/curves/projective_curve.py +3203 -0
  195. sage/schemes/curves/weighted_projective_curve.py +106 -0
  196. sage/schemes/curves/zariski_vankampen.py +1931 -0
  197. sage/schemes/cyclic_covers/all.py +2 -0
  198. sage/schemes/cyclic_covers/charpoly_frobenius.py +320 -0
  199. sage/schemes/cyclic_covers/constructor.py +137 -0
  200. sage/schemes/cyclic_covers/cycliccover_finite_field.py +1309 -0
  201. sage/schemes/cyclic_covers/cycliccover_generic.py +310 -0
  202. sage/schemes/elliptic_curves/BSD.py +991 -0
  203. sage/schemes/elliptic_curves/Qcurves.py +592 -0
  204. sage/schemes/elliptic_curves/addition_formulas_ring.py +94 -0
  205. sage/schemes/elliptic_curves/all.py +49 -0
  206. sage/schemes/elliptic_curves/cardinality.py +609 -0
  207. sage/schemes/elliptic_curves/cm.py +1103 -0
  208. sage/schemes/elliptic_curves/constructor.py +1530 -0
  209. sage/schemes/elliptic_curves/ec_database.py +175 -0
  210. sage/schemes/elliptic_curves/ell_curve_isogeny.py +3971 -0
  211. sage/schemes/elliptic_curves/ell_egros.py +457 -0
  212. sage/schemes/elliptic_curves/ell_field.py +2837 -0
  213. sage/schemes/elliptic_curves/ell_finite_field.py +3249 -0
  214. sage/schemes/elliptic_curves/ell_generic.py +3760 -0
  215. sage/schemes/elliptic_curves/ell_local_data.py +1207 -0
  216. sage/schemes/elliptic_curves/ell_modular_symbols.py +775 -0
  217. sage/schemes/elliptic_curves/ell_number_field.py +4220 -0
  218. sage/schemes/elliptic_curves/ell_padic_field.py +107 -0
  219. sage/schemes/elliptic_curves/ell_point.py +4944 -0
  220. sage/schemes/elliptic_curves/ell_rational_field.py +7184 -0
  221. sage/schemes/elliptic_curves/ell_tate_curve.py +671 -0
  222. sage/schemes/elliptic_curves/ell_torsion.py +436 -0
  223. sage/schemes/elliptic_curves/ell_wp.py +352 -0
  224. sage/schemes/elliptic_curves/formal_group.py +760 -0
  225. sage/schemes/elliptic_curves/gal_reps.py +1459 -0
  226. sage/schemes/elliptic_curves/gal_reps_number_field.py +1663 -0
  227. sage/schemes/elliptic_curves/gp_simon.py +152 -0
  228. sage/schemes/elliptic_curves/heegner.py +7328 -0
  229. sage/schemes/elliptic_curves/height.py +2108 -0
  230. sage/schemes/elliptic_curves/hom.py +1788 -0
  231. sage/schemes/elliptic_curves/hom_composite.py +1084 -0
  232. sage/schemes/elliptic_curves/hom_fractional.py +544 -0
  233. sage/schemes/elliptic_curves/hom_frobenius.py +522 -0
  234. sage/schemes/elliptic_curves/hom_scalar.py +531 -0
  235. sage/schemes/elliptic_curves/hom_sum.py +681 -0
  236. sage/schemes/elliptic_curves/hom_velusqrt.py +1290 -0
  237. sage/schemes/elliptic_curves/homset.py +271 -0
  238. sage/schemes/elliptic_curves/isogeny_class.py +1523 -0
  239. sage/schemes/elliptic_curves/isogeny_small_degree.py +2797 -0
  240. sage/schemes/elliptic_curves/jacobian.py +247 -0
  241. sage/schemes/elliptic_curves/kodaira_symbol.py +344 -0
  242. sage/schemes/elliptic_curves/kraus.py +1014 -0
  243. sage/schemes/elliptic_curves/lseries_ell.py +915 -0
  244. sage/schemes/elliptic_curves/mod5family.py +105 -0
  245. sage/schemes/elliptic_curves/mod_poly.py +197 -0
  246. sage/schemes/elliptic_curves/mod_sym_num.cpython-314t-darwin.so +0 -0
  247. sage/schemes/elliptic_curves/mod_sym_num.pyx +3796 -0
  248. sage/schemes/elliptic_curves/modular_parametrization.py +305 -0
  249. sage/schemes/elliptic_curves/padic_lseries.py +1793 -0
  250. sage/schemes/elliptic_curves/padics.py +1816 -0
  251. sage/schemes/elliptic_curves/period_lattice.py +2234 -0
  252. sage/schemes/elliptic_curves/period_lattice_region.cpython-314t-darwin.so +0 -0
  253. sage/schemes/elliptic_curves/period_lattice_region.pyx +722 -0
  254. sage/schemes/elliptic_curves/saturation.py +716 -0
  255. sage/schemes/elliptic_curves/sha_tate.py +1158 -0
  256. sage/schemes/elliptic_curves/weierstrass_morphism.py +1117 -0
  257. sage/schemes/elliptic_curves/weierstrass_transform.py +200 -0
  258. sage/schemes/hyperelliptic_curves/all.py +6 -0
  259. sage/schemes/hyperelliptic_curves/constructor.py +369 -0
  260. sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py +1948 -0
  261. sage/schemes/hyperelliptic_curves/hyperelliptic_g2.py +192 -0
  262. sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py +936 -0
  263. sage/schemes/hyperelliptic_curves/hyperelliptic_padic_field.py +1332 -0
  264. sage/schemes/hyperelliptic_curves/hyperelliptic_rational_field.py +84 -0
  265. sage/schemes/hyperelliptic_curves/invariants.py +410 -0
  266. sage/schemes/hyperelliptic_curves/jacobian_endomorphism_utils.py +312 -0
  267. sage/schemes/hyperelliptic_curves/jacobian_g2.py +32 -0
  268. sage/schemes/hyperelliptic_curves/jacobian_generic.py +437 -0
  269. sage/schemes/hyperelliptic_curves/jacobian_homset.py +186 -0
  270. sage/schemes/hyperelliptic_curves/jacobian_morphism.py +878 -0
  271. sage/schemes/hyperelliptic_curves/kummer_surface.py +99 -0
  272. sage/schemes/hyperelliptic_curves/mestre.py +302 -0
  273. sage/schemes/hyperelliptic_curves/monsky_washnitzer.py +3863 -0
  274. sage/schemes/jacobians/abstract_jacobian.py +277 -0
  275. sage/schemes/jacobians/all.py +2 -0
  276. sage/schemes/overview.py +161 -0
  277. sage/schemes/plane_conics/all.py +22 -0
  278. sage/schemes/plane_conics/con_field.py +1296 -0
  279. sage/schemes/plane_conics/con_finite_field.py +158 -0
  280. sage/schemes/plane_conics/con_number_field.py +456 -0
  281. sage/schemes/plane_conics/con_rational_field.py +406 -0
  282. sage/schemes/plane_conics/con_rational_function_field.py +581 -0
  283. sage/schemes/plane_conics/constructor.py +249 -0
  284. sage/schemes/plane_quartics/all.py +2 -0
  285. sage/schemes/plane_quartics/quartic_constructor.py +71 -0
  286. sage/schemes/plane_quartics/quartic_generic.py +53 -0
  287. sage/schemes/riemann_surfaces/all.py +1 -0
  288. sage/schemes/riemann_surfaces/riemann_surface.py +4177 -0
  289. sage_wheels/share/cremona/cremona_mini.db +0 -0
  290. sage_wheels/share/ellcurves/rank0 +30427 -0
  291. sage_wheels/share/ellcurves/rank1 +31871 -0
  292. sage_wheels/share/ellcurves/rank10 +6 -0
  293. sage_wheels/share/ellcurves/rank11 +6 -0
  294. sage_wheels/share/ellcurves/rank12 +1 -0
  295. sage_wheels/share/ellcurves/rank14 +1 -0
  296. sage_wheels/share/ellcurves/rank15 +1 -0
  297. sage_wheels/share/ellcurves/rank17 +1 -0
  298. sage_wheels/share/ellcurves/rank19 +1 -0
  299. sage_wheels/share/ellcurves/rank2 +2388 -0
  300. sage_wheels/share/ellcurves/rank20 +1 -0
  301. sage_wheels/share/ellcurves/rank21 +1 -0
  302. sage_wheels/share/ellcurves/rank22 +1 -0
  303. sage_wheels/share/ellcurves/rank23 +1 -0
  304. sage_wheels/share/ellcurves/rank24 +1 -0
  305. sage_wheels/share/ellcurves/rank28 +1 -0
  306. sage_wheels/share/ellcurves/rank3 +836 -0
  307. sage_wheels/share/ellcurves/rank4 +10 -0
  308. sage_wheels/share/ellcurves/rank5 +5 -0
  309. sage_wheels/share/ellcurves/rank6 +5 -0
  310. sage_wheels/share/ellcurves/rank7 +5 -0
  311. sage_wheels/share/ellcurves/rank8 +6 -0
  312. sage_wheels/share/ellcurves/rank9 +7 -0
@@ -0,0 +1,3971 @@
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 E.isogeny(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
+ from sage.misc.misc_c import prod
2249
+ psi = prod([x - invX(xQ) for xQ in self.__kernel_mod_sign.keys()]) # building the list is not redundant; this is slightly faster
2250
+
2251
+ self.__kernel_polynomial = poly_ring(psi)
2252
+
2253
+ ###################################
2254
+ # Kohel's Variant of Velu's Formula
2255
+ ###################################
2256
+
2257
+ def __init_from_kernel_polynomial(self, kernel_polynomial):
2258
+ r"""
2259
+ Private function that initializes the isogeny from a kernel
2260
+ polynomial.
2261
+
2262
+ EXAMPLES:
2263
+
2264
+ These examples inherently exercise this private function::
2265
+
2266
+ sage: R.<x> = GF(7)[]
2267
+ sage: E = EllipticCurve(GF(7), [0,0,0,-1,0])
2268
+ sage: phi = EllipticCurveIsogeny(E, x);phi
2269
+ Isogeny of degree 2
2270
+ from Elliptic Curve defined by y^2 = x^3 + 6*x over Finite Field of size 7
2271
+ to Elliptic Curve defined by y^2 = x^3 + 4*x over Finite Field of size 7
2272
+
2273
+ sage: phi._EllipticCurveIsogeny__init_from_kernel_polynomial(x)
2274
+
2275
+ sage: E = EllipticCurve(GF(7), [0,-1,0,0,1])
2276
+ sage: phi = EllipticCurveIsogeny(E, x+6, degree=3); phi
2277
+ Isogeny of degree 3
2278
+ from Elliptic Curve defined by y^2 = x^3 + 6*x^2 + 1 over Finite Field of size 7
2279
+ to Elliptic Curve defined by y^2 = x^3 + 6*x^2 + 4*x + 2 over Finite Field of size 7
2280
+
2281
+ sage: phi._EllipticCurveIsogeny__init_from_kernel_polynomial(x+6)
2282
+ """
2283
+ poly_ring = self.__poly_ring
2284
+ E = self._domain
2285
+
2286
+ # Convert to a univariate polynomial, even if it had a
2287
+ # bivariate parent, or was given as a list:
2288
+ psi = poly_ring(kernel_polynomial)
2289
+
2290
+ self.__kernel_polynomial = psi
2291
+
2292
+ if psi.leading_coefficient() != 1:
2293
+ raise ValueError("given kernel polynomial is not monic")
2294
+
2295
+ #
2296
+ # Determine if kernel polynomial is entirely 2-torsion
2297
+ #
2298
+ psi_G = two_torsion_part(E, psi).monic()
2299
+
2300
+ if psi_G.degree() != 0: # even degree case
2301
+
2302
+ psi_quo = psi//psi_G
2303
+
2304
+ if psi_quo.degree() != 0:
2305
+ raise NotImplementedError("Kohel's algorithm currently only supports cyclic isogenies (except for [2])")
2306
+
2307
+ phi, omega, v, w, _, d = self.__init_even_kernel_polynomial(E, psi_G)
2308
+
2309
+ else: # odd degree case
2310
+
2311
+ phi, omega, v, w, _, d = self.__init_odd_kernel_polynomial(E, psi)
2312
+
2313
+ #
2314
+ # Set up the necessary instance variables
2315
+ #
2316
+
2317
+ self.__kernel_polynomial = psi
2318
+ self.__inner_kernel_polynomial = psi
2319
+
2320
+ self._degree = Integer(d) # degree of the isogeny
2321
+
2322
+ # As a rational map, the isogeny maps (x,y) to (X,Y), where
2323
+ # X=phi(x)/psi(x)^2 and Y=omega(x,y)/psi(x)^3. Both phi and
2324
+ # psi are univariate polynomials in x, while omega is a
2325
+ # bivariate polynomial in x, y. The names are compatible so
2326
+ # that univariate polynomials automatically coerce into the
2327
+ # bivariate polynomial ring.
2328
+
2329
+ self.__psi = psi
2330
+ self.__phi = phi
2331
+ self.__omega = omega
2332
+
2333
+ self.__v = v
2334
+ self.__w = w
2335
+
2336
+ def __init_even_kernel_polynomial(self, E, psi_G):
2337
+ r"""
2338
+ Return the isogeny parameters for the 2-part of an isogeny.
2339
+
2340
+ INPUT:
2341
+
2342
+ - ``E`` -- an elliptic curve
2343
+
2344
+ - ``psi_G`` -- a univariate polynomial over the base field of
2345
+ ``E`` of degree 1 or 3 dividing its 2-division polynomial
2346
+
2347
+ OUTPUT:
2348
+
2349
+ A tuple (``phi``, ``omega``, ``v``, ``w``, ``n``, ``d``) where:
2350
+
2351
+ - ``phi`` is a univariate polynomial, the numerator of the
2352
+ `X`-coordinate of the isogeny;
2353
+
2354
+ - ``omega`` is a bivariate polynomial, the numerator of the
2355
+ `Y`-coordinate of the isogeny;
2356
+
2357
+ - ``v``, ``w`` are the Vélu parameters of the isogeny;
2358
+
2359
+ - ``n`` is the degree of ``psi``;
2360
+
2361
+ - ``d`` is the degree of the isogeny.
2362
+
2363
+ EXAMPLES:
2364
+
2365
+ These examples inherently exercise this private function::
2366
+
2367
+ sage: R.<x> = GF(7)[]
2368
+ sage: E = EllipticCurve(GF(7), [-1,0])
2369
+ sage: phi = EllipticCurveIsogeny(E, x); phi
2370
+ Isogeny of degree 2
2371
+ from Elliptic Curve defined by y^2 = x^3 + 6*x over Finite Field of size 7
2372
+ to Elliptic Curve defined by y^2 = x^3 + 4*x over Finite Field of size 7
2373
+
2374
+ sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import two_torsion_part
2375
+ sage: psig = two_torsion_part(E,x)
2376
+ sage: phi._EllipticCurveIsogeny__init_even_kernel_polynomial(E,psig)
2377
+ (x^3 + 6*x, (x^3 + x)*y, 6, 0, 1, 2)
2378
+
2379
+ sage: F = GF(2^4, 'alpha'); R.<x> = F[] # needs sage.rings.finite_rings
2380
+ sage: E = EllipticCurve(F, [1,1,0,1,0]) # needs sage.rings.finite_rings
2381
+ sage: phi = EllipticCurveIsogeny(E, x); phi # needs sage.rings.finite_rings
2382
+ Isogeny of degree 2
2383
+ from Elliptic Curve defined by y^2 + x*y = x^3 + x^2 + x over Finite Field in alpha of size 2^4
2384
+ to Elliptic Curve defined by y^2 + x*y = x^3 + x^2 + 1 over Finite Field in alpha of size 2^4
2385
+
2386
+ sage: psig = two_torsion_part(E,x) # needs sage.rings.finite_rings
2387
+ sage: phi._EllipticCurveIsogeny__init_even_kernel_polynomial(E,psig) # needs sage.rings.finite_rings
2388
+ (x^3 + x, (x^3 + x)*y + x^2, 1, 0, 1, 2)
2389
+
2390
+ sage: E = EllipticCurve(GF(7), [0,-1,0,0,1])
2391
+ sage: R.<x> = GF(7)[]
2392
+ sage: f = x^3 + 6*x^2 + 1
2393
+ sage: phi = EllipticCurveIsogeny(E, f); phi
2394
+ Isogeny of degree 4
2395
+ from Elliptic Curve defined by y^2 = x^3 + 6*x^2 + 1 over Finite Field of size 7
2396
+ to Elliptic Curve defined by y^2 = x^3 + 6*x^2 + 2*x + 5 over Finite Field of size 7
2397
+ sage: psig = two_torsion_part(E,f)
2398
+ sage: psig = two_torsion_part(E,f)
2399
+ sage: phi._EllipticCurveIsogeny__init_even_kernel_polynomial(E,psig)
2400
+ (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)
2401
+ """
2402
+ # check if the polynomial really divides the two_torsion_polynomial
2403
+ if self.__check and E.division_polynomial(2, x=self.__poly_ring.gen()) % psi_G != 0:
2404
+ raise ValueError(f"the polynomial {psi_G} does not define a finite subgroup of {E}")
2405
+
2406
+ n = psi_G.degree() # 1 or 3
2407
+ d = n+1 # 2 or 4
2408
+
2409
+ a1, a2, a3, a4, a6 = E.a_invariants()
2410
+ b2, b4, _, _ = E.b_invariants()
2411
+ x = self.__poly_ring.gen()
2412
+ y = self.__mpoly_ring.gen()
2413
+
2414
+ if n == 1:
2415
+ x0 = -psi_G.constant_coefficient()
2416
+
2417
+ # determine y0
2418
+ if self.__base_field.characteristic() == 2:
2419
+ y0 = (x0**3 + a2*x0**2 + a4*x0 + a6).sqrt()
2420
+ else:
2421
+ y0 = -(a1*x0 + a3)/2
2422
+
2423
+ v,w = compute_vw_kohel_even_deg1(x0, y0, a1, a2, a4)
2424
+
2425
+ phi = (x*psi_G + v)*psi_G
2426
+ omega = (y*psi_G**2 - v*(a1*psi_G + (y - y0)))*psi_G
2427
+
2428
+ elif n == 3:
2429
+ s1 = -psi_G[n - 1]
2430
+ s2 = psi_G[n - 2]
2431
+ s3 = -psi_G[n - 3]
2432
+
2433
+ psi_G_pr = psi_G.derivative()
2434
+ psi_G_prpr = psi_G_pr.derivative()
2435
+
2436
+ phi = (psi_G_pr**2) + (-2*psi_G_prpr + (4*x - s1))*psi_G
2437
+ phi_pr = phi.derivative(x)
2438
+
2439
+ psi_2 = 2*y + a1*x + a3
2440
+
2441
+ omega = (psi_2*(phi_pr*psi_G - phi*psi_G_pr) - (a1*phi + a3*psi_G)*psi_G)/2
2442
+
2443
+ phi *= psi_G
2444
+ omega *= psi_G
2445
+
2446
+ v,w = compute_vw_kohel_even_deg3(b2, b4, s1, s2, s3)
2447
+
2448
+ else:
2449
+ raise ValueError(f"input polynomial must have degree 1 or 3, not {n}")
2450
+
2451
+ return phi, omega, v, w, n, d
2452
+
2453
+ def __init_odd_kernel_polynomial(self, E, psi):
2454
+ r"""
2455
+ Return the isogeny parameters for a cyclic isogeny of odd degree.
2456
+
2457
+ INPUT:
2458
+
2459
+ - ``E`` -- an elliptic curve
2460
+
2461
+ - ``psi`` -- a univariate polynomial over the base field of
2462
+ ``E``, assumed to be a kernel polynomial
2463
+
2464
+ OUTPUT:
2465
+
2466
+ A tuple (``phi``, ``omega``, ``v``, ``w``, ``n``, ``d``) where:
2467
+
2468
+ - ``phi`` is a univariate polynomial, the numerator of the
2469
+ `X`-coordinate of the isogeny;
2470
+
2471
+ - ``omega`` is a bivariate polynomial, the numerator of the
2472
+ `Y`-coordinate of the isogeny;
2473
+
2474
+ - ``v``, ``w`` are the Vélu parameters of the isogeny;
2475
+
2476
+ - ``n`` is the degree of ``psi``;
2477
+
2478
+ - ``d`` is the degree of the isogeny.
2479
+
2480
+ EXAMPLES:
2481
+
2482
+ These examples inherently exercise this private function::
2483
+
2484
+ sage: R.<x> = GF(7)[]
2485
+ sage: E = EllipticCurve(GF(7), [0,-1,0,0,1])
2486
+ sage: phi = EllipticCurveIsogeny(E, x+6, degree=3); phi
2487
+ Isogeny of degree 3
2488
+ from Elliptic Curve defined by y^2 = x^3 + 6*x^2 + 1 over Finite Field of size 7
2489
+ to Elliptic Curve defined by y^2 = x^3 + 6*x^2 + 4*x + 2 over Finite Field of size 7
2490
+
2491
+ sage: R.<x> = GF(7)[]
2492
+ sage: phi._EllipticCurveIsogeny__init_odd_kernel_polynomial(E, x+6)
2493
+ (x^3 + 5*x^2 + 3*x + 2, (x^3 + 4*x^2 + x)*y, 2, 6, 1, 3)
2494
+
2495
+ sage: # needs sage.rings.finite_rings
2496
+ sage: F = GF(2^4, 'alpha'); R.<x> = F[]
2497
+ sage: alpha = F.gen()
2498
+ sage: E = EllipticCurve(F, [1,1,F.gen(),F.gen()^2+1,1])
2499
+ sage: f = x + alpha^2 + 1
2500
+ sage: phi = EllipticCurveIsogeny(E, f); phi
2501
+ Isogeny of degree 3
2502
+ 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
2503
+ 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
2504
+
2505
+ sage: R.<x> = F[] # needs sage.rings.finite_rings
2506
+ sage: f = x + alpha^2 + 1 # needs sage.rings.finite_rings
2507
+ sage: phi._EllipticCurveIsogeny__init_odd_kernel_polynomial(E, f) # needs sage.rings.finite_rings
2508
+ (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)
2509
+
2510
+ sage: E = EllipticCurve(j=-262537412640768000)
2511
+ sage: f = E.isogenies_prime_degree()[0].kernel_polynomial()
2512
+ sage: f.degree()
2513
+ 81
2514
+ sage: E.isogeny(kernel=f, check=False)
2515
+ Isogeny of degree 163
2516
+ from Elliptic Curve defined by y^2 + y = x^3 - 2174420*x + 1234136692 over Rational Field
2517
+ to Elliptic Curve defined by y^2 + y = x^3 - 57772164980*x - 5344733777551611 over Rational Field
2518
+ """
2519
+ n = psi.degree()
2520
+ d = 2*n + 1
2521
+
2522
+ # check if the polynomial really divides the torsion polynomial :
2523
+ if self.__check:
2524
+ from .isogeny_small_degree import is_kernel_polynomial
2525
+ if not is_kernel_polynomial(E, d, psi):
2526
+ raise ValueError(f"the polynomial {psi} does not define a finite subgroup of {E}")
2527
+
2528
+ b2, b4, b6, _ = E.b_invariants()
2529
+
2530
+ s1 = s2 = s3 = 0
2531
+ if 1 <= n:
2532
+ s1 = -psi[n-1]
2533
+ if 2 <= n:
2534
+ s2 = psi[n-2]
2535
+ if 3 <= n:
2536
+ s3 = -psi[n-3]
2537
+
2538
+ # initializing these allows us to calculate E2.
2539
+ v, w = compute_vw_kohel_odd(b2, b4, b6, s1, s2, s3, n)
2540
+
2541
+ # initialize the polynomial temporary variables
2542
+
2543
+ psi_pr = psi.derivative()
2544
+ psi_prpr = psi_pr.derivative()
2545
+
2546
+ x = self.__poly_ring.gen()
2547
+
2548
+ phi = (4*x**3 + b2*x**2 + 2*b4*x + b6)*(psi_pr**2 - psi_prpr*psi) \
2549
+ - (6*x**2 + b2*x + b4)*psi_pr*psi + (d*x - 2*s1)*psi**2
2550
+
2551
+ phi_pr = phi.derivative(x)
2552
+
2553
+ if self.__base_field.characteristic() != 2:
2554
+ omega = self.__compute_omega_fast(E, psi, psi_pr, phi, phi_pr)
2555
+ else:
2556
+ omega = self.__compute_omega_general(E, psi, psi_pr, phi, phi_pr)
2557
+
2558
+ return phi, omega, v, w, n, d
2559
+
2560
+ #
2561
+ # This is the fast omega computation that works when characteristic is not 2
2562
+ #
2563
+ def __compute_omega_fast(self, E, psi, psi_pr, phi, phi_pr):
2564
+ r"""
2565
+ Return omega from phi, psi and their derivatives, used when
2566
+ the characteristic field is not 2.
2567
+
2568
+ INPUT:
2569
+
2570
+ - ``E`` -- an elliptic curve
2571
+
2572
+ - ``psi``, ``psi_pr``, ``phi``, ``phi_pr`` -- univariate
2573
+ polynomials over the base field of ``E``, where ``psi``
2574
+ is the kernel polynomial and ``phi`` the numerator of
2575
+ the `X`-coordinate of the isogeny, together with their
2576
+ derivatives
2577
+
2578
+ OUTPUT:
2579
+
2580
+ A bivariate polynomial ``omega`` giving the numerator of
2581
+ the `Y`-coordinate of the isogeny.
2582
+
2583
+ EXAMPLES:
2584
+
2585
+ These examples inherently exercise this private function::
2586
+
2587
+ sage: R.<x> = GF(7)[]
2588
+ sage: E = EllipticCurve(GF(7), [0,-1,0,0,1])
2589
+ sage: phi = EllipticCurveIsogeny(E, x+6, degree=3); phi
2590
+ Isogeny of degree 3
2591
+ from Elliptic Curve defined by y^2 = x^3 + 6*x^2 + 1 over Finite Field of size 7
2592
+ to Elliptic Curve defined by y^2 = x^3 + 6*x^2 + 4*x + 2 over Finite Field of size 7
2593
+
2594
+ sage: R.<x,y> = GF(7)[]
2595
+ sage: psi = phi._EllipticCurveIsogeny__psi
2596
+ sage: psi_pr = psi.derivative()
2597
+ sage: fi = phi._EllipticCurveIsogeny__phi
2598
+ sage: fi_pr = fi.derivative()
2599
+ sage: phi._EllipticCurveIsogeny__compute_omega_fast(E, psi, psi_pr, fi, fi_pr)
2600
+ (x^3 + 4*x^2 + x)*y
2601
+ """
2602
+ a1 = E.a1()
2603
+ a3 = E.a3()
2604
+ x = self.__poly_ring.gen()
2605
+ y = self.__mpoly_ring.gen()
2606
+
2607
+ psi_2 = 2*y + a1*x + a3
2608
+
2609
+ # The formula in Kohel's thesis has some typos:
2610
+ # Notably, the first plus sign should be a minus
2611
+ # as it is below.
2612
+ return phi_pr*psi*psi_2/2 - phi*psi_pr*psi_2 - (a1*phi + a3*psi**2)*psi/2
2613
+
2614
+ def __compute_omega_general(self, E, psi, psi_pr, phi, phi_pr):
2615
+ r"""
2616
+ Return omega from phi, psi and their derivatives, in any
2617
+ characteristic.
2618
+
2619
+ INPUT:
2620
+
2621
+ - ``E`` -- an elliptic curve
2622
+
2623
+ - ``psi``, ``psi_pr``, ``phi``, ``phi_pr`` -- univariate polynomials
2624
+ over the base field of ``E``, where ``psi`` is the kernel
2625
+ polynomial and ``phi`` the numerator of the `X`-coordinate
2626
+ of the isogeny, together with their derivatives
2627
+
2628
+ OUTPUT:
2629
+
2630
+ A bivariate polynomial ``omega`` giving the numerator of
2631
+ the `Y`-coordinate of the isogeny.
2632
+
2633
+ EXAMPLES:
2634
+
2635
+ These examples inherently exercise this private function::
2636
+
2637
+ sage: # needs sage.rings.finite_rings
2638
+ sage: F = GF(2^4, 'alpha'); R.<x> = F[]
2639
+ sage: alpha = F.gen()
2640
+ sage: E = EllipticCurve(F, [1, 1, F.gen(), F.gen()^2+1, 1])
2641
+ sage: f = x + alpha^2 + 1
2642
+ sage: phi = EllipticCurveIsogeny(E, f); phi
2643
+ Isogeny of degree 3
2644
+ 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
2645
+ 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
2646
+
2647
+ sage: # needs sage.rings.finite_rings
2648
+ sage: R.<x,y> = F[]
2649
+ sage: psi = phi._EllipticCurveIsogeny__psi
2650
+ sage: psi_pr = psi.derivative()
2651
+ sage: fi = phi._EllipticCurveIsogeny__phi
2652
+ sage: fi_pr = fi.derivative()
2653
+ sage: phi._EllipticCurveIsogeny__compute_omega_general(E, psi, psi_pr, fi, fi_pr)
2654
+ (x^3 + (alpha^2 + 1)*x^2 + (alpha^2 + 1)*x + alpha)*y + (alpha^2 + alpha + 1)*x^2 + (alpha^2 + alpha)*x + alpha
2655
+
2656
+ A bug fixed in :issue:`7907`::
2657
+
2658
+ sage: # needs sage.rings.finite_rings
2659
+ sage: F = GF(128,'a')
2660
+ sage: a = F.gen()
2661
+ sage: E = EllipticCurve([1,0,0,0,(a**6+a**4+a**2+a)])
2662
+ sage: x = polygen(F)
2663
+ 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
2664
+ ....: + (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)
2665
+ sage: E.isogeny(ker)
2666
+ Isogeny of degree 13
2667
+ 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
2668
+ 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
2669
+ """
2670
+ a1, a2, a3, a4, a6 = E.a_invariants()
2671
+ b2, b4, _, _ = E.b_invariants()
2672
+ x = self.__poly_ring.gen()
2673
+ y = self.__mpoly_ring.gen()
2674
+
2675
+ n = psi.degree()
2676
+ d = 2 * n + 1
2677
+
2678
+ s1 = -psi[n-1] if n > 0 else 0
2679
+
2680
+ psi_prpr = 0
2681
+ cur_x_pow = 1
2682
+
2683
+ # Note: we now get the "derivatives" of psi
2684
+ # these are not actually the derivatives
2685
+ # furthermore, the formulas in Kohel's
2686
+ # thesis are wrong, the correct formulas
2687
+ # are coded below
2688
+
2689
+ from sage.arith.misc import binomial
2690
+
2691
+ for j in range(n - 1):
2692
+ psi_prpr += binomial(j+2, 2) * psi[j+2] * cur_x_pow
2693
+ cur_x_pow = x * cur_x_pow
2694
+
2695
+ psi_prprpr = 0
2696
+ cur_x_pow = 1
2697
+
2698
+ for j in range(n - 2):
2699
+ psi_prprpr += (3 * binomial(j+3, 3)) * psi[j+3] * cur_x_pow
2700
+ cur_x_pow = x * cur_x_pow
2701
+
2702
+ psi_2 = 2 * y + a1 * x + a3
2703
+
2704
+ omega = phi_pr*psi*y - phi*psi_pr*psi_2 \
2705
+ + ((a1*x + a3)*(psi_2**2)*(psi_prpr*psi_pr-psi_prprpr*psi)
2706
+ + (a1*psi_2**2 - 3*(a1*x + a3)*(6*x**2 + b2*x + b4))*psi_prpr*psi
2707
+ + (a1*x**3 + 3*a3*x**2 + (2*a2*a3 - a1*a4)*x + (a3*a4 - 2*a1*a6))*psi_pr**2
2708
+ + (-(3*a1*x**2 + 6*a3*x + (-a1*a4 + 2*a2*a3))
2709
+ + (a1*x + a3)*(d*x - 2*s1) )*psi_pr*psi + (a1*s1 + a3*n)*psi**2) * psi
2710
+
2711
+ return omega
2712
+
2713
+ def __compute_via_kohel_numeric(self, xP, yP):
2714
+ r"""
2715
+ Private function that computes the image of a point under this
2716
+ isogeny, using Kohel's formulas.
2717
+
2718
+ EXAMPLES:
2719
+
2720
+ These examples inherently exercise this private function::
2721
+
2722
+ sage: R.<x> = GF(7)[]
2723
+ sage: E = EllipticCurve(GF(7), [0,-1,0,0,1])
2724
+ sage: phi = EllipticCurveIsogeny(E, x+6, degree=3)
2725
+ sage: P = E((0,1)); phi(P)
2726
+ (2 : 0 : 1)
2727
+ sage: P = E((1,1)); phi(P)
2728
+ (0 : 1 : 0)
2729
+ sage: phi._EllipticCurveIsogeny__compute_via_kohel_numeric(0, 1)
2730
+ (2, 0)
2731
+ sage: phi._EllipticCurveIsogeny__compute_via_kohel_numeric(1, 1)
2732
+ ()
2733
+ """
2734
+ # first check if the point is in the kernel
2735
+ if self.__inner_kernel_polynomial(xP) == 0:
2736
+ return ()
2737
+
2738
+ return self.__compute_via_kohel(xP, yP)
2739
+
2740
+ def __compute_via_kohel(self, xP, yP):
2741
+ r"""
2742
+ Private function that applies Kohel's formulas.
2743
+
2744
+ EXAMPLES:
2745
+
2746
+ These examples inherently exercise this private function::
2747
+
2748
+ sage: R.<x> = GF(7)[]
2749
+ sage: E = EllipticCurve(GF(7), [0,-1,0,0,1])
2750
+ sage: phi = EllipticCurveIsogeny(E, x+6, degree=3)
2751
+ sage: P = E((0,1)); phi(P)
2752
+ (2 : 0 : 1)
2753
+ sage: phi.rational_maps()
2754
+ ((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))
2755
+ sage: phi._EllipticCurveIsogeny__compute_via_kohel(0,1)
2756
+ (2, 0)
2757
+ sage: R.<x,y> = GF(7)[]
2758
+ sage: phi._EllipticCurveIsogeny__compute_via_kohel(x,y)
2759
+ ((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))
2760
+ """
2761
+ a = self.__phi(xP)
2762
+ omega0 = self.__omega[0]
2763
+ omega1 = self.__omega[1]
2764
+ b = omega0(xP) + omega1(xP)*yP
2765
+ c = self.__psi(xP)
2766
+ return a/c**2, b/c**3
2767
+
2768
+ def __initialize_rational_maps_via_kohel(self):
2769
+ r"""
2770
+ Private function that computes and initializes the rational
2771
+ maps of this isogeny.
2772
+
2773
+ EXAMPLES:
2774
+
2775
+ These examples inherently exercise this private function::
2776
+
2777
+ sage: R.<x> = GF(7)[]
2778
+ sage: E = EllipticCurve(GF(7), [0,-1,0,0,1])
2779
+ sage: phi = EllipticCurveIsogeny(E, x+6, degree=3)
2780
+ sage: phi.rational_maps()
2781
+ ((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))
2782
+ sage: phi._EllipticCurveIsogeny__initialize_rational_maps_via_kohel()
2783
+ ((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))
2784
+ """
2785
+ x = self.__poly_ring.gen()
2786
+ y = self.__xyfield.gen(1)
2787
+ return self.__compute_via_kohel(x, y)
2788
+
2789
+ #
2790
+ # Kohel's formula computing the codomain curve
2791
+ #
2792
+ def __compute_codomain_via_kohel(self):
2793
+ r"""
2794
+ Private function that computes and initializes the codomain of
2795
+ the isogeny (via Kohel's.)
2796
+
2797
+ EXAMPLES:
2798
+
2799
+ These examples inherently exercise this private function::
2800
+
2801
+ sage: R.<x> = GF(7)[]
2802
+ sage: E = EllipticCurve(GF(7), [0,-1,0,0,1])
2803
+ sage: phi = EllipticCurveIsogeny(E, x+6, degree=3)
2804
+ sage: phi.codomain()
2805
+ Elliptic Curve defined by y^2 = x^3 + 6*x^2 + 4*x + 2 over Finite Field of size 7
2806
+ sage: phi._EllipticCurveIsogeny__compute_codomain_via_kohel()
2807
+ Elliptic Curve defined by y^2 = x^3 + 6*x^2 + 4*x + 2 over Finite Field of size 7
2808
+ """
2809
+ return compute_codomain_formula(self._domain, self.__v, self.__w)
2810
+
2811
+ #
2812
+ # public isogeny methods
2813
+ #
2814
+
2815
+ def rational_maps(self):
2816
+ r"""
2817
+ Return the pair of rational maps defining this isogeny.
2818
+
2819
+ .. NOTE::
2820
+
2821
+ Both components are returned as elements of the function
2822
+ field `F(x,y)` in two variables over the base field `F`,
2823
+ though the first only involves `x`. To obtain the
2824
+ `x`-coordinate function as a rational function in `F(x)`,
2825
+ use :meth:`x_rational_map`.
2826
+
2827
+ EXAMPLES::
2828
+
2829
+ sage: E = EllipticCurve(QQ, [0,2,0,1,-1])
2830
+ sage: phi = EllipticCurveIsogeny(E, [1])
2831
+ sage: phi.rational_maps()
2832
+ (x, y)
2833
+
2834
+ sage: E = EllipticCurve(GF(17), [0,0,0,3,0])
2835
+ sage: phi = EllipticCurveIsogeny(E, E((0,0)))
2836
+ sage: phi.rational_maps()
2837
+ ((x^2 + 3)/x, (x^2*y - 3*y)/x^2)
2838
+ """
2839
+ self.__initialize_rational_maps()
2840
+ return tuple(self.__xyfield(f) for f in self.__ratl_maps)
2841
+
2842
+ def x_rational_map(self):
2843
+ r"""
2844
+ Return the rational map giving the `x`-coordinate of this isogeny.
2845
+
2846
+ .. NOTE::
2847
+
2848
+ This function returns the `x`-coordinate component of the
2849
+ isogeny as a rational function in `F(x)`, where `F` is the
2850
+ base field. To obtain both coordinate functions as
2851
+ elements of the function field `F(x,y)` in two variables,
2852
+ use :meth:`rational_maps`.
2853
+
2854
+ EXAMPLES::
2855
+
2856
+ sage: E = EllipticCurve(QQ, [0,2,0,1,-1])
2857
+ sage: phi = EllipticCurveIsogeny(E, [1])
2858
+ sage: phi.x_rational_map()
2859
+ x
2860
+
2861
+ sage: E = EllipticCurve(GF(17), [0,0,0,3,0])
2862
+ sage: phi = EllipticCurveIsogeny(E, E((0,0)))
2863
+ sage: phi.x_rational_map()
2864
+ (x^2 + 3)/x
2865
+ """
2866
+ self.__initialize_rational_maps()
2867
+ return self.__ratl_maps[0]
2868
+
2869
+ def scaling_factor(self):
2870
+ r"""
2871
+ Return the Weierstrass scaling factor associated to this
2872
+ elliptic-curve isogeny.
2873
+
2874
+ The scaling factor is the constant `u` (in the base field)
2875
+ such that `\varphi^* \omega_2 = u \omega_1`, where
2876
+ `\varphi: E_1\to E_2` is this isogeny and `\omega_i` are
2877
+ the standard Weierstrass differentials on `E_i` defined by
2878
+ `\mathrm dx/(2y+a_1x+a_3)`.
2879
+
2880
+ EXAMPLES::
2881
+
2882
+ sage: # needs sage.rings.finite_rings
2883
+ sage: E = EllipticCurve(GF(257^2), [0,1])
2884
+ sage: phi = E.isogeny(E.lift_x(240))
2885
+ sage: phi.degree()
2886
+ 43
2887
+ sage: phi.scaling_factor()
2888
+ 1
2889
+ sage: phi.dual().scaling_factor()
2890
+ 43
2891
+
2892
+ TESTS:
2893
+
2894
+ Check for :issue:`36638`::
2895
+
2896
+ sage: phi.scaling_factor().parent() # needs sage.rings.finite_rings
2897
+ Finite Field in z2 of size 257^2
2898
+
2899
+ ALGORITHM: The "inner" isogeny is normalized by construction,
2900
+ so we only need to account for the scaling factors of a pre-
2901
+ and post-isomorphism.
2902
+ """
2903
+ sc = self.__base_field.one()
2904
+ if self.__pre_isomorphism is not None:
2905
+ sc *= self.__pre_isomorphism.scaling_factor()
2906
+ if self.__post_isomorphism is not None:
2907
+ sc *= self.__post_isomorphism.scaling_factor()
2908
+ return sc
2909
+
2910
+ def kernel_polynomial(self):
2911
+ r"""
2912
+ Return the kernel polynomial of this isogeny.
2913
+
2914
+ EXAMPLES::
2915
+
2916
+ sage: E = EllipticCurve(QQ, [0,0,0,2,0])
2917
+ sage: phi = EllipticCurveIsogeny(E, E((0,0)))
2918
+ sage: phi.kernel_polynomial()
2919
+ x
2920
+
2921
+ sage: E = EllipticCurve('11a1')
2922
+ sage: phi = EllipticCurveIsogeny(E, E.torsion_points())
2923
+ sage: phi.kernel_polynomial()
2924
+ x^2 - 21*x + 80
2925
+
2926
+ sage: E = EllipticCurve(GF(17), [1,-1,1,-1,1])
2927
+ sage: phi = EllipticCurveIsogeny(E, [1])
2928
+ sage: phi.kernel_polynomial()
2929
+ 1
2930
+
2931
+ sage: E = EllipticCurve(GF(31), [0,0,0,3,0])
2932
+ sage: phi = EllipticCurveIsogeny(E, [0,3,0,1])
2933
+ sage: phi.kernel_polynomial()
2934
+ x^3 + 3*x
2935
+ """
2936
+ if self.__kernel_polynomial is None:
2937
+ self.__init_kernel_polynomial()
2938
+ return self.__kernel_polynomial
2939
+
2940
+ def inseparable_degree(self):
2941
+ r"""
2942
+ Return the inseparable degree of this isogeny.
2943
+
2944
+ Since this class only implements separable isogenies,
2945
+ this method always returns one.
2946
+
2947
+ TESTS::
2948
+
2949
+ sage: EllipticCurveIsogeny.inseparable_degree(None)
2950
+ 1
2951
+ """
2952
+ return Integer(1)
2953
+
2954
+ def _set_pre_isomorphism(self, preWI):
2955
+ """
2956
+ Modify this isogeny by pre-composing with a
2957
+ :class:`sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism`.
2958
+
2959
+ For internal use only.
2960
+
2961
+ TESTS::
2962
+
2963
+ sage: E = EllipticCurve(GF(31), [1,1,0,1,-1])
2964
+ sage: R.<x> = GF(31)[]
2965
+ sage: f = x^3 + 9*x^2 + x + 30
2966
+ sage: phi = EllipticCurveIsogeny(E, f)
2967
+ sage: Epr = E.short_weierstrass_model()
2968
+ sage: isom = Epr.isomorphism_to(E)
2969
+ sage: phi._set_pre_isomorphism(isom)
2970
+ sage: phi.rational_maps()
2971
+ ((-6*x^4 - 3*x^3 + 12*x^2 + 10*x - 1)/(x^3 + x - 12),
2972
+ (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))
2973
+ sage: phi(Epr((0,22)))
2974
+ (13 : 21 : 1)
2975
+ sage: phi(Epr((3,7)))
2976
+ (14 : 17 : 1)
2977
+
2978
+ sage: E = EllipticCurve(GF(29), [0,0,0,1,0])
2979
+ sage: R.<x> = GF(29)[]
2980
+ sage: f = x^2 + 5
2981
+ sage: phi = EllipticCurveIsogeny(E, f)
2982
+ sage: phi
2983
+ Isogeny of degree 5
2984
+ from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 29
2985
+ to Elliptic Curve defined by y^2 = x^3 + 20*x over Finite Field of size 29
2986
+ sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
2987
+ sage: inv_isom = WeierstrassIsomorphism(E, (1,-2,5,10))
2988
+ sage: Epr = inv_isom.codomain()
2989
+ sage: isom = Epr.isomorphism_to(E)
2990
+ sage: phi._set_pre_isomorphism(isom)
2991
+ sage: phi
2992
+ Isogeny of degree 5
2993
+ from Elliptic Curve defined by y^2 + 10*x*y + 20*y = x^3 + 27*x^2 + 6 over Finite Field of size 29
2994
+ to Elliptic Curve defined by y^2 = x^3 + 20*x over Finite Field of size 29
2995
+ sage: phi(Epr((12,1)))
2996
+ (26 : 0 : 1)
2997
+ sage: phi(Epr((2,9)))
2998
+ (0 : 0 : 1)
2999
+ sage: phi(Epr((21,12)))
3000
+ (3 : 0 : 1)
3001
+ sage: phi.rational_maps()[0]
3002
+ (x^5 - 10*x^4 - 6*x^3 - 7*x^2 - x + 3)/(x^4 - 8*x^3 + 5*x^2 - 14*x - 6)
3003
+
3004
+ sage: E = EllipticCurve('11a1')
3005
+ sage: R.<x> = QQ[]
3006
+ sage: f = x^2 - 21*x + 80
3007
+ sage: phi = EllipticCurveIsogeny(E, f); phi
3008
+ Isogeny of degree 5
3009
+ from Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field
3010
+ to Elliptic Curve defined by y^2 + y = x^3 - x^2 - 7820*x - 263580 over Rational Field
3011
+ sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
3012
+ sage: Epr = E.short_weierstrass_model()
3013
+ sage: isom = Epr.isomorphism_to(E)
3014
+ sage: phi._set_pre_isomorphism(isom)
3015
+ sage: phi
3016
+ Isogeny of degree 5
3017
+ from Elliptic Curve defined by y^2 = x^3 - 13392*x - 1080432 over Rational Field
3018
+ to Elliptic Curve defined by y^2 + y = x^3 - x^2 - 7820*x - 263580 over Rational Field
3019
+ sage: phi(Epr((168,1188)))
3020
+ (0 : 1 : 0)
3021
+ """
3022
+ WIdom = preWI.domain()
3023
+ WIcod = preWI.codomain()
3024
+
3025
+ if not isinstance(preWI, WeierstrassIsomorphism):
3026
+ raise ValueError("invalid parameter: isomorphism must be a WeierstrassIsomorphism")
3027
+
3028
+ if self._domain != WIcod:
3029
+ raise ValueError("invalid parameter: isomorphism must have codomain curve equal to this isogenies' domain")
3030
+
3031
+ if self.__pre_isomorphism is None:
3032
+ isom = preWI
3033
+ domain = WIdom
3034
+ else:
3035
+ isom = self.__pre_isomorphism*preWI
3036
+ domain = WIdom
3037
+
3038
+ self.__clear_cached_values()
3039
+
3040
+ self.__set_pre_isomorphism(domain, isom)
3041
+
3042
+ def _set_post_isomorphism(self, postWI):
3043
+ """
3044
+ Modify this isogeny by post-composing with a
3045
+ :class:`sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism`.
3046
+
3047
+ For internal use only.
3048
+
3049
+ TESTS::
3050
+
3051
+ sage: E = EllipticCurve(j=GF(31)(0))
3052
+ sage: R.<x> = GF(31)[]
3053
+ sage: phi = EllipticCurveIsogeny(E, x+18)
3054
+ sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
3055
+ sage: phi._set_post_isomorphism(WeierstrassIsomorphism(phi.codomain(), (6,8,10,12)))
3056
+ sage: phi
3057
+ Isogeny of degree 3
3058
+ from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 31
3059
+ 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
3060
+
3061
+ sage: E = EllipticCurve(j=GF(47)(0))
3062
+ sage: f = E.torsion_polynomial(3)/3
3063
+ sage: phi = EllipticCurveIsogeny(E, f)
3064
+ sage: E2 = phi.codomain()
3065
+ sage: post_isom = E2.isomorphism_to(E)
3066
+ sage: phi._set_post_isomorphism(post_isom)
3067
+ sage: phi.rational_maps() == E.multiplication_by_m(3)
3068
+ False
3069
+ sage: phi = -phi
3070
+ sage: phi.rational_maps() == E.multiplication_by_m(3)
3071
+ True
3072
+
3073
+ sage: R.<x> = QQ[]
3074
+ sage: K.<a> = NumberField(x^2 + 2)
3075
+ sage: E = EllipticCurve(j=K(1728))
3076
+ sage: ker_list = E.torsion_points()
3077
+ sage: phi = EllipticCurveIsogeny(E, ker_list)
3078
+ sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
3079
+ sage: post_isom = WeierstrassIsomorphism(phi.codomain(), (a,2,3,5))
3080
+ sage: phi
3081
+ Isogeny of degree 4
3082
+ from Elliptic Curve defined by y^2 = x^3 + x over Number Field in a with defining polynomial x^2 + 2
3083
+ to Elliptic Curve defined by y^2 = x^3 + (-44)*x + 112 over Number Field in a with defining polynomial x^2 + 2
3084
+ """
3085
+ WIdom = postWI.domain()
3086
+ WIcod = postWI.codomain()
3087
+
3088
+ if not isinstance(postWI, WeierstrassIsomorphism):
3089
+ raise ValueError("invalid parameter: isomorphism must be a WeierstrassIsomorphism")
3090
+
3091
+ if self._codomain != WIdom:
3092
+ raise ValueError("invalid parameter: isomorphism must have domain curve equal to this isogenies' codomain")
3093
+
3094
+ if self.__post_isomorphism is None:
3095
+ isom = postWI
3096
+ codomain = WIcod
3097
+ else:
3098
+ isom = postWI*self.__post_isomorphism
3099
+ codomain = WIcod
3100
+
3101
+ self.__clear_cached_values()
3102
+
3103
+ self.__set_post_isomorphism(codomain, isom)
3104
+
3105
+ def dual(self):
3106
+ r"""
3107
+ Return the isogeny dual to this isogeny.
3108
+
3109
+ .. NOTE::
3110
+
3111
+ If `\varphi\colon E \to E'` is the given isogeny and `n`
3112
+ is its degree, then the dual is by definition the unique
3113
+ isogeny `\hat\varphi\colon E'\to E` such that the
3114
+ compositions `\hat\varphi\circ\varphi` and
3115
+ `\varphi\circ\hat\varphi` are the multiplication-by-`n`
3116
+ maps on `E` and `E'`, respectively.
3117
+
3118
+ EXAMPLES::
3119
+
3120
+ sage: E = EllipticCurve('11a1')
3121
+ sage: R.<x> = QQ[]
3122
+ sage: f = x^2 - 21*x + 80
3123
+ sage: phi = EllipticCurveIsogeny(E, f)
3124
+ sage: phi_hat = phi.dual()
3125
+ sage: phi_hat.domain() == phi.codomain()
3126
+ True
3127
+ sage: phi_hat.codomain() == phi.domain()
3128
+ True
3129
+ sage: X, Y = phi.rational_maps()
3130
+ sage: (Xhat, Yhat) = phi_hat.rational_maps()
3131
+ sage: Xm = Xhat.subs(x=X, y=Y)
3132
+ sage: Ym = Yhat.subs(x=X, y=Y)
3133
+ sage: (Xm, Ym) == E.multiplication_by_m(5)
3134
+ True
3135
+
3136
+ sage: E = EllipticCurve(GF(37), [0,0,0,1,8])
3137
+ sage: R.<x> = GF(37)[]
3138
+ sage: f = x^3 + x^2 + 28*x + 33
3139
+ sage: phi = EllipticCurveIsogeny(E, f)
3140
+ sage: phi_hat = phi.dual()
3141
+ sage: phi_hat.codomain() == phi.domain()
3142
+ True
3143
+ sage: phi_hat.domain() == phi.codomain()
3144
+ True
3145
+ sage: X, Y = phi.rational_maps()
3146
+ sage: (Xhat, Yhat) = phi_hat.rational_maps()
3147
+ sage: Xm = Xhat.subs(x=X, y=Y)
3148
+ sage: Ym = Yhat.subs(x=X, y=Y)
3149
+ sage: (Xm, Ym) == E.multiplication_by_m(7)
3150
+ True
3151
+
3152
+ sage: E = EllipticCurve(GF(31), [0,0,0,1,8])
3153
+ sage: R.<x> = GF(31)[]
3154
+ sage: f = x^2 + 17*x + 29
3155
+ sage: phi = EllipticCurveIsogeny(E, f)
3156
+ sage: phi_hat = phi.dual()
3157
+ sage: phi_hat.codomain() == phi.domain()
3158
+ True
3159
+ sage: phi_hat.domain() == phi.codomain()
3160
+ True
3161
+ sage: X, Y = phi.rational_maps()
3162
+ sage: (Xhat, Yhat) = phi_hat.rational_maps()
3163
+ sage: Xm = Xhat.subs(x=X, y=Y)
3164
+ sage: Ym = Yhat.subs(x=X, y=Y)
3165
+ sage: (Xm, Ym) == E.multiplication_by_m(5)
3166
+ True
3167
+
3168
+ Inseparable duals should be computed correctly::
3169
+
3170
+ sage: # needs sage.rings.finite_rings
3171
+ sage: z2 = GF(71^2).gen()
3172
+ sage: E = EllipticCurve(j=57*z2+51)
3173
+ sage: E.isogeny(3*E.lift_x(0)).dual()
3174
+ Composite morphism of degree 71 = 71*1^2:
3175
+ From: Elliptic Curve defined by y^2 = x^3 + (32*z2+67)*x + (24*z2+37)
3176
+ over Finite Field in z2 of size 71^2
3177
+ To: Elliptic Curve defined by y^2 = x^3 + (41*z2+56)*x + (18*z2+42)
3178
+ over Finite Field in z2 of size 71^2
3179
+ sage: E.isogeny(E.lift_x(0)).dual()
3180
+ Composite morphism of degree 213 = 71*3:
3181
+ From: Elliptic Curve defined by y^2 = x^3 + (58*z2+31)*x + (34*z2+58)
3182
+ over Finite Field in z2 of size 71^2
3183
+ To: Elliptic Curve defined by y^2 = x^3 + (41*z2+56)*x + (18*z2+42)
3184
+ over Finite Field in z2 of size 71^2
3185
+
3186
+ ...even if pre- or post-isomorphisms are present::
3187
+
3188
+ sage: # needs sage.rings.finite_rings
3189
+ sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
3190
+ sage: phi = E.isogeny(E.lift_x(0))
3191
+ sage: pre = ~WeierstrassIsomorphism(phi.domain(), (z2,2,3,4))
3192
+ sage: post = WeierstrassIsomorphism(phi.codomain(), (5,6,7,8))
3193
+ sage: phi = post * phi * pre
3194
+ sage: phi.dual()
3195
+ Composite morphism of degree 213 = 71*3:
3196
+ From: Elliptic Curve defined
3197
+ by y^2 + 17*x*y + 45*y = x^3 + 30*x^2 + (6*z2+64)*x + (48*z2+65)
3198
+ over Finite Field in z2 of size 71^2
3199
+ To: Elliptic Curve defined
3200
+ by y^2 + (60*z2+22)*x*y + (69*z2+37)*y = x^3 + (32*z2+48)*x^2
3201
+ + (19*z2+58)*x + (56*z2+22)
3202
+ over Finite Field in z2 of size 71^2
3203
+
3204
+ TESTS:
3205
+
3206
+ Test for :issue:`23928`::
3207
+
3208
+ sage: E = EllipticCurve(j=GF(431**2)(4)) # needs sage.rings.finite_rings
3209
+ sage: phi = E.isogeny(E.lift_x(0)) # needs sage.rings.finite_rings
3210
+ sage: phi.dual() # needs sage.rings.finite_rings
3211
+ Isogeny of degree 2
3212
+ from Elliptic Curve defined by y^2 = x^3 + 427*x over Finite Field in z2 of size 431^2
3213
+ to Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 431^2
3214
+
3215
+ Test (for :issue:`7096`)::
3216
+
3217
+ sage: E = EllipticCurve('11a1')
3218
+ sage: phi = E.isogeny(E(5,5))
3219
+ sage: phi.dual().dual() == phi
3220
+ True
3221
+
3222
+ sage: k = GF(103)
3223
+ sage: E = EllipticCurve(k,[11,11])
3224
+ sage: phi = E.isogeny(E(4,4))
3225
+ sage: phi
3226
+ Isogeny of degree 5
3227
+ from Elliptic Curve defined by y^2 = x^3 + 11*x + 11 over Finite Field of size 103
3228
+ to Elliptic Curve defined by y^2 = x^3 + 25*x + 80 over Finite Field of size 103
3229
+ sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
3230
+ sage: phi = WeierstrassIsomorphism(phi.codomain(),(5,0,1,2)) * phi
3231
+ sage: phi.dual().dual() == phi
3232
+ True
3233
+
3234
+ sage: E = EllipticCurve(GF(103),[1,0,0,1,-1])
3235
+ sage: phi = E.isogeny(E(60,85))
3236
+ sage: phi.dual()
3237
+ Isogeny of degree 7
3238
+ from Elliptic Curve defined by y^2 + x*y = x^3 + 84*x + 34 over Finite Field of size 103
3239
+ to Elliptic Curve defined by y^2 + x*y = x^3 + x + 102 over Finite Field of size 103
3240
+
3241
+ Check that :issue:`17293` is fixed::
3242
+
3243
+ sage: # needs sage.rings.number_field
3244
+ sage: k.<s> = QuadraticField(2)
3245
+ sage: E = EllipticCurve(k, [-3*s*(4 + 5*s), 2*s*(2 + 14*s + 11*s^2)])
3246
+ sage: phi = E.isogenies_prime_degree(3)[0]
3247
+ sage: (-phi).dual() == -phi.dual()
3248
+ True
3249
+ sage: phi._EllipticCurveIsogeny__clear_cached_values() # forget the dual
3250
+ sage: -phi.dual() == (-phi).dual()
3251
+ True
3252
+
3253
+ Check that :issue:`37168` is fixed::
3254
+
3255
+ sage: R.<x> = GF(23)[]
3256
+ sage: F.<a> = FiniteField(23^2, modulus=x^2-x+1)
3257
+ sage: E0 = EllipticCurve(F, (0, 1))
3258
+ sage: E1 = EllipticCurve(F, (8, 1))
3259
+ sage: phi = E0.isogeny(kernel=E0((a, 0)), codomain=E1)
3260
+ sage: phi.dual()
3261
+ Isogeny of degree 2
3262
+ from Elliptic Curve defined by y^2 = x^3 + 8*x + 1 over Finite Field in a of size 23^2
3263
+ to Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in a of size 23^2
3264
+ """
3265
+ if self.__base_field.characteristic() in (2, 3):
3266
+ raise NotImplementedError("computation of dual isogenies not yet implemented in characteristics 2 and 3")
3267
+
3268
+ if self.__dual is not None:
3269
+ return self.__dual
3270
+
3271
+ # trac 7096
3272
+ E1, E2pr, _, _ = compute_intermediate_curves(self.codomain(), self.domain())
3273
+
3274
+ F = self.__base_field
3275
+ d = self._degree
3276
+
3277
+ from sage.schemes.elliptic_curves.hom_composite import EllipticCurveHom_composite
3278
+
3279
+ if F(d) == 0: # inseparable dual!
3280
+ p = F.characteristic()
3281
+ k = d.valuation(p)
3282
+
3283
+ from sage.schemes.elliptic_curves.hom_frobenius import EllipticCurveHom_frobenius
3284
+ frob = EllipticCurveHom_frobenius(self._codomain, k)
3285
+
3286
+ dsep = d // p**k
3287
+ if dsep > 1:
3288
+ #TODO: We could also use resultants here; this is much
3289
+ # faster in some cases (but seems worse in general).
3290
+ # Presumably there should be a wrapper function that
3291
+ # decides on the fly which method to use.
3292
+ # Eventually this should become a .separable_part() method.
3293
+
3294
+ f = self.kernel_polynomial()
3295
+
3296
+ psi = self._domain.division_polynomial(p)
3297
+ mu_num = self._domain._multiple_x_numerator(p)
3298
+ mu_den = self._domain._multiple_x_denominator(p)
3299
+
3300
+ for _ in range(k):
3301
+ f //= f.gcd(psi)
3302
+ S = f.parent().quotient_ring(f)
3303
+ mu = S(mu_num) / S(mu_den)
3304
+ f = mu.minpoly()
3305
+
3306
+ sep = self._domain.isogeny(f, codomain=frob.codomain()).dual()
3307
+
3308
+ else:
3309
+ sep = frob.codomain().isomorphism_to(self._domain)
3310
+
3311
+ phi_hat = EllipticCurveHom_composite.from_factors([frob, sep])
3312
+
3313
+ from sage.schemes.elliptic_curves.hom import find_post_isomorphism
3314
+ mult = self._domain.scalar_multiplication(d)
3315
+ rhs = phi_hat * self
3316
+ corr = find_post_isomorphism(mult, rhs)
3317
+ self.__dual = corr * phi_hat
3318
+ return self.__dual
3319
+
3320
+ else:
3321
+ # trac 7096
3322
+ # this should take care of the case when the isogeny is
3323
+ # not normalized.
3324
+ u = self.scaling_factor()
3325
+ E2 = E2pr.change_weierstrass_model(u/F(d), 0, 0, 0)
3326
+
3327
+ phi_hat = EllipticCurveIsogeny(E1, None, E2, d)
3328
+ # assert phi_hat.scaling_factor() == 1
3329
+
3330
+ for pre_iso in self._codomain.isomorphisms(E1):
3331
+ for post_iso in E2.isomorphisms(self._domain):
3332
+ sc = u * pre_iso.scaling_factor() * post_iso.scaling_factor()
3333
+ if sc == d:
3334
+ break
3335
+ else:
3336
+ continue
3337
+ break
3338
+ else:
3339
+ raise RuntimeError("bug in dual()")
3340
+
3341
+ phi_hat._set_pre_isomorphism(pre_iso)
3342
+ phi_hat._set_post_isomorphism(post_iso)
3343
+ phi_hat.__perform_inheritance_housekeeping()
3344
+ return phi_hat
3345
+
3346
+ @staticmethod
3347
+ def _composition_impl(left, right):
3348
+ r"""
3349
+ Return the composition of an ``EllipticCurveIsogeny``
3350
+ with another elliptic-curve morphism.
3351
+
3352
+ Called by :meth:`EllipticCurveHom._composition_`.
3353
+
3354
+ EXAMPLES::
3355
+
3356
+ sage: E = EllipticCurve(GF(127), [5,2])
3357
+ sage: phi = E.isogeny(E.lift_x(47)); E2 = phi.codomain()
3358
+ sage: iso1 = E.change_weierstrass_model(1,1,1,1).isomorphism_to(E)
3359
+ sage: iso2 = E2.isomorphism_to(E2.change_weierstrass_model(39,0,0,0))
3360
+ sage: phi * iso1 # indirect doctest
3361
+ Isogeny of degree 11
3362
+ 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
3363
+ to Elliptic Curve defined by y^2 = x^3 + 37*x + 85 over Finite Field of size 127
3364
+ sage: iso2 * phi # indirect doctest
3365
+ Isogeny of degree 11
3366
+ from Elliptic Curve defined by y^2 = x^3 + 5*x + 2 over Finite Field of size 127
3367
+ to Elliptic Curve defined by y^2 = x^3 + 117*x + 58 over Finite Field of size 127
3368
+ sage: iso2 * phi * iso1 # indirect doctest
3369
+ Isogeny of degree 11
3370
+ 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
3371
+ to Elliptic Curve defined by y^2 = x^3 + 117*x + 58 over Finite Field of size 127
3372
+
3373
+ TESTS:
3374
+
3375
+ We should return ``NotImplemented`` when passed a combination of
3376
+ elliptic-curve morphism types that we don't handle here::
3377
+
3378
+ sage: phi._composition_impl(iso1, iso1**-1)
3379
+ NotImplemented
3380
+ """
3381
+ if isinstance(left, WeierstrassIsomorphism) and isinstance(right, EllipticCurveIsogeny):
3382
+ result = deepcopy(right)
3383
+ result._set_post_isomorphism(left)
3384
+ return result
3385
+
3386
+ if isinstance(left, EllipticCurveIsogeny) and isinstance(right, WeierstrassIsomorphism):
3387
+ assert isinstance(left, EllipticCurveIsogeny)
3388
+ result = deepcopy(left)
3389
+ result._set_pre_isomorphism(right)
3390
+ return result
3391
+
3392
+ return NotImplemented
3393
+
3394
+
3395
+ def compute_isogeny_bmss(E1, E2, l):
3396
+ r"""
3397
+ Compute the kernel polynomial of the unique normalized isogeny
3398
+ of degree ``l`` between ``E1`` and ``E2``.
3399
+
3400
+ Both curves must be given in short Weierstrass form, and the
3401
+ characteristic must be either `0` or no smaller than `4l+4`.
3402
+
3403
+ ALGORITHM: [BMSS2006]_, algorithm *fastElkies'*.
3404
+
3405
+ EXAMPLES::
3406
+
3407
+ sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_isogeny_bmss
3408
+ sage: E1 = EllipticCurve(GF(167), [153, 112])
3409
+ sage: E2 = EllipticCurve(GF(167), [56, 40])
3410
+ sage: compute_isogeny_bmss(E1, E2, 13)
3411
+ x^6 + 139*x^5 + 73*x^4 + 139*x^3 + 120*x^2 + 88*x
3412
+ """
3413
+ # Original author of this function: Rémy Oudompheng.
3414
+ # https://github.com/remyoudompheng/isogeny_weber/blob/64289127a337ac1bf258b711e02fea02b7df5275/isogeny_weber/isogenies.py#L272-L332
3415
+ # Released under the MIT license: https://github.com/remyoudompheng/isogeny_weber/blob/64289127a337ac1bf258b711e02fea02b7df5275/LICENSE
3416
+ # Slightly adjusted for inclusion in the Sage library.
3417
+ if E1.a1() or E1.a2() or E1.a3():
3418
+ raise ValueError('E1 must be a short Weierstrass curve')
3419
+ if E2.a1() or E2.a2() or E2.a3():
3420
+ raise ValueError('E2 must be a short Weierstrass curve')
3421
+ char = E1.base_ring().characteristic()
3422
+ if char != 0 and char < 4*l + 4:
3423
+ raise ValueError('characteristic must be at least 4*degree+4')
3424
+ Rx, x = E1.base_ring()["x"].objgen()
3425
+ # Compute C = 1/(1 + Ax^4 + Bx^6) mod x^4l
3426
+ A, B = E1.a4(), E1.a6()
3427
+ C = (1 + A * x**4 + B * x**6).inverse_series_trunc(4 * l)
3428
+ # Solve differential equation
3429
+ # The number of terms doubles at each iteration.
3430
+ # S'^2 = G(x,S) = (1 + A2 S^4 + B2 S^6) / (1 + Ax^4 + Bx^6)
3431
+ # S = x + O(x^2)
3432
+ A2, B2 = E2.a4(), E2.a6()
3433
+ S = x + (A2 - A) / 10 * x**5 + (B2 - B) / 14 * x**7
3434
+ sprec = 8
3435
+ while sprec < 4 * l:
3436
+ assert sprec % 2 == 0
3437
+ sprec = min(sprec, 2 * l)
3438
+ # s1 => s1 + x^k s2
3439
+ # 2 s1' s2' - dG/dS(x, s1) s2 = G(x, s1) - s1'2
3440
+ s1 = S
3441
+ ds1 = s1.derivative()
3442
+ s1pows = [1, s1]
3443
+ while len(s1pows) < 7:
3444
+ s1pows.append(s1._mul_trunc_(s1pows[-1], 2 * sprec))
3445
+ GS = C * (1 + A2 * s1pows[4] + B2 * s1pows[6])
3446
+ dGS = C * (4 * A2 * s1pows[3] + 6 * B2 * s1pows[5])
3447
+ # s2' = (dGS / 2s1') s2 + (G(x, s1) - s1'2) / (2s1')
3448
+ denom = (2 * ds1).inverse_series_trunc(2 * sprec)
3449
+ a = dGS._mul_trunc_(denom, 2 * sprec)
3450
+ b = (GS - ds1**2)._mul_trunc_(denom, 2 * sprec)
3451
+ s2 = a.add_bigoh(2 * sprec).solve_linear_de(prec=2 * sprec, b=b, f0=0)
3452
+ S = s1 + Rx(s2)
3453
+ sprec = 2 * sprec
3454
+ # Reconstruct:
3455
+ # S = x * T(x^2)
3456
+ # Compute U = 1/T^2
3457
+ # Reconstruct N(1/x) / D(1/x) = U
3458
+ T = Rx([S[2 * i + 1] for i in range(2 * l)])
3459
+ U = T._mul_trunc_(T, 2 * l).inverse_series_trunc(2 * l)
3460
+ _, Q = Rx(U).rational_reconstruction(x ** (2 * l), l, l)
3461
+ Q = Q.add_bigoh((l + 1) // 2)
3462
+ if not Q.is_square():
3463
+ raise ValueError(f"the two curves are not linked by a cyclic normalized isogeny of degree {l}")
3464
+ Q = Q.sqrt()
3465
+ ker = Rx(Q).reverse(degree=l//2)
3466
+ return ker.monic()
3467
+
3468
+
3469
+ def compute_isogeny_stark(E1, E2, ell):
3470
+ r"""
3471
+ Return the kernel polynomial of an isogeny of degree ``ell``
3472
+ from ``E1`` to ``E2``.
3473
+
3474
+ INPUT:
3475
+
3476
+ - ``E1`` -- domain elliptic curve in short Weierstrass form
3477
+
3478
+ - ``E2`` -- codomain elliptic curve in short Weierstrass form
3479
+
3480
+ - ``ell`` -- the degree of an isogeny from ``E1`` to ``E2``
3481
+
3482
+ OUTPUT: the kernel polynomial of an isogeny from ``E1`` to ``E2``
3483
+
3484
+ .. NOTE::
3485
+
3486
+ If there is no degree-``ell``, cyclic, separable, normalized
3487
+ isogeny from ``E1`` to ``E2``, a :exc:`ValueError` will be
3488
+ raised.
3489
+
3490
+ ALGORITHM:
3491
+
3492
+ This function uses Stark's algorithm as presented in Section 6.2
3493
+ of [BMSS2006]_.
3494
+
3495
+ .. NOTE::
3496
+
3497
+ As published in [BMSS2006]_, the algorithm is incorrect, and a
3498
+ correct version (with slightly different notation) can be found
3499
+ in [Mo2009]_. The algorithm originates in [Sta1973]_.
3500
+
3501
+ EXAMPLES::
3502
+
3503
+ sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_isogeny_stark, compute_sequence_of_maps
3504
+
3505
+ sage: E = EllipticCurve(GF(97), [1,0,1,1,0])
3506
+ sage: R.<x> = GF(97)[]; f = x^5 + 27*x^4 + 61*x^3 + 58*x^2 + 28*x + 21
3507
+ sage: phi = EllipticCurveIsogeny(E, f)
3508
+ sage: E2 = phi.codomain()
3509
+ sage: isom1, isom2, E1pr, E2pr, ker_poly = compute_sequence_of_maps(E, E2, 11)
3510
+ sage: compute_isogeny_stark(E1pr, E2pr, 11)
3511
+ 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
3512
+
3513
+ sage: E = EllipticCurve(GF(37), [0,0,0,1,8])
3514
+ sage: R.<x> = GF(37)[]
3515
+ sage: f = (x + 14) * (x + 30)
3516
+ sage: phi = EllipticCurveIsogeny(E, f)
3517
+ sage: E2 = phi.codomain()
3518
+ sage: compute_isogeny_stark(E, E2, 5)
3519
+ x^4 + 14*x^3 + x^2 + 34*x + 21
3520
+ sage: f**2
3521
+ x^4 + 14*x^3 + x^2 + 34*x + 21
3522
+
3523
+ sage: E = EllipticCurve(QQ, [0,0,0,1,0])
3524
+ sage: R.<x> = QQ[]
3525
+ sage: f = x
3526
+ sage: phi = EllipticCurveIsogeny(E, f)
3527
+ sage: E2 = phi.codomain()
3528
+ sage: compute_isogeny_stark(E, E2, 2)
3529
+ x
3530
+
3531
+ TESTS:
3532
+
3533
+ Check for :issue:`21883`::
3534
+
3535
+ sage: E1 = EllipticCurve([0,1])
3536
+ sage: E2 = EllipticCurve([0,-27])
3537
+ sage: E1.isogeny(None, E2, degree=3)
3538
+ 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
3539
+ """
3540
+ K = E1.base_field()
3541
+ R, x = PolynomialRing(K, 'x').objgen()
3542
+
3543
+ wp1 = E1.weierstrass_p(prec=4*ell+4) #BMSS2006 claim 2*ell is enough, but it is not M09
3544
+ wp2 = E2.weierstrass_p(prec=4*ell+4)
3545
+
3546
+ # viewed them as power series in Z = z^2
3547
+ Z = LaurentSeriesRing(K, 'Z').gen()
3548
+ pe1 = pe2 = 1/Z
3549
+ for i in range(2*ell + 1):
3550
+ pe1 += wp1[2*i] * Z**i
3551
+ pe2 += wp2[2*i] * Z**i
3552
+ pe1 = pe1.add_bigoh(2*ell+3)
3553
+ pe2 = pe2.add_bigoh(2*ell+3)
3554
+
3555
+ n = 1
3556
+ q = [R.one(), R.zero()]
3557
+ T = pe2
3558
+
3559
+ while q[n].degree() < ell-1:
3560
+ n += 1
3561
+ a_n = 0
3562
+ r = -T.valuation()
3563
+ while 0 <= r:
3564
+ t_r = T[-r]
3565
+ a_n = a_n + t_r * x**r
3566
+ T = T - t_r*pe1**r
3567
+ r = -T.valuation()
3568
+
3569
+ q_n = a_n*q[n-1] + q[n-2]
3570
+ q.append(q_n)
3571
+
3572
+ if n == ell+1 or T == 0:
3573
+ if T == 0 or T.valuation() < 2:
3574
+ raise ValueError(f"the two curves are not linked by a cyclic normalized isogeny of degree {ell}")
3575
+ break
3576
+
3577
+ T = 1/T
3578
+
3579
+ qn = q[n]
3580
+ qn /= qn.leading_coefficient()
3581
+ return qn
3582
+
3583
+
3584
+ def compute_isogeny_kernel_polynomial(E1, E2, ell, algorithm=None):
3585
+ r"""
3586
+ Return the kernel polynomial of a cyclic, separable, normalized
3587
+ isogeny of degree ``ell`` from ``E1`` to ``E2``.
3588
+
3589
+ INPUT:
3590
+
3591
+ - ``E1`` -- domain elliptic curve in short Weierstrass form
3592
+
3593
+ - ``E2`` -- codomain elliptic curve in short Weierstrass form
3594
+
3595
+ - ``ell`` -- the degree of an isogeny from ``E1`` to ``E2``
3596
+
3597
+ - ``algorithm`` -- ``None`` (default, choose automatically) or
3598
+ ``'bmss'`` (:func:`compute_isogeny_bmss`) or
3599
+ ``'stark'`` (:func:`compute_isogeny_stark`)
3600
+
3601
+ OUTPUT: the kernel polynomial of an isogeny from ``E1`` to ``E2``
3602
+
3603
+ .. NOTE::
3604
+
3605
+ If there is no degree-``ell``, cyclic, separable, normalized
3606
+ isogeny from ``E1`` to ``E2``, a :exc:`ValueError` will be
3607
+ raised.
3608
+
3609
+ EXAMPLES::
3610
+
3611
+ sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_isogeny_kernel_polynomial
3612
+
3613
+ sage: E = EllipticCurve(GF(37), [0,0,0,1,8])
3614
+ sage: R.<x> = GF(37)[]
3615
+ sage: f = (x + 14) * (x + 30)
3616
+ sage: phi = EllipticCurveIsogeny(E, f)
3617
+ sage: E2 = phi.codomain()
3618
+ sage: compute_isogeny_kernel_polynomial(E, E2, 5)
3619
+ x^2 + 7*x + 13
3620
+ sage: f
3621
+ x^2 + 7*x + 13
3622
+
3623
+ sage: # needs sage.rings.number_field
3624
+ sage: R.<x> = QQ[]
3625
+ sage: K.<i> = NumberField(x^2 + 1)
3626
+ sage: E = EllipticCurve(K, [0,0,0,1,0])
3627
+ sage: E2 = EllipticCurve(K, [0,0,0,16,0])
3628
+ sage: compute_isogeny_kernel_polynomial(E, E2, 4)
3629
+ x^3 + x
3630
+
3631
+ TESTS:
3632
+
3633
+ Check that :meth:`Polynomial.radical` is doing the right thing for us::
3634
+
3635
+ sage: E = EllipticCurve(GF(37), [0,0,0,1,8])
3636
+ sage: R.<x> = GF(37)[]
3637
+ sage: f = (x + 10) * (x + 12) * (x + 16)
3638
+ sage: phi = EllipticCurveIsogeny(E, f)
3639
+ sage: E2 = phi.codomain()
3640
+ sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_isogeny_stark
3641
+ sage: ker_poly = compute_isogeny_stark(E, E2, 7); ker_poly
3642
+ x^6 + 2*x^5 + 20*x^4 + 11*x^3 + 36*x^2 + 35*x + 16
3643
+ sage: ker_poly.factor()
3644
+ (x + 10)^2 * (x + 12)^2 * (x + 16)^2
3645
+ sage: poly = ker_poly.radical(); poly
3646
+ x^3 + x^2 + 28*x + 33
3647
+ sage: poly.factor()
3648
+ (x + 10) * (x + 12) * (x + 16)
3649
+ """
3650
+ if algorithm is None:
3651
+ char = E1.base_ring().characteristic()
3652
+ if char != 0 and char < 4*ell + 4:
3653
+ raise NotImplementedError(f'no algorithm for computing kernel polynomial from domain and codomain is implemented for degree {ell} and characteristic {char}')
3654
+ algorithm = 'stark' if ell < 10 else 'bmss'
3655
+
3656
+ if algorithm == 'bmss':
3657
+ return compute_isogeny_bmss(E1, E2, ell)
3658
+ if algorithm == 'stark':
3659
+ return compute_isogeny_stark(E1, E2, ell).radical()
3660
+
3661
+ raise NotImplementedError(f'unknown algorithm {algorithm}')
3662
+
3663
+
3664
+ def compute_intermediate_curves(E1, E2):
3665
+ r"""
3666
+ Return intermediate curves and isomorphisms.
3667
+
3668
+ .. NOTE::
3669
+
3670
+ This is used to compute `\wp` functions from the short
3671
+ Weierstrass model more easily.
3672
+
3673
+ .. WARNING::
3674
+
3675
+ The base field must be of characteristic not equal to `2` or `3`.
3676
+
3677
+ INPUT:
3678
+
3679
+ - ``E1``, ``E2`` -- elliptic curves
3680
+
3681
+ OUTPUT:
3682
+
3683
+ A tuple (``pre_isomorphism``, ``post_isomorphism``,
3684
+ ``intermediate_domain``, ``intermediate_codomain``) where:
3685
+
3686
+ - ``intermediate_domain`` is a short Weierstrass curve isomorphic
3687
+ to ``E1``;
3688
+
3689
+ - ``intermediate_codomain`` is a short Weierstrass curve isomorphic
3690
+ to ``E2``;
3691
+
3692
+ - ``pre_isomorphism`` is a normalized isomorphism from ``E1`` to
3693
+ ``intermediate_domain``;
3694
+
3695
+ - ``post_isomorphism`` is a normalized isomorphism from
3696
+ ``intermediate_codomain`` to ``E2``.
3697
+
3698
+ EXAMPLES::
3699
+
3700
+ sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_intermediate_curves
3701
+ sage: E = EllipticCurve(GF(83), [1,0,1,1,0])
3702
+ sage: R.<x> = GF(83)[]; f = x + 24
3703
+ sage: phi = EllipticCurveIsogeny(E, f)
3704
+ sage: E2 = phi.codomain()
3705
+ sage: compute_intermediate_curves(E, E2)
3706
+ (Elliptic Curve defined by y^2 = x^3 + 62*x + 74 over Finite Field of size 83,
3707
+ Elliptic Curve defined by y^2 = x^3 + 65*x + 69 over Finite Field of size 83,
3708
+ Elliptic-curve morphism:
3709
+ From: Elliptic Curve defined by y^2 + x*y + y = x^3 + x
3710
+ over Finite Field of size 83
3711
+ To: Elliptic Curve defined by y^2 = x^3 + 62*x + 74
3712
+ over Finite Field of size 83
3713
+ Via: (u,r,s,t) = (1, 76, 41, 3),
3714
+ Elliptic-curve morphism:
3715
+ From: Elliptic Curve defined by y^2 = x^3 + 65*x + 69
3716
+ over Finite Field of size 83
3717
+ To: Elliptic Curve defined by y^2 + x*y + y = x^3 + 4*x + 16
3718
+ over Finite Field of size 83
3719
+ Via: (u,r,s,t) = (1, 7, 42, 42))
3720
+
3721
+ sage: # needs sage.rings.number_field
3722
+ sage: R.<x> = QQ[]
3723
+ sage: K.<i> = NumberField(x^2 + 1)
3724
+ sage: E = EllipticCurve(K, [0,0,0,1,0])
3725
+ sage: E2 = EllipticCurve(K, [0,0,0,16,0])
3726
+ sage: compute_intermediate_curves(E, E2)
3727
+ (Elliptic Curve defined by y^2 = x^3 + x
3728
+ over Number Field in i with defining polynomial x^2 + 1,
3729
+ Elliptic Curve defined by y^2 = x^3 + 16*x
3730
+ over Number Field in i with defining polynomial x^2 + 1,
3731
+ Elliptic-curve endomorphism of Elliptic Curve defined by y^2 = x^3 + x
3732
+ over Number Field in i with defining polynomial x^2 + 1
3733
+ Via: (u,r,s,t) = (1, 0, 0, 0),
3734
+ Elliptic-curve endomorphism of Elliptic Curve defined by y^2 = x^3 + 16*x
3735
+ over Number Field in i with defining polynomial x^2 + 1
3736
+ Via: (u,r,s,t) = (1, 0, 0, 0))
3737
+ """
3738
+ if E1.base_ring().characteristic() in (2, 3):
3739
+ raise NotImplementedError("compute_intermediate_curves is only defined for characteristics not 2 or 3")
3740
+
3741
+ # We cannot just use
3742
+ # E1w = E1.short_weierstrass_model()
3743
+ # E2w = E2.short_weierstrass_model()
3744
+ # as the resulting isomorphisms would not be normalised (u=1)
3745
+
3746
+ c4, c6 = E1.c_invariants()
3747
+ E1w = EllipticCurve([0, 0, 0, -c4/48, -c6/864])
3748
+ c4, c6 = E2.c_invariants()
3749
+ E2w = EllipticCurve([0, 0, 0, -c4/48, -c6/864])
3750
+
3751
+ # We cannot even just use pre_iso = E1.isomorphism_to(E1w) since
3752
+ # it may have u=-1; similarly for E2
3753
+
3754
+ urst = [w for w in _isomorphisms(E1, E1w) if w[0] == 1][0]
3755
+ pre_iso = WeierstrassIsomorphism(E1, urst, E1w)
3756
+ urst = [w for w in _isomorphisms(E2w, E2) if w[0] == 1][0]
3757
+ post_iso = WeierstrassIsomorphism(E2w, urst, E2)
3758
+ return E1w, E2w, pre_iso, post_iso
3759
+
3760
+
3761
+ def compute_sequence_of_maps(E1, E2, ell):
3762
+ r"""
3763
+ Return intermediate curves, isomorphisms and kernel polynomial.
3764
+
3765
+ INPUT:
3766
+
3767
+ - ``E1``, ``E2`` -- elliptic curves
3768
+
3769
+ - ``ell`` -- a prime such that there is a degree-``ell`` separable
3770
+ normalized isogeny from ``E1`` to ``E2``
3771
+
3772
+ OUTPUT:
3773
+
3774
+ A tuple (``pre_isom``, ``post_isom``, ``E1pr``, ``E2pr``,
3775
+ ``ker_poly``) where:
3776
+
3777
+ - ``E1pr`` is an elliptic curve in short Weierstrass form
3778
+ isomorphic to ``E1``;
3779
+
3780
+ - ``E2pr`` is an elliptic curve in short Weierstrass form
3781
+ isomorphic to ``E2``;
3782
+
3783
+ - ``pre_isom`` is a normalized isomorphism from ``E1`` to ``E1pr``;
3784
+
3785
+ - ``post_isom`` is a normalized isomorphism from ``E2pr`` to ``E2``;
3786
+
3787
+ - ``ker_poly`` is the kernel polynomial of an ``ell``-isogeny from
3788
+ ``E1pr`` to ``E2pr``.
3789
+
3790
+ EXAMPLES::
3791
+
3792
+ sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_sequence_of_maps
3793
+ sage: E = EllipticCurve('11a1')
3794
+ sage: R.<x> = QQ[]; f = x^2 - 21*x + 80
3795
+ sage: phi = EllipticCurveIsogeny(E, f)
3796
+ sage: E2 = phi.codomain()
3797
+ sage: compute_sequence_of_maps(E, E2, 5)
3798
+ (Elliptic-curve morphism:
3799
+ From: Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20
3800
+ over Rational Field
3801
+ To: Elliptic Curve defined by y^2 = x^3 - 31/3*x - 2501/108
3802
+ over Rational Field
3803
+ Via: (u,r,s,t) = (1, 1/3, 0, -1/2),
3804
+ Elliptic-curve morphism:
3805
+ From: Elliptic Curve defined by y^2 = x^3 - 23461/3*x - 28748141/108
3806
+ over Rational Field
3807
+ To: Elliptic Curve defined by y^2 + y = x^3 - x^2 - 7820*x - 263580
3808
+ over Rational Field
3809
+ Via: (u,r,s,t) = (1, -1/3, 0, 1/2),
3810
+ Elliptic Curve defined by y^2 = x^3 - 31/3*x - 2501/108 over Rational Field,
3811
+ Elliptic Curve defined by y^2 = x^3 - 23461/3*x - 28748141/108 over Rational Field,
3812
+ x^2 - 61/3*x + 658/9)
3813
+
3814
+ sage: # needs sage.rings.number_field
3815
+ sage: K.<i> = NumberField(x^2 + 1)
3816
+ sage: E = EllipticCurve(K, [0,0,0,1,0])
3817
+ sage: E2 = EllipticCurve(K, [0,0,0,16,0])
3818
+ sage: compute_sequence_of_maps(E, E2, 4)
3819
+ (Elliptic-curve endomorphism of Elliptic Curve defined by y^2 = x^3 + x
3820
+ over Number Field in i with defining polynomial x^2 + 1
3821
+ Via: (u,r,s,t) = (1, 0, 0, 0),
3822
+ Elliptic-curve endomorphism of Elliptic Curve defined by y^2 = x^3 + 16*x
3823
+ over Number Field in i with defining polynomial x^2 + 1
3824
+ Via: (u,r,s,t) = (1, 0, 0, 0),
3825
+ Elliptic Curve defined by y^2 = x^3 + x
3826
+ over Number Field in i with defining polynomial x^2 + 1,
3827
+ Elliptic Curve defined by y^2 = x^3 + 16*x
3828
+ over Number Field in i with defining polynomial x^2 + 1,
3829
+ x^3 + x)
3830
+
3831
+ sage: E = EllipticCurve(GF(97), [1,0,1,1,0])
3832
+ sage: R.<x> = GF(97)[]; f = x^5 + 27*x^4 + 61*x^3 + 58*x^2 + 28*x + 21
3833
+ sage: phi = EllipticCurveIsogeny(E, f)
3834
+ sage: E2 = phi.codomain()
3835
+ sage: compute_sequence_of_maps(E, E2, 11)
3836
+ (Elliptic-curve morphism:
3837
+ From: Elliptic Curve defined by y^2 + x*y + y = x^3 + x
3838
+ over Finite Field of size 97
3839
+ To: Elliptic Curve defined by y^2 = x^3 + 52*x + 31
3840
+ over Finite Field of size 97
3841
+ Via: (u,r,s,t) = (1, 8, 48, 44),
3842
+ Elliptic-curve morphism:
3843
+ From: Elliptic Curve defined by y^2 = x^3 + 41*x + 66
3844
+ over Finite Field of size 97
3845
+ To: Elliptic Curve defined by y^2 + x*y + y = x^3 + 87*x + 26
3846
+ over Finite Field of size 97
3847
+ Via: (u,r,s,t) = (1, 89, 49, 49),
3848
+ Elliptic Curve defined by y^2 = x^3 + 52*x + 31 over Finite Field of size 97,
3849
+ Elliptic Curve defined by y^2 = x^3 + 41*x + 66 over Finite Field of size 97,
3850
+ x^5 + 67*x^4 + 13*x^3 + 35*x^2 + 77*x + 69)
3851
+ """
3852
+ E1pr, E2pr, pre_isom, post_isom = compute_intermediate_curves(E1, E2)
3853
+
3854
+ ker_poly = compute_isogeny_kernel_polynomial(E1pr, E2pr, ell)
3855
+
3856
+ return pre_isom, post_isom, E1pr, E2pr, ker_poly
3857
+
3858
+ # Utility functions for manipulating isogeny degree matrices
3859
+
3860
+
3861
+ def fill_isogeny_matrix(M):
3862
+ """
3863
+ Return a filled isogeny matrix giving all degrees from one giving only prime degrees.
3864
+
3865
+ INPUT:
3866
+
3867
+ - ``M`` -- a square symmetric matrix whose off-diagonal `i`, `j`
3868
+ entry is either a prime `l` if the `i`-th and `j`-th curves
3869
+ have an `l`-isogeny between them, otherwise `0`
3870
+
3871
+ OUTPUT:
3872
+
3873
+ A square matrix with entries `1` on the diagonal, and in
3874
+ general the `i`, `j` entry is `d>0` if `d` is the minimal degree
3875
+ of an isogeny from the `i`-th to the `j`-th curve.
3876
+
3877
+ EXAMPLES::
3878
+
3879
+ sage: M = Matrix([[0, 2, 3, 3, 0, 0], [2, 0, 0, 0, 3, 3], [3, 0, 0, 0, 2, 0],
3880
+ ....: [3, 0, 0, 0, 0, 2], [0, 3, 2, 0, 0, 0], [0, 3, 0, 2, 0, 0]]); M
3881
+ [0 2 3 3 0 0]
3882
+ [2 0 0 0 3 3]
3883
+ [3 0 0 0 2 0]
3884
+ [3 0 0 0 0 2]
3885
+ [0 3 2 0 0 0]
3886
+ [0 3 0 2 0 0]
3887
+ sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import fill_isogeny_matrix
3888
+ sage: fill_isogeny_matrix(M)
3889
+ [ 1 2 3 3 6 6]
3890
+ [ 2 1 6 6 3 3]
3891
+ [ 3 6 1 9 2 18]
3892
+ [ 3 6 9 1 18 2]
3893
+ [ 6 3 2 18 1 9]
3894
+ [ 6 3 18 2 9 1]
3895
+ """
3896
+ from sage.matrix.constructor import Matrix
3897
+ from sage.rings.infinity import Infinity
3898
+
3899
+ n = M.nrows()
3900
+ M0 = copy(M)
3901
+ for i in range(n):
3902
+ M0[i,i] = 1
3903
+
3904
+ def fix(d):
3905
+ return d if d != 0 else Infinity
3906
+
3907
+ def fix2(d):
3908
+ return d if d != Infinity else 0
3909
+
3910
+ def pr(M1, M2):
3911
+ 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)])
3912
+
3913
+ M1 = M0
3914
+ M2 = pr(M0, M1)
3915
+ while M1 != M2:
3916
+ M1 = M2
3917
+ M2 = pr(M0, M1)
3918
+ return M1
3919
+
3920
+
3921
+ def unfill_isogeny_matrix(M):
3922
+ """
3923
+ Reverses the action of ``fill_isogeny_matrix``.
3924
+
3925
+ INPUT:
3926
+
3927
+ - ``M`` -- a square symmetric matrix of integers
3928
+
3929
+ OUTPUT:
3930
+
3931
+ A square symmetric matrix obtained from ``M`` by
3932
+ replacing non-prime entries with `0`.
3933
+
3934
+ EXAMPLES::
3935
+
3936
+ sage: M = Matrix([[0, 2, 3, 3, 0, 0], [2, 0, 0, 0, 3, 3], [3, 0, 0, 0, 2, 0],
3937
+ ....: [3, 0, 0, 0, 0, 2], [0, 3, 2, 0, 0, 0], [0, 3, 0, 2, 0, 0]]); M
3938
+ [0 2 3 3 0 0]
3939
+ [2 0 0 0 3 3]
3940
+ [3 0 0 0 2 0]
3941
+ [3 0 0 0 0 2]
3942
+ [0 3 2 0 0 0]
3943
+ [0 3 0 2 0 0]
3944
+ sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import fill_isogeny_matrix, unfill_isogeny_matrix
3945
+ sage: M1 = fill_isogeny_matrix(M); M1
3946
+ [ 1 2 3 3 6 6]
3947
+ [ 2 1 6 6 3 3]
3948
+ [ 3 6 1 9 2 18]
3949
+ [ 3 6 9 1 18 2]
3950
+ [ 6 3 2 18 1 9]
3951
+ [ 6 3 18 2 9 1]
3952
+ sage: unfill_isogeny_matrix(M1)
3953
+ [0 2 3 3 0 0]
3954
+ [2 0 0 0 3 3]
3955
+ [3 0 0 0 2 0]
3956
+ [3 0 0 0 0 2]
3957
+ [0 3 2 0 0 0]
3958
+ [0 3 0 2 0 0]
3959
+ sage: unfill_isogeny_matrix(M1) == M
3960
+ True
3961
+ """
3962
+ n = M.nrows()
3963
+ M1 = copy(M)
3964
+ zero = Integer(0)
3965
+ for i in range(n):
3966
+ M1[i,i] = zero
3967
+ for j in range(i):
3968
+ if not M1[i,j].is_prime():
3969
+ M1[i,j] = zero
3970
+ M1[j,i] = zero
3971
+ return M1