passagemath-schemes 10.6.40__cp314-cp314-macosx_13_0_arm64.whl

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

Potentially problematic release.


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

Files changed (314) hide show
  1. passagemath_schemes/.dylibs/libflint.22.0.dylib +0 -0
  2. passagemath_schemes/.dylibs/libgmp.10.dylib +0 -0
  3. passagemath_schemes/.dylibs/libgmpxx.4.dylib +0 -0
  4. passagemath_schemes/.dylibs/libmpfr.6.dylib +0 -0
  5. passagemath_schemes/__init__.py +3 -0
  6. passagemath_schemes-10.6.40.dist-info/METADATA +204 -0
  7. passagemath_schemes-10.6.40.dist-info/METADATA.bak +205 -0
  8. passagemath_schemes-10.6.40.dist-info/RECORD +314 -0
  9. passagemath_schemes-10.6.40.dist-info/WHEEL +6 -0
  10. passagemath_schemes-10.6.40.dist-info/top_level.txt +3 -0
  11. sage/all__sagemath_schemes.py +23 -0
  12. sage/databases/all__sagemath_schemes.py +7 -0
  13. sage/databases/cremona.py +1723 -0
  14. sage/dynamics/all__sagemath_schemes.py +2 -0
  15. sage/dynamics/arithmetic_dynamics/affine_ds.py +1083 -0
  16. sage/dynamics/arithmetic_dynamics/all.py +14 -0
  17. sage/dynamics/arithmetic_dynamics/berkovich_ds.py +1101 -0
  18. sage/dynamics/arithmetic_dynamics/dynamical_semigroup.py +1543 -0
  19. sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py +2426 -0
  20. sage/dynamics/arithmetic_dynamics/endPN_minimal_model.py +1169 -0
  21. sage/dynamics/arithmetic_dynamics/generic_ds.py +663 -0
  22. sage/dynamics/arithmetic_dynamics/product_projective_ds.py +339 -0
  23. sage/dynamics/arithmetic_dynamics/projective_ds.py +9558 -0
  24. sage/dynamics/arithmetic_dynamics/projective_ds_helper.cpython-314-darwin.so +0 -0
  25. sage/dynamics/arithmetic_dynamics/projective_ds_helper.pyx +301 -0
  26. sage/dynamics/arithmetic_dynamics/wehlerK3.py +2576 -0
  27. sage/lfunctions/all.py +18 -0
  28. sage/lfunctions/dokchitser.py +745 -0
  29. sage/lfunctions/pari.py +818 -0
  30. sage/lfunctions/zero_sums.cpython-314-darwin.so +0 -0
  31. sage/lfunctions/zero_sums.pyx +1847 -0
  32. sage/modular/abvar/abvar.py +5135 -0
  33. sage/modular/abvar/abvar_ambient_jacobian.py +413 -0
  34. sage/modular/abvar/abvar_newform.py +244 -0
  35. sage/modular/abvar/all.py +8 -0
  36. sage/modular/abvar/constructor.py +186 -0
  37. sage/modular/abvar/cuspidal_subgroup.py +371 -0
  38. sage/modular/abvar/finite_subgroup.py +896 -0
  39. sage/modular/abvar/homology.py +720 -0
  40. sage/modular/abvar/homspace.py +998 -0
  41. sage/modular/abvar/lseries.py +415 -0
  42. sage/modular/abvar/morphism.py +935 -0
  43. sage/modular/abvar/torsion_point.py +274 -0
  44. sage/modular/abvar/torsion_subgroup.py +740 -0
  45. sage/modular/all.py +43 -0
  46. sage/modular/arithgroup/all.py +20 -0
  47. sage/modular/arithgroup/arithgroup_element.cpython-314-darwin.so +0 -0
  48. sage/modular/arithgroup/arithgroup_element.pyx +474 -0
  49. sage/modular/arithgroup/arithgroup_generic.py +1402 -0
  50. sage/modular/arithgroup/arithgroup_perm.py +2692 -0
  51. sage/modular/arithgroup/congroup.cpython-314-darwin.so +0 -0
  52. sage/modular/arithgroup/congroup.pyx +334 -0
  53. sage/modular/arithgroup/congroup_gamma.py +363 -0
  54. sage/modular/arithgroup/congroup_gamma0.py +692 -0
  55. sage/modular/arithgroup/congroup_gamma1.py +653 -0
  56. sage/modular/arithgroup/congroup_gammaH.py +1469 -0
  57. sage/modular/arithgroup/congroup_generic.py +628 -0
  58. sage/modular/arithgroup/congroup_sl2z.py +267 -0
  59. sage/modular/arithgroup/farey_symbol.cpython-314-darwin.so +0 -0
  60. sage/modular/arithgroup/farey_symbol.pyx +1066 -0
  61. sage/modular/arithgroup/tests.py +418 -0
  62. sage/modular/btquotients/all.py +4 -0
  63. sage/modular/btquotients/btquotient.py +3753 -0
  64. sage/modular/btquotients/pautomorphicform.py +2570 -0
  65. sage/modular/buzzard.py +100 -0
  66. sage/modular/congroup.py +29 -0
  67. sage/modular/congroup_element.py +13 -0
  68. sage/modular/cusps.py +1109 -0
  69. sage/modular/cusps_nf.py +1270 -0
  70. sage/modular/dims.py +569 -0
  71. sage/modular/dirichlet.py +3310 -0
  72. sage/modular/drinfeld_modform/all.py +2 -0
  73. sage/modular/drinfeld_modform/element.py +446 -0
  74. sage/modular/drinfeld_modform/ring.py +773 -0
  75. sage/modular/drinfeld_modform/tutorial.py +236 -0
  76. sage/modular/etaproducts.py +1065 -0
  77. sage/modular/hecke/algebra.py +746 -0
  78. sage/modular/hecke/all.py +20 -0
  79. sage/modular/hecke/ambient_module.py +1019 -0
  80. sage/modular/hecke/degenmap.py +119 -0
  81. sage/modular/hecke/element.py +325 -0
  82. sage/modular/hecke/hecke_operator.py +780 -0
  83. sage/modular/hecke/homspace.py +206 -0
  84. sage/modular/hecke/module.py +1767 -0
  85. sage/modular/hecke/morphism.py +174 -0
  86. sage/modular/hecke/submodule.py +989 -0
  87. sage/modular/hypergeometric_misc.cpython-314-darwin.so +0 -0
  88. sage/modular/hypergeometric_misc.pxd +4 -0
  89. sage/modular/hypergeometric_misc.pyx +166 -0
  90. sage/modular/hypergeometric_motive.py +2017 -0
  91. sage/modular/local_comp/all.py +2 -0
  92. sage/modular/local_comp/liftings.py +292 -0
  93. sage/modular/local_comp/local_comp.py +1071 -0
  94. sage/modular/local_comp/smoothchar.py +1825 -0
  95. sage/modular/local_comp/type_space.py +748 -0
  96. sage/modular/modform/all.py +30 -0
  97. sage/modular/modform/ambient.py +815 -0
  98. sage/modular/modform/ambient_R.py +177 -0
  99. sage/modular/modform/ambient_eps.py +306 -0
  100. sage/modular/modform/ambient_g0.py +124 -0
  101. sage/modular/modform/ambient_g1.py +204 -0
  102. sage/modular/modform/constructor.py +545 -0
  103. sage/modular/modform/cuspidal_submodule.py +708 -0
  104. sage/modular/modform/defaults.py +14 -0
  105. sage/modular/modform/eis_series.py +505 -0
  106. sage/modular/modform/eisenstein_submodule.py +663 -0
  107. sage/modular/modform/element.py +4131 -0
  108. sage/modular/modform/find_generators.py +59 -0
  109. sage/modular/modform/half_integral.py +154 -0
  110. sage/modular/modform/hecke_operator_on_qexp.py +247 -0
  111. sage/modular/modform/j_invariant.py +47 -0
  112. sage/modular/modform/l_series_gross_zagier.py +133 -0
  113. sage/modular/modform/l_series_gross_zagier_coeffs.cpython-314-darwin.so +0 -0
  114. sage/modular/modform/l_series_gross_zagier_coeffs.pyx +177 -0
  115. sage/modular/modform/notes.py +45 -0
  116. sage/modular/modform/numerical.py +514 -0
  117. sage/modular/modform/periods.py +14 -0
  118. sage/modular/modform/ring.py +1257 -0
  119. sage/modular/modform/space.py +1860 -0
  120. sage/modular/modform/submodule.py +118 -0
  121. sage/modular/modform/tests.py +64 -0
  122. sage/modular/modform/theta.py +110 -0
  123. sage/modular/modform/vm_basis.py +381 -0
  124. sage/modular/modform/weight1.py +220 -0
  125. sage/modular/modform_hecketriangle/abstract_ring.py +1932 -0
  126. sage/modular/modform_hecketriangle/abstract_space.py +2528 -0
  127. sage/modular/modform_hecketriangle/all.py +30 -0
  128. sage/modular/modform_hecketriangle/analytic_type.py +590 -0
  129. sage/modular/modform_hecketriangle/constructor.py +416 -0
  130. sage/modular/modform_hecketriangle/element.py +351 -0
  131. sage/modular/modform_hecketriangle/functors.py +752 -0
  132. sage/modular/modform_hecketriangle/graded_ring.py +541 -0
  133. sage/modular/modform_hecketriangle/graded_ring_element.py +2225 -0
  134. sage/modular/modform_hecketriangle/hecke_triangle_group_element.py +3352 -0
  135. sage/modular/modform_hecketriangle/hecke_triangle_groups.py +1432 -0
  136. sage/modular/modform_hecketriangle/readme.py +1214 -0
  137. sage/modular/modform_hecketriangle/series_constructor.py +580 -0
  138. sage/modular/modform_hecketriangle/space.py +1037 -0
  139. sage/modular/modform_hecketriangle/subspace.py +423 -0
  140. sage/modular/modsym/all.py +17 -0
  141. sage/modular/modsym/ambient.py +3846 -0
  142. sage/modular/modsym/boundary.py +1420 -0
  143. sage/modular/modsym/element.py +336 -0
  144. sage/modular/modsym/g1list.py +178 -0
  145. sage/modular/modsym/ghlist.py +182 -0
  146. sage/modular/modsym/hecke_operator.py +73 -0
  147. sage/modular/modsym/manin_symbol.cpython-314-darwin.so +0 -0
  148. sage/modular/modsym/manin_symbol.pxd +5 -0
  149. sage/modular/modsym/manin_symbol.pyx +497 -0
  150. sage/modular/modsym/manin_symbol_list.py +1295 -0
  151. sage/modular/modsym/modsym.py +400 -0
  152. sage/modular/modsym/modular_symbols.py +384 -0
  153. sage/modular/modsym/p1list.cpython-314-darwin.so +0 -0
  154. sage/modular/modsym/p1list.pxd +29 -0
  155. sage/modular/modsym/p1list.pyx +1372 -0
  156. sage/modular/modsym/p1list_nf.py +1241 -0
  157. sage/modular/modsym/relation_matrix.py +591 -0
  158. sage/modular/modsym/relation_matrix_pyx.cpython-314-darwin.so +0 -0
  159. sage/modular/modsym/relation_matrix_pyx.pyx +108 -0
  160. sage/modular/modsym/space.py +2468 -0
  161. sage/modular/modsym/subspace.py +455 -0
  162. sage/modular/modsym/tests.py +375 -0
  163. sage/modular/multiple_zeta.py +2632 -0
  164. sage/modular/multiple_zeta_F_algebra.py +786 -0
  165. sage/modular/overconvergent/all.py +6 -0
  166. sage/modular/overconvergent/genus0.py +1878 -0
  167. sage/modular/overconvergent/hecke_series.py +1187 -0
  168. sage/modular/overconvergent/weightspace.py +778 -0
  169. sage/modular/pollack_stevens/all.py +4 -0
  170. sage/modular/pollack_stevens/distributions.py +874 -0
  171. sage/modular/pollack_stevens/fund_domain.py +1572 -0
  172. sage/modular/pollack_stevens/manin_map.py +859 -0
  173. sage/modular/pollack_stevens/modsym.py +1593 -0
  174. sage/modular/pollack_stevens/padic_lseries.py +417 -0
  175. sage/modular/pollack_stevens/sigma0.py +534 -0
  176. sage/modular/pollack_stevens/space.py +1076 -0
  177. sage/modular/quasimodform/all.py +3 -0
  178. sage/modular/quasimodform/element.py +845 -0
  179. sage/modular/quasimodform/ring.py +828 -0
  180. sage/modular/quatalg/all.py +3 -0
  181. sage/modular/quatalg/brandt.py +1642 -0
  182. sage/modular/ssmod/all.py +8 -0
  183. sage/modular/ssmod/ssmod.py +827 -0
  184. sage/rings/all__sagemath_schemes.py +1 -0
  185. sage/rings/polynomial/all__sagemath_schemes.py +1 -0
  186. sage/rings/polynomial/binary_form_reduce.py +585 -0
  187. sage/schemes/all.py +41 -0
  188. sage/schemes/berkovich/all.py +6 -0
  189. sage/schemes/berkovich/berkovich_cp_element.py +2582 -0
  190. sage/schemes/berkovich/berkovich_space.py +748 -0
  191. sage/schemes/curves/affine_curve.py +2928 -0
  192. sage/schemes/curves/all.py +33 -0
  193. sage/schemes/curves/closed_point.py +434 -0
  194. sage/schemes/curves/constructor.py +381 -0
  195. sage/schemes/curves/curve.py +542 -0
  196. sage/schemes/curves/plane_curve_arrangement.py +1283 -0
  197. sage/schemes/curves/point.py +463 -0
  198. sage/schemes/curves/projective_curve.py +3026 -0
  199. sage/schemes/curves/zariski_vankampen.py +1932 -0
  200. sage/schemes/cyclic_covers/all.py +2 -0
  201. sage/schemes/cyclic_covers/charpoly_frobenius.py +320 -0
  202. sage/schemes/cyclic_covers/constructor.py +137 -0
  203. sage/schemes/cyclic_covers/cycliccover_finite_field.py +1309 -0
  204. sage/schemes/cyclic_covers/cycliccover_generic.py +310 -0
  205. sage/schemes/elliptic_curves/BSD.py +1036 -0
  206. sage/schemes/elliptic_curves/Qcurves.py +592 -0
  207. sage/schemes/elliptic_curves/addition_formulas_ring.py +94 -0
  208. sage/schemes/elliptic_curves/all.py +49 -0
  209. sage/schemes/elliptic_curves/cardinality.py +609 -0
  210. sage/schemes/elliptic_curves/cm.py +1102 -0
  211. sage/schemes/elliptic_curves/constructor.py +1552 -0
  212. sage/schemes/elliptic_curves/ec_database.py +175 -0
  213. sage/schemes/elliptic_curves/ell_curve_isogeny.py +3972 -0
  214. sage/schemes/elliptic_curves/ell_egros.py +459 -0
  215. sage/schemes/elliptic_curves/ell_field.py +2836 -0
  216. sage/schemes/elliptic_curves/ell_finite_field.py +3359 -0
  217. sage/schemes/elliptic_curves/ell_generic.py +3760 -0
  218. sage/schemes/elliptic_curves/ell_local_data.py +1207 -0
  219. sage/schemes/elliptic_curves/ell_modular_symbols.py +775 -0
  220. sage/schemes/elliptic_curves/ell_number_field.py +4220 -0
  221. sage/schemes/elliptic_curves/ell_padic_field.py +107 -0
  222. sage/schemes/elliptic_curves/ell_point.py +4787 -0
  223. sage/schemes/elliptic_curves/ell_rational_field.py +7368 -0
  224. sage/schemes/elliptic_curves/ell_tate_curve.py +671 -0
  225. sage/schemes/elliptic_curves/ell_torsion.py +436 -0
  226. sage/schemes/elliptic_curves/ell_wp.py +352 -0
  227. sage/schemes/elliptic_curves/formal_group.py +760 -0
  228. sage/schemes/elliptic_curves/gal_reps.py +1459 -0
  229. sage/schemes/elliptic_curves/gal_reps_number_field.py +1669 -0
  230. sage/schemes/elliptic_curves/gp_simon.py +152 -0
  231. sage/schemes/elliptic_curves/heegner.py +7335 -0
  232. sage/schemes/elliptic_curves/height.py +2109 -0
  233. sage/schemes/elliptic_curves/hom.py +1406 -0
  234. sage/schemes/elliptic_curves/hom_composite.py +934 -0
  235. sage/schemes/elliptic_curves/hom_frobenius.py +522 -0
  236. sage/schemes/elliptic_curves/hom_scalar.py +531 -0
  237. sage/schemes/elliptic_curves/hom_sum.py +682 -0
  238. sage/schemes/elliptic_curves/hom_velusqrt.py +1290 -0
  239. sage/schemes/elliptic_curves/homset.py +271 -0
  240. sage/schemes/elliptic_curves/isogeny_class.py +1521 -0
  241. sage/schemes/elliptic_curves/isogeny_small_degree.py +2797 -0
  242. sage/schemes/elliptic_curves/jacobian.py +237 -0
  243. sage/schemes/elliptic_curves/kodaira_symbol.py +344 -0
  244. sage/schemes/elliptic_curves/kraus.py +1014 -0
  245. sage/schemes/elliptic_curves/lseries_ell.py +943 -0
  246. sage/schemes/elliptic_curves/mod5family.py +105 -0
  247. sage/schemes/elliptic_curves/mod_poly.py +197 -0
  248. sage/schemes/elliptic_curves/mod_sym_num.cpython-314-darwin.so +0 -0
  249. sage/schemes/elliptic_curves/mod_sym_num.pyx +3796 -0
  250. sage/schemes/elliptic_curves/modular_parametrization.py +305 -0
  251. sage/schemes/elliptic_curves/padic_lseries.py +1793 -0
  252. sage/schemes/elliptic_curves/padics.py +1816 -0
  253. sage/schemes/elliptic_curves/period_lattice.py +2234 -0
  254. sage/schemes/elliptic_curves/period_lattice_region.cpython-314-darwin.so +0 -0
  255. sage/schemes/elliptic_curves/period_lattice_region.pyx +722 -0
  256. sage/schemes/elliptic_curves/saturation.py +715 -0
  257. sage/schemes/elliptic_curves/sha_tate.py +1158 -0
  258. sage/schemes/elliptic_curves/weierstrass_morphism.py +1117 -0
  259. sage/schemes/elliptic_curves/weierstrass_transform.py +200 -0
  260. sage/schemes/hyperelliptic_curves/all.py +6 -0
  261. sage/schemes/hyperelliptic_curves/constructor.py +291 -0
  262. sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py +1914 -0
  263. sage/schemes/hyperelliptic_curves/hyperelliptic_g2.py +192 -0
  264. sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py +954 -0
  265. sage/schemes/hyperelliptic_curves/hyperelliptic_padic_field.py +1332 -0
  266. sage/schemes/hyperelliptic_curves/hyperelliptic_rational_field.py +84 -0
  267. sage/schemes/hyperelliptic_curves/invariants.py +410 -0
  268. sage/schemes/hyperelliptic_curves/jacobian_endomorphism_utils.py +315 -0
  269. sage/schemes/hyperelliptic_curves/jacobian_g2.py +32 -0
  270. sage/schemes/hyperelliptic_curves/jacobian_generic.py +419 -0
  271. sage/schemes/hyperelliptic_curves/jacobian_homset.py +186 -0
  272. sage/schemes/hyperelliptic_curves/jacobian_morphism.py +875 -0
  273. sage/schemes/hyperelliptic_curves/kummer_surface.py +99 -0
  274. sage/schemes/hyperelliptic_curves/mestre.py +302 -0
  275. sage/schemes/hyperelliptic_curves/monsky_washnitzer.py +3871 -0
  276. sage/schemes/jacobians/abstract_jacobian.py +277 -0
  277. sage/schemes/jacobians/all.py +2 -0
  278. sage/schemes/overview.py +161 -0
  279. sage/schemes/plane_conics/all.py +22 -0
  280. sage/schemes/plane_conics/con_field.py +1296 -0
  281. sage/schemes/plane_conics/con_finite_field.py +158 -0
  282. sage/schemes/plane_conics/con_number_field.py +456 -0
  283. sage/schemes/plane_conics/con_rational_field.py +406 -0
  284. sage/schemes/plane_conics/con_rational_function_field.py +580 -0
  285. sage/schemes/plane_conics/constructor.py +249 -0
  286. sage/schemes/plane_quartics/all.py +2 -0
  287. sage/schemes/plane_quartics/quartic_constructor.py +71 -0
  288. sage/schemes/plane_quartics/quartic_generic.py +73 -0
  289. sage/schemes/riemann_surfaces/all.py +1 -0
  290. sage/schemes/riemann_surfaces/riemann_surface.py +4117 -0
  291. sage_wheels/share/cremona/cremona_mini.db +0 -0
  292. sage_wheels/share/ellcurves/rank0 +30427 -0
  293. sage_wheels/share/ellcurves/rank1 +31871 -0
  294. sage_wheels/share/ellcurves/rank10 +6 -0
  295. sage_wheels/share/ellcurves/rank11 +6 -0
  296. sage_wheels/share/ellcurves/rank12 +1 -0
  297. sage_wheels/share/ellcurves/rank14 +1 -0
  298. sage_wheels/share/ellcurves/rank15 +1 -0
  299. sage_wheels/share/ellcurves/rank17 +1 -0
  300. sage_wheels/share/ellcurves/rank19 +1 -0
  301. sage_wheels/share/ellcurves/rank2 +2388 -0
  302. sage_wheels/share/ellcurves/rank20 +1 -0
  303. sage_wheels/share/ellcurves/rank21 +1 -0
  304. sage_wheels/share/ellcurves/rank22 +1 -0
  305. sage_wheels/share/ellcurves/rank23 +1 -0
  306. sage_wheels/share/ellcurves/rank24 +1 -0
  307. sage_wheels/share/ellcurves/rank28 +1 -0
  308. sage_wheels/share/ellcurves/rank3 +836 -0
  309. sage_wheels/share/ellcurves/rank4 +10 -0
  310. sage_wheels/share/ellcurves/rank5 +5 -0
  311. sage_wheels/share/ellcurves/rank6 +5 -0
  312. sage_wheels/share/ellcurves/rank7 +5 -0
  313. sage_wheels/share/ellcurves/rank8 +6 -0
  314. sage_wheels/share/ellcurves/rank9 +7 -0
@@ -0,0 +1,1521 @@
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):
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):
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):
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):
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):
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, algorithm='Billerey', minimal_models=True):
602
+ r"""
603
+ INPUT:
604
+
605
+ - ``E`` -- an elliptic curve over a number field
606
+
607
+ - ``reducible_primes`` -- list of integers, or ``None`` (default); if
608
+ not ``None`` then this should be a list of primes; in computing the
609
+ isogeny class, only composites isogenies of these degrees will be used.
610
+
611
+ - ``algorithm`` -- string (default: ``'Billerey'``); the algorithm
612
+ to use to compute the reducible primes. Ignored for CM
613
+ curves or if ``reducible_primes`` is provided. Values are
614
+ ``'Billerey'`` (default), ``'Larson'``, and ``'heuristic'``.
615
+
616
+ - ``minimal_models`` -- boolean (default: ``True``); if ``True``,
617
+ all curves in the class will be minimal or semi-minimal
618
+ models. Over fields of larger degree it can be expensive to
619
+ compute these so set to ``False``.
620
+
621
+ EXAMPLES::
622
+
623
+ sage: K.<i> = QuadraticField(-1)
624
+ sage: E = EllipticCurve(K, [0,0,0,0,1])
625
+ sage: C = E.isogeny_class(); C
626
+ Isogeny class of Elliptic Curve defined by y^2 = x^3 + 1
627
+ over Number Field in i with defining polynomial x^2 + 1 with i = 1*I
628
+
629
+ The curves in the class (sorted)::
630
+
631
+ sage: [E1.ainvs() for E1 in C]
632
+ [(0, 0, 0, 0, -27),
633
+ (0, 0, 0, 0, 1),
634
+ (i + 1, i, 0, 3, -i),
635
+ (i + 1, i, 0, 33, 91*i)]
636
+
637
+ The matrix of degrees of cyclic isogenies between curves::
638
+
639
+ sage: C.matrix()
640
+ [1 3 6 2]
641
+ [3 1 2 6]
642
+ [6 2 1 3]
643
+ [2 6 3 1]
644
+
645
+ The array of isogenies themselves is not filled out but only
646
+ contains those used to construct the class, the other entries
647
+ containing the integer 0. This will be changed when the
648
+ class :class:`EllipticCurveIsogeny` allowed composition. In
649
+ this case we used `2`-isogenies to go from 0 to 2 and from 1
650
+ to 3, and `3`-isogenies to go from 0 to 1 and from 2 to 3::
651
+
652
+ sage: isogs = C.isogenies()
653
+ sage: [((i,j), isogs[i][j].degree())
654
+ ....: for i in range(4) for j in range(4) if isogs[i][j] != 0]
655
+ [((0, 1), 3),
656
+ ((0, 3), 2),
657
+ ((1, 0), 3),
658
+ ((1, 2), 2),
659
+ ((2, 1), 2),
660
+ ((2, 3), 3),
661
+ ((3, 0), 2),
662
+ ((3, 2), 3)]
663
+ sage: [((i,j), isogs[i][j].x_rational_map())
664
+ ....: for i in range(4) for j in range(4) if isogs[i][j] != 0]
665
+ [((0, 1), (1/9*x^3 - 12)/x^2),
666
+ ((0, 3), (1/2*i*x^2 - 2*i*x + 15*i)/(x - 3)),
667
+ ((1, 0), (x^3 + 4)/x^2),
668
+ ((1, 2), (1/2*i*x^2 + i)/(x + 1)),
669
+ ((2, 1), (-1/2*i*x^2 - 1/2*i)/(x - 1/2*i)),
670
+ ((2, 3), (x^3 - 2*i*x^2 - 7*x + 4*i)/(x^2 - 2*i*x - 1)),
671
+ ((3, 0), (-1/2*i*x^2 + 2*x - 5/2*i)/(x + 7/2*i)),
672
+ ((3, 2), (1/9*x^3 + 2/3*i*x^2 - 13/3*x - 116/9*i)/(x^2 + 10*i*x - 25))]
673
+
674
+ sage: K.<i> = QuadraticField(-1)
675
+ sage: E = EllipticCurve([1+i, -i, i, 1, 0])
676
+ sage: C = E.isogeny_class(); C
677
+ Isogeny class of Elliptic Curve defined
678
+ by y^2 + (i+1)*x*y + i*y = x^3 + (-i)*x^2 + x
679
+ over Number Field in i with defining polynomial x^2 + 1 with i = 1*I
680
+ sage: len(C)
681
+ 6
682
+ sage: C.matrix()
683
+ [ 1 3 9 18 6 2]
684
+ [ 3 1 3 6 2 6]
685
+ [ 9 3 1 2 6 18]
686
+ [18 6 2 1 3 9]
687
+ [ 6 2 6 3 1 3]
688
+ [ 2 6 18 9 3 1]
689
+ sage: [E1.ainvs() for E1 in C]
690
+ [(i + 1, i - 1, i, -i - 1, -i + 1),
691
+ (i + 1, i - 1, i, 14*i + 4, 7*i + 14),
692
+ (i + 1, i - 1, i, 59*i + 99, 372*i - 410),
693
+ (i + 1, -i, i, -240*i - 399, 2869*i + 2627),
694
+ (i + 1, -i, i, -5*i - 4, 2*i + 5),
695
+ (i + 1, -i, i, 1, 0)]
696
+
697
+ An example with CM by `\sqrt{-5}`::
698
+
699
+ sage: pol = PolynomialRing(QQ,'x')([1,0,3,0,1])
700
+ sage: K.<c> = NumberField(pol)
701
+ sage: j = 1480640 + 565760*c^2
702
+ sage: E = EllipticCurve(j=j)
703
+ sage: E.has_cm()
704
+ True
705
+ sage: E.has_rational_cm()
706
+ True
707
+ sage: E.cm_discriminant()
708
+ -20
709
+ sage: C = E.isogeny_class()
710
+ sage: len(C)
711
+ 2
712
+ sage: C.matrix()
713
+ [1 2]
714
+ [2 1]
715
+ sage: [E.ainvs() for E in C]
716
+ [(0, 0, 0, 83490*c^2 - 147015, -64739840*c^2 - 84465260),
717
+ (0, 0, 0, -161535*c^2 + 70785, -62264180*c^3 + 6229080*c)]
718
+ sage: C.isogenies()[0][1]
719
+ Isogeny of degree 2
720
+ from Elliptic Curve defined by
721
+ y^2 = x^3 + (83490*c^2-147015)*x + (-64739840*c^2-84465260)
722
+ over Number Field in c with defining polynomial x^4 + 3*x^2 + 1
723
+ to Elliptic Curve defined by
724
+ y^2 = x^3 + (-161535*c^2+70785)*x + (-62264180*c^3+6229080*c)
725
+ over Number Field in c with defining polynomial x^4 + 3*x^2 + 1
726
+
727
+ TESTS::
728
+
729
+ sage: TestSuite(C).run()
730
+ """
731
+ self._algorithm = "sage"
732
+ self._reducible_primes = reducible_primes
733
+ self._algorithm = algorithm
734
+ self._minimal_models = minimal_models
735
+ IsogenyClass_EC.__init__(self, E, label=None, empty=False)
736
+
737
+ def copy(self):
738
+ """
739
+ Return a copy (mostly used in reordering).
740
+
741
+ EXAMPLES::
742
+
743
+ sage: K.<i> = QuadraticField(-1)
744
+ sage: E = EllipticCurve(K, [0,0,0,0,1])
745
+ sage: C = E.isogeny_class()
746
+ sage: C2 = C.copy()
747
+ sage: C is C2
748
+ False
749
+ sage: C == C2
750
+ True
751
+ """
752
+ ans = IsogenyClass_EC_NumberField(self.E, reducible_primes=self._reducible_primes, algorithm=self._algorithm, minimal_models=self._minimal_models)
753
+ # The following isn't needed internally, but it will keep
754
+ # things from breaking if this is used for something other
755
+ # than reordering.
756
+ ans.curves = self.curves
757
+ ans._mat = None
758
+ ans._maps = None
759
+ return ans
760
+
761
+ def _compute(self, verbose=False):
762
+ """
763
+ Compute the list of curves, the matrix and prime-degree
764
+ isogenies.
765
+
766
+ EXAMPLES::
767
+
768
+ sage: K.<i> = QuadraticField(-1)
769
+ sage: E = EllipticCurve(K, [0,0,0,0,1])
770
+ sage: C = E.isogeny_class()
771
+ sage: C2 = C.copy()
772
+ sage: C2._mat
773
+ sage: C2._compute()
774
+ sage: C2._mat
775
+ [1 3 6 2]
776
+ [3 1 2 6]
777
+ [6 2 1 3]
778
+ [2 6 3 1]
779
+
780
+ sage: C2._compute(verbose=True)
781
+ 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
782
+ Sorting permutation = {0: 1, 1: 2, 2: 0, 3: 3}
783
+ Matrix = [1 3 6 2]
784
+ [3 1 2 6]
785
+ [6 2 1 3]
786
+ [2 6 3 1]
787
+
788
+ TESTS:
789
+
790
+ Check that :issue:`19030` is fixed (codomains of reverse isogenies were wrong)::
791
+
792
+ sage: x = polygen(QQ, 'x')
793
+ sage: K.<i> = NumberField(x^2 + 1)
794
+ sage: E = EllipticCurve([1, i + 1, 1, -72*i + 8, 95*i + 146])
795
+ sage: C = E.isogeny_class()
796
+ sage: curves = C.curves
797
+ sage: isos = C.isogenies()
798
+ sage: isos[0][3].codomain() == curves[3]
799
+ True
800
+ """
801
+ from sage.schemes.elliptic_curves.ell_curve_isogeny import fill_isogeny_matrix
802
+ from sage.matrix.matrix_space import MatrixSpace
803
+ from sage.sets.set import Set
804
+ self._maps = None
805
+
806
+ if self._minimal_models:
807
+ E = self.E.global_minimal_model(semi_global=True)
808
+ else:
809
+ E = self.E
810
+
811
+ degs = self._reducible_primes
812
+ if degs is None:
813
+ self._reducible_primes = possible_isogeny_degrees(E, algorithm=self._algorithm)
814
+ degs = self._reducible_primes
815
+ if verbose:
816
+ import sys
817
+ sys.stdout.write(" possible isogeny degrees: %s" % degs)
818
+ sys.stdout.flush()
819
+ isogenies = E.isogenies_prime_degree(degs, minimal_models=self._minimal_models)
820
+ if verbose:
821
+ sys.stdout.write(" -actual isogeny degrees: %s" % Set(phi.degree() for phi in isogenies))
822
+ sys.stdout.flush()
823
+ # Add all new codomains to the list and collect degrees:
824
+ curves = [E]
825
+ ncurves = 1
826
+ degs = []
827
+ # tuples (i,j,l,phi) where curve i is l-isogenous to curve j via phi
828
+ tuples = []
829
+
830
+ def add_tup(t):
831
+ for T in [t, [t[1], t[0], t[2], 0]]:
832
+ if T not in tuples:
833
+ tuples.append(T)
834
+ if verbose:
835
+ sys.stdout.write(" -added tuple %s..." % T[:3])
836
+ sys.stdout.flush()
837
+
838
+ for phi in isogenies:
839
+ E2 = phi.codomain()
840
+ d = ZZ(phi.degree())
841
+ if not any(E2.is_isomorphic(E3) for E3 in curves):
842
+ curves.append(E2)
843
+ if verbose:
844
+ sys.stdout.write(" -added curve #%s (degree %s)..." % (ncurves,d))
845
+ sys.stdout.flush()
846
+ add_tup([0,ncurves,d,phi])
847
+ ncurves += 1
848
+ if d not in degs:
849
+ degs.append(d)
850
+ if verbose:
851
+ sys.stdout.write("... relevant degrees: %s..." % degs)
852
+ sys.stdout.write(" -now completing the isogeny class...")
853
+ sys.stdout.flush()
854
+
855
+ i = 1
856
+ while i < ncurves:
857
+ E1 = curves[i]
858
+ if verbose:
859
+ sys.stdout.write(" -processing curve #%s..." % i)
860
+ sys.stdout.flush()
861
+
862
+ isogenies = E1.isogenies_prime_degree(degs, minimal_models=self._minimal_models)
863
+
864
+ for phi in isogenies:
865
+ E2 = phi.codomain()
866
+ d = phi.degree()
867
+ js = [j for j,E3 in enumerate(curves) if E2.is_isomorphic(E3)]
868
+ if js: # seen codomain already -- up to isomorphism
869
+ j = js[0]
870
+ if phi.codomain() != curves[j]:
871
+ phi = E2.isomorphism_to(curves[j]) * phi
872
+ assert phi.domain() == curves[i] and phi.codomain() == curves[j]
873
+ add_tup([i,j,d,phi])
874
+ else:
875
+ curves.append(E2)
876
+ if verbose:
877
+ sys.stdout.write(" -added curve #%s..." % ncurves)
878
+ sys.stdout.flush()
879
+ add_tup([i,ncurves,d,phi])
880
+ ncurves += 1
881
+ i += 1
882
+
883
+ if verbose:
884
+ print("... isogeny class has size %s" % ncurves)
885
+
886
+ # key function for sorting
887
+ if E.has_rational_cm():
888
+ key_function = lambda E: (-E.cm_discriminant(),
889
+ flatten([list(ai) for ai in E.ainvs()]))
890
+ else:
891
+ key_function = lambda E: flatten([list(ai) for ai in E.ainvs()])
892
+
893
+ self.curves = sorted(curves, key=key_function)
894
+ perm = {ind: self.curves.index(Ei)
895
+ for ind, Ei in enumerate(curves)}
896
+ if verbose:
897
+ print("Sorting permutation = %s" % perm)
898
+
899
+ mat = MatrixSpace(ZZ, ncurves)(0)
900
+ self._maps = [[0] * ncurves for _ in range(ncurves)]
901
+ for i, j, l, phi in tuples:
902
+ if phi != 0:
903
+ mat[perm[i], perm[j]] = l
904
+ self._maps[perm[i]][perm[j]] = phi
905
+ self._mat = fill_isogeny_matrix(mat)
906
+ if verbose:
907
+ print("Matrix = %s" % self._mat)
908
+
909
+ if not E.has_rational_cm():
910
+ self._qfmat = None
911
+ return
912
+
913
+ # In the CM case, we will have found some "horizontal"
914
+ # isogenies of composite degree and would like to replace them
915
+ # by isogenies of prime degree, mainly to make the isogeny
916
+ # graph look better. We also construct a matrix whose entries
917
+ # are not degrees of cyclic isogenies, but rather quadratic
918
+ # forms (in 1 or 2 variables) representing the isogeny
919
+ # degrees. For this we take a short cut: properly speaking,
920
+ # when `\text{End}(E_1)=\text{End}(E_2)=O`, the set
921
+ # `\text{Hom}(E_1,E_2)` is a rank `1` projective `O`-module,
922
+ # hence has a well-defined ideal class associated to it, and
923
+ # hence (using an identification between the ideal class group
924
+ # and the group of classes of primitive quadratic forms of the
925
+ # same discriminant) an equivalence class of quadratic forms.
926
+ # But we currently only care about the numbers represented by
927
+ # the form, i.e. which genus it is in rather than the exact
928
+ # class. So it suffices to find one form of the correct
929
+ # discriminant which represents one isogeny degree from `E_1`
930
+ # to `E_2` in order to obtain a form which represents all such
931
+ # degrees.
932
+
933
+ if verbose:
934
+ print("Creating degree matrix (CM case)")
935
+
936
+ allQs = {} # keys: discriminants d
937
+ # values: lists of equivalence classes of
938
+ # primitive forms of discriminant d
939
+
940
+ def find_quadratic_form(d, n):
941
+ if d not in allQs:
942
+ from sage.quadratic_forms.binary_qf import BinaryQF_reduced_representatives
943
+
944
+ allQs[d] = BinaryQF_reduced_representatives(d, primitive_only=True)
945
+ # now test which of the Qs represents n
946
+ for Q in allQs[d]:
947
+ if Q.solve_integer(n):
948
+ return Q
949
+ raise ValueError("No form of discriminant %d represents %s" % (d,n))
950
+
951
+ mat = self._mat
952
+ qfmat = [[0 for i in range(ncurves)] for j in range(ncurves)]
953
+ for i, E1 in enumerate(self.curves):
954
+ for j, E2 in enumerate(self.curves):
955
+ if j < i:
956
+ qfmat[i][j] = qfmat[j][i]
957
+ mat[i,j] = mat[j,i]
958
+ elif i == j:
959
+ qfmat[i][j] = [1]
960
+ # mat[i,j] already 1
961
+ else:
962
+ d = E1.cm_discriminant()
963
+ if d != E2.cm_discriminant():
964
+ qfmat[i][j] = [mat[i,j]]
965
+ # mat[i,j] already unique
966
+ else: # horizontal isogeny
967
+ q = find_quadratic_form(d,mat[i,j])
968
+ qfmat[i][j] = list(q)
969
+ mat[i,j] = q.small_prime_value()
970
+
971
+ self._mat = mat
972
+ self._qfmat = qfmat
973
+ if verbose:
974
+ print("new matrix = %s" % mat)
975
+ print("matrix of forms = %s" % qfmat)
976
+
977
+ def _compute_matrix(self):
978
+ """
979
+ Compute the matrix, assuming that the list of curves is computed.
980
+
981
+ EXAMPLES::
982
+
983
+ sage: # needs sage.groups
984
+ sage: isocls = EllipticCurve('1225h1').isogeny_class('database')
985
+ sage: isocls._mat
986
+ sage: isocls._compute_matrix(); isocls._mat
987
+ [ 0 37]
988
+ [37 0]
989
+ """
990
+ self._mat = self.E.isogeny_class(order=self.curves)._mat
991
+
992
+ def _compute_isogenies(self):
993
+ """
994
+ EXAMPLES::
995
+
996
+ sage: E = EllipticCurve('15a1')
997
+ sage: isocls = E.isogeny_class()
998
+ sage: maps = isocls.isogenies() # indirect doctest
999
+ sage: f = maps[0][1]
1000
+ sage: f.domain() == isocls[0] and f.codomain() == isocls[1]
1001
+ True
1002
+ """
1003
+ recomputed = self.E.isogeny_class(order=self.curves)
1004
+ self._mat = recomputed._mat
1005
+ # The domains and codomains here will be equal, but not the same Python object.
1006
+ self._maps = recomputed._maps
1007
+
1008
+
1009
+ class IsogenyClass_EC_Rational(IsogenyClass_EC_NumberField):
1010
+ r"""
1011
+ Isogeny classes for elliptic curves over `\QQ`.
1012
+ """
1013
+ def __init__(self, E, algorithm='sage', label=None, empty=False):
1014
+ r"""
1015
+ INPUT:
1016
+
1017
+ - ``E`` -- an elliptic curve over `\QQ`
1018
+
1019
+ - ``algorithm`` -- string (default: ``'sage'``); one of the
1020
+ following:
1021
+
1022
+ - ``'sage'`` -- use sage's implementation to compute the curves,
1023
+ matrix and isogenies
1024
+
1025
+ - ``'database'`` -- use the Cremona database (only works if the
1026
+ curve is in the database)
1027
+
1028
+ - ``label`` -- string; the label of this isogeny class
1029
+ (e.g. '15a' or '37.b'), used in printing
1030
+
1031
+ - ``empty`` -- don't compute the curves right now (used when reordering)
1032
+
1033
+ EXAMPLES::
1034
+
1035
+ sage: isocls = EllipticCurve('389a1').isogeny_class(); isocls
1036
+ Elliptic curve isogeny class 389a
1037
+ sage: E = EllipticCurve([0, 0, 0, 0, 1001]) # conductor 108216108
1038
+ sage: E.isogeny_class(order='database')
1039
+ Traceback (most recent call last):
1040
+ ...
1041
+ LookupError: Cremona database does not contain entry for
1042
+ Elliptic Curve defined by y^2 = x^3 + 1001 over Rational Field
1043
+ sage: TestSuite(isocls).run()
1044
+ """
1045
+ self._algorithm = algorithm
1046
+ IsogenyClass_EC.__init__(self, E, label=label, empty=empty)
1047
+
1048
+ def copy(self):
1049
+ """
1050
+ Return a copy (mostly used in reordering).
1051
+
1052
+ EXAMPLES::
1053
+
1054
+ sage: E = EllipticCurve('11a1')
1055
+ sage: C = E.isogeny_class()
1056
+ sage: C2 = C.copy()
1057
+ sage: C is C2
1058
+ False
1059
+ sage: C == C2
1060
+ True
1061
+ """
1062
+ ans = IsogenyClass_EC_Rational(self.E, self._algorithm, self._label, empty=True)
1063
+ # The following isn't needed internally, but it will keep
1064
+ # things from breaking if this is used for something other
1065
+ # than reordering.
1066
+ ans.curves = self.curves
1067
+ ans._mat = None
1068
+ ans._maps = None
1069
+ return ans
1070
+
1071
+ def _compute(self):
1072
+ """
1073
+ Compute the list of curves, and possibly the matrix and
1074
+ prime-degree isogenies (depending on the algorithm selected).
1075
+
1076
+ EXAMPLES::
1077
+
1078
+ sage: isocls = EllipticCurve('48a1').isogeny_class('sage').copy()
1079
+ sage: isocls._mat
1080
+ sage: isocls._compute(); isocls._mat
1081
+ [0 2 2 2 0 0]
1082
+ [2 0 0 0 2 2]
1083
+ [2 0 0 0 0 0]
1084
+ [2 0 0 0 0 0]
1085
+ [0 2 0 0 0 0]
1086
+ [0 2 0 0 0 0]
1087
+ """
1088
+ algorithm = self._algorithm
1089
+ from sage.matrix.matrix_space import MatrixSpace
1090
+ self._maps = None
1091
+ if algorithm == "database":
1092
+ try:
1093
+ label = self.E.cremona_label(space=False)
1094
+ except RuntimeError:
1095
+ raise RuntimeError("unable to find %s in the database" % self.E)
1096
+ db = sage.databases.cremona.CremonaDatabase()
1097
+ curves = db.isogeny_class(label)
1098
+ if not curves:
1099
+ raise RuntimeError("unable to find %s in the database" % self.E)
1100
+ # All curves will have the same conductor and isogeny class,
1101
+ # and there are most 8 of them, so lexicographic sorting is okay.
1102
+ self.curves = tuple(sorted(curves,
1103
+ key=lambda E: E.cremona_label()))
1104
+ self._mat = None
1105
+ elif algorithm == "sage":
1106
+ curves = [self.E.minimal_model()]
1107
+ ijl_triples = []
1108
+ l_list = None
1109
+ i = 0
1110
+ while i < len(curves):
1111
+ E = curves[i]
1112
+ isogs = E.isogenies_prime_degree(l_list)
1113
+ for phi in isogs:
1114
+ Edash = phi.codomain()
1115
+ l = phi.degree()
1116
+ # look to see if Edash is new. Note that the
1117
+ # curves returned by isogenies_prime_degree() are
1118
+ # standard minimal models, so it suffices to check
1119
+ # equality rather than isomorphism here.
1120
+ try:
1121
+ j = curves.index(Edash)
1122
+ except ValueError:
1123
+ j = len(curves)
1124
+ curves.append(Edash)
1125
+ ijl_triples.append((i,j,l,phi))
1126
+ if l_list is None:
1127
+ l_list = list({ZZ(f.degree()) for f in isogs})
1128
+ i += 1
1129
+ self.curves = tuple(curves)
1130
+ ncurves = len(curves)
1131
+ self._mat = MatrixSpace(ZZ,ncurves)(0)
1132
+ self._maps = [[0]*ncurves for _ in range(ncurves)]
1133
+ for i,j,l,phi in ijl_triples:
1134
+ self._mat[i,j] = l
1135
+ self._maps[i][j] = phi
1136
+ else:
1137
+ raise ValueError("unknown algorithm '%s'" % algorithm)
1138
+
1139
+
1140
+ def isogeny_degrees_cm(E, verbose=False):
1141
+ r"""
1142
+ Return a list of primes `\ell` sufficient to generate the
1143
+ isogeny class of `E`, where `E` has CM.
1144
+
1145
+ INPUT:
1146
+
1147
+ - ``E`` -- an elliptic curve defined over a number field
1148
+
1149
+ OUTPUT:
1150
+
1151
+ A finite list of primes `\ell` such that every curve isogenous to
1152
+ this curve can be obtained by a finite sequence of isogenies of
1153
+ degree one of the primes in the list. This list is not
1154
+ necessarily minimal.
1155
+
1156
+ ALGORITHM:
1157
+
1158
+ For curves with CM by the order `O` of discriminant `d`, the
1159
+ Galois representation is always non-surjective and the curve will
1160
+ admit `\ell`-isogenies for infinitely many primes `\ell`, but
1161
+ there are only finitely many codomains `E'`. The primes can be
1162
+ divided according to the discriminant `d'` of the CM order `O'`
1163
+ associated to `E`: either `O=O'`, or one contains the other with
1164
+ index `\ell`, since `\ell O\subset O'` and vice versa.
1165
+
1166
+ Case (1): `O=O'`. The degrees of all isogenies between `E` and
1167
+ `E'` are precisely the integers represented by one of the classes
1168
+ of binary quadratic forms `Q` of discriminant `d`. Hence to
1169
+ obtain all possible isomorphism classes of codomain `E'`, we need
1170
+ only use one prime `\ell` represented by each such class `Q`. It
1171
+ would in fact suffice to use primes represented by forms which
1172
+ generate the class group. Here we simply omit the principal class
1173
+ and one from each pair of inverse classes, and include a prime
1174
+ represented by each of the remaining forms.
1175
+
1176
+ Case (2): `[O':O]=\ell`: so `d=\ell^2d;`. We include all prime
1177
+ divisors of `d`.
1178
+
1179
+ Case (3): `[O:O']=\ell`: we may assume that `\ell` does not divide
1180
+ `d` as we have already included these, so `\ell` either splits or
1181
+ is inert in `O`; the class numbers satisfy `h(O')=(\ell\pm1)h(O)`
1182
+ accordingly. We include all primes `\ell` such that `\ell\pm1`
1183
+ divides the degree `[K:\QQ]`.
1184
+
1185
+ For curves with only potential CM we proceed as in the CM case,
1186
+ using `2[K:\QQ]` instead of `[K:\QQ]`.
1187
+
1188
+ EXAMPLES:
1189
+
1190
+ For curves with CM by a quadratic order of class number greater
1191
+ than `1`, we use the structure of the class group to only give one
1192
+ prime in each ideal class::
1193
+
1194
+ sage: pol = PolynomialRing(QQ,'x')([1,-3,5,-5,5,-3,1])
1195
+ sage: L.<a> = NumberField(pol)
1196
+ sage: j = hilbert_class_polynomial(-23).roots(L, multiplicities=False)[0]
1197
+ sage: E = EllipticCurve(j=j)
1198
+ sage: from sage.schemes.elliptic_curves.isogeny_class import isogeny_degrees_cm
1199
+ sage: isogeny_degrees_cm(E, verbose=True)
1200
+ CM case, discriminant = -23
1201
+ initial primes: {2}
1202
+ upward primes: {}
1203
+ downward ramified primes: {}
1204
+ downward split primes: {2, 3}
1205
+ downward inert primes: {5}
1206
+ primes generating the class group: [2]
1207
+ Set of primes before filtering: {2, 3, 5}
1208
+ List of primes after filtering: [2, 3]
1209
+ [2, 3]
1210
+
1211
+ TESTS:
1212
+
1213
+ Check that :issue:`36780` is fixed::
1214
+
1215
+ sage: x = polygen(QQ)
1216
+ sage: L5.<r5> = NumberField(x^2 - 5)
1217
+ sage: E = EllipticCurve(L5, [0, -4325477943600*r5 - 4195572876000])
1218
+ sage: from sage.schemes.elliptic_curves.isogeny_class import isogeny_degrees_cm
1219
+ sage: isogeny_degrees_cm(E)
1220
+ [3, 5]
1221
+ """
1222
+ if not E.has_cm():
1223
+ raise ValueError("possible_isogeny_degrees_cm(E) requires E to be an elliptic curve with CM")
1224
+ d = E.cm_discriminant()
1225
+
1226
+ if verbose:
1227
+ print("CM case, discriminant = %s" % d)
1228
+
1229
+ from sage.libs.pari import pari
1230
+ from sage.sets.set import Set
1231
+ from sage.arith.misc import kronecker as kronecker_symbol
1232
+
1233
+ n = E.base_field().absolute_degree()
1234
+ if not E.has_rational_cm():
1235
+ n *= 2
1236
+ # For discriminants with extra units there's an extra factor in the class number formula:
1237
+ if d == -4:
1238
+ n *= 2
1239
+ if d == -3:
1240
+ n *= 3
1241
+ divs = n.divisors()
1242
+
1243
+ data = pari(d).quadclassunit()
1244
+ # This has 4 components: the class number, class group
1245
+ # structure (ignored), class group generators (as quadratic
1246
+ # forms) and regulator (=1 since d<0, ignored).
1247
+
1248
+ h = data[0].sage()
1249
+
1250
+ # We must have 2*h dividing n, and will need the quotient so
1251
+ # see if the j-invariants of any proper sub-orders could lie
1252
+ # in the same field
1253
+
1254
+ n_over_2h = n//(2*h)
1255
+
1256
+ # Collect possible primes. First put in 2, and also 3 for
1257
+ # discriminant -3 (special case because of units):
1258
+
1259
+ L = Set([ZZ(2), ZZ(3)]) if d == -3 else Set([ZZ(2)])
1260
+ if verbose:
1261
+ print("initial primes: %s" % L)
1262
+
1263
+ # Step 1: "vertical" primes l such that the isogenous curve
1264
+ # has CM by an order whose index is l or 1/l times the index
1265
+ # of the order O of discriminant d. The latter case can only
1266
+ # happen when l^2 divides d.
1267
+
1268
+ # (a) ramified primes
1269
+
1270
+ ram_l = d.odd_part().prime_factors()
1271
+
1272
+ # if the CM is not rational we include all ramified primes,
1273
+ # which is simpler than using the class group later:
1274
+
1275
+ if not E.has_rational_cm():
1276
+ L1 = Set(ram_l)
1277
+ L += L1
1278
+ if verbose:
1279
+ print("ramified primes: %s" % L1)
1280
+
1281
+ else:
1282
+
1283
+ # "Upward" primes (index divided by l):
1284
+
1285
+ L1 = Set([l for l in ram_l if d.valuation(l) > 1])
1286
+ L += L1
1287
+ if verbose:
1288
+ print("upward primes: %s" % L1)
1289
+
1290
+ # "Downward" ramified primes; index multiplied by l, class
1291
+ # number multiplied by l, so l must divide n/2h:
1292
+
1293
+ L1 = Set([l for l in ram_l if l.divides(n_over_2h)])
1294
+ L += L1
1295
+ if verbose:
1296
+ print("downward ramified primes: %s" % L1)
1297
+
1298
+ # (b) Downward split primes; the suborder has class number (l-1)*h, so
1299
+ # l-1 must divide n/2h:
1300
+
1301
+ L1 = Set([lm1+1 for lm1 in divs
1302
+ if (lm1+1).is_prime() and kronecker_symbol(d,lm1+1) == +1])
1303
+ L += L1
1304
+ if verbose:
1305
+ print("downward split primes: %s" % L1)
1306
+
1307
+ # (c) Downward inert primes; the suborder has class number (l+1)*h, so
1308
+ # l+1 must divide n/2h:
1309
+
1310
+ L1 = Set([lp1-1 for lp1 in divs
1311
+ if (lp1-1).is_prime() and kronecker_symbol(d,lp1-1) == -1])
1312
+ L += L1
1313
+ if verbose:
1314
+ print("downward inert primes: %s" % L1)
1315
+
1316
+ # Horizontal primes (rational CM only): same order, degrees are
1317
+ # all integers represented by some binary quadratic form of
1318
+ # discriminant d, so we find a prime represented by each form.
1319
+
1320
+ if E.has_rational_cm():
1321
+ from sage.quadratic_forms.binary_qf import BinaryQF
1322
+ Qs = [BinaryQF(list(q)) for q in data[2]]
1323
+
1324
+ L1 = [Q.small_prime_value() for Q in Qs]
1325
+ if verbose:
1326
+ print("primes generating the class group: %s" % L1)
1327
+ L += Set(L1)
1328
+
1329
+ # Return sorted list
1330
+
1331
+ if verbose:
1332
+ print("Set of primes before filtering: %s" % L)
1333
+
1334
+ # This filter will quickly eliminate most false entries in the set
1335
+ from .gal_reps_number_field import Frobenius_filter
1336
+ L = Frobenius_filter(E, sorted(L))
1337
+ if verbose:
1338
+ print("List of primes after filtering: %s" % L)
1339
+ return L
1340
+
1341
+
1342
+ def possible_isogeny_degrees(E, algorithm='Billerey', max_l=None,
1343
+ num_l=None, exact=True, verbose=False):
1344
+ r"""
1345
+ Return a list of primes `\ell` sufficient to generate the
1346
+ isogeny class of `E`.
1347
+
1348
+ INPUT:
1349
+
1350
+ - ``E`` -- an elliptic curve defined over a number field
1351
+
1352
+ - ``algorithm`` -- string (default: ``'Billerey'``); algorithm to be
1353
+ used for non-CM curves: either ``'Billerey'``, ``'Larson'``, or
1354
+ ``'heuristic'``. Only relevant for non-CM curves and base fields
1355
+ other than `\QQ`.
1356
+
1357
+ - ``max_l`` -- integer or ``None``; only relevant for non-CM curves
1358
+ and algorithms ``'Billerey'`` and ``'heuristic'``. Controls the maximum
1359
+ prime used in either algorithm. If ``None``, use the default
1360
+ for that algorithm.
1361
+
1362
+ - ``num_l`` -- integer or ``None``; only relevant for non-CM curves
1363
+ and algorithm ``'Billerey'``. Controls the maximum number of primes
1364
+ used in the algorithm. If ``None``, use the default for that
1365
+ algorithm.
1366
+
1367
+ - ``exact`` -- boolean (default: ``True``); if ``True``, perform an
1368
+ additional check that the primes returned are all reducible. If
1369
+ ``False``, skip this step, in which case some of the primes
1370
+ returned may be irreducible.
1371
+
1372
+ OUTPUT:
1373
+
1374
+ A finite list of primes `\ell` such that every curve isogenous to
1375
+ this curve can be obtained by a finite sequence of isogenies of
1376
+ degree one of the primes in the list.
1377
+
1378
+ ALGORITHM:
1379
+
1380
+ For curves without CM, the set may be taken to be the finite set
1381
+ of primes at which the Galois representation is not surjective,
1382
+ since the existence of an `\ell`-isogeny is equivalent to the
1383
+ image of the mod-`\ell` Galois representation being contained in a
1384
+ Borel subgroup. Two rigorous algorithms have been implemented to
1385
+ determine this set, due to Larson and Billeray respectively. We
1386
+ also provide a non-rigorous 'heuristic' algorithm which only tests
1387
+ reducible primes up to a bound depending on the degree of the
1388
+ base field.
1389
+
1390
+ For curves with CM see the documentation for :meth:`isogeny_degrees_cm()`.
1391
+
1392
+ EXAMPLES:
1393
+
1394
+ For curves without CM we determine the primes at which the mod `p`
1395
+ Galois representation is reducible, i.e. contained in a Borel
1396
+ subgroup::
1397
+
1398
+ sage: from sage.schemes.elliptic_curves.isogeny_class import possible_isogeny_degrees
1399
+ sage: E = EllipticCurve('11a1')
1400
+ sage: possible_isogeny_degrees(E)
1401
+ [5]
1402
+ sage: possible_isogeny_degrees(E, algorithm='Larson')
1403
+ [5]
1404
+ sage: possible_isogeny_degrees(E, algorithm='Billerey')
1405
+ [5]
1406
+ sage: possible_isogeny_degrees(E, algorithm='heuristic')
1407
+ [5]
1408
+
1409
+ We check that in this case `E` really does have rational
1410
+ `5`-isogenies::
1411
+
1412
+ sage: [phi.degree() for phi in E.isogenies_prime_degree()]
1413
+ [5, 5]
1414
+
1415
+ Over an extension field::
1416
+
1417
+ sage: E3 = E.change_ring(CyclotomicField(3))
1418
+ sage: possible_isogeny_degrees(E3) # long time (5s)
1419
+ [5]
1420
+ sage: [phi.degree() for phi in E3.isogenies_prime_degree()]
1421
+ [5, 5]
1422
+
1423
+ A higher degree example (LMFDB curve 5.5.170701.1-4.1-b1)::
1424
+
1425
+ sage: x = polygen(QQ)
1426
+ sage: K.<a> = NumberField(x^5 - x^4 - 6*x^3 + 4*x + 1)
1427
+ sage: E = EllipticCurve(K, [a^3 - a^2 - 5*a + 1, a^4 - a^3 - 5*a^2 - a + 1,
1428
+ ....: -a^4 + 2*a^3 + 5*a^2 - 5*a - 3, a^4 - a^3 - 5*a^2 - a,
1429
+ ....: -3*a^4 + 4*a^3 + 17*a^2 - 6*a - 12])
1430
+ sage: possible_isogeny_degrees(E, algorithm='heuristic')
1431
+ [2]
1432
+ sage: possible_isogeny_degrees(E, algorithm='Billerey')
1433
+ [2]
1434
+ sage: possible_isogeny_degrees(E, algorithm='Larson')
1435
+ [2]
1436
+
1437
+ LMFDB curve 4.4.8112.1-108.1-a5::
1438
+
1439
+ sage: x = polygen(QQ)
1440
+ sage: K.<a> = NumberField(x^4 - 5*x^2 + 3)
1441
+ sage: E = EllipticCurve(K, [a^2 - 2, -a^2 + 3, a^2 - 2, -50*a^2 + 35, 95*a^2 - 67])
1442
+ sage: possible_isogeny_degrees(E, exact=False, algorithm='Billerey') # long time (6.5s)
1443
+ [2, 5]
1444
+ sage: possible_isogeny_degrees(E, exact=False, algorithm='Larson')
1445
+ [2, 5]
1446
+ sage: possible_isogeny_degrees(E, exact=False, algorithm='heuristic')
1447
+ [2, 5]
1448
+ sage: possible_isogeny_degrees(E)
1449
+ [2, 5]
1450
+
1451
+ This function only returns the primes which are isogeny degrees::
1452
+
1453
+ sage: Set(E.isogeny_class().matrix().list()) # long time (7s)
1454
+ {1, 2, 4, 5, 20, 10}
1455
+
1456
+ For curves with CM by a quadratic order of class number greater
1457
+ than `1`, we use the structure of the class group to only give one
1458
+ prime in each ideal class::
1459
+
1460
+ sage: pol = PolynomialRing(QQ,'x')([1,-3,5,-5,5,-3,1])
1461
+ sage: L.<a> = NumberField(pol)
1462
+ sage: j = hilbert_class_polynomial(-23).roots(L, multiplicities=False)[0]
1463
+ sage: E = EllipticCurve(j=j)
1464
+ sage: from sage.schemes.elliptic_curves.isogeny_class import possible_isogeny_degrees
1465
+ sage: possible_isogeny_degrees(E, verbose=True)
1466
+ CM case, discriminant = -23
1467
+ initial primes: {2}
1468
+ upward primes: {}
1469
+ downward ramified primes: {}
1470
+ downward split primes: {2, 3}
1471
+ downward inert primes: {5}
1472
+ primes generating the class group: [2]
1473
+ Set of primes before filtering: {2, 3, 5}
1474
+ List of primes after filtering: [2, 3]
1475
+ [2, 3]
1476
+ """
1477
+ if E.has_cm():
1478
+ return isogeny_degrees_cm(E, verbose)
1479
+
1480
+ if E.base_field() == QQ:
1481
+ from sage.schemes.elliptic_curves.gal_reps_number_field import reducible_primes_naive
1482
+ return reducible_primes_naive(E, max_l=37, verbose=verbose)
1483
+
1484
+ # Non-CM case
1485
+
1486
+ # NB The following functions first computes a finite set
1487
+ # containing the reducible primes, then checks that each is
1488
+ # reducible by computing l-isogenies. This appears circular
1489
+ # but the computated l-isogenies for a fixed prime l is
1490
+ # cached.
1491
+
1492
+ if verbose:
1493
+ print("Non-CM case, using {} algorithm".format(algorithm))
1494
+
1495
+ # First we obtain a finite set of primes containing the reducible
1496
+ # ones Each of these algorithms includes application of the
1497
+ # "Frobenius filter" eliminating any ell for which there exists a
1498
+ # prime P of good reduction such that the Frobenius polynomial at
1499
+ # P does not factor modulo ell.
1500
+
1501
+ if algorithm == 'Larson':
1502
+ L = E.galois_representation().isogeny_bound()
1503
+
1504
+ elif algorithm == 'Billerey':
1505
+ from sage.schemes.elliptic_curves.gal_reps_number_field import reducible_primes_Billerey
1506
+ L = reducible_primes_Billerey(E, num_l=num_l, max_l=max_l, verbose=verbose)
1507
+
1508
+ elif algorithm == 'heuristic':
1509
+ from sage.schemes.elliptic_curves.gal_reps_number_field import reducible_primes_naive
1510
+ L = reducible_primes_naive(E, max_l=max_l, num_P=num_l, verbose=verbose)
1511
+
1512
+ else:
1513
+ raise ValueError("algorithm for possible_isogeny_degrees must be one of 'Larson', 'Billerey', 'heuristic'")
1514
+
1515
+ # The set L may contain irreducible primes. We optionally test
1516
+ # each one to see if it is actually reducible, by computing ell-isogenies:
1517
+
1518
+ if exact:
1519
+ L = [l for l in L if E.isogenies_prime_degree(l, minimal_models=False)]
1520
+
1521
+ return L