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.
- 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.40.dist-info/METADATA +204 -0
- passagemath_schemes-10.6.40.dist-info/METADATA.bak +205 -0
- passagemath_schemes-10.6.40.dist-info/RECORD +314 -0
- passagemath_schemes-10.6.40.dist-info/WHEEL +6 -0
- passagemath_schemes-10.6.40.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-314-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-314-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-314-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-314-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-314-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-314-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-314-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-314-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.cpython-314-darwin.so +0 -0
- sage/modular/modsym/p1list.pxd +29 -0
- sage/modular/modsym/p1list.pyx +1372 -0
- sage/modular/modsym/p1list_nf.py +1241 -0
- sage/modular/modsym/relation_matrix.py +591 -0
- sage/modular/modsym/relation_matrix_pyx.cpython-314-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-314-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-314-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,2692 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-schemes
|
|
2
|
+
# sage.doctest: needs sage.groups
|
|
3
|
+
r"""
|
|
4
|
+
Arithmetic subgroups defined by permutations of cosets
|
|
5
|
+
|
|
6
|
+
A subgroup of finite index `H` of a finitely generated group `G` is completely
|
|
7
|
+
described by the action of a set of generators of `G` on the right cosets `H
|
|
8
|
+
\backslash G = \{Hg\}_{g \in G}`. After some arbitrary choice of numbering one
|
|
9
|
+
can identify the action of generators as elements of a symmetric group acting
|
|
10
|
+
transitively (and satisfying the relations of the relators in G). As
|
|
11
|
+
`\SL_2(\ZZ)` has a very simple presentation as a central extension of a free
|
|
12
|
+
product of cyclic groups, one can easily design algorithms from this point of
|
|
13
|
+
view.
|
|
14
|
+
|
|
15
|
+
The generators of `\SL_2(\ZZ)` used in this module are named as follows `s_2`,
|
|
16
|
+
`s_3`, `l`, `r` which are defined by
|
|
17
|
+
|
|
18
|
+
.. MATH::
|
|
19
|
+
|
|
20
|
+
s_2 = \begin{pmatrix} 0 & -1 \\ 1 & 0 \end{pmatrix},\quad
|
|
21
|
+
s_3 = \begin{pmatrix} 0 & 1 \\ -1 & 1 \end{pmatrix},\quad
|
|
22
|
+
l = \begin{pmatrix} 1 & 1 \\ 0 & 1 \end{pmatrix},\quad
|
|
23
|
+
r = \begin{pmatrix} 1 & 0 \\ 1 & 1 \end{pmatrix}.
|
|
24
|
+
|
|
25
|
+
Those generators satisfy the following relations
|
|
26
|
+
|
|
27
|
+
.. MATH::
|
|
28
|
+
|
|
29
|
+
s_2^2 = s_3^3 = -1, \quad r = s_2^{-1}\ l^{-1}\ s_2.
|
|
30
|
+
|
|
31
|
+
In particular not all four are needed to generate the whole group `\SL_2(\ZZ)`.
|
|
32
|
+
Three couples which generate `\SL_2(\ZZ)` are of particular
|
|
33
|
+
interest:
|
|
34
|
+
|
|
35
|
+
- `(l,r)` as they are also semigroup generators for the semigroup of matrices
|
|
36
|
+
in `\SL_2(\ZZ)` with nonnegative entries,
|
|
37
|
+
- `(l,s_2)` as they are closely related to the continued fraction algorithm,
|
|
38
|
+
- `(s_2,s_3)` as the group `\PSL_2(\ZZ)` is the free product of the finite
|
|
39
|
+
cyclic groups generated by these two elements.
|
|
40
|
+
|
|
41
|
+
Part of these functions are based on Chris Kurth's *KFarey* package [Kur2008]_.
|
|
42
|
+
For tests see the file ``sage.modular.arithgroup.tests``.
|
|
43
|
+
|
|
44
|
+
REFERENCES:
|
|
45
|
+
|
|
46
|
+
- [ASD1971]_
|
|
47
|
+
- [Gor2009]_
|
|
48
|
+
- [HL2014]_
|
|
49
|
+
- [Hsu1996]_
|
|
50
|
+
- [Hsu1997]_
|
|
51
|
+
- [KSV2011]_
|
|
52
|
+
- [Kul1991]_
|
|
53
|
+
- [Kur2008]_
|
|
54
|
+
- [KL2008]_
|
|
55
|
+
- [Ver]_
|
|
56
|
+
|
|
57
|
+
.. TODO::
|
|
58
|
+
|
|
59
|
+
- modular Farey symbols
|
|
60
|
+
|
|
61
|
+
- computation of generators of a modular subgroup with a standard surface
|
|
62
|
+
group presentation. In other words, compute a presentation of the form
|
|
63
|
+
|
|
64
|
+
.. MATH::
|
|
65
|
+
|
|
66
|
+
\langle x_i,y_i,c_j |\ \prod_i [x_i,y_i] \prod_j c_j^{\nu_j} = 1\rangle
|
|
67
|
+
|
|
68
|
+
where the elements `x_i` and `y_i` are hyperbolic and `c_j` are parabolic
|
|
69
|
+
(`\nu_j=\infty`) or elliptic elements (`\nu_j < \infty`).
|
|
70
|
+
|
|
71
|
+
- computation of centralizer.
|
|
72
|
+
|
|
73
|
+
- generation of modular (even) subgroups of fixed index.
|
|
74
|
+
|
|
75
|
+
AUTHORS:
|
|
76
|
+
|
|
77
|
+
- Chris Kurth (2008): created KFarey package
|
|
78
|
+
|
|
79
|
+
- David Loeffler (2009): adapted functions from KFarey for inclusion into Sage
|
|
80
|
+
|
|
81
|
+
- Vincent Delecroix (2010): implementation for odd groups, new design,
|
|
82
|
+
improvements, documentation
|
|
83
|
+
|
|
84
|
+
- David Loeffler (2011): congruence testing for odd subgroups, enumeration of
|
|
85
|
+
liftings of projective subgroups
|
|
86
|
+
|
|
87
|
+
- David Loeffler & Thomas Hamilton (2012): generalised Hsu congruence test for
|
|
88
|
+
odd subgroups
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
################################################################################
|
|
92
|
+
#
|
|
93
|
+
# Copyright (C) 2009, The Sage Group -- http://www.sagemath.org/
|
|
94
|
+
#
|
|
95
|
+
# Distributed under the terms of the GNU General Public License (GPL)
|
|
96
|
+
#
|
|
97
|
+
# The full text of the GPL is available at:
|
|
98
|
+
#
|
|
99
|
+
# http://www.gnu.org/licenses/
|
|
100
|
+
#
|
|
101
|
+
################################################################################
|
|
102
|
+
|
|
103
|
+
from sage.arith.functions import lcm
|
|
104
|
+
from sage.arith.misc import CRT_basis
|
|
105
|
+
from sage.groups.perm_gps.constructor import PermutationGroupElement as PermutationConstructor
|
|
106
|
+
from sage.groups.perm_gps.permgroup import PermutationGroup
|
|
107
|
+
from sage.groups.perm_gps.permgroup_element import PermutationGroupElement
|
|
108
|
+
from sage.misc.cachefunc import cached_method
|
|
109
|
+
from sage.misc.misc_c import prod
|
|
110
|
+
from sage.rings.integer_ring import ZZ
|
|
111
|
+
|
|
112
|
+
from .all import SL2Z
|
|
113
|
+
from .arithgroup_generic import ArithmeticSubgroup
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
Idm = SL2Z([1,0,0,1]) # identity
|
|
117
|
+
|
|
118
|
+
Lm = SL2Z([1,1,0,1]) # parabolic that fixes infinity
|
|
119
|
+
Rm = SL2Z([1,0,1,1]) # parabolic that fixes 0
|
|
120
|
+
S2m = SL2Z([0,-1,1,0]) # elliptic of order 2 (fix i)
|
|
121
|
+
S3m = SL2Z([0,1,-1,1]) # elliptic of order 3 (fix j)
|
|
122
|
+
|
|
123
|
+
S2mi = SL2Z([0,1,-1,0]) # the inverse of S2m in SL(2,Z)
|
|
124
|
+
S3mi = SL2Z([1,-1,1,0]) # the inverse of S3m in SL(2,Z)
|
|
125
|
+
Lmi = SL2Z([1,-1,0,1]) # the inverse of Lm in SL(2,Z)
|
|
126
|
+
Rmi = SL2Z([1,0,-1,1]) # the inverse of Rm in SL(2,Z)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def sl2z_word_problem(A):
|
|
130
|
+
r"""
|
|
131
|
+
Given an element of `\SL_2(\ZZ)`, express it as a word in the generators L =
|
|
132
|
+
[1,1,0,1] and R = [1,0,1,1].
|
|
133
|
+
|
|
134
|
+
The return format is a list of pairs ``(a,b)``, where ``a = 0`` or ``1``
|
|
135
|
+
denoting ``L`` or ``R`` respectively, and ``b`` is an integer exponent.
|
|
136
|
+
|
|
137
|
+
See also the function :func:`eval_sl2z_word`.
|
|
138
|
+
|
|
139
|
+
EXAMPLES::
|
|
140
|
+
|
|
141
|
+
sage: from sage.modular.arithgroup.arithgroup_perm import eval_sl2z_word, sl2z_word_problem
|
|
142
|
+
sage: m = SL2Z([1,0,0,1])
|
|
143
|
+
sage: eval_sl2z_word(sl2z_word_problem(m)) == m
|
|
144
|
+
True
|
|
145
|
+
sage: m = SL2Z([0,-1,1,0])
|
|
146
|
+
sage: eval_sl2z_word(sl2z_word_problem(m)) == m
|
|
147
|
+
True
|
|
148
|
+
sage: m = SL2Z([7,8,-50,-57])
|
|
149
|
+
sage: eval_sl2z_word(sl2z_word_problem(m)) == m
|
|
150
|
+
True
|
|
151
|
+
"""
|
|
152
|
+
A = SL2Z(A)
|
|
153
|
+
output = []
|
|
154
|
+
|
|
155
|
+
# If A00 is zero
|
|
156
|
+
if A[0, 0] == 0:
|
|
157
|
+
c = A[1,1]
|
|
158
|
+
if c != 1:
|
|
159
|
+
A = A*Lm**(c-1)*Rm*Lmi
|
|
160
|
+
output.extend([(0,1-c),(1,-1),(0,1)])
|
|
161
|
+
else:
|
|
162
|
+
A = A*Rm*Lmi
|
|
163
|
+
output.extend([(1,-1),(0,1)])
|
|
164
|
+
|
|
165
|
+
if A[0, 0] < 0: # Make sure A00 is positive
|
|
166
|
+
A = SL2Z(-1)*A
|
|
167
|
+
output.extend([(1,-1), (0,1), (1,-1), (0,1), (1,-1), (0,1)])
|
|
168
|
+
|
|
169
|
+
if A[0,1] < 0: # if A01 is negative make it positive
|
|
170
|
+
n = (-A[0,1]/A[0,0]).ceil() # n s.t. 0 <= A[0,1]+n*A[0,0] < A[0,0]
|
|
171
|
+
A = A*Lm**n
|
|
172
|
+
output.append((0, -n))
|
|
173
|
+
# At this point A00>0 and A01>=0
|
|
174
|
+
while not (A[0,0] == 0 or A[0,1] == 0):
|
|
175
|
+
if A[0,0] > A[0,1]:
|
|
176
|
+
n = (A[0,0]/A[0,1]).floor()
|
|
177
|
+
A = A*SL2Z([1,0,-n,1])
|
|
178
|
+
output.append((1, n))
|
|
179
|
+
|
|
180
|
+
else: # A[0,0]<=A[0,1]
|
|
181
|
+
n = (A[0,1]/A[0,0]).floor()
|
|
182
|
+
A = A*SL2Z([1,-n,0,1])
|
|
183
|
+
output.append((0, n))
|
|
184
|
+
|
|
185
|
+
if A == SL2Z.one():
|
|
186
|
+
pass # done, so don't add R^0
|
|
187
|
+
elif A[0, 0] == 0:
|
|
188
|
+
c = A[1, 1]
|
|
189
|
+
if c != 1:
|
|
190
|
+
A = A*Lm**(c-1)*Rm*Lmi
|
|
191
|
+
output.extend([(0,1-c),(1,-1),(0, 1)])
|
|
192
|
+
else:
|
|
193
|
+
A = A*Rm*Lmi
|
|
194
|
+
output.extend([(1,-1),(0,1)])
|
|
195
|
+
else:
|
|
196
|
+
c = A[1,0]
|
|
197
|
+
if c:
|
|
198
|
+
A = A*Rm**(-c)
|
|
199
|
+
output.append((1,c))
|
|
200
|
+
|
|
201
|
+
output.reverse()
|
|
202
|
+
return output
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def eval_sl2z_word(w):
|
|
206
|
+
r"""
|
|
207
|
+
Given a word in the format output by :func:`sl2z_word_problem`, convert it back
|
|
208
|
+
into an element of `\SL_2(\ZZ)`.
|
|
209
|
+
|
|
210
|
+
EXAMPLES::
|
|
211
|
+
|
|
212
|
+
sage: from sage.modular.arithgroup.arithgroup_perm import eval_sl2z_word
|
|
213
|
+
sage: eval_sl2z_word([(0, 1), (1, -1), (0, 0), (1, 3), (0, 2), (1, 9), (0, -1)])
|
|
214
|
+
[ 66 -59]
|
|
215
|
+
[ 47 -42]
|
|
216
|
+
"""
|
|
217
|
+
mat = [Lm, Rm]
|
|
218
|
+
w0 = Idm
|
|
219
|
+
w1 = w
|
|
220
|
+
return w0 * prod((mat[a[0]]**a[1] for a in w1), Idm)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def word_of_perms(w, p1, p2):
|
|
224
|
+
r"""
|
|
225
|
+
Given a word `w` as a list of 2-tuples ``(index,power)`` and permutations
|
|
226
|
+
``p1`` and ``p2`` return the product of ``p1`` and ``p2`` that corresponds
|
|
227
|
+
to ``w``.
|
|
228
|
+
|
|
229
|
+
EXAMPLES::
|
|
230
|
+
|
|
231
|
+
sage: import sage.modular.arithgroup.arithgroup_perm as ap
|
|
232
|
+
sage: S2 = SymmetricGroup(4)
|
|
233
|
+
sage: p1 = S2('(1,2)(3,4)')
|
|
234
|
+
sage: p2 = S2('(1,2,3,4)')
|
|
235
|
+
sage: ap.word_of_perms([(1,1),(0,1)], p1, p2) == p2 * p1
|
|
236
|
+
True
|
|
237
|
+
sage: ap.word_of_perms([(0,1),(1,1)], p1, p2) == p1 * p2
|
|
238
|
+
True
|
|
239
|
+
"""
|
|
240
|
+
if not isinstance(p1, PermutationGroupElement):
|
|
241
|
+
p1 = PermutationConstructor(p1)
|
|
242
|
+
if not isinstance(p2, PermutationGroupElement):
|
|
243
|
+
p2 = PermutationConstructor(p2)
|
|
244
|
+
|
|
245
|
+
G = p1.parent()
|
|
246
|
+
if G != p2.parent(): # find a minimal parent
|
|
247
|
+
G2 = p2.parent()
|
|
248
|
+
if G.has_coerce_map_from(G2):
|
|
249
|
+
p2 = G(p2)
|
|
250
|
+
elif G2.has_coerce_map_from(G):
|
|
251
|
+
G = G2
|
|
252
|
+
p1 = G(p1)
|
|
253
|
+
else:
|
|
254
|
+
G = PermutationGroup([p1,p2])
|
|
255
|
+
p1 = G(p1)
|
|
256
|
+
p2 = G(p2)
|
|
257
|
+
|
|
258
|
+
M = G.identity()
|
|
259
|
+
p = [p1, p2]
|
|
260
|
+
m = [p1.order(),p2.order()]
|
|
261
|
+
|
|
262
|
+
for i,j in w:
|
|
263
|
+
M *= p[i]**(j % m[i])
|
|
264
|
+
|
|
265
|
+
return M
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def _equalize_perms(l):
|
|
269
|
+
r"""
|
|
270
|
+
Transform a list of lists into a list of lists with identical length. Each
|
|
271
|
+
list ``p`` in the argument is completed with ``range(len(p),n)`` where
|
|
272
|
+
``n`` is the maximal length of the lists in ``l``. Note that the lists are
|
|
273
|
+
modified in-place (rather than returning new lists).
|
|
274
|
+
|
|
275
|
+
EXAMPLES::
|
|
276
|
+
|
|
277
|
+
sage: from sage.modular.arithgroup.arithgroup_perm import _equalize_perms
|
|
278
|
+
sage: l = [[],[1,0],[3,0,1,2]]
|
|
279
|
+
sage: _equalize_perms(l)
|
|
280
|
+
sage: l
|
|
281
|
+
[[0, 1, 2, 3], [1, 0, 2, 3], [3, 0, 1, 2]]
|
|
282
|
+
"""
|
|
283
|
+
n = max(map(len,l))
|
|
284
|
+
if n == 0:
|
|
285
|
+
n = 1
|
|
286
|
+
for p in l:
|
|
287
|
+
p.extend(list(range(len(p), n)))
|
|
288
|
+
|
|
289
|
+
# Tedious point: in order to unpickle pickled objects from prior to patch
|
|
290
|
+
# #11422, this function needs to accept two non-keyword arguments, to be
|
|
291
|
+
# interpreted as L and R. Hence the order of the arguments is slightly
|
|
292
|
+
# different from the class __init__ methods.
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def ArithmeticSubgroup_Permutation(
|
|
296
|
+
L=None, R=None, S2=None, S3=None,
|
|
297
|
+
relabel=False,
|
|
298
|
+
check=True):
|
|
299
|
+
r"""
|
|
300
|
+
Construct a subgroup of `\SL_2(\ZZ)` from the action of generators on its
|
|
301
|
+
right cosets.
|
|
302
|
+
|
|
303
|
+
Return an arithmetic subgroup knowing the action, given by permutations, of
|
|
304
|
+
at least two standard generators on the its cosets. The generators
|
|
305
|
+
considered are the following matrices:
|
|
306
|
+
|
|
307
|
+
.. MATH::
|
|
308
|
+
|
|
309
|
+
s_2 = \begin{pmatrix} 0 & -1 \\ 1 & 0 \end{pmatrix},\quad
|
|
310
|
+
s_3 = \begin{pmatrix} 0 & 1 \\ -1 & 1 \end{pmatrix},\quad
|
|
311
|
+
l = \begin{pmatrix} 1 & 1 \\ 0 & 1\end{pmatrix},\quad
|
|
312
|
+
r = \begin{pmatrix} 1 & 0 \\ 1 & 1 \end{pmatrix}.
|
|
313
|
+
|
|
314
|
+
An error will be raised if only one permutation is given. If no arguments
|
|
315
|
+
are given at all, the full modular group `\SL(2, \ZZ)` is returned.
|
|
316
|
+
|
|
317
|
+
INPUT:
|
|
318
|
+
|
|
319
|
+
- ``S2``, ``S3``, ``L``, ``R`` -- permutations; action of matrices on the
|
|
320
|
+
right cosets (each coset is identified to an element of `\{1,\dots,n\}`
|
|
321
|
+
where `1` is reserved for the identity coset)
|
|
322
|
+
|
|
323
|
+
- ``relabel`` -- boolean (default: ``False``); if ``True``, renumber the cosets in a
|
|
324
|
+
canonical way
|
|
325
|
+
|
|
326
|
+
- ``check`` -- boolean (default: ``True``); check that the input is valid (it
|
|
327
|
+
may be time efficient but less safe to set it to False)
|
|
328
|
+
|
|
329
|
+
EXAMPLES::
|
|
330
|
+
|
|
331
|
+
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)",S3="(1,2,3)")
|
|
332
|
+
sage: G
|
|
333
|
+
Arithmetic subgroup with permutations of right cosets
|
|
334
|
+
S2=(1,2)(3,4)
|
|
335
|
+
S3=(1,2,3)
|
|
336
|
+
L=(1,4,3)
|
|
337
|
+
R=(2,4,3)
|
|
338
|
+
sage: G.index()
|
|
339
|
+
4
|
|
340
|
+
|
|
341
|
+
sage: G = ArithmeticSubgroup_Permutation(); G
|
|
342
|
+
Arithmetic subgroup with permutations of right cosets
|
|
343
|
+
S2=()
|
|
344
|
+
S3=()
|
|
345
|
+
L=()
|
|
346
|
+
R=()
|
|
347
|
+
sage: G == SL2Z
|
|
348
|
+
True
|
|
349
|
+
|
|
350
|
+
Some invalid inputs::
|
|
351
|
+
|
|
352
|
+
sage: ArithmeticSubgroup_Permutation(S2="(1,2)")
|
|
353
|
+
Traceback (most recent call last):
|
|
354
|
+
...
|
|
355
|
+
ValueError: Need at least two generators
|
|
356
|
+
sage: ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(3,4,5)")
|
|
357
|
+
Traceback (most recent call last):
|
|
358
|
+
...
|
|
359
|
+
ValueError: Permutations do not generate a transitive group
|
|
360
|
+
sage: ArithmeticSubgroup_Permutation(L="(1,2)",R="(1,2,3)")
|
|
361
|
+
Traceback (most recent call last):
|
|
362
|
+
...
|
|
363
|
+
ValueError: Wrong relations between generators
|
|
364
|
+
sage: ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="()")
|
|
365
|
+
Traceback (most recent call last):
|
|
366
|
+
...
|
|
367
|
+
ValueError: S2^2 does not equal to S3^3
|
|
368
|
+
sage: ArithmeticSubgroup_Permutation(S2="(1,4,2,5,3)", S3="(1,3,5,2,4)")
|
|
369
|
+
Traceback (most recent call last):
|
|
370
|
+
...
|
|
371
|
+
ValueError: S2^2 = S3^3 must have order 1 or 2
|
|
372
|
+
|
|
373
|
+
The input checks can be disabled for speed::
|
|
374
|
+
|
|
375
|
+
sage: ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(3,4,5)", check=False) # don't do this!
|
|
376
|
+
Arithmetic subgroup with permutations of right cosets
|
|
377
|
+
S2=(1,2)
|
|
378
|
+
S3=(3,4,5)
|
|
379
|
+
L=(1,2)(3,5,4)
|
|
380
|
+
R=(1,2)(3,4,5)
|
|
381
|
+
"""
|
|
382
|
+
gens = [x for x in [S2,S3,L,R] if x is not None]
|
|
383
|
+
if len(gens) == 0:
|
|
384
|
+
S2 = S3 = L = R = ''
|
|
385
|
+
elif len(gens) < 2:
|
|
386
|
+
raise ValueError("Need at least two generators")
|
|
387
|
+
|
|
388
|
+
if S2 is not None:
|
|
389
|
+
S2 = PermutationConstructor(S2,check=check)
|
|
390
|
+
if S3 is not None:
|
|
391
|
+
S3 = PermutationConstructor(S3,check=check)
|
|
392
|
+
if L is not None:
|
|
393
|
+
L = PermutationConstructor(L,check=check)
|
|
394
|
+
if R is not None:
|
|
395
|
+
R = PermutationConstructor(R,check=check)
|
|
396
|
+
|
|
397
|
+
if L is not None:
|
|
398
|
+
if R is not None: # initialize from L,R
|
|
399
|
+
if S2 is None:
|
|
400
|
+
S2 = R * ~L * R
|
|
401
|
+
if S3 is None:
|
|
402
|
+
S3 = L * ~R
|
|
403
|
+
elif S2 is not None: # initialize from L,S2
|
|
404
|
+
if S3 is None:
|
|
405
|
+
S3 = ~S2 * ~L
|
|
406
|
+
if R is None:
|
|
407
|
+
R = ~S2 * ~L * S2
|
|
408
|
+
elif S3 is not None: # initialize from L,S3
|
|
409
|
+
if S2 is None:
|
|
410
|
+
S2 = ~L * ~S3
|
|
411
|
+
if R is None:
|
|
412
|
+
R = S3 * ~L * ~S3
|
|
413
|
+
elif R is not None:
|
|
414
|
+
if S2 is not None: # initialize from R, S2
|
|
415
|
+
if L is None:
|
|
416
|
+
L = ~S2 * ~R * S2
|
|
417
|
+
if S3 is None:
|
|
418
|
+
S3 = R * ~S2
|
|
419
|
+
elif S3 is not None: # initialize from R, S3
|
|
420
|
+
if L is None:
|
|
421
|
+
L = ~S3 * ~R * S3
|
|
422
|
+
if S2 is None:
|
|
423
|
+
S2 = ~S3 * R
|
|
424
|
+
else: # initialize from S2, S3
|
|
425
|
+
if L is None:
|
|
426
|
+
L = ~S3 * ~S2
|
|
427
|
+
if R is None:
|
|
428
|
+
R = S3 * S2
|
|
429
|
+
|
|
430
|
+
if check and (L != ~S3 * ~S2 or R != S3 * S2):
|
|
431
|
+
raise ValueError("Wrong relations between generators")
|
|
432
|
+
|
|
433
|
+
inv = S2*S2
|
|
434
|
+
|
|
435
|
+
if check:
|
|
436
|
+
if inv != S3*S3*S3:
|
|
437
|
+
raise ValueError("S2^2 does not equal to S3^3")
|
|
438
|
+
elif not (inv*inv).is_one():
|
|
439
|
+
raise ValueError("S2^2 = S3^3 must have order 1 or 2")
|
|
440
|
+
|
|
441
|
+
# Check transitivity. This is the most expensive check, so we do it
|
|
442
|
+
# last.
|
|
443
|
+
G = PermutationGroup(gens)
|
|
444
|
+
if not G.is_transitive():
|
|
445
|
+
raise ValueError("Permutations do not generate a transitive group")
|
|
446
|
+
|
|
447
|
+
s2 = [i-1 for i in S2.domain()]
|
|
448
|
+
s3 = [i-1 for i in S3.domain()]
|
|
449
|
+
l = [i-1 for i in L.domain()]
|
|
450
|
+
r = [i-1 for i in R.domain()]
|
|
451
|
+
_equalize_perms((s2,s3,l,r))
|
|
452
|
+
|
|
453
|
+
if inv.is_one(): # the group is even
|
|
454
|
+
G = EvenArithmeticSubgroup_Permutation(s2,s3,l,r)
|
|
455
|
+
else: # the group is odd
|
|
456
|
+
G = OddArithmeticSubgroup_Permutation(s2,s3,l,r)
|
|
457
|
+
|
|
458
|
+
if relabel:
|
|
459
|
+
G.relabel()
|
|
460
|
+
|
|
461
|
+
return G
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
class ArithmeticSubgroup_Permutation_class(ArithmeticSubgroup):
|
|
465
|
+
r"""
|
|
466
|
+
A subgroup of `\SL_2(\ZZ)` defined by the action of generators on its
|
|
467
|
+
coset graph.
|
|
468
|
+
|
|
469
|
+
The class stores the action of generators `s_2, s_3, l, r` on right cosets
|
|
470
|
+
`Hg` of a finite index subgroup `H < \SL_2(\ZZ)`. In particular the action of
|
|
471
|
+
`\SL_2(\ZZ)` on the cosets is on right.
|
|
472
|
+
|
|
473
|
+
.. MATH::
|
|
474
|
+
|
|
475
|
+
s_2 = \begin{pmatrix} 0 & -1 \\ 1 & 0 \end{pmatrix},\quad
|
|
476
|
+
s_3 = \begin{pmatrix} 0 & 1 \\ -1 & 1 \end{pmatrix},\quad
|
|
477
|
+
l = \begin{pmatrix} 1 & 1 \\ 0 & 1\end{pmatrix},\quad
|
|
478
|
+
r = \begin{pmatrix} 1 & 0 \\ 1 & 1 \end{pmatrix}.
|
|
479
|
+
|
|
480
|
+
TESTS::
|
|
481
|
+
|
|
482
|
+
sage: s2 = PermutationGroupElement('(1,2)(3,4)(5,6)')
|
|
483
|
+
sage: s3 = PermutationGroupElement('(1,3,5)(2,4,6)')
|
|
484
|
+
sage: G = ArithmeticSubgroup_Permutation(S2=s2, S3=s3)
|
|
485
|
+
sage: G.S2() == s2
|
|
486
|
+
True
|
|
487
|
+
sage: G.S3() == s3
|
|
488
|
+
True
|
|
489
|
+
sage: G == ArithmeticSubgroup_Permutation(L=G.L(), R=G.R())
|
|
490
|
+
True
|
|
491
|
+
sage: G == ArithmeticSubgroup_Permutation(L=G.L(), S2=G.S2())
|
|
492
|
+
True
|
|
493
|
+
sage: G == ArithmeticSubgroup_Permutation(L=G.L(), S3=G.S3())
|
|
494
|
+
True
|
|
495
|
+
sage: G == ArithmeticSubgroup_Permutation(R=G.R(), S2=G.S2())
|
|
496
|
+
True
|
|
497
|
+
sage: G == ArithmeticSubgroup_Permutation(R=G.R(), S3=G.S3())
|
|
498
|
+
True
|
|
499
|
+
sage: G == ArithmeticSubgroup_Permutation(S2=G.S2(), S3=G.S3())
|
|
500
|
+
True
|
|
501
|
+
|
|
502
|
+
sage: G = ArithmeticSubgroup_Permutation(S2='',S3='')
|
|
503
|
+
sage: TestSuite(G).run()
|
|
504
|
+
|
|
505
|
+
sage: S2 = '(1,2)(3,4)(5,6)'
|
|
506
|
+
sage: S3 = '(1,2,3)(4,5,6)'
|
|
507
|
+
sage: G = ArithmeticSubgroup_Permutation(S2=S2, S3=S3)
|
|
508
|
+
sage: TestSuite(G).run()
|
|
509
|
+
"""
|
|
510
|
+
|
|
511
|
+
def __eq__(self, other):
|
|
512
|
+
r"""
|
|
513
|
+
Equality test.
|
|
514
|
+
|
|
515
|
+
TESTS::
|
|
516
|
+
|
|
517
|
+
sage: G2 = Gamma(2)
|
|
518
|
+
sage: G3 = Gamma(3)
|
|
519
|
+
sage: H = ArithmeticSubgroup_Permutation(S2="(1,4)(2,6)(3,5)",S3="(1,2,3)(4,5,6)")
|
|
520
|
+
sage: (G2 == H) and (H == G2)
|
|
521
|
+
True
|
|
522
|
+
sage: (G3 == H) or (H == G3)
|
|
523
|
+
False
|
|
524
|
+
|
|
525
|
+
sage: G2 = Gamma1(2)
|
|
526
|
+
sage: G3 = Gamma1(3)
|
|
527
|
+
sage: H = ArithmeticSubgroup_Permutation(S2="(1,6,4,3)(2,7,5,8)",S3="(1,2,3,4,5,6)(7,8)")
|
|
528
|
+
sage: (G2 == H) or (H == G2)
|
|
529
|
+
False
|
|
530
|
+
sage: (G3 == H) and (H == G3)
|
|
531
|
+
True
|
|
532
|
+
"""
|
|
533
|
+
if isinstance(other, ArithmeticSubgroup_Permutation_class):
|
|
534
|
+
return (self.is_odd() == other.is_odd() and
|
|
535
|
+
self.index() == other.index() and
|
|
536
|
+
self.relabel(inplace=False)._S2 == other.relabel(inplace=False)._S2 and
|
|
537
|
+
self.relabel(inplace=False)._S3 == other.relabel(inplace=False)._S3)
|
|
538
|
+
|
|
539
|
+
elif isinstance(other, ArithmeticSubgroup):
|
|
540
|
+
return self == other.as_permutation_group()
|
|
541
|
+
|
|
542
|
+
else:
|
|
543
|
+
return False
|
|
544
|
+
|
|
545
|
+
def __ne__(self, other):
|
|
546
|
+
"""
|
|
547
|
+
Check that ``self`` is not equal to ``other``.
|
|
548
|
+
|
|
549
|
+
TESTS::
|
|
550
|
+
|
|
551
|
+
sage: G2 = Gamma(2)
|
|
552
|
+
sage: G3 = Gamma(3)
|
|
553
|
+
sage: H = ArithmeticSubgroup_Permutation(S2="(1,4)(2,6)(3,5)",S3="(1,2,3)(4,5,6)")
|
|
554
|
+
sage: (G2 != H) or (H != G2)
|
|
555
|
+
False
|
|
556
|
+
sage: (G3 != H) and (H != G3)
|
|
557
|
+
True
|
|
558
|
+
|
|
559
|
+
sage: G2 = Gamma1(2)
|
|
560
|
+
sage: G3 = Gamma1(3)
|
|
561
|
+
sage: H = ArithmeticSubgroup_Permutation(S2="(1,6,4,3)(2,7,5,8)",S3="(1,2,3,4,5,6)(7,8)")
|
|
562
|
+
sage: (G2 != H) and (H != G2)
|
|
563
|
+
True
|
|
564
|
+
sage: (G3 != H) or (H != G3)
|
|
565
|
+
False
|
|
566
|
+
"""
|
|
567
|
+
return not (self == other)
|
|
568
|
+
|
|
569
|
+
def __hash__(self):
|
|
570
|
+
r"""
|
|
571
|
+
Return a hash value.
|
|
572
|
+
|
|
573
|
+
TESTS::
|
|
574
|
+
|
|
575
|
+
sage: G1 = ArithmeticSubgroup_Permutation(S2='(1,2)(3,4)(5,6)',S3='(1,2,3)(4,5,6)')
|
|
576
|
+
sage: G2 = ArithmeticSubgroup_Permutation(S2='(1,2)(3,4)(5,6)',S3='(1,5,6)(4,2,3)')
|
|
577
|
+
sage: hash(G1) == hash(G2)
|
|
578
|
+
False
|
|
579
|
+
"""
|
|
580
|
+
return hash((tuple(self.relabel(inplace=False)._S2),
|
|
581
|
+
tuple(self.relabel(inplace=False)._S3)))
|
|
582
|
+
|
|
583
|
+
def _repr_(self):
|
|
584
|
+
r"""
|
|
585
|
+
String representation of ``self``.
|
|
586
|
+
|
|
587
|
+
EXAMPLES::
|
|
588
|
+
|
|
589
|
+
sage: G = Gamma(2).as_permutation_group()
|
|
590
|
+
sage: repr(G) #indirect doctest
|
|
591
|
+
'Arithmetic subgroup with permutations of right cosets\n S2=(1,4)(2,6)(3,5)\n S3=(1,2,3)(4,5,6)\n L=(1,5)(2,4)(3,6)\n R=(1,6)(2,5)(3,4)'
|
|
592
|
+
sage: G = Gamma(3).as_permutation_group()
|
|
593
|
+
sage: repr(G) #indirect doctest
|
|
594
|
+
'Arithmetic subgroup of index 24'
|
|
595
|
+
"""
|
|
596
|
+
if self.index() < 20:
|
|
597
|
+
return "Arithmetic subgroup with permutations of right cosets\n S2=%s\n S3=%s\n L=%s\n R=%s" % (
|
|
598
|
+
self.S2(), self.S3(), self.L(), self.R())
|
|
599
|
+
|
|
600
|
+
else:
|
|
601
|
+
return "Arithmetic subgroup of index %d" % self.index()
|
|
602
|
+
|
|
603
|
+
#
|
|
604
|
+
# Attribute access
|
|
605
|
+
#
|
|
606
|
+
|
|
607
|
+
def S2(self):
|
|
608
|
+
r"""
|
|
609
|
+
Return the action of the matrix `s_2` as a permutation of cosets.
|
|
610
|
+
|
|
611
|
+
.. MATH::
|
|
612
|
+
|
|
613
|
+
s_2 = \begin{pmatrix}0&-1\\1&0\end{pmatrix}
|
|
614
|
+
|
|
615
|
+
EXAMPLES::
|
|
616
|
+
|
|
617
|
+
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(1,2,3)")
|
|
618
|
+
sage: G.S2()
|
|
619
|
+
(1,2)
|
|
620
|
+
"""
|
|
621
|
+
return PermutationConstructor([i+1 for i in self._S2], check=False)
|
|
622
|
+
|
|
623
|
+
def S3(self):
|
|
624
|
+
r"""
|
|
625
|
+
Return the action of the matrix `s_3` as a permutation of cosets.
|
|
626
|
+
|
|
627
|
+
.. MATH::
|
|
628
|
+
|
|
629
|
+
s_3 = \begin{pmatrix} 0 & 1 \\ -1 & 1 \end{pmatrix},\quad
|
|
630
|
+
|
|
631
|
+
EXAMPLES::
|
|
632
|
+
|
|
633
|
+
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(1,2,3)")
|
|
634
|
+
sage: G.S3()
|
|
635
|
+
(1,2,3)
|
|
636
|
+
"""
|
|
637
|
+
|
|
638
|
+
return PermutationConstructor([i+1 for i in self._S3], check=False)
|
|
639
|
+
|
|
640
|
+
def L(self):
|
|
641
|
+
r"""
|
|
642
|
+
Return the action of the matrix `l` as a permutation of cosets.
|
|
643
|
+
|
|
644
|
+
.. MATH::
|
|
645
|
+
|
|
646
|
+
l = \begin{pmatrix}1&1\\0&1\end{pmatrix}
|
|
647
|
+
|
|
648
|
+
EXAMPLES::
|
|
649
|
+
|
|
650
|
+
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(1,2,3)")
|
|
651
|
+
sage: G.L()
|
|
652
|
+
(1,3)
|
|
653
|
+
"""
|
|
654
|
+
return PermutationConstructor([i+1 for i in self._L], check=False)
|
|
655
|
+
|
|
656
|
+
def R(self):
|
|
657
|
+
r"""
|
|
658
|
+
Return the action of the matrix `r` as a permutation of cosets.
|
|
659
|
+
|
|
660
|
+
.. MATH::
|
|
661
|
+
|
|
662
|
+
r = \begin{pmatrix}1&0\\1&1\end{pmatrix}
|
|
663
|
+
|
|
664
|
+
EXAMPLES::
|
|
665
|
+
|
|
666
|
+
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(1,2,3)")
|
|
667
|
+
sage: G.R()
|
|
668
|
+
(2,3)
|
|
669
|
+
"""
|
|
670
|
+
return PermutationConstructor([i+1 for i in self._R], check=False)
|
|
671
|
+
|
|
672
|
+
def perm_group(self):
|
|
673
|
+
r"""
|
|
674
|
+
Return the underlying permutation group.
|
|
675
|
+
|
|
676
|
+
The permutation group returned is isomorphic to the action of the
|
|
677
|
+
generators `s_2` (element of order two), `s_3` (element of order 3), `l`
|
|
678
|
+
(parabolic element) and `r` (parabolic element) on right cosets (the
|
|
679
|
+
action is on the right).
|
|
680
|
+
|
|
681
|
+
EXAMPLES::
|
|
682
|
+
|
|
683
|
+
sage: import sage.modular.arithgroup.arithgroup_perm as ap
|
|
684
|
+
sage: ap.HsuExample10().perm_group()
|
|
685
|
+
Permutation Group with generators [(1,2)(3,4)(5,6)(7,8)(9,10), (1,8,3)(2,4,6)(5,7,10), (1,4)(2,5,9,10,8)(3,7,6), (1,7,9,10,6)(2,3)(4,5,8)]
|
|
686
|
+
"""
|
|
687
|
+
# we set canonicalize to False as otherwise PermutationGroup changes the
|
|
688
|
+
# order of the generators.
|
|
689
|
+
return PermutationGroup([self.S2(), self.S3(), self.L(), self.R()], canonicalize=False)
|
|
690
|
+
|
|
691
|
+
def index(self):
|
|
692
|
+
r"""
|
|
693
|
+
Return the index of this modular subgroup in the full modular group.
|
|
694
|
+
|
|
695
|
+
EXAMPLES::
|
|
696
|
+
|
|
697
|
+
sage: G = Gamma(2)
|
|
698
|
+
sage: P = G.as_permutation_group()
|
|
699
|
+
sage: P.index()
|
|
700
|
+
6
|
|
701
|
+
sage: G.index() == P.index()
|
|
702
|
+
True
|
|
703
|
+
|
|
704
|
+
sage: G = Gamma0(8)
|
|
705
|
+
sage: P = G.as_permutation_group()
|
|
706
|
+
sage: P.index()
|
|
707
|
+
12
|
|
708
|
+
sage: G.index() == P.index()
|
|
709
|
+
True
|
|
710
|
+
|
|
711
|
+
sage: G = Gamma1(6)
|
|
712
|
+
sage: P = G.as_permutation_group()
|
|
713
|
+
sage: P.index()
|
|
714
|
+
24
|
|
715
|
+
sage: G.index() == P.index()
|
|
716
|
+
True
|
|
717
|
+
"""
|
|
718
|
+
return len(self._S2)
|
|
719
|
+
|
|
720
|
+
#
|
|
721
|
+
# Canonical renumbering
|
|
722
|
+
#
|
|
723
|
+
|
|
724
|
+
def relabel(self, inplace=True):
|
|
725
|
+
r"""
|
|
726
|
+
Relabel the cosets of this modular subgroup in a canonical way.
|
|
727
|
+
|
|
728
|
+
The implementation of modular subgroup by action of generators on cosets
|
|
729
|
+
depends on the choice of a numbering. This function provides
|
|
730
|
+
canonical labels in the sense that two equal subgroups with different
|
|
731
|
+
labels are relabeled the same way. The default implementation relabels
|
|
732
|
+
the group itself. If you want to get a relabeled copy of your modular
|
|
733
|
+
subgroup, put to ``False`` the option ``inplace``.
|
|
734
|
+
|
|
735
|
+
ALGORITHM:
|
|
736
|
+
|
|
737
|
+
We give an overview of how the canonical labels for the modular subgroup
|
|
738
|
+
are built. The procedure only uses the permutations `S3` and `S2` that
|
|
739
|
+
define the modular subgroup and can be used to renumber any
|
|
740
|
+
transitive action of the symmetric group. In other words, the algorithm
|
|
741
|
+
construct a canonical representative of a transitive subgroup in its
|
|
742
|
+
conjugacy class in the full symmetric group.
|
|
743
|
+
|
|
744
|
+
1. The identity is still numbered `0` and set the current vertex to be
|
|
745
|
+
the identity.
|
|
746
|
+
|
|
747
|
+
2. Number the cycle of `S3` the current vertex belongs to: if the
|
|
748
|
+
current vertex is labeled `n` then the numbering in such way that the
|
|
749
|
+
cycle becomes `(n, n+1, \ldots, n+k)`).
|
|
750
|
+
|
|
751
|
+
3. Find a new current vertex using the permutation `S2`.
|
|
752
|
+
If all vertices are relabeled then it's done, otherwise go to step 2.
|
|
753
|
+
|
|
754
|
+
EXAMPLES::
|
|
755
|
+
|
|
756
|
+
sage: S2 = "(1,2)(3,4)(5,6)"; S3 = "(1,2,3)(4,5,6)"
|
|
757
|
+
sage: G1 = ArithmeticSubgroup_Permutation(S2=S2,S3=S3); G1
|
|
758
|
+
Arithmetic subgroup with permutations of right cosets
|
|
759
|
+
S2=(1,2)(3,4)(5,6)
|
|
760
|
+
S3=(1,2,3)(4,5,6)
|
|
761
|
+
L=(1,4,5,3)
|
|
762
|
+
R=(2,4,6,3)
|
|
763
|
+
sage: G1.relabel(); G1
|
|
764
|
+
Arithmetic subgroup with permutations of right cosets
|
|
765
|
+
S2=(1,2)(3,4)(5,6)
|
|
766
|
+
S3=(1,2,3)(4,5,6)
|
|
767
|
+
L=(1,4,5,3)
|
|
768
|
+
R=(2,4,6,3)
|
|
769
|
+
|
|
770
|
+
sage: S2 = "(1,2)(3,5)(4,6)"; S3 = "(1,2,3)(4,5,6)"
|
|
771
|
+
sage: G2 = ArithmeticSubgroup_Permutation(S2=S2,S3=S3); G2
|
|
772
|
+
Arithmetic subgroup with permutations of right cosets
|
|
773
|
+
S2=(1,2)(3,5)(4,6)
|
|
774
|
+
S3=(1,2,3)(4,5,6)
|
|
775
|
+
L=(1,5,6,3)
|
|
776
|
+
R=(2,5,4,3)
|
|
777
|
+
sage: G2.relabel(); G2
|
|
778
|
+
Arithmetic subgroup with permutations of right cosets
|
|
779
|
+
S2=(1,2)(3,4)(5,6)
|
|
780
|
+
S3=(1,2,3)(4,5,6)
|
|
781
|
+
L=(1,4,5,3)
|
|
782
|
+
R=(2,4,6,3)
|
|
783
|
+
|
|
784
|
+
sage: S2 = "(1,2)(3,6)(4,5)"; S3 = "(1,2,3)(4,5,6)"
|
|
785
|
+
sage: G3 = ArithmeticSubgroup_Permutation(S2=S2,S3=S3); G3
|
|
786
|
+
Arithmetic subgroup with permutations of right cosets
|
|
787
|
+
S2=(1,2)(3,6)(4,5)
|
|
788
|
+
S3=(1,2,3)(4,5,6)
|
|
789
|
+
L=(1,6,4,3)
|
|
790
|
+
R=(2,6,5,3)
|
|
791
|
+
sage: G4 = G3.relabel(inplace=False)
|
|
792
|
+
sage: G4
|
|
793
|
+
Arithmetic subgroup with permutations of right cosets
|
|
794
|
+
S2=(1,2)(3,4)(5,6)
|
|
795
|
+
S3=(1,2,3)(4,5,6)
|
|
796
|
+
L=(1,4,5,3)
|
|
797
|
+
R=(2,4,6,3)
|
|
798
|
+
sage: G3 is G4
|
|
799
|
+
False
|
|
800
|
+
|
|
801
|
+
TESTS::
|
|
802
|
+
|
|
803
|
+
sage: S2 = "(1,2)(3,6)(4,5)"
|
|
804
|
+
sage: S3 = "(1,2,3)(4,5,6)"
|
|
805
|
+
sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)
|
|
806
|
+
sage: H = G.relabel(inplace=False)
|
|
807
|
+
sage: G is H
|
|
808
|
+
False
|
|
809
|
+
sage: G._S2 is H._S2 or G._S3 is H._S3 or G._L is H._L or G._R is H._R
|
|
810
|
+
False
|
|
811
|
+
sage: G.relabel(inplace=False) is H
|
|
812
|
+
True
|
|
813
|
+
sage: H.relabel(inplace=False) is H
|
|
814
|
+
True
|
|
815
|
+
sage: G.relabel(); G
|
|
816
|
+
Arithmetic subgroup with permutations of right cosets
|
|
817
|
+
S2=(1,2)(3,4)(5,6)
|
|
818
|
+
S3=(1,2,3)(4,5,6)
|
|
819
|
+
L=(1,4,5,3)
|
|
820
|
+
R=(2,4,6,3)
|
|
821
|
+
sage: G.relabel(inplace=False) is G
|
|
822
|
+
True
|
|
823
|
+
"""
|
|
824
|
+
if hasattr(self,'_canonical_label_group'):
|
|
825
|
+
if inplace:
|
|
826
|
+
if self is not self._canonical_label_group:
|
|
827
|
+
self.__dict__ = self._canonical_label_group.__dict__
|
|
828
|
+
self._canonical_label_group = self
|
|
829
|
+
else:
|
|
830
|
+
return self._canonical_label_group
|
|
831
|
+
|
|
832
|
+
if inplace:
|
|
833
|
+
G = self
|
|
834
|
+
else:
|
|
835
|
+
from copy import deepcopy
|
|
836
|
+
G = deepcopy(self)
|
|
837
|
+
|
|
838
|
+
n = G.index()
|
|
839
|
+
mapping = G._canonical_rooted_labels()
|
|
840
|
+
S2 = G._S2
|
|
841
|
+
S3 = G._S3
|
|
842
|
+
L = G._L
|
|
843
|
+
R = G._R
|
|
844
|
+
G._S2 = [None]*n
|
|
845
|
+
G._S3 = [None]*n
|
|
846
|
+
G._L = [None]*n
|
|
847
|
+
G._R = [None]*n
|
|
848
|
+
|
|
849
|
+
for i in range(n):
|
|
850
|
+
G._S2[mapping[i]] = mapping[S2[i]]
|
|
851
|
+
G._S3[mapping[i]] = mapping[S3[i]]
|
|
852
|
+
G._L[mapping[i]] = mapping[L[i]]
|
|
853
|
+
G._R[mapping[i]] = mapping[R[i]]
|
|
854
|
+
|
|
855
|
+
self._canonical_label_group = G
|
|
856
|
+
G._canonical_label_group = G
|
|
857
|
+
|
|
858
|
+
if not inplace:
|
|
859
|
+
return G
|
|
860
|
+
|
|
861
|
+
def _canonical_unrooted_labels(self):
|
|
862
|
+
r"""
|
|
863
|
+
Return the smallest label among rooted label.
|
|
864
|
+
|
|
865
|
+
OUTPUT:
|
|
866
|
+
|
|
867
|
+
A 3-tuple of lists corresponding to permutations. The first list is the
|
|
868
|
+
mapping that gives the canonical labels and the second and third one
|
|
869
|
+
correspond to the permutations obtained by the conjugation of ``S2`` and
|
|
870
|
+
``S3``.
|
|
871
|
+
|
|
872
|
+
EXAMPLES::
|
|
873
|
+
|
|
874
|
+
sage: S2 = "(1,2)(4,5)"
|
|
875
|
+
sage: S3 = "(1,3,4)(2,5,6)"
|
|
876
|
+
sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)
|
|
877
|
+
sage: s2,s3 = G._S2,G._S3
|
|
878
|
+
sage: m,ss2,ss3 = G._canonical_unrooted_labels()
|
|
879
|
+
sage: all(ss2[m[i]] == m[s2[i]] for i in range(6))
|
|
880
|
+
True
|
|
881
|
+
sage: all(ss3[m[i]] == m[s3[i]] for i in range(6))
|
|
882
|
+
True
|
|
883
|
+
|
|
884
|
+
sage: S2 = "(1,2)(3,4)(5,6)"
|
|
885
|
+
sage: S3 = "(1,3,4)(2,5,6)"
|
|
886
|
+
sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)
|
|
887
|
+
sage: s2,s3 = G._S2,G._S3
|
|
888
|
+
sage: m,ss2,ss3 = G._canonical_unrooted_labels()
|
|
889
|
+
sage: all(ss2[m[i]] == m[s2[i]] for i in range(6))
|
|
890
|
+
True
|
|
891
|
+
sage: all(ss3[m[i]] == m[s3[i]] for i in range(6))
|
|
892
|
+
True
|
|
893
|
+
|
|
894
|
+
sage: S2 = "(1,2)(3,4)(5,6)"
|
|
895
|
+
sage: S3 = "(1,3,5)(2,4,6)"
|
|
896
|
+
sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)
|
|
897
|
+
sage: s2,s3 = G._S2,G._S3
|
|
898
|
+
sage: m,ss2,ss3 = G._canonical_unrooted_labels()
|
|
899
|
+
sage: all(ss2[m[i]] == m[s2[i]] for i in range(6))
|
|
900
|
+
True
|
|
901
|
+
sage: all(ss3[m[i]] == m[s3[i]] for i in range(6))
|
|
902
|
+
True
|
|
903
|
+
"""
|
|
904
|
+
n = self.index()
|
|
905
|
+
S2_win = [None] * n
|
|
906
|
+
S3_win = [None] * n
|
|
907
|
+
S2_test = [None] * n
|
|
908
|
+
S3_test = [None] * n
|
|
909
|
+
|
|
910
|
+
m_win = self._canonical_rooted_labels(0)
|
|
911
|
+
for i in range(n): # conjugation
|
|
912
|
+
S2_win[m_win[i]] = m_win[self._S2[i]]
|
|
913
|
+
S3_win[m_win[i]] = m_win[self._S3[i]]
|
|
914
|
+
|
|
915
|
+
for j0 in range(1,self.index()):
|
|
916
|
+
m_test = self._canonical_rooted_labels(j0)
|
|
917
|
+
for i in range(n):
|
|
918
|
+
S2_test[m_test[i]] = m_test[self._S2[i]]
|
|
919
|
+
S3_test[m_test[i]] = m_test[self._S3[i]]
|
|
920
|
+
|
|
921
|
+
for i in range(n-1):
|
|
922
|
+
if (S2_test[i] < S2_win[i] or
|
|
923
|
+
(S2_test[i] == S2_win[i] and S3_test[i] < S3_win[i])):
|
|
924
|
+
S2_win,S2_test = S2_test,S2_win
|
|
925
|
+
S3_win,S3_test = S3_test,S3_win
|
|
926
|
+
m_win = m_test
|
|
927
|
+
break
|
|
928
|
+
|
|
929
|
+
return m_win, S2_win, S3_win
|
|
930
|
+
|
|
931
|
+
def _canonical_rooted_labels(self, j0=0):
|
|
932
|
+
r"""
|
|
933
|
+
Return the permutation which correspond to the renumbering of ``self`` in
|
|
934
|
+
order to get canonical labels.
|
|
935
|
+
|
|
936
|
+
If ``j0`` is 0 then the renumbering corresponds to the same group. If
|
|
937
|
+
not, the renumbering corresponds to the conjugated subgroup such that
|
|
938
|
+
``j0`` becomes the identity coset.
|
|
939
|
+
|
|
940
|
+
EXAMPLES::
|
|
941
|
+
|
|
942
|
+
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(1,2,3)")
|
|
943
|
+
sage: G._canonical_rooted_labels(0)
|
|
944
|
+
[0, 1, 2]
|
|
945
|
+
sage: G._canonical_rooted_labels(1)
|
|
946
|
+
[2, 0, 1]
|
|
947
|
+
sage: G._canonical_rooted_labels(2)
|
|
948
|
+
[1, 2, 0]
|
|
949
|
+
"""
|
|
950
|
+
x = self._S3
|
|
951
|
+
y = self._S2
|
|
952
|
+
n = len(x)
|
|
953
|
+
|
|
954
|
+
k = 0
|
|
955
|
+
mapping = [None] * n
|
|
956
|
+
waiting = []
|
|
957
|
+
|
|
958
|
+
while True:
|
|
959
|
+
# initialize at j0
|
|
960
|
+
mapping[j0] = k
|
|
961
|
+
waiting.append(j0)
|
|
962
|
+
k += 1
|
|
963
|
+
# complete x cycle from j0
|
|
964
|
+
j = x[j0]
|
|
965
|
+
while j != j0:
|
|
966
|
+
mapping[j] = k
|
|
967
|
+
waiting.append(j)
|
|
968
|
+
k += 1
|
|
969
|
+
j = x[j]
|
|
970
|
+
|
|
971
|
+
# if everybody is labelled do not go further
|
|
972
|
+
if k == n:
|
|
973
|
+
break
|
|
974
|
+
|
|
975
|
+
# find another guy with y
|
|
976
|
+
j0 = y[waiting.pop(0)]
|
|
977
|
+
while mapping[j0] is not None:
|
|
978
|
+
j0 = y[waiting.pop(0)]
|
|
979
|
+
|
|
980
|
+
return mapping
|
|
981
|
+
|
|
982
|
+
#
|
|
983
|
+
# Contains and random element
|
|
984
|
+
#
|
|
985
|
+
|
|
986
|
+
@cached_method
|
|
987
|
+
def _index_to_lr_cusp_width(self):
|
|
988
|
+
r"""
|
|
989
|
+
Precomputation of cusps data of ``self`` for this modular subgroup.
|
|
990
|
+
|
|
991
|
+
This is a central precomputation for the ``.__contains__()`` method and
|
|
992
|
+
consists in two lists of positive integers ``lc`` and ``rc`` of length
|
|
993
|
+
the index of the subgroup. They are defined as follows: the number
|
|
994
|
+
``lc[i]`` (resp ``rc[i]``) is the length of the cycle of ``L`` (resp.
|
|
995
|
+
``R``) which contains ``i``.
|
|
996
|
+
|
|
997
|
+
EXAMPLES::
|
|
998
|
+
|
|
999
|
+
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(1,2,3)")
|
|
1000
|
+
sage: G.L()
|
|
1001
|
+
(1,3)
|
|
1002
|
+
sage: G.R()
|
|
1003
|
+
(2,3)
|
|
1004
|
+
sage: G._index_to_lr_cusp_width()
|
|
1005
|
+
([2, 1, 2], [1, 2, 2])
|
|
1006
|
+
"""
|
|
1007
|
+
G = self.relabel(inplace=False)
|
|
1008
|
+
|
|
1009
|
+
l = G.L()
|
|
1010
|
+
l_cycle_length = [None]*self.index()
|
|
1011
|
+
for c in l.cycle_tuples(singletons=True):
|
|
1012
|
+
for i in c:
|
|
1013
|
+
l_cycle_length[i-1] = len(c)
|
|
1014
|
+
|
|
1015
|
+
r = G.R()
|
|
1016
|
+
r_cycle_length = [None]*self.index()
|
|
1017
|
+
for c in r.cycle_tuples(singletons=True):
|
|
1018
|
+
for i in c:
|
|
1019
|
+
r_cycle_length[i-1] = len(c)
|
|
1020
|
+
|
|
1021
|
+
return (l_cycle_length, r_cycle_length)
|
|
1022
|
+
|
|
1023
|
+
def _contains_sl2(self, a, b, c, d):
|
|
1024
|
+
r"""
|
|
1025
|
+
Test whether ``[a,b;c,d]`` is in the group or not.
|
|
1026
|
+
|
|
1027
|
+
ALGORITHM:
|
|
1028
|
+
|
|
1029
|
+
An element of `\SL_2(\ZZ)` is in a given modular subgroup if it does not
|
|
1030
|
+
permute the identity coset!
|
|
1031
|
+
|
|
1032
|
+
TESTS::
|
|
1033
|
+
|
|
1034
|
+
sage: G = Gamma(4)
|
|
1035
|
+
sage: m1 = G([1,4,0,1])
|
|
1036
|
+
sage: m2 = G([17,4,4,1])
|
|
1037
|
+
sage: m3 = G([1,-4,-4,17])
|
|
1038
|
+
sage: m4 = SL2Z([1,2,0,1])
|
|
1039
|
+
sage: P = G.as_permutation_group()
|
|
1040
|
+
sage: m1 in P
|
|
1041
|
+
True
|
|
1042
|
+
sage: m2 in P
|
|
1043
|
+
True
|
|
1044
|
+
sage: m3 in P
|
|
1045
|
+
True
|
|
1046
|
+
sage: m4 in P
|
|
1047
|
+
False
|
|
1048
|
+
"""
|
|
1049
|
+
w = sl2z_word_problem([a,b,c,d])
|
|
1050
|
+
|
|
1051
|
+
perms = [self.relabel(inplace=False)._L,self.relabel(inplace=False)._R]
|
|
1052
|
+
widths = self._index_to_lr_cusp_width()
|
|
1053
|
+
|
|
1054
|
+
k = 0
|
|
1055
|
+
for (i,j) in w:
|
|
1056
|
+
for _ in range(j % widths[i][k]):
|
|
1057
|
+
k = perms[i][k]
|
|
1058
|
+
|
|
1059
|
+
return not k
|
|
1060
|
+
|
|
1061
|
+
def random_element(self, initial_steps=30):
|
|
1062
|
+
r"""
|
|
1063
|
+
Return a random element in this subgroup.
|
|
1064
|
+
|
|
1065
|
+
The algorithm uses a random walk on the Cayley graph of `\SL_2(\ZZ)` stopped
|
|
1066
|
+
at the first time it reaches the subgroup after at least
|
|
1067
|
+
``initial_steps`` steps.
|
|
1068
|
+
|
|
1069
|
+
INPUT:
|
|
1070
|
+
|
|
1071
|
+
- ``initial_steps`` -- positive integer (default: 30)
|
|
1072
|
+
|
|
1073
|
+
EXAMPLES::
|
|
1074
|
+
|
|
1075
|
+
sage: G = ArithmeticSubgroup_Permutation(S2='(1,3)(4,5)',S3='(1,2,5)(3,4,6)')
|
|
1076
|
+
sage: all(G.random_element() in G for _ in range(10))
|
|
1077
|
+
True
|
|
1078
|
+
"""
|
|
1079
|
+
from sage.misc.prandom import randint
|
|
1080
|
+
|
|
1081
|
+
i = 0
|
|
1082
|
+
m = SL2Z(1)
|
|
1083
|
+
for _ in range(initial_steps):
|
|
1084
|
+
j = randint(0,1)
|
|
1085
|
+
if j == 0:
|
|
1086
|
+
i = self._S2[i]
|
|
1087
|
+
m = m*S2m
|
|
1088
|
+
else:
|
|
1089
|
+
i = self._S3[i]
|
|
1090
|
+
m = m*S3m
|
|
1091
|
+
|
|
1092
|
+
while i != 0:
|
|
1093
|
+
j = randint(0,1)
|
|
1094
|
+
if j == 0:
|
|
1095
|
+
i = self._S2[i]
|
|
1096
|
+
m = m*S2m
|
|
1097
|
+
else:
|
|
1098
|
+
i = self._S3[i]
|
|
1099
|
+
m = m*S3m
|
|
1100
|
+
|
|
1101
|
+
return m
|
|
1102
|
+
|
|
1103
|
+
def permutation_action(self, x):
|
|
1104
|
+
r"""
|
|
1105
|
+
Given an element ``x`` of `\SL_2(\ZZ)`, compute the permutation of the
|
|
1106
|
+
cosets of ``self`` given by right multiplication by ``x``.
|
|
1107
|
+
|
|
1108
|
+
EXAMPLES::
|
|
1109
|
+
|
|
1110
|
+
sage: import sage.modular.arithgroup.arithgroup_perm as ap
|
|
1111
|
+
sage: ap.HsuExample10().permutation_action(SL2Z([32, -21, -67, 44]))
|
|
1112
|
+
(1,4,6,2,10,5,3,7,8,9)
|
|
1113
|
+
"""
|
|
1114
|
+
return word_of_perms(sl2z_word_problem(x), self.L(), self.R())
|
|
1115
|
+
|
|
1116
|
+
#
|
|
1117
|
+
# Group stuff
|
|
1118
|
+
#
|
|
1119
|
+
|
|
1120
|
+
def is_normal(self) -> bool:
|
|
1121
|
+
r"""
|
|
1122
|
+
Test whether the group is normal.
|
|
1123
|
+
|
|
1124
|
+
EXAMPLES::
|
|
1125
|
+
|
|
1126
|
+
sage: G = Gamma(2).as_permutation_group()
|
|
1127
|
+
sage: G.is_normal()
|
|
1128
|
+
True
|
|
1129
|
+
|
|
1130
|
+
sage: G = Gamma1(2).as_permutation_group()
|
|
1131
|
+
sage: G.is_normal()
|
|
1132
|
+
False
|
|
1133
|
+
"""
|
|
1134
|
+
N = self.index()
|
|
1135
|
+
G = self.relabel(inplace=False)
|
|
1136
|
+
s2 = G._S2
|
|
1137
|
+
s3 = G._S3
|
|
1138
|
+
ss2 = [None] * N
|
|
1139
|
+
ss3 = [None] * N
|
|
1140
|
+
for j in [s2[0], s3[0]]:
|
|
1141
|
+
m = G._canonical_rooted_labels(j)
|
|
1142
|
+
for i in range(N):
|
|
1143
|
+
ss2[m[i]] = m[s2[i]]
|
|
1144
|
+
ss3[m[i]] = m[s3[i]]
|
|
1145
|
+
if s2 != ss2 or s3 != ss3:
|
|
1146
|
+
return False
|
|
1147
|
+
return True
|
|
1148
|
+
|
|
1149
|
+
def _conjugate(self, j0):
|
|
1150
|
+
r"""
|
|
1151
|
+
Return the conjugate of ``self`` rooted at j0.
|
|
1152
|
+
|
|
1153
|
+
EXAMPLES::
|
|
1154
|
+
|
|
1155
|
+
sage: G = ArithmeticSubgroup_Permutation(S2='(1,2)(3,4)',S3='(1,2,3)(4,5,6)')
|
|
1156
|
+
sage: G
|
|
1157
|
+
Arithmetic subgroup with permutations of right cosets
|
|
1158
|
+
S2=(1,2)(3,4)
|
|
1159
|
+
S3=(1,2,3)(4,5,6)
|
|
1160
|
+
L=(1,4,6,5,3)
|
|
1161
|
+
R=(2,4,5,6,3)
|
|
1162
|
+
sage: G._conjugate(0) == G
|
|
1163
|
+
True
|
|
1164
|
+
sage: G._conjugate(4)
|
|
1165
|
+
Arithmetic subgroup with permutations of right cosets
|
|
1166
|
+
S2=(3,4)(5,6)
|
|
1167
|
+
S3=(1,2,3)(4,5,6)
|
|
1168
|
+
L=(1,4,5,3,2)
|
|
1169
|
+
R=(1,2,4,6,3)
|
|
1170
|
+
"""
|
|
1171
|
+
N = self.index()
|
|
1172
|
+
s2 = self._S2
|
|
1173
|
+
s3 = self._S3
|
|
1174
|
+
l = self._L
|
|
1175
|
+
r = self._R
|
|
1176
|
+
ss2 = [None]*N
|
|
1177
|
+
ss3 = [None]*N
|
|
1178
|
+
ll = [None]*N
|
|
1179
|
+
rr = [None]*N
|
|
1180
|
+
|
|
1181
|
+
m = self._canonical_rooted_labels(j0)
|
|
1182
|
+
for i in range(N):
|
|
1183
|
+
ss2[m[i]] = m[s2[i]]
|
|
1184
|
+
ss3[m[i]] = m[s3[i]]
|
|
1185
|
+
ll[m[i]] = m[l[i]]
|
|
1186
|
+
rr[m[i]] = m[r[i]]
|
|
1187
|
+
return self.__class__(ss2,ss3,ll,rr,True)
|
|
1188
|
+
|
|
1189
|
+
def coset_graph(self,
|
|
1190
|
+
right_cosets=False,
|
|
1191
|
+
s2_edges=True, s3_edges=True, l_edges=False, r_edges=False,
|
|
1192
|
+
s2_label='s2', s3_label='s3', l_label='l', r_label='r'):
|
|
1193
|
+
r"""
|
|
1194
|
+
Return the right (or left) coset graph.
|
|
1195
|
+
|
|
1196
|
+
INPUT:
|
|
1197
|
+
|
|
1198
|
+
- ``right_cosets`` -- boolean (default: ``False``); right or left coset graph
|
|
1199
|
+
|
|
1200
|
+
- ``s2_edges`` -- boolean (default: ``True``); put edges associated to s2
|
|
1201
|
+
|
|
1202
|
+
- ``s3_edges`` -- boolean (default: ``True``); put edges associated to s3
|
|
1203
|
+
|
|
1204
|
+
- ``l_edges`` -- boolean (default: ``False``); put edges associated to l
|
|
1205
|
+
|
|
1206
|
+
- ``r_edges`` -- boolean (default: ``False``); put edges associated to r
|
|
1207
|
+
|
|
1208
|
+
- ``s2_label``, ``s3_label``, ``l_label``, ``r_label`` -- the labels to
|
|
1209
|
+
put on the edges corresponding to the generators action. Use ``None``
|
|
1210
|
+
for no label.
|
|
1211
|
+
|
|
1212
|
+
EXAMPLES::
|
|
1213
|
+
|
|
1214
|
+
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="()")
|
|
1215
|
+
sage: G
|
|
1216
|
+
Arithmetic subgroup with permutations of right cosets
|
|
1217
|
+
S2=(1,2)
|
|
1218
|
+
S3=()
|
|
1219
|
+
L=(1,2)
|
|
1220
|
+
R=(1,2)
|
|
1221
|
+
sage: G.index()
|
|
1222
|
+
2
|
|
1223
|
+
sage: G.coset_graph()
|
|
1224
|
+
Looped multi-digraph on 2 vertices
|
|
1225
|
+
"""
|
|
1226
|
+
from sage.graphs.digraph import DiGraph
|
|
1227
|
+
res = DiGraph(multiedges=True, loops=True)
|
|
1228
|
+
res.add_vertices(list(range(self.index())))
|
|
1229
|
+
|
|
1230
|
+
if right_cosets: # invert the permutations
|
|
1231
|
+
S2 = [None]*self.index()
|
|
1232
|
+
S3 = [None]*self.index()
|
|
1233
|
+
L = [None]*self.index()
|
|
1234
|
+
R = [None]*self.index()
|
|
1235
|
+
for i in range(self.index()):
|
|
1236
|
+
S2[self._S2[i]] = i
|
|
1237
|
+
S3[self._S3[i]] = i
|
|
1238
|
+
L[self._L[i]] = i
|
|
1239
|
+
R[self._R[i]] = i
|
|
1240
|
+
|
|
1241
|
+
else:
|
|
1242
|
+
S2 = self._S2
|
|
1243
|
+
S3 = self._S3
|
|
1244
|
+
L = self._L
|
|
1245
|
+
R = self._R
|
|
1246
|
+
|
|
1247
|
+
if s2_edges:
|
|
1248
|
+
if s2_label is not None:
|
|
1249
|
+
res.add_edges((i, S2[i], s2_label) for i in range(self.index()))
|
|
1250
|
+
else:
|
|
1251
|
+
res.add_edges((i, S2[i]) for i in range(self.index()))
|
|
1252
|
+
|
|
1253
|
+
if s3_edges:
|
|
1254
|
+
if s3_label is not None:
|
|
1255
|
+
res.add_edges((i, S3[i], s3_label) for i in range(self.index()))
|
|
1256
|
+
else:
|
|
1257
|
+
res.add_edges((i, S3) for i in range(self.index()))
|
|
1258
|
+
|
|
1259
|
+
if l_edges:
|
|
1260
|
+
if l_label is not None:
|
|
1261
|
+
res.add_edges((i,L[i],l_label) for i in range(self.index()))
|
|
1262
|
+
else:
|
|
1263
|
+
res.add_edges((i,L[i]) for i in range(self.index()))
|
|
1264
|
+
|
|
1265
|
+
if r_edges:
|
|
1266
|
+
if r_label is not None:
|
|
1267
|
+
res.add_edges((i,R[i],r_label) for i in range(self.index()))
|
|
1268
|
+
else:
|
|
1269
|
+
res.add_edges((i,R[i]) for i in range(self.index()))
|
|
1270
|
+
|
|
1271
|
+
res.plot.options['color_by_label'] = True
|
|
1272
|
+
|
|
1273
|
+
if s2_label or s3_label or l_label or r_label:
|
|
1274
|
+
res.plot.options['edge_labels'] = True
|
|
1275
|
+
|
|
1276
|
+
return res
|
|
1277
|
+
|
|
1278
|
+
def generalised_level(self):
|
|
1279
|
+
r"""
|
|
1280
|
+
Return the generalised level of this subgroup.
|
|
1281
|
+
|
|
1282
|
+
The *generalised level* of a subgroup of the modular group is the least
|
|
1283
|
+
common multiple of the widths of the cusps. It was proven by Wohlfart
|
|
1284
|
+
that for even congruence subgroups, the (conventional) level coincides
|
|
1285
|
+
with the generalised level. For odd congruence subgroups the level is
|
|
1286
|
+
either the generalised level, or twice the generalised level [KSV2011]_.
|
|
1287
|
+
|
|
1288
|
+
EXAMPLES::
|
|
1289
|
+
|
|
1290
|
+
sage: G = Gamma(2).as_permutation_group()
|
|
1291
|
+
sage: G.generalised_level()
|
|
1292
|
+
2
|
|
1293
|
+
sage: G = Gamma0(3).as_permutation_group()
|
|
1294
|
+
sage: G.generalised_level()
|
|
1295
|
+
3
|
|
1296
|
+
"""
|
|
1297
|
+
return lcm(self.cusp_widths())
|
|
1298
|
+
|
|
1299
|
+
def congruence_closure(self):
|
|
1300
|
+
r"""
|
|
1301
|
+
Return the smallest congruence subgroup containing ``self``. If ``self`` is
|
|
1302
|
+
congruence, this is just self, but represented as a congruence subgroup
|
|
1303
|
+
data type. If ``self`` is not congruence, then it may be larger.
|
|
1304
|
+
|
|
1305
|
+
In practice, we use the following criterion: let `m` be the generalised
|
|
1306
|
+
level of ``self``. If this subgroup is even, let `n = m`, else let `n =
|
|
1307
|
+
2m`. Then any congruence subgroup containing ``self`` contains `\Gamma(n)`
|
|
1308
|
+
(a generalisation of Wohlfahrt's theorem due to Kiming, Verrill and
|
|
1309
|
+
Schuett). So we compute the image of ``self`` modulo `n` and return the
|
|
1310
|
+
preimage of that.
|
|
1311
|
+
|
|
1312
|
+
.. NOTE::
|
|
1313
|
+
|
|
1314
|
+
If you just want to know if the subgroup is congruence or not, it
|
|
1315
|
+
is *much* faster to use :meth:`~is_congruence`.
|
|
1316
|
+
|
|
1317
|
+
EXAMPLES::
|
|
1318
|
+
|
|
1319
|
+
sage: Gamma1(3).as_permutation_group().congruence_closure()
|
|
1320
|
+
Congruence subgroup of SL(2,Z) of level 3, preimage of:
|
|
1321
|
+
Matrix group over Ring of integers modulo 3 with 2 generators (
|
|
1322
|
+
[1 1] [1 2]
|
|
1323
|
+
[0 1], [0 1]
|
|
1324
|
+
)
|
|
1325
|
+
sage: sage.modular.arithgroup.arithgroup_perm.HsuExample10().congruence_closure() # long time (11s on sage.math, 2012)
|
|
1326
|
+
Modular Group SL(2,Z)
|
|
1327
|
+
"""
|
|
1328
|
+
if self.is_even():
|
|
1329
|
+
N = self.generalised_level()
|
|
1330
|
+
else:
|
|
1331
|
+
N = 2*self.generalised_level()
|
|
1332
|
+
|
|
1333
|
+
from .congroup_generic import CongruenceSubgroup_constructor as CS
|
|
1334
|
+
return CS(N, [x.matrix() for x in self.gens()])
|
|
1335
|
+
|
|
1336
|
+
def is_congruence(self) -> bool:
|
|
1337
|
+
r"""
|
|
1338
|
+
Return ``True`` if this is a congruence subgroup, and ``False``
|
|
1339
|
+
otherwise.
|
|
1340
|
+
|
|
1341
|
+
ALGORITHM:
|
|
1342
|
+
|
|
1343
|
+
Uses Hsu's algorithm [Hsu1996]_. Adapted from Chris Kurth's
|
|
1344
|
+
implementation in KFarey [Kur2008]_.
|
|
1345
|
+
|
|
1346
|
+
For *odd* subgroups, Hsu's algorithm still works with minor
|
|
1347
|
+
modifications, using the extension of Wohlfarht's theorem due to
|
|
1348
|
+
Kiming, Schuett and Verrill [KSV2011]_. See [HL2014]_ for details.
|
|
1349
|
+
|
|
1350
|
+
The algorithm is as follows. Let `G` be a finite-index subgroup of
|
|
1351
|
+
`\SL(2, \ZZ)`, and let `L` and `R` be the permutations of the
|
|
1352
|
+
cosets of `G` given by the elements `\begin{pmatrix} 1 & 1 \\ 0 & 1
|
|
1353
|
+
\end{pmatrix}` and `\begin{pmatrix} 1 & 1 \\ 0 & 1 \end{pmatrix}`. Let
|
|
1354
|
+
`N` be the generalized level of `G` (if `G` is even) or twice the
|
|
1355
|
+
generalized level (if `G` is odd). Then:
|
|
1356
|
+
|
|
1357
|
+
- if `N` is odd, `G` is congruence if and only if the relation
|
|
1358
|
+
|
|
1359
|
+
.. MATH::
|
|
1360
|
+
|
|
1361
|
+
(L R^{-1} L)^2 = (R^2 L^{1/2})^3
|
|
1362
|
+
|
|
1363
|
+
holds, where `1/2` is understood as the multiplicative inverse of 2
|
|
1364
|
+
modulo N.
|
|
1365
|
+
|
|
1366
|
+
- if `N` is a power of 2, then `G` is congruence if and only
|
|
1367
|
+
if the relations
|
|
1368
|
+
|
|
1369
|
+
.. MATH::
|
|
1370
|
+
|
|
1371
|
+
\begin{array}{cc}
|
|
1372
|
+
(L R^{-1} L)^{-1} S (L R^{-1} L) S = 1 & (A1)\\
|
|
1373
|
+
S^{-1} R S = R^{25} & (A2)\\
|
|
1374
|
+
(L R^{-1} L)^2 = (S R^5 L R^{-1} L)^3 & (A3) \\
|
|
1375
|
+
\end{array}
|
|
1376
|
+
|
|
1377
|
+
hold, where `S = L^{20} R^{1/5} L^{-4} R^{-1}`, `1/5` being the inverse
|
|
1378
|
+
of 5 modulo N.
|
|
1379
|
+
|
|
1380
|
+
- if `N` is neither odd nor a power of 2, seven relations (B1-7) hold,
|
|
1381
|
+
for which see [HL2014]_, or the source code of this function.
|
|
1382
|
+
|
|
1383
|
+
If the Sage verbosity flag is set (using ``set_verbose()``), then extra
|
|
1384
|
+
output will be produced indicating which of the relations (A1-3) or
|
|
1385
|
+
(B1-7) is not satisfied.
|
|
1386
|
+
|
|
1387
|
+
EXAMPLES:
|
|
1388
|
+
|
|
1389
|
+
Test if `\SL_2(\ZZ)` is congruence::
|
|
1390
|
+
|
|
1391
|
+
sage: a = ArithmeticSubgroup_Permutation(L='',R='')
|
|
1392
|
+
sage: a.index()
|
|
1393
|
+
1
|
|
1394
|
+
sage: a.is_congruence()
|
|
1395
|
+
True
|
|
1396
|
+
|
|
1397
|
+
This example is congruence -- it is `\Gamma_0(3)` in disguise::
|
|
1398
|
+
|
|
1399
|
+
sage: S2 = SymmetricGroup(4)
|
|
1400
|
+
sage: l = S2((2,3,4))
|
|
1401
|
+
sage: r = S2((1,3,4))
|
|
1402
|
+
sage: G = ArithmeticSubgroup_Permutation(L=l,R=r)
|
|
1403
|
+
sage: G
|
|
1404
|
+
Arithmetic subgroup with permutations of right cosets
|
|
1405
|
+
S2=(1,2)(3,4)
|
|
1406
|
+
S3=(1,4,2)
|
|
1407
|
+
L=(2,3,4)
|
|
1408
|
+
R=(1,3,4)
|
|
1409
|
+
sage: G.is_congruence()
|
|
1410
|
+
True
|
|
1411
|
+
|
|
1412
|
+
This one is noncongruence::
|
|
1413
|
+
|
|
1414
|
+
sage: import sage.modular.arithgroup.arithgroup_perm as ap
|
|
1415
|
+
sage: ap.HsuExample10().is_congruence()
|
|
1416
|
+
False
|
|
1417
|
+
|
|
1418
|
+
The following example (taken from [KSV2011]_) shows that a lifting of a
|
|
1419
|
+
congruence subgroup of `\PSL(2,\ZZ)` to a subgroup of `\SL(2,
|
|
1420
|
+
\ZZ)` need not necessarily be congruence::
|
|
1421
|
+
|
|
1422
|
+
sage: S2 = "(1,3,13,15)(2,4,14,16)(5,7,17,19)(6,10,18,22)(8,12,20,24)(9,11,21,23)"
|
|
1423
|
+
sage: S3 = "(1,14,15,13,2,3)(4,5,6,16,17,18)(7,8,9,19,20,21)(10,11,12,22,23,24)"
|
|
1424
|
+
sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)
|
|
1425
|
+
sage: G.is_congruence()
|
|
1426
|
+
False
|
|
1427
|
+
sage: G.to_even_subgroup().is_congruence()
|
|
1428
|
+
True
|
|
1429
|
+
|
|
1430
|
+
In fact `G` is a lifting to `\SL(2,\ZZ)` of the group
|
|
1431
|
+
`\bar{\Gamma}_0(6)`::
|
|
1432
|
+
|
|
1433
|
+
sage: G.to_even_subgroup() == Gamma0(6)
|
|
1434
|
+
True
|
|
1435
|
+
"""
|
|
1436
|
+
from sage.misc.verbose import verbose
|
|
1437
|
+
if self.index() == 1: # the group is SL2Z (trivial case)
|
|
1438
|
+
return True
|
|
1439
|
+
|
|
1440
|
+
L = self.L() # action of L
|
|
1441
|
+
R = self.R() # action of R
|
|
1442
|
+
|
|
1443
|
+
if self.is_even():
|
|
1444
|
+
N = L.order() # generalised level of the group
|
|
1445
|
+
else:
|
|
1446
|
+
N = 2 * L.order()
|
|
1447
|
+
|
|
1448
|
+
# write N as N = em where e = 2^k and m odd
|
|
1449
|
+
m = N.odd_part()
|
|
1450
|
+
e = N // m
|
|
1451
|
+
|
|
1452
|
+
if e == 1:
|
|
1453
|
+
# N is odd
|
|
1454
|
+
# this only gets called if self is even
|
|
1455
|
+
onehalf = ZZ(2).inverse_mod(N) # i.e. 2^(-1) mod N
|
|
1456
|
+
rel = (R*R*L**(-onehalf))**3
|
|
1457
|
+
return rel.is_one()
|
|
1458
|
+
|
|
1459
|
+
elif m == 1:
|
|
1460
|
+
# N is a power of 2
|
|
1461
|
+
onefifth = ZZ(5).inverse_mod(N) # i.e. 5^(-1) mod N
|
|
1462
|
+
S = L**20*R**onefifth*L**(-4)*~R
|
|
1463
|
+
|
|
1464
|
+
# congruence if the three below permutations are trivial
|
|
1465
|
+
rel = (~L*R*~L) * S * (L*~R*L) * S
|
|
1466
|
+
if not rel.is_one():
|
|
1467
|
+
verbose("Failed relation A1")
|
|
1468
|
+
return False
|
|
1469
|
+
|
|
1470
|
+
rel = ~S*R*S*R**(-25)
|
|
1471
|
+
if not rel.is_one():
|
|
1472
|
+
verbose("Failed relation A2")
|
|
1473
|
+
return False
|
|
1474
|
+
|
|
1475
|
+
rel = (S*R**5*L*~R*L)**3 * ~(L * ~R * L)**2
|
|
1476
|
+
if not rel.is_one():
|
|
1477
|
+
verbose("Failed relation A3")
|
|
1478
|
+
return False
|
|
1479
|
+
|
|
1480
|
+
return True
|
|
1481
|
+
|
|
1482
|
+
else:
|
|
1483
|
+
# e>1, m>1
|
|
1484
|
+
onehalf = ZZ(2).inverse_mod(m) # i.e. 2^(-1) mod m
|
|
1485
|
+
onefifth = ZZ(5).inverse_mod(e) # i.e. 5^(-1) mod e
|
|
1486
|
+
c, d = CRT_basis([m, e])
|
|
1487
|
+
# c=0 mod e, c=1 mod m; d=1 mod e, d=0 mod m
|
|
1488
|
+
a = L**c
|
|
1489
|
+
b = R**c
|
|
1490
|
+
l = L**d
|
|
1491
|
+
r = R**d
|
|
1492
|
+
s = l**20 * r**onefifth * l**(-4) * ~r
|
|
1493
|
+
|
|
1494
|
+
# Congruence if the seven permutations below are trivial:
|
|
1495
|
+
rel = ~a*~r*a*r
|
|
1496
|
+
if not rel.is_one():
|
|
1497
|
+
verbose("Failed relation B1")
|
|
1498
|
+
return False
|
|
1499
|
+
|
|
1500
|
+
rel = (a*~b*a)**4
|
|
1501
|
+
if not rel.is_one():
|
|
1502
|
+
verbose("Failed relation B2")
|
|
1503
|
+
return False
|
|
1504
|
+
|
|
1505
|
+
rel = (a*~b*a)**2*(~a*b)**3
|
|
1506
|
+
if not rel.is_one():
|
|
1507
|
+
verbose("Failed relation B3")
|
|
1508
|
+
return False
|
|
1509
|
+
|
|
1510
|
+
rel = (a*~b*a)**2*(b*b*a**(-onehalf))**(-3)
|
|
1511
|
+
if not rel.is_one():
|
|
1512
|
+
verbose("Failed relation B4")
|
|
1513
|
+
return False
|
|
1514
|
+
|
|
1515
|
+
rel = (~l*r*~l)*s*(l*~r*l)*s
|
|
1516
|
+
if not rel.is_one():
|
|
1517
|
+
verbose("Failed relation B5")
|
|
1518
|
+
return False
|
|
1519
|
+
|
|
1520
|
+
rel = ~s*r*s*r**(-25)
|
|
1521
|
+
if not rel.is_one():
|
|
1522
|
+
verbose("Failed relation B6")
|
|
1523
|
+
return False
|
|
1524
|
+
|
|
1525
|
+
rel = (l*~r*l)**2*(s*r**5*l*~r*l)**(-3)
|
|
1526
|
+
if not rel.is_one():
|
|
1527
|
+
verbose("Failed relation B7")
|
|
1528
|
+
return False
|
|
1529
|
+
|
|
1530
|
+
return True
|
|
1531
|
+
|
|
1532
|
+
def surgroups(self):
|
|
1533
|
+
r"""
|
|
1534
|
+
Return an iterator through the non-trivial intermediate groups between
|
|
1535
|
+
`SL(2,\ZZ)` and this finite index group.
|
|
1536
|
+
|
|
1537
|
+
EXAMPLES::
|
|
1538
|
+
|
|
1539
|
+
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)(5,6)", S3="(1,2,3)(4,5,6)")
|
|
1540
|
+
sage: H = next(G.surgroups())
|
|
1541
|
+
sage: H
|
|
1542
|
+
Arithmetic subgroup with permutations of right cosets
|
|
1543
|
+
S2=(1,2)
|
|
1544
|
+
S3=(1,2,3)
|
|
1545
|
+
L=(1,3)
|
|
1546
|
+
R=(2,3)
|
|
1547
|
+
sage: G.is_subgroup(H)
|
|
1548
|
+
True
|
|
1549
|
+
|
|
1550
|
+
The principal congruence group `\Gamma(3)` has thirteen surgroups::
|
|
1551
|
+
|
|
1552
|
+
sage: G = Gamma(3).as_permutation_group()
|
|
1553
|
+
sage: G.index()
|
|
1554
|
+
24
|
|
1555
|
+
sage: l = []
|
|
1556
|
+
sage: for H in G.surgroups():
|
|
1557
|
+
....: l.append(H.index())
|
|
1558
|
+
....: assert G.is_subgroup(H) and H.is_congruence()
|
|
1559
|
+
sage: l
|
|
1560
|
+
[6, 3, 4, 8, 4, 8, 4, 12, 4, 6, 6, 8, 8]
|
|
1561
|
+
"""
|
|
1562
|
+
from sage.libs.gap.libgap import libgap
|
|
1563
|
+
P = libgap(self.perm_group())
|
|
1564
|
+
for b in P.AllBlocks():
|
|
1565
|
+
orbit = P.Orbit(b, libgap.OnSets)
|
|
1566
|
+
action = P.Action(orbit, libgap.OnSets)
|
|
1567
|
+
S2, S3, L, R = action.GeneratorsOfGroup()
|
|
1568
|
+
yield ArithmeticSubgroup_Permutation(S2=S2, S3=S3, L=L, R=R, check=False)
|
|
1569
|
+
|
|
1570
|
+
|
|
1571
|
+
class OddArithmeticSubgroup_Permutation(ArithmeticSubgroup_Permutation_class):
|
|
1572
|
+
r"""
|
|
1573
|
+
An arithmetic subgroup of `\SL(2, \ZZ)` not containing `-1`,
|
|
1574
|
+
represented in terms of the right action of `\SL(2, \ZZ)` on its
|
|
1575
|
+
cosets.
|
|
1576
|
+
|
|
1577
|
+
EXAMPLES::
|
|
1578
|
+
|
|
1579
|
+
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="(1,3)(2,4)")
|
|
1580
|
+
sage: G
|
|
1581
|
+
Arithmetic subgroup with permutations of right cosets
|
|
1582
|
+
S2=(1,2,3,4)
|
|
1583
|
+
S3=(1,3)(2,4)
|
|
1584
|
+
L=(1,2,3,4)
|
|
1585
|
+
R=(1,4,3,2)
|
|
1586
|
+
sage: type(G)
|
|
1587
|
+
<class 'sage.modular.arithgroup.arithgroup_perm.OddArithmeticSubgroup_Permutation_with_category'>
|
|
1588
|
+
"""
|
|
1589
|
+
|
|
1590
|
+
def __init__(self, S2, S3, L, R, canonical_labels=False):
|
|
1591
|
+
r"""
|
|
1592
|
+
TESTS::
|
|
1593
|
+
|
|
1594
|
+
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="(1,3)(2,4)")
|
|
1595
|
+
sage: G
|
|
1596
|
+
Arithmetic subgroup with permutations of right cosets
|
|
1597
|
+
S2=(1,2,3,4)
|
|
1598
|
+
S3=(1,3)(2,4)
|
|
1599
|
+
L=(1,2,3,4)
|
|
1600
|
+
R=(1,4,3,2)
|
|
1601
|
+
sage: TestSuite(G).run()
|
|
1602
|
+
"""
|
|
1603
|
+
self._S2 = S2
|
|
1604
|
+
self._S3 = S3
|
|
1605
|
+
self._L = L
|
|
1606
|
+
self._R = R
|
|
1607
|
+
if canonical_labels:
|
|
1608
|
+
self._canonical_label_group = self
|
|
1609
|
+
ArithmeticSubgroup_Permutation_class.__init__(self)
|
|
1610
|
+
|
|
1611
|
+
def __reduce__(self):
|
|
1612
|
+
r"""
|
|
1613
|
+
Return the data used to construct ``self``. Used in pickling.
|
|
1614
|
+
|
|
1615
|
+
TESTS::
|
|
1616
|
+
|
|
1617
|
+
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="(1,3)(2,4)")
|
|
1618
|
+
sage: G == loads(dumps(G)) #indirect doctest
|
|
1619
|
+
True
|
|
1620
|
+
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="(1,3)(2,4)",relabel=True)
|
|
1621
|
+
sage: GG = loads(dumps(G))
|
|
1622
|
+
sage: GG == G #indirect doctest
|
|
1623
|
+
True
|
|
1624
|
+
sage: GG.relabel(inplace=False) is GG
|
|
1625
|
+
True
|
|
1626
|
+
"""
|
|
1627
|
+
if hasattr(self,'_canonical_label_group'):
|
|
1628
|
+
canonical_labels = (self is self._canonical_label_group)
|
|
1629
|
+
else:
|
|
1630
|
+
canonical_labels = False
|
|
1631
|
+
return (OddArithmeticSubgroup_Permutation,
|
|
1632
|
+
(self._S2,self._S3,self._L,self._R,canonical_labels))
|
|
1633
|
+
|
|
1634
|
+
def is_odd(self) -> bool:
|
|
1635
|
+
r"""
|
|
1636
|
+
Test whether the group is odd.
|
|
1637
|
+
|
|
1638
|
+
EXAMPLES::
|
|
1639
|
+
|
|
1640
|
+
sage: G = ArithmeticSubgroup_Permutation(S2="(1,6,4,3)(2,7,5,8)",S3="(1,2,3,4,5,6)(7,8)")
|
|
1641
|
+
sage: G.is_odd()
|
|
1642
|
+
True
|
|
1643
|
+
"""
|
|
1644
|
+
return True
|
|
1645
|
+
|
|
1646
|
+
def is_even(self) -> bool:
|
|
1647
|
+
r"""
|
|
1648
|
+
Test whether the group is even.
|
|
1649
|
+
|
|
1650
|
+
EXAMPLES::
|
|
1651
|
+
|
|
1652
|
+
sage: G = ArithmeticSubgroup_Permutation(S2="(1,6,4,3)(2,7,5,8)",S3="(1,2,3,4,5,6)(7,8)")
|
|
1653
|
+
sage: G.is_even()
|
|
1654
|
+
False
|
|
1655
|
+
"""
|
|
1656
|
+
return False
|
|
1657
|
+
|
|
1658
|
+
def to_even_subgroup(self, relabel=True):
|
|
1659
|
+
r"""
|
|
1660
|
+
Return the group with `-Id` added in it.
|
|
1661
|
+
|
|
1662
|
+
EXAMPLES::
|
|
1663
|
+
|
|
1664
|
+
sage: G = Gamma1(3).as_permutation_group()
|
|
1665
|
+
sage: G.to_even_subgroup()
|
|
1666
|
+
Arithmetic subgroup with permutations of right cosets
|
|
1667
|
+
S2=(1,3)(2,4)
|
|
1668
|
+
S3=(1,2,3)
|
|
1669
|
+
L=(2,3,4)
|
|
1670
|
+
R=(1,4,2)
|
|
1671
|
+
|
|
1672
|
+
sage: H = ArithmeticSubgroup_Permutation(S2 = '(1,4,11,14)(2,7,12,17)(3,5,13,15)(6,9,16,19)(8,10,18,20)', S3 = '(1,2,3,11,12,13)(4,5,6,14,15,16)(7,8,9,17,18,19)(10,20)')
|
|
1673
|
+
sage: G = H.to_even_subgroup(relabel=False); G
|
|
1674
|
+
Arithmetic subgroup with permutations of right cosets
|
|
1675
|
+
S2=(1,4)(2,7)(3,5)(6,9)(8,10)
|
|
1676
|
+
S3=(1,2,3)(4,5,6)(7,8,9)
|
|
1677
|
+
L=(1,5)(2,4,9,10,8)(3,7,6)
|
|
1678
|
+
R=(1,7,10,8,6)(2,5,9)(3,4)
|
|
1679
|
+
sage: H.is_subgroup(G)
|
|
1680
|
+
True
|
|
1681
|
+
"""
|
|
1682
|
+
N = self.index()
|
|
1683
|
+
|
|
1684
|
+
# build equivalence classes in e
|
|
1685
|
+
s2 = self._S2
|
|
1686
|
+
e = []
|
|
1687
|
+
e2i = [None]*N
|
|
1688
|
+
for i in range(N):
|
|
1689
|
+
j = s2[s2[i]]
|
|
1690
|
+
if i < j:
|
|
1691
|
+
e2i[i] = e2i[j] = len(e)
|
|
1692
|
+
e.append((i,j))
|
|
1693
|
+
|
|
1694
|
+
# build the quotient permutations
|
|
1695
|
+
ss2 = [None]*(N//2)
|
|
1696
|
+
ss3 = [None]*(N//2)
|
|
1697
|
+
ll = [None]*(N//2)
|
|
1698
|
+
rr = [None]*(N//2)
|
|
1699
|
+
|
|
1700
|
+
s3 = self._S3
|
|
1701
|
+
l = self._L
|
|
1702
|
+
r = self._R
|
|
1703
|
+
for (j0,j1) in e:
|
|
1704
|
+
ss2[e2i[j0]] = e2i[s2[j0]]
|
|
1705
|
+
ss3[e2i[j0]] = e2i[s3[j0]]
|
|
1706
|
+
ll[e2i[j0]] = e2i[l[j0]]
|
|
1707
|
+
rr[e2i[j0]] = e2i[r[j0]]
|
|
1708
|
+
|
|
1709
|
+
G = EvenArithmeticSubgroup_Permutation(ss2,ss3,ll,rr)
|
|
1710
|
+
if relabel:
|
|
1711
|
+
G.relabel()
|
|
1712
|
+
return G
|
|
1713
|
+
|
|
1714
|
+
def nu2(self):
|
|
1715
|
+
r"""
|
|
1716
|
+
Return the number of elliptic points of order 2.
|
|
1717
|
+
|
|
1718
|
+
EXAMPLES::
|
|
1719
|
+
|
|
1720
|
+
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="(1,3)(2,4)")
|
|
1721
|
+
sage: G.nu2()
|
|
1722
|
+
0
|
|
1723
|
+
|
|
1724
|
+
sage: G = Gamma1(2).as_permutation_group()
|
|
1725
|
+
sage: G.nu2()
|
|
1726
|
+
1
|
|
1727
|
+
"""
|
|
1728
|
+
return sum(1 for c in self.S2().cycle_tuples() if len(c) == 2)
|
|
1729
|
+
|
|
1730
|
+
def nu3(self):
|
|
1731
|
+
r"""
|
|
1732
|
+
Return the number of elliptic points of order 3.
|
|
1733
|
+
|
|
1734
|
+
EXAMPLES::
|
|
1735
|
+
|
|
1736
|
+
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="(1,3)(2,4)")
|
|
1737
|
+
sage: G.nu3()
|
|
1738
|
+
2
|
|
1739
|
+
|
|
1740
|
+
sage: G = Gamma1(3).as_permutation_group()
|
|
1741
|
+
sage: G.nu3()
|
|
1742
|
+
1
|
|
1743
|
+
"""
|
|
1744
|
+
return sum(1 for c in self.S3().cycle_tuples() if len(c) == 2)
|
|
1745
|
+
|
|
1746
|
+
def nirregcusps(self):
|
|
1747
|
+
r"""
|
|
1748
|
+
Return the number of irregular cusps.
|
|
1749
|
+
|
|
1750
|
+
The cusps are associated to cycles of the permutations `L` or `R`.
|
|
1751
|
+
The irregular cusps are the one which are stabilised by `-Id`.
|
|
1752
|
+
|
|
1753
|
+
EXAMPLES::
|
|
1754
|
+
|
|
1755
|
+
sage: S2 = "(1,3,2,4)(5,7,6,8)(9,11,10,12)"
|
|
1756
|
+
sage: S3 = "(1,3,5,2,4,6)(7,9,11,8,10,12)"
|
|
1757
|
+
sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)
|
|
1758
|
+
sage: G.nirregcusps()
|
|
1759
|
+
3
|
|
1760
|
+
"""
|
|
1761
|
+
inv = self.S2()**2
|
|
1762
|
+
n = 0
|
|
1763
|
+
for c in self.L().cycle_tuples(singletons=True):
|
|
1764
|
+
if inv(c[0]) in c:
|
|
1765
|
+
n += 1
|
|
1766
|
+
return n
|
|
1767
|
+
|
|
1768
|
+
def nregcusps(self):
|
|
1769
|
+
r"""
|
|
1770
|
+
Return the number of regular cusps of the group.
|
|
1771
|
+
|
|
1772
|
+
The cusps are associated to cycles of `L` or `R`. The irregular cusps
|
|
1773
|
+
correspond to the ones which are not stabilised by `-Id`.
|
|
1774
|
+
|
|
1775
|
+
EXAMPLES::
|
|
1776
|
+
|
|
1777
|
+
sage: G = Gamma1(3).as_permutation_group()
|
|
1778
|
+
sage: G.nregcusps()
|
|
1779
|
+
2
|
|
1780
|
+
"""
|
|
1781
|
+
inv = self.S2()**2
|
|
1782
|
+
n = 0
|
|
1783
|
+
for c in self.L().cycle_tuples(singletons=True):
|
|
1784
|
+
if inv(c[0]) not in c:
|
|
1785
|
+
n += 1
|
|
1786
|
+
return n//2
|
|
1787
|
+
|
|
1788
|
+
def cusp_widths(self, exp=False):
|
|
1789
|
+
r"""
|
|
1790
|
+
Return the list of cusp widths.
|
|
1791
|
+
|
|
1792
|
+
INPUT:
|
|
1793
|
+
|
|
1794
|
+
- ``exp`` -- boolean (default: ``False``); if ``True``, return a
|
|
1795
|
+
dictionary with keys the possible widths and with values the number
|
|
1796
|
+
of cusp with that width
|
|
1797
|
+
|
|
1798
|
+
EXAMPLES::
|
|
1799
|
+
|
|
1800
|
+
sage: G = Gamma1(5).as_permutation_group()
|
|
1801
|
+
sage: G.cusp_widths()
|
|
1802
|
+
[1, 1, 5, 5]
|
|
1803
|
+
sage: G.cusp_widths(exp=True)
|
|
1804
|
+
{1: 2, 5: 2}
|
|
1805
|
+
"""
|
|
1806
|
+
inv = self.S2()**2
|
|
1807
|
+
L = self.L()
|
|
1808
|
+
cusps = {c[0] for c in L.cycle_tuples(singletons=True)}
|
|
1809
|
+
if exp:
|
|
1810
|
+
widths = {}
|
|
1811
|
+
else:
|
|
1812
|
+
widths = []
|
|
1813
|
+
|
|
1814
|
+
while cusps:
|
|
1815
|
+
c0 = cusps.pop()
|
|
1816
|
+
c = L.orbit(c0)
|
|
1817
|
+
if inv(c0) not in c:
|
|
1818
|
+
c1 = min(L.orbit(inv(c0)))
|
|
1819
|
+
cusps.remove(c1)
|
|
1820
|
+
if exp:
|
|
1821
|
+
if len(c) not in widths:
|
|
1822
|
+
widths[len(c)] = 0
|
|
1823
|
+
widths[len(c)] += 1
|
|
1824
|
+
else:
|
|
1825
|
+
widths.append(len(c))
|
|
1826
|
+
else:
|
|
1827
|
+
c2 = len(c) // 2
|
|
1828
|
+
if exp:
|
|
1829
|
+
if c2 not in widths:
|
|
1830
|
+
widths[c2] = 0
|
|
1831
|
+
widths[c2] += 1
|
|
1832
|
+
else:
|
|
1833
|
+
widths.append(c2)
|
|
1834
|
+
|
|
1835
|
+
if exp:
|
|
1836
|
+
return widths
|
|
1837
|
+
return sorted(widths)
|
|
1838
|
+
|
|
1839
|
+
def ncusps(self):
|
|
1840
|
+
r"""
|
|
1841
|
+
Return the number of cusps.
|
|
1842
|
+
|
|
1843
|
+
EXAMPLES::
|
|
1844
|
+
|
|
1845
|
+
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="(1,3)(2,4)")
|
|
1846
|
+
sage: G.ncusps()
|
|
1847
|
+
1
|
|
1848
|
+
|
|
1849
|
+
sage: G = Gamma1(3).as_permutation_group()
|
|
1850
|
+
sage: G.ncusps()
|
|
1851
|
+
2
|
|
1852
|
+
"""
|
|
1853
|
+
inv = self.S2()**2
|
|
1854
|
+
n = 0
|
|
1855
|
+
m = 0
|
|
1856
|
+
for c in self.L().cycle_tuples(singletons=True):
|
|
1857
|
+
if inv(c[0]) in c:
|
|
1858
|
+
n += 1
|
|
1859
|
+
else:
|
|
1860
|
+
m += 1
|
|
1861
|
+
return n + m//2
|
|
1862
|
+
|
|
1863
|
+
|
|
1864
|
+
class EvenArithmeticSubgroup_Permutation(ArithmeticSubgroup_Permutation_class):
|
|
1865
|
+
r"""
|
|
1866
|
+
An arithmetic subgroup of `\SL(2, \ZZ)` containing `-1`, represented
|
|
1867
|
+
in terms of the right action of `\SL(2, \ZZ)` on its cosets.
|
|
1868
|
+
|
|
1869
|
+
EXAMPLES:
|
|
1870
|
+
|
|
1871
|
+
Construct a noncongruence subgroup of index 7 (the smallest possible)::
|
|
1872
|
+
|
|
1873
|
+
sage: a2 = SymmetricGroup(7)([(1,2),(3,4),(6,7)]); a3 = SymmetricGroup(7)([(1,2,3),(4,5,6)])
|
|
1874
|
+
sage: G = ArithmeticSubgroup_Permutation(S2=a2, S3=a3); G
|
|
1875
|
+
Arithmetic subgroup with permutations of right cosets
|
|
1876
|
+
S2=(1,2)(3,4)(6,7)
|
|
1877
|
+
S3=(1,2,3)(4,5,6)
|
|
1878
|
+
L=(1,4,7,6,5,3)
|
|
1879
|
+
R=(2,4,5,7,6,3)
|
|
1880
|
+
sage: G.index()
|
|
1881
|
+
7
|
|
1882
|
+
sage: G.dimension_cusp_forms(4)
|
|
1883
|
+
1
|
|
1884
|
+
sage: G.is_congruence()
|
|
1885
|
+
False
|
|
1886
|
+
|
|
1887
|
+
Convert some standard congruence subgroups into permutation form::
|
|
1888
|
+
|
|
1889
|
+
sage: G = Gamma0(8).as_permutation_group()
|
|
1890
|
+
sage: G.index()
|
|
1891
|
+
12
|
|
1892
|
+
sage: G.is_congruence()
|
|
1893
|
+
True
|
|
1894
|
+
|
|
1895
|
+
sage: G = Gamma0(12).as_permutation_group()
|
|
1896
|
+
sage: G
|
|
1897
|
+
Arithmetic subgroup of index 24
|
|
1898
|
+
sage: G.is_congruence()
|
|
1899
|
+
True
|
|
1900
|
+
|
|
1901
|
+
The following is the unique index 2 even subgroup of `\SL_2(\ZZ)`::
|
|
1902
|
+
|
|
1903
|
+
sage: w = SymmetricGroup(2)([2,1])
|
|
1904
|
+
sage: G = ArithmeticSubgroup_Permutation(L=w, R=w)
|
|
1905
|
+
sage: G.dimension_cusp_forms(6)
|
|
1906
|
+
1
|
|
1907
|
+
sage: G.genus()
|
|
1908
|
+
0
|
|
1909
|
+
"""
|
|
1910
|
+
def __init__(self, S2, S3, L, R, canonical_labels=False):
|
|
1911
|
+
r"""
|
|
1912
|
+
TESTS::
|
|
1913
|
+
|
|
1914
|
+
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)(5,6)",S3="(1,2,3)(4,5,6)")
|
|
1915
|
+
sage: G == loads(dumps(G))
|
|
1916
|
+
True
|
|
1917
|
+
sage: G is loads(dumps(G))
|
|
1918
|
+
False
|
|
1919
|
+
"""
|
|
1920
|
+
self._S2 = S2
|
|
1921
|
+
self._S3 = S3
|
|
1922
|
+
self._L = L
|
|
1923
|
+
self._R = R
|
|
1924
|
+
if canonical_labels:
|
|
1925
|
+
self._canonical_label_group = self
|
|
1926
|
+
ArithmeticSubgroup_Permutation_class.__init__(self)
|
|
1927
|
+
|
|
1928
|
+
def __reduce__(self):
|
|
1929
|
+
r"""
|
|
1930
|
+
Data for pickling.
|
|
1931
|
+
|
|
1932
|
+
TESTS::
|
|
1933
|
+
|
|
1934
|
+
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)",S3="(1,2,4)")
|
|
1935
|
+
sage: G == loads(dumps(G)) #indirect doctest
|
|
1936
|
+
True
|
|
1937
|
+
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)",S3="(1,2,4)",relabel=True)
|
|
1938
|
+
sage: GG = loads(dumps(G))
|
|
1939
|
+
sage: G == GG #indirect doctest
|
|
1940
|
+
True
|
|
1941
|
+
sage: GG.relabel(inplace=False) is GG
|
|
1942
|
+
True
|
|
1943
|
+
"""
|
|
1944
|
+
if hasattr(self, '_canonical_label_group'):
|
|
1945
|
+
canonical_labels = (self is self._canonical_label_group)
|
|
1946
|
+
else:
|
|
1947
|
+
canonical_labels = False
|
|
1948
|
+
return (EvenArithmeticSubgroup_Permutation,
|
|
1949
|
+
(self._S2, self._S3, self._L, self._R, canonical_labels))
|
|
1950
|
+
|
|
1951
|
+
def is_odd(self) -> bool:
|
|
1952
|
+
r"""
|
|
1953
|
+
Return ``True`` if this subgroup does not contain the matrix `-Id`.
|
|
1954
|
+
|
|
1955
|
+
EXAMPLES::
|
|
1956
|
+
|
|
1957
|
+
sage: G = Gamma(2).as_permutation_group()
|
|
1958
|
+
sage: G.is_odd()
|
|
1959
|
+
False
|
|
1960
|
+
"""
|
|
1961
|
+
return False
|
|
1962
|
+
|
|
1963
|
+
def is_even(self) -> bool:
|
|
1964
|
+
r"""
|
|
1965
|
+
Return ``True`` if this subgroup contains the matrix `-Id`.
|
|
1966
|
+
|
|
1967
|
+
EXAMPLES::
|
|
1968
|
+
|
|
1969
|
+
sage: G = Gamma(2).as_permutation_group()
|
|
1970
|
+
sage: G.is_even()
|
|
1971
|
+
True
|
|
1972
|
+
"""
|
|
1973
|
+
return True
|
|
1974
|
+
|
|
1975
|
+
def nu2(self):
|
|
1976
|
+
r"""
|
|
1977
|
+
Return the number of orbits of elliptic points of order 2 for this
|
|
1978
|
+
arithmetic subgroup.
|
|
1979
|
+
|
|
1980
|
+
EXAMPLES::
|
|
1981
|
+
|
|
1982
|
+
sage: G = ArithmeticSubgroup_Permutation(S2="(1,4)(2)(3)",S3="(1,2,3)(4)")
|
|
1983
|
+
sage: G.nu2()
|
|
1984
|
+
2
|
|
1985
|
+
"""
|
|
1986
|
+
return sum(1 for i in range(self.index()) if self._S2[i] == i)
|
|
1987
|
+
|
|
1988
|
+
def nu3(self):
|
|
1989
|
+
r"""
|
|
1990
|
+
Return the number of orbits of elliptic points of order 3 for this
|
|
1991
|
+
arithmetic subgroup.
|
|
1992
|
+
|
|
1993
|
+
EXAMPLES::
|
|
1994
|
+
|
|
1995
|
+
sage: G = ArithmeticSubgroup_Permutation(S2="(1,4)(2)(3)",S3="(1,2,3)(4)")
|
|
1996
|
+
sage: G.nu3()
|
|
1997
|
+
1
|
|
1998
|
+
"""
|
|
1999
|
+
return sum(1 for i in range(self.index()) if self._S3[i] == i)
|
|
2000
|
+
|
|
2001
|
+
def ncusps(self):
|
|
2002
|
+
r"""
|
|
2003
|
+
Return the number of cusps of this arithmetic subgroup.
|
|
2004
|
+
|
|
2005
|
+
EXAMPLES::
|
|
2006
|
+
|
|
2007
|
+
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)(5,6)",S3="(1,2,3)(4,5,6)")
|
|
2008
|
+
sage: G.ncusps()
|
|
2009
|
+
3
|
|
2010
|
+
"""
|
|
2011
|
+
return len(self.L().cycle_tuples(True))
|
|
2012
|
+
|
|
2013
|
+
def _spanning_tree_kulkarni(self, root=0, on_right=True):
|
|
2014
|
+
r"""
|
|
2015
|
+
Return a spanning tree for the coset graph of the group for the
|
|
2016
|
+
generators `S2` and `S3`.
|
|
2017
|
+
|
|
2018
|
+
.. WARNING::
|
|
2019
|
+
|
|
2020
|
+
The output is randomized in order to be able to obtain any
|
|
2021
|
+
spanning tree of the coset graph. The algorithm mainly follows
|
|
2022
|
+
Kulkarni's paper.
|
|
2023
|
+
|
|
2024
|
+
INPUT:
|
|
2025
|
+
|
|
2026
|
+
- ``on_right`` -- boolean (default: ``True``); if ``False``,
|
|
2027
|
+
return spanning tree for the left cosets
|
|
2028
|
+
|
|
2029
|
+
OUTPUT:
|
|
2030
|
+
|
|
2031
|
+
- ``tree`` -- a spanning tree (with an embedding) of the graph
|
|
2032
|
+
associated to the action of ``S2`` and ``S3`` on the cosets
|
|
2033
|
+
|
|
2034
|
+
- ``reps`` -- list of matrices in `\SL_2(\ZZ)`; representatives
|
|
2035
|
+
of the cosets with respect to the spanning tree
|
|
2036
|
+
|
|
2037
|
+
- ``word_reps`` -- list of lists with ``s2`` and ``s3``; word
|
|
2038
|
+
representatives of the cosets with respect to the spanning tree
|
|
2039
|
+
|
|
2040
|
+
- ``gens`` -- list of 3-tuples ``(in,out,label)``; the list of edges in
|
|
2041
|
+
the graph which are not in the spanning tree
|
|
2042
|
+
|
|
2043
|
+
EXAMPLES::
|
|
2044
|
+
|
|
2045
|
+
sage: G = ArithmeticSubgroup_Permutation(S2='(1,2)(3,4)',S3='(1,2,3)')
|
|
2046
|
+
sage: tree,reps,wreps,gens = G._spanning_tree_kulkarni()
|
|
2047
|
+
sage: tree
|
|
2048
|
+
Digraph on 4 vertices
|
|
2049
|
+
sage: emb = tree.get_embedding()
|
|
2050
|
+
|
|
2051
|
+
sage: ascii_art(reps)
|
|
2052
|
+
[ [1 0] [ 0 1] [-1 1] [1 1] ]
|
|
2053
|
+
[ [0 1], [-1 1], [-1 0], [0 1] ]
|
|
2054
|
+
|
|
2055
|
+
sage: for w in wreps: print(','.join(w))
|
|
2056
|
+
s3
|
|
2057
|
+
s3,s3
|
|
2058
|
+
s3,s3,s2
|
|
2059
|
+
|
|
2060
|
+
sage: edges = G.coset_graph().edges(sort=True); edges
|
|
2061
|
+
[(0, 1, 's2'), (0, 1, 's3'), (1, 0, 's2'), (1, 2, 's3'), (2, 0, 's3'), (2, 3, 's2'), (3, 2, 's2'), (3, 3, 's3')]
|
|
2062
|
+
sage: len(gens)
|
|
2063
|
+
2
|
|
2064
|
+
sage: (3, 3, 's3') in gens
|
|
2065
|
+
True
|
|
2066
|
+
sage: any(e in gens for e in edges[:5])
|
|
2067
|
+
True
|
|
2068
|
+
"""
|
|
2069
|
+
from sage.graphs.digraph import DiGraph
|
|
2070
|
+
from sage.misc.prandom import randint
|
|
2071
|
+
|
|
2072
|
+
N = len(self._S2)
|
|
2073
|
+
|
|
2074
|
+
if on_right:
|
|
2075
|
+
s2 = self._S2
|
|
2076
|
+
s3 = self._S3
|
|
2077
|
+
|
|
2078
|
+
else:
|
|
2079
|
+
s2 = [None] * N
|
|
2080
|
+
s3 = [None] * N
|
|
2081
|
+
for i in range(N):
|
|
2082
|
+
s2[self._S2[i]] = i
|
|
2083
|
+
s3[self._S3[i]] = i
|
|
2084
|
+
|
|
2085
|
+
# the tree and the lift
|
|
2086
|
+
tree = DiGraph(multiedges=False, loops=False)
|
|
2087
|
+
gens = []
|
|
2088
|
+
|
|
2089
|
+
reps = [None] * self.index()
|
|
2090
|
+
word_reps = [None] * self.index()
|
|
2091
|
+
reps[root] = SL2Z.one()
|
|
2092
|
+
word_reps[root] = []
|
|
2093
|
+
|
|
2094
|
+
x0 = root
|
|
2095
|
+
tree.add_vertex(x0)
|
|
2096
|
+
l = [x0]
|
|
2097
|
+
orientation = {x0: []}
|
|
2098
|
+
while True:
|
|
2099
|
+
# complete the current 3-loop in the tree
|
|
2100
|
+
if s3[x0] != x0: # loop of length 3
|
|
2101
|
+
x1 = s3[x0]
|
|
2102
|
+
x2 = s3[x1]
|
|
2103
|
+
orientation[x0].append(x1)
|
|
2104
|
+
orientation[x1] = [x0, x2]
|
|
2105
|
+
orientation[x2] = [x1]
|
|
2106
|
+
tree.add_edge(x0, x1, 's3')
|
|
2107
|
+
tree.add_edge(x1, x2, 's3')
|
|
2108
|
+
if on_right:
|
|
2109
|
+
reps[x1] = reps[x0] * S3m
|
|
2110
|
+
reps[x2] = reps[x1] * S3m
|
|
2111
|
+
word_reps[x1] = word_reps[x0] + ['s3']
|
|
2112
|
+
word_reps[x2] = word_reps[x1] + ['s3']
|
|
2113
|
+
else:
|
|
2114
|
+
reps[x1] = S3m * reps[x0]
|
|
2115
|
+
reps[x2] = S3m * reps[x1]
|
|
2116
|
+
word_reps[x1] = ['s3'] + word_reps[x0]
|
|
2117
|
+
word_reps[x2] = ['s3'] + word_reps[x1]
|
|
2118
|
+
l.append(x1)
|
|
2119
|
+
l.append(x2)
|
|
2120
|
+
else: # elliptic generator
|
|
2121
|
+
gens.append((x0, x0, 's3'))
|
|
2122
|
+
|
|
2123
|
+
# now perform links with s while we find another guy
|
|
2124
|
+
while l:
|
|
2125
|
+
x1 = l.pop(randint(0, len(l) - 1))
|
|
2126
|
+
x0 = s2[x1]
|
|
2127
|
+
|
|
2128
|
+
if x1 != x0: # loop of length 2
|
|
2129
|
+
if x0 in tree:
|
|
2130
|
+
gens.append((x1, x0, 's2'))
|
|
2131
|
+
del l[l.index(x0)] # x0 must be in l
|
|
2132
|
+
else:
|
|
2133
|
+
orientation[x1].append(x0)
|
|
2134
|
+
orientation[x0] = [x1]
|
|
2135
|
+
tree.add_edge(x1, x0, 's2')
|
|
2136
|
+
if on_right:
|
|
2137
|
+
reps[x0] = reps[x1] * S2m
|
|
2138
|
+
word_reps[x0] = word_reps[x1] + ['s2']
|
|
2139
|
+
else:
|
|
2140
|
+
reps[x0] = S2m * reps[x1]
|
|
2141
|
+
word_reps[x0] = ['s2'] + word_reps[x1]
|
|
2142
|
+
break
|
|
2143
|
+
else: # elliptic generator
|
|
2144
|
+
gens.append((x1, x1, 's2'))
|
|
2145
|
+
|
|
2146
|
+
else:
|
|
2147
|
+
break
|
|
2148
|
+
|
|
2149
|
+
tree.set_embedding(orientation)
|
|
2150
|
+
return tree, reps, word_reps, gens
|
|
2151
|
+
|
|
2152
|
+
def _spanning_tree_verrill(self, root=0, on_right=True):
|
|
2153
|
+
r"""
|
|
2154
|
+
Return a spanning tree with generators `S2` and `L`.
|
|
2155
|
+
|
|
2156
|
+
The algorithm follows the one of Helena Verrill.
|
|
2157
|
+
|
|
2158
|
+
OUTPUT:
|
|
2159
|
+
|
|
2160
|
+
- ``tree`` -- a spanning tree of the graph associated to the action of
|
|
2161
|
+
``L`` and ``S2`` on the cosets
|
|
2162
|
+
|
|
2163
|
+
- ``reps`` -- list of matrices in `\SL_2(\ZZ)`; representatives of the
|
|
2164
|
+
cosets with respect to the spanning tree
|
|
2165
|
+
|
|
2166
|
+
- ``word_reps`` -- list of string with ``s`` and ``l`` -- word
|
|
2167
|
+
representatives of the cosets with respect to the spanning tree
|
|
2168
|
+
|
|
2169
|
+
- ``gens`` -- list of 3-tuples ``(in,out,label)``; the list of edges in
|
|
2170
|
+
the graph which are not in the spanning tree
|
|
2171
|
+
|
|
2172
|
+
EXAMPLES::
|
|
2173
|
+
|
|
2174
|
+
sage: G = ArithmeticSubgroup_Permutation(S2='(1,2)(3,4)',S3='(1,2,3)')
|
|
2175
|
+
sage: tree,reps,wreps,gens=G._spanning_tree_verrill()
|
|
2176
|
+
sage: tree
|
|
2177
|
+
Digraph on 4 vertices
|
|
2178
|
+
sage: for m in reps:
|
|
2179
|
+
....: print(m)
|
|
2180
|
+
....: print("\n****")
|
|
2181
|
+
[1 0]
|
|
2182
|
+
[0 1]
|
|
2183
|
+
****
|
|
2184
|
+
[ 0 -1]
|
|
2185
|
+
[ 1 0]
|
|
2186
|
+
****
|
|
2187
|
+
[1 2]
|
|
2188
|
+
[0 1]
|
|
2189
|
+
****
|
|
2190
|
+
[1 1]
|
|
2191
|
+
[0 1]
|
|
2192
|
+
****
|
|
2193
|
+
sage: wreps
|
|
2194
|
+
['', 's', 'll', 'l']
|
|
2195
|
+
sage: len(gens)
|
|
2196
|
+
3
|
|
2197
|
+
sage: (1, 1, 'l') in gens
|
|
2198
|
+
True
|
|
2199
|
+
sage: (2, 3, 's') in gens or (3, 2, 's') in gens
|
|
2200
|
+
True
|
|
2201
|
+
sage: (2, 0, 'l') in gens
|
|
2202
|
+
True
|
|
2203
|
+
|
|
2204
|
+
.. TODO::
|
|
2205
|
+
|
|
2206
|
+
Take care of the shape of the spanning tree as in Helena
|
|
2207
|
+
Verrill's program.
|
|
2208
|
+
"""
|
|
2209
|
+
from sage.misc.prandom import randint
|
|
2210
|
+
|
|
2211
|
+
if on_right:
|
|
2212
|
+
s = self._S2
|
|
2213
|
+
l = self._L
|
|
2214
|
+
else:
|
|
2215
|
+
s = [None]*self.index()
|
|
2216
|
+
l = [None]*self.index()
|
|
2217
|
+
for i in range(self.index()):
|
|
2218
|
+
s[self._S2[i]] = i
|
|
2219
|
+
l[self._L[i]] = i
|
|
2220
|
+
|
|
2221
|
+
from sage.graphs.digraph import DiGraph
|
|
2222
|
+
tree = DiGraph(multiedges=False,loops=False)
|
|
2223
|
+
gens = []
|
|
2224
|
+
|
|
2225
|
+
reps = [None]*self.index()
|
|
2226
|
+
word_reps = [None]*self.index()
|
|
2227
|
+
reps[root] = SL2Z(1)
|
|
2228
|
+
word_reps[root] = ''
|
|
2229
|
+
|
|
2230
|
+
x0 = root
|
|
2231
|
+
tree.add_vertex(x0)
|
|
2232
|
+
waiting = [x0]
|
|
2233
|
+
|
|
2234
|
+
while True:
|
|
2235
|
+
# complete the current l-loop in the tree from x0
|
|
2236
|
+
x = x0
|
|
2237
|
+
xx = l[x]
|
|
2238
|
+
while xx != x0:
|
|
2239
|
+
tree.add_edge(x,xx,'l')
|
|
2240
|
+
if on_right:
|
|
2241
|
+
reps[xx] = reps[x] * Lm
|
|
2242
|
+
word_reps[xx] = word_reps[x] + 'l'
|
|
2243
|
+
else:
|
|
2244
|
+
reps[xx] = Lm * reps[x]
|
|
2245
|
+
word_reps[xx] = 'l' + word_reps[x]
|
|
2246
|
+
waiting.append(xx)
|
|
2247
|
+
x = xx
|
|
2248
|
+
xx = l[x]
|
|
2249
|
+
|
|
2250
|
+
gens.append((x,x0,'l'))
|
|
2251
|
+
|
|
2252
|
+
# now perform links with s while we find another guy which will
|
|
2253
|
+
# become the new x0
|
|
2254
|
+
while waiting:
|
|
2255
|
+
x0 = None
|
|
2256
|
+
while waiting and x0 is None:
|
|
2257
|
+
x1 = waiting.pop(randint(0,len(waiting)-1))
|
|
2258
|
+
x0 = s[x1]
|
|
2259
|
+
|
|
2260
|
+
if x0 is not None:
|
|
2261
|
+
if x1 != x0: # loop of length 2
|
|
2262
|
+
if x0 in tree:
|
|
2263
|
+
gens.append((x1,x0,'s'))
|
|
2264
|
+
if x0 in waiting:
|
|
2265
|
+
del waiting[waiting.index(x0)] # x0 must be in l
|
|
2266
|
+
else:
|
|
2267
|
+
tree.add_edge(x1,x0,'s')
|
|
2268
|
+
if on_right:
|
|
2269
|
+
reps[x0] = reps[x1] * S2m
|
|
2270
|
+
word_reps[x0] = word_reps[x1] + 's'
|
|
2271
|
+
else:
|
|
2272
|
+
reps[x0] = S2m * reps[x1]
|
|
2273
|
+
word_reps[x0] = 's' + word_reps[x1]
|
|
2274
|
+
break
|
|
2275
|
+
else: # elliptic generator
|
|
2276
|
+
gens.append((x1,x1,'s'))
|
|
2277
|
+
|
|
2278
|
+
else:
|
|
2279
|
+
break
|
|
2280
|
+
|
|
2281
|
+
return tree, reps, word_reps,gens
|
|
2282
|
+
|
|
2283
|
+
def todd_coxeter_s2_s3(self):
|
|
2284
|
+
r"""
|
|
2285
|
+
Return a 4-tuple ``(coset_reps, gens, s2, s3)`` where ``coset_reps``
|
|
2286
|
+
are coset representatives of the subgroup, ``gens`` is a list of
|
|
2287
|
+
generators, ``s2`` and ``s3`` are the action of the matrices `S2` and
|
|
2288
|
+
`S3` on the list of cosets.
|
|
2289
|
+
|
|
2290
|
+
The so called *Todd-Coxeter algorithm* is a general method for coset
|
|
2291
|
+
enumeration for a subgroup of a group given by generators and relations.
|
|
2292
|
+
|
|
2293
|
+
EXAMPLES::
|
|
2294
|
+
|
|
2295
|
+
sage: G = ArithmeticSubgroup_Permutation(S2='(1,2)(3,4)',S3='(1,2,3)')
|
|
2296
|
+
sage: G.genus()
|
|
2297
|
+
0
|
|
2298
|
+
sage: reps,gens,s2,s3=G.todd_coxeter_s2_s3()
|
|
2299
|
+
sage: g1,g2 = gens
|
|
2300
|
+
sage: g1 in G and g2 in G
|
|
2301
|
+
True
|
|
2302
|
+
sage: Matrix(2, 2, [-1, 3, -1, 2]) in gens
|
|
2303
|
+
True
|
|
2304
|
+
sage: Matrix(2, 2, [-1, 0, 1, -1]) in gens or Matrix(2, 2, [1, 0, 1, 1]) in gens
|
|
2305
|
+
True
|
|
2306
|
+
sage: S2 = SL2Z([0,-1,1,0])
|
|
2307
|
+
sage: S3 = SL2Z([0,1,-1,1])
|
|
2308
|
+
sage: reps[0] == SL2Z([1,0,0,1])
|
|
2309
|
+
True
|
|
2310
|
+
sage: all(reps[i]*S2*~reps[s2[i]] in G for i in range(4))
|
|
2311
|
+
True
|
|
2312
|
+
sage: all(reps[i]*S3*~reps[s3[i]] in G for i in range(4))
|
|
2313
|
+
True
|
|
2314
|
+
"""
|
|
2315
|
+
tree,reps,wreps,edges = self._spanning_tree_kulkarni()
|
|
2316
|
+
|
|
2317
|
+
gens = []
|
|
2318
|
+
for e in edges:
|
|
2319
|
+
if e[2] == 's2':
|
|
2320
|
+
gens.append(self(reps[e[0]] * S2m * ~reps[e[1]]))
|
|
2321
|
+
elif e[2] == 's3':
|
|
2322
|
+
gens.append(self(reps[e[0]] * S3m * ~reps[e[1]]))
|
|
2323
|
+
else:
|
|
2324
|
+
raise ValueError("this should not happen")
|
|
2325
|
+
|
|
2326
|
+
return reps, gens, self._S2[:], self._S3[:]
|
|
2327
|
+
|
|
2328
|
+
def todd_coxeter_l_s2(self):
|
|
2329
|
+
r"""
|
|
2330
|
+
Return a 4-tuple ``(coset_reps, gens, l, s2)`` where ``coset_reps`` is
|
|
2331
|
+
a list of coset representatives of the subgroup, ``gens`` a list of
|
|
2332
|
+
generators, ``l`` and ``s2`` are list that corresponds to the action of
|
|
2333
|
+
the matrix `S2` and `L` on the cosets.
|
|
2334
|
+
|
|
2335
|
+
EXAMPLES::
|
|
2336
|
+
|
|
2337
|
+
sage: G = ArithmeticSubgroup_Permutation(S2='(1,2)(3,4)',S3='(1,2,3)')
|
|
2338
|
+
sage: reps,gens,l,s=G.todd_coxeter_l_s2()
|
|
2339
|
+
sage: reps
|
|
2340
|
+
[
|
|
2341
|
+
[1 0] [ 0 -1] [1 2] [1 1]
|
|
2342
|
+
[0 1], [ 1 0], [0 1], [0 1]
|
|
2343
|
+
]
|
|
2344
|
+
sage: len(gens)
|
|
2345
|
+
3
|
|
2346
|
+
sage: Matrix(2, 2, [1, 3, 0, 1]) in gens
|
|
2347
|
+
True
|
|
2348
|
+
sage: Matrix(2, 2, [1, 0, -1, 1]) in gens
|
|
2349
|
+
True
|
|
2350
|
+
sage: Matrix(2, 2, [1, -3, 1, -2]) in gens or Matrix(2, 2, [2, -3, 1, -1]) in gens
|
|
2351
|
+
True
|
|
2352
|
+
sage: l
|
|
2353
|
+
[3, 1, 0, 2]
|
|
2354
|
+
sage: s
|
|
2355
|
+
[1, 0, 3, 2]
|
|
2356
|
+
sage: S2 = SL2Z([0,-1,1,0])
|
|
2357
|
+
sage: L = SL2Z([1,1,0,1])
|
|
2358
|
+
sage: reps[0] == SL2Z([1,0,0,1])
|
|
2359
|
+
True
|
|
2360
|
+
sage: all(reps[i]*S2*~reps[s[i]] in G for i in range(4))
|
|
2361
|
+
True
|
|
2362
|
+
sage: all(reps[i]*L*~reps[l[i]] in G for i in range(4))
|
|
2363
|
+
True
|
|
2364
|
+
"""
|
|
2365
|
+
tree,reps,wreps,edges = self._spanning_tree_verrill()
|
|
2366
|
+
|
|
2367
|
+
gens = []
|
|
2368
|
+
for e in edges:
|
|
2369
|
+
if e[2] == 'l':
|
|
2370
|
+
gens.append(self(reps[e[0]] * Lm * ~reps[e[1]]))
|
|
2371
|
+
elif e[2] == 's':
|
|
2372
|
+
gens.append(self(reps[e[0]] * S2m * ~reps[e[1]]))
|
|
2373
|
+
else:
|
|
2374
|
+
raise ValueError("this should not happen")
|
|
2375
|
+
|
|
2376
|
+
return reps, gens, self._L[:], self._S2[:]
|
|
2377
|
+
|
|
2378
|
+
todd_coxeter = todd_coxeter_l_s2
|
|
2379
|
+
|
|
2380
|
+
def coset_reps(self):
|
|
2381
|
+
r"""
|
|
2382
|
+
Return coset representatives.
|
|
2383
|
+
|
|
2384
|
+
EXAMPLES::
|
|
2385
|
+
|
|
2386
|
+
sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)",S3="(1,2,3)")
|
|
2387
|
+
sage: c = G.coset_reps()
|
|
2388
|
+
sage: len(c)
|
|
2389
|
+
4
|
|
2390
|
+
sage: [g in G for g in c]
|
|
2391
|
+
[True, False, False, False]
|
|
2392
|
+
"""
|
|
2393
|
+
return self.todd_coxeter()[0]
|
|
2394
|
+
|
|
2395
|
+
def cusp_widths(self, exp=False):
|
|
2396
|
+
r"""
|
|
2397
|
+
Return the list of cusp widths of the group.
|
|
2398
|
+
|
|
2399
|
+
EXAMPLES::
|
|
2400
|
+
|
|
2401
|
+
sage: G = Gamma(2).as_permutation_group()
|
|
2402
|
+
sage: G.cusp_widths()
|
|
2403
|
+
[2, 2, 2]
|
|
2404
|
+
sage: G.cusp_widths(exp=True)
|
|
2405
|
+
{2: 3}
|
|
2406
|
+
|
|
2407
|
+
sage: S2 = "(1,2)(3,4)(5,6)"
|
|
2408
|
+
sage: S3 = "(1,2,3)(4,5,6)"
|
|
2409
|
+
sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)
|
|
2410
|
+
sage: G.cusp_widths()
|
|
2411
|
+
[1, 1, 4]
|
|
2412
|
+
sage: G.cusp_widths(exp=True)
|
|
2413
|
+
{1: 2, 4: 1}
|
|
2414
|
+
|
|
2415
|
+
sage: S2 = "(1,2)(3,4)(5,6)"
|
|
2416
|
+
sage: S3 = "(1,3,5)(2,4,6)"
|
|
2417
|
+
sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)
|
|
2418
|
+
sage: G.cusp_widths()
|
|
2419
|
+
[6]
|
|
2420
|
+
sage: G.cusp_widths(exp=True)
|
|
2421
|
+
{6: 1}
|
|
2422
|
+
"""
|
|
2423
|
+
seen = [True]*self.index()
|
|
2424
|
+
|
|
2425
|
+
if exp:
|
|
2426
|
+
widths = {}
|
|
2427
|
+
else:
|
|
2428
|
+
widths = []
|
|
2429
|
+
for i in range(self.index()):
|
|
2430
|
+
if seen[i]:
|
|
2431
|
+
seen[i] = False
|
|
2432
|
+
j = self._L[i]
|
|
2433
|
+
n = 1
|
|
2434
|
+
while j != i:
|
|
2435
|
+
seen[j] = False
|
|
2436
|
+
n += 1
|
|
2437
|
+
j = self._L[j]
|
|
2438
|
+
if exp:
|
|
2439
|
+
if n not in widths:
|
|
2440
|
+
widths[n] = 0
|
|
2441
|
+
widths[n] += 1
|
|
2442
|
+
else:
|
|
2443
|
+
widths.append(n)
|
|
2444
|
+
|
|
2445
|
+
if exp:
|
|
2446
|
+
return widths
|
|
2447
|
+
return sorted(widths)
|
|
2448
|
+
|
|
2449
|
+
def to_even_subgroup(self, relabel=True):
|
|
2450
|
+
r"""
|
|
2451
|
+
Return the subgroup generated by ``self`` and ``-Id``. Since ``self`` is even,
|
|
2452
|
+
this is just ``self``. Provided for compatibility.
|
|
2453
|
+
|
|
2454
|
+
EXAMPLES::
|
|
2455
|
+
|
|
2456
|
+
sage: G = Gamma0(4).as_permutation_group()
|
|
2457
|
+
sage: H = G.to_even_subgroup()
|
|
2458
|
+
sage: H == G
|
|
2459
|
+
True
|
|
2460
|
+
"""
|
|
2461
|
+
if relabel:
|
|
2462
|
+
return self.relabel(inplace=False)
|
|
2463
|
+
else:
|
|
2464
|
+
return self
|
|
2465
|
+
|
|
2466
|
+
def one_odd_subgroup(self, random=False):
|
|
2467
|
+
r"""
|
|
2468
|
+
Return an odd subgroup of index 2 in `\Gamma`, where `\Gamma` is this
|
|
2469
|
+
subgroup. If the optional argument ``random`` is False (the default),
|
|
2470
|
+
this returns an arbitrary but consistent choice from the set of index 2
|
|
2471
|
+
odd subgroups. If ``random`` is True, then it will choose one of these
|
|
2472
|
+
at random.
|
|
2473
|
+
|
|
2474
|
+
For details of the algorithm used, see the docstring for the related
|
|
2475
|
+
function :meth:`odd_subgroups`, which returns a list of all the
|
|
2476
|
+
index 2 odd subgroups.
|
|
2477
|
+
|
|
2478
|
+
EXAMPLES:
|
|
2479
|
+
|
|
2480
|
+
Starting from `\Gamma(4)` we get back `\Gamma(4)`::
|
|
2481
|
+
|
|
2482
|
+
sage: G = Gamma(4).as_permutation_group()
|
|
2483
|
+
sage: G.is_odd(), G.index()
|
|
2484
|
+
(True, 48)
|
|
2485
|
+
sage: Ge = G.to_even_subgroup()
|
|
2486
|
+
sage: Go = Ge.one_odd_subgroup()
|
|
2487
|
+
sage: Go.is_odd(), Go.index()
|
|
2488
|
+
(True, 48)
|
|
2489
|
+
sage: Go == G
|
|
2490
|
+
True
|
|
2491
|
+
|
|
2492
|
+
Starting from `\Gamma(6)` we get a different group::
|
|
2493
|
+
|
|
2494
|
+
sage: G = Gamma(6).as_permutation_group()
|
|
2495
|
+
sage: G.is_odd(), G.index()
|
|
2496
|
+
(True, 144)
|
|
2497
|
+
sage: Ge = G.to_even_subgroup()
|
|
2498
|
+
sage: Go = Ge.one_odd_subgroup()
|
|
2499
|
+
sage: Go.is_odd(), Go.index()
|
|
2500
|
+
(True, 144)
|
|
2501
|
+
sage: Go == G
|
|
2502
|
+
False
|
|
2503
|
+
|
|
2504
|
+
An error will be raised if there are no such subgroups, which occurs if
|
|
2505
|
+
and only if the group contains an element of order 4::
|
|
2506
|
+
|
|
2507
|
+
sage: Gamma0(10).as_permutation_group().one_odd_subgroup()
|
|
2508
|
+
Traceback (most recent call last):
|
|
2509
|
+
...
|
|
2510
|
+
ValueError: Group contains an element of order 4, hence no index 2 odd subgroups
|
|
2511
|
+
|
|
2512
|
+
Testing randomness::
|
|
2513
|
+
|
|
2514
|
+
sage: G = Gamma(6).as_permutation_group().to_even_subgroup()
|
|
2515
|
+
sage: G1 = G.one_odd_subgroup(random=True) # random
|
|
2516
|
+
sage: G1.is_subgroup(G)
|
|
2517
|
+
True
|
|
2518
|
+
"""
|
|
2519
|
+
if self.nu2() != 0:
|
|
2520
|
+
raise ValueError("Group contains an element of order 4, hence no index 2 odd subgroups")
|
|
2521
|
+
n = self.index()
|
|
2522
|
+
s2old, s3old = self.S2(), self.S3()
|
|
2523
|
+
s2cycs = s2old.cycle_tuples() # no singletons can exist
|
|
2524
|
+
s3cycs = s3old.cycle_tuples(singletons=True)
|
|
2525
|
+
s2 = PermutationConstructor([x + tuple(y + n for y in x) for x in s2cycs])
|
|
2526
|
+
s3 = PermutationConstructor([x + tuple(y + n for y in x) for x in s3cycs])
|
|
2527
|
+
|
|
2528
|
+
if random is False:
|
|
2529
|
+
return ArithmeticSubgroup_Permutation(S2=s2,S3=s3,check=False)
|
|
2530
|
+
|
|
2531
|
+
from sage.misc.prandom import randint
|
|
2532
|
+
|
|
2533
|
+
t = []
|
|
2534
|
+
for i in range(1,n+1):
|
|
2535
|
+
if randint(0,1):
|
|
2536
|
+
t.append((i,n+i))
|
|
2537
|
+
t = PermutationConstructor(t)
|
|
2538
|
+
return ArithmeticSubgroup_Permutation(S2=s2,S3=t*s3*t,check=False)
|
|
2539
|
+
|
|
2540
|
+
def odd_subgroups(self):
|
|
2541
|
+
r"""
|
|
2542
|
+
Return a list of the odd subgroups of index 2 in `\Gamma`, where
|
|
2543
|
+
`\Gamma` is this subgroup. (Equivalently, return the liftings of
|
|
2544
|
+
`\bar{\Gamma} \le \PSL(2, \ZZ)` to `\SL(2, \ZZ)`.) This can
|
|
2545
|
+
take rather a long time if the index of this subgroup is large.
|
|
2546
|
+
|
|
2547
|
+
.. SEEALSO:: :meth:`one_odd_subgroup`, which returns just one of the
|
|
2548
|
+
odd subgroups (which is much quicker than enumerating them all).
|
|
2549
|
+
|
|
2550
|
+
ALGORITHM:
|
|
2551
|
+
|
|
2552
|
+
- If `\Gamma` has an element of order 4, then there are no index 2 odd
|
|
2553
|
+
subgroups, so return the empty set.
|
|
2554
|
+
|
|
2555
|
+
- If `\Gamma` has no elements of order 4, then the permutation `S_2` is
|
|
2556
|
+
a combination of 2-cycles with no fixed points on `\{1, \dots, N\}`.
|
|
2557
|
+
We construct the permutation `\tilde{S}_2` of `\{1, \dots, 2N\}`
|
|
2558
|
+
which has a 4-cycle `(a, b, a+N, b+N)` for each 2-cycle `(a,b)` in
|
|
2559
|
+
``S2``. Similarly, we construct a permutation `\tilde{S}_3` which has
|
|
2560
|
+
a 6-cycle `(a,b,c,a+N,b+N,c+N)` for each 3-cycle `(a,b,c)` in `S_3`,
|
|
2561
|
+
and a 2-cycle `(a,a+N)` for each fixed point `a` of `S_3`.
|
|
2562
|
+
|
|
2563
|
+
Then the permutations `\tilde{S}_2` and `\tilde{S}_3` satisfy
|
|
2564
|
+
`\tilde{S}_2^2 = \tilde{S}_3^3 = \iota` where `\iota` is the order 2
|
|
2565
|
+
permutation interchanging `a` and `a+N` for each `a`. So the subgroup
|
|
2566
|
+
corresponding to these permutations is an index 2 odd subgroup of
|
|
2567
|
+
`\Gamma`.
|
|
2568
|
+
|
|
2569
|
+
- The other index 2 odd subgroups of `\Gamma` are obtained from the
|
|
2570
|
+
pairs `\tilde{S}_2, \tilde{S}_3^\sigma` where `\sigma` is an element
|
|
2571
|
+
of the group generated by the 2-cycles `(a, a+N)`.
|
|
2572
|
+
|
|
2573
|
+
Studying the permutations in the first example below gives a good
|
|
2574
|
+
illustration of the algorithm.
|
|
2575
|
+
|
|
2576
|
+
EXAMPLES::
|
|
2577
|
+
|
|
2578
|
+
sage: G = sage.modular.arithgroup.arithgroup_perm.HsuExample10()
|
|
2579
|
+
sage: [G.S2(), G.S3()]
|
|
2580
|
+
[(1,2)(3,4)(5,6)(7,8)(9,10), (1,8,3)(2,4,6)(5,7,10)]
|
|
2581
|
+
sage: X = G.odd_subgroups()
|
|
2582
|
+
sage: for u in X: print([u.S2(), u.S3()])
|
|
2583
|
+
[(1,2,11,12)(3,4,13,14)(5,6,15,16)(7,8,17,18)(9,10,19,20), (1,8,3,11,18,13)(2,4,6,12,14,16)(5,7,10,15,17,20)(9,19)]
|
|
2584
|
+
[(1,2,11,12)(3,4,13,14)(5,6,15,16)(7,8,17,18)(9,10,19,20), (1,18,13,11,8,3)(2,4,6,12,14,16)(5,7,10,15,17,20)(9,19)]
|
|
2585
|
+
[(1,2,11,12)(3,4,13,14)(5,6,15,16)(7,8,17,18)(9,10,19,20), (1,8,13,11,18,3)(2,4,6,12,14,16)(5,7,10,15,17,20)(9,19)]
|
|
2586
|
+
[(1,2,11,12)(3,4,13,14)(5,6,15,16)(7,8,17,18)(9,10,19,20), (1,18,3,11,8,13)(2,4,6,12,14,16)(5,7,10,15,17,20)(9,19)]
|
|
2587
|
+
|
|
2588
|
+
A projective congruence subgroup may have noncongruence liftings, as the example of `\bar{\Gamma}_0(6)` illustrates (see [KSV2011]_)::
|
|
2589
|
+
|
|
2590
|
+
sage: X = Gamma0(6).as_permutation_group().odd_subgroups(); Sequence([[u.S2(), u.S3()] for u in X],cr=True)
|
|
2591
|
+
[[(1,3,13,15)(2,4,14,16)(5,7,17,19)(6,10,18,22)(8,12,20,24)(9,11,21,23),
|
|
2592
|
+
(1,2,3,13,14,15)(4,5,6,16,17,18)(7,8,9,19,20,21)(10,11,12,22,23,24)],
|
|
2593
|
+
[(1,3,13,15)(2,4,14,16)(5,7,17,19)(6,10,18,22)(8,12,20,24)(9,11,21,23),
|
|
2594
|
+
(1,14,15,13,2,3)(4,5,6,16,17,18)(7,8,9,19,20,21)(10,11,12,22,23,24)],
|
|
2595
|
+
[(1,3,13,15)(2,4,14,16)(5,7,17,19)(6,10,18,22)(8,12,20,24)(9,11,21,23),
|
|
2596
|
+
(1,2,3,13,14,15)(4,17,6,16,5,18)(7,8,9,19,20,21)(10,11,12,22,23,24)],
|
|
2597
|
+
[(1,3,13,15)(2,4,14,16)(5,7,17,19)(6,10,18,22)(8,12,20,24)(9,11,21,23),
|
|
2598
|
+
(1,14,15,13,2,3)(4,17,6,16,5,18)(7,8,9,19,20,21)(10,11,12,22,23,24)],
|
|
2599
|
+
[(1,3,13,15)(2,4,14,16)(5,7,17,19)(6,10,18,22)(8,12,20,24)(9,11,21,23),
|
|
2600
|
+
(1,2,3,13,14,15)(4,5,6,16,17,18)(7,20,9,19,8,21)(10,11,12,22,23,24)],
|
|
2601
|
+
[(1,3,13,15)(2,4,14,16)(5,7,17,19)(6,10,18,22)(8,12,20,24)(9,11,21,23),
|
|
2602
|
+
(1,14,15,13,2,3)(4,5,6,16,17,18)(7,20,9,19,8,21)(10,11,12,22,23,24)],
|
|
2603
|
+
[(1,3,13,15)(2,4,14,16)(5,7,17,19)(6,10,18,22)(8,12,20,24)(9,11,21,23),
|
|
2604
|
+
(1,2,3,13,14,15)(4,17,6,16,5,18)(7,20,9,19,8,21)(10,11,12,22,23,24)],
|
|
2605
|
+
[(1,3,13,15)(2,4,14,16)(5,7,17,19)(6,10,18,22)(8,12,20,24)(9,11,21,23),
|
|
2606
|
+
(1,14,15,13,2,3)(4,17,6,16,5,18)(7,20,9,19,8,21)(10,11,12,22,23,24)]]
|
|
2607
|
+
sage: [u.is_congruence() for u in X]
|
|
2608
|
+
[True, False, False, True, True, False, False, True]
|
|
2609
|
+
"""
|
|
2610
|
+
if self.nu2() != 0:
|
|
2611
|
+
return []
|
|
2612
|
+
n = self.index()
|
|
2613
|
+
s2old, s3old = self.S2(), self.S3()
|
|
2614
|
+
s2cycs = s2old.cycle_tuples() # no singletons can exist
|
|
2615
|
+
s3cycs = s3old.cycle_tuples(singletons=True)
|
|
2616
|
+
s2 = PermutationConstructor([x + tuple(y + n for y in x) for x in s2cycs])
|
|
2617
|
+
s3 = PermutationConstructor([x + tuple(y + n for y in x) for x in s3cycs])
|
|
2618
|
+
H = ArithmeticSubgroup_Permutation(S2=s2,S3=s3)
|
|
2619
|
+
|
|
2620
|
+
bucket = {H}
|
|
2621
|
+
res = [H]
|
|
2622
|
+
# We use a set *and* a list since checking whether an element is in a
|
|
2623
|
+
# set is very fast, but on the other hand we want the order the results
|
|
2624
|
+
# are returned to be at least somewhat canonical.
|
|
2625
|
+
ts = [PermutationConstructor(list(range(1,1+2*n)))]
|
|
2626
|
+
|
|
2627
|
+
for i in range(1,n+1):
|
|
2628
|
+
|
|
2629
|
+
t = PermutationConstructor([(i, n+i)], check=False)
|
|
2630
|
+
|
|
2631
|
+
s3c = t*s3*t
|
|
2632
|
+
|
|
2633
|
+
if s3c == s3:
|
|
2634
|
+
# t commutes with s3; nothing to see here.
|
|
2635
|
+
continue
|
|
2636
|
+
|
|
2637
|
+
HH = ArithmeticSubgroup_Permutation(S2=s2,S3=s3c,check=False)
|
|
2638
|
+
|
|
2639
|
+
if HH not in bucket:
|
|
2640
|
+
# Because the liftings are indexed by Hom(self, +-1) which is a
|
|
2641
|
+
# vector space over F2, either HH is already familiar, or all
|
|
2642
|
+
# the subgroups one gets by acting by t are new.
|
|
2643
|
+
|
|
2644
|
+
bucket.add(HH)
|
|
2645
|
+
res.append(HH)
|
|
2646
|
+
ts.append(t)
|
|
2647
|
+
for tt in ts[1:-1]:
|
|
2648
|
+
ts.append(tt*t)
|
|
2649
|
+
res.append(ArithmeticSubgroup_Permutation(S2=s2,S3=tt*s3c*tt,check=False))
|
|
2650
|
+
bucket.add(res[-1])
|
|
2651
|
+
|
|
2652
|
+
return res
|
|
2653
|
+
|
|
2654
|
+
|
|
2655
|
+
def HsuExample10():
|
|
2656
|
+
r"""
|
|
2657
|
+
An example of an index 10 arithmetic subgroup studied by Tim Hsu.
|
|
2658
|
+
|
|
2659
|
+
EXAMPLES::
|
|
2660
|
+
|
|
2661
|
+
sage: import sage.modular.arithgroup.arithgroup_perm as ap
|
|
2662
|
+
sage: ap.HsuExample10()
|
|
2663
|
+
Arithmetic subgroup with permutations of right cosets
|
|
2664
|
+
S2=(1,2)(3,4)(5,6)(7,8)(9,10)
|
|
2665
|
+
S3=(1,8,3)(2,4,6)(5,7,10)
|
|
2666
|
+
L=(1,4)(2,5,9,10,8)(3,7,6)
|
|
2667
|
+
R=(1,7,9,10,6)(2,3)(4,5,8)
|
|
2668
|
+
"""
|
|
2669
|
+
return ArithmeticSubgroup_Permutation(
|
|
2670
|
+
L="(1,4)(2,5,9,10,8)(3,7,6)",
|
|
2671
|
+
R="(1,7,9,10,6)(2,3)(4,5,8)",
|
|
2672
|
+
relabel=False)
|
|
2673
|
+
|
|
2674
|
+
|
|
2675
|
+
def HsuExample18():
|
|
2676
|
+
r"""
|
|
2677
|
+
An example of an index 18 arithmetic subgroup studied by Tim Hsu.
|
|
2678
|
+
|
|
2679
|
+
EXAMPLES::
|
|
2680
|
+
|
|
2681
|
+
sage: import sage.modular.arithgroup.arithgroup_perm as ap
|
|
2682
|
+
sage: ap.HsuExample18()
|
|
2683
|
+
Arithmetic subgroup with permutations of right cosets
|
|
2684
|
+
S2=(1,5)(2,11)(3,10)(4,15)(6,18)(7,12)(8,14)(9,16)(13,17)
|
|
2685
|
+
S3=(1,7,11)(2,18,5)(3,9,15)(4,14,10)(6,17,12)(8,13,16)
|
|
2686
|
+
L=(1,2)(3,4)(5,6,7)(8,9,10)(11,12,13,14,15,16,17,18)
|
|
2687
|
+
R=(1,12,18)(2,6,13,9,4,8,17,7)(3,16,14)(5,11)(10,15)
|
|
2688
|
+
"""
|
|
2689
|
+
return ArithmeticSubgroup_Permutation(
|
|
2690
|
+
L="(1,2)(3,4)(5,6,7)(8,9,10)(11,12,13,14,15,16,17,18)",
|
|
2691
|
+
R="(1,12,18)(2,6,13,9,4,8,17,7)(3,16,14)(5,11)(10,15)",
|
|
2692
|
+
relabel=False)
|