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.
- passagemath_schemes/.dylibs/libflint.21.0.dylib +0 -0
- passagemath_schemes/.dylibs/libgmp.10.dylib +0 -0
- passagemath_schemes/.dylibs/libgmpxx.4.dylib +0 -0
- passagemath_schemes/.dylibs/libmpfr.6.dylib +0 -0
- passagemath_schemes/__init__.py +3 -0
- passagemath_schemes-10.6.38.dist-info/METADATA +204 -0
- passagemath_schemes-10.6.38.dist-info/METADATA.bak +205 -0
- passagemath_schemes-10.6.38.dist-info/RECORD +314 -0
- passagemath_schemes-10.6.38.dist-info/WHEEL +6 -0
- passagemath_schemes-10.6.38.dist-info/top_level.txt +3 -0
- sage/all__sagemath_schemes.py +23 -0
- sage/databases/all__sagemath_schemes.py +7 -0
- sage/databases/cremona.py +1723 -0
- sage/dynamics/all__sagemath_schemes.py +2 -0
- sage/dynamics/arithmetic_dynamics/affine_ds.py +1083 -0
- sage/dynamics/arithmetic_dynamics/all.py +14 -0
- sage/dynamics/arithmetic_dynamics/berkovich_ds.py +1101 -0
- sage/dynamics/arithmetic_dynamics/dynamical_semigroup.py +1543 -0
- sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py +2426 -0
- sage/dynamics/arithmetic_dynamics/endPN_minimal_model.py +1169 -0
- sage/dynamics/arithmetic_dynamics/generic_ds.py +663 -0
- sage/dynamics/arithmetic_dynamics/product_projective_ds.py +339 -0
- sage/dynamics/arithmetic_dynamics/projective_ds.py +9558 -0
- sage/dynamics/arithmetic_dynamics/projective_ds_helper.cpython-314t-darwin.so +0 -0
- sage/dynamics/arithmetic_dynamics/projective_ds_helper.pyx +301 -0
- sage/dynamics/arithmetic_dynamics/wehlerK3.py +2576 -0
- sage/lfunctions/all.py +18 -0
- sage/lfunctions/dokchitser.py +745 -0
- sage/lfunctions/pari.py +818 -0
- sage/lfunctions/zero_sums.cpython-314t-darwin.so +0 -0
- sage/lfunctions/zero_sums.pyx +1847 -0
- sage/modular/abvar/abvar.py +5135 -0
- sage/modular/abvar/abvar_ambient_jacobian.py +413 -0
- sage/modular/abvar/abvar_newform.py +244 -0
- sage/modular/abvar/all.py +8 -0
- sage/modular/abvar/constructor.py +186 -0
- sage/modular/abvar/cuspidal_subgroup.py +371 -0
- sage/modular/abvar/finite_subgroup.py +896 -0
- sage/modular/abvar/homology.py +720 -0
- sage/modular/abvar/homspace.py +998 -0
- sage/modular/abvar/lseries.py +415 -0
- sage/modular/abvar/morphism.py +935 -0
- sage/modular/abvar/torsion_point.py +274 -0
- sage/modular/abvar/torsion_subgroup.py +740 -0
- sage/modular/all.py +43 -0
- sage/modular/arithgroup/all.py +20 -0
- sage/modular/arithgroup/arithgroup_element.cpython-314t-darwin.so +0 -0
- sage/modular/arithgroup/arithgroup_element.pyx +474 -0
- sage/modular/arithgroup/arithgroup_generic.py +1402 -0
- sage/modular/arithgroup/arithgroup_perm.py +2692 -0
- sage/modular/arithgroup/congroup.cpython-314t-darwin.so +0 -0
- sage/modular/arithgroup/congroup.pyx +334 -0
- sage/modular/arithgroup/congroup_gamma.py +363 -0
- sage/modular/arithgroup/congroup_gamma0.py +692 -0
- sage/modular/arithgroup/congroup_gamma1.py +653 -0
- sage/modular/arithgroup/congroup_gammaH.py +1469 -0
- sage/modular/arithgroup/congroup_generic.py +628 -0
- sage/modular/arithgroup/congroup_sl2z.py +267 -0
- sage/modular/arithgroup/farey_symbol.cpython-314t-darwin.so +0 -0
- sage/modular/arithgroup/farey_symbol.pyx +1066 -0
- sage/modular/arithgroup/tests.py +418 -0
- sage/modular/btquotients/all.py +4 -0
- sage/modular/btquotients/btquotient.py +3753 -0
- sage/modular/btquotients/pautomorphicform.py +2570 -0
- sage/modular/buzzard.py +100 -0
- sage/modular/congroup.py +29 -0
- sage/modular/congroup_element.py +13 -0
- sage/modular/cusps.py +1109 -0
- sage/modular/cusps_nf.py +1270 -0
- sage/modular/dims.py +569 -0
- sage/modular/dirichlet.py +3310 -0
- sage/modular/drinfeld_modform/all.py +2 -0
- sage/modular/drinfeld_modform/element.py +446 -0
- sage/modular/drinfeld_modform/ring.py +773 -0
- sage/modular/drinfeld_modform/tutorial.py +236 -0
- sage/modular/etaproducts.py +1065 -0
- sage/modular/hecke/algebra.py +746 -0
- sage/modular/hecke/all.py +20 -0
- sage/modular/hecke/ambient_module.py +1019 -0
- sage/modular/hecke/degenmap.py +119 -0
- sage/modular/hecke/element.py +325 -0
- sage/modular/hecke/hecke_operator.py +780 -0
- sage/modular/hecke/homspace.py +206 -0
- sage/modular/hecke/module.py +1767 -0
- sage/modular/hecke/morphism.py +174 -0
- sage/modular/hecke/submodule.py +989 -0
- sage/modular/hypergeometric_misc.cpython-314t-darwin.so +0 -0
- sage/modular/hypergeometric_misc.pxd +4 -0
- sage/modular/hypergeometric_misc.pyx +166 -0
- sage/modular/hypergeometric_motive.py +2017 -0
- sage/modular/local_comp/all.py +2 -0
- sage/modular/local_comp/liftings.py +292 -0
- sage/modular/local_comp/local_comp.py +1071 -0
- sage/modular/local_comp/smoothchar.py +1825 -0
- sage/modular/local_comp/type_space.py +748 -0
- sage/modular/modform/all.py +30 -0
- sage/modular/modform/ambient.py +815 -0
- sage/modular/modform/ambient_R.py +177 -0
- sage/modular/modform/ambient_eps.py +306 -0
- sage/modular/modform/ambient_g0.py +124 -0
- sage/modular/modform/ambient_g1.py +204 -0
- sage/modular/modform/constructor.py +545 -0
- sage/modular/modform/cuspidal_submodule.py +708 -0
- sage/modular/modform/defaults.py +14 -0
- sage/modular/modform/eis_series.py +505 -0
- sage/modular/modform/eisenstein_submodule.py +663 -0
- sage/modular/modform/element.py +4131 -0
- sage/modular/modform/find_generators.py +59 -0
- sage/modular/modform/half_integral.py +154 -0
- sage/modular/modform/hecke_operator_on_qexp.py +247 -0
- sage/modular/modform/j_invariant.py +47 -0
- sage/modular/modform/l_series_gross_zagier.py +133 -0
- sage/modular/modform/l_series_gross_zagier_coeffs.cpython-314t-darwin.so +0 -0
- sage/modular/modform/l_series_gross_zagier_coeffs.pyx +177 -0
- sage/modular/modform/notes.py +45 -0
- sage/modular/modform/numerical.py +514 -0
- sage/modular/modform/periods.py +14 -0
- sage/modular/modform/ring.py +1257 -0
- sage/modular/modform/space.py +1860 -0
- sage/modular/modform/submodule.py +118 -0
- sage/modular/modform/tests.py +64 -0
- sage/modular/modform/theta.py +110 -0
- sage/modular/modform/vm_basis.py +381 -0
- sage/modular/modform/weight1.py +220 -0
- sage/modular/modform_hecketriangle/abstract_ring.py +1932 -0
- sage/modular/modform_hecketriangle/abstract_space.py +2528 -0
- sage/modular/modform_hecketriangle/all.py +30 -0
- sage/modular/modform_hecketriangle/analytic_type.py +590 -0
- sage/modular/modform_hecketriangle/constructor.py +416 -0
- sage/modular/modform_hecketriangle/element.py +351 -0
- sage/modular/modform_hecketriangle/functors.py +752 -0
- sage/modular/modform_hecketriangle/graded_ring.py +541 -0
- sage/modular/modform_hecketriangle/graded_ring_element.py +2225 -0
- sage/modular/modform_hecketriangle/hecke_triangle_group_element.py +3352 -0
- sage/modular/modform_hecketriangle/hecke_triangle_groups.py +1432 -0
- sage/modular/modform_hecketriangle/readme.py +1214 -0
- sage/modular/modform_hecketriangle/series_constructor.py +580 -0
- sage/modular/modform_hecketriangle/space.py +1037 -0
- sage/modular/modform_hecketriangle/subspace.py +423 -0
- sage/modular/modsym/all.py +17 -0
- sage/modular/modsym/ambient.py +3846 -0
- sage/modular/modsym/boundary.py +1420 -0
- sage/modular/modsym/element.py +336 -0
- sage/modular/modsym/g1list.py +178 -0
- sage/modular/modsym/ghlist.py +182 -0
- sage/modular/modsym/hecke_operator.py +73 -0
- sage/modular/modsym/manin_symbol.cpython-314t-darwin.so +0 -0
- sage/modular/modsym/manin_symbol.pxd +5 -0
- sage/modular/modsym/manin_symbol.pyx +497 -0
- sage/modular/modsym/manin_symbol_list.py +1295 -0
- sage/modular/modsym/modsym.py +400 -0
- sage/modular/modsym/modular_symbols.py +384 -0
- sage/modular/modsym/p1list.cpython-314t-darwin.so +0 -0
- sage/modular/modsym/p1list.pxd +29 -0
- sage/modular/modsym/p1list.pyx +1372 -0
- sage/modular/modsym/p1list_nf.py +1241 -0
- sage/modular/modsym/relation_matrix.py +591 -0
- sage/modular/modsym/relation_matrix_pyx.cpython-314t-darwin.so +0 -0
- sage/modular/modsym/relation_matrix_pyx.pyx +108 -0
- sage/modular/modsym/space.py +2468 -0
- sage/modular/modsym/subspace.py +455 -0
- sage/modular/modsym/tests.py +375 -0
- sage/modular/multiple_zeta.py +2632 -0
- sage/modular/multiple_zeta_F_algebra.py +786 -0
- sage/modular/overconvergent/all.py +6 -0
- sage/modular/overconvergent/genus0.py +1878 -0
- sage/modular/overconvergent/hecke_series.py +1187 -0
- sage/modular/overconvergent/weightspace.py +778 -0
- sage/modular/pollack_stevens/all.py +4 -0
- sage/modular/pollack_stevens/distributions.py +874 -0
- sage/modular/pollack_stevens/fund_domain.py +1572 -0
- sage/modular/pollack_stevens/manin_map.py +859 -0
- sage/modular/pollack_stevens/modsym.py +1593 -0
- sage/modular/pollack_stevens/padic_lseries.py +417 -0
- sage/modular/pollack_stevens/sigma0.py +534 -0
- sage/modular/pollack_stevens/space.py +1076 -0
- sage/modular/quasimodform/all.py +3 -0
- sage/modular/quasimodform/element.py +845 -0
- sage/modular/quasimodform/ring.py +828 -0
- sage/modular/quatalg/all.py +3 -0
- sage/modular/quatalg/brandt.py +1642 -0
- sage/modular/ssmod/all.py +8 -0
- sage/modular/ssmod/ssmod.py +827 -0
- sage/rings/all__sagemath_schemes.py +1 -0
- sage/rings/polynomial/all__sagemath_schemes.py +1 -0
- sage/rings/polynomial/binary_form_reduce.py +585 -0
- sage/schemes/all.py +41 -0
- sage/schemes/berkovich/all.py +6 -0
- sage/schemes/berkovich/berkovich_cp_element.py +2582 -0
- sage/schemes/berkovich/berkovich_space.py +748 -0
- sage/schemes/curves/affine_curve.py +2928 -0
- sage/schemes/curves/all.py +33 -0
- sage/schemes/curves/closed_point.py +434 -0
- sage/schemes/curves/constructor.py +381 -0
- sage/schemes/curves/curve.py +542 -0
- sage/schemes/curves/plane_curve_arrangement.py +1283 -0
- sage/schemes/curves/point.py +463 -0
- sage/schemes/curves/projective_curve.py +3026 -0
- sage/schemes/curves/zariski_vankampen.py +1932 -0
- sage/schemes/cyclic_covers/all.py +2 -0
- sage/schemes/cyclic_covers/charpoly_frobenius.py +320 -0
- sage/schemes/cyclic_covers/constructor.py +137 -0
- sage/schemes/cyclic_covers/cycliccover_finite_field.py +1309 -0
- sage/schemes/cyclic_covers/cycliccover_generic.py +310 -0
- sage/schemes/elliptic_curves/BSD.py +1036 -0
- sage/schemes/elliptic_curves/Qcurves.py +592 -0
- sage/schemes/elliptic_curves/addition_formulas_ring.py +94 -0
- sage/schemes/elliptic_curves/all.py +49 -0
- sage/schemes/elliptic_curves/cardinality.py +609 -0
- sage/schemes/elliptic_curves/cm.py +1102 -0
- sage/schemes/elliptic_curves/constructor.py +1552 -0
- sage/schemes/elliptic_curves/ec_database.py +175 -0
- sage/schemes/elliptic_curves/ell_curve_isogeny.py +3972 -0
- sage/schemes/elliptic_curves/ell_egros.py +459 -0
- sage/schemes/elliptic_curves/ell_field.py +2836 -0
- sage/schemes/elliptic_curves/ell_finite_field.py +3359 -0
- sage/schemes/elliptic_curves/ell_generic.py +3760 -0
- sage/schemes/elliptic_curves/ell_local_data.py +1207 -0
- sage/schemes/elliptic_curves/ell_modular_symbols.py +775 -0
- sage/schemes/elliptic_curves/ell_number_field.py +4220 -0
- sage/schemes/elliptic_curves/ell_padic_field.py +107 -0
- sage/schemes/elliptic_curves/ell_point.py +4787 -0
- sage/schemes/elliptic_curves/ell_rational_field.py +7368 -0
- sage/schemes/elliptic_curves/ell_tate_curve.py +671 -0
- sage/schemes/elliptic_curves/ell_torsion.py +436 -0
- sage/schemes/elliptic_curves/ell_wp.py +352 -0
- sage/schemes/elliptic_curves/formal_group.py +760 -0
- sage/schemes/elliptic_curves/gal_reps.py +1459 -0
- sage/schemes/elliptic_curves/gal_reps_number_field.py +1669 -0
- sage/schemes/elliptic_curves/gp_simon.py +152 -0
- sage/schemes/elliptic_curves/heegner.py +7335 -0
- sage/schemes/elliptic_curves/height.py +2109 -0
- sage/schemes/elliptic_curves/hom.py +1406 -0
- sage/schemes/elliptic_curves/hom_composite.py +934 -0
- sage/schemes/elliptic_curves/hom_frobenius.py +522 -0
- sage/schemes/elliptic_curves/hom_scalar.py +531 -0
- sage/schemes/elliptic_curves/hom_sum.py +682 -0
- sage/schemes/elliptic_curves/hom_velusqrt.py +1290 -0
- sage/schemes/elliptic_curves/homset.py +271 -0
- sage/schemes/elliptic_curves/isogeny_class.py +1521 -0
- sage/schemes/elliptic_curves/isogeny_small_degree.py +2797 -0
- sage/schemes/elliptic_curves/jacobian.py +237 -0
- sage/schemes/elliptic_curves/kodaira_symbol.py +344 -0
- sage/schemes/elliptic_curves/kraus.py +1014 -0
- sage/schemes/elliptic_curves/lseries_ell.py +943 -0
- sage/schemes/elliptic_curves/mod5family.py +105 -0
- sage/schemes/elliptic_curves/mod_poly.py +197 -0
- sage/schemes/elliptic_curves/mod_sym_num.cpython-314t-darwin.so +0 -0
- sage/schemes/elliptic_curves/mod_sym_num.pyx +3796 -0
- sage/schemes/elliptic_curves/modular_parametrization.py +305 -0
- sage/schemes/elliptic_curves/padic_lseries.py +1793 -0
- sage/schemes/elliptic_curves/padics.py +1816 -0
- sage/schemes/elliptic_curves/period_lattice.py +2234 -0
- sage/schemes/elliptic_curves/period_lattice_region.cpython-314t-darwin.so +0 -0
- sage/schemes/elliptic_curves/period_lattice_region.pyx +722 -0
- sage/schemes/elliptic_curves/saturation.py +715 -0
- sage/schemes/elliptic_curves/sha_tate.py +1158 -0
- sage/schemes/elliptic_curves/weierstrass_morphism.py +1117 -0
- sage/schemes/elliptic_curves/weierstrass_transform.py +200 -0
- sage/schemes/hyperelliptic_curves/all.py +6 -0
- sage/schemes/hyperelliptic_curves/constructor.py +291 -0
- sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py +1914 -0
- sage/schemes/hyperelliptic_curves/hyperelliptic_g2.py +192 -0
- sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py +954 -0
- sage/schemes/hyperelliptic_curves/hyperelliptic_padic_field.py +1332 -0
- sage/schemes/hyperelliptic_curves/hyperelliptic_rational_field.py +84 -0
- sage/schemes/hyperelliptic_curves/invariants.py +410 -0
- sage/schemes/hyperelliptic_curves/jacobian_endomorphism_utils.py +315 -0
- sage/schemes/hyperelliptic_curves/jacobian_g2.py +32 -0
- sage/schemes/hyperelliptic_curves/jacobian_generic.py +419 -0
- sage/schemes/hyperelliptic_curves/jacobian_homset.py +186 -0
- sage/schemes/hyperelliptic_curves/jacobian_morphism.py +875 -0
- sage/schemes/hyperelliptic_curves/kummer_surface.py +99 -0
- sage/schemes/hyperelliptic_curves/mestre.py +302 -0
- sage/schemes/hyperelliptic_curves/monsky_washnitzer.py +3871 -0
- sage/schemes/jacobians/abstract_jacobian.py +277 -0
- sage/schemes/jacobians/all.py +2 -0
- sage/schemes/overview.py +161 -0
- sage/schemes/plane_conics/all.py +22 -0
- sage/schemes/plane_conics/con_field.py +1296 -0
- sage/schemes/plane_conics/con_finite_field.py +158 -0
- sage/schemes/plane_conics/con_number_field.py +456 -0
- sage/schemes/plane_conics/con_rational_field.py +406 -0
- sage/schemes/plane_conics/con_rational_function_field.py +580 -0
- sage/schemes/plane_conics/constructor.py +249 -0
- sage/schemes/plane_quartics/all.py +2 -0
- sage/schemes/plane_quartics/quartic_constructor.py +71 -0
- sage/schemes/plane_quartics/quartic_generic.py +73 -0
- sage/schemes/riemann_surfaces/all.py +1 -0
- sage/schemes/riemann_surfaces/riemann_surface.py +4117 -0
- sage_wheels/share/cremona/cremona_mini.db +0 -0
- sage_wheels/share/ellcurves/rank0 +30427 -0
- sage_wheels/share/ellcurves/rank1 +31871 -0
- sage_wheels/share/ellcurves/rank10 +6 -0
- sage_wheels/share/ellcurves/rank11 +6 -0
- sage_wheels/share/ellcurves/rank12 +1 -0
- sage_wheels/share/ellcurves/rank14 +1 -0
- sage_wheels/share/ellcurves/rank15 +1 -0
- sage_wheels/share/ellcurves/rank17 +1 -0
- sage_wheels/share/ellcurves/rank19 +1 -0
- sage_wheels/share/ellcurves/rank2 +2388 -0
- sage_wheels/share/ellcurves/rank20 +1 -0
- sage_wheels/share/ellcurves/rank21 +1 -0
- sage_wheels/share/ellcurves/rank22 +1 -0
- sage_wheels/share/ellcurves/rank23 +1 -0
- sage_wheels/share/ellcurves/rank24 +1 -0
- sage_wheels/share/ellcurves/rank28 +1 -0
- sage_wheels/share/ellcurves/rank3 +836 -0
- sage_wheels/share/ellcurves/rank4 +10 -0
- sage_wheels/share/ellcurves/rank5 +5 -0
- sage_wheels/share/ellcurves/rank6 +5 -0
- sage_wheels/share/ellcurves/rank7 +5 -0
- sage_wheels/share/ellcurves/rank8 +6 -0
- sage_wheels/share/ellcurves/rank9 +7 -0
|
@@ -0,0 +1,1469 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-schemes
|
|
2
|
+
# sage.doctest: needs sage.libs.pari
|
|
3
|
+
r"""
|
|
4
|
+
Congruence subgroup `\Gamma_H(N)`
|
|
5
|
+
|
|
6
|
+
AUTHORS:
|
|
7
|
+
|
|
8
|
+
- Jordi Quer
|
|
9
|
+
- David Loeffler
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
################################################################################
|
|
13
|
+
#
|
|
14
|
+
# Copyright (C) 2009, The Sage Group -- http://www.sagemath.org/
|
|
15
|
+
#
|
|
16
|
+
# Distributed under the terms of the GNU General Public License (GPL)
|
|
17
|
+
#
|
|
18
|
+
# The full text of the GPL is available at:
|
|
19
|
+
#
|
|
20
|
+
# https://www.gnu.org/licenses/
|
|
21
|
+
#
|
|
22
|
+
################################################################################
|
|
23
|
+
|
|
24
|
+
from sage.arith.functions import lcm
|
|
25
|
+
from sage.arith.misc import euler_phi, gcd, divisors, get_inverse_mod, get_gcd, factor, xgcd
|
|
26
|
+
from sage.modular.modsym.p1list import lift_to_sl2z
|
|
27
|
+
from .congroup_generic import CongruenceSubgroup
|
|
28
|
+
from sage.modular.cusps import Cusp
|
|
29
|
+
from sage.misc.cachefunc import cached_method
|
|
30
|
+
from sage.rings.integer_ring import ZZ
|
|
31
|
+
from sage.rings.finite_rings.integer_mod_ring import Zmod
|
|
32
|
+
from sage.groups.matrix_gps.finitely_generated import MatrixGroup
|
|
33
|
+
from sage.matrix.constructor import matrix
|
|
34
|
+
from sage.structure.richcmp import richcmp_method, richcmp
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
_gammaH_cache = {}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def GammaH_constructor(level, H):
|
|
41
|
+
r"""
|
|
42
|
+
Return the congruence subgroup `\Gamma_H(N)`, which is the subgroup of
|
|
43
|
+
`SL_2(\ZZ)` consisting of matrices of the form `\begin{pmatrix} a & b \\
|
|
44
|
+
c & d \end{pmatrix}` with `N | c` and `a, d \in H`, for `H` a specified
|
|
45
|
+
subgroup of `(\ZZ/N\ZZ)^\times`.
|
|
46
|
+
|
|
47
|
+
INPUT:
|
|
48
|
+
|
|
49
|
+
- ``level`` -- integer
|
|
50
|
+
- ``H`` -- either 0, 1, or a list
|
|
51
|
+
* If H is a list, return `\Gamma_H(N)`, where `H`
|
|
52
|
+
is the subgroup of `(\ZZ/N\ZZ)^*` **generated** by the
|
|
53
|
+
elements of the list.
|
|
54
|
+
* If H = 0, returns `\Gamma_0(N)`.
|
|
55
|
+
* If H = 1, returns `\Gamma_1(N)`.
|
|
56
|
+
|
|
57
|
+
EXAMPLES::
|
|
58
|
+
|
|
59
|
+
sage: GammaH(11,0) # indirect doctest
|
|
60
|
+
Congruence Subgroup Gamma0(11)
|
|
61
|
+
sage: GammaH(11,1)
|
|
62
|
+
Congruence Subgroup Gamma1(11)
|
|
63
|
+
sage: GammaH(11,[10])
|
|
64
|
+
Congruence Subgroup Gamma_H(11) with H generated by [10]
|
|
65
|
+
sage: GammaH(11,[10,1])
|
|
66
|
+
Congruence Subgroup Gamma_H(11) with H generated by [10]
|
|
67
|
+
sage: GammaH(14,[10])
|
|
68
|
+
Traceback (most recent call last):
|
|
69
|
+
...
|
|
70
|
+
ArithmeticError: The generators [10] must be units modulo 14
|
|
71
|
+
"""
|
|
72
|
+
from .all import Gamma0, Gamma1, SL2Z
|
|
73
|
+
if level == 1:
|
|
74
|
+
return SL2Z
|
|
75
|
+
elif H == 0:
|
|
76
|
+
return Gamma0(level)
|
|
77
|
+
elif H == 1:
|
|
78
|
+
return Gamma1(level)
|
|
79
|
+
|
|
80
|
+
H = _normalize_H(H, level)
|
|
81
|
+
if H == []:
|
|
82
|
+
return Gamma1(level)
|
|
83
|
+
|
|
84
|
+
Hlist = _list_subgroup(level, H)
|
|
85
|
+
if len(Hlist) == euler_phi(level):
|
|
86
|
+
return Gamma0(level)
|
|
87
|
+
|
|
88
|
+
key = (level, tuple(H))
|
|
89
|
+
try:
|
|
90
|
+
return _gammaH_cache[key]
|
|
91
|
+
except KeyError:
|
|
92
|
+
_gammaH_cache[key] = GammaH_class(level, H, Hlist)
|
|
93
|
+
return _gammaH_cache[key]
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def is_GammaH(x):
|
|
97
|
+
"""
|
|
98
|
+
Return ``True`` if x is a congruence subgroup of type GammaH.
|
|
99
|
+
|
|
100
|
+
EXAMPLES::
|
|
101
|
+
|
|
102
|
+
sage: from sage.modular.arithgroup.all import is_GammaH
|
|
103
|
+
sage: is_GammaH(GammaH(13, [2]))
|
|
104
|
+
doctest:warning...
|
|
105
|
+
DeprecationWarning: The function is_GammaH is deprecated; use 'isinstance(..., GammaH_class)' instead.
|
|
106
|
+
See https://github.com/sagemath/sage/issues/38035 for details.
|
|
107
|
+
True
|
|
108
|
+
sage: is_GammaH(Gamma0(6))
|
|
109
|
+
True
|
|
110
|
+
sage: is_GammaH(Gamma1(6))
|
|
111
|
+
True
|
|
112
|
+
sage: is_GammaH(sage.modular.arithgroup.congroup_generic.CongruenceSubgroup(5))
|
|
113
|
+
False
|
|
114
|
+
"""
|
|
115
|
+
from sage.misc.superseded import deprecation
|
|
116
|
+
deprecation(38035, "The function is_GammaH is deprecated; use 'isinstance(..., GammaH_class)' instead.")
|
|
117
|
+
return isinstance(x, GammaH_class)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _normalize_H(H, level):
|
|
121
|
+
"""
|
|
122
|
+
Normalize representatives for a given subgroup H of the units
|
|
123
|
+
modulo level.
|
|
124
|
+
|
|
125
|
+
.. NOTE::
|
|
126
|
+
|
|
127
|
+
This function does *not* make any attempt to find a minimal
|
|
128
|
+
set of generators for H. It simply normalizes the inputs for use
|
|
129
|
+
in hashing.
|
|
130
|
+
|
|
131
|
+
EXAMPLES::
|
|
132
|
+
|
|
133
|
+
sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([23], 10)
|
|
134
|
+
[3]
|
|
135
|
+
sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([1,5], 7)
|
|
136
|
+
[3]
|
|
137
|
+
sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([4,18], 14)
|
|
138
|
+
Traceback (most recent call last):
|
|
139
|
+
...
|
|
140
|
+
ArithmeticError: The generators [4, 4] must be units modulo 14
|
|
141
|
+
sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([3,17], 14)
|
|
142
|
+
[3]
|
|
143
|
+
sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([-1,7,9], 10)
|
|
144
|
+
[3, 9]
|
|
145
|
+
|
|
146
|
+
TESTS::
|
|
147
|
+
|
|
148
|
+
sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([4, 16], 21)
|
|
149
|
+
[4]
|
|
150
|
+
"""
|
|
151
|
+
H = [ZZ(h) % level for h in H]
|
|
152
|
+
for h in H:
|
|
153
|
+
if gcd(h, level) > 1:
|
|
154
|
+
raise ArithmeticError('The generators %s must be units modulo %s' % (H, level))
|
|
155
|
+
H = {u for u in H if u > 1}
|
|
156
|
+
final_H = set()
|
|
157
|
+
for h in H:
|
|
158
|
+
inv_h = h.inverse_mod(level)
|
|
159
|
+
if inv_h <= h:
|
|
160
|
+
final_H.add(inv_h)
|
|
161
|
+
else:
|
|
162
|
+
final_H.add(h)
|
|
163
|
+
return sorted(final_H)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@richcmp_method
|
|
167
|
+
class GammaH_class(CongruenceSubgroup):
|
|
168
|
+
r"""
|
|
169
|
+
The congruence subgroup `\Gamma_H(N)` for some subgroup `H \trianglelefteq
|
|
170
|
+
(\ZZ / N\ZZ)^\times`, which is the subgroup of `\SL_2(\ZZ)` consisting of
|
|
171
|
+
matrices of the form `\begin{pmatrix} a &
|
|
172
|
+
b \\ c & d \end{pmatrix}` with `N \mid c` and `a, d \in H`.
|
|
173
|
+
|
|
174
|
+
TESTS:
|
|
175
|
+
|
|
176
|
+
We test calculation of various invariants of the group::
|
|
177
|
+
|
|
178
|
+
sage: GammaH(33,[2]).projective_index()
|
|
179
|
+
96
|
|
180
|
+
sage: GammaH(33,[2]).genus()
|
|
181
|
+
5
|
|
182
|
+
sage: GammaH(7,[2]).genus()
|
|
183
|
+
0
|
|
184
|
+
sage: GammaH(23, [1..22]).genus()
|
|
185
|
+
2
|
|
186
|
+
sage: Gamma0(23).genus()
|
|
187
|
+
2
|
|
188
|
+
sage: GammaH(23, [1]).genus()
|
|
189
|
+
12
|
|
190
|
+
sage: Gamma1(23).genus()
|
|
191
|
+
12
|
|
192
|
+
|
|
193
|
+
We calculate the dimensions of some modular forms spaces::
|
|
194
|
+
|
|
195
|
+
sage: GammaH(33,[2]).dimension_cusp_forms(2)
|
|
196
|
+
5
|
|
197
|
+
sage: GammaH(33,[2]).dimension_cusp_forms(3)
|
|
198
|
+
0
|
|
199
|
+
sage: GammaH(33,[2,5]).dimension_cusp_forms(2)
|
|
200
|
+
3
|
|
201
|
+
sage: GammaH(32079, [21676]).dimension_cusp_forms(20)
|
|
202
|
+
180266112
|
|
203
|
+
|
|
204
|
+
An example in weight 1::
|
|
205
|
+
|
|
206
|
+
sage: GammaH(20, [9]).dimension_cusp_forms(1)
|
|
207
|
+
0
|
|
208
|
+
"""
|
|
209
|
+
|
|
210
|
+
def __init__(self, level, H, Hlist=None):
|
|
211
|
+
r"""
|
|
212
|
+
The congruence subgroup `\Gamma_H(N)`.
|
|
213
|
+
|
|
214
|
+
The subgroup `H` must be given as a list.
|
|
215
|
+
|
|
216
|
+
EXAMPLES::
|
|
217
|
+
|
|
218
|
+
sage: GammaH(117, [4])
|
|
219
|
+
Congruence Subgroup Gamma_H(117) with H generated by [4]
|
|
220
|
+
sage: G = GammaH(16, [7])
|
|
221
|
+
sage: TestSuite(G).run()
|
|
222
|
+
sage: G is loads(dumps(G))
|
|
223
|
+
True
|
|
224
|
+
"""
|
|
225
|
+
CongruenceSubgroup.__init__(self, level)
|
|
226
|
+
self.__H = H
|
|
227
|
+
if Hlist is None:
|
|
228
|
+
Hlist = _list_subgroup(level, H)
|
|
229
|
+
self.__Hlist = Hlist
|
|
230
|
+
|
|
231
|
+
def restrict(self, M):
|
|
232
|
+
r"""
|
|
233
|
+
Return the subgroup of `\Gamma_0(M)`, for `M` a divisor of `N`,
|
|
234
|
+
obtained by taking the image of this group under reduction modulo `N`.
|
|
235
|
+
|
|
236
|
+
EXAMPLES::
|
|
237
|
+
|
|
238
|
+
sage: G = GammaH(33,[2])
|
|
239
|
+
sage: G.restrict(11)
|
|
240
|
+
Congruence Subgroup Gamma0(11)
|
|
241
|
+
sage: G.restrict(1)
|
|
242
|
+
Modular Group SL(2,Z)
|
|
243
|
+
sage: G.restrict(15)
|
|
244
|
+
Traceback (most recent call last):
|
|
245
|
+
...
|
|
246
|
+
ValueError: M (=15) must be a divisor of the level (33) of self
|
|
247
|
+
"""
|
|
248
|
+
M = ZZ(M)
|
|
249
|
+
if self.level() % M:
|
|
250
|
+
raise ValueError("M (=%s) must be a divisor of the level (%s) of self" % (M, self.level()))
|
|
251
|
+
return self._new_group_from_level(M)
|
|
252
|
+
|
|
253
|
+
def extend(self, M):
|
|
254
|
+
r"""
|
|
255
|
+
Return the subgroup of `\Gamma_0(M)`, for `M` a multiple of `N`,
|
|
256
|
+
obtained by taking the preimage of this group under the reduction map;
|
|
257
|
+
in other words, the intersection of this group with `\Gamma_0(M)`.
|
|
258
|
+
|
|
259
|
+
EXAMPLES::
|
|
260
|
+
|
|
261
|
+
sage: G = GammaH(33, [2])
|
|
262
|
+
sage: G.extend(99)
|
|
263
|
+
Congruence Subgroup Gamma_H(99) with H generated by [2, 17, 68]
|
|
264
|
+
sage: G.extend(11)
|
|
265
|
+
Traceback (most recent call last):
|
|
266
|
+
...
|
|
267
|
+
ValueError: M (=11) must be a multiple of the level (33) of self
|
|
268
|
+
"""
|
|
269
|
+
M = ZZ(M)
|
|
270
|
+
if M % self.level():
|
|
271
|
+
raise ValueError("M (=%s) must be a multiple of the level (%s) of self" % (M, self.level()))
|
|
272
|
+
return self._new_group_from_level(M)
|
|
273
|
+
|
|
274
|
+
def __reduce__(self):
|
|
275
|
+
"""
|
|
276
|
+
Used for pickling ``self``.
|
|
277
|
+
|
|
278
|
+
EXAMPLES::
|
|
279
|
+
|
|
280
|
+
sage: GammaH(92,[45,47]).__reduce__()
|
|
281
|
+
(<function GammaH_constructor at ...>, (92, [45, 47]))
|
|
282
|
+
"""
|
|
283
|
+
return GammaH_constructor, (self.level(), self.__H)
|
|
284
|
+
|
|
285
|
+
def divisor_subgroups(self):
|
|
286
|
+
r"""
|
|
287
|
+
Given this congruence subgroup `\Gamma_H(N)`, return all
|
|
288
|
+
subgroups `\Gamma_G(M)` for `M` a divisor of `N` and such that
|
|
289
|
+
`G` is equal to the image of `H` modulo `M`.
|
|
290
|
+
|
|
291
|
+
EXAMPLES::
|
|
292
|
+
|
|
293
|
+
sage: G = GammaH(33,[2]); G
|
|
294
|
+
Congruence Subgroup Gamma_H(33) with H generated by [2]
|
|
295
|
+
sage: G._list_of_elements_in_H()
|
|
296
|
+
[1, 2, 4, 8, 16, 17, 25, 29, 31, 32]
|
|
297
|
+
sage: G.divisor_subgroups()
|
|
298
|
+
[Modular Group SL(2,Z),
|
|
299
|
+
Congruence Subgroup Gamma0(3),
|
|
300
|
+
Congruence Subgroup Gamma0(11),
|
|
301
|
+
Congruence Subgroup Gamma_H(33) with H generated by [2]]
|
|
302
|
+
"""
|
|
303
|
+
v = self.__H
|
|
304
|
+
ans = []
|
|
305
|
+
for M in self.level().divisors():
|
|
306
|
+
w = [a % M for a in v if a % M]
|
|
307
|
+
ans.append(GammaH_constructor(M, w))
|
|
308
|
+
return ans
|
|
309
|
+
|
|
310
|
+
def to_even_subgroup(self):
|
|
311
|
+
r"""
|
|
312
|
+
Return the smallest even subgroup of `SL(2, \ZZ)` containing ``self``.
|
|
313
|
+
|
|
314
|
+
EXAMPLES::
|
|
315
|
+
|
|
316
|
+
sage: GammaH(11, [4]).to_even_subgroup()
|
|
317
|
+
Congruence Subgroup Gamma0(11)
|
|
318
|
+
sage: Gamma1(11).to_even_subgroup()
|
|
319
|
+
Congruence Subgroup Gamma_H(11) with H generated by [10]
|
|
320
|
+
"""
|
|
321
|
+
if self.is_even():
|
|
322
|
+
return self
|
|
323
|
+
else:
|
|
324
|
+
return GammaH_constructor(self.level(), self._generators_for_H() + [-1])
|
|
325
|
+
|
|
326
|
+
def __richcmp__(self, other, op):
|
|
327
|
+
"""
|
|
328
|
+
Compare ``self`` to ``other``.
|
|
329
|
+
|
|
330
|
+
The ordering on congruence subgroups of the form GammaH(N) for some H
|
|
331
|
+
is first by level, then by the order of H, then lexicographically by H.
|
|
332
|
+
In particular, this means that we have Gamma1(N) < GammaH(N) <
|
|
333
|
+
Gamma0(N) for every nontrivial proper subgroup H.
|
|
334
|
+
|
|
335
|
+
EXAMPLES::
|
|
336
|
+
|
|
337
|
+
sage: G = GammaH(86, [9])
|
|
338
|
+
sage: G == G
|
|
339
|
+
True
|
|
340
|
+
sage: G != GammaH(86, [11])
|
|
341
|
+
True
|
|
342
|
+
sage: Gamma1(11) < Gamma0(11)
|
|
343
|
+
True
|
|
344
|
+
sage: Gamma1(11) == GammaH(11, [])
|
|
345
|
+
True
|
|
346
|
+
sage: Gamma0(11) == GammaH(11, [2])
|
|
347
|
+
True
|
|
348
|
+
sage: G = Gamma0(86)
|
|
349
|
+
sage: G == G
|
|
350
|
+
True
|
|
351
|
+
sage: G != GammaH(86, [11])
|
|
352
|
+
True
|
|
353
|
+
sage: Gamma1(17) < Gamma0(17)
|
|
354
|
+
True
|
|
355
|
+
sage: Gamma0(1) == SL2Z
|
|
356
|
+
True
|
|
357
|
+
sage: Gamma0(2) == Gamma1(2)
|
|
358
|
+
True
|
|
359
|
+
|
|
360
|
+
sage: [x._list_of_elements_in_H() for x in sorted(Gamma0(24).gamma_h_subgroups())] # optional - gap_package_polycyclic
|
|
361
|
+
[[1],
|
|
362
|
+
[1, 5],
|
|
363
|
+
[1, 7],
|
|
364
|
+
[1, 11],
|
|
365
|
+
[1, 13],
|
|
366
|
+
[1, 17],
|
|
367
|
+
[1, 19],
|
|
368
|
+
[1, 23],
|
|
369
|
+
[1, 5, 7, 11],
|
|
370
|
+
[1, 5, 13, 17],
|
|
371
|
+
[1, 5, 19, 23],
|
|
372
|
+
[1, 7, 13, 19],
|
|
373
|
+
[1, 7, 17, 23],
|
|
374
|
+
[1, 11, 13, 23],
|
|
375
|
+
[1, 11, 17, 19],
|
|
376
|
+
[1, 5, 7, 11, 13, 17, 19, 23]]
|
|
377
|
+
"""
|
|
378
|
+
if isinstance(other, GammaH_class):
|
|
379
|
+
return richcmp((self.level(), -self.index(),
|
|
380
|
+
self._list_of_elements_in_H()),
|
|
381
|
+
(other.level(), -other.index(),
|
|
382
|
+
other._list_of_elements_in_H()), op)
|
|
383
|
+
else:
|
|
384
|
+
return NotImplemented
|
|
385
|
+
|
|
386
|
+
def _generators_for_H(self):
|
|
387
|
+
"""
|
|
388
|
+
Return generators for the subgroup `H` of the units mod
|
|
389
|
+
``self.level()`` that defines ``self``.
|
|
390
|
+
|
|
391
|
+
EXAMPLES::
|
|
392
|
+
|
|
393
|
+
sage: GammaH(17,[4])._generators_for_H()
|
|
394
|
+
[4]
|
|
395
|
+
sage: GammaH(12,[-1])._generators_for_H()
|
|
396
|
+
[11]
|
|
397
|
+
"""
|
|
398
|
+
return self.__H
|
|
399
|
+
|
|
400
|
+
def _repr_(self):
|
|
401
|
+
"""
|
|
402
|
+
Return the string representation of ``self``.
|
|
403
|
+
|
|
404
|
+
EXAMPLES::
|
|
405
|
+
|
|
406
|
+
sage: GammaH(123, [55])._repr_()
|
|
407
|
+
'Congruence Subgroup Gamma_H(123) with H generated by [55]'
|
|
408
|
+
"""
|
|
409
|
+
return "Congruence Subgroup Gamma_H(%s) with H generated by %s" % (self.level(), self.__H)
|
|
410
|
+
|
|
411
|
+
def _latex_(self):
|
|
412
|
+
r"""
|
|
413
|
+
Return the \LaTeX representation of ``self``.
|
|
414
|
+
|
|
415
|
+
EXAMPLES::
|
|
416
|
+
|
|
417
|
+
sage: GammaH(5,[4])._latex_()
|
|
418
|
+
'\\Gamma_H(5, [4])'
|
|
419
|
+
"""
|
|
420
|
+
return '\\Gamma_H(%s, %s)' % (self.level(), self.__H)
|
|
421
|
+
|
|
422
|
+
def _list_of_elements_in_H(self):
|
|
423
|
+
"""
|
|
424
|
+
Return a sorted list of Python ints that are representatives
|
|
425
|
+
between 1 and N-1 of the elements of H.
|
|
426
|
+
|
|
427
|
+
WARNING: Do not change this returned list.
|
|
428
|
+
|
|
429
|
+
EXAMPLES::
|
|
430
|
+
|
|
431
|
+
sage: G = GammaH(11,[3]); G
|
|
432
|
+
Congruence Subgroup Gamma_H(11) with H generated by [3]
|
|
433
|
+
sage: G._list_of_elements_in_H()
|
|
434
|
+
[1, 3, 4, 5, 9]
|
|
435
|
+
"""
|
|
436
|
+
return self.__Hlist
|
|
437
|
+
|
|
438
|
+
def is_even(self) -> bool:
|
|
439
|
+
"""
|
|
440
|
+
Return ``True`` precisely if this subgroup contains the matrix -1.
|
|
441
|
+
|
|
442
|
+
EXAMPLES::
|
|
443
|
+
|
|
444
|
+
sage: GammaH(10, [3]).is_even()
|
|
445
|
+
True
|
|
446
|
+
sage: GammaH(14, [1]).is_even()
|
|
447
|
+
False
|
|
448
|
+
"""
|
|
449
|
+
if self.level() == 1:
|
|
450
|
+
return True
|
|
451
|
+
v = self._list_of_elements_in_H()
|
|
452
|
+
return int(self.level() - 1) in v
|
|
453
|
+
|
|
454
|
+
@cached_method
|
|
455
|
+
def generators(self, algorithm='farey'):
|
|
456
|
+
r"""
|
|
457
|
+
Return generators for this congruence subgroup. The result is cached.
|
|
458
|
+
|
|
459
|
+
INPUT:
|
|
460
|
+
|
|
461
|
+
- ``algorithm`` -- string; either ``'farey'`` (default) or
|
|
462
|
+
``todd-coxeter``
|
|
463
|
+
|
|
464
|
+
If ``algorithm`` is set to ``'farey'``, then the generators will be
|
|
465
|
+
calculated using Farey symbols, which will always return a *minimal*
|
|
466
|
+
generating set. See :mod:`~sage.modular.arithgroup.farey_symbol` for
|
|
467
|
+
more information.
|
|
468
|
+
|
|
469
|
+
If ``algorithm`` is set to ``'todd-coxeter'``, a simpler algorithm
|
|
470
|
+
based on Todd-Coxeter enumeration will be used. This tends to return
|
|
471
|
+
far larger sets of generators.
|
|
472
|
+
|
|
473
|
+
EXAMPLES::
|
|
474
|
+
|
|
475
|
+
sage: GammaH(7, [2]).generators()
|
|
476
|
+
[
|
|
477
|
+
[1 1] [ 2 -1] [ 4 -3]
|
|
478
|
+
[0 1], [ 7 -3], [ 7 -5]
|
|
479
|
+
]
|
|
480
|
+
sage: GammaH(7, [2]).generators(algorithm='todd-coxeter')
|
|
481
|
+
[
|
|
482
|
+
[1 1] [-13 4] [ 15 4] [-3 -1] [ 1 -1] [1 0] [1 1] [-3 -1]
|
|
483
|
+
[0 1], [ 42 -13], [-49 -13], [ 7 2], [ 0 1], [7 1], [0 1], [ 7 2],
|
|
484
|
+
<BLANKLINE>
|
|
485
|
+
[-13 4] [-5 -1] [-5 -2] [-10 3] [ 1 0] [ 2 -1] [1 0]
|
|
486
|
+
[ 42 -13], [21 4], [28 11], [ 63 -19], [-7 1], [ 7 -3], [7 1],
|
|
487
|
+
<BLANKLINE>
|
|
488
|
+
[-3 -1] [ 15 -4] [ 2 -1] [-5 1] [ 8 -3] [11 5] [-13 -4]
|
|
489
|
+
[ 7 2], [ 49 -13], [ 7 -3], [14 -3], [-21 8], [35 16], [-42 -13]
|
|
490
|
+
]
|
|
491
|
+
"""
|
|
492
|
+
if algorithm == "farey":
|
|
493
|
+
return self.farey_symbol().generators()
|
|
494
|
+
elif algorithm == "todd-coxeter":
|
|
495
|
+
from sage.modular.modsym.ghlist import GHlist
|
|
496
|
+
from .congroup import generators_helper
|
|
497
|
+
level = self.level()
|
|
498
|
+
gen_list = generators_helper(GHlist(self), level)
|
|
499
|
+
return [self(g, check=False) for g in gen_list]
|
|
500
|
+
else:
|
|
501
|
+
raise ValueError("Unknown algorithm '%s' (should be either 'farey' or 'todd-coxeter')" % algorithm)
|
|
502
|
+
|
|
503
|
+
def _coset_reduction_data_first_coord(self):
|
|
504
|
+
"""
|
|
505
|
+
Compute data used for determining the canonical coset
|
|
506
|
+
representative of an element of SL_2(Z) modulo ``self``.
|
|
507
|
+
|
|
508
|
+
This function specifically returns data needed for the first
|
|
509
|
+
part of the reduction step (the first coordinate).
|
|
510
|
+
|
|
511
|
+
INPUT:
|
|
512
|
+
|
|
513
|
+
- ``self`` -- a congruence subgroup Gamma_0(N), Gamma_1(N), or Gamma_H(N)
|
|
514
|
+
|
|
515
|
+
OUTPUT: list v such that
|
|
516
|
+
|
|
517
|
+
v[u] = (min(u*h: h in H),
|
|
518
|
+
gcd(u,N) ,
|
|
519
|
+
an h such that h*u = min(u*h: h in H)).
|
|
520
|
+
|
|
521
|
+
EXAMPLES::
|
|
522
|
+
|
|
523
|
+
sage: G = Gamma0(12)
|
|
524
|
+
sage: G._coset_reduction_data_first_coord()
|
|
525
|
+
[(0, 12, 0), (1, 1, 1), (2, 2, 1), (3, 3, 1), (4, 4, 1), (1, 1, 5), (6, 6, 1),
|
|
526
|
+
(1, 1, 7), (4, 4, 5), (3, 3, 7), (2, 2, 5), (1, 1, 11)]
|
|
527
|
+
"""
|
|
528
|
+
H = [int(x) for x in self._list_of_elements_in_H()]
|
|
529
|
+
N = int(self.level())
|
|
530
|
+
|
|
531
|
+
# Get some useful fast functions for inverse and gcd
|
|
532
|
+
inverse_mod = get_inverse_mod(N) # optimal inverse function
|
|
533
|
+
gcd = get_gcd(N) # optimal gcd function
|
|
534
|
+
|
|
535
|
+
# We will be filling this list in below.
|
|
536
|
+
reduct_data = [0] * N
|
|
537
|
+
|
|
538
|
+
# We can fill in 0 and all elements of H immediately
|
|
539
|
+
reduct_data[0] = (0, N, 0)
|
|
540
|
+
for u in H:
|
|
541
|
+
reduct_data[u] = (1, 1, inverse_mod(u, N))
|
|
542
|
+
|
|
543
|
+
# Make a table of the reduction of H (mod N/d), one for each
|
|
544
|
+
# divisor d.
|
|
545
|
+
repr_H_mod_N_over_d = {}
|
|
546
|
+
for d in divisors(N):
|
|
547
|
+
# We special-case N == d because in this case,
|
|
548
|
+
# 1 % N_over_d is 0
|
|
549
|
+
if N == d:
|
|
550
|
+
repr_H_mod_N_over_d[d] = [1]
|
|
551
|
+
break
|
|
552
|
+
N_over_d = N // d
|
|
553
|
+
# For each element of H, we look at its image mod
|
|
554
|
+
# N_over_d. If we haven't yet seen it, add it on to
|
|
555
|
+
# the end of z.
|
|
556
|
+
w = [0] * N_over_d
|
|
557
|
+
z = [1]
|
|
558
|
+
for x in H:
|
|
559
|
+
val = x % N_over_d
|
|
560
|
+
if not w[val]:
|
|
561
|
+
w[val] = 1
|
|
562
|
+
z.append(x)
|
|
563
|
+
repr_H_mod_N_over_d[d] = z
|
|
564
|
+
|
|
565
|
+
# Compute the rest of the tuples. The values left to process
|
|
566
|
+
# are those where reduct_data has a 0. Note that several of
|
|
567
|
+
# these values are processed on each loop below, so re-index
|
|
568
|
+
# each time.
|
|
569
|
+
while True:
|
|
570
|
+
try:
|
|
571
|
+
u = reduct_data.index(0)
|
|
572
|
+
except ValueError:
|
|
573
|
+
break
|
|
574
|
+
d = gcd(u, N)
|
|
575
|
+
for x in repr_H_mod_N_over_d[d]:
|
|
576
|
+
reduct_data[(u * x) % N] = (u, d, inverse_mod(x, N))
|
|
577
|
+
|
|
578
|
+
return reduct_data
|
|
579
|
+
|
|
580
|
+
def _coset_reduction_data_second_coord(self):
|
|
581
|
+
"""
|
|
582
|
+
Compute data used for determining the canonical coset
|
|
583
|
+
representative of an element of SL_2(Z) modulo ``self``.
|
|
584
|
+
|
|
585
|
+
This function specifically returns data needed for the second
|
|
586
|
+
part of the reduction step (the second coordinate).
|
|
587
|
+
|
|
588
|
+
OUTPUT:
|
|
589
|
+
|
|
590
|
+
A dictionary v with keys the divisors of N such that v[d]
|
|
591
|
+
is the subgroup {h in H : h = 1 (mod N/d)}.
|
|
592
|
+
|
|
593
|
+
EXAMPLES::
|
|
594
|
+
|
|
595
|
+
sage: G = GammaH(240,[7,239])
|
|
596
|
+
sage: G._coset_reduction_data_second_coord()
|
|
597
|
+
{1: [1],
|
|
598
|
+
2: [1],
|
|
599
|
+
3: [1],
|
|
600
|
+
4: [1],
|
|
601
|
+
5: [1, 49],
|
|
602
|
+
6: [1],
|
|
603
|
+
8: [1],
|
|
604
|
+
10: [1, 49],
|
|
605
|
+
12: [1],
|
|
606
|
+
15: [1, 49],
|
|
607
|
+
16: [1],
|
|
608
|
+
20: [1, 49],
|
|
609
|
+
24: [1, 191],
|
|
610
|
+
30: [1, 49, 137, 233],
|
|
611
|
+
40: [1, 7, 49, 103],
|
|
612
|
+
48: [1, 191],
|
|
613
|
+
60: [1, 49, 137, 233],
|
|
614
|
+
80: [1, 7, 49, 103],
|
|
615
|
+
120: [1, 7, 49, 103, 137, 191, 233, 239],
|
|
616
|
+
240: [1, 7, 49, 103, 137, 191, 233, 239]}
|
|
617
|
+
sage: G = GammaH(1200,[-1,7]); G
|
|
618
|
+
Congruence Subgroup Gamma_H(1200) with H generated by [7, 1199]
|
|
619
|
+
sage: K = sorted(G._coset_reduction_data_second_coord())
|
|
620
|
+
sage: K == divisors(1200)
|
|
621
|
+
True
|
|
622
|
+
"""
|
|
623
|
+
H = self._list_of_elements_in_H()
|
|
624
|
+
N = self.level()
|
|
625
|
+
v = {1: [1], N: H}
|
|
626
|
+
for d in divisors(N):
|
|
627
|
+
if 1 < d < N:
|
|
628
|
+
N_over_d = N // d
|
|
629
|
+
v[d] = [x for x in H if x % N_over_d == 1]
|
|
630
|
+
return v
|
|
631
|
+
|
|
632
|
+
@cached_method
|
|
633
|
+
def _coset_reduction_data(self):
|
|
634
|
+
"""
|
|
635
|
+
Compute data used for determining the canonical coset
|
|
636
|
+
representative of an element of SL_2(Z) modulo ``self``.
|
|
637
|
+
|
|
638
|
+
EXAMPLES::
|
|
639
|
+
|
|
640
|
+
sage: G = GammaH(13, [-1]); G
|
|
641
|
+
Congruence Subgroup Gamma_H(13) with H generated by [12]
|
|
642
|
+
sage: G._coset_reduction_data()
|
|
643
|
+
([(0, 13, 0), (1, 1, 1), (2, 1, 1), (3, 1, 1), (4, 1, 1), (5, 1, 1), (6, 1, 1), (6, 1, 12), (5, 1, 12), (4, 1, 12), (3, 1, 12), (2, 1, 12), (1, 1, 12)], {1: [1], 13: [1, 12]})
|
|
644
|
+
"""
|
|
645
|
+
return (self._coset_reduction_data_first_coord(),
|
|
646
|
+
self._coset_reduction_data_second_coord())
|
|
647
|
+
|
|
648
|
+
def _reduce_coset(self, uu, vv):
|
|
649
|
+
r"""
|
|
650
|
+
Compute a canonical form for a given Manin symbol.
|
|
651
|
+
|
|
652
|
+
INPUT:
|
|
653
|
+
|
|
654
|
+
Two integers (uu,vv) that define an element of `(Z/NZ)^2`.
|
|
655
|
+
|
|
656
|
+
- ``uu`` -- integer
|
|
657
|
+
- ``vv`` -- integer
|
|
658
|
+
|
|
659
|
+
OUTPUT: pair of integers that are equivalent to (uu,vv)
|
|
660
|
+
|
|
661
|
+
.. NOTE::
|
|
662
|
+
|
|
663
|
+
We do *not* require that gcd(uu,vv,N) = 1. If the gcd is
|
|
664
|
+
not 1, we return (0,0).
|
|
665
|
+
|
|
666
|
+
EXAMPLES:
|
|
667
|
+
|
|
668
|
+
An example at level 9::
|
|
669
|
+
|
|
670
|
+
sage: G = GammaH(9,[4]); G
|
|
671
|
+
Congruence Subgroup Gamma_H(9) with H generated by [4]
|
|
672
|
+
sage: a = []
|
|
673
|
+
sage: for i in range(G.level()):
|
|
674
|
+
....: for j in range(G.level()):
|
|
675
|
+
....: a.append(G._reduce_coset(i,j))
|
|
676
|
+
sage: v = list(set(a))
|
|
677
|
+
sage: v.sort()
|
|
678
|
+
sage: v
|
|
679
|
+
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6), (1, 7), (1, 8), (2, 0), (2, 1), (2, 2), (2, 3), (2, 4), (2, 5), (2, 6), (2, 7), (2, 8), (3, 1), (3, 2), (6, 1), (6, 2)]
|
|
680
|
+
|
|
681
|
+
An example at level 100::
|
|
682
|
+
|
|
683
|
+
sage: G = GammaH(100,[3,7]); G
|
|
684
|
+
Congruence Subgroup Gamma_H(100) with H generated by [3, 7]
|
|
685
|
+
sage: a = []
|
|
686
|
+
sage: for i in range(G.level()):
|
|
687
|
+
....: for j in range(G.level()):
|
|
688
|
+
....: a.append(G._reduce_coset(i,j))
|
|
689
|
+
sage: v = list(set(a))
|
|
690
|
+
sage: v.sort()
|
|
691
|
+
sage: len(v)
|
|
692
|
+
361
|
|
693
|
+
|
|
694
|
+
This demonstrates the problem underlying :issue:`1220`::
|
|
695
|
+
|
|
696
|
+
sage: G = GammaH(99, [67])
|
|
697
|
+
sage: G._reduce_coset(11,-3)
|
|
698
|
+
(11, 96)
|
|
699
|
+
sage: G._reduce_coset(77, -3)
|
|
700
|
+
(11, 96)
|
|
701
|
+
"""
|
|
702
|
+
N = int(self.level())
|
|
703
|
+
u = uu % N
|
|
704
|
+
v = vv % N
|
|
705
|
+
first, second = self._coset_reduction_data()
|
|
706
|
+
|
|
707
|
+
if gcd(first[u][1], first[v][1]) != 1:
|
|
708
|
+
return (0, 0)
|
|
709
|
+
if not u:
|
|
710
|
+
return (0, first[v][0])
|
|
711
|
+
if not v:
|
|
712
|
+
return (first[u][0], 0)
|
|
713
|
+
|
|
714
|
+
new_u = first[u][0]
|
|
715
|
+
d = first[u][1]
|
|
716
|
+
new_v = (first[u][2] * v) % N
|
|
717
|
+
H_ls = second[d]
|
|
718
|
+
if len(H_ls) > 1:
|
|
719
|
+
new_v = min((new_v * h) % N for h in H_ls)
|
|
720
|
+
|
|
721
|
+
return (new_u, new_v)
|
|
722
|
+
|
|
723
|
+
def reduce_cusp(self, c):
|
|
724
|
+
r"""
|
|
725
|
+
Compute a minimal representative for the given cusp c. Returns
|
|
726
|
+
a cusp c' which is equivalent to the given cusp, and is in
|
|
727
|
+
lowest terms with minimal positive denominator, and minimal
|
|
728
|
+
positive numerator for that denominator.
|
|
729
|
+
|
|
730
|
+
Two cusps `u_1/v_1` and `u_2/v_2` are equivalent modulo `\Gamma_H(N)`
|
|
731
|
+
if and only if
|
|
732
|
+
|
|
733
|
+
.. MATH::
|
|
734
|
+
|
|
735
|
+
v_1 = h v_2 \bmod N\quad \text{and}\quad u_1 = h^{-1} u_2 \bmod {\rm gcd}(v_1,N)
|
|
736
|
+
|
|
737
|
+
or
|
|
738
|
+
|
|
739
|
+
.. MATH::
|
|
740
|
+
|
|
741
|
+
v_1 = -h v_2 \bmod N\quad \text{and}\quad u_1 = -h^{-1} u_2 \bmod {\rm gcd}(v_1,N)
|
|
742
|
+
|
|
743
|
+
for some `h \in H`.
|
|
744
|
+
|
|
745
|
+
EXAMPLES::
|
|
746
|
+
|
|
747
|
+
sage: GammaH(6,[5]).reduce_cusp(5/3)
|
|
748
|
+
1/3
|
|
749
|
+
sage: GammaH(12,[5]).reduce_cusp(Cusp(8,9))
|
|
750
|
+
1/3
|
|
751
|
+
sage: GammaH(12,[5]).reduce_cusp(5/12)
|
|
752
|
+
Infinity
|
|
753
|
+
sage: GammaH(12,[]).reduce_cusp(Cusp(5,12))
|
|
754
|
+
5/12
|
|
755
|
+
sage: GammaH(21,[5]).reduce_cusp(Cusp(-9/14))
|
|
756
|
+
1/7
|
|
757
|
+
sage: Gamma1(5).reduce_cusp(oo)
|
|
758
|
+
Infinity
|
|
759
|
+
sage: Gamma1(5).reduce_cusp(0)
|
|
760
|
+
0
|
|
761
|
+
"""
|
|
762
|
+
return self._reduce_cusp(c)[0]
|
|
763
|
+
|
|
764
|
+
def _reduce_cusp(self, c):
|
|
765
|
+
r"""
|
|
766
|
+
Compute a minimal representative for the given cusp c.
|
|
767
|
+
|
|
768
|
+
Returns a pair (c', t), where c' is the minimal representative
|
|
769
|
+
for the given cusp, and t is either 1 or -1, as explained
|
|
770
|
+
below. Largely for internal use.
|
|
771
|
+
|
|
772
|
+
The minimal representative for a cusp is the element in `P^1(Q)`
|
|
773
|
+
in lowest terms with minimal positive denominator, and minimal
|
|
774
|
+
positive numerator for that denominator.
|
|
775
|
+
|
|
776
|
+
Two cusps `u1/v1` and `u2/v2` are equivalent modulo `\Gamma_H(N)`
|
|
777
|
+
if and only if
|
|
778
|
+
|
|
779
|
+
- `v1 = h*v2 (mod N)` and `u1 = h^(-1)*u2 (mod gcd(v1,N))`
|
|
780
|
+
|
|
781
|
+
or
|
|
782
|
+
|
|
783
|
+
- `v1 = -h*v2 (mod N)` and `u1 = -h^(-1)*u2 (mod gcd(v1,N))`
|
|
784
|
+
|
|
785
|
+
for some `h \in H`. Then t is 1 or -1 as c and c' fall into
|
|
786
|
+
the first or second case, respectively.
|
|
787
|
+
|
|
788
|
+
EXAMPLES::
|
|
789
|
+
|
|
790
|
+
sage: GammaH(6,[5])._reduce_cusp(Cusp(5,3))
|
|
791
|
+
(1/3, -1)
|
|
792
|
+
sage: GammaH(12,[5])._reduce_cusp(Cusp(8,9))
|
|
793
|
+
(1/3, -1)
|
|
794
|
+
sage: GammaH(12,[5])._reduce_cusp(Cusp(5,12))
|
|
795
|
+
(Infinity, 1)
|
|
796
|
+
sage: GammaH(12,[])._reduce_cusp(Cusp(5,12))
|
|
797
|
+
(5/12, 1)
|
|
798
|
+
sage: GammaH(21,[5])._reduce_cusp(Cusp(-9/14))
|
|
799
|
+
(1/7, 1)
|
|
800
|
+
"""
|
|
801
|
+
c = Cusp(c)
|
|
802
|
+
N = int(self.level())
|
|
803
|
+
Cusps = c.parent()
|
|
804
|
+
v = int(c.denominator() % N)
|
|
805
|
+
H = self._list_of_elements_in_H()
|
|
806
|
+
|
|
807
|
+
# First, if N | v, take care of this case. If u is in \pm H,
|
|
808
|
+
# then we return Infinity. If not, let u_0 be the minimum
|
|
809
|
+
# of \{ h*u | h \in \pm H \}. Then return u_0/N.
|
|
810
|
+
if not v:
|
|
811
|
+
u = c.numerator() % N
|
|
812
|
+
if u in H:
|
|
813
|
+
return Cusps((1, 0)), 1
|
|
814
|
+
if (N - u) in H:
|
|
815
|
+
return Cusps((1, 0)), -1
|
|
816
|
+
ls = [(u * h) % N for h in H]
|
|
817
|
+
m1 = min(ls)
|
|
818
|
+
m2 = N - max(ls)
|
|
819
|
+
if m1 < m2:
|
|
820
|
+
return Cusps((m1, N)), 1
|
|
821
|
+
else:
|
|
822
|
+
return Cusps((m2, N)), -1
|
|
823
|
+
|
|
824
|
+
u = int(c.numerator() % v)
|
|
825
|
+
gcd = get_gcd(N)
|
|
826
|
+
d = gcd(v, N)
|
|
827
|
+
|
|
828
|
+
# If (N,v) == 1, let v_0 be the minimal element
|
|
829
|
+
# in \{ v * h | h \in \pm H \}. Then we either return
|
|
830
|
+
# Infinity or 1/v_0, as v is or is not in \pm H,
|
|
831
|
+
# respectively.
|
|
832
|
+
if d == 1:
|
|
833
|
+
if v in H:
|
|
834
|
+
return Cusps((0, 1)), 1
|
|
835
|
+
if (N - v) in H:
|
|
836
|
+
return Cusps((0, 1)), -1
|
|
837
|
+
ls = [(v * h) % N for h in H]
|
|
838
|
+
m1 = min(ls)
|
|
839
|
+
m2 = N - max(ls)
|
|
840
|
+
if m1 < m2:
|
|
841
|
+
return Cusps((1, m1)), 1
|
|
842
|
+
else:
|
|
843
|
+
return Cusps((1, m2)), -1
|
|
844
|
+
|
|
845
|
+
val_min = v
|
|
846
|
+
inv_mod = get_inverse_mod(N)
|
|
847
|
+
|
|
848
|
+
# Now we're in the case (N,v) > 1. So we have to do several
|
|
849
|
+
# steps: first, compute v_0 as above. While computing this
|
|
850
|
+
# minimum, keep track of *all* pairs of (h,s) which give this
|
|
851
|
+
# value of v_0.
|
|
852
|
+
hs_ls = [(1, 1)]
|
|
853
|
+
for h in H:
|
|
854
|
+
tmp = (v * h) % N
|
|
855
|
+
|
|
856
|
+
if tmp < val_min:
|
|
857
|
+
val_min = tmp
|
|
858
|
+
hs_ls = [(inv_mod(h, N), 1)]
|
|
859
|
+
elif tmp == val_min:
|
|
860
|
+
hs_ls.append((inv_mod(h, N), 1))
|
|
861
|
+
|
|
862
|
+
if (N - tmp) < val_min:
|
|
863
|
+
val_min = N - tmp
|
|
864
|
+
hs_ls = [(inv_mod(h, N), -1)]
|
|
865
|
+
elif (N - tmp) == val_min:
|
|
866
|
+
hs_ls.append((inv_mod(h, N), -1))
|
|
867
|
+
|
|
868
|
+
# Finally, we find our minimal numerator. Let u_1 be the
|
|
869
|
+
# minimum of s*h^-1*u mod d as (h,s) ranges over the elements
|
|
870
|
+
# of hs_ls. We must find the smallest integer u_0 which is
|
|
871
|
+
# smaller than v_0, congruent to u_1 mod d, and coprime to
|
|
872
|
+
# v_0. Then u_0/v_0 is our minimal representative.
|
|
873
|
+
u_min = val_min
|
|
874
|
+
sign = None
|
|
875
|
+
for h_inv, s in hs_ls:
|
|
876
|
+
tmp = (h_inv * s * u) % d
|
|
877
|
+
while gcd(tmp, val_min) > 1 and tmp < u_min:
|
|
878
|
+
tmp += d
|
|
879
|
+
if tmp < u_min:
|
|
880
|
+
u_min = tmp
|
|
881
|
+
sign = s
|
|
882
|
+
|
|
883
|
+
return Cusps((u_min, val_min)), sign
|
|
884
|
+
|
|
885
|
+
def _find_cusps(self):
|
|
886
|
+
r"""
|
|
887
|
+
Return an ordered list of inequivalent cusps for ``self``, i.e. a
|
|
888
|
+
set of representatives for the orbits of ``self`` on
|
|
889
|
+
`\mathbf{P}^1(\QQ)`. These are returned in a reduced
|
|
890
|
+
form; see self.reduce_cusp for the definition of reduced.
|
|
891
|
+
|
|
892
|
+
ALGORITHM:
|
|
893
|
+
Lemma 3.2 in Cremona's 1997 book shows that for the action
|
|
894
|
+
of Gamma1(N) on "signed projective space"
|
|
895
|
+
`\Q^2 / (\Q_{\geq 0}^+)`, we have `u_1/v_1 \sim u_2 / v_2`
|
|
896
|
+
if and only if `v_1 = v_2 \bmod N` and `u_1 = u_2 \bmod
|
|
897
|
+
gcd(v_1, N)`. It follows that every orbit has a
|
|
898
|
+
representative `u/v` with `v \le N` and `0 \le u \le
|
|
899
|
+
gcd(v, N)`. We iterate through all pairs `(u,v)`
|
|
900
|
+
satisfying this.
|
|
901
|
+
|
|
902
|
+
Having found a set containing at least one of every
|
|
903
|
+
equivalence class modulo Gamma1(N), we can be sure of
|
|
904
|
+
picking up every class modulo GammaH(N) since this
|
|
905
|
+
contains Gamma1(N); and the reduce_cusp call does the
|
|
906
|
+
checking to make sure we don't get any duplicates.
|
|
907
|
+
|
|
908
|
+
EXAMPLES::
|
|
909
|
+
|
|
910
|
+
sage: Gamma1(5)._find_cusps()
|
|
911
|
+
[0, 2/5, 1/2, Infinity]
|
|
912
|
+
sage: Gamma1(35)._find_cusps()
|
|
913
|
+
[0, 2/35, 1/17, 1/16, 1/15, 1/14, 1/13, 1/12, 3/35, 1/11, 1/10, 1/9, 4/35, 1/8, 2/15, 1/7, 1/6, 6/35, 1/5, 3/14, 8/35, 1/4, 9/35, 4/15, 2/7, 3/10, 11/35, 1/3, 12/35, 5/14, 13/35, 2/5, 3/7, 16/35, 17/35, 1/2, 8/15, 4/7, 3/5, 9/14, 7/10, 5/7, 11/14, 4/5, 6/7, 9/10, 13/14, Infinity]
|
|
914
|
+
sage: Gamma1(24)._find_cusps() == Gamma1(24).cusps(algorithm='modsym')
|
|
915
|
+
True
|
|
916
|
+
sage: GammaH(24, [13,17])._find_cusps() == GammaH(24,[13,17]).cusps(algorithm='modsym')
|
|
917
|
+
True
|
|
918
|
+
"""
|
|
919
|
+
|
|
920
|
+
s = []
|
|
921
|
+
hashes = []
|
|
922
|
+
N = self.level()
|
|
923
|
+
|
|
924
|
+
for d in range(1, 1 + N):
|
|
925
|
+
w = N.gcd(d)
|
|
926
|
+
M = int(w) if w > 1 else 2
|
|
927
|
+
for a in range(1, M):
|
|
928
|
+
if gcd(a, w) != 1:
|
|
929
|
+
continue
|
|
930
|
+
while gcd(a, d) != 1:
|
|
931
|
+
a += w
|
|
932
|
+
c = self.reduce_cusp(Cusp(a, d))
|
|
933
|
+
h = hash(c)
|
|
934
|
+
if h not in hashes:
|
|
935
|
+
hashes.append(h)
|
|
936
|
+
s.append(c)
|
|
937
|
+
return sorted(s)
|
|
938
|
+
|
|
939
|
+
def _contains_sl2(self, a, b, c, d):
|
|
940
|
+
r"""
|
|
941
|
+
Test whether [a,b,c,d] is an element of this subgroup.
|
|
942
|
+
|
|
943
|
+
EXAMPLES::
|
|
944
|
+
|
|
945
|
+
sage: G = GammaH(10, [3])
|
|
946
|
+
sage: [1, 0, -10, 1] in G
|
|
947
|
+
True
|
|
948
|
+
sage: matrix(ZZ, 2, [7, 1, 20, 3]) in G
|
|
949
|
+
True
|
|
950
|
+
sage: SL2Z.0 in G
|
|
951
|
+
False
|
|
952
|
+
sage: GammaH(10, [9])([7, 1, 20, 3]) # indirect doctest
|
|
953
|
+
Traceback (most recent call last):
|
|
954
|
+
...
|
|
955
|
+
TypeError: matrix [ 7 1]
|
|
956
|
+
[20 3] is not an element of Congruence Subgroup Gamma_H(10) with H generated by [9]
|
|
957
|
+
"""
|
|
958
|
+
N = self.level()
|
|
959
|
+
return (c % N == 0) and (d % N in self._list_of_elements_in_H())
|
|
960
|
+
|
|
961
|
+
def gamma0_coset_reps(self):
|
|
962
|
+
r"""
|
|
963
|
+
Return a set of coset representatives for ``self \\ Gamma0(N)``, where
|
|
964
|
+
N is the level of ``self``.
|
|
965
|
+
|
|
966
|
+
EXAMPLES::
|
|
967
|
+
|
|
968
|
+
sage: GammaH(108, [1,-1]).gamma0_coset_reps()
|
|
969
|
+
[
|
|
970
|
+
[1 0] [-43 -2] [ 31 2] [-49 -5] [ 25 3] [-19 -3]
|
|
971
|
+
[0 1], [108 5], [108 7], [108 11], [108 13], [108 17],
|
|
972
|
+
<BLANKLINE>
|
|
973
|
+
[-17 -3] [ 47 10] [ 13 3] [ 41 11] [ 7 2] [-37 -12]
|
|
974
|
+
[108 19], [108 23], [108 25], [108 29], [108 31], [108 35],
|
|
975
|
+
<BLANKLINE>
|
|
976
|
+
[-35 -12] [ 29 11] [ -5 -2] [ 23 10] [-11 -5] [ 53 26]
|
|
977
|
+
[108 37], [108 41], [108 43], [108 47], [108 49], [108 53]
|
|
978
|
+
]
|
|
979
|
+
"""
|
|
980
|
+
from .all import SL2Z
|
|
981
|
+
N = self.level()
|
|
982
|
+
return [SL2Z(lift_to_sl2z(0, d.lift(), N)) for d in _GammaH_coset_helper(N, self._list_of_elements_in_H())]
|
|
983
|
+
|
|
984
|
+
def coset_reps(self):
|
|
985
|
+
r"""
|
|
986
|
+
Return a set of coset representatives for ``self \\ SL2Z``.
|
|
987
|
+
|
|
988
|
+
EXAMPLES::
|
|
989
|
+
|
|
990
|
+
sage: list(Gamma1(3).coset_reps())
|
|
991
|
+
[
|
|
992
|
+
[1 0] [-1 0] [ 0 -1] [ 0 1] [1 0] [-1 0] [ 0 -1] [ 0 1]
|
|
993
|
+
[0 1], [ 0 -1], [ 1 0], [-1 0], [1 1], [-1 -1], [ 1 2], [-1 -2]
|
|
994
|
+
]
|
|
995
|
+
sage: len(list(Gamma1(31).coset_reps())) == 31**2 - 1
|
|
996
|
+
True
|
|
997
|
+
"""
|
|
998
|
+
from .all import Gamma0, SL2Z
|
|
999
|
+
reps1 = Gamma0(self.level()).coset_reps()
|
|
1000
|
+
for r in reps1:
|
|
1001
|
+
reps2 = self.gamma0_coset_reps()
|
|
1002
|
+
for t in reps2:
|
|
1003
|
+
yield SL2Z(t) * r
|
|
1004
|
+
|
|
1005
|
+
def is_subgroup(self, other) -> bool:
|
|
1006
|
+
r"""
|
|
1007
|
+
Return ``True`` if ``self`` is a subgroup of ``right``, and ``False``
|
|
1008
|
+
otherwise.
|
|
1009
|
+
|
|
1010
|
+
EXAMPLES::
|
|
1011
|
+
|
|
1012
|
+
sage: GammaH(24,[7]).is_subgroup(SL2Z)
|
|
1013
|
+
True
|
|
1014
|
+
sage: GammaH(24,[7]).is_subgroup(Gamma0(8))
|
|
1015
|
+
True
|
|
1016
|
+
sage: GammaH(24, []).is_subgroup(GammaH(24, [7]))
|
|
1017
|
+
True
|
|
1018
|
+
sage: GammaH(24, []).is_subgroup(Gamma1(24))
|
|
1019
|
+
True
|
|
1020
|
+
sage: GammaH(24, [17]).is_subgroup(GammaH(24, [7]))
|
|
1021
|
+
False
|
|
1022
|
+
sage: GammaH(1371, [169]).is_subgroup(GammaH(457, [169]))
|
|
1023
|
+
True
|
|
1024
|
+
"""
|
|
1025
|
+
|
|
1026
|
+
from .all import Gamma0_class, Gamma1_class
|
|
1027
|
+
if not isinstance(other, GammaH_class):
|
|
1028
|
+
raise NotImplementedError
|
|
1029
|
+
|
|
1030
|
+
# level of self should divide level of other
|
|
1031
|
+
if self.level() % other.level():
|
|
1032
|
+
return False
|
|
1033
|
+
|
|
1034
|
+
# easy cases
|
|
1035
|
+
if isinstance(other, Gamma0_class):
|
|
1036
|
+
return True # recall self is a GammaH, so it's contained in Gamma0
|
|
1037
|
+
|
|
1038
|
+
if isinstance(other, Gamma1_class) and len(self._generators_for_H()) > 0:
|
|
1039
|
+
return False
|
|
1040
|
+
|
|
1041
|
+
else:
|
|
1042
|
+
# difficult case
|
|
1043
|
+
t = other._list_of_elements_in_H()
|
|
1044
|
+
for x in self._generators_for_H():
|
|
1045
|
+
if x not in t:
|
|
1046
|
+
return False
|
|
1047
|
+
return True
|
|
1048
|
+
|
|
1049
|
+
def index(self):
|
|
1050
|
+
r"""
|
|
1051
|
+
Return the index of ``self`` in SL2Z.
|
|
1052
|
+
|
|
1053
|
+
EXAMPLES::
|
|
1054
|
+
|
|
1055
|
+
sage: [G.index() for G in Gamma0(40).gamma_h_subgroups()] # optional - gap_package_polycyclic
|
|
1056
|
+
[72, 144, 144, 144, 144, 288, 288, 288, 288, 144, 288, 288, 576, 576, 144, 288, 288, 576, 576, 144, 288, 288, 576, 576, 288, 576, 1152]
|
|
1057
|
+
"""
|
|
1058
|
+
from .all import Gamma1
|
|
1059
|
+
return Gamma1(self.level()).index() / len(self._list_of_elements_in_H())
|
|
1060
|
+
|
|
1061
|
+
def nu2(self):
|
|
1062
|
+
r"""
|
|
1063
|
+
Return the number of orbits of elliptic points of order 2 for this
|
|
1064
|
+
group.
|
|
1065
|
+
|
|
1066
|
+
EXAMPLES::
|
|
1067
|
+
|
|
1068
|
+
sage: [H.nu2() for n in [1..10] for H in Gamma0(n).gamma_h_subgroups()] # optional - gap_package_polycyclic
|
|
1069
|
+
[1, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0]
|
|
1070
|
+
sage: GammaH(33,[2]).nu2()
|
|
1071
|
+
0
|
|
1072
|
+
sage: GammaH(5,[2]).nu2()
|
|
1073
|
+
2
|
|
1074
|
+
|
|
1075
|
+
AUTHORS:
|
|
1076
|
+
|
|
1077
|
+
- Jordi Quer
|
|
1078
|
+
"""
|
|
1079
|
+
N = self.level()
|
|
1080
|
+
H = self._list_of_elements_in_H()
|
|
1081
|
+
if N % 4 == 0:
|
|
1082
|
+
return ZZ(0)
|
|
1083
|
+
for p, r in N.factor():
|
|
1084
|
+
if p % 4 == 3:
|
|
1085
|
+
return ZZ(0)
|
|
1086
|
+
return (euler_phi(N) // len(H)) * len([x for x in H if (x**2 + 1) % N == 0])
|
|
1087
|
+
|
|
1088
|
+
def nu3(self):
|
|
1089
|
+
r"""
|
|
1090
|
+
Return the number of orbits of elliptic points of order 3 for this
|
|
1091
|
+
group.
|
|
1092
|
+
|
|
1093
|
+
EXAMPLES::
|
|
1094
|
+
|
|
1095
|
+
sage: [H.nu3() for n in [1..10] for H in Gamma0(n).gamma_h_subgroups()] # optional - gap_package_polycyclic
|
|
1096
|
+
[1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
|
1097
|
+
sage: GammaH(33,[2]).nu3()
|
|
1098
|
+
0
|
|
1099
|
+
sage: GammaH(7,[2]).nu3()
|
|
1100
|
+
2
|
|
1101
|
+
|
|
1102
|
+
AUTHORS:
|
|
1103
|
+
|
|
1104
|
+
- Jordi Quer
|
|
1105
|
+
"""
|
|
1106
|
+
N = self.level()
|
|
1107
|
+
H = self._list_of_elements_in_H()
|
|
1108
|
+
if N % 9 == 0:
|
|
1109
|
+
return ZZ(0)
|
|
1110
|
+
for p, r in N.factor():
|
|
1111
|
+
if p % 3 == 2:
|
|
1112
|
+
return ZZ(0)
|
|
1113
|
+
lenHpm = len(H)
|
|
1114
|
+
if N - ZZ(1) not in H:
|
|
1115
|
+
lenHpm *= 2
|
|
1116
|
+
return (euler_phi(N) // lenHpm) * len([1 for x in H if not (x**2 + x + 1) % N])
|
|
1117
|
+
|
|
1118
|
+
def ncusps(self):
|
|
1119
|
+
r"""
|
|
1120
|
+
Return the number of orbits of cusps (regular or otherwise) for this subgroup.
|
|
1121
|
+
|
|
1122
|
+
EXAMPLES::
|
|
1123
|
+
|
|
1124
|
+
sage: GammaH(33,[2]).ncusps()
|
|
1125
|
+
8
|
|
1126
|
+
sage: GammaH(32079, [21676]).ncusps()
|
|
1127
|
+
28800
|
|
1128
|
+
|
|
1129
|
+
AUTHORS:
|
|
1130
|
+
|
|
1131
|
+
- Jordi Quer
|
|
1132
|
+
"""
|
|
1133
|
+
N = self.level()
|
|
1134
|
+
H = self._list_of_elements_in_H()
|
|
1135
|
+
c = ZZ(0)
|
|
1136
|
+
for d in (d for d in N.divisors() if d**2 <= N):
|
|
1137
|
+
Nd = lcm(d, N // d)
|
|
1138
|
+
Hd = {x % Nd for x in H}
|
|
1139
|
+
lenHd = len(Hd)
|
|
1140
|
+
if Nd - 1 not in Hd:
|
|
1141
|
+
lenHd *= 2
|
|
1142
|
+
summand = euler_phi(d) * euler_phi(N // d) // lenHd
|
|
1143
|
+
if d**2 == N:
|
|
1144
|
+
c = c + summand
|
|
1145
|
+
else:
|
|
1146
|
+
c = c + 2 * summand
|
|
1147
|
+
return c
|
|
1148
|
+
|
|
1149
|
+
def nregcusps(self):
|
|
1150
|
+
r"""
|
|
1151
|
+
Return the number of orbits of regular cusps for this subgroup. A cusp is regular
|
|
1152
|
+
if we may find a parabolic element generating the stabiliser of that
|
|
1153
|
+
cusp whose eigenvalues are both +1 rather than -1. If G contains -1,
|
|
1154
|
+
all cusps are regular.
|
|
1155
|
+
|
|
1156
|
+
EXAMPLES::
|
|
1157
|
+
|
|
1158
|
+
sage: GammaH(20, [17]).nregcusps()
|
|
1159
|
+
4
|
|
1160
|
+
sage: GammaH(20, [17]).nirregcusps()
|
|
1161
|
+
2
|
|
1162
|
+
sage: GammaH(3212, [2045, 2773]).nregcusps()
|
|
1163
|
+
1440
|
|
1164
|
+
sage: GammaH(3212, [2045, 2773]).nirregcusps()
|
|
1165
|
+
720
|
|
1166
|
+
|
|
1167
|
+
AUTHOR:
|
|
1168
|
+
|
|
1169
|
+
- Jordi Quer
|
|
1170
|
+
"""
|
|
1171
|
+
if self.is_even():
|
|
1172
|
+
return self.ncusps()
|
|
1173
|
+
|
|
1174
|
+
N = self.level()
|
|
1175
|
+
H = self._list_of_elements_in_H()
|
|
1176
|
+
|
|
1177
|
+
c = ZZ(0)
|
|
1178
|
+
for d in (d for d in divisors(N) if d**2 <= N):
|
|
1179
|
+
Nd = lcm(d, N // d)
|
|
1180
|
+
Hd = {x % Nd for x in H}
|
|
1181
|
+
if Nd - 1 not in Hd:
|
|
1182
|
+
summand = euler_phi(d) * euler_phi(N // d) // (2 * len(Hd))
|
|
1183
|
+
if d**2 == N:
|
|
1184
|
+
c = c + summand
|
|
1185
|
+
else:
|
|
1186
|
+
c = c + 2 * summand
|
|
1187
|
+
return c
|
|
1188
|
+
|
|
1189
|
+
def nirregcusps(self):
|
|
1190
|
+
r"""
|
|
1191
|
+
Return the number of irregular cusps for this subgroup.
|
|
1192
|
+
|
|
1193
|
+
EXAMPLES::
|
|
1194
|
+
|
|
1195
|
+
sage: GammaH(3212, [2045, 2773]).nirregcusps()
|
|
1196
|
+
720
|
|
1197
|
+
"""
|
|
1198
|
+
|
|
1199
|
+
return self.ncusps() - self.nregcusps()
|
|
1200
|
+
|
|
1201
|
+
def dimension_cusp_forms(self, k=2):
|
|
1202
|
+
r"""
|
|
1203
|
+
Return the dimension of the space of weight k cusp forms for this
|
|
1204
|
+
group. For `k \ge 2`, this is given by a standard formula in terms of k
|
|
1205
|
+
and various invariants of the group; see Diamond + Shurman, "A First
|
|
1206
|
+
Course in Modular Forms", section 3.5 and 3.6. If k is not given,
|
|
1207
|
+
default to k = 2.
|
|
1208
|
+
|
|
1209
|
+
For dimensions of spaces of cusp forms with character for Gamma1, use
|
|
1210
|
+
the dimension_cusp_forms method of the Gamma1 class, or the standalone
|
|
1211
|
+
function dimension_cusp_forms().
|
|
1212
|
+
|
|
1213
|
+
For weight 1 cusp forms, there is no simple formula for the dimensions,
|
|
1214
|
+
so we first try to rule out nonzero cusp forms existing via
|
|
1215
|
+
Riemann-Roch, and if this fails, we trigger computation of the cusp
|
|
1216
|
+
form space using Schaeffer's algorithm; this can be quite expensive in
|
|
1217
|
+
large levels.
|
|
1218
|
+
|
|
1219
|
+
EXAMPLES::
|
|
1220
|
+
|
|
1221
|
+
sage: GammaH(31, [23]).dimension_cusp_forms(10)
|
|
1222
|
+
69
|
|
1223
|
+
sage: GammaH(31, [7]).dimension_cusp_forms(1)
|
|
1224
|
+
1
|
|
1225
|
+
"""
|
|
1226
|
+
k = ZZ(k)
|
|
1227
|
+
if k != 1:
|
|
1228
|
+
return CongruenceSubgroup.dimension_cusp_forms(self, k)
|
|
1229
|
+
else:
|
|
1230
|
+
from sage.modular.modform.weight1 import dimension_wt1_cusp_forms_gH
|
|
1231
|
+
return dimension_wt1_cusp_forms_gH(self)
|
|
1232
|
+
|
|
1233
|
+
def dimension_new_cusp_forms(self, k=2, p=0):
|
|
1234
|
+
r"""
|
|
1235
|
+
Return the dimension of the space of new (or `p`-new)
|
|
1236
|
+
weight `k` cusp forms for this congruence subgroup.
|
|
1237
|
+
|
|
1238
|
+
INPUT:
|
|
1239
|
+
|
|
1240
|
+
- ``k`` -- integer (default: 2); the weight. Not fully implemented for
|
|
1241
|
+
`k = 1`.
|
|
1242
|
+
- ``p`` -- integer (default: 0); if nonzero, compute the `p`-new
|
|
1243
|
+
subspace
|
|
1244
|
+
|
|
1245
|
+
OUTPUT: integer
|
|
1246
|
+
|
|
1247
|
+
EXAMPLES::
|
|
1248
|
+
|
|
1249
|
+
sage: GammaH(33,[2]).dimension_new_cusp_forms()
|
|
1250
|
+
3
|
|
1251
|
+
sage: Gamma1(4*25).dimension_new_cusp_forms(2, p=5)
|
|
1252
|
+
225
|
|
1253
|
+
sage: Gamma1(33).dimension_new_cusp_forms(2)
|
|
1254
|
+
19
|
|
1255
|
+
sage: Gamma1(33).dimension_new_cusp_forms(2,p=11)
|
|
1256
|
+
21
|
|
1257
|
+
"""
|
|
1258
|
+
N = self.level()
|
|
1259
|
+
if p == 0 or N % p:
|
|
1260
|
+
return sum(H.dimension_cusp_forms(k) * mumu(N // H.level())
|
|
1261
|
+
for H in self.divisor_subgroups())
|
|
1262
|
+
return self.dimension_cusp_forms(k) - \
|
|
1263
|
+
2 * self.restrict(N // p).dimension_new_cusp_forms(k)
|
|
1264
|
+
|
|
1265
|
+
def image_mod_n(self):
|
|
1266
|
+
r"""
|
|
1267
|
+
Return the image of this group in `SL(2, \ZZ / N\ZZ)`.
|
|
1268
|
+
|
|
1269
|
+
EXAMPLES::
|
|
1270
|
+
|
|
1271
|
+
sage: Gamma0(3).image_mod_n()
|
|
1272
|
+
Matrix group over Ring of integers modulo 3 with 2 generators (
|
|
1273
|
+
[2 0] [1 1]
|
|
1274
|
+
[0 2], [0 1]
|
|
1275
|
+
)
|
|
1276
|
+
|
|
1277
|
+
TESTS::
|
|
1278
|
+
|
|
1279
|
+
sage: for n in [2..20]: # optional - gap_package_polycyclic
|
|
1280
|
+
....: for g in Gamma0(n).gamma_h_subgroups():
|
|
1281
|
+
....: G = g.image_mod_n()
|
|
1282
|
+
....: assert G.order() == Gamma(n).index() / g.index()
|
|
1283
|
+
"""
|
|
1284
|
+
N = self.level()
|
|
1285
|
+
if N == 1:
|
|
1286
|
+
raise NotImplementedError("matrix groups over ring of integers modulo 1 not implemented")
|
|
1287
|
+
gens = [matrix(Zmod(N), 2, 2, [x, 0, 0, Zmod(N).one() / x]) for x in self._generators_for_H()]
|
|
1288
|
+
gens += [matrix(Zmod(N), 2, 2, [1, 1, 0, 1])]
|
|
1289
|
+
return MatrixGroup(gens)
|
|
1290
|
+
|
|
1291
|
+
def atkin_lehner_matrix(self, Q):
|
|
1292
|
+
r"""
|
|
1293
|
+
Return the matrix of the Atkin--Lehner--Li operator `W_Q` associated to
|
|
1294
|
+
an exact divisor `Q` of `N`, where `N` is the level of this group; that
|
|
1295
|
+
is, `gcd(Q, N/Q) = 1`.
|
|
1296
|
+
|
|
1297
|
+
.. NOTE::
|
|
1298
|
+
|
|
1299
|
+
We follow the conventions of [AL1978]_ here, so `W_Q` is given by
|
|
1300
|
+
the action of any matrix of the form `\begin{pmatrix} Qx & y \\ Nz
|
|
1301
|
+
& Qw \end{pmatrix}` where `x,y,z,w` are integers such that `y = 1
|
|
1302
|
+
\bmod Q`, `x = 1 \bmod N/Q`, and `det(W_Q) = Q`. For convenience,
|
|
1303
|
+
we actually always choose `x = y = 1`.
|
|
1304
|
+
|
|
1305
|
+
INPUT:
|
|
1306
|
+
|
|
1307
|
+
- ``Q`` -- an integer dividing `N`, where `N` is the level of
|
|
1308
|
+
this group. If this divisor does not satisfy `gcd(Q, N/Q) = 1`, it
|
|
1309
|
+
will be replaced by the unique integer with this property having
|
|
1310
|
+
the same prime factors as `Q`.
|
|
1311
|
+
|
|
1312
|
+
EXAMPLES::
|
|
1313
|
+
|
|
1314
|
+
sage: Gamma1(994).atkin_lehner_matrix(71)
|
|
1315
|
+
[ 71 1]
|
|
1316
|
+
[4970 71]
|
|
1317
|
+
sage: Gamma1(996).atkin_lehner_matrix(2)
|
|
1318
|
+
[ 4 1]
|
|
1319
|
+
[-996 -248]
|
|
1320
|
+
sage: Gamma1(15).atkin_lehner_matrix(7)
|
|
1321
|
+
Traceback (most recent call last):
|
|
1322
|
+
...
|
|
1323
|
+
ValueError: Q must divide the level
|
|
1324
|
+
"""
|
|
1325
|
+
# normalise Q
|
|
1326
|
+
Q = ZZ(Q)
|
|
1327
|
+
N = self.level()
|
|
1328
|
+
if not Q.divides(N):
|
|
1329
|
+
raise ValueError("Q must divide the level")
|
|
1330
|
+
Q = N // N.prime_to_m_part(Q)
|
|
1331
|
+
|
|
1332
|
+
_, z, w = xgcd(-N // Q, Q)
|
|
1333
|
+
# so w * Q - z*(N/Q) = 1
|
|
1334
|
+
return matrix(ZZ, 2, 2, [Q, 1, N * z, Q * w])
|
|
1335
|
+
|
|
1336
|
+
@cached_method
|
|
1337
|
+
def characters_mod_H(self, sign=None, galois_orbits=False):
|
|
1338
|
+
r"""
|
|
1339
|
+
Return the characters of `(\ZZ / N\ZZ)^*`, of the specified sign, which
|
|
1340
|
+
are trivial on H.
|
|
1341
|
+
|
|
1342
|
+
INPUT:
|
|
1343
|
+
|
|
1344
|
+
- ``sign`` -- (default: ``None``) if not ``None``, return only
|
|
1345
|
+
characters of the given sign
|
|
1346
|
+
|
|
1347
|
+
- ``galois_orbits`` -- (default: ``False``) if ``True``, return only
|
|
1348
|
+
one character from each Galois orbit
|
|
1349
|
+
|
|
1350
|
+
EXAMPLES::
|
|
1351
|
+
|
|
1352
|
+
sage: GammaH(5, [-1]).characters_mod_H()
|
|
1353
|
+
[Dirichlet character modulo 5 of conductor 5 mapping 2 |--> -1,
|
|
1354
|
+
Dirichlet character modulo 5 of conductor 1 mapping 2 |--> 1]
|
|
1355
|
+
sage: Gamma1(31).characters_mod_H(galois_orbits=True,sign=-1)
|
|
1356
|
+
[Dirichlet character modulo 31 of conductor 31 mapping 3 |--> zeta30,
|
|
1357
|
+
Dirichlet character modulo 31 of conductor 31 mapping 3 |--> zeta30^3,
|
|
1358
|
+
Dirichlet character modulo 31 of conductor 31 mapping 3 |--> zeta30^5,
|
|
1359
|
+
Dirichlet character modulo 31 of conductor 31 mapping 3 |--> -1]
|
|
1360
|
+
sage: GammaH(31, [-1]).characters_mod_H(sign=-1)
|
|
1361
|
+
[]
|
|
1362
|
+
"""
|
|
1363
|
+
if sign == -1 and self.is_even():
|
|
1364
|
+
# shortcut for trivial special case
|
|
1365
|
+
return []
|
|
1366
|
+
|
|
1367
|
+
from sage.modular.dirichlet import DirichletGroup
|
|
1368
|
+
chis = DirichletGroup(self.level()).galois_orbits()
|
|
1369
|
+
A = []
|
|
1370
|
+
for U in chis:
|
|
1371
|
+
chi = U[0]
|
|
1372
|
+
if sign is not None:
|
|
1373
|
+
if chi(-1) != sign:
|
|
1374
|
+
continue
|
|
1375
|
+
if not all(chi(h) == 1 for h in self._generators_for_H()):
|
|
1376
|
+
continue
|
|
1377
|
+
if galois_orbits:
|
|
1378
|
+
A.append(chi)
|
|
1379
|
+
else:
|
|
1380
|
+
A += U
|
|
1381
|
+
return A
|
|
1382
|
+
|
|
1383
|
+
|
|
1384
|
+
def _list_subgroup(N, gens):
|
|
1385
|
+
r"""
|
|
1386
|
+
Given an integer ``N`` and a list of integers ``gens``, return a list of
|
|
1387
|
+
the elements of the subgroup of `(\ZZ / N\ZZ)^\times` generated by the
|
|
1388
|
+
elements of ``gens``.
|
|
1389
|
+
|
|
1390
|
+
EXAMPLES::
|
|
1391
|
+
|
|
1392
|
+
sage: sage.modular.arithgroup.congroup_gammaH._list_subgroup(11, [3])
|
|
1393
|
+
[1, 3, 4, 5, 9]
|
|
1394
|
+
"""
|
|
1395
|
+
H = {1}
|
|
1396
|
+
N = int(N)
|
|
1397
|
+
for g in gens:
|
|
1398
|
+
if gcd(g, N) != 1:
|
|
1399
|
+
raise ValueError("gen (=%s) is not in (Z/%sZ)^*" % (g, N))
|
|
1400
|
+
gk = int(g) % N
|
|
1401
|
+
sbgrp = [gk]
|
|
1402
|
+
while gk not in H:
|
|
1403
|
+
gk = (gk * g) % N
|
|
1404
|
+
sbgrp.append(gk)
|
|
1405
|
+
H = {(x * h) % N for x in sbgrp for h in H}
|
|
1406
|
+
return sorted(H)
|
|
1407
|
+
|
|
1408
|
+
|
|
1409
|
+
def _GammaH_coset_helper(N, H):
|
|
1410
|
+
r"""
|
|
1411
|
+
Return a list of coset representatives for H in (Z / NZ)^*.
|
|
1412
|
+
|
|
1413
|
+
EXAMPLES::
|
|
1414
|
+
|
|
1415
|
+
sage: from sage.modular.arithgroup.congroup_gammaH import _GammaH_coset_helper
|
|
1416
|
+
sage: _GammaH_coset_helper(108, [1, 107])
|
|
1417
|
+
[1, 5, 7, 11, 13, 17, 19, 23, 25, 29, 31, 35, 37, 41, 43, 47, 49, 53]
|
|
1418
|
+
"""
|
|
1419
|
+
t = [Zmod(N)(1)]
|
|
1420
|
+
W = [Zmod(N)(h) for h in H]
|
|
1421
|
+
HH = [Zmod(N)(h) for h in H]
|
|
1422
|
+
k = euler_phi(N)
|
|
1423
|
+
|
|
1424
|
+
for i in range(1, N):
|
|
1425
|
+
if gcd(i, N) != 1:
|
|
1426
|
+
continue
|
|
1427
|
+
if i not in W:
|
|
1428
|
+
t.append(t[0] * i)
|
|
1429
|
+
W += [i * h for h in HH]
|
|
1430
|
+
if len(W) == k:
|
|
1431
|
+
break
|
|
1432
|
+
return t
|
|
1433
|
+
|
|
1434
|
+
|
|
1435
|
+
def mumu(N):
|
|
1436
|
+
"""
|
|
1437
|
+
Return 0 if any cube divides `N`. Otherwise return
|
|
1438
|
+
`(-2)^v` where `v` is the number of primes that
|
|
1439
|
+
exactly divide `N`.
|
|
1440
|
+
|
|
1441
|
+
This is similar to the Möbius function.
|
|
1442
|
+
|
|
1443
|
+
INPUT:
|
|
1444
|
+
|
|
1445
|
+
- ``N`` -- integer at least 1
|
|
1446
|
+
|
|
1447
|
+
OUTPUT: integer
|
|
1448
|
+
|
|
1449
|
+
EXAMPLES::
|
|
1450
|
+
|
|
1451
|
+
sage: from sage.modular.arithgroup.congroup_gammaH import mumu
|
|
1452
|
+
sage: mumu(27)
|
|
1453
|
+
0
|
|
1454
|
+
sage: mumu(6*25)
|
|
1455
|
+
4
|
|
1456
|
+
sage: mumu(7*9*25)
|
|
1457
|
+
-2
|
|
1458
|
+
sage: mumu(9*25)
|
|
1459
|
+
1
|
|
1460
|
+
"""
|
|
1461
|
+
if N < 1:
|
|
1462
|
+
raise ValueError("N must be at least 1")
|
|
1463
|
+
p = 1
|
|
1464
|
+
for _, r in factor(N):
|
|
1465
|
+
if r > 2:
|
|
1466
|
+
return ZZ.zero()
|
|
1467
|
+
elif r == 1:
|
|
1468
|
+
p *= -2
|
|
1469
|
+
return ZZ(p)
|