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,1290 @@
1
+ # sage_setup: distribution = sagemath-schemes
2
+ r"""
3
+ Square‑root Vélu algorithm for elliptic-curve isogenies
4
+
5
+ The square-root Vélu algorithm, also called the √élu algorithm,
6
+ computes isogenies of elliptic curves in time `\tilde
7
+ O(\sqrt\ell)` rather than naïvely `O(\ell)`, where `\ell` is the degree.
8
+
9
+ The core idea is to reindex the points in the kernel subgroup in a
10
+ baby-step-giant-step manner, then use fast resultant computations to evaluate
11
+ "elliptic polynomials" (see :class:`FastEllipticPolynomial`) in essentially
12
+ square-root time.
13
+
14
+ Based on experiments with Sage version 9.7, the isogeny degree where
15
+ :class:`EllipticCurveHom_velusqrt` begins to outperform
16
+ :class:`~sage.schemes.elliptic_curves.ell_curve_isogeny.EllipticCurveIsogeny`
17
+ can be as low as `\approx 100`, but is typically closer to `\approx 1000`,
18
+ depending on the exact situation.
19
+
20
+ REFERENCES: [BDLS2020]_
21
+
22
+ EXAMPLES::
23
+
24
+ sage: from sage.schemes.elliptic_curves.hom_velusqrt import EllipticCurveHom_velusqrt
25
+ sage: E = EllipticCurve(GF(6666679), [5,5])
26
+ sage: K = E(9970, 1003793, 1)
27
+ sage: K.order()
28
+ 10009
29
+ sage: phi = EllipticCurveHom_velusqrt(E, K)
30
+ sage: phi
31
+ Elliptic-curve isogeny (using square-root Vélu) of degree 10009:
32
+ From: Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Finite Field of size 6666679
33
+ To: Elliptic Curve defined by y^2 = x^3 + 227975*x + 3596133 over Finite Field of size 6666679
34
+ sage: phi.codomain()
35
+ Elliptic Curve defined by y^2 = x^3 + 227975*x + 3596133 over Finite Field of size 6666679
36
+
37
+ Note that the isogeny is usually not identical to the one computed by
38
+ :class:`~sage.schemes.elliptic_curves.ell_curve_isogeny.EllipticCurveIsogeny`::
39
+
40
+ sage: psi = EllipticCurveIsogeny(E, K)
41
+ sage: psi
42
+ Isogeny of degree 10009
43
+ from Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Finite Field of size 6666679
44
+ to Elliptic Curve defined by y^2 = x^3 + 5344836*x + 3950273 over Finite Field of size 6666679
45
+
46
+ However, they are certainly separable isogenies with the same kernel
47
+ and must therefore be equal *up to post-isomorphism*::
48
+
49
+ sage: isos = psi.codomain().isomorphisms(phi.codomain())
50
+ sage: sum(iso * psi == phi for iso in isos) # needs sage.symbolic
51
+ 1
52
+
53
+ Just like
54
+ :class:`~sage.schemes.elliptic_curves.ell_curve_isogeny.EllipticCurveIsogeny`,
55
+ the constructor supports a ``model`` keyword argument::
56
+
57
+ sage: E = EllipticCurve(GF(6666679), [1,1])
58
+ sage: K = E(9091, 517864)
59
+ sage: phi = EllipticCurveHom_velusqrt(E, K, model='montgomery')
60
+ sage: phi
61
+ Elliptic-curve isogeny (using square-root Vélu) of degree 2999:
62
+ From: Elliptic Curve defined by y^2 = x^3 + x + 1 over Finite Field of size 6666679
63
+ To: Elliptic Curve defined by y^2 = x^3 + 1559358*x^2 + x over Finite Field of size 6666679
64
+
65
+ Internally, :class:`EllipticCurveHom_velusqrt` works on short
66
+ Weierstraß curves, but it performs the conversion automatically::
67
+
68
+ sage: E = EllipticCurve(GF(101), [1,2,3,4,5])
69
+ sage: K = E(1, 2)
70
+ sage: K.order()
71
+ 37
72
+ sage: EllipticCurveHom_velusqrt(E, K)
73
+ Elliptic-curve isogeny (using square-root Vélu) of degree 37:
74
+ From: Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 4*x + 5 over Finite Field of size 101
75
+ To: Elliptic Curve defined by y^2 = x^3 + 66*x + 86 over Finite Field of size 101
76
+
77
+ However, this does imply not all elliptic curves are supported.
78
+ Curves without a short Weierstraß model exist in characteristics
79
+ `2` and `3`::
80
+
81
+ sage: F.<t> = GF(3^3)
82
+ sage: E = EllipticCurve(F, [1,1,1,1,1])
83
+ sage: P = E(t^2+2, 1)
84
+ sage: P.order()
85
+ 19
86
+ sage: EllipticCurveHom_velusqrt(E, P)
87
+ Traceback (most recent call last):
88
+ ...
89
+ NotImplementedError: only implemented for curves having a short Weierstrass model
90
+
91
+ Furthermore, the implementation is restricted to finite fields,
92
+ since this appears to be the most relevant application for the
93
+ square-root Vélu algorithm::
94
+
95
+ sage: E = EllipticCurve('26b1')
96
+ sage: P = E(1,0)
97
+ sage: P.order()
98
+ 7
99
+ sage: EllipticCurveHom_velusqrt(E, P)
100
+ Traceback (most recent call last):
101
+ ...
102
+ NotImplementedError: only implemented for elliptic curves over finite fields
103
+
104
+ .. NOTE::
105
+
106
+ Some of the methods inherited from :class:`EllipticCurveHom` compute data
107
+ whose size is linear in the degree; this includes kernel polynomial and
108
+ rational maps. In consequence, those methods cannot possibly run in the
109
+ otherwise advertised square-root complexity, as merely storing the result
110
+ already takes linear time.
111
+
112
+ AUTHORS:
113
+
114
+ - Lorenz Panny (2022)
115
+ """
116
+
117
+ # ****************************************************************************
118
+ # Copyright (C) 2022 Lorenz Panny
119
+ #
120
+ # This program is free software: you can redistribute it and/or modify
121
+ # it under the terms of the GNU General Public License as published by
122
+ # the Free Software Foundation, either version 2 of the License, or
123
+ # (at your option) any later version.
124
+ # https://www.gnu.org/licenses/
125
+ # ****************************************************************************
126
+
127
+ from sage.misc.cachefunc import cached_method
128
+ from sage.misc.misc_c import prod
129
+ from sage.rings.generic import ProductTree, prod_with_derivative
130
+ from sage.rings.integer import Integer
131
+ from sage.structure.all import coercion_model as cm
132
+ from sage.structure.richcmp import op_EQ
133
+ from sage.structure.sequence import Sequence
134
+
135
+ from .constructor import EllipticCurve
136
+ from .ell_finite_field import EllipticCurve_finite_field
137
+ from .hom import EllipticCurveHom, compare_via_evaluation
138
+
139
+
140
+ class _VeluBoundObj:
141
+ """
142
+ Helper object to define the point in which isogeny
143
+ computation should start using square-roor Velu formulae
144
+ instead of Velu.
145
+
146
+ EXAMPLES ::
147
+
148
+ sage: from sage.schemes.elliptic_curves.hom_velusqrt import _velu_sqrt_bound
149
+ sage: _velu_sqrt_bound.get()
150
+ 1000
151
+ sage: _velu_sqrt_bound.set(50)
152
+ sage: _velu_sqrt_bound.get()
153
+ 50
154
+ """
155
+ def __init__(self):
156
+ self.bound = Integer(1000)
157
+
158
+ def set(self, b):
159
+ self.bound = b
160
+
161
+ def get(self):
162
+ return self.bound
163
+
164
+ def __repr__(self):
165
+ return f"VeluSqrtBound Object with bound = {self.bound}"
166
+
167
+
168
+ _velu_sqrt_bound = _VeluBoundObj()
169
+
170
+
171
+ def _choose_IJK(n):
172
+ r"""
173
+ Helper function to choose an "index system" for the set
174
+ `\{1,3,5,7,...,n-2\}` where `n \geq 5` is an odd integer.
175
+
176
+ INPUT:
177
+
178
+ - ``n`` -- odd :class:`~sage.rings.integer.Integer` `\geq 5`
179
+
180
+ REFERENCES: [BDLS2020]_, Examples 4.7 and 4.12
181
+
182
+ EXAMPLES::
183
+
184
+ sage: from sage.schemes.elliptic_curves.hom_velusqrt import _choose_IJK
185
+ sage: IJK = _choose_IJK(101); IJK
186
+ (range(10, 91, 20), range(1, 10, 2), range(101, 101, 2))
187
+ sage: I,J,K = IJK
188
+ sage: sorted([i + s*j for i in iter(I) for j in iter(J) for s in (+1,-1)] + list(iter(K)))
189
+ [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51,
190
+ 53, 55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95, 97, 99]
191
+
192
+ TESTS::
193
+
194
+ sage: for n in range(5,1000,2):
195
+ ....: I,J,K = _choose_IJK(ZZ(n))
196
+ ....: assert sorted([i + s*j for i in iter(I) for j in iter(J) for s in (+1,-1)] + list(iter(K))) == sorted(range(1,n,2))
197
+ """
198
+ if n % 2 != 1 or n < 5:
199
+ raise ValueError('n must be odd and >= 5')
200
+ b = (n-1).isqrt() // 2
201
+ c = (n-1) // (4*b)
202
+ I = range(2*b, 2*b*(2*c-1)+1, 4*b)
203
+ J = range(1, 2*b, 2)
204
+ K = range(4*b*c+1, n, 2)
205
+ return I, J, K
206
+
207
+
208
+ def _points_range(rr, P, Q=None):
209
+ r"""
210
+ Return an iterator yielding all points `Q + [i]P` where `i` runs
211
+ through the :class:`range` object ``rr``.
212
+
213
+ INPUT:
214
+
215
+ - ``rr`` -- :class:`range` object defining a sequence `S \subseteq \ZZ`
216
+ - ``P`` -- element of an additive abelian group
217
+ - ``Q`` -- element of the same group, or ``None``
218
+
219
+ EXAMPLES::
220
+
221
+ sage: from sage.schemes.elliptic_curves.hom_velusqrt import _points_range
222
+ sage: E = EllipticCurve(GF(1123), [4,5])
223
+ sage: P = E(1, 75)
224
+ sage: 2*P
225
+ (1038 : 498 : 1)
226
+ sage: 5*P
227
+ (236 : 598 : 1)
228
+ sage: 8*P
229
+ (717 : 530 : 1)
230
+ sage: list(_points_range(range(2,10,3), P))
231
+ [(1038 : 498 : 1), (236 : 598 : 1), (717 : 530 : 1)]
232
+ sage: Q = E(7, 202)
233
+ sage: Q + 2*P
234
+ (65 : 717 : 1)
235
+ sage: Q + 5*P
236
+ (1119 : 788 : 1)
237
+ sage: Q + 8*P
238
+ (949 : 315 : 1)
239
+ sage: list(_points_range(range(2,10,3), P, Q))
240
+ [(65 : 717 : 1), (1119 : 788 : 1), (949 : 315 : 1)]
241
+ """
242
+ if not rr:
243
+ return
244
+ a,b,s = rr.start, rr.stop, rr.step
245
+ R = a*P if Q is None else Q + a*P
246
+ yield R
247
+ sP = s*P
248
+ for _ in range(a+s, b, s):
249
+ yield (R := R + sP)
250
+
251
+
252
+ class FastEllipticPolynomial:
253
+ r"""
254
+ A class to represent and evaluate an *elliptic polynomial*,
255
+ and optionally its derivative, in essentially square-root time.
256
+
257
+ The elliptic polynomials computed by this class are of the form
258
+
259
+ .. MATH::
260
+
261
+ h_S(Z) = \prod_{i\in S} (Z - x(Q + [i]P))
262
+
263
+ where `P` is a point of odd order `n \geq 5` and `Q` is either ``None``,
264
+ in which case it is assumed to be `\infty`, or an arbitrary point which is
265
+ not a multiple of `P`.
266
+
267
+ The index set `S` is chosen as follows:
268
+
269
+ - If `Q` is given, then `S = \{0,1,2,3,...,n-1\}`.
270
+
271
+ - If `Q` is omitted, then `S = \{1,3,5,...,n-2\}`. Note that in this case,
272
+ `h_{\{1,2,3,...,n-1\}}` can be computed as `h_S^2` since `n` is odd.
273
+
274
+ INPUT:
275
+
276
+ - ``E`` -- an elliptic curve in short Weierstraß form
277
+ - ``n`` -- an odd integer `\geq 5`
278
+ - ``P`` -- a point on `E`
279
+ - ``Q`` -- a point on `E`, or ``None``
280
+
281
+ ALGORITHM: [BDLS2020]_, Algorithm 2
282
+
283
+ .. NOTE::
284
+
285
+ Currently only implemented for short Weierstraß curves.
286
+
287
+ EXAMPLES::
288
+
289
+ sage: from sage.schemes.elliptic_curves.hom_velusqrt import FastEllipticPolynomial
290
+ sage: E = EllipticCurve(GF(71), [5,5])
291
+ sage: P = E(4, 35)
292
+ sage: hP = FastEllipticPolynomial(E, P.order(), P); hP
293
+ Fast elliptic polynomial prod(Z - x(i*P) for i in range(1,n,2)) with n = 19, P = (4 : 35 : 1)
294
+ sage: hP(7)
295
+ 19
296
+ sage: prod(7 - (i*P).x() for i in range(1,P.order(),2))
297
+ 19
298
+
299
+ Passing `Q` changes the index set::
300
+
301
+ sage: Q = E(0, 17)
302
+ sage: hPQ = FastEllipticPolynomial(E, P.order(), P, Q)
303
+ sage: hPQ(7)
304
+ 58
305
+ sage: prod(7 - (Q+i*P).x() for i in range(P.order()))
306
+ 58
307
+
308
+ The call syntax has an optional keyword argument ``derivative``, which
309
+ will make the function return the pair `(h_S(\alpha), h_S'(\alpha))`
310
+ instead of just `h_S(\alpha)`::
311
+
312
+ sage: hP(7, derivative=True)
313
+ (19, 15)
314
+ sage: R.<Z> = E.base_field()[]
315
+ sage: HP = prod(Z - (i*P).x() for i in range(1,P.order(),2))
316
+ sage: HP
317
+ Z^9 + 16*Z^8 + 57*Z^7 + 6*Z^6 + 45*Z^5 + 31*Z^4 + 46*Z^3 + 10*Z^2 + 28*Z + 41
318
+ sage: HP(7)
319
+ 19
320
+ sage: HP.derivative()(7)
321
+ 15
322
+
323
+ ::
324
+
325
+ sage: hPQ(7, derivative=True)
326
+ (58, 62)
327
+ sage: R.<Z> = E.base_field()[]
328
+ sage: HPQ = prod(Z - (Q+i*P).x() for i in range(P.order()))
329
+ sage: HPQ
330
+ Z^19 + 53*Z^18 + 67*Z^17 + 39*Z^16 + 56*Z^15 + 32*Z^14 + 44*Z^13 + 6*Z^12 + 27*Z^11 + 29*Z^10 + 38*Z^9 + 48*Z^8 + 38*Z^7 + 43*Z^6 + 21*Z^5 + 25*Z^4 + 33*Z^3 + 49*Z^2 + 60*Z
331
+ sage: HPQ(7)
332
+ 58
333
+ sage: HPQ.derivative()(7)
334
+ 62
335
+
336
+ The input can be an element of any algebra over the base ring::
337
+
338
+ sage: R.<T> = GF(71)[]
339
+ sage: S.<t> = R.quotient(T^2)
340
+ sage: hP(7 + t)
341
+ 15*t + 19
342
+ """
343
+ def __init__(self, E, n, P, Q=None):
344
+ r"""
345
+ Initialize this elliptic polynomial and precompute some
346
+ input-independent data required for evaluation.
347
+
348
+ EXAMPLES::
349
+
350
+ sage: from sage.schemes.elliptic_curves.hom_velusqrt import FastEllipticPolynomial
351
+ sage: E = EllipticCurve(GF(71), [5,5])
352
+ sage: P = E(0, 17)
353
+ sage: FastEllipticPolynomial(E, P.order(), P)
354
+ Fast elliptic polynomial prod(Z - x(i*P) for i in range(1,n,2)) with n = 57, P = (0 : 17 : 1)
355
+ """
356
+ if any(E.a_invariants()[:-2]):
357
+ raise NotImplementedError('only implemented for short Weierstrass curves')
358
+
359
+ n = Integer(n)
360
+
361
+ if Q is None:
362
+ IJK = _choose_IJK(n) # [1,3,5,7,...,n-4,n-2]
363
+ else:
364
+ IJK = _choose_IJK(2*n+1) # [1,3,5,7,...,2n-1] = [0,1,2,3,...,n-2,n-1]
365
+
366
+ self.base = E.base_ring()
367
+ R, Z = self.base['Z'].objgen()
368
+
369
+ # Cassels, Lectures on Elliptic Curves, p.132
370
+ A,B = E.a_invariants()[-2:]
371
+ Fs = lambda X,Y: (
372
+ (X - Y)**2,
373
+ -2 * (X*Y + A) * (X + Y) - 4*B,
374
+ (X*Y - A)**2 - 4*B*(X+Y),
375
+ )
376
+
377
+ I, J, K = IJK
378
+ xI = (R.x() for R in _points_range(I, P, Q))
379
+ xJ = [R.x() for R in _points_range(J, P )]
380
+ xK = (R.x() for R in _points_range(K, P, Q))
381
+
382
+ self.hItree = ProductTree(Z - xi for xi in xI)
383
+
384
+ self.EJparts = [Fs(Z,xj) for xj in xJ]
385
+
386
+ DJ = prod(F0j for F0j,_,_ in self.EJparts)
387
+ self.DeltaIJ = self._hI_resultant(DJ)
388
+
389
+ self.hK = R(prod(Z - xk for xk in xK))
390
+ self.dhK = self.hK.derivative()
391
+
392
+ if Q is None:
393
+ self._repr = f"Fast elliptic polynomial prod(Z - x(i*P) for i in range(1,n,2)) with {n = }, {P = }"
394
+ else:
395
+ self._repr = f"Fast elliptic polynomial prod(Z - x(Q+i*P) for i in range(n)) with {n = }, {P = }, {Q = }"
396
+
397
+ def __call__(self, alpha, *, derivative=False):
398
+ r"""
399
+ Evaluate this elliptic polynomial at a point `\alpha`,
400
+ and if ``derivative`` is set to ``True`` also return
401
+ the evaluation of the derivative at `\alpha`.
402
+
403
+ INPUT:
404
+
405
+ - ``alpha`` -- an element of any algebra over the base ring
406
+ - ``derivative`` -- boolean (default: ``False``)
407
+
408
+ EXAMPLES::
409
+
410
+ sage: from sage.schemes.elliptic_curves.hom_velusqrt import FastEllipticPolynomial
411
+ sage: E = EllipticCurve(GF(71), [5,5])
412
+ sage: P = E(4, 35)
413
+ sage: hP = FastEllipticPolynomial(E, P.order(), P); hP
414
+ Fast elliptic polynomial prod(Z - x(i*P) for i in range(1,n,2)) with n = 19, P = (4 : 35 : 1)
415
+ sage: hP(7)
416
+ 19
417
+ sage: hP(7, derivative=True)
418
+ (19, 15)
419
+ """
420
+ base = cm.common_parent(self.base, alpha)
421
+
422
+ EJparts = [tuple(F.base_extend(base) for F in part) for part in self.EJparts]
423
+
424
+ EJfacs = [(F0j * alpha + F1j) * alpha + F2j for F0j,F1j,F2j in EJparts]
425
+ if not derivative:
426
+ EJ = prod(EJfacs)
427
+ else:
428
+ dEJfacs = [2 * F0j * alpha + F1j for F0j,F1j,_ in EJparts]
429
+ EJ, dEJ = prod_with_derivative(zip(EJfacs, dEJfacs))
430
+
431
+ EJrems = self.hItree.remainders(EJ)
432
+ R = self._hI_resultant(EJ, EJrems)
433
+ hK = self.hK(alpha)
434
+ res = hK * R / self.DeltaIJ
435
+ res = base(res)
436
+
437
+ if not derivative:
438
+ return res
439
+
440
+ dEJrems = self.hItree.remainders(dEJ)
441
+ cnt = EJrems.count(0)
442
+ if cnt == 0:
443
+ dR = sum(R // EJrem * dEJrem for EJrem, dEJrem in zip(EJrems, dEJrems))
444
+ elif cnt == 1:
445
+ dR = prod(EJrem or dEJrem for EJrem, dEJrem in zip(EJrems, dEJrems))
446
+ else:
447
+ dR = 0
448
+ dhK = self.dhK(alpha)
449
+ dres = (dhK * R + hK * dR) / self.DeltaIJ
450
+ dres = base(dres)
451
+
452
+ return res, dres
453
+
454
+ def _hI_resultant(self, poly, rems=None):
455
+ r"""
456
+ Internal helper function to evaluate a resultant with `h_I` quickly,
457
+ using the product tree constructed in :meth:`__init__`.
458
+
459
+ INPUT:
460
+
461
+ - ``poly`` -- an element of the base ring of this product tree,
462
+ which must be a polynomial ring supporting ``%``
463
+ - ``rems`` -- result of ``self.hItree.remainders(poly)``, or ``None``
464
+
465
+ EXAMPLES::
466
+
467
+ sage: from sage.schemes.elliptic_curves.hom_velusqrt import FastEllipticPolynomial
468
+ sage: E = EllipticCurve(GF(71), [5,5])
469
+ sage: P = E(4, 35)
470
+ sage: hP = FastEllipticPolynomial(E, P.order(), P)
471
+ sage: f = GF(71)['Z']([5,4,3,2,1])
472
+ sage: hP._hI_resultant(f)
473
+ 66
474
+ sage: prod(f(r) for fi in hP.hItree.layers[0]
475
+ ....: for r in fi.roots(multiplicities=False))
476
+ 66
477
+
478
+ ::
479
+
480
+ sage: Q = E(0, 17)
481
+ sage: hPQ = FastEllipticPolynomial(E, P.order(), P, Q)
482
+ sage: f = GF(71)['Z']([9,8,7,6,5,4,3,2,1])
483
+ sage: hPQ._hI_resultant(f)
484
+ 36
485
+ sage: prod(f(r) for fi in hPQ.hItree.layers[0]
486
+ ....: for r in fi.roots(multiplicities=False))
487
+ 36
488
+ """
489
+ if rems is None:
490
+ rems = self.hItree.remainders(poly)
491
+ r = prod(rems)
492
+ s = -1 if len(self.hItree) % 2 == 1 == poly.degree() else 1
493
+ assert r.is_constant()
494
+ return s * r[0]
495
+
496
+ def __repr__(self):
497
+ r"""
498
+ Return a string representation of this elliptic polynomial.
499
+
500
+ EXAMPLES::
501
+
502
+ sage: from sage.schemes.elliptic_curves.hom_velusqrt import FastEllipticPolynomial
503
+ sage: E = EllipticCurve(GF(71), [5,5])
504
+ sage: P = E(4, 35)
505
+ sage: FastEllipticPolynomial(E, P.order(), P)
506
+ Fast elliptic polynomial prod(Z - x(i*P) for i in range(1,n,2)) with n = 19, P = (4 : 35 : 1)
507
+ sage: Q = E(0, 17)
508
+ sage: FastEllipticPolynomial(E, P.order(), P, Q)
509
+ Fast elliptic polynomial prod(Z - x(Q+i*P) for i in range(n)) with n = 19, P = (4 : 35 : 1), Q = (0 : 17 : 1)
510
+ """
511
+ return self._repr
512
+
513
+
514
+ def _point_outside_subgroup(P):
515
+ r"""
516
+ Simple helper function to return a point on an elliptic
517
+ curve `E` that is not a multiple of a given point `P`.
518
+ The base field is extended if (and only if) necessary.
519
+
520
+ INPUT:
521
+
522
+ - ``P`` -- a point on an elliptic curve over a finite field
523
+
524
+ EXAMPLES::
525
+
526
+ sage: from sage.schemes.elliptic_curves.hom_velusqrt import _point_outside_subgroup
527
+ sage: E = EllipticCurve(GF(71), [5,5])
528
+ sage: P = E(4, 35)
529
+ sage: Q = _point_outside_subgroup(P); Q # random
530
+ (14 : 11 : 1)
531
+ sage: Q.log(P)
532
+ Traceback (most recent call last):
533
+ ...
534
+ ValueError: ECDLog problem has no solution (...)
535
+
536
+ An example where `P` generates `E(\mathbb F_q)`::
537
+
538
+ sage: E.<P> = EllipticCurve(GF(71), [5,5])
539
+ sage: P.order() == E.cardinality()
540
+ True
541
+ sage: Q = _point_outside_subgroup(P); Q # random
542
+ (35*z2 + 7 : 24*z2 + 7 : 1)
543
+ sage: Q.log(Q.curve()(P))
544
+ Traceback (most recent call last):
545
+ ...
546
+ ValueError: ECDLog problem has no solution (...)
547
+
548
+ An example where the group is non-cyclic::
549
+
550
+ sage: E.<P,_> = EllipticCurve(GF(71^2), [0,1])
551
+ sage: E.abelian_group()
552
+ Additive abelian group isomorphic to Z/72 + Z/72 embedded in Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in z2 of size 71^2
553
+ sage: P = E.random_point()
554
+ sage: Q = _point_outside_subgroup(P); Q # random
555
+ (18*z2 + 46 : 58*z2 + 61 : 1)
556
+ sage: Q in E
557
+ True
558
+ sage: Q.log(P)
559
+ Traceback (most recent call last):
560
+ ...
561
+ ValueError: ECDLog problem has no solution (...)
562
+
563
+ .. NOTE::
564
+
565
+ The field extension is only needed when `P` generates the
566
+ entire rational subgroup of `E`. But in that case, the
567
+ isogeny defined by `P` is simply `\pi-1` (where `\pi` is
568
+ Frobenius). Thus, once `\pi-1` can be represented in Sage,
569
+ we may just return that in
570
+ :meth:`~sage.schemes.elliptic_curves.ell_field.EllipticCurve_field.isogeny`
571
+ rather than insisting on using square-root Vélu.
572
+ """
573
+ E = P.curve()
574
+ n = P.order()
575
+ if n == E.order():
576
+ d = 2 + (n == 7 and E.base_field().cardinality() == 3)
577
+ F = E.base_field().extension(d)
578
+ E = E.base_extend(F)
579
+ P = E(P)
580
+ # assert E.cardinality() > n
581
+ for _ in range(1000):
582
+ Q = E.random_point()
583
+ if n*Q or not P.weil_pairing(Q,n).is_one():
584
+ return Q
585
+ raise NotImplementedError('could not find a point outside the kernel')
586
+
587
+
588
+ class EllipticCurveHom_velusqrt(EllipticCurveHom):
589
+ r"""
590
+ This class implements separable odd-degree isogenies of elliptic
591
+ curves over finite fields using the square-root Vélu algorithm.
592
+
593
+ The complexity is `\tilde O(\sqrt{\ell})` base-field operations,
594
+ where `\ell` is the degree.
595
+
596
+ REFERENCES: [BDLS2020]_
597
+
598
+ INPUT:
599
+
600
+ - ``E`` -- an elliptic curve over a finite field
601
+ - ``P`` -- a point on `E` of odd order `\geq 9`
602
+ - ``codomain`` -- codomain elliptic curve (optional)
603
+ - ``model`` -- string (optional); input to
604
+ :meth:`~sage.schemes.elliptic_curves.ell_field.compute_model`
605
+ - ``Q`` -- a point on `E` outside `\langle P\rangle`, or ``None``
606
+
607
+ EXAMPLES::
608
+
609
+ sage: from sage.schemes.elliptic_curves.hom_velusqrt import EllipticCurveHom_velusqrt
610
+ sage: F.<t> = GF(10009^3)
611
+ sage: E = EllipticCurve(F, [t,t])
612
+ sage: K = E(2154*t^2 + 5711*t + 2899, 7340*t^2 + 4653*t + 6935)
613
+ sage: phi = EllipticCurveHom_velusqrt(E, K); phi
614
+ Elliptic-curve isogeny (using square-root Vélu) of degree 601:
615
+ From: Elliptic Curve defined by y^2 = x^3 + t*x + t over Finite Field in t of size 10009^3
616
+ To: Elliptic Curve defined by y^2 = x^3 + (263*t^2+3173*t+4759)*x + (3898*t^2+6111*t+9443) over Finite Field in t of size 10009^3
617
+ sage: phi(K)
618
+ (0 : 1 : 0)
619
+ sage: P = E(2, 3163*t^2 + 7293*t + 5999)
620
+ sage: phi(P)
621
+ (6085*t^2 + 855*t + 8720 : 8078*t^2 + 9889*t + 6030 : 1)
622
+ sage: Q = E(6, 5575*t^2 + 6607*t + 9991)
623
+ sage: phi(Q)
624
+ (626*t^2 + 9749*t + 1291 : 5931*t^2 + 8549*t + 3111 : 1)
625
+ sage: phi(P + Q)
626
+ (983*t^2 + 4894*t + 4072 : 5047*t^2 + 9325*t + 336 : 1)
627
+ sage: phi(P) + phi(Q)
628
+ (983*t^2 + 4894*t + 4072 : 5047*t^2 + 9325*t + 336 : 1)
629
+
630
+ TESTS:
631
+
632
+ Check on a random example that the isogeny is a well-defined
633
+ group homomorphism with the correct kernel::
634
+
635
+ sage: from sage.schemes.elliptic_curves.hom_velusqrt import _random_example_for_testing
636
+ sage: E, K = _random_example_for_testing()
637
+ sage: phi = EllipticCurveHom_velusqrt(E, K)
638
+ sage: not phi(K)
639
+ True
640
+ sage: not phi(randrange(2^99) * K)
641
+ True
642
+ sage: P = E.random_point()
643
+ sage: phi(P) in phi.codomain()
644
+ True
645
+ sage: Q = E.random_point()
646
+ sage: phi(Q) in phi.codomain()
647
+ True
648
+ sage: phi(P + Q) == phi(P) + phi(Q)
649
+ True
650
+
651
+ Check that the isogeny preserves the field of definition::
652
+
653
+ sage: Sequence(K).universe() == phi.domain().base_field()
654
+ True
655
+ sage: phi.codomain().base_field() == phi.domain().base_field()
656
+ True
657
+
658
+ Check that the isogeny affects the Weil pairing in the correct way::
659
+
660
+ sage: m = lcm(P.order(), Q.order())
661
+ sage: e1 = P.weil_pairing(Q, m)
662
+ sage: e2 = phi(P).weil_pairing(phi(Q), m)
663
+ sage: e2 == e1^phi.degree()
664
+ True
665
+
666
+ Check that the isogeny matches (up to isomorphism) the one from
667
+ :class:`~sage.schemes.elliptic_curves.ell_curve_isogeny.EllipticCurveIsogeny`::
668
+
669
+ sage: psi = EllipticCurveIsogeny(E, K)
670
+ sage: check = lambda iso: all(iso(psi(Q)) == phi(Q) for Q in E.gens())
671
+ sage: any(map(check, psi.codomain().isomorphisms(phi.codomain())))
672
+ True
673
+
674
+ .. SEEALSO::
675
+
676
+ :class:`~sage.schemes.elliptic_curves.ell_curve_isogeny.EllipticCurveIsogeny`
677
+ """
678
+ def __init__(self, E, P, *, codomain=None, model=None, Q=None):
679
+ r"""
680
+ Initialize this square-root Vélu isogeny from a kernel point of odd order.
681
+
682
+ EXAMPLES::
683
+
684
+ sage: from sage.schemes.elliptic_curves.hom_velusqrt import EllipticCurveHom_velusqrt
685
+ sage: E = EllipticCurve(GF(71), [5,5])
686
+ sage: P = E(-2, 22)
687
+ sage: EllipticCurveHom_velusqrt(E, P)
688
+ Elliptic-curve isogeny (using square-root Vélu) of degree 19:
689
+ From: Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Finite Field of size 71
690
+ To: Elliptic Curve defined by y^2 = x^3 + 13*x + 11 over Finite Field of size 71
691
+
692
+ ::
693
+
694
+ sage: E.<P> = EllipticCurve(GF(419), [1,0])
695
+ sage: K = 4*P
696
+ sage: EllipticCurveHom_velusqrt(E, K)
697
+ Elliptic-curve isogeny (using square-root Vélu) of degree 105:
698
+ From: Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 419
699
+ To: Elliptic Curve defined by y^2 = x^3 + 301*x + 86 over Finite Field of size 419
700
+ sage: E2 = EllipticCurve(GF(419), [0,6,0,385,42])
701
+ sage: EllipticCurveHom_velusqrt(E, K, codomain=E2)
702
+ Elliptic-curve isogeny (using square-root Vélu) of degree 105:
703
+ From: Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 419
704
+ To: Elliptic Curve defined by y^2 = x^3 + 6*x^2 + 385*x + 42 over Finite Field of size 419
705
+ sage: EllipticCurveHom_velusqrt(E, K, model='montgomery')
706
+ Elliptic-curve isogeny (using square-root Vélu) of degree 105:
707
+ From: Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 419
708
+ To: Elliptic Curve defined by y^2 = x^3 + 6*x^2 + x over Finite Field of size 419
709
+
710
+ Note that the implementation in fact also works in almost all
711
+ cases when the degree is `5` or `7`. The reason we restrict to
712
+ degrees `\geq 9` is that (only!) when trying to compute a
713
+ `7`-isogeny from a rational point on an elliptic curve defined
714
+ over `\GF{3}`, the point `Q` required in the formulas has to be
715
+ defined over a cubic extension rather than an at most quadratic
716
+ extension, which can result in the constructed isogeny being
717
+ irrational. See :issue:`34467`. The assertion in the following
718
+ example currently fails if the minimum degree is lowered::
719
+
720
+ sage: E = EllipticCurve(GF(3), [2,1])
721
+ sage: P, = E.gens()
722
+ sage: P.order()
723
+ 7
724
+ sage: psi = E.isogeny(P)
725
+ sage: phi = E.isogeny(P, algorithm='velusqrt') # not tested
726
+ sage: phi._Q.base_ring() # not tested
727
+ Finite Field in z3 of size 3^3
728
+ sage: assert phi.codomain().is_isomorphic(psi.codomain()) # not tested
729
+ """
730
+ if not isinstance(E, EllipticCurve_finite_field):
731
+ raise NotImplementedError('only implemented for elliptic curves over finite fields')
732
+
733
+ if codomain is not None and model is not None:
734
+ raise ValueError('cannot specify a codomain curve and model name simultaneously')
735
+
736
+ try:
737
+ P = E(P)
738
+ except TypeError:
739
+ raise ValueError('given kernel point P does not lie on E')
740
+ self._degree = P.order()
741
+ if self._degree % 2 != 1 or self._degree < 9:
742
+ raise NotImplementedError('only implemented for odd degrees >= 9')
743
+
744
+ try:
745
+ self._raw_domain = E.short_weierstrass_model()
746
+ except ValueError:
747
+ raise NotImplementedError('only implemented for curves having a short Weierstrass model')
748
+ self._pre_iso = E.isomorphism_to(self._raw_domain)
749
+ self._P = self._pre_iso(P)
750
+
751
+ if Q is not None:
752
+ self._Q = self._pre_iso(E(Q))
753
+ EE = E
754
+ else:
755
+ self._Q = _point_outside_subgroup(self._P) # may extend base field
756
+ EE = self._Q.curve()
757
+ self._P = EE(self._P)
758
+
759
+ self._internal_base_ring = EE.base_ring()
760
+
761
+ self._h0 = FastEllipticPolynomial(EE, self._degree, self._P)
762
+ self._h1 = FastEllipticPolynomial(EE, self._degree, self._P, self._Q)
763
+
764
+ self._domain = E
765
+ self._compute_codomain(model=model)
766
+
767
+ if codomain is not None:
768
+ self._post_iso = self._codomain.isomorphism_to(codomain) * self._post_iso
769
+ self._codomain = codomain
770
+
771
+ super().__init__(self._domain, self._codomain)
772
+
773
+ def _raw_eval(self, x, y=None):
774
+ r"""
775
+ Evaluate the "inner" square-root Vélu isogeny
776
+ (i.e., without applying pre- and post-isomorphism)
777
+ at either just an `x`-coordinate or a pair
778
+ `(x,y)` of coordinates.
779
+
780
+ If the given point lies in the kernel, the empty tuple
781
+ ``()`` is returned.
782
+
783
+ No checking of the input coordinates is performed.
784
+
785
+ ALGORITHM:
786
+
787
+ - [Ren2018]_, Theorem 1
788
+ - :class:`FastEllipticPolynomial`
789
+
790
+ EXAMPLES::
791
+
792
+ sage: from sage.schemes.elliptic_curves.hom_velusqrt import EllipticCurveHom_velusqrt
793
+ sage: E = EllipticCurve(GF(65537), [1,1])
794
+ sage: P = E(2112, 803)
795
+ sage: phi = EllipticCurveHom_velusqrt(E, P, Q=(32924,0))
796
+ sage: phi._raw_domain is E
797
+ True
798
+ sage: phi._raw_codomain
799
+ Elliptic Curve defined by y^2 = x^3 + ... over Finite Field of size 65537
800
+ sage: Q = E(42, 15860)
801
+ sage: phi._raw_eval(Q.x())
802
+ 11958
803
+ sage: phi._raw_eval(*Q.xy())
804
+ (11958, 42770)
805
+ sage: phi._raw_codomain.defining_polynomial()(*phi._raw_eval(*Q.xy()), 1)
806
+ 0
807
+
808
+ No checking is performed::
809
+
810
+ sage: E.defining_polynomial()(123, 456, 1)
811
+ 50907
812
+ sage: phi._raw_eval(123, 456)
813
+ (3805, 29941)
814
+
815
+ TESTS::
816
+
817
+ sage: {t.parent() for t in phi._raw_eval(*Q.xy())}
818
+ {Finite Field of size 65537}
819
+ sage: {t.parent() for t in phi._raw_eval(123, 456)}
820
+ {Finite Field of size 65537}
821
+ """
822
+ if y is None:
823
+ h0 = self._h0(x)
824
+ h1 = self._h1(x)
825
+ else:
826
+ h0, h0d = self._h0(x, derivative=True)
827
+ h1, h1d = self._h1(x, derivative=True)
828
+
829
+ # assert h0 == prod(x - ( i*self._P).x() for i in range(1,self._P.order(),2))
830
+ # assert h1 == prod(x - (self._Q+i*self._P).x() for i in range( self._P.order() ))
831
+
832
+ if not h0:
833
+ return ()
834
+
835
+ xx = h1 / h0**2
836
+
837
+ if y is None:
838
+ return xx
839
+
840
+ # assert h0d == sum(prod(x - ( i*self._P).x() for i in range(1,self._P.order(),2) if i!=j) for j in range(1,self._P.order(),2))
841
+ # assert h1d == sum(prod(x - (self._Q+i*self._P).x() for i in range( self._P.order() ) if i!=j) for j in range( self._P.order() ))
842
+
843
+ yy = y * (h1d - 2 * h1 / h0 * h0d) / h0**2
844
+
845
+ return xx, yy
846
+
847
+ def _compute_codomain(self, model=None):
848
+ r"""
849
+ Helper method to compute the codomain of this
850
+ square-root Vélu isogeny
851
+ once the data for :meth:`_raw_eval` has been initialized.
852
+
853
+ Called by the constructor.
854
+
855
+ INPUT:
856
+
857
+ - ``model`` -- string (optional); input to
858
+ :meth:`~sage.schemes.elliptic_curves.ell_field.compute_model`
859
+
860
+ EXAMPLES::
861
+
862
+ sage: from sage.schemes.elliptic_curves.hom_velusqrt import EllipticCurveHom_velusqrt
863
+ sage: E = EllipticCurve(GF(71), [0,5,0,1,0])
864
+ sage: P = E(4, 19)
865
+ sage: phi = EllipticCurveHom_velusqrt(E, P)
866
+ sage: phi._raw_codomain
867
+ Elliptic Curve defined by y^2 = x^3 + ... over Finite Field of size 71
868
+ sage: phi._codomain
869
+ Elliptic Curve defined by y^2 = x^3 + 8*x + 34 over Finite Field of size 71
870
+ sage: phi.codomain()
871
+ Elliptic Curve defined by y^2 = x^3 + 8*x + 34 over Finite Field of size 71
872
+
873
+ Passing a ``model`` parameter is supported::
874
+
875
+ sage: phi._compute_codomain('montgomery')
876
+ sage: phi
877
+ Elliptic-curve isogeny (using square-root Vélu) of degree 19:
878
+ From: Elliptic Curve defined by y^2 = x^3 + 5*x^2 + x over Finite Field of size 71
879
+ To: Elliptic Curve defined by y^2 = x^3 + 40*x^2 + x over Finite Field of size 71
880
+
881
+ TESTS::
882
+
883
+ sage: F.<t> = GF(5^2)
884
+ sage: E = EllipticCurve([3*t, 2*t+4, 3*t+2, t+4, 3*t])
885
+ sage: K = E(3*t, 2)
886
+ sage: EllipticCurveHom_velusqrt(E, K) # indirect doctest
887
+ Elliptic-curve isogeny (using square-root Vélu) of degree 19:
888
+ From: Elliptic Curve defined by y^2 + 3*t*x*y + (3*t+2)*y = x^3 + (2*t+4)*x^2 + (t+4)*x + 3*t over Finite Field in t of size 5^2
889
+ To: Elliptic Curve defined by y^2 = x^3 + (4*t+3)*x + 2 over Finite Field in t of size 5^2
890
+ """
891
+ R, Z = self._internal_base_ring['Z'].objgen()
892
+ poly = self._raw_domain.two_division_polynomial().monic()(Z)
893
+
894
+ f = 1
895
+ for g,_ in poly.factor():
896
+ if g.degree() == 1:
897
+ f *= Z - self._raw_eval(-g[0])
898
+ else:
899
+ K, X0 = self._internal_base_ring.extension(g,'T').objgen()
900
+ imX0 = self._raw_eval(X0)
901
+ try:
902
+ imX0 = imX0.polynomial() # K is a FiniteField
903
+ except AttributeError:
904
+ imX0 = imX0.lift() # K is a PolynomialQuotientRing
905
+ V = R['V'].gen()
906
+ f *= (Z - imX0(V)).resultant(g(V))
907
+
908
+ a6,a4,a2,_ = f.monic().list()
909
+
910
+ self._raw_codomain = EllipticCurve(self._domain.base_ring(), [0,a2,0,a4,a6])
911
+
912
+ if model is None:
913
+ model = 'short_weierstrass'
914
+
915
+ from sage.schemes.elliptic_curves.ell_field import compute_model
916
+ self._codomain = compute_model(self._raw_codomain, model)
917
+ self._post_iso = self._raw_codomain.isomorphism_to(self._codomain)
918
+
919
+ def _eval(self, P):
920
+ r"""
921
+ Evaluate this square-root Vélu isogeny at a point.
922
+
923
+ INPUT:
924
+
925
+ - ``P`` -- point on the domain, defined over any algebra over the base field
926
+
927
+ EXAMPLES::
928
+
929
+ sage: from sage.schemes.elliptic_curves.hom_velusqrt import EllipticCurveHom_velusqrt
930
+ sage: E = EllipticCurve(GF(71), [0,5,0,1,0])
931
+ sage: K = E(4, 19)
932
+ sage: phi = EllipticCurveHom_velusqrt(E, K, model='montgomery')
933
+ sage: phi
934
+ Elliptic-curve isogeny (using square-root Vélu) of degree 19:
935
+ From: Elliptic Curve defined by y^2 = x^3 + 5*x^2 + x over Finite Field of size 71
936
+ To: Elliptic Curve defined by y^2 = x^3 + 40*x^2 + x over Finite Field of size 71
937
+ sage: phi(K)
938
+ (0 : 1 : 0)
939
+ sage: phi(5*K)
940
+ (0 : 1 : 0)
941
+ sage: phi(E(0))
942
+ (0 : 1 : 0)
943
+ sage: phi(E(0,0))
944
+ (0 : 0 : 1)
945
+ sage: phi(E(7,13))
946
+ (70 : 31 : 1)
947
+
948
+ TESTS::
949
+
950
+ sage: P,Q = (E.random_point() for _ in 'PQ')
951
+ sage: assert phi(P) in phi.codomain()
952
+ sage: assert phi(Q) in phi.codomain()
953
+ sage: assert phi(P + Q) == phi(P) + phi(Q)
954
+
955
+ Randomized testing::
956
+
957
+ sage: from sage.schemes.elliptic_curves.hom_velusqrt import EllipticCurveHom_velusqrt
958
+ sage: from sage.schemes.elliptic_curves.hom_velusqrt import _random_example_for_testing
959
+ sage: E, K = _random_example_for_testing()
960
+ sage: phi = EllipticCurveHom_velusqrt(E, K)
961
+ sage: phi.degree() == K.order()
962
+ True
963
+ sage: P = E.random_point()
964
+ sage: phi(P) in phi.codomain()
965
+ True
966
+ sage: Q = E.random_point()
967
+ sage: phi(Q) in phi.codomain()
968
+ True
969
+ sage: phi(P + Q) == phi(P) + phi(Q)
970
+ True
971
+ """
972
+ if self._domain.defining_polynomial()(*P):
973
+ raise ValueError(f'{P} not on {self._domain}')
974
+
975
+ k = Sequence(P).universe()
976
+
977
+ if not P:
978
+ return self._codomain(0).change_ring(k)
979
+
980
+ P = self._pre_iso._eval(P)
981
+
982
+ xy = self._raw_eval(*P.xy())
983
+
984
+ if xy == ():
985
+ return self._codomain(0).change_ring(k)
986
+
987
+ return self._post_iso._eval(Sequence(xy, k) + [1])
988
+
989
+ _call_ = _eval
990
+
991
+ def _repr_(self):
992
+ r"""
993
+ Return basic information about this square-root Vélu isogeny as a string.
994
+
995
+ EXAMPLES::
996
+
997
+ sage: from sage.schemes.elliptic_curves.hom_velusqrt import EllipticCurveHom_velusqrt
998
+ sage: E.<P> = EllipticCurve(GF(71), [5,5])
999
+ sage: phi = EllipticCurveHom_velusqrt(E, P)
1000
+ sage: phi # indirect doctest
1001
+ Elliptic-curve isogeny (using square-root Vélu) of degree 57:
1002
+ From: Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Finite Field of size 71
1003
+ To: Elliptic Curve defined by y^2 = x^3 + 19*x + 45 over Finite Field of size 71
1004
+ """
1005
+ return f'Elliptic-curve isogeny (using square-root Vélu) of degree {self._degree}:' \
1006
+ f'\n From: {self._domain}' \
1007
+ f'\n To: {self._codomain}'
1008
+
1009
+ @staticmethod
1010
+ def _comparison_impl(left, right, op):
1011
+ r"""
1012
+ Compare a square-root Vélu isogeny to another elliptic-curve morphism.
1013
+
1014
+ Called by :meth:`EllipticCurveHom._richcmp_`.
1015
+
1016
+ INPUT:
1017
+
1018
+ - ``left``, ``right`` -- :class:`~sage.schemes.elliptic_curves.hom.EllipticCurveHom`
1019
+ objects
1020
+
1021
+ ALGORITHM:
1022
+
1023
+ :func:`~sage.schemes.elliptic_curves.hom.compare_via_evaluation`
1024
+
1025
+ EXAMPLES::
1026
+
1027
+ sage: from sage.schemes.elliptic_curves.hom_velusqrt import EllipticCurveHom_velusqrt
1028
+ sage: E = EllipticCurve(GF(101), [5,5,5,5,5])
1029
+ sage: phi = EllipticCurveHom_velusqrt(E, E.lift_x(11)); phi
1030
+ Elliptic-curve isogeny (using square-root Vélu) of degree 59:
1031
+ From: Elliptic Curve defined by y^2 + 5*x*y + 5*y = x^3 + 5*x^2 + 5*x + 5 over Finite Field of size 101
1032
+ To: Elliptic Curve defined by y^2 = x^3 + 15*x + 25 over Finite Field of size 101
1033
+ sage: psi = EllipticCurveHom_velusqrt(E, E.lift_x(-1)); psi
1034
+ Elliptic-curve isogeny (using square-root Vélu) of degree 59:
1035
+ From: Elliptic Curve defined by y^2 + 5*x*y + 5*y = x^3 + 5*x^2 + 5*x + 5 over Finite Field of size 101
1036
+ To: Elliptic Curve defined by y^2 = x^3 + 15*x + 25 over Finite Field of size 101
1037
+ sage: phi == psi # needs sage.symbolic
1038
+ True
1039
+ """
1040
+ if op != op_EQ:
1041
+ return NotImplemented
1042
+ return compare_via_evaluation(left, right)
1043
+
1044
+ @cached_method
1045
+ def kernel_polynomial(self):
1046
+ r"""
1047
+ Return the kernel polynomial of this square-root Vélu isogeny.
1048
+
1049
+ .. NOTE::
1050
+
1051
+ The data returned by this method has size linear in the degree.
1052
+
1053
+ EXAMPLES::
1054
+
1055
+ sage: E = EllipticCurve(GF(65537^2,'a'), [5,5])
1056
+ sage: K = E.cardinality()//31 * E.gens()[0]
1057
+ sage: phi = E.isogeny(K, algorithm='velusqrt')
1058
+ sage: h = phi.kernel_polynomial(); h
1059
+ x^15 + 21562*x^14 + 8571*x^13 + 20029*x^12 + 1775*x^11 + 60402*x^10 + 17481*x^9 + 46543*x^8 + 46519*x^7 + 18590*x^6 + 36554*x^5 + 36499*x^4 + 48857*x^3 + 3066*x^2 + 23264*x + 53937
1060
+ sage: h == E.isogeny(K).kernel_polynomial()
1061
+ True
1062
+ sage: h(K.x())
1063
+ 0
1064
+
1065
+ TESTS::
1066
+
1067
+ sage: phi.kernel_polynomial().parent()
1068
+ Univariate Polynomial Ring in x over Finite Field in a of size 65537^2
1069
+ """
1070
+ R, x = self._domain.base_ring()['x'].objgen()
1071
+ h0 = self._h0(x)
1072
+ h = h0(self._pre_iso.x_rational_map())
1073
+ return R(h).monic()
1074
+
1075
+ @cached_method
1076
+ def dual(self):
1077
+ r"""
1078
+ Return the dual of this square-root Vélu
1079
+ isogeny as an :class:`EllipticCurveHom`.
1080
+
1081
+ .. NOTE::
1082
+
1083
+ The dual is computed by :class:`EllipticCurveIsogeny`,
1084
+ hence it does not benefit from the square-root Vélu speedup.
1085
+
1086
+ EXAMPLES::
1087
+
1088
+ sage: E = EllipticCurve(GF(101^2), [1, 1, 1, 1, 1])
1089
+ sage: K = E.cardinality() // 11 * E.gens()[0]
1090
+ sage: phi = E.isogeny(K, algorithm='velusqrt'); phi
1091
+ Elliptic-curve isogeny (using square-root Vélu) of degree 11:
1092
+ From: Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 + x + 1
1093
+ over Finite Field in z2 of size 101^2
1094
+ To: Elliptic Curve defined by y^2 = x^3 + 39*x + 40
1095
+ over Finite Field in z2 of size 101^2
1096
+ sage: phi.dual()
1097
+ Isogeny of degree 11
1098
+ from Elliptic Curve defined by y^2 = x^3 + 39*x + 40
1099
+ over Finite Field in z2 of size 101^2
1100
+ to Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 + x + 1
1101
+ over Finite Field in z2 of size 101^2
1102
+ sage: phi.dual() * phi == phi.domain().scalar_multiplication(11) # needs sage.symbolic
1103
+ True
1104
+ sage: phi * phi.dual() == phi.codomain().scalar_multiplication(11) # needs sage.symbolic
1105
+ True
1106
+ """
1107
+ # FIXME: This code fails if the degree is divisible by the characteristic.
1108
+ F = self._raw_domain.base_ring()
1109
+ from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
1110
+ isom = ~WeierstrassIsomorphism(self._raw_domain, (~F(self._degree), 0, 0, 0))
1111
+ from sage.schemes.elliptic_curves.ell_curve_isogeny import EllipticCurveIsogeny
1112
+ phi = EllipticCurveIsogeny(self._raw_codomain, None, isom.domain(), self._degree)
1113
+ return ~self._pre_iso * isom * phi * ~self._post_iso
1114
+
1115
+ @cached_method
1116
+ def rational_maps(self):
1117
+ r"""
1118
+ Return the pair of explicit rational maps of this square-root Vélu isogeny
1119
+ as fractions of bivariate polynomials in `x` and `y`.
1120
+
1121
+ .. NOTE::
1122
+
1123
+ The data returned by this method has size linear in the degree.
1124
+
1125
+ EXAMPLES::
1126
+
1127
+ sage: E = EllipticCurve(GF(101^2), [1, 1, 1, 1, 1])
1128
+ sage: K = (E.cardinality() // 11) * E.gens()[0]
1129
+ sage: phi = E.isogeny(K, algorithm='velusqrt'); phi
1130
+ Elliptic-curve isogeny (using square-root Vélu) of degree 11:
1131
+ From: Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 + x + 1 over Finite Field in z2 of size 101^2
1132
+ To: Elliptic Curve defined by y^2 = x^3 + 39*x + 40 over Finite Field in z2 of size 101^2
1133
+ sage: phi.rational_maps()
1134
+ ((-17*x^11 - 34*x^10 - 36*x^9 + ... - 29*x^2 - 25*x - 25)/(x^10 + 10*x^9 + 19*x^8 - ... + x^2 + 47*x + 24),
1135
+ (-3*x^16 - 6*x^15*y - 48*x^15 + ... - 49*x - 9*y + 46)/(x^15 + 15*x^14 - 35*x^13 - ... + 3*x^2 - 45*x + 47))
1136
+
1137
+ TESTS::
1138
+
1139
+ sage: phi.rational_maps()[0].parent()
1140
+ Fraction Field of Multivariate Polynomial Ring in x, y over Finite Field in z2 of size 101^2
1141
+ sage: phi.rational_maps()[1].parent()
1142
+ Fraction Field of Multivariate Polynomial Ring in x, y over Finite Field in z2 of size 101^2
1143
+ """
1144
+ S = self._internal_base_ring['x,y']
1145
+ fx, fy = map(S, self._pre_iso.rational_maps())
1146
+ fx, fy = self._raw_eval(fx, fy)
1147
+ gx, gy = self._post_iso.rational_maps()
1148
+ fx, fy = gx(fx, fy), gy(fx, fy)
1149
+ R = self._domain.base_ring()['x,y'].fraction_field()
1150
+ return R(fx), R(fy)
1151
+
1152
+ @cached_method
1153
+ def x_rational_map(self):
1154
+ r"""
1155
+ Return the `x`-coordinate rational map of
1156
+ this square-root Vélu isogeny
1157
+ as a univariate rational function in `x`.
1158
+
1159
+ .. NOTE::
1160
+
1161
+ The data returned by this method has size linear in the degree.
1162
+
1163
+ EXAMPLES::
1164
+
1165
+ sage: E = EllipticCurve(GF(101^2), [1, 1, 1, 1, 1])
1166
+ sage: K = (E.cardinality() // 11) * E.gens()[0]
1167
+ sage: phi = E.isogeny(K, algorithm='velusqrt'); phi
1168
+ Elliptic-curve isogeny (using square-root Vélu) of degree 11:
1169
+ From: Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 + x + 1 over Finite Field in z2 of size 101^2
1170
+ To: Elliptic Curve defined by y^2 = x^3 + 39*x + 40 over Finite Field in z2 of size 101^2
1171
+ sage: phi.x_rational_map()
1172
+ (84*x^11 + 67*x^10 + 65*x^9 + ... + 72*x^2 + 76*x + 76)/(x^10 + 10*x^9 + 19*x^8 + ... + x^2 + 47*x + 24)
1173
+ sage: phi.x_rational_map() == phi.rational_maps()[0]
1174
+ True
1175
+
1176
+ TESTS::
1177
+
1178
+ sage: phi.x_rational_map().parent()
1179
+ Fraction Field of Univariate Polynomial Ring in x over Finite Field in z2 of size 101^2
1180
+ """
1181
+ S = self._internal_base_ring['x']
1182
+ fx = S(self._pre_iso.x_rational_map())
1183
+ fx = self._raw_eval(fx)
1184
+ gx = self._post_iso.x_rational_map()
1185
+ fx = gx(fx)
1186
+ R = self._domain.base_ring()['x'].fraction_field()
1187
+ return R(fx)
1188
+
1189
+ def scaling_factor(self):
1190
+ r"""
1191
+ Return the Weierstrass scaling factor associated to this
1192
+ square-root Vélu isogeny.
1193
+
1194
+ The scaling factor is the constant `u` (in the base field)
1195
+ such that `\varphi^* \omega_2 = u \omega_1`, where
1196
+ `\varphi: E_1\to E_2` is this isogeny and `\omega_i` are
1197
+ the standard Weierstrass differentials on `E_i` defined by
1198
+ `\mathrm dx/(2y+a_1x+a_3)`.
1199
+
1200
+ EXAMPLES::
1201
+
1202
+ sage: E = EllipticCurve(GF(101^2), [1, 1, 1, 1, 1])
1203
+ sage: K = (E.cardinality() // 11) * E.gens()[0]
1204
+ sage: phi = E.isogeny(K, algorithm='velusqrt', model='montgomery'); phi
1205
+ Elliptic-curve isogeny (using square-root Vélu) of degree 11:
1206
+ From: Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 + x + 1 over Finite Field in z2 of size 101^2
1207
+ To: Elliptic Curve defined by y^2 = x^3 + 61*x^2 + x over Finite Field in z2 of size 101^2
1208
+ sage: phi.scaling_factor()
1209
+ 55
1210
+ sage: phi.scaling_factor() == phi.formal()[1]
1211
+ True
1212
+ """
1213
+ return self._pre_iso.scaling_factor() * self._post_iso.scaling_factor()
1214
+
1215
+ def inseparable_degree(self):
1216
+ r"""
1217
+ Return the inseparable degree of this square-root Vélu
1218
+ isogeny.
1219
+
1220
+ Since :class:`EllipticCurveHom_velusqrt` only implements
1221
+ separable isogenies, this method always returns one.
1222
+
1223
+ TESTS::
1224
+
1225
+ sage: from sage.schemes.elliptic_curves.hom_velusqrt import EllipticCurveHom_velusqrt
1226
+ sage: EllipticCurveHom_velusqrt.inseparable_degree(None)
1227
+ 1
1228
+ """
1229
+ return Integer(1)
1230
+
1231
+
1232
+ def _random_example_for_testing():
1233
+ r"""
1234
+ Function to generate somewhat random valid Vélu inputs
1235
+ for testing purposes.
1236
+
1237
+ EXAMPLES::
1238
+
1239
+ sage: from sage.schemes.elliptic_curves.hom_velusqrt import _random_example_for_testing
1240
+ sage: E, K = _random_example_for_testing()
1241
+ sage: E # random
1242
+ Elliptic Curve defined by y^2 + (t^3+6*t^2)*x*y + (t^3+3*t^2+2*t+2)*y = x^3 + (6*t^3+2*t^2+t)*x^2 + (3*t^3+2*t^2+6*t+1)*x + (t^3+2*t^2+2) over Finite Field in t of size 7^4
1243
+ sage: E.short_weierstrass_model()
1244
+ Elliptic Curve defined by y^2 = x^3 + ... over Finite Field ...
1245
+ sage: K # random
1246
+ (3*t^3 + 4*t^2 + 4*t + 3 : 6*t^3 + 5*t^2 + 5*t : 1)
1247
+ sage: K.order() # random
1248
+ 101
1249
+ sage: K in E
1250
+ True
1251
+ sage: K.order() % 2
1252
+ 1
1253
+ sage: 5 <= K.order()
1254
+ True
1255
+ """
1256
+ from sage.rings.fast_arith import prime_range
1257
+ from sage.misc.prandom import choice, randrange
1258
+ from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF
1259
+ from sage.arith.functions import lcm
1260
+ from sage.rings.finite_rings.integer_mod import Mod
1261
+
1262
+ while True:
1263
+ p = choice(prime_range(2, 100))
1264
+ e = randrange(1,5)
1265
+ F,t = GF((p,e),'t').objgen()
1266
+ try:
1267
+ E = EllipticCurve([F.random_element() for _ in range(5)])
1268
+ except ArithmeticError:
1269
+ continue
1270
+ try:
1271
+ E.short_weierstrass_model()
1272
+ except ValueError:
1273
+ continue
1274
+ if E.cardinality() < 9:
1275
+ continue
1276
+ A = E.abelian_group()
1277
+ ds = max(A.invariants()).prime_to_m_part(2).divisors()
1278
+ ds = [d for d in ds if 9 <= d < 1000]
1279
+ if ds:
1280
+ deg = choice(ds)
1281
+ break
1282
+ G = A.torsion_subgroup(deg)
1283
+ os = G.generator_orders()
1284
+ while True:
1285
+ v = [randrange(o) for o in os]
1286
+ if lcm(Mod(c,o).additive_order() for c,o in zip(v,os)) == deg:
1287
+ break
1288
+ K = G(v).element()
1289
+ assert K.order() == deg
1290
+ return E, K