passagemath-schemes 10.8.1a4__cp314-cp314t-macosx_13_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (312) hide show
  1. passagemath_schemes/.dylibs/libflint.22.0.dylib +0 -0
  2. passagemath_schemes/.dylibs/libgmp.10.dylib +0 -0
  3. passagemath_schemes/.dylibs/libgmpxx.4.dylib +0 -0
  4. passagemath_schemes/.dylibs/libmpfr.6.dylib +0 -0
  5. passagemath_schemes/__init__.py +3 -0
  6. passagemath_schemes-10.8.1a4.dist-info/METADATA +203 -0
  7. passagemath_schemes-10.8.1a4.dist-info/METADATA.bak +204 -0
  8. passagemath_schemes-10.8.1a4.dist-info/RECORD +312 -0
  9. passagemath_schemes-10.8.1a4.dist-info/WHEEL +6 -0
  10. passagemath_schemes-10.8.1a4.dist-info/top_level.txt +3 -0
  11. sage/all__sagemath_schemes.py +23 -0
  12. sage/databases/all__sagemath_schemes.py +7 -0
  13. sage/databases/cremona.py +1723 -0
  14. sage/dynamics/all__sagemath_schemes.py +2 -0
  15. sage/dynamics/arithmetic_dynamics/affine_ds.py +1083 -0
  16. sage/dynamics/arithmetic_dynamics/all.py +14 -0
  17. sage/dynamics/arithmetic_dynamics/berkovich_ds.py +1101 -0
  18. sage/dynamics/arithmetic_dynamics/dynamical_semigroup.py +1543 -0
  19. sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py +2426 -0
  20. sage/dynamics/arithmetic_dynamics/endPN_minimal_model.py +1169 -0
  21. sage/dynamics/arithmetic_dynamics/generic_ds.py +663 -0
  22. sage/dynamics/arithmetic_dynamics/product_projective_ds.py +339 -0
  23. sage/dynamics/arithmetic_dynamics/projective_ds.py +9556 -0
  24. sage/dynamics/arithmetic_dynamics/projective_ds_helper.cpython-314t-darwin.so +0 -0
  25. sage/dynamics/arithmetic_dynamics/projective_ds_helper.pyx +301 -0
  26. sage/dynamics/arithmetic_dynamics/wehlerK3.py +2578 -0
  27. sage/lfunctions/all.py +18 -0
  28. sage/lfunctions/dokchitser.py +727 -0
  29. sage/lfunctions/pari.py +971 -0
  30. sage/lfunctions/zero_sums.cpython-314t-darwin.so +0 -0
  31. sage/lfunctions/zero_sums.pyx +1847 -0
  32. sage/modular/abvar/abvar.py +5132 -0
  33. sage/modular/abvar/abvar_ambient_jacobian.py +414 -0
  34. sage/modular/abvar/abvar_newform.py +246 -0
  35. sage/modular/abvar/all.py +8 -0
  36. sage/modular/abvar/constructor.py +187 -0
  37. sage/modular/abvar/cuspidal_subgroup.py +371 -0
  38. sage/modular/abvar/finite_subgroup.py +896 -0
  39. sage/modular/abvar/homology.py +721 -0
  40. sage/modular/abvar/homspace.py +989 -0
  41. sage/modular/abvar/lseries.py +415 -0
  42. sage/modular/abvar/morphism.py +935 -0
  43. sage/modular/abvar/torsion_point.py +274 -0
  44. sage/modular/abvar/torsion_subgroup.py +741 -0
  45. sage/modular/all.py +43 -0
  46. sage/modular/arithgroup/all.py +20 -0
  47. sage/modular/arithgroup/arithgroup_element.cpython-314t-darwin.so +0 -0
  48. sage/modular/arithgroup/arithgroup_element.pyx +474 -0
  49. sage/modular/arithgroup/arithgroup_generic.py +1406 -0
  50. sage/modular/arithgroup/arithgroup_perm.py +2692 -0
  51. sage/modular/arithgroup/congroup.cpython-314t-darwin.so +0 -0
  52. sage/modular/arithgroup/congroup.pyx +334 -0
  53. sage/modular/arithgroup/congroup_gamma.py +361 -0
  54. sage/modular/arithgroup/congroup_gamma0.py +692 -0
  55. sage/modular/arithgroup/congroup_gamma1.py +659 -0
  56. sage/modular/arithgroup/congroup_gammaH.py +1491 -0
  57. sage/modular/arithgroup/congroup_generic.py +630 -0
  58. sage/modular/arithgroup/congroup_sl2z.py +266 -0
  59. sage/modular/arithgroup/farey_symbol.cpython-314t-darwin.so +0 -0
  60. sage/modular/arithgroup/farey_symbol.pyx +1067 -0
  61. sage/modular/arithgroup/tests.py +425 -0
  62. sage/modular/btquotients/all.py +4 -0
  63. sage/modular/btquotients/btquotient.py +3736 -0
  64. sage/modular/btquotients/pautomorphicform.py +2564 -0
  65. sage/modular/buzzard.py +100 -0
  66. sage/modular/congroup.py +29 -0
  67. sage/modular/congroup_element.py +13 -0
  68. sage/modular/cusps.py +1107 -0
  69. sage/modular/cusps_nf.py +1270 -0
  70. sage/modular/dims.py +571 -0
  71. sage/modular/dirichlet.py +3310 -0
  72. sage/modular/drinfeld_modform/all.py +2 -0
  73. sage/modular/drinfeld_modform/element.py +446 -0
  74. sage/modular/drinfeld_modform/ring.py +773 -0
  75. sage/modular/drinfeld_modform/tutorial.py +236 -0
  76. sage/modular/etaproducts.py +1076 -0
  77. sage/modular/hecke/algebra.py +725 -0
  78. sage/modular/hecke/all.py +19 -0
  79. sage/modular/hecke/ambient_module.py +994 -0
  80. sage/modular/hecke/degenmap.py +119 -0
  81. sage/modular/hecke/element.py +302 -0
  82. sage/modular/hecke/hecke_operator.py +736 -0
  83. sage/modular/hecke/homspace.py +185 -0
  84. sage/modular/hecke/module.py +1744 -0
  85. sage/modular/hecke/morphism.py +139 -0
  86. sage/modular/hecke/submodule.py +970 -0
  87. sage/modular/hypergeometric_misc.cpython-314t-darwin.so +0 -0
  88. sage/modular/hypergeometric_misc.pxd +4 -0
  89. sage/modular/hypergeometric_misc.pyx +166 -0
  90. sage/modular/hypergeometric_motive.py +2020 -0
  91. sage/modular/local_comp/all.py +2 -0
  92. sage/modular/local_comp/liftings.py +292 -0
  93. sage/modular/local_comp/local_comp.py +1070 -0
  94. sage/modular/local_comp/smoothchar.py +1825 -0
  95. sage/modular/local_comp/type_space.py +748 -0
  96. sage/modular/modform/all.py +30 -0
  97. sage/modular/modform/ambient.py +817 -0
  98. sage/modular/modform/ambient_R.py +177 -0
  99. sage/modular/modform/ambient_eps.py +306 -0
  100. sage/modular/modform/ambient_g0.py +120 -0
  101. sage/modular/modform/ambient_g1.py +199 -0
  102. sage/modular/modform/constructor.py +545 -0
  103. sage/modular/modform/cuspidal_submodule.py +708 -0
  104. sage/modular/modform/defaults.py +14 -0
  105. sage/modular/modform/eis_series.py +487 -0
  106. sage/modular/modform/eisenstein_submodule.py +663 -0
  107. sage/modular/modform/element.py +4105 -0
  108. sage/modular/modform/half_integral.py +154 -0
  109. sage/modular/modform/hecke_operator_on_qexp.py +247 -0
  110. sage/modular/modform/j_invariant.py +47 -0
  111. sage/modular/modform/l_series_gross_zagier.py +127 -0
  112. sage/modular/modform/l_series_gross_zagier_coeffs.cpython-314t-darwin.so +0 -0
  113. sage/modular/modform/l_series_gross_zagier_coeffs.pyx +177 -0
  114. sage/modular/modform/notes.py +45 -0
  115. sage/modular/modform/numerical.py +514 -0
  116. sage/modular/modform/periods.py +14 -0
  117. sage/modular/modform/ring.py +1257 -0
  118. sage/modular/modform/space.py +1859 -0
  119. sage/modular/modform/submodule.py +118 -0
  120. sage/modular/modform/tests.py +64 -0
  121. sage/modular/modform/theta.py +110 -0
  122. sage/modular/modform/vm_basis.py +380 -0
  123. sage/modular/modform/weight1.py +221 -0
  124. sage/modular/modform_hecketriangle/abstract_ring.py +1932 -0
  125. sage/modular/modform_hecketriangle/abstract_space.py +2527 -0
  126. sage/modular/modform_hecketriangle/all.py +30 -0
  127. sage/modular/modform_hecketriangle/analytic_type.py +590 -0
  128. sage/modular/modform_hecketriangle/constructor.py +416 -0
  129. sage/modular/modform_hecketriangle/element.py +351 -0
  130. sage/modular/modform_hecketriangle/functors.py +752 -0
  131. sage/modular/modform_hecketriangle/graded_ring.py +541 -0
  132. sage/modular/modform_hecketriangle/graded_ring_element.py +2225 -0
  133. sage/modular/modform_hecketriangle/hecke_triangle_group_element.py +3349 -0
  134. sage/modular/modform_hecketriangle/hecke_triangle_groups.py +1426 -0
  135. sage/modular/modform_hecketriangle/readme.py +1214 -0
  136. sage/modular/modform_hecketriangle/series_constructor.py +580 -0
  137. sage/modular/modform_hecketriangle/space.py +1037 -0
  138. sage/modular/modform_hecketriangle/subspace.py +423 -0
  139. sage/modular/modsym/all.py +17 -0
  140. sage/modular/modsym/ambient.py +3844 -0
  141. sage/modular/modsym/boundary.py +1420 -0
  142. sage/modular/modsym/element.py +336 -0
  143. sage/modular/modsym/g1list.py +178 -0
  144. sage/modular/modsym/ghlist.py +182 -0
  145. sage/modular/modsym/hecke_operator.py +73 -0
  146. sage/modular/modsym/manin_symbol.cpython-314t-darwin.so +0 -0
  147. sage/modular/modsym/manin_symbol.pxd +5 -0
  148. sage/modular/modsym/manin_symbol.pyx +497 -0
  149. sage/modular/modsym/manin_symbol_list.py +1291 -0
  150. sage/modular/modsym/modsym.py +400 -0
  151. sage/modular/modsym/modular_symbols.py +384 -0
  152. sage/modular/modsym/p1list_nf.py +1241 -0
  153. sage/modular/modsym/relation_matrix.py +591 -0
  154. sage/modular/modsym/relation_matrix_pyx.cpython-314t-darwin.so +0 -0
  155. sage/modular/modsym/relation_matrix_pyx.pyx +108 -0
  156. sage/modular/modsym/space.py +2468 -0
  157. sage/modular/modsym/subspace.py +455 -0
  158. sage/modular/modsym/tests.py +376 -0
  159. sage/modular/multiple_zeta.py +2635 -0
  160. sage/modular/multiple_zeta_F_algebra.py +789 -0
  161. sage/modular/overconvergent/all.py +6 -0
  162. sage/modular/overconvergent/genus0.py +1879 -0
  163. sage/modular/overconvergent/hecke_series.py +1187 -0
  164. sage/modular/overconvergent/weightspace.py +776 -0
  165. sage/modular/pollack_stevens/all.py +4 -0
  166. sage/modular/pollack_stevens/distributions.py +874 -0
  167. sage/modular/pollack_stevens/fund_domain.py +1572 -0
  168. sage/modular/pollack_stevens/manin_map.py +856 -0
  169. sage/modular/pollack_stevens/modsym.py +1590 -0
  170. sage/modular/pollack_stevens/padic_lseries.py +417 -0
  171. sage/modular/pollack_stevens/sigma0.py +534 -0
  172. sage/modular/pollack_stevens/space.py +1078 -0
  173. sage/modular/quasimodform/all.py +3 -0
  174. sage/modular/quasimodform/element.py +846 -0
  175. sage/modular/quasimodform/ring.py +826 -0
  176. sage/modular/quatalg/all.py +3 -0
  177. sage/modular/quatalg/brandt.py +1642 -0
  178. sage/modular/ssmod/all.py +8 -0
  179. sage/modular/ssmod/ssmod.py +827 -0
  180. sage/rings/all__sagemath_schemes.py +1 -0
  181. sage/rings/polynomial/all__sagemath_schemes.py +1 -0
  182. sage/rings/polynomial/binary_form_reduce.py +585 -0
  183. sage/schemes/all.py +41 -0
  184. sage/schemes/berkovich/all.py +6 -0
  185. sage/schemes/berkovich/berkovich_cp_element.py +2582 -0
  186. sage/schemes/berkovich/berkovich_space.py +700 -0
  187. sage/schemes/curves/affine_curve.py +2924 -0
  188. sage/schemes/curves/all.py +33 -0
  189. sage/schemes/curves/closed_point.py +434 -0
  190. sage/schemes/curves/constructor.py +397 -0
  191. sage/schemes/curves/curve.py +542 -0
  192. sage/schemes/curves/plane_curve_arrangement.py +1283 -0
  193. sage/schemes/curves/point.py +463 -0
  194. sage/schemes/curves/projective_curve.py +3203 -0
  195. sage/schemes/curves/weighted_projective_curve.py +106 -0
  196. sage/schemes/curves/zariski_vankampen.py +1931 -0
  197. sage/schemes/cyclic_covers/all.py +2 -0
  198. sage/schemes/cyclic_covers/charpoly_frobenius.py +320 -0
  199. sage/schemes/cyclic_covers/constructor.py +137 -0
  200. sage/schemes/cyclic_covers/cycliccover_finite_field.py +1309 -0
  201. sage/schemes/cyclic_covers/cycliccover_generic.py +310 -0
  202. sage/schemes/elliptic_curves/BSD.py +991 -0
  203. sage/schemes/elliptic_curves/Qcurves.py +592 -0
  204. sage/schemes/elliptic_curves/addition_formulas_ring.py +94 -0
  205. sage/schemes/elliptic_curves/all.py +49 -0
  206. sage/schemes/elliptic_curves/cardinality.py +609 -0
  207. sage/schemes/elliptic_curves/cm.py +1103 -0
  208. sage/schemes/elliptic_curves/constructor.py +1530 -0
  209. sage/schemes/elliptic_curves/ec_database.py +175 -0
  210. sage/schemes/elliptic_curves/ell_curve_isogeny.py +3971 -0
  211. sage/schemes/elliptic_curves/ell_egros.py +457 -0
  212. sage/schemes/elliptic_curves/ell_field.py +2837 -0
  213. sage/schemes/elliptic_curves/ell_finite_field.py +3249 -0
  214. sage/schemes/elliptic_curves/ell_generic.py +3760 -0
  215. sage/schemes/elliptic_curves/ell_local_data.py +1207 -0
  216. sage/schemes/elliptic_curves/ell_modular_symbols.py +775 -0
  217. sage/schemes/elliptic_curves/ell_number_field.py +4220 -0
  218. sage/schemes/elliptic_curves/ell_padic_field.py +107 -0
  219. sage/schemes/elliptic_curves/ell_point.py +4944 -0
  220. sage/schemes/elliptic_curves/ell_rational_field.py +7184 -0
  221. sage/schemes/elliptic_curves/ell_tate_curve.py +671 -0
  222. sage/schemes/elliptic_curves/ell_torsion.py +436 -0
  223. sage/schemes/elliptic_curves/ell_wp.py +352 -0
  224. sage/schemes/elliptic_curves/formal_group.py +760 -0
  225. sage/schemes/elliptic_curves/gal_reps.py +1459 -0
  226. sage/schemes/elliptic_curves/gal_reps_number_field.py +1663 -0
  227. sage/schemes/elliptic_curves/gp_simon.py +152 -0
  228. sage/schemes/elliptic_curves/heegner.py +7328 -0
  229. sage/schemes/elliptic_curves/height.py +2108 -0
  230. sage/schemes/elliptic_curves/hom.py +1788 -0
  231. sage/schemes/elliptic_curves/hom_composite.py +1084 -0
  232. sage/schemes/elliptic_curves/hom_fractional.py +544 -0
  233. sage/schemes/elliptic_curves/hom_frobenius.py +522 -0
  234. sage/schemes/elliptic_curves/hom_scalar.py +531 -0
  235. sage/schemes/elliptic_curves/hom_sum.py +681 -0
  236. sage/schemes/elliptic_curves/hom_velusqrt.py +1290 -0
  237. sage/schemes/elliptic_curves/homset.py +271 -0
  238. sage/schemes/elliptic_curves/isogeny_class.py +1523 -0
  239. sage/schemes/elliptic_curves/isogeny_small_degree.py +2797 -0
  240. sage/schemes/elliptic_curves/jacobian.py +247 -0
  241. sage/schemes/elliptic_curves/kodaira_symbol.py +344 -0
  242. sage/schemes/elliptic_curves/kraus.py +1014 -0
  243. sage/schemes/elliptic_curves/lseries_ell.py +915 -0
  244. sage/schemes/elliptic_curves/mod5family.py +105 -0
  245. sage/schemes/elliptic_curves/mod_poly.py +197 -0
  246. sage/schemes/elliptic_curves/mod_sym_num.cpython-314t-darwin.so +0 -0
  247. sage/schemes/elliptic_curves/mod_sym_num.pyx +3796 -0
  248. sage/schemes/elliptic_curves/modular_parametrization.py +305 -0
  249. sage/schemes/elliptic_curves/padic_lseries.py +1793 -0
  250. sage/schemes/elliptic_curves/padics.py +1816 -0
  251. sage/schemes/elliptic_curves/period_lattice.py +2234 -0
  252. sage/schemes/elliptic_curves/period_lattice_region.cpython-314t-darwin.so +0 -0
  253. sage/schemes/elliptic_curves/period_lattice_region.pyx +722 -0
  254. sage/schemes/elliptic_curves/saturation.py +716 -0
  255. sage/schemes/elliptic_curves/sha_tate.py +1158 -0
  256. sage/schemes/elliptic_curves/weierstrass_morphism.py +1117 -0
  257. sage/schemes/elliptic_curves/weierstrass_transform.py +200 -0
  258. sage/schemes/hyperelliptic_curves/all.py +6 -0
  259. sage/schemes/hyperelliptic_curves/constructor.py +369 -0
  260. sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py +1948 -0
  261. sage/schemes/hyperelliptic_curves/hyperelliptic_g2.py +192 -0
  262. sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py +936 -0
  263. sage/schemes/hyperelliptic_curves/hyperelliptic_padic_field.py +1332 -0
  264. sage/schemes/hyperelliptic_curves/hyperelliptic_rational_field.py +84 -0
  265. sage/schemes/hyperelliptic_curves/invariants.py +410 -0
  266. sage/schemes/hyperelliptic_curves/jacobian_endomorphism_utils.py +312 -0
  267. sage/schemes/hyperelliptic_curves/jacobian_g2.py +32 -0
  268. sage/schemes/hyperelliptic_curves/jacobian_generic.py +437 -0
  269. sage/schemes/hyperelliptic_curves/jacobian_homset.py +186 -0
  270. sage/schemes/hyperelliptic_curves/jacobian_morphism.py +878 -0
  271. sage/schemes/hyperelliptic_curves/kummer_surface.py +99 -0
  272. sage/schemes/hyperelliptic_curves/mestre.py +302 -0
  273. sage/schemes/hyperelliptic_curves/monsky_washnitzer.py +3863 -0
  274. sage/schemes/jacobians/abstract_jacobian.py +277 -0
  275. sage/schemes/jacobians/all.py +2 -0
  276. sage/schemes/overview.py +161 -0
  277. sage/schemes/plane_conics/all.py +22 -0
  278. sage/schemes/plane_conics/con_field.py +1296 -0
  279. sage/schemes/plane_conics/con_finite_field.py +158 -0
  280. sage/schemes/plane_conics/con_number_field.py +456 -0
  281. sage/schemes/plane_conics/con_rational_field.py +406 -0
  282. sage/schemes/plane_conics/con_rational_function_field.py +581 -0
  283. sage/schemes/plane_conics/constructor.py +249 -0
  284. sage/schemes/plane_quartics/all.py +2 -0
  285. sage/schemes/plane_quartics/quartic_constructor.py +71 -0
  286. sage/schemes/plane_quartics/quartic_generic.py +53 -0
  287. sage/schemes/riemann_surfaces/all.py +1 -0
  288. sage/schemes/riemann_surfaces/riemann_surface.py +4177 -0
  289. sage_wheels/share/cremona/cremona_mini.db +0 -0
  290. sage_wheels/share/ellcurves/rank0 +30427 -0
  291. sage_wheels/share/ellcurves/rank1 +31871 -0
  292. sage_wheels/share/ellcurves/rank10 +6 -0
  293. sage_wheels/share/ellcurves/rank11 +6 -0
  294. sage_wheels/share/ellcurves/rank12 +1 -0
  295. sage_wheels/share/ellcurves/rank14 +1 -0
  296. sage_wheels/share/ellcurves/rank15 +1 -0
  297. sage_wheels/share/ellcurves/rank17 +1 -0
  298. sage_wheels/share/ellcurves/rank19 +1 -0
  299. sage_wheels/share/ellcurves/rank2 +2388 -0
  300. sage_wheels/share/ellcurves/rank20 +1 -0
  301. sage_wheels/share/ellcurves/rank21 +1 -0
  302. sage_wheels/share/ellcurves/rank22 +1 -0
  303. sage_wheels/share/ellcurves/rank23 +1 -0
  304. sage_wheels/share/ellcurves/rank24 +1 -0
  305. sage_wheels/share/ellcurves/rank28 +1 -0
  306. sage_wheels/share/ellcurves/rank3 +836 -0
  307. sage_wheels/share/ellcurves/rank4 +10 -0
  308. sage_wheels/share/ellcurves/rank5 +5 -0
  309. sage_wheels/share/ellcurves/rank6 +5 -0
  310. sage_wheels/share/ellcurves/rank7 +5 -0
  311. sage_wheels/share/ellcurves/rank8 +6 -0
  312. sage_wheels/share/ellcurves/rank9 +7 -0
@@ -0,0 +1,1788 @@
1
+ # sage_setup: distribution = sagemath-schemes
2
+ """
3
+ Elliptic-curve morphisms
4
+
5
+ This class serves as a common parent for various specializations of
6
+ morphisms between elliptic curves, with the aim of providing a common
7
+ interface regardless of implementation details.
8
+
9
+ Current implementations of elliptic-curve morphisms (child classes):
10
+
11
+ - :class:`~sage.schemes.elliptic_curves.ell_curve_isogeny.EllipticCurveIsogeny`
12
+ - :class:`~sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism`
13
+ - :class:`~sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite`
14
+ - :class:`~sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_sum`
15
+ - :class:`~sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar`
16
+ - :class:`~sage.schemes.elliptic_curves.hom_frobenius.EllipticCurveHom_frobenius`
17
+ - :class:`~sage.schemes.elliptic_curves.hom_velusqrt.EllipticCurveHom_velusqrt`
18
+ - :class:`~sage.schemes.elliptic_curves.hom_fractional.EllipticCurveHom_fractional`
19
+
20
+ AUTHORS:
21
+
22
+ - See authors of :class:`EllipticCurveIsogeny`. Some of the code
23
+ in this class was lifted from there.
24
+
25
+ - Lorenz Panny (2021): Refactor isogenies and isomorphisms into
26
+ the common :class:`EllipticCurveHom` interface.
27
+
28
+ - Lorenz Panny (2022): :meth:`~EllipticCurveHom.matrix_on_subgroup`
29
+
30
+ - Lorenz Panny (2023): :meth:`~EllipticCurveHom.trace`, :meth:`~EllipticCurveHom.characteristic_polynomial`
31
+ """
32
+ from sage.misc.cachefunc import cached_method
33
+ from sage.structure.richcmp import richcmp_not_equal, richcmp, op_EQ, op_NE
34
+
35
+ from sage.categories.morphism import Morphism
36
+
37
+ from sage.arith.misc import integer_floor
38
+
39
+ from sage.rings.integer_ring import ZZ
40
+ from sage.rings.finite_rings import finite_field_base
41
+ from sage.rings.number_field import number_field_base
42
+
43
+ import sage.schemes.elliptic_curves.weierstrass_morphism as wm
44
+
45
+
46
+ class EllipticCurveHom(Morphism):
47
+ """
48
+ Base class for elliptic-curve morphisms.
49
+ """
50
+ def __init__(self, *args, **kwds):
51
+ r"""
52
+ Constructor for elliptic-curve morphisms.
53
+
54
+ EXAMPLES::
55
+
56
+ sage: E = EllipticCurve(GF(257^2), [5,5])
57
+ sage: P = E.lift_x(1)
58
+ sage: E.isogeny(P) # indirect doctest
59
+ Isogeny of degree 127 from Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Finite Field in z2 of size 257^2 to Elliptic Curve defined by y^2 = x^3 + 151*x + 22 over Finite Field in z2 of size 257^2
60
+ sage: E.isogeny(P, algorithm='factored') # indirect doctest
61
+ Composite morphism of degree 127:
62
+ From: Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Finite Field in z2 of size 257^2
63
+ To: Elliptic Curve defined by y^2 = x^3 + 151*x + 22 over Finite Field in z2 of size 257^2
64
+ sage: E.isogeny(P, algorithm='velusqrt') # indirect doctest
65
+ Elliptic-curve isogeny (using square-root Vélu) of degree 127:
66
+ From: Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Finite Field in z2 of size 257^2
67
+ To: Elliptic Curve defined by y^2 = x^3 + 119*x + 231 over Finite Field in z2 of size 257^2
68
+ sage: E.montgomery_model(morphism=True) # indirect doctest
69
+ (Elliptic Curve defined by y^2 = x^3 + (199*z2+73)*x^2 + x over Finite Field in z2 of size 257^2,
70
+ Elliptic-curve morphism:
71
+ From: Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Finite Field in z2 of size 257^2
72
+ To: Elliptic Curve defined by y^2 = x^3 + (199*z2+73)*x^2 + x over Finite Field in z2 of size 257^2
73
+ Via: (u,r,s,t) = (88*z2 + 253, 208*z2 + 90, 0, 0))
74
+ """
75
+ super().__init__(*args, **kwds)
76
+
77
+ # Over finite fields, isogenous curves have the same number of
78
+ # rational points, hence we copy over the cached curve orders.
79
+ if isinstance(self.base_ring(), finite_field_base.FiniteField) and self.degree():
80
+ self._codomain._fetch_cached_order(self._domain)
81
+ self._domain._fetch_cached_order(self._codomain)
82
+
83
+ def _repr_type(self):
84
+ r"""
85
+ Return a textual representation of what kind of morphism
86
+ this is. Used by :meth:`Morphism._repr_`.
87
+
88
+ TESTS::
89
+
90
+ sage: from sage.schemes.elliptic_curves.hom import EllipticCurveHom
91
+ sage: EllipticCurveHom._repr_type(None)
92
+ 'Elliptic-curve'
93
+ """
94
+ return 'Elliptic-curve'
95
+
96
+ @staticmethod
97
+ def _composition_impl(left, right):
98
+ r"""
99
+ Called by :meth:`_composition_`.
100
+
101
+ TESTS::
102
+
103
+ sage: from sage.schemes.elliptic_curves.hom import EllipticCurveHom
104
+ sage: EllipticCurveHom._composition_impl(None, None)
105
+ NotImplemented
106
+ """
107
+ return NotImplemented
108
+
109
+ def _composition_(self, other, homset):
110
+ r"""
111
+ Return the composition of this elliptic-curve morphism
112
+ with another elliptic-curve morphism.
113
+
114
+ EXAMPLES::
115
+
116
+ sage: E = EllipticCurve(GF(19), [1,0])
117
+ sage: phi = E.isogeny(E(0,0))
118
+ sage: iso = E.change_weierstrass_model(5,0,0,0).isomorphism_to(E)
119
+ sage: phi * iso
120
+ Isogeny of degree 2 from Elliptic Curve defined by y^2 = x^3 + 9*x over Finite Field of size 19 to Elliptic Curve defined by y^2 = x^3 + 15*x over Finite Field of size 19
121
+ sage: phi.dual() * phi
122
+ Composite morphism of degree 4 = 2^2:
123
+ From: Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 19
124
+ To: Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 19
125
+ """
126
+ if not isinstance(self, EllipticCurveHom) or not isinstance(other, EllipticCurveHom):
127
+ raise TypeError(f'cannot compose {type(self)} with {type(other)}')
128
+
129
+ ret = self._composition_impl(self, other)
130
+
131
+ if ret is NotImplemented:
132
+ ret = other._composition_impl(self, other)
133
+
134
+ if ret is NotImplemented:
135
+ from sage.schemes.elliptic_curves.hom_composite import EllipticCurveHom_composite
136
+ ret = EllipticCurveHom_composite.from_factors([other, self])
137
+
138
+ return ret
139
+
140
+ def _add_(self, other):
141
+ r"""
142
+ Add two :class:`EllipticCurveHom` objects by constructing a
143
+ formal :class:`EllipticCurveHom_sum`.
144
+
145
+ EXAMPLES::
146
+
147
+ sage: E = EllipticCurve(GF(101), [5,5])
148
+ sage: phi = E.isogenies_prime_degree(7)[0]
149
+ sage: phi + phi # indirect doctest
150
+ Sum morphism:
151
+ From: Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Finite Field of size 101
152
+ To: Elliptic Curve defined by y^2 = x^3 + 12*x + 98 over Finite Field of size 101
153
+ Via: (Isogeny of degree 7 from Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Finite Field of size 101 to Elliptic Curve defined by y^2 = x^3 + 12*x + 98 over Finite Field of size 101, Isogeny of degree 7 from Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Finite Field of size 101 to Elliptic Curve defined by y^2 = x^3 + 12*x + 98 over Finite Field of size 101)
154
+ """
155
+ from sage.schemes.elliptic_curves.hom_sum import EllipticCurveHom_sum
156
+ phis = []
157
+ if isinstance(self, EllipticCurveHom_sum):
158
+ phis += self.summands()
159
+ else:
160
+ phis.append(self)
161
+ if isinstance(other, EllipticCurveHom_sum):
162
+ phis += other.summands()
163
+ else:
164
+ phis.append(other)
165
+
166
+ #TODO should probably try to simplify some more?
167
+
168
+ assert other.domain() == self.domain() and other.codomain() == self.codomain()
169
+ return EllipticCurveHom_sum(phis, domain=self.domain(), codomain=self.codomain())
170
+
171
+ def _sub_(self, other):
172
+ r"""
173
+ Subtract two :class:`EllipticCurveHom` objects by negating
174
+ and constructing a formal :class:`EllipticCurveHom_sum`.
175
+
176
+ EXAMPLES::
177
+
178
+ sage: E = EllipticCurve(GF(101), [5,5])
179
+ sage: phi = E.isogenies_prime_degree(7)[0]
180
+ sage: phi - phi # indirect doctest
181
+ Sum morphism:
182
+ From: Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Finite Field of size 101
183
+ To: Elliptic Curve defined by y^2 = x^3 + 12*x + 98 over Finite Field of size 101
184
+ Via: (Isogeny of degree 7 from Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Finite Field of size 101 to Elliptic Curve defined by y^2 = x^3 + 12*x + 98 over Finite Field of size 101, Isogeny of degree 7 from Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Finite Field of size 101 to Elliptic Curve defined by y^2 = x^3 + 12*x + 98 over Finite Field of size 101)
185
+ """
186
+ return self + (-other)
187
+
188
+ @staticmethod
189
+ def _comparison_impl(left, right, op):
190
+ r"""
191
+ Called by :meth:`_richcmp_`.
192
+
193
+ TESTS::
194
+
195
+ sage: from sage.schemes.elliptic_curves.hom import EllipticCurveHom
196
+ sage: EllipticCurveHom._comparison_impl(None, None, None)
197
+ NotImplemented
198
+ """
199
+ return NotImplemented
200
+
201
+ def _richcmp_(self, other, op):
202
+ r"""
203
+ Compare :class:`EllipticCurveHom` objects.
204
+
205
+ ALGORITHM:
206
+
207
+ The method first makes sure that domain, codomain and degree match.
208
+ Then, it determines if there is a specialized comparison method by
209
+ trying :meth:`_comparison_impl` on either input. If not, it falls
210
+ back to comparing :meth:`rational_maps`.
211
+
212
+ EXAMPLES::
213
+
214
+ sage: E = EllipticCurve(QQ, [0,0,0,1,0])
215
+ sage: phi_v = EllipticCurveIsogeny(E, E((0,0)))
216
+ sage: phi_k = EllipticCurveIsogeny(E, [0,1])
217
+ sage: phi_k == phi_v
218
+ True
219
+ sage: E_F17 = EllipticCurve(GF(17), [0,0,0,1,0])
220
+ sage: phi_p = EllipticCurveIsogeny(E_F17, [0,1])
221
+ sage: phi_p == phi_v
222
+ False
223
+ sage: E = EllipticCurve('11a1')
224
+ sage: phi = E.isogeny(E(5,5))
225
+ sage: phi == phi
226
+ True
227
+ sage: phi == -phi
228
+ False
229
+ sage: psi = E.isogeny(phi.kernel_polynomial())
230
+ sage: phi == psi
231
+ True
232
+ sage: phi.dual() == psi.dual()
233
+ True
234
+
235
+ ::
236
+
237
+ sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism, identity_morphism
238
+ sage: E = EllipticCurve([9,9])
239
+ sage: F = E.change_ring(GF(71))
240
+ sage: wE = identity_morphism(E)
241
+ sage: wF = identity_morphism(F)
242
+ sage: mE = E.scalar_multiplication(1)
243
+ sage: mE == wE
244
+ True
245
+ sage: [a == wF for a in (wE,mE)]
246
+ [False, False]
247
+
248
+ .. SEEALSO::
249
+
250
+ - :meth:`_comparison_impl`
251
+ - :func:`compare_via_evaluation`
252
+ """
253
+ if not isinstance(self, EllipticCurveHom) or not isinstance(other, EllipticCurveHom):
254
+ raise TypeError(f'cannot compare {type(self)} to {type(other)}')
255
+
256
+ if op == op_NE:
257
+ return not self._richcmp_(other, op_EQ)
258
+
259
+ # We first compare domain, codomain, and degree; cf. Issue #11327
260
+
261
+ lx, rx = self.domain(), other.domain()
262
+ if lx != rx:
263
+ return richcmp_not_equal(lx, rx, op)
264
+
265
+ lx, rx = self.codomain(), other.codomain()
266
+ if lx != rx:
267
+ return richcmp_not_equal(lx, rx, op)
268
+
269
+ lx, rx = self.degree(), other.degree()
270
+ if lx != rx:
271
+ return richcmp_not_equal(lx, rx, op)
272
+
273
+ # Check the Weierstraß scaling factor, too (should be fast)
274
+
275
+ if op == op_EQ or op == op_NE:
276
+ lx, rx = self.scaling_factor(), other.scaling_factor()
277
+ if lx != rx:
278
+ return richcmp_not_equal(lx, rx, op)
279
+
280
+ # Do self or other have specialized comparison methods?
281
+
282
+ ret = self._comparison_impl(self, other, op)
283
+ if ret is not NotImplemented:
284
+ return ret
285
+
286
+ ret = other._comparison_impl(self, other, op)
287
+ if ret is not NotImplemented:
288
+ return ret
289
+
290
+ # If not, fall back to comparing rational maps; cf. Issue #11327
291
+
292
+ return richcmp(self.rational_maps(), other.rational_maps(), op)
293
+
294
+ def degree(self):
295
+ r"""
296
+ Return the degree of this elliptic-curve morphism.
297
+
298
+ EXAMPLES::
299
+
300
+ sage: E = EllipticCurve(QQ, [0,0,0,1,0])
301
+ sage: phi = EllipticCurveIsogeny(E, E((0,0)))
302
+ sage: phi.degree()
303
+ 2
304
+ sage: phi = EllipticCurveIsogeny(E, [0,1,0,1])
305
+ sage: phi.degree()
306
+ 4
307
+
308
+ sage: E = EllipticCurve(GF(31), [1,0,0,1,2])
309
+ sage: phi = EllipticCurveIsogeny(E, [17, 1])
310
+ sage: phi.degree()
311
+ 3
312
+
313
+ Degrees are multiplicative, so the degree of a composite isogeny
314
+ is the product of the degrees of the individual factors::
315
+
316
+ sage: from sage.schemes.elliptic_curves.hom_composite import EllipticCurveHom_composite
317
+ sage: E = EllipticCurve(GF(419), [1,0])
318
+ sage: P, = E.gens()
319
+ sage: phi = EllipticCurveHom_composite(E, P+P)
320
+ sage: phi.degree()
321
+ 210
322
+ sage: phi.degree() == prod(f.degree() for f in phi.factors())
323
+ True
324
+
325
+ Isomorphisms always have degree `1` by definition::
326
+
327
+ sage: E1 = EllipticCurve([1,2,3,4,5])
328
+ sage: E2 = EllipticCurve_from_j(E1.j_invariant())
329
+ sage: E1.isomorphism_to(E2).degree()
330
+ 1
331
+
332
+ TESTS::
333
+
334
+ sage: from sage.schemes.elliptic_curves.hom import EllipticCurveHom
335
+ sage: EllipticCurveHom.degree(None)
336
+ Traceback (most recent call last):
337
+ ...
338
+ NotImplementedError: ...
339
+ """
340
+ try:
341
+ return self._degree
342
+ except AttributeError:
343
+ raise NotImplementedError('children must implement')
344
+
345
+ @cached_method
346
+ def trace(self):
347
+ r"""
348
+ Return the trace of this elliptic-curve morphism, which must
349
+ be an endomorphism.
350
+
351
+ ALGORITHM: :func:`compute_trace_generic`
352
+
353
+ EXAMPLES::
354
+
355
+ sage: E = EllipticCurve(QQ, [42, 42])
356
+ sage: m5 = E.scalar_multiplication(5)
357
+ sage: m5.trace()
358
+ 10
359
+
360
+ ::
361
+
362
+ sage: E = EllipticCurve(GF(71^2), [45, 45])
363
+ sage: P = E.lift_x(27)
364
+ sage: P.order()
365
+ 71
366
+ sage: tau = E.isogeny(P, codomain=E)
367
+ sage: tau.trace()
368
+ -1
369
+
370
+ TESTS:
371
+
372
+ Make sure the cached value of the trace is not accidentally
373
+ copied on composition with automorphisms::
374
+
375
+ sage: aut = E.automorphisms()[1] # [-1]
376
+ sage: (aut * tau).trace()
377
+ 1
378
+
379
+ It also works for more complicated :class:`EllipticCurveHom`
380
+ children::
381
+
382
+ sage: tau = E.isogeny(P, codomain=E, algorithm='velusqrt')
383
+ sage: tau.trace()
384
+ -1
385
+
386
+ Check that negation commutes with taking the trace::
387
+
388
+ sage: (-tau).trace()
389
+ 1
390
+
391
+ The trace is only defined for endomorphisms. If this method is called
392
+ on an isogeny that is not an endomorphism a ``ValueError`` will be raised.
393
+ The elliptic curve below does not have CM and the isogeny phi is of
394
+ degree 2, hence it is not an endomorphism::
395
+
396
+ sage: E = EllipticCurve([17,42])
397
+ sage: phi = E.isogenies_prime_degree()[0]
398
+ sage: phi.trace()
399
+ Traceback (most recent call last):
400
+ ...
401
+ ValueError: trace only makes sense for endomorphisms
402
+
403
+ """
404
+ F = self.domain().base_field()
405
+ if F.characteristic().is_zero():
406
+ if self.domain() != self.codomain():
407
+ raise ValueError('trace only makes sense for endomorphisms')
408
+ d = self.degree()
409
+ s = self.scaling_factor()
410
+ return ZZ(s + d/s)
411
+ return compute_trace_generic(self)
412
+
413
+ def characteristic_polynomial(self):
414
+ r"""
415
+ Return the characteristic polynomial of this elliptic-curve
416
+ morphism, which must be an endomorphism.
417
+
418
+ .. SEEALSO::
419
+
420
+ - :meth:`degree`
421
+ - :meth:`trace`
422
+
423
+ EXAMPLES::
424
+
425
+ sage: E = EllipticCurve(QQ, [42, 42])
426
+ sage: m5 = E.scalar_multiplication(5)
427
+ sage: m5.characteristic_polynomial()
428
+ x^2 - 10*x + 25
429
+
430
+ ::
431
+
432
+ sage: E = EllipticCurve(GF(71), [42, 42])
433
+ sage: pi = E.frobenius_endomorphism()
434
+ sage: pi.characteristic_polynomial()
435
+ x^2 - 8*x + 71
436
+ sage: E.frobenius().charpoly()
437
+ x^2 - 8*x + 71
438
+
439
+ TESTS::
440
+
441
+ sage: m5.characteristic_polynomial().parent()
442
+ Univariate Polynomial Ring in x over Integer Ring
443
+ sage: pi.characteristic_polynomial().parent()
444
+ Univariate Polynomial Ring in x over Integer Ring
445
+ """
446
+ R = ZZ['x']
447
+ return R([self.degree(), -self.trace(), 1])
448
+
449
+ def kernel_polynomial(self):
450
+ r"""
451
+ Return the kernel polynomial of this elliptic-curve morphism.
452
+
453
+ Implemented by child classes. For examples, see:
454
+
455
+ - :meth:`EllipticCurveIsogeny.kernel_polynomial`
456
+ - :meth:`sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism.kernel_polynomial`
457
+ - :meth:`sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite.kernel_polynomial`
458
+ - :meth:`sage.schemes.elliptic_curves.hom_sum.EllipticCurveHom_sum.kernel_polynomial`
459
+ - :meth:`sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.kernel_polynomial`
460
+ - :meth:`sage.schemes.elliptic_curves.hom_frobenius.EllipticCurveHom_frobenius.kernel_polynomial`
461
+ - :meth:`sage.schemes.elliptic_curves.hom_velusqrt.EllipticCurveHom_velusqrt.kernel_polynomial`
462
+ - :meth:`sage.schemes.elliptic_curves.hom_fractional.EllipticCurveHom_fractional.kernel_polynomial`
463
+
464
+ TESTS::
465
+
466
+ sage: from sage.schemes.elliptic_curves.hom import EllipticCurveHom
467
+ sage: EllipticCurveHom.kernel_polynomial(None)
468
+ Traceback (most recent call last):
469
+ ...
470
+ NotImplementedError: ...
471
+ """
472
+ raise NotImplementedError('children must implement')
473
+
474
+ def kernel_points(self):
475
+ """
476
+ Return an iterator over the points in the kernel of this
477
+ elliptic-curve morphism.
478
+
479
+ EXAMPLES::
480
+
481
+ sage: E.<P, Q> = EllipticCurve(GF(5^2), [1, 2, 3, 3, 1])
482
+ sage: f = E.isogeny([P*3, Q*3])
483
+ sage: set(f.kernel_points())
484
+ {(0 : 1 : 0), (4 : 4 : 1), (2*z2 + 4 : 4*z2 + 4 : 1), (3*z2 + 1 : z2 + 3 : 1)}
485
+
486
+ In the inseparable case::
487
+
488
+ sage: E = EllipticCurve(GF(23), [1,1])
489
+ sage: set(E.scalar_multiplication(23).kernel_points())
490
+ {(0 : 1 : 0)}
491
+
492
+ Check that the result is consistent with
493
+ :meth:`~sage.schemes.elliptic_curves.ell_point.EllipticCurvePoint_field.division_points`::
494
+
495
+ sage: set(E.scalar_multiplication(4).kernel_points()) == set(E(0).division_points(4))
496
+ True
497
+ """
498
+ E = self.domain()
499
+ yield E.zero()
500
+ for x in self.kernel_polynomial().roots(multiplicities=False):
501
+ yield from E.lift_x(x, all=True)
502
+
503
+ def dual(self):
504
+ r"""
505
+ Return the dual of this elliptic-curve morphism.
506
+
507
+ Implemented by child classes. For examples, see:
508
+
509
+ - :meth:`EllipticCurveIsogeny.dual`
510
+ - :meth:`sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism.dual`
511
+ - :meth:`sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite.dual`
512
+ - :meth:`sage.schemes.elliptic_curves.hom_sum.EllipticCurveHom_sum.dual`
513
+ - :meth:`sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.dual`
514
+ - :meth:`sage.schemes.elliptic_curves.hom_frobenius.EllipticCurveHom_frobenius.dual`
515
+ - :meth:`sage.schemes.elliptic_curves.hom_velusqrt.EllipticCurveHom_velusqrt.dual`
516
+ - :meth:`sage.schemes.elliptic_curves.hom_fractional.EllipticCurveHom_fractional.dual`
517
+
518
+ TESTS::
519
+
520
+ sage: from sage.schemes.elliptic_curves.hom import EllipticCurveHom
521
+ sage: EllipticCurveHom.dual(None)
522
+ Traceback (most recent call last):
523
+ ...
524
+ NotImplementedError: ...
525
+ """
526
+ raise NotImplementedError('children must implement')
527
+
528
+ def rational_maps(self):
529
+ r"""
530
+ Return the pair of explicit rational maps defining this
531
+ elliptic-curve morphism as fractions of bivariate
532
+ polynomials in `x` and `y`.
533
+
534
+ Implemented by child classes. For examples, see:
535
+
536
+ - :meth:`EllipticCurveIsogeny.rational_maps`
537
+ - :meth:`sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism.rational_maps`
538
+ - :meth:`sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite.rational_maps`
539
+ - :meth:`sage.schemes.elliptic_curves.hom_sum.EllipticCurveHom_sum.rational_maps`
540
+ - :meth:`sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.rational_maps`
541
+ - :meth:`sage.schemes.elliptic_curves.hom_frobenius.EllipticCurveHom_frobenius.rational_maps`
542
+ - :meth:`sage.schemes.elliptic_curves.hom_velusqrt.EllipticCurveHom_velusqrt.rational_maps`
543
+ - :meth:`sage.schemes.elliptic_curves.hom_fractional.EllipticCurveHom_fractional.rational_maps`
544
+
545
+ TESTS::
546
+
547
+ sage: from sage.schemes.elliptic_curves.hom import EllipticCurveHom
548
+ sage: EllipticCurveHom.rational_maps(None)
549
+ Traceback (most recent call last):
550
+ ...
551
+ NotImplementedError: ...
552
+ """
553
+ raise NotImplementedError('children must implement')
554
+
555
+ def x_rational_map(self):
556
+ r"""
557
+ Return the `x`-coordinate rational map of this elliptic-curve
558
+ morphism as a univariate rational expression in `x`.
559
+
560
+ Implemented by child classes. For examples, see:
561
+
562
+ - :meth:`EllipticCurveIsogeny.x_rational_map`
563
+ - :meth:`sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism.x_rational_map`
564
+ - :meth:`sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite.x_rational_map`
565
+ - :meth:`sage.schemes.elliptic_curves.hom_sum.EllipticCurveHom_sum.x_rational_map`
566
+ - :meth:`sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.x_rational_map`
567
+ - :meth:`sage.schemes.elliptic_curves.hom_frobenius.EllipticCurveHom_frobenius.x_rational_map`
568
+ - :meth:`sage.schemes.elliptic_curves.hom_velusqrt.EllipticCurveHom_velusqrt.x_rational_map`
569
+ - :meth:`sage.schemes.elliptic_curves.hom_fractional.EllipticCurveHom_fractional.x_rational_map`
570
+
571
+ TESTS::
572
+
573
+ sage: from sage.schemes.elliptic_curves.hom import EllipticCurveHom
574
+ sage: EllipticCurveHom.x_rational_map(None)
575
+ Traceback (most recent call last):
576
+ ...
577
+ NotImplementedError: ...
578
+ """
579
+ # TODO: could have a default implementation that simply
580
+ # returns the first component of rational_maps()
581
+ raise NotImplementedError('children must implement')
582
+
583
+ def inverse_image(self, Q, /, *, all=False):
584
+ """
585
+ Return an arbitrary element ``P`` in the domain such that
586
+ ``self(P) == Q``, or raise ``ValueError`` if no such
587
+ element exists.
588
+
589
+ INPUT:
590
+
591
+ - ``Q`` -- a point
592
+ - ``all`` -- if true, returns an iterator over all points
593
+ in the inverse image
594
+
595
+ EXAMPLES::
596
+
597
+ sage: E.<P, Q> = EllipticCurve(GF(5^2), [1, 2, 3, 3, 1])
598
+ sage: f = E.isogeny([P*3])
599
+ sage: f(f.inverse_image(f(Q))) == f(Q)
600
+ True
601
+ sage: E.scalar_multiplication(-1).inverse_image(P) == -P
602
+ True
603
+ sage: f.inverse_image(f.codomain().0)
604
+ Traceback (most recent call last):
605
+ ...
606
+ ValueError: ...
607
+ sage: len(list(f.inverse_image(f(Q), all=True)))
608
+ 2
609
+
610
+ Check that the result is consistent with
611
+ :meth:`~sage.schemes.elliptic_curves.ell_point.EllipticCurvePoint_field.division_points`::
612
+
613
+ sage: E = EllipticCurve('37a'); E
614
+ Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field
615
+ sage: P = E(0, -1)
616
+ sage: (P * 5).division_points(5)
617
+ [(0 : -1 : 1)]
618
+ sage: E.scalar_multiplication(5).inverse_image(P * 5)
619
+ (0 : -1 : 1)
620
+
621
+ Points from wrong curves cannot be passed in::
622
+
623
+ sage: f.inverse_image(Q)
624
+ Traceback (most recent call last):
625
+ ...
626
+ TypeError: input must be a point in the codomain
627
+
628
+ TESTS::
629
+
630
+ sage: f.inverse_image(E.zero())
631
+ Traceback (most recent call last):
632
+ ...
633
+ TypeError: input must be a point in the codomain
634
+ sage: f.inverse_image(f.codomain().zero())
635
+ (0 : 1 : 0)
636
+
637
+ You can give a tuple as input::
638
+
639
+ sage: f.inverse_image((0, 2)) # random
640
+ (2 : 3*z2 + 1 : 1)
641
+ sage: f(f.inverse_image((0, 2)))
642
+ (0 : 2 : 1)
643
+
644
+ Stress test::
645
+
646
+ sage: # long time
647
+ ....: for p in primes(2, 12):
648
+ ....: for a in range(p):
649
+ ....: for b in range(p):
650
+ ....: try: E = EllipticCurve(GF(p), [a, b]); P = E.0
651
+ ....: except: continue # maybe singular curve or E.0 doesn't exist
652
+ ....: for n in P.order().divisors():
653
+ ....: f = E.isogeny(P*n)
654
+ ....: for R in E:
655
+ ....: Q = f(R)
656
+ ....: assert f(f.inverse_image(Q)) == Q
657
+ ....: for Q in f.codomain():
658
+ ....: try:
659
+ ....: ignore = f.inverse_image(Q)
660
+ ....: except ValueError: # no inverse image found
661
+ ....: continue
662
+ """
663
+ if Q not in self.codomain():
664
+ raise TypeError('input must be a point in the codomain')
665
+ Q = self.codomain()(Q)
666
+ if Q.is_zero():
667
+ if all:
668
+ return self.kernel_points()
669
+ else:
670
+ return Q
671
+ if all:
672
+ try:
673
+ P = self.inverse_image(Q)
674
+ except ValueError:
675
+ return ()
676
+ return (K + P for K in self.kernel_points())
677
+ if not self.base_ring().is_exact():
678
+ from warnings import warn
679
+ warn('computing inverse image over inexact base ring is not guaranteed to be correct')
680
+ E = self.domain()
681
+ for Px in (self.x_rational_map() - Q.x()).numerator().roots(multiplicities=False):
682
+ for P in E.lift_x(Px, all=True):
683
+ if self(P) == Q:
684
+ return P
685
+ if self.base_ring().is_exact():
686
+ raise ValueError('no inverse image found')
687
+ else:
688
+ raise NotImplementedError
689
+
690
+ def scaling_factor(self):
691
+ r"""
692
+ Return the Weierstrass scaling factor associated to this
693
+ elliptic-curve morphism.
694
+
695
+ The scaling factor is the constant `u` (in the base field)
696
+ such that `\varphi^* \omega_2 = u \omega_1`, where
697
+ `\varphi: E_1\to E_2` is this morphism and `\omega_i` are
698
+ the standard Weierstrass differentials on `E_i` defined by
699
+ `\mathrm dx/(2y+a_1x+a_3)`.
700
+
701
+ Implemented by child classes. For examples, see:
702
+
703
+ - :meth:`EllipticCurveIsogeny.scaling_factor`
704
+ - :meth:`sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism.scaling_factor`
705
+ - :meth:`sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite.scaling_factor`
706
+ - :meth:`sage.schemes.elliptic_curves.hom_sum.EllipticCurveHom_sum.scaling_factor`
707
+ - :meth:`sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.scaling_factor`
708
+ - :meth:`sage.schemes.elliptic_curves.hom_velusqrt.EllipticCurveHom_velusqrt.scaling_factor`
709
+ - :meth:`sage.schemes.elliptic_curves.hom_fractional.EllipticCurveHom_fractional.scaling_factor`
710
+
711
+ TESTS::
712
+
713
+ sage: from sage.schemes.elliptic_curves.hom import EllipticCurveHom
714
+ sage: EllipticCurveHom.scaling_factor(None)
715
+ Traceback (most recent call last):
716
+ ...
717
+ NotImplementedError: ...
718
+ """
719
+ # TODO: could have a default implementation that simply
720
+ # returns .formal()[1], but it seems safer to fail
721
+ # visibly to make sure we would notice regressions
722
+ raise NotImplementedError('children must implement')
723
+
724
+ def formal(self, prec=20):
725
+ r"""
726
+ Return the formal isogeny associated to this elliptic-curve
727
+ morphism as a power series in the variable `t=-x/y` on the
728
+ domain curve.
729
+
730
+ INPUT:
731
+
732
+ - ``prec`` -- (default: 20) the precision with which the
733
+ computations in the formal group are carried out
734
+
735
+ EXAMPLES::
736
+
737
+ sage: E = EllipticCurve(GF(13),[1,7])
738
+ sage: phi = E.isogeny(E(10,4))
739
+ sage: phi.formal()
740
+ t + 12*t^13 + 2*t^17 + 8*t^19 + 2*t^21 + O(t^23)
741
+
742
+ ::
743
+
744
+ sage: E = EllipticCurve([0,1])
745
+ sage: phi = E.isogeny(E(2,3))
746
+ sage: phi.formal(prec=10)
747
+ t + 54*t^5 + 255*t^7 + 2430*t^9 + 19278*t^11 + O(t^13)
748
+
749
+ ::
750
+
751
+ sage: E = EllipticCurve('11a2')
752
+ sage: R.<x> = QQ[]
753
+ sage: phi = E.isogeny(x^2 + 101*x + 12751/5)
754
+ sage: phi.formal(prec=7)
755
+ t - 2724/5*t^5 + 209046/5*t^7 - 4767/5*t^8 + 29200946/5*t^9 + O(t^10)
756
+ """
757
+ Eh = self._domain.formal()
758
+ f, g = self.rational_maps()
759
+ xh = Eh.x(prec=prec)
760
+ assert not self.is_separable() or xh.valuation() == -2, f"xh has valuation {xh.valuation()} (should be -2)"
761
+ yh = Eh.y(prec=prec)
762
+ assert not self.is_separable() or yh.valuation() == -3, f"yh has valuation {yh.valuation()} (should be -3)"
763
+ fh = f(xh,yh)
764
+ assert not self.is_separable() or fh.valuation() == -2, f"fh has valuation {fh.valuation()} (should be -2)"
765
+ gh = g(xh,yh)
766
+ assert not self.is_separable() or gh.valuation() == -3, f"gh has valuation {gh.valuation()} (should be -3)"
767
+ th = -fh/gh
768
+ assert not self.is_separable() or th.valuation() == +1, f"th has valuation {th.valuation()} (should be +1)"
769
+ return th
770
+
771
+ def is_normalized(self):
772
+ r"""
773
+ Determine whether this morphism is a normalized isogeny.
774
+
775
+ .. NOTE::
776
+
777
+ An isogeny `\varphi\colon E_1\to E_2` between two given
778
+ Weierstrass equations is said to be *normalized* if the
779
+ `\varphi^*(\omega_2) = \omega_1`, where `\omega_1` and
780
+ `\omega_2` are the invariant differentials on `E_1` and
781
+ `E_2` corresponding to the given equation.
782
+
783
+ EXAMPLES::
784
+
785
+ sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
786
+ sage: E = EllipticCurve(GF(7), [0,0,0,1,0])
787
+ sage: R.<x> = GF(7)[]
788
+ sage: phi = EllipticCurveIsogeny(E, x)
789
+ sage: phi.is_normalized()
790
+ True
791
+ sage: isom = WeierstrassIsomorphism(phi.codomain(), (3, 0, 0, 0))
792
+ sage: phi = isom * phi
793
+ sage: phi.is_normalized()
794
+ False
795
+ sage: isom = WeierstrassIsomorphism(phi.codomain(), (5, 0, 0, 0))
796
+ sage: phi = isom * phi
797
+ sage: phi.is_normalized()
798
+ True
799
+ sage: isom = WeierstrassIsomorphism(phi.codomain(), (1, 1, 1, 1))
800
+ sage: phi = isom * phi
801
+ sage: phi.is_normalized()
802
+ True
803
+
804
+ ::
805
+
806
+ sage: F = GF(2^5, 'alpha'); alpha = F.gen()
807
+ sage: E = EllipticCurve(F, [1,0,1,1,1])
808
+ sage: R.<x> = F[]
809
+ sage: phi = EllipticCurveIsogeny(E, x+1)
810
+ sage: isom = WeierstrassIsomorphism(phi.codomain(), (alpha, 0, 0, 0))
811
+ sage: phi.is_normalized()
812
+ True
813
+ sage: phi = isom * phi
814
+ sage: phi.is_normalized()
815
+ False
816
+ sage: isom = WeierstrassIsomorphism(phi.codomain(), (1/alpha, 0, 0, 0))
817
+ sage: phi = isom * phi
818
+ sage: phi.is_normalized()
819
+ True
820
+ sage: isom = WeierstrassIsomorphism(phi.codomain(), (1, 1, 1, 1))
821
+ sage: phi = isom * phi
822
+ sage: phi.is_normalized()
823
+ True
824
+
825
+ ::
826
+
827
+ sage: E = EllipticCurve('11a1')
828
+ sage: R.<x> = QQ[]
829
+ sage: f = x^3 - x^2 - 10*x - 79/4
830
+ sage: phi = EllipticCurveIsogeny(E, f)
831
+ sage: isom = WeierstrassIsomorphism(phi.codomain(), (2, 0, 0, 0))
832
+ sage: phi.is_normalized()
833
+ True
834
+ sage: phi = isom * phi
835
+ sage: phi.is_normalized()
836
+ False
837
+ sage: isom = WeierstrassIsomorphism(phi.codomain(), (1/2, 0, 0, 0))
838
+ sage: phi = isom * phi
839
+ sage: phi.is_normalized()
840
+ True
841
+ sage: isom = WeierstrassIsomorphism(phi.codomain(), (1, 1, 1, 1))
842
+ sage: phi = isom * phi
843
+ sage: phi.is_normalized()
844
+ True
845
+
846
+ ALGORITHM: We check if :meth:`scaling_factor` returns `1`.
847
+ """
848
+ return self.scaling_factor().is_one()
849
+
850
+ def inseparable_degree(self):
851
+ r"""
852
+ Return the inseparable degree of this isogeny.
853
+
854
+ Implemented by child classes. For examples, see:
855
+
856
+ - :meth:`EllipticCurveIsogeny.inseparable_degree`
857
+ - :meth:`sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism.inseparable_degree`
858
+ - :meth:`sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite.inseparable_degree`
859
+ - :meth:`sage.schemes.elliptic_curves.hom_sum.EllipticCurveHom_sum.inseparable_degree`
860
+ - :meth:`sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.inseparable_degree`
861
+ - :meth:`sage.schemes.elliptic_curves.hom_frobenius.EllipticCurveHom_frobenius.inseparable_degree`
862
+ - :meth:`sage.schemes.elliptic_curves.hom_velusqrt.EllipticCurveHom_velusqrt.inseparable_degree`
863
+ - :meth:`sage.schemes.elliptic_curves.hom_fractional.EllipticCurveHom_fractional.inseparable_degree`
864
+
865
+ TESTS::
866
+
867
+ sage: from sage.schemes.elliptic_curves.hom import EllipticCurveHom
868
+ sage: EllipticCurveHom.inseparable_degree(None)
869
+ Traceback (most recent call last):
870
+ ...
871
+ NotImplementedError: ...
872
+ """
873
+ raise NotImplementedError('children must implement')
874
+
875
+ def separable_degree(self):
876
+ r"""
877
+ Return the separable degree of this isogeny.
878
+
879
+ The separable degree is the result of dividing the :meth:`degree`
880
+ by the :meth:`inseparable_degree`.
881
+
882
+ EXAMPLES::
883
+
884
+ sage: E = EllipticCurve(GF(11), [5,5])
885
+ sage: E.is_supersingular()
886
+ False
887
+ sage: E.scalar_multiplication(-77).separable_degree()
888
+ 539
889
+ sage: E = EllipticCurve(GF(11), [5,0])
890
+ sage: E.is_supersingular()
891
+ True
892
+ sage: E.scalar_multiplication(-77).separable_degree()
893
+ 49
894
+ """
895
+ return self.degree() // self.inseparable_degree()
896
+
897
+ def is_separable(self):
898
+ r"""
899
+ Determine whether or not this morphism is a separable isogeny.
900
+
901
+ EXAMPLES::
902
+
903
+ sage: E = EllipticCurve(GF(17), [0,0,0,3,0])
904
+ sage: phi = EllipticCurveIsogeny(E, E((0,0)))
905
+ sage: phi.is_separable()
906
+ True
907
+
908
+ ::
909
+
910
+ sage: E = EllipticCurve('11a1')
911
+ sage: phi = EllipticCurveIsogeny(E, E.torsion_points())
912
+ sage: phi.is_separable()
913
+ True
914
+
915
+ ::
916
+
917
+ sage: E = EllipticCurve(GF(31337), [0,1]) # needs sage.rings.finite_rings
918
+ sage: {f.is_separable() for f in E.automorphisms()} # needs sage.rings.finite_rings
919
+ {True}
920
+
921
+ ::
922
+
923
+ sage: # needs sage.rings.finite_rings
924
+ sage: from sage.schemes.elliptic_curves.hom_composite import EllipticCurveHom_composite
925
+ sage: E = EllipticCurve(GF(7^2), [3,2])
926
+ sage: P = E.lift_x(1)
927
+ sage: phi = EllipticCurveHom_composite(E, P); phi
928
+ Composite morphism of degree 7:
929
+ From: Elliptic Curve defined by y^2 = x^3 + 3*x + 2 over Finite Field in z2 of size 7^2
930
+ To: Elliptic Curve defined by y^2 = x^3 + 3*x + 2 over Finite Field in z2 of size 7^2
931
+ sage: phi.is_separable()
932
+ True
933
+
934
+ ::
935
+
936
+ sage: E = EllipticCurve(GF(11), [4,4])
937
+ sage: E.scalar_multiplication(11).is_separable()
938
+ False
939
+ sage: E.scalar_multiplication(-11).is_separable()
940
+ False
941
+ sage: E.scalar_multiplication(777).is_separable()
942
+ True
943
+ sage: E.scalar_multiplication(-1).is_separable()
944
+ True
945
+ sage: E.scalar_multiplication(77).is_separable()
946
+ False
947
+ sage: E.scalar_multiplication(121).is_separable()
948
+ False
949
+
950
+ ::
951
+
952
+ sage: from sage.schemes.elliptic_curves.hom_frobenius import EllipticCurveHom_frobenius
953
+ sage: E = EllipticCurve(GF(11), [1,1])
954
+ sage: pi = EllipticCurveHom_frobenius(E)
955
+ sage: pi.degree()
956
+ 11
957
+ sage: pi.is_separable()
958
+ False
959
+ sage: pi = EllipticCurveHom_frobenius(E, 0)
960
+ sage: pi.degree()
961
+ 1
962
+ sage: pi.is_separable()
963
+ True
964
+
965
+ ::
966
+
967
+ sage: E = EllipticCurve(GF(17), [0,0,0,3,0])
968
+ sage: phi = E.isogeny(E((1,2)), algorithm='velusqrt')
969
+ sage: phi.is_separable()
970
+ True
971
+ """
972
+ if self.is_zero():
973
+ raise ValueError('constant zero map is not an isogeny')
974
+ return self.inseparable_degree().is_one()
975
+
976
+ def is_surjective(self):
977
+ r"""
978
+ Determine whether or not this morphism is surjective.
979
+
980
+ EXAMPLES::
981
+
982
+ sage: E = EllipticCurve('11a1')
983
+ sage: R.<x> = QQ[]
984
+ sage: f = x^2 + x - 29/5
985
+ sage: phi = EllipticCurveIsogeny(E, f)
986
+ sage: phi.is_surjective()
987
+ True
988
+
989
+ ::
990
+
991
+ sage: E = EllipticCurve(GF(7), [0,0,0,1,0])
992
+ sage: phi = EllipticCurveIsogeny(E, E((0,0)))
993
+ sage: phi.is_surjective()
994
+ True
995
+
996
+ ::
997
+
998
+ sage: F = GF(2^5, 'omega')
999
+ sage: E = EllipticCurve(j=F(0))
1000
+ sage: R.<x> = F[]
1001
+ sage: phi = EllipticCurveIsogeny(E, x)
1002
+ sage: phi.is_surjective()
1003
+ True
1004
+ """
1005
+ return bool(self.degree())
1006
+
1007
+ def is_injective(self):
1008
+ r"""
1009
+ Determine whether or not this morphism has trivial kernel.
1010
+
1011
+ The kernel is trivial if and only if this morphism is a
1012
+ purely inseparable isogeny.
1013
+
1014
+ EXAMPLES::
1015
+
1016
+ sage: E = EllipticCurve('11a1')
1017
+ sage: R.<x> = QQ[]
1018
+ sage: f = x^2 + x - 29/5
1019
+ sage: phi = EllipticCurveIsogeny(E, f)
1020
+ sage: phi.is_injective()
1021
+ False
1022
+ sage: phi = EllipticCurveIsogeny(E, R(1))
1023
+ sage: phi.is_injective()
1024
+ True
1025
+
1026
+ ::
1027
+
1028
+ sage: F = GF(7)
1029
+ sage: E = EllipticCurve(j=F(0))
1030
+ sage: phi = EllipticCurveIsogeny(E, [ E((0,-1)), E((0,1))])
1031
+ sage: phi.is_injective()
1032
+ False
1033
+ sage: phi = EllipticCurveIsogeny(E, E(0))
1034
+ sage: phi.is_injective()
1035
+ True
1036
+
1037
+ ::
1038
+
1039
+ sage: from sage.schemes.elliptic_curves.hom_composite import EllipticCurveHom_composite
1040
+ sage: E = EllipticCurve([1,0])
1041
+ sage: phi = EllipticCurveHom_composite(E, E(0,0))
1042
+ sage: phi.is_injective()
1043
+ False
1044
+ sage: E = EllipticCurve_from_j(GF(3).algebraic_closure()(0))
1045
+ sage: nu = EllipticCurveHom_composite.from_factors(E.automorphisms())
1046
+ sage: nu
1047
+ Composite morphism of degree 1 = 1^12:
1048
+ From: Elliptic Curve defined by y^2 = x^3 + x
1049
+ over Algebraic closure of Finite Field of size 3
1050
+ To: Elliptic Curve defined by y^2 = x^3 + x
1051
+ over Algebraic closure of Finite Field of size 3
1052
+ sage: nu.is_injective()
1053
+ True
1054
+
1055
+ ::
1056
+
1057
+ sage: E = EllipticCurve(GF(23), [1,0])
1058
+ sage: E.scalar_multiplication(4).is_injective()
1059
+ False
1060
+ sage: E.scalar_multiplication(5).is_injective()
1061
+ False
1062
+ sage: E.scalar_multiplication(1).is_injective()
1063
+ True
1064
+ sage: E.scalar_multiplication(-1).is_injective()
1065
+ True
1066
+ sage: E.scalar_multiplication(23).is_injective()
1067
+ True
1068
+ sage: E.scalar_multiplication(-23).is_injective()
1069
+ True
1070
+ sage: E.scalar_multiplication(0).is_injective()
1071
+ False
1072
+
1073
+ ::
1074
+
1075
+ sage: from sage.schemes.elliptic_curves.hom_frobenius import EllipticCurveHom_frobenius
1076
+ sage: E = EllipticCurve(GF(11), [1,1])
1077
+ sage: pi = EllipticCurveHom_frobenius(E, 5)
1078
+ sage: pi.is_injective()
1079
+ True
1080
+ """
1081
+ if self.is_zero():
1082
+ return False
1083
+ return self.separable_degree().is_one()
1084
+
1085
+ def is_zero(self):
1086
+ r"""
1087
+ Check whether this elliptic-curve morphism is the zero map.
1088
+
1089
+ EXAMPLES::
1090
+
1091
+ sage: E = EllipticCurve(j=GF(7)(0))
1092
+ sage: phi = EllipticCurveIsogeny(E, [E(0,1), E(0,-1)])
1093
+ sage: phi.is_zero()
1094
+ False
1095
+ """
1096
+ return not self.degree()
1097
+
1098
+ def __neg__(self):
1099
+ r"""
1100
+ Return the negative of this elliptic-curve morphism. In other
1101
+ words, return `[-1]\circ\varphi` where `\varphi` is ``self``
1102
+ and `[-1]` is the negation automorphism on the codomain curve.
1103
+
1104
+ EXAMPLES::
1105
+
1106
+ sage: from sage.schemes.elliptic_curves.hom import EllipticCurveHom
1107
+ sage: E = EllipticCurve(GF(1019), [5,5])
1108
+ sage: phi = E.isogeny(E.lift_x(73))
1109
+ sage: f,g = phi.rational_maps()
1110
+ sage: psi = EllipticCurveHom.__neg__(phi)
1111
+ sage: psi.rational_maps() == (f, -g)
1112
+ True
1113
+ """
1114
+ return wm.negation_morphism(self.codomain()) * self
1115
+
1116
+ @cached_method
1117
+ def __hash__(self):
1118
+ r"""
1119
+ Return a hash value for this elliptic-curve morphism.
1120
+
1121
+ ALGORITHM:
1122
+
1123
+ Hash a tuple containing the domain, codomain, and kernel
1124
+ polynomial of this morphism. (The base field is factored
1125
+ into the computation as part of the (co)domain hashes.)
1126
+
1127
+ EXAMPLES::
1128
+
1129
+ sage: E = EllipticCurve(QQ, [0,0,0,1,0])
1130
+ sage: phi_v = EllipticCurveIsogeny(E, E((0,0)))
1131
+ sage: phi_k = EllipticCurveIsogeny(E, [0,1])
1132
+ sage: phi_k.__hash__() == phi_v.__hash__()
1133
+ True
1134
+ sage: E_F17 = EllipticCurve(GF(17), [0,0,0,1,1])
1135
+ sage: phi_p = EllipticCurveIsogeny(E_F17, E_F17([0,1]))
1136
+ sage: phi_p.__hash__() == phi_v.__hash__()
1137
+ False
1138
+
1139
+ ::
1140
+
1141
+ sage: E = EllipticCurve('49a3')
1142
+ sage: R.<X> = QQ[]
1143
+ sage: EllipticCurveIsogeny(E,X^3-13*X^2-58*X+503,check=False)
1144
+ Isogeny of degree 7 from Elliptic Curve defined by y^2 + x*y = x^3 - x^2 - 107*x + 552 over Rational Field to Elliptic Curve defined by y^2 + x*y = x^3 - x^2 - 5252*x - 178837 over Rational Field
1145
+ """
1146
+ return hash((self.domain(), self.codomain(), self.kernel_polynomial(), self.scaling_factor()))
1147
+
1148
+ def as_morphism(self):
1149
+ r"""
1150
+ Return ``self`` as a morphism of projective schemes.
1151
+
1152
+ EXAMPLES::
1153
+
1154
+ sage: k = GF(11)
1155
+ sage: E = EllipticCurve(k, [1,1])
1156
+ sage: Q = E(6,5)
1157
+ sage: phi = E.isogeny(Q)
1158
+ sage: mor = phi.as_morphism()
1159
+ sage: mor.domain() == E
1160
+ True
1161
+ sage: mor.codomain() == phi.codomain()
1162
+ True
1163
+ sage: mor(Q) == phi(Q)
1164
+ True
1165
+
1166
+ TESTS::
1167
+
1168
+ sage: mor(0*Q)
1169
+ (0 : 1 : 0)
1170
+ sage: mor(1*Q)
1171
+ (0 : 1 : 0)
1172
+ """
1173
+ from sage.schemes.curves.constructor import Curve
1174
+ X_affine = Curve(self.domain()).affine_patch(2)
1175
+ Y_affine = Curve(self.codomain()).affine_patch(2)
1176
+ return X_affine.hom(self.rational_maps(), Y_affine).homogenize(2)
1177
+
1178
+ def matrix_on_subgroup(self, domain_gens, codomain_gens=None):
1179
+ r"""
1180
+ Return the matrix by which this isogeny acts on the
1181
+ `n`-torsion subgroup with respect to the given bases.
1182
+
1183
+ INPUT:
1184
+
1185
+ - ``domain_gens`` -- basis `(P,Q)` of some `n`-torsion
1186
+ subgroup on the domain of this elliptic-curve morphism
1187
+
1188
+ - ``codomain_gens`` -- basis `(R,S)` of the `n`-torsion
1189
+ on the codomain of this morphism, or (default) ``None``
1190
+ if ``self`` is an endomorphism
1191
+
1192
+ OUTPUT:
1193
+
1194
+ A `2\times 2` matrix `M` over `\ZZ/n`, such that the
1195
+ image of any point `[a]P + [b]Q` under this morphism
1196
+ equals `[c]R + [d]S` where `(c\ d)^T = (a\ b) M`.
1197
+
1198
+ EXAMPLES::
1199
+
1200
+ sage: F.<i> = GF(419^2, modulus=[1,0,1])
1201
+ sage: E = EllipticCurve(F, [1,0])
1202
+ sage: P = E(3, 176*i)
1203
+ sage: Q = E(i+7, 67*i+48)
1204
+ sage: P.weil_pairing(Q, 420).multiplicative_order()
1205
+ 420
1206
+ sage: iota = E.automorphisms()[2]; iota
1207
+ Elliptic-curve endomorphism of Elliptic Curve defined by y^2 = x^3 + x over Finite Field in i of size 419^2
1208
+ Via: (u,r,s,t) = (i, 0, 0, 0)
1209
+ sage: iota^2 == E.scalar_multiplication(-1)
1210
+ True
1211
+ sage: mat = iota.matrix_on_subgroup((P,Q)); mat
1212
+ [301 386]
1213
+ [ 83 119]
1214
+ sage: mat.parent()
1215
+ Full MatrixSpace of 2 by 2 dense matrices over Ring of integers modulo 420
1216
+ sage: iota(P) == 301*P + 386*Q
1217
+ True
1218
+ sage: iota(Q) == 83*P + 119*Q
1219
+ True
1220
+ sage: a,b = 123, 456
1221
+ sage: c,d = vector((a,b)) * mat; (c,d)
1222
+ (111, 102)
1223
+ sage: iota(a*P + b*Q) == c*P + d*Q
1224
+ True
1225
+
1226
+ One important application of this is to compute generators of
1227
+ the kernel subgroup of an isogeny, when the `n`-torsion subgroup
1228
+ containing the kernel is accessible::
1229
+
1230
+ sage: K = E(83*i-16, 9*i-147)
1231
+ sage: K.order()
1232
+ 7
1233
+ sage: phi = E.isogeny(K)
1234
+ sage: R,S = phi.codomain().gens()
1235
+ sage: mat = phi.matrix_on_subgroup((P,Q), (R,S))
1236
+ sage: mat # random -- depends on R,S
1237
+ [124 263]
1238
+ [115 141]
1239
+ sage: kermat = mat.left_kernel_matrix(); kermat
1240
+ [300 60]
1241
+ sage: ker = [ZZ(v[0])*P + ZZ(v[1])*Q for v in kermat]
1242
+ sage: {phi(T) for T in ker}
1243
+ {(0 : 1 : 0)}
1244
+ sage: phi == E.isogeny(ker)
1245
+ True
1246
+
1247
+ We can also compute the matrix of a Frobenius endomorphism
1248
+ (:class:`~sage.schemes.elliptic_curves.hom_frobenius.EllipticCurveHom_frobenius`)
1249
+ on a large enough subgroup to verify point-counting results::
1250
+
1251
+ sage: F.<a> = GF((101, 36))
1252
+ sage: E = EllipticCurve(GF(101), [1,1])
1253
+ sage: EE = E.change_ring(F)
1254
+ sage: P,Q = EE.torsion_basis(37)
1255
+ sage: pi = EE.frobenius_isogeny()
1256
+ sage: M = pi.matrix_on_subgroup((P,Q))
1257
+ sage: M.parent()
1258
+ Full MatrixSpace of 2 by 2 dense matrices over Ring of integers modulo 37
1259
+ sage: M.trace()
1260
+ 34
1261
+ sage: E.trace_of_frobenius()
1262
+ -3
1263
+
1264
+ .. SEEALSO::
1265
+
1266
+ To compute a basis of the `n`-torsion, you may use
1267
+ :meth:`~sage.schemes.elliptic_curves.ell_finite_field.EllipticCurve_finite_field.torsion_basis`.
1268
+ """
1269
+ if codomain_gens is None:
1270
+ if not self.is_endomorphism():
1271
+ raise ValueError('basis of codomain subgroup is required for non-endomorphisms')
1272
+ codomain_gens = domain_gens
1273
+
1274
+ P,Q = domain_gens
1275
+ R,S = codomain_gens
1276
+
1277
+ ords = {P.order() for P in (P,Q,R,S)}
1278
+ if len(ords) != 1:
1279
+ #TODO: Is there some meaningful way to lift this restriction?
1280
+ raise ValueError('generator points must all have the same order')
1281
+ n, = ords
1282
+
1283
+ if P.weil_pairing(Q, n).multiplicative_order() != n:
1284
+ raise ValueError('generator points on domain are not independent')
1285
+ if R.weil_pairing(S, n).multiplicative_order() != n:
1286
+ raise ValueError('generator points on codomain are not independent')
1287
+
1288
+ imP = self._eval(P)
1289
+ imQ = self._eval(Q)
1290
+
1291
+ vecP = imP.log([R, S])
1292
+ vecQ = imQ.log([R, S])
1293
+
1294
+ from sage.matrix.constructor import matrix
1295
+ from sage.rings.finite_rings.integer_mod_ring import Zmod
1296
+ return matrix(Zmod(n), [vecP, vecQ])
1297
+
1298
+ def __truediv__(self, other):
1299
+ r"""
1300
+ Internal helper function to provide the `\phi/d` syntax
1301
+ for dividing an isogeny by an integer.
1302
+
1303
+ To divide an isogeny by another isogeny (on the left or
1304
+ right), use :meth:`divide_left` or :meth:`divide_right`.
1305
+
1306
+ EXAMPLES::
1307
+
1308
+ sage: E = EllipticCurve(GF(419), [-1, 0])
1309
+ sage: (E.frobenius_isogeny() + 1) / 2
1310
+ Fractional elliptic-curve morphism of degree 105:
1311
+ Numerator: Sum morphism:
1312
+ From: Elliptic Curve defined by y^2 = x^3 + 418*x over Finite Field of size 419
1313
+ To: Elliptic Curve defined by y^2 = x^3 + 418*x over Finite Field of size 419
1314
+ Via: (Frobenius endomorphism of degree 419:
1315
+ From: Elliptic Curve defined by y^2 = x^3 + 418*x over Finite Field of size 419
1316
+ To: Elliptic Curve defined by y^2 = x^3 + 418*x over Finite Field of size 419,
1317
+ Scalar-multiplication endomorphism [1]
1318
+ of Elliptic Curve defined by y^2 = x^3 + 418*x over Finite Field of size 419)
1319
+ Denominator: 2
1320
+ """
1321
+ from sage.rings.integer import Integer
1322
+ if not isinstance(other, (int, Integer)):
1323
+ return NotImplemented
1324
+ from sage.schemes.elliptic_curves.hom_fractional import EllipticCurveHom_fractional
1325
+ return EllipticCurveHom_fractional(self, other)
1326
+
1327
+ def divide_left(self, psi):
1328
+ r"""
1329
+ Return an isogeny `\chi` such that `\psi\circ\chi = \varphi`,
1330
+ where `\varphi` is this isogeny, if such a `\chi` exists.
1331
+
1332
+ EXAMPLES::
1333
+
1334
+ sage: E = EllipticCurve('54.b2')
1335
+ sage: K = next(T for T in E.torsion_points() if T.order() == 9)
1336
+ sage: phi, psi = E.isogeny(K).factors()
1337
+ sage: chain = psi * phi; chain
1338
+ Composite morphism of degree 9 = 3^2:
1339
+ From: Elliptic Curve defined by y^2 + x*y + y = x^3 - x^2 - 14*x + 29 over Rational Field
1340
+ To: Elliptic Curve defined by y^2 + x*y + y = x^3 - x^2 - 2324*x - 43091 over Rational Field
1341
+ sage: chain.divide_right(phi)
1342
+ Fractional elliptic-curve morphism of degree 3:
1343
+ Numerator: Composite morphism of degree 27 = 3^3:
1344
+ From: Elliptic Curve defined by y^2 + x*y + y = x^3 - x^2 + 106*x - 323 over Rational Field
1345
+ To: Elliptic Curve defined by y^2 + x*y + y = x^3 - x^2 - 2324*x - 43091 over Rational Field
1346
+ Denominator: 3
1347
+ sage: chain.divide_right(phi) == psi
1348
+ True
1349
+ """
1350
+ from sage.schemes.elliptic_curves.hom_fractional import EllipticCurveHom_fractional
1351
+ numer = psi.dual() * self
1352
+ denom = psi.degree()
1353
+ return EllipticCurveHom_fractional(numer, denom)
1354
+
1355
+ def divide_right(self, psi):
1356
+ r"""
1357
+ Return an isogeny `\chi` such that `\chi\circ\psi = \varphi`,
1358
+ where `\varphi` is this isogeny, if such a `\chi` exists.
1359
+
1360
+ EXAMPLES::
1361
+
1362
+ sage: E = EllipticCurve('54.b2')
1363
+ sage: K = next(T for T in E.torsion_points() if T.order() == 9)
1364
+ sage: phi, psi = E.isogeny(K).factors()
1365
+ sage: chain = psi * phi; chain
1366
+ Composite morphism of degree 9 = 3^2:
1367
+ From: Elliptic Curve defined by y^2 + x*y + y = x^3 - x^2 - 14*x + 29 over Rational Field
1368
+ To: Elliptic Curve defined by y^2 + x*y + y = x^3 - x^2 - 2324*x - 43091 over Rational Field
1369
+ sage: chain.divide_left(psi)
1370
+ Fractional elliptic-curve morphism of degree 3:
1371
+ Numerator: Composite morphism of degree 27 = 3^3:
1372
+ From: Elliptic Curve defined by y^2 + x*y + y = x^3 - x^2 - 14*x + 29 over Rational Field
1373
+ To: Elliptic Curve defined by y^2 + x*y + y = x^3 - x^2 + 106*x - 323 over Rational Field
1374
+ Denominator: 3
1375
+ sage: chain.divide_left(psi) == phi
1376
+ True
1377
+ """
1378
+ from sage.schemes.elliptic_curves.hom_fractional import EllipticCurveHom_fractional
1379
+ numer = self * psi.dual()
1380
+ denom = psi.degree()
1381
+ return EllipticCurveHom_fractional(numer, denom)
1382
+
1383
+ def minimal_polynomial(self):
1384
+ r"""
1385
+ Return a minimal polynomial of the kernel subgroup of this isogeny, as
1386
+ defined in [EPSV2023]_, Definition 15: That is, some polynomial `f` such
1387
+ that the points on the domain curve whose `x`-coordinates are roots of `f`
1388
+ generate the kernel of this isogeny.
1389
+
1390
+ .. SEEALSO::
1391
+
1392
+ :meth:`EllipticCurve_field.kernel_polynomial_from_divisor()`
1393
+
1394
+ EXAMPLES::
1395
+
1396
+ sage: E = EllipticCurve(GF(419), [32, 41])
1397
+ sage: phi = E.isogeny(E.lift_x(30)); phi
1398
+ Isogeny of degree 7
1399
+ from Elliptic Curve defined by y^2 = x^3 + 32*x + 41 over Finite Field of size 419
1400
+ to Elliptic Curve defined by y^2 = x^3 + 316*x + 241 over Finite Field of size 419
1401
+ sage: f = phi.minimal_polynomial(); f # random -- one of x+161, x+201, x+389
1402
+ x + 161
1403
+ sage: f.divides(phi.kernel_polynomial())
1404
+ True
1405
+ sage: E.kernel_polynomial_from_divisor(f, 7) == phi.kernel_polynomial()
1406
+ True
1407
+
1408
+ It also works for rational isogenies with irrational kernel points::
1409
+
1410
+ sage: E = EllipticCurve(GF(127^2), [1,0])
1411
+ sage: phi = E.isogenies_prime_degree(17)[0]; phi
1412
+ Isogeny of degree 17
1413
+ from Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 127^2
1414
+ to Elliptic Curve defined by y^2 = x^3 + (16*z2+26)*x over Finite Field in z2 of size 127^2
1415
+ sage: phi.kernel_polynomial()
1416
+ x^8 + (68*z2 + 97)*x^6 + (59*z2 + 40)*x^4 + (59*z2 + 38)*x^2 + 4*z2 + 13
1417
+ sage: phi.kernel_polynomial().factor()
1418
+ (x^4 + (11*z2 + 32)*x^2 + 48*z2 + 70) * (x^4 + (57*z2 + 65)*x^2 + 20*z2 + 25)
1419
+ sage: phi.minimal_polynomial().factor()
1420
+ x^4 + (57*z2 + 65)*x^2 + 20*z2 + 25
1421
+ """
1422
+ #FIXME This can probably be implemented better!
1423
+ h = self.kernel_polynomial()
1424
+ for f,_ in reversed(h.factor()):
1425
+ if self.domain().kernel_polynomial_from_divisor(f, self.degree()) == h:
1426
+ return f
1427
+ raise ValueError('not a cyclic isogeny')
1428
+
1429
+ def push_subgroup(self, f):
1430
+ r"""
1431
+ Given a minimal polynomial (see :meth:`minimal_polynomial`) of a
1432
+ subgroup `G` of the domain curve of this isogeny, return a minimal
1433
+ polynomial of the image of `G` under this isogeny.
1434
+
1435
+ ALGORITHM: [EPSV2023]_, Algorithm 5 (``PushSubgroup``)
1436
+
1437
+ EXAMPLES::
1438
+
1439
+ sage: E = EllipticCurve(GF(419), [32, 41])
1440
+ sage: K = E.lift_x(30)
1441
+ sage: phi = E.isogeny(K); phi
1442
+ Isogeny of degree 7
1443
+ from Elliptic Curve defined by y^2 = x^3 + 32*x + 41 over Finite Field of size 419
1444
+ to Elliptic Curve defined by y^2 = x^3 + 316*x + 241 over Finite Field of size 419
1445
+ sage: psi = E.isogeny(E.lift_x(54), algorithm='factored'); psi
1446
+ Composite morphism of degree 15 = 3*5:
1447
+ From: Elliptic Curve defined by y^2 = x^3 + 32*x + 41 over Finite Field of size 419
1448
+ To: Elliptic Curve defined by y^2 = x^3 + 36*x + 305 over Finite Field of size 419
1449
+ sage: f = phi.minimal_polynomial(); f # random -- one of x+161, x+201, x+389
1450
+ x + 161
1451
+ sage: g = psi.push_subgroup(f); g # random -- one of x+148, x+333, x+249
1452
+ x + 148
1453
+ sage: h = psi.codomain().kernel_polynomial_from_divisor(g, phi.degree()); h
1454
+ x^3 + 311*x^2 + 196*x + 44
1455
+ sage: chi = psi.codomain().isogeny(h); chi
1456
+ Isogeny of degree 7
1457
+ from Elliptic Curve defined by y^2 = x^3 + 36*x + 305 over Finite Field of size 419
1458
+ to Elliptic Curve defined by y^2 = x^3 + 186*x + 37 over Finite Field of size 419
1459
+ sage: (chi * psi)(K)
1460
+ (0 : 1 : 0)
1461
+
1462
+ It also works for rational isogenies with irrational kernel points::
1463
+
1464
+ sage: E = EllipticCurve(GF(127^2), [1,0])
1465
+ sage: phi = E.isogenies_prime_degree(13)[0]; phi
1466
+ Isogeny of degree 13
1467
+ from Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 127^2
1468
+ to Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 127^2
1469
+ sage: psi = E.isogenies_prime_degree(17)[0]; psi
1470
+ Isogeny of degree 17
1471
+ from Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 127^2
1472
+ to Elliptic Curve defined by y^2 = x^3 + (16*z2+26)*x over Finite Field in z2 of size 127^2
1473
+ sage: f_phi = phi.minimal_polynomial()
1474
+ sage: g_phi = psi.push_subgroup(f_phi)
1475
+ sage: h_phi = psi.codomain().kernel_polynomial_from_divisor(g_phi, phi.degree())
1476
+ sage: phi_pushed = psi.codomain().isogeny(h_phi); phi_pushed
1477
+ Isogeny of degree 13 from Elliptic Curve defined by y^2 = x^3 + (16*z2+26)*x over Finite Field in z2 of size 127^2 to Elliptic Curve defined by y^2 = x^3 + (110*z2+61)*x over Finite Field in z2 of size 127^2
1478
+ sage: f_psi = psi.minimal_polynomial()
1479
+ sage: g_psi = phi.push_subgroup(f_psi)
1480
+ sage: h_psi = phi.codomain().kernel_polynomial_from_divisor(g_psi, psi.degree())
1481
+ sage: psi_pushed = phi.codomain().isogeny(h_psi); psi_pushed
1482
+ Isogeny of degree 17 from Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 127^2 to Elliptic Curve defined by y^2 = x^3 + (16*z2+26)*x over Finite Field in z2 of size 127^2
1483
+ sage: any(iso * psi_pushed * phi == phi_pushed * psi
1484
+ ....: for iso in psi_pushed.codomain().isomorphisms(phi_pushed.codomain()))
1485
+ True
1486
+
1487
+ If the subgroup represented by `f` intersects nontrivially with the
1488
+ kernel of this isogeny, the method still works correctly::
1489
+
1490
+ sage: E = EllipticCurve(GF(419), [1,0])
1491
+ sage: phi = next(E.isogenies_degree(7)); phi
1492
+ Isogeny of degree 7
1493
+ from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 419
1494
+ to Elliptic Curve defined by y^2 = x^3 + 285*x + 87 over Finite Field of size 419
1495
+ sage: psi = next(E.isogenies_degree(21)); psi
1496
+ Composite morphism of degree 21 = 7*3:
1497
+ From: Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 419
1498
+ To: Elliptic Curve defined by y^2 = x^3 + 134*x + 230 over Finite Field of size 419
1499
+ sage: phi.kernel_polynomial().gcd(psi.kernel_polynomial())
1500
+ x^3 + 274*x^2 + 350*x + 6
1501
+ sage: f = phi.minimal_polynomial()
1502
+ sage: psi.push_subgroup(f)
1503
+ 1
1504
+ """
1505
+ g = self.x_rational_map()
1506
+ g1, g2 = g.numerator(), g.denominator()
1507
+ gker = g2.gcd(f)
1508
+ f1 = f // gker
1509
+ R = f1.parent()
1510
+ S = R.quotient_ring(f1)
1511
+ alpha = S(g1 * g2.inverse_mod(f1))
1512
+ if not alpha:
1513
+ return R.one()
1514
+ return alpha.minpoly()
1515
+
1516
+
1517
+ def compare_via_evaluation(left, right):
1518
+ r"""
1519
+ Test if two elliptic-curve morphisms are equal by evaluating
1520
+ them at enough points.
1521
+
1522
+ INPUT:
1523
+
1524
+ - ``left``, ``right`` -- :class:`EllipticCurveHom` objects
1525
+
1526
+ ALGORITHM:
1527
+
1528
+ We use the fact that two isogenies of equal degree `d` must be
1529
+ the same if and only if they behave identically on more than
1530
+ `4d` points. (It suffices to check this on a few points that
1531
+ generate a large enough subgroup.)
1532
+
1533
+ If the domain curve does not have sufficiently many rational
1534
+ points, the base field is extended first: Taking an extension
1535
+ of degree `O(\log(d))` suffices.
1536
+
1537
+ EXAMPLES::
1538
+
1539
+ sage: E = EllipticCurve(GF(83), [1,0])
1540
+ sage: phi = E.isogeny(12*E.0, model='montgomery'); phi
1541
+ Isogeny of degree 7 from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 83 to Elliptic Curve defined by y^2 = x^3 + 70*x^2 + x over Finite Field of size 83
1542
+ sage: psi = phi.dual(); psi
1543
+ Isogeny of degree 7 from Elliptic Curve defined by y^2 = x^3 + 70*x^2 + x over Finite Field of size 83 to Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 83
1544
+ sage: from sage.schemes.elliptic_curves.hom_composite import EllipticCurveHom_composite
1545
+ sage: mu = EllipticCurveHom_composite.from_factors([phi, psi])
1546
+ sage: from sage.schemes.elliptic_curves.hom import compare_via_evaluation
1547
+ sage: compare_via_evaluation(mu, E.scalar_multiplication(7)) # needs sage.symbolic
1548
+ True
1549
+
1550
+ .. SEEALSO::
1551
+
1552
+ - :meth:`sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite._richcmp_`
1553
+ """
1554
+ if left.domain() != right.domain():
1555
+ return False
1556
+ if left.codomain() != right.codomain():
1557
+ return False
1558
+ if left.degree() != right.degree():
1559
+ return False
1560
+
1561
+ E = left.domain()
1562
+ F = E.base_ring()
1563
+
1564
+ d = left.degree()
1565
+ if isinstance(F, finite_field_base.FiniteField):
1566
+ # check at a random rational point first
1567
+ P = E.random_point()
1568
+ if left(P) != right(P):
1569
+ return False
1570
+
1571
+ # then extend to a field with enough points to conclude
1572
+ q = F.cardinality()
1573
+ e = integer_floor(1 + 2 * (2*d.sqrt() + 1).log(q)) # from Hasse bound
1574
+ e = next(i for i, n in enumerate(E.count_points(e+1), 1) if n > 4*d)
1575
+ EE = E.base_extend(F.extension(e, 'U')) # named extension is faster
1576
+ Ps = EE.gens()
1577
+ return all(left._eval(P) == right._eval(P) for P in Ps)
1578
+
1579
+ elif isinstance(F, number_field_base.NumberField):
1580
+ for _ in range(100):
1581
+ P = E.lift_x(F.random_element(), extend=True)
1582
+ if P._has_order_at_least(4*d + 1, attempts=50):
1583
+ # if P.height(precision=250) == 0: # slow sometimes
1584
+ return left._eval(P) == right._eval(P)
1585
+ assert False, "couldn't find a point of large enough order"
1586
+
1587
+ else:
1588
+ raise NotImplementedError('not implemented for this base field')
1589
+
1590
+
1591
+ def find_post_isomorphism(phi, psi):
1592
+ r"""
1593
+ Given two isogenies `\phi: E\to E'` and `\psi: E\to E''`
1594
+ which are equal up to post-isomorphism defined over the
1595
+ same field, find that isomorphism.
1596
+
1597
+ In other words, this function computes an isomorphism
1598
+ `\alpha: E'\to E''` such that `\alpha\circ\phi = \psi`.
1599
+
1600
+ ALGORITHM:
1601
+
1602
+ Start with a list of all isomorphisms `E'\to E''`. Then
1603
+ repeatedly evaluate `\phi` and `\psi` at random points
1604
+ `P` to filter the list for isomorphisms `\alpha` with
1605
+ `\alpha(\phi(P)) = \psi(P)`. Once only one candidate is
1606
+ left, return it. Periodically extend the base field to
1607
+ avoid getting stuck (say, if all candidate isomorphisms
1608
+ act the same on all rational points).
1609
+
1610
+ EXAMPLES::
1611
+
1612
+ sage: from sage.schemes.elliptic_curves.hom import find_post_isomorphism
1613
+ sage: E = EllipticCurve(GF(7^2), [1,0])
1614
+ sage: f = E.scalar_multiplication(1)
1615
+ sage: g = choice(E.automorphisms())
1616
+ sage: find_post_isomorphism(f, g) == g
1617
+ True
1618
+
1619
+ ::
1620
+
1621
+ sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
1622
+ sage: from sage.schemes.elliptic_curves.hom_composite import EllipticCurveHom_composite
1623
+ sage: x = polygen(ZZ, 'x')
1624
+ sage: F.<i> = GF(883^2, modulus=x^2+1)
1625
+ sage: E = EllipticCurve(F, [1,0])
1626
+ sage: P = E.lift_x(117)
1627
+ sage: Q = E.lift_x(774)
1628
+ sage: w = WeierstrassIsomorphism(E, [i,0,0,0])
1629
+ sage: phi = EllipticCurveHom_composite(E, [P,w(Q)]) * w
1630
+ sage: psi = EllipticCurveHom_composite(E, [Q,w(P)])
1631
+ sage: phi.kernel_polynomial() == psi.kernel_polynomial()
1632
+ True
1633
+ sage: find_post_isomorphism(phi, psi)
1634
+ Elliptic-curve morphism:
1635
+ From: Elliptic Curve defined by y^2 = x^3 + 320*x + 482 over Finite Field in i of size 883^2
1636
+ To: Elliptic Curve defined by y^2 = x^3 + 320*x + 401 over Finite Field in i of size 883^2
1637
+ Via: (u,r,s,t) = (882*i, 0, 0, 0)
1638
+ """
1639
+ E = phi.domain()
1640
+ if psi.domain() != E:
1641
+ raise ValueError('domains do not match')
1642
+
1643
+ isos = phi.codomain().isomorphisms(psi.codomain())
1644
+ if not isos:
1645
+ raise ValueError('codomains not isomorphic')
1646
+
1647
+ F = E.base_ring()
1648
+
1649
+ if isinstance(F, finite_field_base.FiniteField):
1650
+ while len(isos) > 1:
1651
+ for _ in range(20):
1652
+ P = E.random_point()
1653
+ im_phi, im_psi = (phi._eval(P), psi._eval(P))
1654
+ isos = [iso for iso in isos if iso._eval(im_phi) == im_psi]
1655
+ if len(isos) <= 1:
1656
+ break
1657
+ else:
1658
+ E = E.base_extend(E.base_field().extension(2, 'U')) # named extension is faster
1659
+
1660
+ elif isinstance(F, number_field_base.NumberField):
1661
+ for _ in range(100):
1662
+ P = E.lift_x(F.random_element(), extend=True)
1663
+ if P.has_finite_order():
1664
+ continue
1665
+ break
1666
+ else:
1667
+ assert False, "couldn't find a point of infinite order"
1668
+ im_phi, im_psi = (phi._eval(P), psi._eval(P))
1669
+ isos = [iso for iso in isos if iso._eval(im_phi) == im_psi]
1670
+
1671
+ else:
1672
+ # fall back to generic method
1673
+ sc = psi.scaling_factor() / phi.scaling_factor()
1674
+ isos = [iso for iso in isos if iso.u == sc]
1675
+
1676
+ assert len(isos) <= 1
1677
+ if isos:
1678
+ return isos[0]
1679
+
1680
+ # found no suitable isomorphism -- either doesn't exist or a bug
1681
+ raise ValueError('isogenies not equal up to post-isomorphism')
1682
+
1683
+
1684
+ def compute_trace_generic(phi):
1685
+ r"""
1686
+ Compute the trace of the given elliptic-curve endomorphism.
1687
+
1688
+ ALGORITHM: Simple variant of Schoof's algorithm.
1689
+ For enough small primes `\ell`, we find an order-`\ell` point `P`
1690
+ on `E` and use a discrete-logarithm calculation to find the unique
1691
+ scalar `t_\ell \in \{0,...,\ell-1\}` such that
1692
+ `\varphi^2(P)+[\deg(\varphi)]P = [t_\ell]\varphi(P)`.
1693
+ Then `t_\ell` equals the trace of `\varphi` modulo `\ell`, which
1694
+ can therefore be recovered using the Chinese remainder theorem.
1695
+
1696
+ EXAMPLES:
1697
+
1698
+ It works over finite fields::
1699
+
1700
+ sage: from sage.schemes.elliptic_curves.hom import compute_trace_generic
1701
+ sage: E = EllipticCurve(GF(31337), [1,1])
1702
+ sage: compute_trace_generic(E.frobenius_endomorphism())
1703
+ 314
1704
+
1705
+ It works over `\QQ`::
1706
+
1707
+ sage: from sage.schemes.elliptic_curves.hom import compute_trace_generic
1708
+ sage: E = EllipticCurve(QQ, [1,2,3,4,5])
1709
+ sage: dbl = E.scalar_multiplication(2)
1710
+ sage: compute_trace_generic(dbl)
1711
+ 4
1712
+
1713
+ It works over number fields (for a CM curve)::
1714
+
1715
+ sage: from sage.schemes.elliptic_curves.hom import compute_trace_generic
1716
+ sage: x = polygen(QQ)
1717
+ sage: K.<t> = NumberField(5*x^2 - 2*x + 1)
1718
+ sage: E = EllipticCurve(K, [1,0])
1719
+ sage: phi = E.isogeny([t,0,1], codomain=E) # phi = 2 + i
1720
+ sage: compute_trace_generic(phi)
1721
+ 4
1722
+
1723
+ TESTS:
1724
+
1725
+ Check on random elliptic curves over finite fields that
1726
+ the result for Frobenius matches
1727
+ :meth:`~sage.schemes.elliptic_curves.ell_finite_field.EllipticCurve_finite_field.trace_of_frobenius`::
1728
+
1729
+ sage: # needs sage.symbolic
1730
+ sage: from sage.schemes.elliptic_curves.hom import compute_trace_generic
1731
+ sage: p = random_prime(10^3)
1732
+ sage: e = randrange(1, ceil(log(10^5,p)))
1733
+ sage: F.<t> = GF((p, e))
1734
+ sage: E = choice(EllipticCurve(j=F.random_element()).twists())
1735
+ sage: pi = E.frobenius_endomorphism()
1736
+ sage: compute_trace_generic(pi) == E.trace_of_frobenius()
1737
+ True
1738
+
1739
+ Check that the nonexistence of `p`-torsion for supersingular curves
1740
+ does not cause trouble::
1741
+
1742
+ sage: from sage.schemes.elliptic_curves.hom import compute_trace_generic
1743
+ sage: E = EllipticCurve(GF(5), [0,1])
1744
+ sage: E.division_polynomial(5)
1745
+ 4
1746
+ sage: m7 = E.scalar_multiplication(7)
1747
+ sage: compute_trace_generic(-m7)
1748
+ -14
1749
+ """
1750
+ from sage.rings.finite_rings.integer_mod import Mod
1751
+ from sage.groups.generic import discrete_log
1752
+ from sage.sets.primes import Primes
1753
+ from sage.schemes.elliptic_curves.ell_field import point_of_order
1754
+
1755
+ E = phi.domain()
1756
+ if phi.codomain() != E:
1757
+ raise ValueError('trace only makes sense for endomorphisms')
1758
+
1759
+ d = phi.degree()
1760
+
1761
+ M = 4 * d.isqrt() + 1 # |trace| <= 2 sqrt(deg)
1762
+ tr = Mod(0,1)
1763
+
1764
+ F = E.base_field()
1765
+ p = F.characteristic()
1766
+ if p:
1767
+ s = phi.scaling_factor()
1768
+ if s:
1769
+ tr = Mod(ZZ(s + d/s), p)
1770
+
1771
+ for l in Primes():
1772
+ if tr.modulus() >= M:
1773
+ break
1774
+ try:
1775
+ P = point_of_order(E, l)
1776
+ except ValueError:
1777
+ continue # supersingular and l == p
1778
+
1779
+ Q = phi._eval(P)
1780
+ if not Q: # we learn nothing when P lies in the kernel
1781
+ continue
1782
+ R = phi._eval(Q)
1783
+ t = discrete_log(R + d*P, Q, ord=l, operation='+')
1784
+ # assert not R - t*Q + d*P
1785
+
1786
+ tr = tr.crt(Mod(t, l))
1787
+
1788
+ return tr.lift_centered()