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,1523 @@
1
+ # sage_setup: distribution = sagemath-schemes
2
+ # sage.doctest: needs sage.rings.number_field
3
+ r"""
4
+ Isogeny class of elliptic curves over number fields
5
+
6
+ AUTHORS:
7
+
8
+ - David Roe (2012-03-29) -- initial version.
9
+ - John Cremona (2014-08) -- extend to number fields.
10
+ """
11
+
12
+ ##############################################################################
13
+ # Copyright (C) 2012-2014 David Roe <roed.math@gmail.com>
14
+ # John Cremona <john.cremona@gmail.com>
15
+ # William Stein <wstein@gmail.com>
16
+ #
17
+ # Distributed under the terms of the GNU General Public License (GPL)
18
+ #
19
+ # This code is distributed in the hope that it will be useful,
20
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
21
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22
+ # General Public License for more details.
23
+ #
24
+ # The full text of the GPL is available at:
25
+ #
26
+ # https://www.gnu.org/licenses/
27
+ ##############################################################################
28
+
29
+ from sage.structure.sage_object import SageObject
30
+ from sage.structure.richcmp import richcmp_method, richcmp
31
+ import sage.databases.cremona
32
+ from sage.rings.integer_ring import ZZ
33
+ from sage.rings.rational_field import QQ
34
+ from sage.misc.flatten import flatten
35
+ from sage.misc.cachefunc import cached_method
36
+ from sage.schemes.elliptic_curves.ell_field import EllipticCurve_field
37
+ from sage.schemes.elliptic_curves.ell_number_field import EllipticCurve_number_field
38
+
39
+
40
+ @richcmp_method
41
+ class IsogenyClass_EC(SageObject):
42
+ r"""
43
+ Isogeny class of an elliptic curve.
44
+
45
+ .. NOTE::
46
+
47
+ The current implementation chooses a curve from each isomorphism
48
+ class in the isogeny class. Over `\QQ` this is a unique reduced
49
+ minimal model in each isomorphism class. Over number fields the
50
+ model chosen may change in future.
51
+ """
52
+
53
+ def __init__(self, E, label=None, empty=False) -> None:
54
+ r"""
55
+ Over `\QQ` we use curves since minimal models exist and there
56
+ is a canonical choice of one.
57
+
58
+ INPUT:
59
+
60
+ - ``label`` -- string or ``None``, a Cremona or LMFDB label, used
61
+ in printing; ignored if base field is not `\QQ`
62
+
63
+ EXAMPLES::
64
+
65
+ sage: cls = EllipticCurve('1011b1').isogeny_class()
66
+ sage: print("\n".join(repr(E) for E in cls.curves))
67
+ Elliptic Curve defined by y^2 + x*y = x^3 - 8*x - 9 over Rational Field
68
+ Elliptic Curve defined by y^2 + x*y = x^3 - 23*x + 30 over Rational Field
69
+ """
70
+ self.E = E
71
+ self._label = label
72
+ if not empty:
73
+ self._compute()
74
+
75
+ def __len__(self) -> int:
76
+ """
77
+ The number of curves in the class.
78
+
79
+ EXAMPLES::
80
+
81
+ sage: E = EllipticCurve('15a')
82
+ sage: len(E.isogeny_class()) # indirect doctest
83
+ 8
84
+ """
85
+ return len(self.curves)
86
+
87
+ def __iter__(self):
88
+ """
89
+ Iterator over curves in the class.
90
+
91
+ EXAMPLES::
92
+
93
+ sage: E = EllipticCurve('15a')
94
+ sage: all(C.conductor() == 15 for C in E.isogeny_class()) # indirect doctest
95
+ True
96
+ """
97
+ return iter(self.curves)
98
+
99
+ def __getitem__(self, i):
100
+ """
101
+ Return the `i`-th curve in the class.
102
+
103
+ EXAMPLES::
104
+
105
+ sage: # needs sage.groups
106
+ sage: E = EllipticCurve('990j1')
107
+ sage: iso = E.isogeny_class(order='lmfdb') # orders lexicographically on a-invariants
108
+ sage: iso[2] == E # indirect doctest
109
+ True
110
+ """
111
+ return self.curves[i]
112
+
113
+ def index(self, C):
114
+ """
115
+ Return the index of a curve in this class.
116
+
117
+ INPUT:
118
+
119
+ - ``C`` -- an elliptic curve in this isogeny class
120
+
121
+ OUTPUT:
122
+
123
+ - ``i`` -- integer so that the ``i`` th curve in the class
124
+ is isomorphic to ``C``
125
+
126
+ EXAMPLES::
127
+
128
+ sage: # needs sage.groups
129
+ sage: E = EllipticCurve('990j1')
130
+ sage: iso = E.isogeny_class(order='lmfdb') # orders lexicographically on a-invariants
131
+ sage: iso.index(E.short_weierstrass_model())
132
+ 2
133
+ """
134
+ # This will need updating once we start talking about curves
135
+ # over more general number fields
136
+ if not isinstance(C, EllipticCurve_number_field):
137
+ raise ValueError("x not in isogeny class")
138
+ for i, E in enumerate(self.curves):
139
+ if C.is_isomorphic(E):
140
+ return i
141
+ raise ValueError("%s is not in isogeny class %s" % (C, self))
142
+
143
+ def __richcmp__(self, other, op) -> bool:
144
+ """
145
+ Compare ``self`` and ``other``.
146
+
147
+ If they are different, compares the sorted underlying lists of
148
+ curves.
149
+
150
+ Note that two isogeny classes with different orderings will
151
+ compare as the same. If you want to include the ordering,
152
+ just compare the list of curves.
153
+
154
+ EXAMPLES::
155
+
156
+ sage: E = EllipticCurve('990j1')
157
+ sage: EE = EllipticCurve('990j4')
158
+ sage: E.isogeny_class() == EE.isogeny_class() # indirect doctest
159
+ True
160
+ """
161
+ if isinstance(other, IsogenyClass_EC):
162
+ return richcmp(sorted(e.a_invariants() for e in self.curves),
163
+ sorted(f.a_invariants() for f in other.curves), op)
164
+ return NotImplemented
165
+
166
+ def __hash__(self) -> int:
167
+ """
168
+ Hash is based on the a-invariants of the sorted list of
169
+ minimal models.
170
+
171
+ EXAMPLES::
172
+
173
+ sage: E = EllipticCurve('990j1')
174
+ sage: C = E.isogeny_class()
175
+ sage: hash(C) == hash(tuple(sorted([curve.a_invariants() for curve in C.curves]))) # indirect doctest
176
+ True
177
+ """
178
+ try:
179
+ return self._hash
180
+ except AttributeError:
181
+ self._hash = hash(tuple(sorted(E.a_invariants() for E in self.curves)))
182
+ return self._hash
183
+
184
+ def _repr_(self):
185
+ r"""
186
+ The string representation of this isogeny class.
187
+
188
+ .. NOTE::
189
+
190
+ Over `\QQ`, the string representation depends on whether an
191
+ LMFDB or Cremona label for the curve is known when this
192
+ isogeny class is constructed. Over general number fields,
193
+ instead of labels the representation uses that of the curve
194
+ initially used to create the class.
195
+
196
+ EXAMPLES:
197
+
198
+ If the curve is constructed from an LMFDB label then that
199
+ label is used::
200
+
201
+ sage: E = EllipticCurve('462.f3')
202
+ sage: E.isogeny_class() # indirect doctest
203
+ Elliptic curve isogeny class 462.f
204
+
205
+ If the curve is constructed from a Cremona label then that
206
+ label is used::
207
+
208
+ sage: E = EllipticCurve('990j1')
209
+ sage: E.isogeny_class()
210
+ Elliptic curve isogeny class 990j
211
+
212
+ Otherwise, including curves whose base field is not `\QQ`,the
213
+ representation is determined from the curve used to create the
214
+ class::
215
+
216
+ sage: E = EllipticCurve([1,2,3,4,5])
217
+ sage: E.isogeny_class()
218
+ Isogeny class of Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 4*x + 5 over Rational Field
219
+
220
+ sage: K.<i> = QuadraticField(-1)
221
+ sage: E = EllipticCurve(K, [0,0,0,0,1]); E
222
+ Elliptic Curve defined by y^2 = x^3 + 1 over Number Field in i with defining polynomial x^2 + 1 with i = 1*I
223
+ sage: C = E.isogeny_class()
224
+ sage: C
225
+ Isogeny class of Elliptic Curve defined by y^2 = x^3 + 1 over Number Field in i with defining polynomial x^2 + 1 with i = 1*I
226
+ sage: C.curves
227
+ [Elliptic Curve defined by y^2 = x^3 + (-27) over Number Field in i with defining polynomial x^2 + 1 with i = 1*I,
228
+ Elliptic Curve defined by y^2 = x^3 + 1 over Number Field in i with defining polynomial x^2 + 1 with i = 1*I,
229
+ Elliptic Curve defined by y^2 + (i+1)*x*y = x^3 + i*x^2 + 3*x + (-i) over Number Field in i with defining polynomial x^2 + 1 with i = 1*I,
230
+ Elliptic Curve defined by y^2 + (i+1)*x*y = x^3 + i*x^2 + 33*x + 91*i over Number Field in i with defining polynomial x^2 + 1 with i = 1*I]
231
+ """
232
+ if self._label:
233
+ return "Elliptic curve isogeny class %s" % (self._label)
234
+ else:
235
+ return "Isogeny class of %r" % (self.E)
236
+
237
+ def __contains__(self, x) -> bool:
238
+ """
239
+ INPUT:
240
+
241
+ - ``x`` -- a Python object
242
+
243
+ OUTPUT: boolean; ``True`` iff ``x`` is an elliptic curve in this
244
+ isogeny class
245
+
246
+ .. NOTE::
247
+
248
+ If the input is isomorphic but not identical to a curve in
249
+ the class, then ``False`` will be returned.
250
+
251
+ EXAMPLES::
252
+
253
+ sage: cls = EllipticCurve('15a3').isogeny_class()
254
+ sage: E = EllipticCurve('15a7'); E in cls
255
+ True
256
+ sage: E.short_weierstrass_model() in cls
257
+ True
258
+ """
259
+ if not isinstance(x, EllipticCurve_field):
260
+ return False
261
+ return any(x.is_isomorphic(y) for y in self.curves)
262
+
263
+ @cached_method
264
+ def matrix(self, fill=True):
265
+ """
266
+ Return the matrix whose entries give the minimal degrees of
267
+ isogenies between curves in this class.
268
+
269
+ INPUT:
270
+
271
+ - ``fill`` -- boolean (default: ``True``); if ``False`` then the
272
+ matrix will contain only zeros and prime entries. If ``True`` it
273
+ will fill in the other degrees.
274
+
275
+ EXAMPLES::
276
+
277
+ sage: isocls = EllipticCurve('15a3').isogeny_class()
278
+ sage: isocls.matrix()
279
+ [ 1 2 2 2 4 4 8 8]
280
+ [ 2 1 4 4 8 8 16 16]
281
+ [ 2 4 1 4 8 8 16 16]
282
+ [ 2 4 4 1 2 2 4 4]
283
+ [ 4 8 8 2 1 4 8 8]
284
+ [ 4 8 8 2 4 1 2 2]
285
+ [ 8 16 16 4 8 2 1 4]
286
+ [ 8 16 16 4 8 2 4 1]
287
+ sage: isocls.matrix(fill=False)
288
+ [0 2 2 2 0 0 0 0]
289
+ [2 0 0 0 0 0 0 0]
290
+ [2 0 0 0 0 0 0 0]
291
+ [2 0 0 0 2 2 0 0]
292
+ [0 0 0 2 0 0 0 0]
293
+ [0 0 0 2 0 0 2 2]
294
+ [0 0 0 0 0 2 0 0]
295
+ [0 0 0 0 0 2 0 0]
296
+ """
297
+ if self._mat is None:
298
+ self._compute_matrix()
299
+ mat = self._mat
300
+ if fill and mat[0, 0] == 0:
301
+ from sage.schemes.elliptic_curves.ell_curve_isogeny import fill_isogeny_matrix
302
+ mat = fill_isogeny_matrix(mat)
303
+ if not fill and mat[0, 0] == 1:
304
+ from sage.schemes.elliptic_curves.ell_curve_isogeny import unfill_isogeny_matrix
305
+ mat = unfill_isogeny_matrix(mat)
306
+ return mat
307
+
308
+ @cached_method
309
+ def qf_matrix(self):
310
+ """
311
+ Return the array whose entries are quadratic forms
312
+ representing the degrees of isogenies between curves in this
313
+ class (CM case only).
314
+
315
+ OUTPUT:
316
+
317
+ a `2x2` array (list of lists) of list, each of the form [2] or
318
+ [2,1,3] representing the coefficients of an integral quadratic
319
+ form in 1 or 2 variables whose values are the possible isogeny
320
+ degrees between the i'th and j'th curve in the class.
321
+
322
+ EXAMPLES::
323
+
324
+ sage: pol = PolynomialRing(QQ,'x')([1,0,3,0,1])
325
+ sage: K.<c> = NumberField(pol)
326
+ sage: j = 1480640 + 565760*c^2
327
+ sage: E = EllipticCurve(j=j)
328
+ sage: C = E.isogeny_class()
329
+ sage: C.qf_matrix()
330
+ [[[1], [2, 2, 3]], [[2, 2, 3], [1]]]
331
+ """
332
+ if self._qfmat is None:
333
+ raise ValueError("qf_matrix only defined for isogeny classes with rational CM")
334
+ else:
335
+ return self._qfmat
336
+
337
+ @cached_method
338
+ def isogenies(self, fill=False):
339
+ r"""
340
+ Return a list of lists of isogenies and 0s, corresponding to
341
+ the entries of :meth:`matrix`
342
+
343
+ INPUT:
344
+
345
+ - ``fill`` -- boolean (default: ``False``); whether to only return
346
+ prime degree isogenies. Currently only implemented for
347
+ ``fill=False``.
348
+
349
+ OUTPUT:
350
+
351
+ - a list of lists, where the ``j`` th entry of the ``i`` th list
352
+ is either zero or a prime degree isogeny from the ``i`` th curve
353
+ in this class to the ``j`` th curve.
354
+
355
+ .. WARNING::
356
+
357
+ The domains and codomains of the isogenies will have the same
358
+ Weierstrass equation as the curves in this class, but they
359
+ may not be identical python objects in the current
360
+ implementation.
361
+
362
+ EXAMPLES::
363
+
364
+ sage: isocls = EllipticCurve('15a3').isogeny_class()
365
+ sage: f = isocls.isogenies()[0][1]; f
366
+ Isogeny of degree 2
367
+ from Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 5*x + 2 over Rational Field
368
+ to Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 80*x + 242 over Rational Field
369
+ sage: f.domain() == isocls.curves[0] and f.codomain() == isocls.curves[1]
370
+ True
371
+ """
372
+ if fill:
373
+ raise NotImplementedError
374
+ isogenies = self._maps
375
+ if isogenies is None:
376
+ self._compute_isogenies()
377
+ isogenies = self._maps
378
+ return isogenies
379
+
380
+ @cached_method
381
+ def graph(self):
382
+ r"""
383
+ Return a graph whose vertices correspond to curves in this
384
+ class, and whose edges correspond to prime degree isogenies.
385
+
386
+ .. NOTE::
387
+
388
+ There are only finitely many possible isogeny graphs for
389
+ curves over `\QQ` [Maz1978b]. This function tries to lay out
390
+ the graph nicely by special casing each isogeny graph.
391
+ This could also be done over other number fields, such as
392
+ quadratic fields.
393
+
394
+ .. NOTE::
395
+
396
+ The vertices are labeled 1 to n rather than 0 to n-1 to
397
+ match LMFDB and Cremona labels for curves over `\QQ`.
398
+
399
+ EXAMPLES::
400
+
401
+ sage: # needs sage.graphs
402
+ sage: isocls = EllipticCurve('15a3').isogeny_class()
403
+ sage: G = isocls.graph()
404
+ sage: sorted(G._pos.items())
405
+ [(1, [-0.8660254, 0.5]), (2, [-0.8660254, 1.5]), (3, [-1.7320508, 0]),
406
+ (4, [0, 0]), (5, [0, -1]), (6, [0.8660254, 0.5]),
407
+ (7, [0.8660254, 1.5]), (8, [1.7320508, 0])]
408
+ """
409
+ from sage.graphs.graph import Graph
410
+
411
+ if self.E.base_field() is not QQ:
412
+ M = self.matrix(fill=False)
413
+ n = len(self)
414
+ G = Graph(M, format='weighted_adjacency_matrix')
415
+ D = {v: self.curves[v] for v in G.vertices(sort=False)}
416
+ G.set_vertices(D)
417
+ if self._qfmat: # i.e. self.E.has_rational_cm():
418
+ for i in range(n):
419
+ for j in range(n):
420
+ if M[i, j]:
421
+ G.set_edge_label(i, j, str(self._qfmat[i][j]))
422
+ G.relabel(list(range(1, n + 1)))
423
+ return G
424
+
425
+ M = self.matrix(fill=False)
426
+ n = M.nrows() # = M.ncols()
427
+ G = Graph(M, format='weighted_adjacency_matrix')
428
+ N = self.matrix(fill=True)
429
+ D = {v: self.curves[v] for v in G.vertices(sort=False)}
430
+ # The maximum degree classifies the shape of the isogeny
431
+ # graph, though the number of vertices is often enough.
432
+ # This only holds over Q, so this code will need to change
433
+ # once other isogeny classes are implemented.
434
+
435
+ if n == 1:
436
+ # one vertex
437
+ pass
438
+ elif n == 2:
439
+ # one edge, two vertices. We align horizontally and put
440
+ # the lower number on the left vertex.
441
+ G.set_pos(pos={0: [-0.5, 0], 1: [0.5, 0]})
442
+ else:
443
+ maxdegree = max(max(N))
444
+ if n == 3:
445
+ # o--o--o
446
+ centervert = next(i for i in range(3) if max(N.row(i)) < maxdegree)
447
+ other = [i for i in range(3) if i != centervert]
448
+ G.set_pos(pos={centervert: [0, 0], other[0]: [-1, 0], other[1]: [1, 0]})
449
+ elif maxdegree == 4:
450
+ # o--o<8
451
+ centervert = next(i for i in range(4) if max(N.row(i)) < maxdegree)
452
+ other = [i for i in range(4) if i != centervert]
453
+ G.set_pos(pos={centervert: [0, 0], other[0]: [0, 1],
454
+ other[1]: [-0.8660254, -0.5], other[2]: [0.8660254, -0.5]})
455
+ elif maxdegree == 27:
456
+ # o--o--o--o
457
+ centers = [i for i in range(4) if list(N.row(i)).count(3) == 2]
458
+ left = next(j for j in range(4) if N[centers[0], j] == 3 and j not in centers)
459
+ right = next(j for j in range(4) if N[centers[1], j] == 3 and j not in centers)
460
+ G.set_pos(pos={left: [-1.5, 0], centers[0]: [-0.5, 0],
461
+ centers[1]: [0.5, 0], right: [1.5, 0]})
462
+ elif n == 4:
463
+ # square
464
+ opp = next(i for i in range(1, 4) if not N[0, i].is_prime())
465
+ other = [i for i in range(1, 4) if i != opp]
466
+ G.set_pos(pos={0: [1, 1], other[0]: [-1, 1],
467
+ opp: [-1, -1], other[1]: [1, -1]})
468
+ elif maxdegree == 8:
469
+ # 8>o--o<8
470
+ centers = [i for i in range(6) if list(N.row(i)).count(2) == 3]
471
+ left = [j for j in range(6) if N[centers[0], j] == 2 and j not in centers]
472
+ right = [j for j in range(6) if N[centers[1], j] == 2 and j not in centers]
473
+ G.set_pos(pos={centers[0]: [-0.5, 0], left[0]: [-1, 0.8660254],
474
+ left[1]: [-1, -0.8660254], centers[1]: [0.5, 0],
475
+ right[0]: [1, 0.8660254], right[1]: [1, -0.8660254]})
476
+ elif maxdegree == 18:
477
+ # two squares joined on an edge
478
+ centers = [i for i in range(6) if list(N.row(i)).count(3) == 2]
479
+ top = [j for j in range(6) if N[centers[0], j] == 3]
480
+ bl = next(j for j in range(6) if N[top[0], j] == 2)
481
+ br = next(j for j in range(6) if N[top[1], j] == 2)
482
+ G.set_pos(pos={centers[0]: [0, 0.5], centers[1]: [0, -0.5],
483
+ top[0]: [-1, 0.5], top[1]: [1, 0.5],
484
+ bl: [-1, -0.5], br: [1, -0.5]})
485
+ elif maxdegree == 16:
486
+ # tree from bottom, 3 regular except for the leaves.
487
+ centers = [i for i in range(8) if list(N.row(i)).count(2) == 3]
488
+ center = next(i for i in centers if len([j for j in centers if N[i, j] == 2]) == 2)
489
+ centers.remove(center)
490
+ bottom = next(j for j in range(8) if N[center, j] == 2 and j not in centers)
491
+ left = [j for j in range(8) if N[centers[0], j] == 2 and j != center]
492
+ right = [j for j in range(8) if N[centers[1], j] == 2 and j != center]
493
+ G.set_pos(pos={center: [0, 0], bottom: [0, -1], centers[0]: [-0.8660254, 0.5],
494
+ centers[1]: [0.8660254, 0.5], left[0]: [-0.8660254, 1.5],
495
+ right[0]: [0.8660254, 1.5], left[1]: [-1.7320508, 0], right[1]: [1.7320508, 0]})
496
+ elif maxdegree == 12:
497
+ # tent
498
+ centers = [i for i in range(8) if list(N.row(i)).count(2) == 3]
499
+ left = [j for j in range(8) if N[centers[0], j] == 2]
500
+ right = []
501
+ for i in range(3):
502
+ right.append(next(j for j in range(8) if N[centers[1], j] == 2 and N[left[i], j] == 3))
503
+ G.set_pos(pos={centers[0]: [-0.75, 0], centers[1]: [0.75, 0], left[0]: [-0.75, 1],
504
+ right[0]: [0.75, 1], left[1]: [-1.25, -0.75], right[1]: [0.25, -0.75],
505
+ left[2]: [-0.25, -0.25], right[2]: [1.25, -0.25]})
506
+ G.set_vertices(D)
507
+ G.relabel(list(range(1, n + 1)))
508
+ return G
509
+
510
+ @cached_method
511
+ def reorder(self, order):
512
+ r"""
513
+ Return a new isogeny class with the curves reordered.
514
+
515
+ INPUT:
516
+
517
+ - ``order`` -- ``None``, a string or an iterable over all curves in
518
+ this class. See
519
+ :meth:`sage.schemes.elliptic_curves.ell_rational_field.EllipticCurve_rational_field.isogeny_class`
520
+ for more details.
521
+
522
+ OUTPUT:
523
+
524
+ Another :class:`IsogenyClass_EC` with the curves reordered (and
525
+ matrices and maps changed as appropriate).
526
+
527
+ EXAMPLES::
528
+
529
+ sage: # needs sage.groups
530
+ sage: isocls = EllipticCurve('15a1').isogeny_class()
531
+ sage: print("\n".join(repr(C) for C in isocls.curves))
532
+ Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 10*x - 10 over Rational Field
533
+ Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 5*x + 2 over Rational Field
534
+ Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 + 35*x - 28 over Rational Field
535
+ Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 135*x - 660 over Rational Field
536
+ Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 80*x + 242 over Rational Field
537
+ Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 over Rational Field
538
+ Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 110*x - 880 over Rational Field
539
+ Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 2160*x - 39540 over Rational Field
540
+ sage: isocls2 = isocls.reorder('lmfdb')
541
+ sage: print("\n".join(repr(C) for C in isocls2.curves))
542
+ Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 2160*x - 39540 over Rational Field
543
+ Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 135*x - 660 over Rational Field
544
+ Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 110*x - 880 over Rational Field
545
+ Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 80*x + 242 over Rational Field
546
+ Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 10*x - 10 over Rational Field
547
+ Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 5*x + 2 over Rational Field
548
+ Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 over Rational Field
549
+ Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 + 35*x - 28 over Rational Field
550
+ """
551
+ if order is None or isinstance(order, str) and order == self._algorithm:
552
+ return self
553
+ if isinstance(order, str):
554
+ if order == "lmfdb":
555
+ reordered_curves = sorted(self.curves,
556
+ key=lambda E: E.a_invariants())
557
+ else:
558
+ reordered_curves = list(self.E.isogeny_class(algorithm=order))
559
+ elif isinstance(order, (list, tuple, IsogenyClass_EC)):
560
+ reordered_curves = list(order)
561
+ if len(reordered_curves) != len(self.curves):
562
+ raise ValueError("Incorrect length")
563
+ else:
564
+ raise TypeError("order parameter should be a string, list of curves or isogeny class")
565
+ need_perm = self._mat is not None
566
+ cpy = self.copy()
567
+ curves = []
568
+ perm = []
569
+ for E in reordered_curves:
570
+ try:
571
+ j = self.curves.index(E)
572
+ except ValueError:
573
+ try:
574
+ j = self.curves.index(E.minimal_model())
575
+ except ValueError:
576
+ raise ValueError("order does not yield a permutation of curves")
577
+ curves.append(self.curves[j])
578
+ if need_perm:
579
+ perm.append(j+1)
580
+ cpy.curves = tuple(curves)
581
+ if need_perm:
582
+ from sage.groups.perm_gps.permgroup_named import SymmetricGroup
583
+ perm = SymmetricGroup(len(self.curves))(perm)
584
+ cpy._mat = perm.matrix() * self._mat * (~perm).matrix()
585
+ if self._maps is not None:
586
+ n = len(self._maps)
587
+ cpy._maps = [self._maps[perm(i+1)-1] for i in range(n)]
588
+ for i in range(n):
589
+ cpy._maps[i] = [cpy._maps[i][perm(jj + 1)-1]
590
+ for jj in range(n)]
591
+ else:
592
+ cpy._mat = None
593
+ cpy._maps = None
594
+ return cpy
595
+
596
+
597
+ class IsogenyClass_EC_NumberField(IsogenyClass_EC):
598
+ """
599
+ Isogeny classes for elliptic curves over number fields.
600
+ """
601
+ def __init__(self, E, reducible_primes=None,
602
+ algorithm='Billerey', minimal_models=True) -> None:
603
+ r"""
604
+ INPUT:
605
+
606
+ - ``E`` -- an elliptic curve over a number field
607
+
608
+ - ``reducible_primes`` -- list of integers, or ``None`` (default); if
609
+ not ``None`` then this should be a list of primes; in computing the
610
+ isogeny class, only composites isogenies of these degrees will be used.
611
+
612
+ - ``algorithm`` -- string (default: ``'Billerey'``); the algorithm
613
+ to use to compute the reducible primes. Ignored for CM
614
+ curves or if ``reducible_primes`` is provided. Values are
615
+ ``'Billerey'`` (default), ``'Larson'``, and ``'heuristic'``.
616
+
617
+ - ``minimal_models`` -- boolean (default: ``True``); if ``True``,
618
+ all curves in the class will be minimal or semi-minimal
619
+ models. Over fields of larger degree it can be expensive to
620
+ compute these so set to ``False``.
621
+
622
+ EXAMPLES::
623
+
624
+ sage: K.<i> = QuadraticField(-1)
625
+ sage: E = EllipticCurve(K, [0,0,0,0,1])
626
+ sage: C = E.isogeny_class(); C
627
+ Isogeny class of Elliptic Curve defined by y^2 = x^3 + 1
628
+ over Number Field in i with defining polynomial x^2 + 1 with i = 1*I
629
+
630
+ The curves in the class (sorted)::
631
+
632
+ sage: [E1.ainvs() for E1 in C]
633
+ [(0, 0, 0, 0, -27),
634
+ (0, 0, 0, 0, 1),
635
+ (i + 1, i, 0, 3, -i),
636
+ (i + 1, i, 0, 33, 91*i)]
637
+
638
+ The matrix of degrees of cyclic isogenies between curves::
639
+
640
+ sage: C.matrix()
641
+ [1 3 6 2]
642
+ [3 1 2 6]
643
+ [6 2 1 3]
644
+ [2 6 3 1]
645
+
646
+ The array of isogenies themselves is not filled out but only
647
+ contains those used to construct the class, the other entries
648
+ containing the integer 0. This will be changed when the
649
+ class :class:`EllipticCurveIsogeny` allowed composition. In
650
+ this case we used `2`-isogenies to go from 0 to 2 and from 1
651
+ to 3, and `3`-isogenies to go from 0 to 1 and from 2 to 3::
652
+
653
+ sage: isogs = C.isogenies()
654
+ sage: [((i,j), isogs[i][j].degree())
655
+ ....: for i in range(4) for j in range(4) if isogs[i][j] != 0]
656
+ [((0, 1), 3),
657
+ ((0, 3), 2),
658
+ ((1, 0), 3),
659
+ ((1, 2), 2),
660
+ ((2, 1), 2),
661
+ ((2, 3), 3),
662
+ ((3, 0), 2),
663
+ ((3, 2), 3)]
664
+ sage: [((i,j), isogs[i][j].x_rational_map())
665
+ ....: for i in range(4) for j in range(4) if isogs[i][j] != 0]
666
+ [((0, 1), (1/9*x^3 - 12)/x^2),
667
+ ((0, 3), (1/2*i*x^2 - 2*i*x + 15*i)/(x - 3)),
668
+ ((1, 0), (x^3 + 4)/x^2),
669
+ ((1, 2), (1/2*i*x^2 + i)/(x + 1)),
670
+ ((2, 1), (-1/2*i*x^2 - 1/2*i)/(x - 1/2*i)),
671
+ ((2, 3), (x^3 - 2*i*x^2 - 7*x + 4*i)/(x^2 - 2*i*x - 1)),
672
+ ((3, 0), (-1/2*i*x^2 + 2*x - 5/2*i)/(x + 7/2*i)),
673
+ ((3, 2), (1/9*x^3 + 2/3*i*x^2 - 13/3*x - 116/9*i)/(x^2 + 10*i*x - 25))]
674
+
675
+ sage: K.<i> = QuadraticField(-1)
676
+ sage: E = EllipticCurve([1+i, -i, i, 1, 0])
677
+ sage: C = E.isogeny_class(); C
678
+ Isogeny class of Elliptic Curve defined
679
+ by y^2 + (i+1)*x*y + i*y = x^3 + (-i)*x^2 + x
680
+ over Number Field in i with defining polynomial x^2 + 1 with i = 1*I
681
+ sage: len(C)
682
+ 6
683
+ sage: C.matrix()
684
+ [ 1 3 9 18 6 2]
685
+ [ 3 1 3 6 2 6]
686
+ [ 9 3 1 2 6 18]
687
+ [18 6 2 1 3 9]
688
+ [ 6 2 6 3 1 3]
689
+ [ 2 6 18 9 3 1]
690
+ sage: [E1.ainvs() for E1 in C]
691
+ [(i + 1, i - 1, i, -i - 1, -i + 1),
692
+ (i + 1, i - 1, i, 14*i + 4, 7*i + 14),
693
+ (i + 1, i - 1, i, 59*i + 99, 372*i - 410),
694
+ (i + 1, -i, i, -240*i - 399, 2869*i + 2627),
695
+ (i + 1, -i, i, -5*i - 4, 2*i + 5),
696
+ (i + 1, -i, i, 1, 0)]
697
+
698
+ An example with CM by `\sqrt{-5}`::
699
+
700
+ sage: pol = PolynomialRing(QQ,'x')([1,0,3,0,1])
701
+ sage: K.<c> = NumberField(pol)
702
+ sage: j = 1480640 + 565760*c^2
703
+ sage: E = EllipticCurve(j=j)
704
+ sage: E.has_cm()
705
+ True
706
+ sage: E.has_rational_cm()
707
+ True
708
+ sage: E.cm_discriminant()
709
+ -20
710
+ sage: C = E.isogeny_class()
711
+ sage: len(C)
712
+ 2
713
+ sage: C.matrix()
714
+ [1 2]
715
+ [2 1]
716
+ sage: [E.ainvs() for E in C]
717
+ [(0, 0, 0, 83490*c^2 - 147015, -64739840*c^2 - 84465260),
718
+ (0, 0, 0, -161535*c^2 + 70785, -62264180*c^3 + 6229080*c)]
719
+ sage: C.isogenies()[0][1]
720
+ Isogeny of degree 2
721
+ from Elliptic Curve defined by
722
+ y^2 = x^3 + (83490*c^2-147015)*x + (-64739840*c^2-84465260)
723
+ over Number Field in c with defining polynomial x^4 + 3*x^2 + 1
724
+ to Elliptic Curve defined by
725
+ y^2 = x^3 + (-161535*c^2+70785)*x + (-62264180*c^3+6229080*c)
726
+ over Number Field in c with defining polynomial x^4 + 3*x^2 + 1
727
+
728
+ TESTS::
729
+
730
+ sage: TestSuite(C).run()
731
+ """
732
+ self._algorithm = "sage"
733
+ self._reducible_primes = reducible_primes
734
+ self._algorithm = algorithm
735
+ self._minimal_models = minimal_models
736
+ IsogenyClass_EC.__init__(self, E, label=None, empty=False)
737
+
738
+ def copy(self):
739
+ """
740
+ Return a copy (mostly used in reordering).
741
+
742
+ EXAMPLES::
743
+
744
+ sage: K.<i> = QuadraticField(-1)
745
+ sage: E = EllipticCurve(K, [0,0,0,0,1])
746
+ sage: C = E.isogeny_class()
747
+ sage: C2 = C.copy()
748
+ sage: C is C2
749
+ False
750
+ sage: C == C2
751
+ True
752
+ """
753
+ ans = IsogenyClass_EC_NumberField(self.E, reducible_primes=self._reducible_primes, algorithm=self._algorithm, minimal_models=self._minimal_models)
754
+ # The following isn't needed internally, but it will keep
755
+ # things from breaking if this is used for something other
756
+ # than reordering.
757
+ ans.curves = self.curves
758
+ ans._mat = None
759
+ ans._maps = None
760
+ return ans
761
+
762
+ def _compute(self, verbose=False):
763
+ """
764
+ Compute the list of curves, the matrix and prime-degree
765
+ isogenies.
766
+
767
+ EXAMPLES::
768
+
769
+ sage: K.<i> = QuadraticField(-1)
770
+ sage: E = EllipticCurve(K, [0,0,0,0,1])
771
+ sage: C = E.isogeny_class()
772
+ sage: C2 = C.copy()
773
+ sage: C2._mat
774
+ sage: C2._compute()
775
+ sage: C2._mat
776
+ [1 3 6 2]
777
+ [3 1 2 6]
778
+ [6 2 1 3]
779
+ [2 6 3 1]
780
+
781
+ sage: C2._compute(verbose=True)
782
+ possible isogeny degrees: [2, 3] -actual isogeny degrees: {2, 3} -added curve #1 (degree 2)... -added tuple [0, 1, 2]... -added tuple [1, 0, 2]... -added curve #2 (degree 3)... -added tuple [0, 2, 3]... -added tuple [2, 0, 3]...... relevant degrees: [2, 3]... -now completing the isogeny class... -processing curve #1... -added tuple [1, 0, 2]... -added tuple [0, 1, 2]... -added curve #3... -added tuple [1, 3, 3]... -added tuple [3, 1, 3]... -processing curve #2... -added tuple [2, 3, 2]... -added tuple [3, 2, 2]... -added tuple [2, 0, 3]... -added tuple [0, 2, 3]... -processing curve #3... -added tuple [3, 2, 2]... -added tuple [2, 3, 2]... -added tuple [3, 1, 3]... -added tuple [1, 3, 3]...... isogeny class has size 4
783
+ Sorting permutation = {0: 1, 1: 2, 2: 0, 3: 3}
784
+ Matrix = [1 3 6 2]
785
+ [3 1 2 6]
786
+ [6 2 1 3]
787
+ [2 6 3 1]
788
+
789
+ TESTS:
790
+
791
+ Check that :issue:`19030` is fixed (codomains of reverse isogenies were wrong)::
792
+
793
+ sage: x = polygen(QQ, 'x')
794
+ sage: K.<i> = NumberField(x^2 + 1)
795
+ sage: E = EllipticCurve([1, i + 1, 1, -72*i + 8, 95*i + 146])
796
+ sage: C = E.isogeny_class()
797
+ sage: curves = C.curves
798
+ sage: isos = C.isogenies()
799
+ sage: isos[0][3].codomain() == curves[3]
800
+ True
801
+ """
802
+ from sage.schemes.elliptic_curves.ell_curve_isogeny import fill_isogeny_matrix
803
+ from sage.matrix.matrix_space import MatrixSpace
804
+ from sage.sets.set import Set
805
+ self._maps = None
806
+
807
+ if self._minimal_models:
808
+ E = self.E.global_minimal_model(semi_global=True)
809
+ else:
810
+ E = self.E
811
+
812
+ degs = self._reducible_primes
813
+ if degs is None:
814
+ self._reducible_primes = possible_isogeny_degrees(E, algorithm=self._algorithm)
815
+ degs = self._reducible_primes
816
+ if verbose:
817
+ import sys
818
+ sys.stdout.write(" possible isogeny degrees: %s" % degs)
819
+ sys.stdout.flush()
820
+ isogenies = E.isogenies_prime_degree(degs, minimal_models=self._minimal_models)
821
+ if verbose:
822
+ sys.stdout.write(" -actual isogeny degrees: %s" % Set(phi.degree() for phi in isogenies))
823
+ sys.stdout.flush()
824
+ # Add all new codomains to the list and collect degrees:
825
+ curves = [E]
826
+ ncurves = 1
827
+ degs = []
828
+ # tuples (i,j,l,phi) where curve i is l-isogenous to curve j via phi
829
+ tuples = []
830
+
831
+ def add_tup(t):
832
+ for T in [t, [t[1], t[0], t[2], 0]]:
833
+ if T not in tuples:
834
+ tuples.append(T)
835
+ if verbose:
836
+ sys.stdout.write(" -added tuple %s..." % T[:3])
837
+ sys.stdout.flush()
838
+
839
+ for phi in isogenies:
840
+ E2 = phi.codomain()
841
+ d = ZZ(phi.degree())
842
+ if not any(E2.is_isomorphic(E3) for E3 in curves):
843
+ curves.append(E2)
844
+ if verbose:
845
+ sys.stdout.write(" -added curve #%s (degree %s)..." % (ncurves,d))
846
+ sys.stdout.flush()
847
+ add_tup([0,ncurves,d,phi])
848
+ ncurves += 1
849
+ if d not in degs:
850
+ degs.append(d)
851
+ if verbose:
852
+ sys.stdout.write("... relevant degrees: %s..." % degs)
853
+ sys.stdout.write(" -now completing the isogeny class...")
854
+ sys.stdout.flush()
855
+
856
+ i = 1
857
+ while i < ncurves:
858
+ E1 = curves[i]
859
+ if verbose:
860
+ sys.stdout.write(" -processing curve #%s..." % i)
861
+ sys.stdout.flush()
862
+
863
+ isogenies = E1.isogenies_prime_degree(degs, minimal_models=self._minimal_models)
864
+
865
+ for phi in isogenies:
866
+ E2 = phi.codomain()
867
+ d = phi.degree()
868
+ js = [j for j,E3 in enumerate(curves) if E2.is_isomorphic(E3)]
869
+ if js: # seen codomain already -- up to isomorphism
870
+ j = js[0]
871
+ if phi.codomain() != curves[j]:
872
+ phi = E2.isomorphism_to(curves[j]) * phi
873
+ assert phi.domain() == curves[i] and phi.codomain() == curves[j]
874
+ add_tup([i,j,d,phi])
875
+ else:
876
+ curves.append(E2)
877
+ if verbose:
878
+ sys.stdout.write(" -added curve #%s..." % ncurves)
879
+ sys.stdout.flush()
880
+ add_tup([i,ncurves,d,phi])
881
+ ncurves += 1
882
+ i += 1
883
+
884
+ if verbose:
885
+ print("... isogeny class has size %s" % ncurves)
886
+
887
+ # key function for sorting
888
+ if E.has_rational_cm():
889
+ key_function = lambda E: (-E.cm_discriminant(),
890
+ flatten([list(ai) for ai in E.ainvs()]))
891
+ else:
892
+ key_function = lambda E: flatten([list(ai) for ai in E.ainvs()])
893
+
894
+ self.curves = sorted(curves, key=key_function)
895
+ perm = {ind: self.curves.index(Ei)
896
+ for ind, Ei in enumerate(curves)}
897
+ if verbose:
898
+ print("Sorting permutation = %s" % perm)
899
+
900
+ mat = MatrixSpace(ZZ, ncurves)(0)
901
+ self._maps = [[0] * ncurves for _ in range(ncurves)]
902
+ for i, j, l, phi in tuples:
903
+ if phi != 0:
904
+ mat[perm[i], perm[j]] = l
905
+ self._maps[perm[i]][perm[j]] = phi
906
+ self._mat = fill_isogeny_matrix(mat)
907
+ if verbose:
908
+ print("Matrix = %s" % self._mat)
909
+
910
+ if not E.has_rational_cm():
911
+ self._qfmat = None
912
+ return
913
+
914
+ # In the CM case, we will have found some "horizontal"
915
+ # isogenies of composite degree and would like to replace them
916
+ # by isogenies of prime degree, mainly to make the isogeny
917
+ # graph look better. We also construct a matrix whose entries
918
+ # are not degrees of cyclic isogenies, but rather quadratic
919
+ # forms (in 1 or 2 variables) representing the isogeny
920
+ # degrees. For this we take a short cut: properly speaking,
921
+ # when `\text{End}(E_1)=\text{End}(E_2)=O`, the set
922
+ # `\text{Hom}(E_1,E_2)` is a rank `1` projective `O`-module,
923
+ # hence has a well-defined ideal class associated to it, and
924
+ # hence (using an identification between the ideal class group
925
+ # and the group of classes of primitive quadratic forms of the
926
+ # same discriminant) an equivalence class of quadratic forms.
927
+ # But we currently only care about the numbers represented by
928
+ # the form, i.e. which genus it is in rather than the exact
929
+ # class. So it suffices to find one form of the correct
930
+ # discriminant which represents one isogeny degree from `E_1`
931
+ # to `E_2` in order to obtain a form which represents all such
932
+ # degrees.
933
+
934
+ if verbose:
935
+ print("Creating degree matrix (CM case)")
936
+
937
+ allQs = {} # keys: discriminants d
938
+ # values: lists of equivalence classes of
939
+ # primitive forms of discriminant d
940
+
941
+ def find_quadratic_form(d, n):
942
+ if d not in allQs:
943
+ from sage.quadratic_forms.binary_qf import BinaryQF_reduced_representatives
944
+
945
+ allQs[d] = BinaryQF_reduced_representatives(d, primitive_only=True)
946
+ # now test which of the Qs represents n
947
+ for Q in allQs[d]:
948
+ if Q.solve_integer(n):
949
+ return Q
950
+ raise ValueError("No form of discriminant %d represents %s" % (d,n))
951
+
952
+ mat = self._mat
953
+ qfmat = [[0 for i in range(ncurves)] for j in range(ncurves)]
954
+ for i, E1 in enumerate(self.curves):
955
+ for j, E2 in enumerate(self.curves):
956
+ if j < i:
957
+ qfmat[i][j] = qfmat[j][i]
958
+ mat[i,j] = mat[j,i]
959
+ elif i == j:
960
+ qfmat[i][j] = [1]
961
+ # mat[i,j] already 1
962
+ else:
963
+ d = E1.cm_discriminant()
964
+ if d != E2.cm_discriminant():
965
+ qfmat[i][j] = [mat[i,j]]
966
+ # mat[i,j] already unique
967
+ else: # horizontal isogeny
968
+ q = find_quadratic_form(d,mat[i,j])
969
+ qfmat[i][j] = list(q)
970
+ mat[i,j] = q.small_prime_value()
971
+
972
+ self._mat = mat
973
+ self._qfmat = qfmat
974
+ if verbose:
975
+ print("new matrix = %s" % mat)
976
+ print("matrix of forms = %s" % qfmat)
977
+
978
+ def _compute_matrix(self):
979
+ """
980
+ Compute the matrix, assuming that the list of curves is computed.
981
+
982
+ EXAMPLES::
983
+
984
+ sage: # needs sage.groups
985
+ sage: isocls = EllipticCurve('1225h1').isogeny_class('database')
986
+ sage: isocls._mat
987
+ sage: isocls._compute_matrix(); isocls._mat
988
+ [ 0 37]
989
+ [37 0]
990
+ """
991
+ self._mat = self.E.isogeny_class(order=self.curves)._mat
992
+
993
+ def _compute_isogenies(self):
994
+ """
995
+ EXAMPLES::
996
+
997
+ sage: E = EllipticCurve('15a1')
998
+ sage: isocls = E.isogeny_class()
999
+ sage: maps = isocls.isogenies() # indirect doctest
1000
+ sage: f = maps[0][1]
1001
+ sage: f.domain() == isocls[0] and f.codomain() == isocls[1]
1002
+ True
1003
+ """
1004
+ recomputed = self.E.isogeny_class(order=self.curves)
1005
+ self._mat = recomputed._mat
1006
+ # The domains and codomains here will be equal, but not the same Python object.
1007
+ self._maps = recomputed._maps
1008
+
1009
+
1010
+ class IsogenyClass_EC_Rational(IsogenyClass_EC_NumberField):
1011
+ r"""
1012
+ Isogeny classes for elliptic curves over `\QQ`.
1013
+ """
1014
+ def __init__(self, E, algorithm='sage', label=None, empty=False) -> None:
1015
+ r"""
1016
+ INPUT:
1017
+
1018
+ - ``E`` -- an elliptic curve over `\QQ`
1019
+
1020
+ - ``algorithm`` -- string (default: ``'sage'``); one of the
1021
+ following:
1022
+
1023
+ - ``'sage'`` -- use sage's implementation to compute the curves,
1024
+ matrix and isogenies
1025
+
1026
+ - ``'database'`` -- use the Cremona database (only works if the
1027
+ curve is in the database)
1028
+
1029
+ - ``label`` -- string; the label of this isogeny class
1030
+ (e.g. '15a' or '37.b'), used in printing
1031
+
1032
+ - ``empty`` -- don't compute the curves right now (used when reordering)
1033
+
1034
+ EXAMPLES::
1035
+
1036
+ sage: isocls = EllipticCurve('389a1').isogeny_class(); isocls
1037
+ Elliptic curve isogeny class 389a
1038
+ sage: E = EllipticCurve([0, 0, 0, 0, 1001]) # conductor 108216108
1039
+ sage: E.isogeny_class(order='database')
1040
+ Traceback (most recent call last):
1041
+ ...
1042
+ LookupError: Cremona database does not contain entry for
1043
+ Elliptic Curve defined by y^2 = x^3 + 1001 over Rational Field
1044
+ sage: TestSuite(isocls).run()
1045
+ """
1046
+ self._algorithm = algorithm
1047
+ IsogenyClass_EC.__init__(self, E, label=label, empty=empty)
1048
+
1049
+ def copy(self):
1050
+ """
1051
+ Return a copy (mostly used in reordering).
1052
+
1053
+ EXAMPLES::
1054
+
1055
+ sage: E = EllipticCurve('11a1')
1056
+ sage: C = E.isogeny_class()
1057
+ sage: C2 = C.copy()
1058
+ sage: C is C2
1059
+ False
1060
+ sage: C == C2
1061
+ True
1062
+ """
1063
+ ans = IsogenyClass_EC_Rational(self.E, self._algorithm, self._label, empty=True)
1064
+ # The following isn't needed internally, but it will keep
1065
+ # things from breaking if this is used for something other
1066
+ # than reordering.
1067
+ ans.curves = self.curves
1068
+ ans._mat = None
1069
+ ans._maps = None
1070
+ return ans
1071
+
1072
+ def _compute(self):
1073
+ """
1074
+ Compute the list of curves, and possibly the matrix and
1075
+ prime-degree isogenies (depending on the algorithm selected).
1076
+
1077
+ EXAMPLES::
1078
+
1079
+ sage: isocls = EllipticCurve('48a1').isogeny_class('sage').copy()
1080
+ sage: isocls._mat
1081
+ sage: isocls._compute(); isocls._mat
1082
+ [0 2 2 2 0 0]
1083
+ [2 0 0 0 2 2]
1084
+ [2 0 0 0 0 0]
1085
+ [2 0 0 0 0 0]
1086
+ [0 2 0 0 0 0]
1087
+ [0 2 0 0 0 0]
1088
+ """
1089
+ algorithm = self._algorithm
1090
+ from sage.matrix.matrix_space import MatrixSpace
1091
+ self._maps = None
1092
+ if algorithm == "database":
1093
+ try:
1094
+ label = self.E.cremona_label(space=False)
1095
+ except RuntimeError:
1096
+ raise RuntimeError("unable to find %s in the database" % self.E)
1097
+ db = sage.databases.cremona.CremonaDatabase()
1098
+ curves = db.isogeny_class(label)
1099
+ if not curves:
1100
+ raise RuntimeError("unable to find %s in the database" % self.E)
1101
+ # All curves will have the same conductor and isogeny class,
1102
+ # and there are most 8 of them, so lexicographic sorting is okay.
1103
+ self.curves = tuple(sorted(curves,
1104
+ key=lambda E: E.cremona_label()))
1105
+ self._mat = None
1106
+ elif algorithm == "sage":
1107
+ curves = [self.E.minimal_model()]
1108
+ ijl_triples = []
1109
+ l_list = None
1110
+ i = 0
1111
+ while i < len(curves):
1112
+ E = curves[i]
1113
+ isogs = E.isogenies_prime_degree(l_list)
1114
+ for phi in isogs:
1115
+ Edash = phi.codomain()
1116
+ l = phi.degree()
1117
+ # look to see if Edash is new. Note that the
1118
+ # curves returned by isogenies_prime_degree() are
1119
+ # standard minimal models, so it suffices to check
1120
+ # equality rather than isomorphism here.
1121
+ try:
1122
+ j = curves.index(Edash)
1123
+ except ValueError:
1124
+ j = len(curves)
1125
+ curves.append(Edash)
1126
+ ijl_triples.append((i,j,l,phi))
1127
+ if l_list is None:
1128
+ l_list = list({ZZ(f.degree()) for f in isogs})
1129
+ i += 1
1130
+ self.curves = tuple(curves)
1131
+ ncurves = len(curves)
1132
+ self._mat = MatrixSpace(ZZ,ncurves)(0)
1133
+ self._maps = [[0]*ncurves for _ in range(ncurves)]
1134
+ for i,j,l,phi in ijl_triples:
1135
+ self._mat[i,j] = l
1136
+ self._maps[i][j] = phi
1137
+ else:
1138
+ raise ValueError("unknown algorithm '%s'" % algorithm)
1139
+
1140
+
1141
+ def isogeny_degrees_cm(E, verbose=False):
1142
+ r"""
1143
+ Return a list of primes `\ell` sufficient to generate the
1144
+ isogeny class of `E`, where `E` has CM.
1145
+
1146
+ INPUT:
1147
+
1148
+ - ``E`` -- an elliptic curve defined over a number field
1149
+
1150
+ OUTPUT:
1151
+
1152
+ A finite list of primes `\ell` such that every curve isogenous to
1153
+ this curve can be obtained by a finite sequence of isogenies of
1154
+ degree one of the primes in the list. This list is not
1155
+ necessarily minimal.
1156
+
1157
+ ALGORITHM:
1158
+
1159
+ For curves with CM by the order `O` of discriminant `d`, the
1160
+ Galois representation is always non-surjective and the curve will
1161
+ admit `\ell`-isogenies for infinitely many primes `\ell`, but
1162
+ there are only finitely many codomains `E'`. The primes can be
1163
+ divided according to the discriminant `d'` of the CM order `O'`
1164
+ associated to `E`: either `O=O'`, or one contains the other with
1165
+ index `\ell`, since `\ell O\subset O'` and vice versa.
1166
+
1167
+ Case (1): `O=O'`. The degrees of all isogenies between `E` and
1168
+ `E'` are precisely the integers represented by one of the classes
1169
+ of binary quadratic forms `Q` of discriminant `d`. Hence to
1170
+ obtain all possible isomorphism classes of codomain `E'`, we need
1171
+ only use one prime `\ell` represented by each such class `Q`. It
1172
+ would in fact suffice to use primes represented by forms which
1173
+ generate the class group. Here we simply omit the principal class
1174
+ and one from each pair of inverse classes, and include a prime
1175
+ represented by each of the remaining forms.
1176
+
1177
+ Case (2): `[O':O]=\ell`: so `d=\ell^2d;`. We include all prime
1178
+ divisors of `d`.
1179
+
1180
+ Case (3): `[O:O']=\ell`: we may assume that `\ell` does not divide
1181
+ `d` as we have already included these, so `\ell` either splits or
1182
+ is inert in `O`; the class numbers satisfy `h(O')=(\ell\pm1)h(O)`
1183
+ accordingly. We include all primes `\ell` such that `\ell\pm1`
1184
+ divides the degree `[K:\QQ]`.
1185
+
1186
+ For curves with only potential CM we proceed as in the CM case,
1187
+ using `2[K:\QQ]` instead of `[K:\QQ]`.
1188
+
1189
+ EXAMPLES:
1190
+
1191
+ For curves with CM by a quadratic order of class number greater
1192
+ than `1`, we use the structure of the class group to only give one
1193
+ prime in each ideal class::
1194
+
1195
+ sage: pol = PolynomialRing(QQ,'x')([1,-3,5,-5,5,-3,1])
1196
+ sage: L.<a> = NumberField(pol)
1197
+ sage: j = hilbert_class_polynomial(-23).roots(L, multiplicities=False)[0]
1198
+ sage: E = EllipticCurve(j=j)
1199
+ sage: from sage.schemes.elliptic_curves.isogeny_class import isogeny_degrees_cm
1200
+ sage: isogeny_degrees_cm(E, verbose=True)
1201
+ CM case, discriminant = -23
1202
+ initial primes: {2}
1203
+ upward primes: {}
1204
+ downward ramified primes: {}
1205
+ downward split primes: {2, 3}
1206
+ downward inert primes: {5}
1207
+ primes generating the class group: [2]
1208
+ Set of primes before filtering: {2, 3, 5}
1209
+ List of primes after filtering: [2, 3]
1210
+ [2, 3]
1211
+
1212
+ TESTS:
1213
+
1214
+ Check that :issue:`36780` is fixed::
1215
+
1216
+ sage: x = polygen(QQ)
1217
+ sage: L5.<r5> = NumberField(x^2 - 5)
1218
+ sage: E = EllipticCurve(L5, [0, -4325477943600*r5 - 4195572876000])
1219
+ sage: from sage.schemes.elliptic_curves.isogeny_class import isogeny_degrees_cm
1220
+ sage: isogeny_degrees_cm(E)
1221
+ [3, 5]
1222
+ """
1223
+ if not E.has_cm():
1224
+ raise ValueError("possible_isogeny_degrees_cm(E) requires E to be an elliptic curve with CM")
1225
+ d = E.cm_discriminant()
1226
+
1227
+ if verbose:
1228
+ print("CM case, discriminant = %s" % d)
1229
+
1230
+ from sage.libs.pari import pari
1231
+ from sage.sets.set import Set
1232
+ from sage.arith.misc import kronecker as kronecker_symbol
1233
+
1234
+ n = E.base_field().absolute_degree()
1235
+ if not E.has_rational_cm():
1236
+ n *= 2
1237
+ # For discriminants with extra units there's an extra factor in the class number formula:
1238
+ if d == -4:
1239
+ n *= 2
1240
+ if d == -3:
1241
+ n *= 3
1242
+ divs = n.divisors()
1243
+
1244
+ data = pari(d).quadclassunit()
1245
+ # This has 4 components: the class number, class group
1246
+ # structure (ignored), class group generators (as quadratic
1247
+ # forms) and regulator (=1 since d<0, ignored).
1248
+
1249
+ h = data[0].sage()
1250
+
1251
+ # We must have 2*h dividing n, and will need the quotient so
1252
+ # see if the j-invariants of any proper sub-orders could lie
1253
+ # in the same field
1254
+
1255
+ n_over_2h = n//(2*h)
1256
+
1257
+ # Collect possible primes. First put in 2, and also 3 for
1258
+ # discriminant -3 (special case because of units):
1259
+
1260
+ L = Set([ZZ(2), ZZ(3)]) if d == -3 else Set([ZZ(2)])
1261
+ if verbose:
1262
+ print("initial primes: %s" % L)
1263
+
1264
+ # Step 1: "vertical" primes l such that the isogenous curve
1265
+ # has CM by an order whose index is l or 1/l times the index
1266
+ # of the order O of discriminant d. The latter case can only
1267
+ # happen when l^2 divides d.
1268
+
1269
+ # (a) ramified primes
1270
+
1271
+ ram_l = d.odd_part().prime_factors()
1272
+
1273
+ # if the CM is not rational we include all ramified primes,
1274
+ # which is simpler than using the class group later:
1275
+
1276
+ if not E.has_rational_cm():
1277
+ L1 = Set(ram_l)
1278
+ L += L1
1279
+ if verbose:
1280
+ print("ramified primes: %s" % L1)
1281
+
1282
+ else:
1283
+
1284
+ # "Upward" primes (index divided by l):
1285
+
1286
+ L1 = Set([l for l in ram_l if d.valuation(l) > 1])
1287
+ L += L1
1288
+ if verbose:
1289
+ print("upward primes: %s" % L1)
1290
+
1291
+ # "Downward" ramified primes; index multiplied by l, class
1292
+ # number multiplied by l, so l must divide n/2h:
1293
+
1294
+ L1 = Set([l for l in ram_l if l.divides(n_over_2h)])
1295
+ L += L1
1296
+ if verbose:
1297
+ print("downward ramified primes: %s" % L1)
1298
+
1299
+ # (b) Downward split primes; the suborder has class number (l-1)*h, so
1300
+ # l-1 must divide n/2h:
1301
+
1302
+ L1 = Set([lm1+1 for lm1 in divs
1303
+ if (lm1+1).is_prime() and kronecker_symbol(d,lm1+1) == +1])
1304
+ L += L1
1305
+ if verbose:
1306
+ print("downward split primes: %s" % L1)
1307
+
1308
+ # (c) Downward inert primes; the suborder has class number (l+1)*h, so
1309
+ # l+1 must divide n/2h:
1310
+
1311
+ L1 = Set([lp1-1 for lp1 in divs
1312
+ if (lp1-1).is_prime() and kronecker_symbol(d,lp1-1) == -1])
1313
+ L += L1
1314
+ if verbose:
1315
+ print("downward inert primes: %s" % L1)
1316
+
1317
+ # Horizontal primes (rational CM only): same order, degrees are
1318
+ # all integers represented by some binary quadratic form of
1319
+ # discriminant d, so we find a prime represented by each form.
1320
+
1321
+ if E.has_rational_cm():
1322
+ from sage.quadratic_forms.binary_qf import BinaryQF
1323
+ Qs = [BinaryQF(list(q)) for q in data[2]]
1324
+
1325
+ L1 = [Q.small_prime_value() for Q in Qs]
1326
+ if verbose:
1327
+ print("primes generating the class group: %s" % L1)
1328
+ L += Set(L1)
1329
+
1330
+ # Return sorted list
1331
+
1332
+ if verbose:
1333
+ print("Set of primes before filtering: %s" % L)
1334
+
1335
+ # This filter will quickly eliminate most false entries in the set
1336
+ from .gal_reps_number_field import Frobenius_filter
1337
+ L = Frobenius_filter(E, sorted(L))
1338
+ if verbose:
1339
+ print("List of primes after filtering: %s" % L)
1340
+ return L
1341
+
1342
+
1343
+ def possible_isogeny_degrees(E, algorithm='Billerey', max_l=None,
1344
+ num_l=None, exact=True, verbose=False):
1345
+ r"""
1346
+ Return a list of primes `\ell` sufficient to generate the
1347
+ isogeny class of `E`.
1348
+
1349
+ INPUT:
1350
+
1351
+ - ``E`` -- an elliptic curve defined over a number field
1352
+
1353
+ - ``algorithm`` -- string (default: ``'Billerey'``); algorithm to be
1354
+ used for non-CM curves: either ``'Billerey'``, ``'Larson'``, or
1355
+ ``'heuristic'``. Only relevant for non-CM curves and base fields
1356
+ other than `\QQ`.
1357
+
1358
+ - ``max_l`` -- integer or ``None``; only relevant for non-CM curves
1359
+ and algorithms ``'Billerey'`` and ``'heuristic'``. Controls the maximum
1360
+ prime used in either algorithm. If ``None``, use the default
1361
+ for that algorithm.
1362
+
1363
+ - ``num_l`` -- integer or ``None``; only relevant for non-CM curves
1364
+ and algorithm ``'Billerey'``. Controls the maximum number of primes
1365
+ used in the algorithm. If ``None``, use the default for that
1366
+ algorithm.
1367
+
1368
+ - ``exact`` -- boolean (default: ``True``); if ``True``, perform an
1369
+ additional check that the primes returned are all reducible. If
1370
+ ``False``, skip this step, in which case some of the primes
1371
+ returned may be irreducible.
1372
+
1373
+ OUTPUT:
1374
+
1375
+ A finite list of primes `\ell` such that every curve isogenous to
1376
+ this curve can be obtained by a finite sequence of isogenies of
1377
+ degree one of the primes in the list.
1378
+
1379
+ ALGORITHM:
1380
+
1381
+ For curves without CM, the set may be taken to be the finite set
1382
+ of primes at which the Galois representation is not surjective,
1383
+ since the existence of an `\ell`-isogeny is equivalent to the
1384
+ image of the mod-`\ell` Galois representation being contained in a
1385
+ Borel subgroup. Two rigorous algorithms have been implemented to
1386
+ determine this set, due to Larson and Billeray respectively. We
1387
+ also provide a non-rigorous 'heuristic' algorithm which only tests
1388
+ reducible primes up to a bound depending on the degree of the
1389
+ base field.
1390
+
1391
+ For curves with CM see the documentation for :meth:`isogeny_degrees_cm()`.
1392
+
1393
+ EXAMPLES:
1394
+
1395
+ For curves without CM we determine the primes at which the mod `p`
1396
+ Galois representation is reducible, i.e. contained in a Borel
1397
+ subgroup::
1398
+
1399
+ sage: from sage.schemes.elliptic_curves.isogeny_class import possible_isogeny_degrees
1400
+ sage: E = EllipticCurve('11a1')
1401
+ sage: possible_isogeny_degrees(E)
1402
+ [5]
1403
+ sage: possible_isogeny_degrees(E, algorithm='Larson')
1404
+ [5]
1405
+ sage: possible_isogeny_degrees(E, algorithm='Billerey')
1406
+ [5]
1407
+ sage: possible_isogeny_degrees(E, algorithm='heuristic')
1408
+ [5]
1409
+
1410
+ We check that in this case `E` really does have rational
1411
+ `5`-isogenies::
1412
+
1413
+ sage: [phi.degree() for phi in E.isogenies_prime_degree()]
1414
+ [5, 5]
1415
+
1416
+ Over an extension field::
1417
+
1418
+ sage: # long time
1419
+ sage: E3 = E.change_ring(CyclotomicField(3))
1420
+ sage: possible_isogeny_degrees(E3)
1421
+ [5]
1422
+ sage: [phi.degree() for phi in E3.isogenies_prime_degree()]
1423
+ [5, 5]
1424
+
1425
+ A higher degree example (LMFDB curve 5.5.170701.1-4.1-b1)::
1426
+
1427
+ sage: x = polygen(QQ)
1428
+ sage: K.<a> = NumberField(x^5 - x^4 - 6*x^3 + 4*x + 1)
1429
+ sage: E = EllipticCurve(K, [a^3 - a^2 - 5*a + 1, a^4 - a^3 - 5*a^2 - a + 1,
1430
+ ....: -a^4 + 2*a^3 + 5*a^2 - 5*a - 3, a^4 - a^3 - 5*a^2 - a,
1431
+ ....: -3*a^4 + 4*a^3 + 17*a^2 - 6*a - 12])
1432
+ sage: possible_isogeny_degrees(E, algorithm='heuristic')
1433
+ [2]
1434
+ sage: possible_isogeny_degrees(E, algorithm='Billerey')
1435
+ [2]
1436
+ sage: possible_isogeny_degrees(E, algorithm='Larson')
1437
+ [2]
1438
+
1439
+ LMFDB curve 4.4.8112.1-108.1-a5::
1440
+
1441
+ sage: x = polygen(QQ)
1442
+ sage: K.<a> = NumberField(x^4 - 5*x^2 + 3)
1443
+ sage: E = EllipticCurve(K, [a^2 - 2, -a^2 + 3, a^2 - 2, -50*a^2 + 35, 95*a^2 - 67])
1444
+ sage: possible_isogeny_degrees(E, exact=False, algorithm='Billerey') # long time (6.5s)
1445
+ [2, 5]
1446
+ sage: possible_isogeny_degrees(E, exact=False, algorithm='Larson')
1447
+ [2, 5]
1448
+ sage: possible_isogeny_degrees(E, exact=False, algorithm='heuristic')
1449
+ [2, 5]
1450
+ sage: possible_isogeny_degrees(E)
1451
+ [2, 5]
1452
+
1453
+ This function only returns the primes which are isogeny degrees::
1454
+
1455
+ sage: Set(E.isogeny_class().matrix().list()) # long time (7s)
1456
+ {1, 2, 4, 5, 20, 10}
1457
+
1458
+ For curves with CM by a quadratic order of class number greater
1459
+ than `1`, we use the structure of the class group to only give one
1460
+ prime in each ideal class::
1461
+
1462
+ sage: pol = PolynomialRing(QQ,'x')([1,-3,5,-5,5,-3,1])
1463
+ sage: L.<a> = NumberField(pol)
1464
+ sage: j = hilbert_class_polynomial(-23).roots(L, multiplicities=False)[0]
1465
+ sage: E = EllipticCurve(j=j)
1466
+ sage: from sage.schemes.elliptic_curves.isogeny_class import possible_isogeny_degrees
1467
+ sage: possible_isogeny_degrees(E, verbose=True)
1468
+ CM case, discriminant = -23
1469
+ initial primes: {2}
1470
+ upward primes: {}
1471
+ downward ramified primes: {}
1472
+ downward split primes: {2, 3}
1473
+ downward inert primes: {5}
1474
+ primes generating the class group: [2]
1475
+ Set of primes before filtering: {2, 3, 5}
1476
+ List of primes after filtering: [2, 3]
1477
+ [2, 3]
1478
+ """
1479
+ if E.has_cm():
1480
+ return isogeny_degrees_cm(E, verbose)
1481
+
1482
+ if E.base_field() == QQ:
1483
+ from sage.schemes.elliptic_curves.gal_reps_number_field import reducible_primes_naive
1484
+ return reducible_primes_naive(E, max_l=37, verbose=verbose)
1485
+
1486
+ # Non-CM case
1487
+
1488
+ # NB The following functions first computes a finite set
1489
+ # containing the reducible primes, then checks that each is
1490
+ # reducible by computing l-isogenies. This appears circular
1491
+ # but the computated l-isogenies for a fixed prime l is
1492
+ # cached.
1493
+
1494
+ if verbose:
1495
+ print("Non-CM case, using {} algorithm".format(algorithm))
1496
+
1497
+ # First we obtain a finite set of primes containing the reducible
1498
+ # ones Each of these algorithms includes application of the
1499
+ # "Frobenius filter" eliminating any ell for which there exists a
1500
+ # prime P of good reduction such that the Frobenius polynomial at
1501
+ # P does not factor modulo ell.
1502
+
1503
+ if algorithm == 'Larson':
1504
+ L = E.galois_representation().isogeny_bound()
1505
+
1506
+ elif algorithm == 'Billerey':
1507
+ from sage.schemes.elliptic_curves.gal_reps_number_field import reducible_primes_Billerey
1508
+ L = reducible_primes_Billerey(E, num_l=num_l, max_l=max_l, verbose=verbose)
1509
+
1510
+ elif algorithm == 'heuristic':
1511
+ from sage.schemes.elliptic_curves.gal_reps_number_field import reducible_primes_naive
1512
+ L = reducible_primes_naive(E, max_l=max_l, num_P=num_l, verbose=verbose)
1513
+
1514
+ else:
1515
+ raise ValueError("algorithm for possible_isogeny_degrees must be one of 'Larson', 'Billerey', 'heuristic'")
1516
+
1517
+ # The set L may contain irreducible primes. We optionally test
1518
+ # each one to see if it is actually reducible, by computing ell-isogenies:
1519
+
1520
+ if exact:
1521
+ L = [l for l in L if E.isogenies_prime_degree(l, minimal_models=False)]
1522
+
1523
+ return L