passagemath-schemes 10.6.38__cp314-cp314t-macosx_13_0_arm64.whl

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

Potentially problematic release.


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

Files changed (314) hide show
  1. passagemath_schemes/.dylibs/libflint.21.0.dylib +0 -0
  2. passagemath_schemes/.dylibs/libgmp.10.dylib +0 -0
  3. passagemath_schemes/.dylibs/libgmpxx.4.dylib +0 -0
  4. passagemath_schemes/.dylibs/libmpfr.6.dylib +0 -0
  5. passagemath_schemes/__init__.py +3 -0
  6. passagemath_schemes-10.6.38.dist-info/METADATA +204 -0
  7. passagemath_schemes-10.6.38.dist-info/METADATA.bak +205 -0
  8. passagemath_schemes-10.6.38.dist-info/RECORD +314 -0
  9. passagemath_schemes-10.6.38.dist-info/WHEEL +6 -0
  10. passagemath_schemes-10.6.38.dist-info/top_level.txt +3 -0
  11. sage/all__sagemath_schemes.py +23 -0
  12. sage/databases/all__sagemath_schemes.py +7 -0
  13. sage/databases/cremona.py +1723 -0
  14. sage/dynamics/all__sagemath_schemes.py +2 -0
  15. sage/dynamics/arithmetic_dynamics/affine_ds.py +1083 -0
  16. sage/dynamics/arithmetic_dynamics/all.py +14 -0
  17. sage/dynamics/arithmetic_dynamics/berkovich_ds.py +1101 -0
  18. sage/dynamics/arithmetic_dynamics/dynamical_semigroup.py +1543 -0
  19. sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py +2426 -0
  20. sage/dynamics/arithmetic_dynamics/endPN_minimal_model.py +1169 -0
  21. sage/dynamics/arithmetic_dynamics/generic_ds.py +663 -0
  22. sage/dynamics/arithmetic_dynamics/product_projective_ds.py +339 -0
  23. sage/dynamics/arithmetic_dynamics/projective_ds.py +9558 -0
  24. sage/dynamics/arithmetic_dynamics/projective_ds_helper.cpython-314t-darwin.so +0 -0
  25. sage/dynamics/arithmetic_dynamics/projective_ds_helper.pyx +301 -0
  26. sage/dynamics/arithmetic_dynamics/wehlerK3.py +2576 -0
  27. sage/lfunctions/all.py +18 -0
  28. sage/lfunctions/dokchitser.py +745 -0
  29. sage/lfunctions/pari.py +818 -0
  30. sage/lfunctions/zero_sums.cpython-314t-darwin.so +0 -0
  31. sage/lfunctions/zero_sums.pyx +1847 -0
  32. sage/modular/abvar/abvar.py +5135 -0
  33. sage/modular/abvar/abvar_ambient_jacobian.py +413 -0
  34. sage/modular/abvar/abvar_newform.py +244 -0
  35. sage/modular/abvar/all.py +8 -0
  36. sage/modular/abvar/constructor.py +186 -0
  37. sage/modular/abvar/cuspidal_subgroup.py +371 -0
  38. sage/modular/abvar/finite_subgroup.py +896 -0
  39. sage/modular/abvar/homology.py +720 -0
  40. sage/modular/abvar/homspace.py +998 -0
  41. sage/modular/abvar/lseries.py +415 -0
  42. sage/modular/abvar/morphism.py +935 -0
  43. sage/modular/abvar/torsion_point.py +274 -0
  44. sage/modular/abvar/torsion_subgroup.py +740 -0
  45. sage/modular/all.py +43 -0
  46. sage/modular/arithgroup/all.py +20 -0
  47. sage/modular/arithgroup/arithgroup_element.cpython-314t-darwin.so +0 -0
  48. sage/modular/arithgroup/arithgroup_element.pyx +474 -0
  49. sage/modular/arithgroup/arithgroup_generic.py +1402 -0
  50. sage/modular/arithgroup/arithgroup_perm.py +2692 -0
  51. sage/modular/arithgroup/congroup.cpython-314t-darwin.so +0 -0
  52. sage/modular/arithgroup/congroup.pyx +334 -0
  53. sage/modular/arithgroup/congroup_gamma.py +363 -0
  54. sage/modular/arithgroup/congroup_gamma0.py +692 -0
  55. sage/modular/arithgroup/congroup_gamma1.py +653 -0
  56. sage/modular/arithgroup/congroup_gammaH.py +1469 -0
  57. sage/modular/arithgroup/congroup_generic.py +628 -0
  58. sage/modular/arithgroup/congroup_sl2z.py +267 -0
  59. sage/modular/arithgroup/farey_symbol.cpython-314t-darwin.so +0 -0
  60. sage/modular/arithgroup/farey_symbol.pyx +1066 -0
  61. sage/modular/arithgroup/tests.py +418 -0
  62. sage/modular/btquotients/all.py +4 -0
  63. sage/modular/btquotients/btquotient.py +3753 -0
  64. sage/modular/btquotients/pautomorphicform.py +2570 -0
  65. sage/modular/buzzard.py +100 -0
  66. sage/modular/congroup.py +29 -0
  67. sage/modular/congroup_element.py +13 -0
  68. sage/modular/cusps.py +1109 -0
  69. sage/modular/cusps_nf.py +1270 -0
  70. sage/modular/dims.py +569 -0
  71. sage/modular/dirichlet.py +3310 -0
  72. sage/modular/drinfeld_modform/all.py +2 -0
  73. sage/modular/drinfeld_modform/element.py +446 -0
  74. sage/modular/drinfeld_modform/ring.py +773 -0
  75. sage/modular/drinfeld_modform/tutorial.py +236 -0
  76. sage/modular/etaproducts.py +1065 -0
  77. sage/modular/hecke/algebra.py +746 -0
  78. sage/modular/hecke/all.py +20 -0
  79. sage/modular/hecke/ambient_module.py +1019 -0
  80. sage/modular/hecke/degenmap.py +119 -0
  81. sage/modular/hecke/element.py +325 -0
  82. sage/modular/hecke/hecke_operator.py +780 -0
  83. sage/modular/hecke/homspace.py +206 -0
  84. sage/modular/hecke/module.py +1767 -0
  85. sage/modular/hecke/morphism.py +174 -0
  86. sage/modular/hecke/submodule.py +989 -0
  87. sage/modular/hypergeometric_misc.cpython-314t-darwin.so +0 -0
  88. sage/modular/hypergeometric_misc.pxd +4 -0
  89. sage/modular/hypergeometric_misc.pyx +166 -0
  90. sage/modular/hypergeometric_motive.py +2017 -0
  91. sage/modular/local_comp/all.py +2 -0
  92. sage/modular/local_comp/liftings.py +292 -0
  93. sage/modular/local_comp/local_comp.py +1071 -0
  94. sage/modular/local_comp/smoothchar.py +1825 -0
  95. sage/modular/local_comp/type_space.py +748 -0
  96. sage/modular/modform/all.py +30 -0
  97. sage/modular/modform/ambient.py +815 -0
  98. sage/modular/modform/ambient_R.py +177 -0
  99. sage/modular/modform/ambient_eps.py +306 -0
  100. sage/modular/modform/ambient_g0.py +124 -0
  101. sage/modular/modform/ambient_g1.py +204 -0
  102. sage/modular/modform/constructor.py +545 -0
  103. sage/modular/modform/cuspidal_submodule.py +708 -0
  104. sage/modular/modform/defaults.py +14 -0
  105. sage/modular/modform/eis_series.py +505 -0
  106. sage/modular/modform/eisenstein_submodule.py +663 -0
  107. sage/modular/modform/element.py +4131 -0
  108. sage/modular/modform/find_generators.py +59 -0
  109. sage/modular/modform/half_integral.py +154 -0
  110. sage/modular/modform/hecke_operator_on_qexp.py +247 -0
  111. sage/modular/modform/j_invariant.py +47 -0
  112. sage/modular/modform/l_series_gross_zagier.py +133 -0
  113. sage/modular/modform/l_series_gross_zagier_coeffs.cpython-314t-darwin.so +0 -0
  114. sage/modular/modform/l_series_gross_zagier_coeffs.pyx +177 -0
  115. sage/modular/modform/notes.py +45 -0
  116. sage/modular/modform/numerical.py +514 -0
  117. sage/modular/modform/periods.py +14 -0
  118. sage/modular/modform/ring.py +1257 -0
  119. sage/modular/modform/space.py +1860 -0
  120. sage/modular/modform/submodule.py +118 -0
  121. sage/modular/modform/tests.py +64 -0
  122. sage/modular/modform/theta.py +110 -0
  123. sage/modular/modform/vm_basis.py +381 -0
  124. sage/modular/modform/weight1.py +220 -0
  125. sage/modular/modform_hecketriangle/abstract_ring.py +1932 -0
  126. sage/modular/modform_hecketriangle/abstract_space.py +2528 -0
  127. sage/modular/modform_hecketriangle/all.py +30 -0
  128. sage/modular/modform_hecketriangle/analytic_type.py +590 -0
  129. sage/modular/modform_hecketriangle/constructor.py +416 -0
  130. sage/modular/modform_hecketriangle/element.py +351 -0
  131. sage/modular/modform_hecketriangle/functors.py +752 -0
  132. sage/modular/modform_hecketriangle/graded_ring.py +541 -0
  133. sage/modular/modform_hecketriangle/graded_ring_element.py +2225 -0
  134. sage/modular/modform_hecketriangle/hecke_triangle_group_element.py +3352 -0
  135. sage/modular/modform_hecketriangle/hecke_triangle_groups.py +1432 -0
  136. sage/modular/modform_hecketriangle/readme.py +1214 -0
  137. sage/modular/modform_hecketriangle/series_constructor.py +580 -0
  138. sage/modular/modform_hecketriangle/space.py +1037 -0
  139. sage/modular/modform_hecketriangle/subspace.py +423 -0
  140. sage/modular/modsym/all.py +17 -0
  141. sage/modular/modsym/ambient.py +3846 -0
  142. sage/modular/modsym/boundary.py +1420 -0
  143. sage/modular/modsym/element.py +336 -0
  144. sage/modular/modsym/g1list.py +178 -0
  145. sage/modular/modsym/ghlist.py +182 -0
  146. sage/modular/modsym/hecke_operator.py +73 -0
  147. sage/modular/modsym/manin_symbol.cpython-314t-darwin.so +0 -0
  148. sage/modular/modsym/manin_symbol.pxd +5 -0
  149. sage/modular/modsym/manin_symbol.pyx +497 -0
  150. sage/modular/modsym/manin_symbol_list.py +1295 -0
  151. sage/modular/modsym/modsym.py +400 -0
  152. sage/modular/modsym/modular_symbols.py +384 -0
  153. sage/modular/modsym/p1list.cpython-314t-darwin.so +0 -0
  154. sage/modular/modsym/p1list.pxd +29 -0
  155. sage/modular/modsym/p1list.pyx +1372 -0
  156. sage/modular/modsym/p1list_nf.py +1241 -0
  157. sage/modular/modsym/relation_matrix.py +591 -0
  158. sage/modular/modsym/relation_matrix_pyx.cpython-314t-darwin.so +0 -0
  159. sage/modular/modsym/relation_matrix_pyx.pyx +108 -0
  160. sage/modular/modsym/space.py +2468 -0
  161. sage/modular/modsym/subspace.py +455 -0
  162. sage/modular/modsym/tests.py +375 -0
  163. sage/modular/multiple_zeta.py +2632 -0
  164. sage/modular/multiple_zeta_F_algebra.py +786 -0
  165. sage/modular/overconvergent/all.py +6 -0
  166. sage/modular/overconvergent/genus0.py +1878 -0
  167. sage/modular/overconvergent/hecke_series.py +1187 -0
  168. sage/modular/overconvergent/weightspace.py +778 -0
  169. sage/modular/pollack_stevens/all.py +4 -0
  170. sage/modular/pollack_stevens/distributions.py +874 -0
  171. sage/modular/pollack_stevens/fund_domain.py +1572 -0
  172. sage/modular/pollack_stevens/manin_map.py +859 -0
  173. sage/modular/pollack_stevens/modsym.py +1593 -0
  174. sage/modular/pollack_stevens/padic_lseries.py +417 -0
  175. sage/modular/pollack_stevens/sigma0.py +534 -0
  176. sage/modular/pollack_stevens/space.py +1076 -0
  177. sage/modular/quasimodform/all.py +3 -0
  178. sage/modular/quasimodform/element.py +845 -0
  179. sage/modular/quasimodform/ring.py +828 -0
  180. sage/modular/quatalg/all.py +3 -0
  181. sage/modular/quatalg/brandt.py +1642 -0
  182. sage/modular/ssmod/all.py +8 -0
  183. sage/modular/ssmod/ssmod.py +827 -0
  184. sage/rings/all__sagemath_schemes.py +1 -0
  185. sage/rings/polynomial/all__sagemath_schemes.py +1 -0
  186. sage/rings/polynomial/binary_form_reduce.py +585 -0
  187. sage/schemes/all.py +41 -0
  188. sage/schemes/berkovich/all.py +6 -0
  189. sage/schemes/berkovich/berkovich_cp_element.py +2582 -0
  190. sage/schemes/berkovich/berkovich_space.py +748 -0
  191. sage/schemes/curves/affine_curve.py +2928 -0
  192. sage/schemes/curves/all.py +33 -0
  193. sage/schemes/curves/closed_point.py +434 -0
  194. sage/schemes/curves/constructor.py +381 -0
  195. sage/schemes/curves/curve.py +542 -0
  196. sage/schemes/curves/plane_curve_arrangement.py +1283 -0
  197. sage/schemes/curves/point.py +463 -0
  198. sage/schemes/curves/projective_curve.py +3026 -0
  199. sage/schemes/curves/zariski_vankampen.py +1932 -0
  200. sage/schemes/cyclic_covers/all.py +2 -0
  201. sage/schemes/cyclic_covers/charpoly_frobenius.py +320 -0
  202. sage/schemes/cyclic_covers/constructor.py +137 -0
  203. sage/schemes/cyclic_covers/cycliccover_finite_field.py +1309 -0
  204. sage/schemes/cyclic_covers/cycliccover_generic.py +310 -0
  205. sage/schemes/elliptic_curves/BSD.py +1036 -0
  206. sage/schemes/elliptic_curves/Qcurves.py +592 -0
  207. sage/schemes/elliptic_curves/addition_formulas_ring.py +94 -0
  208. sage/schemes/elliptic_curves/all.py +49 -0
  209. sage/schemes/elliptic_curves/cardinality.py +609 -0
  210. sage/schemes/elliptic_curves/cm.py +1102 -0
  211. sage/schemes/elliptic_curves/constructor.py +1552 -0
  212. sage/schemes/elliptic_curves/ec_database.py +175 -0
  213. sage/schemes/elliptic_curves/ell_curve_isogeny.py +3972 -0
  214. sage/schemes/elliptic_curves/ell_egros.py +459 -0
  215. sage/schemes/elliptic_curves/ell_field.py +2836 -0
  216. sage/schemes/elliptic_curves/ell_finite_field.py +3359 -0
  217. sage/schemes/elliptic_curves/ell_generic.py +3760 -0
  218. sage/schemes/elliptic_curves/ell_local_data.py +1207 -0
  219. sage/schemes/elliptic_curves/ell_modular_symbols.py +775 -0
  220. sage/schemes/elliptic_curves/ell_number_field.py +4220 -0
  221. sage/schemes/elliptic_curves/ell_padic_field.py +107 -0
  222. sage/schemes/elliptic_curves/ell_point.py +4787 -0
  223. sage/schemes/elliptic_curves/ell_rational_field.py +7368 -0
  224. sage/schemes/elliptic_curves/ell_tate_curve.py +671 -0
  225. sage/schemes/elliptic_curves/ell_torsion.py +436 -0
  226. sage/schemes/elliptic_curves/ell_wp.py +352 -0
  227. sage/schemes/elliptic_curves/formal_group.py +760 -0
  228. sage/schemes/elliptic_curves/gal_reps.py +1459 -0
  229. sage/schemes/elliptic_curves/gal_reps_number_field.py +1669 -0
  230. sage/schemes/elliptic_curves/gp_simon.py +152 -0
  231. sage/schemes/elliptic_curves/heegner.py +7335 -0
  232. sage/schemes/elliptic_curves/height.py +2109 -0
  233. sage/schemes/elliptic_curves/hom.py +1406 -0
  234. sage/schemes/elliptic_curves/hom_composite.py +934 -0
  235. sage/schemes/elliptic_curves/hom_frobenius.py +522 -0
  236. sage/schemes/elliptic_curves/hom_scalar.py +531 -0
  237. sage/schemes/elliptic_curves/hom_sum.py +682 -0
  238. sage/schemes/elliptic_curves/hom_velusqrt.py +1290 -0
  239. sage/schemes/elliptic_curves/homset.py +271 -0
  240. sage/schemes/elliptic_curves/isogeny_class.py +1521 -0
  241. sage/schemes/elliptic_curves/isogeny_small_degree.py +2797 -0
  242. sage/schemes/elliptic_curves/jacobian.py +237 -0
  243. sage/schemes/elliptic_curves/kodaira_symbol.py +344 -0
  244. sage/schemes/elliptic_curves/kraus.py +1014 -0
  245. sage/schemes/elliptic_curves/lseries_ell.py +943 -0
  246. sage/schemes/elliptic_curves/mod5family.py +105 -0
  247. sage/schemes/elliptic_curves/mod_poly.py +197 -0
  248. sage/schemes/elliptic_curves/mod_sym_num.cpython-314t-darwin.so +0 -0
  249. sage/schemes/elliptic_curves/mod_sym_num.pyx +3796 -0
  250. sage/schemes/elliptic_curves/modular_parametrization.py +305 -0
  251. sage/schemes/elliptic_curves/padic_lseries.py +1793 -0
  252. sage/schemes/elliptic_curves/padics.py +1816 -0
  253. sage/schemes/elliptic_curves/period_lattice.py +2234 -0
  254. sage/schemes/elliptic_curves/period_lattice_region.cpython-314t-darwin.so +0 -0
  255. sage/schemes/elliptic_curves/period_lattice_region.pyx +722 -0
  256. sage/schemes/elliptic_curves/saturation.py +715 -0
  257. sage/schemes/elliptic_curves/sha_tate.py +1158 -0
  258. sage/schemes/elliptic_curves/weierstrass_morphism.py +1117 -0
  259. sage/schemes/elliptic_curves/weierstrass_transform.py +200 -0
  260. sage/schemes/hyperelliptic_curves/all.py +6 -0
  261. sage/schemes/hyperelliptic_curves/constructor.py +291 -0
  262. sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py +1914 -0
  263. sage/schemes/hyperelliptic_curves/hyperelliptic_g2.py +192 -0
  264. sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py +954 -0
  265. sage/schemes/hyperelliptic_curves/hyperelliptic_padic_field.py +1332 -0
  266. sage/schemes/hyperelliptic_curves/hyperelliptic_rational_field.py +84 -0
  267. sage/schemes/hyperelliptic_curves/invariants.py +410 -0
  268. sage/schemes/hyperelliptic_curves/jacobian_endomorphism_utils.py +315 -0
  269. sage/schemes/hyperelliptic_curves/jacobian_g2.py +32 -0
  270. sage/schemes/hyperelliptic_curves/jacobian_generic.py +419 -0
  271. sage/schemes/hyperelliptic_curves/jacobian_homset.py +186 -0
  272. sage/schemes/hyperelliptic_curves/jacobian_morphism.py +875 -0
  273. sage/schemes/hyperelliptic_curves/kummer_surface.py +99 -0
  274. sage/schemes/hyperelliptic_curves/mestre.py +302 -0
  275. sage/schemes/hyperelliptic_curves/monsky_washnitzer.py +3871 -0
  276. sage/schemes/jacobians/abstract_jacobian.py +277 -0
  277. sage/schemes/jacobians/all.py +2 -0
  278. sage/schemes/overview.py +161 -0
  279. sage/schemes/plane_conics/all.py +22 -0
  280. sage/schemes/plane_conics/con_field.py +1296 -0
  281. sage/schemes/plane_conics/con_finite_field.py +158 -0
  282. sage/schemes/plane_conics/con_number_field.py +456 -0
  283. sage/schemes/plane_conics/con_rational_field.py +406 -0
  284. sage/schemes/plane_conics/con_rational_function_field.py +580 -0
  285. sage/schemes/plane_conics/constructor.py +249 -0
  286. sage/schemes/plane_quartics/all.py +2 -0
  287. sage/schemes/plane_quartics/quartic_constructor.py +71 -0
  288. sage/schemes/plane_quartics/quartic_generic.py +73 -0
  289. sage/schemes/riemann_surfaces/all.py +1 -0
  290. sage/schemes/riemann_surfaces/riemann_surface.py +4117 -0
  291. sage_wheels/share/cremona/cremona_mini.db +0 -0
  292. sage_wheels/share/ellcurves/rank0 +30427 -0
  293. sage_wheels/share/ellcurves/rank1 +31871 -0
  294. sage_wheels/share/ellcurves/rank10 +6 -0
  295. sage_wheels/share/ellcurves/rank11 +6 -0
  296. sage_wheels/share/ellcurves/rank12 +1 -0
  297. sage_wheels/share/ellcurves/rank14 +1 -0
  298. sage_wheels/share/ellcurves/rank15 +1 -0
  299. sage_wheels/share/ellcurves/rank17 +1 -0
  300. sage_wheels/share/ellcurves/rank19 +1 -0
  301. sage_wheels/share/ellcurves/rank2 +2388 -0
  302. sage_wheels/share/ellcurves/rank20 +1 -0
  303. sage_wheels/share/ellcurves/rank21 +1 -0
  304. sage_wheels/share/ellcurves/rank22 +1 -0
  305. sage_wheels/share/ellcurves/rank23 +1 -0
  306. sage_wheels/share/ellcurves/rank24 +1 -0
  307. sage_wheels/share/ellcurves/rank28 +1 -0
  308. sage_wheels/share/ellcurves/rank3 +836 -0
  309. sage_wheels/share/ellcurves/rank4 +10 -0
  310. sage_wheels/share/ellcurves/rank5 +5 -0
  311. sage_wheels/share/ellcurves/rank6 +5 -0
  312. sage_wheels/share/ellcurves/rank7 +5 -0
  313. sage_wheels/share/ellcurves/rank8 +6 -0
  314. sage_wheels/share/ellcurves/rank9 +7 -0
@@ -0,0 +1,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)