passagemath-schemes 10.6.47__cp312-cp312-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.
- passagemath_schemes/.dylibs/libflint.22.0.dylib +0 -0
- passagemath_schemes/.dylibs/libgmp.10.dylib +0 -0
- passagemath_schemes/.dylibs/libgmpxx.4.dylib +0 -0
- passagemath_schemes/.dylibs/libmpfr.6.dylib +0 -0
- passagemath_schemes/__init__.py +3 -0
- passagemath_schemes-10.6.47.dist-info/METADATA +204 -0
- passagemath_schemes-10.6.47.dist-info/METADATA.bak +205 -0
- passagemath_schemes-10.6.47.dist-info/RECORD +311 -0
- passagemath_schemes-10.6.47.dist-info/WHEEL +6 -0
- passagemath_schemes-10.6.47.dist-info/top_level.txt +3 -0
- sage/all__sagemath_schemes.py +23 -0
- sage/databases/all__sagemath_schemes.py +7 -0
- sage/databases/cremona.py +1723 -0
- sage/dynamics/all__sagemath_schemes.py +2 -0
- sage/dynamics/arithmetic_dynamics/affine_ds.py +1083 -0
- sage/dynamics/arithmetic_dynamics/all.py +14 -0
- sage/dynamics/arithmetic_dynamics/berkovich_ds.py +1101 -0
- sage/dynamics/arithmetic_dynamics/dynamical_semigroup.py +1543 -0
- sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py +2426 -0
- sage/dynamics/arithmetic_dynamics/endPN_minimal_model.py +1169 -0
- sage/dynamics/arithmetic_dynamics/generic_ds.py +663 -0
- sage/dynamics/arithmetic_dynamics/product_projective_ds.py +339 -0
- sage/dynamics/arithmetic_dynamics/projective_ds.py +9558 -0
- sage/dynamics/arithmetic_dynamics/projective_ds_helper.cpython-312-darwin.so +0 -0
- sage/dynamics/arithmetic_dynamics/projective_ds_helper.pyx +301 -0
- sage/dynamics/arithmetic_dynamics/wehlerK3.py +2576 -0
- sage/lfunctions/all.py +18 -0
- sage/lfunctions/dokchitser.py +745 -0
- sage/lfunctions/pari.py +818 -0
- sage/lfunctions/zero_sums.cpython-312-darwin.so +0 -0
- sage/lfunctions/zero_sums.pyx +1847 -0
- sage/modular/abvar/abvar.py +5135 -0
- sage/modular/abvar/abvar_ambient_jacobian.py +413 -0
- sage/modular/abvar/abvar_newform.py +244 -0
- sage/modular/abvar/all.py +8 -0
- sage/modular/abvar/constructor.py +186 -0
- sage/modular/abvar/cuspidal_subgroup.py +371 -0
- sage/modular/abvar/finite_subgroup.py +896 -0
- sage/modular/abvar/homology.py +720 -0
- sage/modular/abvar/homspace.py +998 -0
- sage/modular/abvar/lseries.py +415 -0
- sage/modular/abvar/morphism.py +935 -0
- sage/modular/abvar/torsion_point.py +274 -0
- sage/modular/abvar/torsion_subgroup.py +740 -0
- sage/modular/all.py +43 -0
- sage/modular/arithgroup/all.py +20 -0
- sage/modular/arithgroup/arithgroup_element.cpython-312-darwin.so +0 -0
- sage/modular/arithgroup/arithgroup_element.pyx +474 -0
- sage/modular/arithgroup/arithgroup_generic.py +1402 -0
- sage/modular/arithgroup/arithgroup_perm.py +2692 -0
- sage/modular/arithgroup/congroup.cpython-312-darwin.so +0 -0
- sage/modular/arithgroup/congroup.pyx +334 -0
- sage/modular/arithgroup/congroup_gamma.py +363 -0
- sage/modular/arithgroup/congroup_gamma0.py +692 -0
- sage/modular/arithgroup/congroup_gamma1.py +653 -0
- sage/modular/arithgroup/congroup_gammaH.py +1469 -0
- sage/modular/arithgroup/congroup_generic.py +628 -0
- sage/modular/arithgroup/congroup_sl2z.py +267 -0
- sage/modular/arithgroup/farey_symbol.cpython-312-darwin.so +0 -0
- sage/modular/arithgroup/farey_symbol.pyx +1066 -0
- sage/modular/arithgroup/tests.py +418 -0
- sage/modular/btquotients/all.py +4 -0
- sage/modular/btquotients/btquotient.py +3753 -0
- sage/modular/btquotients/pautomorphicform.py +2570 -0
- sage/modular/buzzard.py +100 -0
- sage/modular/congroup.py +29 -0
- sage/modular/congroup_element.py +13 -0
- sage/modular/cusps.py +1109 -0
- sage/modular/cusps_nf.py +1270 -0
- sage/modular/dims.py +569 -0
- sage/modular/dirichlet.py +3310 -0
- sage/modular/drinfeld_modform/all.py +2 -0
- sage/modular/drinfeld_modform/element.py +446 -0
- sage/modular/drinfeld_modform/ring.py +773 -0
- sage/modular/drinfeld_modform/tutorial.py +236 -0
- sage/modular/etaproducts.py +1065 -0
- sage/modular/hecke/algebra.py +746 -0
- sage/modular/hecke/all.py +20 -0
- sage/modular/hecke/ambient_module.py +1019 -0
- sage/modular/hecke/degenmap.py +119 -0
- sage/modular/hecke/element.py +325 -0
- sage/modular/hecke/hecke_operator.py +780 -0
- sage/modular/hecke/homspace.py +206 -0
- sage/modular/hecke/module.py +1767 -0
- sage/modular/hecke/morphism.py +174 -0
- sage/modular/hecke/submodule.py +989 -0
- sage/modular/hypergeometric_misc.cpython-312-darwin.so +0 -0
- sage/modular/hypergeometric_misc.pxd +4 -0
- sage/modular/hypergeometric_misc.pyx +166 -0
- sage/modular/hypergeometric_motive.py +2017 -0
- sage/modular/local_comp/all.py +2 -0
- sage/modular/local_comp/liftings.py +292 -0
- sage/modular/local_comp/local_comp.py +1071 -0
- sage/modular/local_comp/smoothchar.py +1825 -0
- sage/modular/local_comp/type_space.py +748 -0
- sage/modular/modform/all.py +30 -0
- sage/modular/modform/ambient.py +815 -0
- sage/modular/modform/ambient_R.py +177 -0
- sage/modular/modform/ambient_eps.py +306 -0
- sage/modular/modform/ambient_g0.py +124 -0
- sage/modular/modform/ambient_g1.py +204 -0
- sage/modular/modform/constructor.py +545 -0
- sage/modular/modform/cuspidal_submodule.py +708 -0
- sage/modular/modform/defaults.py +14 -0
- sage/modular/modform/eis_series.py +505 -0
- sage/modular/modform/eisenstein_submodule.py +663 -0
- sage/modular/modform/element.py +4131 -0
- sage/modular/modform/find_generators.py +59 -0
- sage/modular/modform/half_integral.py +154 -0
- sage/modular/modform/hecke_operator_on_qexp.py +247 -0
- sage/modular/modform/j_invariant.py +47 -0
- sage/modular/modform/l_series_gross_zagier.py +133 -0
- sage/modular/modform/l_series_gross_zagier_coeffs.cpython-312-darwin.so +0 -0
- sage/modular/modform/l_series_gross_zagier_coeffs.pyx +177 -0
- sage/modular/modform/notes.py +45 -0
- sage/modular/modform/numerical.py +514 -0
- sage/modular/modform/periods.py +14 -0
- sage/modular/modform/ring.py +1257 -0
- sage/modular/modform/space.py +1860 -0
- sage/modular/modform/submodule.py +118 -0
- sage/modular/modform/tests.py +64 -0
- sage/modular/modform/theta.py +110 -0
- sage/modular/modform/vm_basis.py +381 -0
- sage/modular/modform/weight1.py +220 -0
- sage/modular/modform_hecketriangle/abstract_ring.py +1932 -0
- sage/modular/modform_hecketriangle/abstract_space.py +2528 -0
- sage/modular/modform_hecketriangle/all.py +30 -0
- sage/modular/modform_hecketriangle/analytic_type.py +590 -0
- sage/modular/modform_hecketriangle/constructor.py +416 -0
- sage/modular/modform_hecketriangle/element.py +351 -0
- sage/modular/modform_hecketriangle/functors.py +752 -0
- sage/modular/modform_hecketriangle/graded_ring.py +541 -0
- sage/modular/modform_hecketriangle/graded_ring_element.py +2225 -0
- sage/modular/modform_hecketriangle/hecke_triangle_group_element.py +3352 -0
- sage/modular/modform_hecketriangle/hecke_triangle_groups.py +1432 -0
- sage/modular/modform_hecketriangle/readme.py +1214 -0
- sage/modular/modform_hecketriangle/series_constructor.py +580 -0
- sage/modular/modform_hecketriangle/space.py +1037 -0
- sage/modular/modform_hecketriangle/subspace.py +423 -0
- sage/modular/modsym/all.py +17 -0
- sage/modular/modsym/ambient.py +3846 -0
- sage/modular/modsym/boundary.py +1420 -0
- sage/modular/modsym/element.py +336 -0
- sage/modular/modsym/g1list.py +178 -0
- sage/modular/modsym/ghlist.py +182 -0
- sage/modular/modsym/hecke_operator.py +73 -0
- sage/modular/modsym/manin_symbol.cpython-312-darwin.so +0 -0
- sage/modular/modsym/manin_symbol.pxd +5 -0
- sage/modular/modsym/manin_symbol.pyx +497 -0
- sage/modular/modsym/manin_symbol_list.py +1295 -0
- sage/modular/modsym/modsym.py +400 -0
- sage/modular/modsym/modular_symbols.py +384 -0
- sage/modular/modsym/p1list_nf.py +1241 -0
- sage/modular/modsym/relation_matrix.py +591 -0
- sage/modular/modsym/relation_matrix_pyx.cpython-312-darwin.so +0 -0
- sage/modular/modsym/relation_matrix_pyx.pyx +108 -0
- sage/modular/modsym/space.py +2468 -0
- sage/modular/modsym/subspace.py +455 -0
- sage/modular/modsym/tests.py +375 -0
- sage/modular/multiple_zeta.py +2632 -0
- sage/modular/multiple_zeta_F_algebra.py +786 -0
- sage/modular/overconvergent/all.py +6 -0
- sage/modular/overconvergent/genus0.py +1878 -0
- sage/modular/overconvergent/hecke_series.py +1187 -0
- sage/modular/overconvergent/weightspace.py +778 -0
- sage/modular/pollack_stevens/all.py +4 -0
- sage/modular/pollack_stevens/distributions.py +874 -0
- sage/modular/pollack_stevens/fund_domain.py +1572 -0
- sage/modular/pollack_stevens/manin_map.py +859 -0
- sage/modular/pollack_stevens/modsym.py +1593 -0
- sage/modular/pollack_stevens/padic_lseries.py +417 -0
- sage/modular/pollack_stevens/sigma0.py +534 -0
- sage/modular/pollack_stevens/space.py +1076 -0
- sage/modular/quasimodform/all.py +3 -0
- sage/modular/quasimodform/element.py +845 -0
- sage/modular/quasimodform/ring.py +828 -0
- sage/modular/quatalg/all.py +3 -0
- sage/modular/quatalg/brandt.py +1642 -0
- sage/modular/ssmod/all.py +8 -0
- sage/modular/ssmod/ssmod.py +827 -0
- sage/rings/all__sagemath_schemes.py +1 -0
- sage/rings/polynomial/all__sagemath_schemes.py +1 -0
- sage/rings/polynomial/binary_form_reduce.py +585 -0
- sage/schemes/all.py +41 -0
- sage/schemes/berkovich/all.py +6 -0
- sage/schemes/berkovich/berkovich_cp_element.py +2582 -0
- sage/schemes/berkovich/berkovich_space.py +748 -0
- sage/schemes/curves/affine_curve.py +2928 -0
- sage/schemes/curves/all.py +33 -0
- sage/schemes/curves/closed_point.py +434 -0
- sage/schemes/curves/constructor.py +381 -0
- sage/schemes/curves/curve.py +542 -0
- sage/schemes/curves/plane_curve_arrangement.py +1283 -0
- sage/schemes/curves/point.py +463 -0
- sage/schemes/curves/projective_curve.py +3026 -0
- sage/schemes/curves/zariski_vankampen.py +1932 -0
- sage/schemes/cyclic_covers/all.py +2 -0
- sage/schemes/cyclic_covers/charpoly_frobenius.py +320 -0
- sage/schemes/cyclic_covers/constructor.py +137 -0
- sage/schemes/cyclic_covers/cycliccover_finite_field.py +1309 -0
- sage/schemes/cyclic_covers/cycliccover_generic.py +310 -0
- sage/schemes/elliptic_curves/BSD.py +1036 -0
- sage/schemes/elliptic_curves/Qcurves.py +592 -0
- sage/schemes/elliptic_curves/addition_formulas_ring.py +94 -0
- sage/schemes/elliptic_curves/all.py +49 -0
- sage/schemes/elliptic_curves/cardinality.py +609 -0
- sage/schemes/elliptic_curves/cm.py +1102 -0
- sage/schemes/elliptic_curves/constructor.py +1552 -0
- sage/schemes/elliptic_curves/ec_database.py +175 -0
- sage/schemes/elliptic_curves/ell_curve_isogeny.py +3972 -0
- sage/schemes/elliptic_curves/ell_egros.py +459 -0
- sage/schemes/elliptic_curves/ell_field.py +2836 -0
- sage/schemes/elliptic_curves/ell_finite_field.py +3359 -0
- sage/schemes/elliptic_curves/ell_generic.py +3760 -0
- sage/schemes/elliptic_curves/ell_local_data.py +1207 -0
- sage/schemes/elliptic_curves/ell_modular_symbols.py +775 -0
- sage/schemes/elliptic_curves/ell_number_field.py +4220 -0
- sage/schemes/elliptic_curves/ell_padic_field.py +107 -0
- sage/schemes/elliptic_curves/ell_point.py +4787 -0
- sage/schemes/elliptic_curves/ell_rational_field.py +7368 -0
- sage/schemes/elliptic_curves/ell_tate_curve.py +671 -0
- sage/schemes/elliptic_curves/ell_torsion.py +436 -0
- sage/schemes/elliptic_curves/ell_wp.py +352 -0
- sage/schemes/elliptic_curves/formal_group.py +760 -0
- sage/schemes/elliptic_curves/gal_reps.py +1459 -0
- sage/schemes/elliptic_curves/gal_reps_number_field.py +1669 -0
- sage/schemes/elliptic_curves/gp_simon.py +152 -0
- sage/schemes/elliptic_curves/heegner.py +7335 -0
- sage/schemes/elliptic_curves/height.py +2109 -0
- sage/schemes/elliptic_curves/hom.py +1406 -0
- sage/schemes/elliptic_curves/hom_composite.py +934 -0
- sage/schemes/elliptic_curves/hom_frobenius.py +522 -0
- sage/schemes/elliptic_curves/hom_scalar.py +531 -0
- sage/schemes/elliptic_curves/hom_sum.py +682 -0
- sage/schemes/elliptic_curves/hom_velusqrt.py +1290 -0
- sage/schemes/elliptic_curves/homset.py +271 -0
- sage/schemes/elliptic_curves/isogeny_class.py +1521 -0
- sage/schemes/elliptic_curves/isogeny_small_degree.py +2797 -0
- sage/schemes/elliptic_curves/jacobian.py +237 -0
- sage/schemes/elliptic_curves/kodaira_symbol.py +344 -0
- sage/schemes/elliptic_curves/kraus.py +1014 -0
- sage/schemes/elliptic_curves/lseries_ell.py +943 -0
- sage/schemes/elliptic_curves/mod5family.py +105 -0
- sage/schemes/elliptic_curves/mod_poly.py +197 -0
- sage/schemes/elliptic_curves/mod_sym_num.cpython-312-darwin.so +0 -0
- sage/schemes/elliptic_curves/mod_sym_num.pyx +3796 -0
- sage/schemes/elliptic_curves/modular_parametrization.py +305 -0
- sage/schemes/elliptic_curves/padic_lseries.py +1793 -0
- sage/schemes/elliptic_curves/padics.py +1816 -0
- sage/schemes/elliptic_curves/period_lattice.py +2234 -0
- sage/schemes/elliptic_curves/period_lattice_region.cpython-312-darwin.so +0 -0
- sage/schemes/elliptic_curves/period_lattice_region.pyx +722 -0
- sage/schemes/elliptic_curves/saturation.py +715 -0
- sage/schemes/elliptic_curves/sha_tate.py +1158 -0
- sage/schemes/elliptic_curves/weierstrass_morphism.py +1117 -0
- sage/schemes/elliptic_curves/weierstrass_transform.py +200 -0
- sage/schemes/hyperelliptic_curves/all.py +6 -0
- sage/schemes/hyperelliptic_curves/constructor.py +291 -0
- sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py +1914 -0
- sage/schemes/hyperelliptic_curves/hyperelliptic_g2.py +192 -0
- sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py +954 -0
- sage/schemes/hyperelliptic_curves/hyperelliptic_padic_field.py +1332 -0
- sage/schemes/hyperelliptic_curves/hyperelliptic_rational_field.py +84 -0
- sage/schemes/hyperelliptic_curves/invariants.py +410 -0
- sage/schemes/hyperelliptic_curves/jacobian_endomorphism_utils.py +315 -0
- sage/schemes/hyperelliptic_curves/jacobian_g2.py +32 -0
- sage/schemes/hyperelliptic_curves/jacobian_generic.py +419 -0
- sage/schemes/hyperelliptic_curves/jacobian_homset.py +186 -0
- sage/schemes/hyperelliptic_curves/jacobian_morphism.py +875 -0
- sage/schemes/hyperelliptic_curves/kummer_surface.py +99 -0
- sage/schemes/hyperelliptic_curves/mestre.py +302 -0
- sage/schemes/hyperelliptic_curves/monsky_washnitzer.py +3871 -0
- sage/schemes/jacobians/abstract_jacobian.py +277 -0
- sage/schemes/jacobians/all.py +2 -0
- sage/schemes/overview.py +161 -0
- sage/schemes/plane_conics/all.py +22 -0
- sage/schemes/plane_conics/con_field.py +1296 -0
- sage/schemes/plane_conics/con_finite_field.py +158 -0
- sage/schemes/plane_conics/con_number_field.py +456 -0
- sage/schemes/plane_conics/con_rational_field.py +406 -0
- sage/schemes/plane_conics/con_rational_function_field.py +580 -0
- sage/schemes/plane_conics/constructor.py +249 -0
- sage/schemes/plane_quartics/all.py +2 -0
- sage/schemes/plane_quartics/quartic_constructor.py +71 -0
- sage/schemes/plane_quartics/quartic_generic.py +73 -0
- sage/schemes/riemann_surfaces/all.py +1 -0
- sage/schemes/riemann_surfaces/riemann_surface.py +4117 -0
- sage_wheels/share/cremona/cremona_mini.db +0 -0
- sage_wheels/share/ellcurves/rank0 +30427 -0
- sage_wheels/share/ellcurves/rank1 +31871 -0
- sage_wheels/share/ellcurves/rank10 +6 -0
- sage_wheels/share/ellcurves/rank11 +6 -0
- sage_wheels/share/ellcurves/rank12 +1 -0
- sage_wheels/share/ellcurves/rank14 +1 -0
- sage_wheels/share/ellcurves/rank15 +1 -0
- sage_wheels/share/ellcurves/rank17 +1 -0
- sage_wheels/share/ellcurves/rank19 +1 -0
- sage_wheels/share/ellcurves/rank2 +2388 -0
- sage_wheels/share/ellcurves/rank20 +1 -0
- sage_wheels/share/ellcurves/rank21 +1 -0
- sage_wheels/share/ellcurves/rank22 +1 -0
- sage_wheels/share/ellcurves/rank23 +1 -0
- sage_wheels/share/ellcurves/rank24 +1 -0
- sage_wheels/share/ellcurves/rank28 +1 -0
- sage_wheels/share/ellcurves/rank3 +836 -0
- sage_wheels/share/ellcurves/rank4 +10 -0
- sage_wheels/share/ellcurves/rank5 +5 -0
- sage_wheels/share/ellcurves/rank6 +5 -0
- sage_wheels/share/ellcurves/rank7 +5 -0
- sage_wheels/share/ellcurves/rank8 +6 -0
- 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
|