passagemath-schemes 10.6.40__cp314-cp314-macosx_13_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of passagemath-schemes might be problematic. Click here for more details.

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