passagemath-schemes 10.6.40__cp314-cp314-macosx_13_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of passagemath-schemes might be problematic. Click here for more details.
- passagemath_schemes/.dylibs/libflint.22.0.dylib +0 -0
- passagemath_schemes/.dylibs/libgmp.10.dylib +0 -0
- passagemath_schemes/.dylibs/libgmpxx.4.dylib +0 -0
- passagemath_schemes/.dylibs/libmpfr.6.dylib +0 -0
- passagemath_schemes/__init__.py +3 -0
- passagemath_schemes-10.6.40.dist-info/METADATA +204 -0
- passagemath_schemes-10.6.40.dist-info/METADATA.bak +205 -0
- passagemath_schemes-10.6.40.dist-info/RECORD +314 -0
- passagemath_schemes-10.6.40.dist-info/WHEEL +6 -0
- passagemath_schemes-10.6.40.dist-info/top_level.txt +3 -0
- sage/all__sagemath_schemes.py +23 -0
- sage/databases/all__sagemath_schemes.py +7 -0
- sage/databases/cremona.py +1723 -0
- sage/dynamics/all__sagemath_schemes.py +2 -0
- sage/dynamics/arithmetic_dynamics/affine_ds.py +1083 -0
- sage/dynamics/arithmetic_dynamics/all.py +14 -0
- sage/dynamics/arithmetic_dynamics/berkovich_ds.py +1101 -0
- sage/dynamics/arithmetic_dynamics/dynamical_semigroup.py +1543 -0
- sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py +2426 -0
- sage/dynamics/arithmetic_dynamics/endPN_minimal_model.py +1169 -0
- sage/dynamics/arithmetic_dynamics/generic_ds.py +663 -0
- sage/dynamics/arithmetic_dynamics/product_projective_ds.py +339 -0
- sage/dynamics/arithmetic_dynamics/projective_ds.py +9558 -0
- sage/dynamics/arithmetic_dynamics/projective_ds_helper.cpython-314-darwin.so +0 -0
- sage/dynamics/arithmetic_dynamics/projective_ds_helper.pyx +301 -0
- sage/dynamics/arithmetic_dynamics/wehlerK3.py +2576 -0
- sage/lfunctions/all.py +18 -0
- sage/lfunctions/dokchitser.py +745 -0
- sage/lfunctions/pari.py +818 -0
- sage/lfunctions/zero_sums.cpython-314-darwin.so +0 -0
- sage/lfunctions/zero_sums.pyx +1847 -0
- sage/modular/abvar/abvar.py +5135 -0
- sage/modular/abvar/abvar_ambient_jacobian.py +413 -0
- sage/modular/abvar/abvar_newform.py +244 -0
- sage/modular/abvar/all.py +8 -0
- sage/modular/abvar/constructor.py +186 -0
- sage/modular/abvar/cuspidal_subgroup.py +371 -0
- sage/modular/abvar/finite_subgroup.py +896 -0
- sage/modular/abvar/homology.py +720 -0
- sage/modular/abvar/homspace.py +998 -0
- sage/modular/abvar/lseries.py +415 -0
- sage/modular/abvar/morphism.py +935 -0
- sage/modular/abvar/torsion_point.py +274 -0
- sage/modular/abvar/torsion_subgroup.py +740 -0
- sage/modular/all.py +43 -0
- sage/modular/arithgroup/all.py +20 -0
- sage/modular/arithgroup/arithgroup_element.cpython-314-darwin.so +0 -0
- sage/modular/arithgroup/arithgroup_element.pyx +474 -0
- sage/modular/arithgroup/arithgroup_generic.py +1402 -0
- sage/modular/arithgroup/arithgroup_perm.py +2692 -0
- sage/modular/arithgroup/congroup.cpython-314-darwin.so +0 -0
- sage/modular/arithgroup/congroup.pyx +334 -0
- sage/modular/arithgroup/congroup_gamma.py +363 -0
- sage/modular/arithgroup/congroup_gamma0.py +692 -0
- sage/modular/arithgroup/congroup_gamma1.py +653 -0
- sage/modular/arithgroup/congroup_gammaH.py +1469 -0
- sage/modular/arithgroup/congroup_generic.py +628 -0
- sage/modular/arithgroup/congroup_sl2z.py +267 -0
- sage/modular/arithgroup/farey_symbol.cpython-314-darwin.so +0 -0
- sage/modular/arithgroup/farey_symbol.pyx +1066 -0
- sage/modular/arithgroup/tests.py +418 -0
- sage/modular/btquotients/all.py +4 -0
- sage/modular/btquotients/btquotient.py +3753 -0
- sage/modular/btquotients/pautomorphicform.py +2570 -0
- sage/modular/buzzard.py +100 -0
- sage/modular/congroup.py +29 -0
- sage/modular/congroup_element.py +13 -0
- sage/modular/cusps.py +1109 -0
- sage/modular/cusps_nf.py +1270 -0
- sage/modular/dims.py +569 -0
- sage/modular/dirichlet.py +3310 -0
- sage/modular/drinfeld_modform/all.py +2 -0
- sage/modular/drinfeld_modform/element.py +446 -0
- sage/modular/drinfeld_modform/ring.py +773 -0
- sage/modular/drinfeld_modform/tutorial.py +236 -0
- sage/modular/etaproducts.py +1065 -0
- sage/modular/hecke/algebra.py +746 -0
- sage/modular/hecke/all.py +20 -0
- sage/modular/hecke/ambient_module.py +1019 -0
- sage/modular/hecke/degenmap.py +119 -0
- sage/modular/hecke/element.py +325 -0
- sage/modular/hecke/hecke_operator.py +780 -0
- sage/modular/hecke/homspace.py +206 -0
- sage/modular/hecke/module.py +1767 -0
- sage/modular/hecke/morphism.py +174 -0
- sage/modular/hecke/submodule.py +989 -0
- sage/modular/hypergeometric_misc.cpython-314-darwin.so +0 -0
- sage/modular/hypergeometric_misc.pxd +4 -0
- sage/modular/hypergeometric_misc.pyx +166 -0
- sage/modular/hypergeometric_motive.py +2017 -0
- sage/modular/local_comp/all.py +2 -0
- sage/modular/local_comp/liftings.py +292 -0
- sage/modular/local_comp/local_comp.py +1071 -0
- sage/modular/local_comp/smoothchar.py +1825 -0
- sage/modular/local_comp/type_space.py +748 -0
- sage/modular/modform/all.py +30 -0
- sage/modular/modform/ambient.py +815 -0
- sage/modular/modform/ambient_R.py +177 -0
- sage/modular/modform/ambient_eps.py +306 -0
- sage/modular/modform/ambient_g0.py +124 -0
- sage/modular/modform/ambient_g1.py +204 -0
- sage/modular/modform/constructor.py +545 -0
- sage/modular/modform/cuspidal_submodule.py +708 -0
- sage/modular/modform/defaults.py +14 -0
- sage/modular/modform/eis_series.py +505 -0
- sage/modular/modform/eisenstein_submodule.py +663 -0
- sage/modular/modform/element.py +4131 -0
- sage/modular/modform/find_generators.py +59 -0
- sage/modular/modform/half_integral.py +154 -0
- sage/modular/modform/hecke_operator_on_qexp.py +247 -0
- sage/modular/modform/j_invariant.py +47 -0
- sage/modular/modform/l_series_gross_zagier.py +133 -0
- sage/modular/modform/l_series_gross_zagier_coeffs.cpython-314-darwin.so +0 -0
- sage/modular/modform/l_series_gross_zagier_coeffs.pyx +177 -0
- sage/modular/modform/notes.py +45 -0
- sage/modular/modform/numerical.py +514 -0
- sage/modular/modform/periods.py +14 -0
- sage/modular/modform/ring.py +1257 -0
- sage/modular/modform/space.py +1860 -0
- sage/modular/modform/submodule.py +118 -0
- sage/modular/modform/tests.py +64 -0
- sage/modular/modform/theta.py +110 -0
- sage/modular/modform/vm_basis.py +381 -0
- sage/modular/modform/weight1.py +220 -0
- sage/modular/modform_hecketriangle/abstract_ring.py +1932 -0
- sage/modular/modform_hecketriangle/abstract_space.py +2528 -0
- sage/modular/modform_hecketriangle/all.py +30 -0
- sage/modular/modform_hecketriangle/analytic_type.py +590 -0
- sage/modular/modform_hecketriangle/constructor.py +416 -0
- sage/modular/modform_hecketriangle/element.py +351 -0
- sage/modular/modform_hecketriangle/functors.py +752 -0
- sage/modular/modform_hecketriangle/graded_ring.py +541 -0
- sage/modular/modform_hecketriangle/graded_ring_element.py +2225 -0
- sage/modular/modform_hecketriangle/hecke_triangle_group_element.py +3352 -0
- sage/modular/modform_hecketriangle/hecke_triangle_groups.py +1432 -0
- sage/modular/modform_hecketriangle/readme.py +1214 -0
- sage/modular/modform_hecketriangle/series_constructor.py +580 -0
- sage/modular/modform_hecketriangle/space.py +1037 -0
- sage/modular/modform_hecketriangle/subspace.py +423 -0
- sage/modular/modsym/all.py +17 -0
- sage/modular/modsym/ambient.py +3846 -0
- sage/modular/modsym/boundary.py +1420 -0
- sage/modular/modsym/element.py +336 -0
- sage/modular/modsym/g1list.py +178 -0
- sage/modular/modsym/ghlist.py +182 -0
- sage/modular/modsym/hecke_operator.py +73 -0
- sage/modular/modsym/manin_symbol.cpython-314-darwin.so +0 -0
- sage/modular/modsym/manin_symbol.pxd +5 -0
- sage/modular/modsym/manin_symbol.pyx +497 -0
- sage/modular/modsym/manin_symbol_list.py +1295 -0
- sage/modular/modsym/modsym.py +400 -0
- sage/modular/modsym/modular_symbols.py +384 -0
- sage/modular/modsym/p1list.cpython-314-darwin.so +0 -0
- sage/modular/modsym/p1list.pxd +29 -0
- sage/modular/modsym/p1list.pyx +1372 -0
- sage/modular/modsym/p1list_nf.py +1241 -0
- sage/modular/modsym/relation_matrix.py +591 -0
- sage/modular/modsym/relation_matrix_pyx.cpython-314-darwin.so +0 -0
- sage/modular/modsym/relation_matrix_pyx.pyx +108 -0
- sage/modular/modsym/space.py +2468 -0
- sage/modular/modsym/subspace.py +455 -0
- sage/modular/modsym/tests.py +375 -0
- sage/modular/multiple_zeta.py +2632 -0
- sage/modular/multiple_zeta_F_algebra.py +786 -0
- sage/modular/overconvergent/all.py +6 -0
- sage/modular/overconvergent/genus0.py +1878 -0
- sage/modular/overconvergent/hecke_series.py +1187 -0
- sage/modular/overconvergent/weightspace.py +778 -0
- sage/modular/pollack_stevens/all.py +4 -0
- sage/modular/pollack_stevens/distributions.py +874 -0
- sage/modular/pollack_stevens/fund_domain.py +1572 -0
- sage/modular/pollack_stevens/manin_map.py +859 -0
- sage/modular/pollack_stevens/modsym.py +1593 -0
- sage/modular/pollack_stevens/padic_lseries.py +417 -0
- sage/modular/pollack_stevens/sigma0.py +534 -0
- sage/modular/pollack_stevens/space.py +1076 -0
- sage/modular/quasimodform/all.py +3 -0
- sage/modular/quasimodform/element.py +845 -0
- sage/modular/quasimodform/ring.py +828 -0
- sage/modular/quatalg/all.py +3 -0
- sage/modular/quatalg/brandt.py +1642 -0
- sage/modular/ssmod/all.py +8 -0
- sage/modular/ssmod/ssmod.py +827 -0
- sage/rings/all__sagemath_schemes.py +1 -0
- sage/rings/polynomial/all__sagemath_schemes.py +1 -0
- sage/rings/polynomial/binary_form_reduce.py +585 -0
- sage/schemes/all.py +41 -0
- sage/schemes/berkovich/all.py +6 -0
- sage/schemes/berkovich/berkovich_cp_element.py +2582 -0
- sage/schemes/berkovich/berkovich_space.py +748 -0
- sage/schemes/curves/affine_curve.py +2928 -0
- sage/schemes/curves/all.py +33 -0
- sage/schemes/curves/closed_point.py +434 -0
- sage/schemes/curves/constructor.py +381 -0
- sage/schemes/curves/curve.py +542 -0
- sage/schemes/curves/plane_curve_arrangement.py +1283 -0
- sage/schemes/curves/point.py +463 -0
- sage/schemes/curves/projective_curve.py +3026 -0
- sage/schemes/curves/zariski_vankampen.py +1932 -0
- sage/schemes/cyclic_covers/all.py +2 -0
- sage/schemes/cyclic_covers/charpoly_frobenius.py +320 -0
- sage/schemes/cyclic_covers/constructor.py +137 -0
- sage/schemes/cyclic_covers/cycliccover_finite_field.py +1309 -0
- sage/schemes/cyclic_covers/cycliccover_generic.py +310 -0
- sage/schemes/elliptic_curves/BSD.py +1036 -0
- sage/schemes/elliptic_curves/Qcurves.py +592 -0
- sage/schemes/elliptic_curves/addition_formulas_ring.py +94 -0
- sage/schemes/elliptic_curves/all.py +49 -0
- sage/schemes/elliptic_curves/cardinality.py +609 -0
- sage/schemes/elliptic_curves/cm.py +1102 -0
- sage/schemes/elliptic_curves/constructor.py +1552 -0
- sage/schemes/elliptic_curves/ec_database.py +175 -0
- sage/schemes/elliptic_curves/ell_curve_isogeny.py +3972 -0
- sage/schemes/elliptic_curves/ell_egros.py +459 -0
- sage/schemes/elliptic_curves/ell_field.py +2836 -0
- sage/schemes/elliptic_curves/ell_finite_field.py +3359 -0
- sage/schemes/elliptic_curves/ell_generic.py +3760 -0
- sage/schemes/elliptic_curves/ell_local_data.py +1207 -0
- sage/schemes/elliptic_curves/ell_modular_symbols.py +775 -0
- sage/schemes/elliptic_curves/ell_number_field.py +4220 -0
- sage/schemes/elliptic_curves/ell_padic_field.py +107 -0
- sage/schemes/elliptic_curves/ell_point.py +4787 -0
- sage/schemes/elliptic_curves/ell_rational_field.py +7368 -0
- sage/schemes/elliptic_curves/ell_tate_curve.py +671 -0
- sage/schemes/elliptic_curves/ell_torsion.py +436 -0
- sage/schemes/elliptic_curves/ell_wp.py +352 -0
- sage/schemes/elliptic_curves/formal_group.py +760 -0
- sage/schemes/elliptic_curves/gal_reps.py +1459 -0
- sage/schemes/elliptic_curves/gal_reps_number_field.py +1669 -0
- sage/schemes/elliptic_curves/gp_simon.py +152 -0
- sage/schemes/elliptic_curves/heegner.py +7335 -0
- sage/schemes/elliptic_curves/height.py +2109 -0
- sage/schemes/elliptic_curves/hom.py +1406 -0
- sage/schemes/elliptic_curves/hom_composite.py +934 -0
- sage/schemes/elliptic_curves/hom_frobenius.py +522 -0
- sage/schemes/elliptic_curves/hom_scalar.py +531 -0
- sage/schemes/elliptic_curves/hom_sum.py +682 -0
- sage/schemes/elliptic_curves/hom_velusqrt.py +1290 -0
- sage/schemes/elliptic_curves/homset.py +271 -0
- sage/schemes/elliptic_curves/isogeny_class.py +1521 -0
- sage/schemes/elliptic_curves/isogeny_small_degree.py +2797 -0
- sage/schemes/elliptic_curves/jacobian.py +237 -0
- sage/schemes/elliptic_curves/kodaira_symbol.py +344 -0
- sage/schemes/elliptic_curves/kraus.py +1014 -0
- sage/schemes/elliptic_curves/lseries_ell.py +943 -0
- sage/schemes/elliptic_curves/mod5family.py +105 -0
- sage/schemes/elliptic_curves/mod_poly.py +197 -0
- sage/schemes/elliptic_curves/mod_sym_num.cpython-314-darwin.so +0 -0
- sage/schemes/elliptic_curves/mod_sym_num.pyx +3796 -0
- sage/schemes/elliptic_curves/modular_parametrization.py +305 -0
- sage/schemes/elliptic_curves/padic_lseries.py +1793 -0
- sage/schemes/elliptic_curves/padics.py +1816 -0
- sage/schemes/elliptic_curves/period_lattice.py +2234 -0
- sage/schemes/elliptic_curves/period_lattice_region.cpython-314-darwin.so +0 -0
- sage/schemes/elliptic_curves/period_lattice_region.pyx +722 -0
- sage/schemes/elliptic_curves/saturation.py +715 -0
- sage/schemes/elliptic_curves/sha_tate.py +1158 -0
- sage/schemes/elliptic_curves/weierstrass_morphism.py +1117 -0
- sage/schemes/elliptic_curves/weierstrass_transform.py +200 -0
- sage/schemes/hyperelliptic_curves/all.py +6 -0
- sage/schemes/hyperelliptic_curves/constructor.py +291 -0
- sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py +1914 -0
- sage/schemes/hyperelliptic_curves/hyperelliptic_g2.py +192 -0
- sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py +954 -0
- sage/schemes/hyperelliptic_curves/hyperelliptic_padic_field.py +1332 -0
- sage/schemes/hyperelliptic_curves/hyperelliptic_rational_field.py +84 -0
- sage/schemes/hyperelliptic_curves/invariants.py +410 -0
- sage/schemes/hyperelliptic_curves/jacobian_endomorphism_utils.py +315 -0
- sage/schemes/hyperelliptic_curves/jacobian_g2.py +32 -0
- sage/schemes/hyperelliptic_curves/jacobian_generic.py +419 -0
- sage/schemes/hyperelliptic_curves/jacobian_homset.py +186 -0
- sage/schemes/hyperelliptic_curves/jacobian_morphism.py +875 -0
- sage/schemes/hyperelliptic_curves/kummer_surface.py +99 -0
- sage/schemes/hyperelliptic_curves/mestre.py +302 -0
- sage/schemes/hyperelliptic_curves/monsky_washnitzer.py +3871 -0
- sage/schemes/jacobians/abstract_jacobian.py +277 -0
- sage/schemes/jacobians/all.py +2 -0
- sage/schemes/overview.py +161 -0
- sage/schemes/plane_conics/all.py +22 -0
- sage/schemes/plane_conics/con_field.py +1296 -0
- sage/schemes/plane_conics/con_finite_field.py +158 -0
- sage/schemes/plane_conics/con_number_field.py +456 -0
- sage/schemes/plane_conics/con_rational_field.py +406 -0
- sage/schemes/plane_conics/con_rational_function_field.py +580 -0
- sage/schemes/plane_conics/constructor.py +249 -0
- sage/schemes/plane_quartics/all.py +2 -0
- sage/schemes/plane_quartics/quartic_constructor.py +71 -0
- sage/schemes/plane_quartics/quartic_generic.py +73 -0
- sage/schemes/riemann_surfaces/all.py +1 -0
- sage/schemes/riemann_surfaces/riemann_surface.py +4117 -0
- sage_wheels/share/cremona/cremona_mini.db +0 -0
- sage_wheels/share/ellcurves/rank0 +30427 -0
- sage_wheels/share/ellcurves/rank1 +31871 -0
- sage_wheels/share/ellcurves/rank10 +6 -0
- sage_wheels/share/ellcurves/rank11 +6 -0
- sage_wheels/share/ellcurves/rank12 +1 -0
- sage_wheels/share/ellcurves/rank14 +1 -0
- sage_wheels/share/ellcurves/rank15 +1 -0
- sage_wheels/share/ellcurves/rank17 +1 -0
- sage_wheels/share/ellcurves/rank19 +1 -0
- sage_wheels/share/ellcurves/rank2 +2388 -0
- sage_wheels/share/ellcurves/rank20 +1 -0
- sage_wheels/share/ellcurves/rank21 +1 -0
- sage_wheels/share/ellcurves/rank22 +1 -0
- sage_wheels/share/ellcurves/rank23 +1 -0
- sage_wheels/share/ellcurves/rank24 +1 -0
- sage_wheels/share/ellcurves/rank28 +1 -0
- sage_wheels/share/ellcurves/rank3 +836 -0
- sage_wheels/share/ellcurves/rank4 +10 -0
- sage_wheels/share/ellcurves/rank5 +5 -0
- sage_wheels/share/ellcurves/rank6 +5 -0
- sage_wheels/share/ellcurves/rank7 +5 -0
- sage_wheels/share/ellcurves/rank8 +6 -0
- sage_wheels/share/ellcurves/rank9 +7 -0
|
@@ -0,0 +1,1642 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-schemes
|
|
2
|
+
r"""
|
|
3
|
+
Brandt modules
|
|
4
|
+
|
|
5
|
+
Introduction
|
|
6
|
+
------------
|
|
7
|
+
|
|
8
|
+
The construction of Brandt modules provides us with a method to
|
|
9
|
+
compute modular forms, as outlined in Pizer's paper [Piz1980]_.
|
|
10
|
+
|
|
11
|
+
Given a prime number `p` and a positive integer `M` with `p\nmid M`,
|
|
12
|
+
the *Brandt module* `B(p, M)` is the free abelian group on right ideal
|
|
13
|
+
classes of a quaternion order of level `pM` in the quaternion algebra
|
|
14
|
+
ramified precisely at the places `p` and `\infty`. This Brandt module
|
|
15
|
+
carries a natural Hecke action given by Brandt matrices. There exists
|
|
16
|
+
a non-canonical Hecke algebra isomorphism between `B(p, M)` and a
|
|
17
|
+
certain subspace of `S_{2}(\Gamma_0(pM))` containing the newforms.
|
|
18
|
+
|
|
19
|
+
Quaternion Algebras
|
|
20
|
+
-------------------
|
|
21
|
+
|
|
22
|
+
A quaternion algebra over `\QQ` is a central simple algebra of
|
|
23
|
+
dimension 4 over `\QQ`. Such an algebra `A` is said to be ramified at
|
|
24
|
+
a place `v` of `\QQ` if and only if `A \otimes \QQ_v` is a division
|
|
25
|
+
algebra. Otherwise `A` is said to be split at `v`.
|
|
26
|
+
|
|
27
|
+
``A = QuaternionAlgebra(p)`` returns the quaternion algebra `A` over
|
|
28
|
+
`\QQ` ramified precisely at the places `p` and `\infty`.
|
|
29
|
+
|
|
30
|
+
``A = QuaternionAlgebra(a, b)`` returns the quaternion algebra `A`
|
|
31
|
+
over `\QQ` with basis `\{1, i, j, k\}` such that `i^2 = a`, `j^2 = b`
|
|
32
|
+
and `ij = -ji = k.`
|
|
33
|
+
|
|
34
|
+
An order `R` in a quaternion algebra `A` over `\QQ` is a 4-dimensional
|
|
35
|
+
lattice in `A` which is also a subring containing the identity. A
|
|
36
|
+
maximal order is one that is not properly contained in another order.
|
|
37
|
+
|
|
38
|
+
A particularly important kind of orders are those that have a level;
|
|
39
|
+
see Definition 1.2 in [Piz1980]_. This is a positive integer `N` such
|
|
40
|
+
that every prime that ramifies in `A` divides `N` to an odd power.
|
|
41
|
+
The maximal orders are those that have level equal to the discriminant
|
|
42
|
+
of `A`.
|
|
43
|
+
|
|
44
|
+
``R = A.maximal_order()`` returns a maximal order `R` in the quaternion
|
|
45
|
+
algebra `A.`
|
|
46
|
+
|
|
47
|
+
A right `\mathcal{O}`-ideal `I` is a lattice in `A` such that for
|
|
48
|
+
every prime `p` there exists `a_p\in A_p^*` with `I_p =
|
|
49
|
+
a_p\mathcal{O}_p`. Two right `\mathcal{O}`-ideals `I` and `J` are said
|
|
50
|
+
to belong to the same class if `I=aJ` for some `a \in A^*`. Left
|
|
51
|
+
`\mathcal{O}`-ideals are defined in a similar fashion.
|
|
52
|
+
|
|
53
|
+
The right order of `I` is the subring of `A` consisting of elements
|
|
54
|
+
`a` with `Ia \subseteq I`.
|
|
55
|
+
|
|
56
|
+
Brandt Modules
|
|
57
|
+
--------------
|
|
58
|
+
|
|
59
|
+
``B = BrandtModule(p, M=1)`` returns the Brandt module associated to
|
|
60
|
+
the prime number `p` and the integer `M`, with `p` not dividing `M`.
|
|
61
|
+
|
|
62
|
+
``A = B.quaternion_algebra()`` returns the quaternion algebra attached
|
|
63
|
+
to `B`; this is the quaternion algebra over `\QQ` ramified exactly at
|
|
64
|
+
`p` and `\infty`.
|
|
65
|
+
|
|
66
|
+
``O = B.order_of_level_N()`` returns an order `\mathcal{O}` of level
|
|
67
|
+
`N = pM` in `A`.
|
|
68
|
+
|
|
69
|
+
``B.right_ideals()`` returns a tuple of representatives for all right
|
|
70
|
+
ideal classes of `\mathcal{O}`.
|
|
71
|
+
|
|
72
|
+
The implementation of this method is especially interesting. It
|
|
73
|
+
depends on the construction of a Hecke module defined as a free
|
|
74
|
+
abelian group on right ideal classes of a quaternion algebra with the
|
|
75
|
+
following action:
|
|
76
|
+
|
|
77
|
+
.. MATH::
|
|
78
|
+
|
|
79
|
+
T_n[I] = \sum_{\phi} [J]
|
|
80
|
+
|
|
81
|
+
where `(n,pM)=1` and the sum is over cyclic `\mathcal{O}`-module
|
|
82
|
+
homomorphisms `\phi\colon I\rightarrow J` of degree `n` up to
|
|
83
|
+
isomorphism of `J`. Equivalently one can sum over the inclusions of
|
|
84
|
+
the submodules `J \rightarrow n^{-1}I`. The rough idea is to start
|
|
85
|
+
with the trivial ideal class containing the order `\mathcal{O}`
|
|
86
|
+
itself. Using the method ``cyclic_submodules(self, I, q)`` one then
|
|
87
|
+
repeatedly computes `T_q([\mathcal{O}])` for some prime `q` not
|
|
88
|
+
dividing the level of `\mathcal{O}` and tests for equivalence among
|
|
89
|
+
the resulting ideals. A theorem of Serre asserts that one gets a
|
|
90
|
+
complete set of ideal class representatives after a finite number of
|
|
91
|
+
repetitions.
|
|
92
|
+
|
|
93
|
+
One can prove that two ideals `I` and `J` are equivalent if and only
|
|
94
|
+
if there exists an element `\alpha \in I \overline{J}` such
|
|
95
|
+
`N(\alpha)=N(I)N(J)`.
|
|
96
|
+
|
|
97
|
+
``is_right_equivalent(I,J)`` returns true if `I` and `J` are equivalent. This
|
|
98
|
+
method first compares the theta series of `I` and `J`. If they are the
|
|
99
|
+
same, it computes the theta series of the lattice `I\overline(J)`. It
|
|
100
|
+
returns true if the `n`-th coefficient of this series is nonzero
|
|
101
|
+
where `n=N(J)N(I)`.
|
|
102
|
+
|
|
103
|
+
The theta series of a lattice `L` over the quaternion algebra `A` is
|
|
104
|
+
defined as
|
|
105
|
+
|
|
106
|
+
.. MATH::
|
|
107
|
+
|
|
108
|
+
\theta_L(q)=\sum_{x \in L} q^{\frac{N(x)}{N(L)}}
|
|
109
|
+
|
|
110
|
+
``L.theta_series(T,q)`` returns a power series representing `\theta_L(q)`
|
|
111
|
+
up to a precision of `\mathcal{O}(q^{T+1})`.
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
Hecke Structure
|
|
115
|
+
---------------
|
|
116
|
+
|
|
117
|
+
The Hecke structure defined on the Brandt module is given by the
|
|
118
|
+
Brandt matrices which can be computed using the definition of the
|
|
119
|
+
Hecke operators given earlier.
|
|
120
|
+
|
|
121
|
+
``hecke_matrix_from_defn(self,n)`` returns the matrix of the `n`-th Hecke
|
|
122
|
+
operator `B_{0}(n)` acting on self, computed directly from the
|
|
123
|
+
definition.
|
|
124
|
+
|
|
125
|
+
However, one can efficiently compute Brandt matrices using theta
|
|
126
|
+
series. In fact, let `\{I_{1},.....,I_{h}\}` be a set of right
|
|
127
|
+
`\mathcal{O}`-ideal class representatives. The (i,j) entry in the
|
|
128
|
+
Brandt matrix `B_{0}(n)` is the product of the `n`-th coefficient in
|
|
129
|
+
the theta series of the lattice `I_{i}\overline{I_{j}}` and the first
|
|
130
|
+
coefficient in the theta series of the lattice
|
|
131
|
+
`I_{i}\overline{I_{i}}`.
|
|
132
|
+
|
|
133
|
+
``compute_hecke_matrix_brandt(self,n)`` returns the `n`-th Hecke matrix,
|
|
134
|
+
computed using theta series.
|
|
135
|
+
|
|
136
|
+
EXAMPLES::
|
|
137
|
+
|
|
138
|
+
sage: B = BrandtModule(23)
|
|
139
|
+
|
|
140
|
+
sage: B.maximal_order()
|
|
141
|
+
Order of Quaternion Algebra (-1, -23) with base ring Rational Field with basis (1/2 + 1/2*j, 1/2*i + 1/2*k, j, k)
|
|
142
|
+
|
|
143
|
+
sage: B.right_ideals()
|
|
144
|
+
(Fractional ideal (4, 4*i, 2 + 2*j, 2*i + 2*k),
|
|
145
|
+
Fractional ideal (8, 8*i, 2 + 2*j, 6*i + 2*k),
|
|
146
|
+
Fractional ideal (16, 16*i, 10 + 8*i + 2*j, 8 + 6*i + 2*k))
|
|
147
|
+
|
|
148
|
+
sage: B.hecke_matrix(2)
|
|
149
|
+
[1 2 0]
|
|
150
|
+
[1 1 1]
|
|
151
|
+
[0 3 0]
|
|
152
|
+
|
|
153
|
+
sage: B.brandt_series(3)
|
|
154
|
+
[1/4 + q + q^2 + O(q^3) 1/4 + q^2 + O(q^3) 1/4 + O(q^3)]
|
|
155
|
+
[ 1/2 + 2*q^2 + O(q^3) 1/2 + q + q^2 + O(q^3) 1/2 + 3*q^2 + O(q^3)]
|
|
156
|
+
[ 1/6 + O(q^3) 1/6 + q^2 + O(q^3) 1/6 + q + O(q^3)]
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
REFERENCES:
|
|
160
|
+
|
|
161
|
+
- [Piz1980]_
|
|
162
|
+
- [Koh2000]_
|
|
163
|
+
|
|
164
|
+
Further Examples
|
|
165
|
+
----------------
|
|
166
|
+
|
|
167
|
+
We decompose a Brandt module over both `\ZZ` and `\QQ`. ::
|
|
168
|
+
|
|
169
|
+
sage: B = BrandtModule(43, base_ring=ZZ); B
|
|
170
|
+
Brandt module of dimension 4 of level 43 of weight 2 over Integer Ring
|
|
171
|
+
sage: D = B.decomposition()
|
|
172
|
+
sage: D
|
|
173
|
+
[Subspace of dimension 1 of Brandt module of dimension 4 of level 43 of weight 2 over Integer Ring,
|
|
174
|
+
Subspace of dimension 1 of Brandt module of dimension 4 of level 43 of weight 2 over Integer Ring,
|
|
175
|
+
Subspace of dimension 2 of Brandt module of dimension 4 of level 43 of weight 2 over Integer Ring]
|
|
176
|
+
sage: D[0].basis()
|
|
177
|
+
((0, 0, 1, -1),)
|
|
178
|
+
sage: D[1].basis()
|
|
179
|
+
((1, 2, 2, 2),)
|
|
180
|
+
sage: D[2].basis()
|
|
181
|
+
((1, 1, -1, -1), (0, 2, -1, -1))
|
|
182
|
+
sage: B = BrandtModule(43, base_ring=QQ); B
|
|
183
|
+
Brandt module of dimension 4 of level 43 of weight 2 over Rational Field
|
|
184
|
+
sage: B.decomposition()[2].basis()
|
|
185
|
+
((1, 0, -1/2, -1/2), (0, 1, -1/2, -1/2))
|
|
186
|
+
|
|
187
|
+
AUTHORS:
|
|
188
|
+
|
|
189
|
+
- Jon Bober
|
|
190
|
+
- Alia Hamieh
|
|
191
|
+
- Victoria de Quehen
|
|
192
|
+
- William Stein
|
|
193
|
+
- Gonzalo Tornaria
|
|
194
|
+
"""
|
|
195
|
+
|
|
196
|
+
# ****************************************************************************
|
|
197
|
+
# Copyright (C) 2009 William Stein <wstein@gmail.com>
|
|
198
|
+
#
|
|
199
|
+
# This program is free software: you can redistribute it and/or modify
|
|
200
|
+
# it under the terms of the GNU General Public License as published by
|
|
201
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
202
|
+
# (at your option) any later version.
|
|
203
|
+
# https://www.gnu.org/licenses/
|
|
204
|
+
# ****************************************************************************
|
|
205
|
+
|
|
206
|
+
from sage.arith.misc import gcd, prime_divisors, kronecker, next_prime
|
|
207
|
+
from sage.categories.commutative_rings import CommutativeRings
|
|
208
|
+
from sage.matrix.constructor import matrix
|
|
209
|
+
from sage.matrix.matrix_space import MatrixSpace
|
|
210
|
+
from sage.misc.cachefunc import cached_method
|
|
211
|
+
from sage.misc.lazy_import import lazy_import
|
|
212
|
+
from sage.misc.misc_c import prod
|
|
213
|
+
from sage.misc.verbose import verbose
|
|
214
|
+
from sage.modular.dirichlet import TrivialCharacter
|
|
215
|
+
from sage.modular.hecke.ambient_module import AmbientHeckeModule
|
|
216
|
+
from sage.modular.hecke.element import HeckeModuleElement
|
|
217
|
+
from sage.modular.hecke.submodule import HeckeSubmodule
|
|
218
|
+
from sage.rings.finite_rings.finite_field_constructor import GF
|
|
219
|
+
from sage.rings.integer import Integer
|
|
220
|
+
from sage.rings.integer_ring import ZZ
|
|
221
|
+
from sage.rings.power_series_ring import PowerSeriesRing
|
|
222
|
+
from sage.rings.rational_field import QQ
|
|
223
|
+
from sage.structure.richcmp import richcmp, richcmp_method
|
|
224
|
+
|
|
225
|
+
lazy_import('sage.algebras.quatalg.quaternion_algebra', ['QuaternionAlgebra', 'basis_for_quaternion_lattice'])
|
|
226
|
+
lazy_import('sage.algebras.quatalg.quaternion_algebra_cython', 'rational_matrix_from_rational_quaternions')
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
cache = {}
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def BrandtModule(N, M=1, weight=2, base_ring=QQ, use_cache=True):
|
|
233
|
+
"""
|
|
234
|
+
Return the Brandt module of given weight associated to the prime
|
|
235
|
+
power `p^r` and integer `M`, where `p` and `M` are coprime.
|
|
236
|
+
|
|
237
|
+
INPUT:
|
|
238
|
+
|
|
239
|
+
- ``N`` -- a product of primes with odd exponents
|
|
240
|
+
- ``M`` -- integer coprime to `q` (default: 1)
|
|
241
|
+
- ``weight`` -- integer that is at least 2 (default: 2)
|
|
242
|
+
- ``base_ring`` -- the base ring (default: ``QQ``)
|
|
243
|
+
- ``use_cache`` -- whether to use the cache (default: ``True``)
|
|
244
|
+
|
|
245
|
+
OUTPUT: a Brandt module
|
|
246
|
+
|
|
247
|
+
EXAMPLES::
|
|
248
|
+
|
|
249
|
+
sage: BrandtModule(17)
|
|
250
|
+
Brandt module of dimension 2 of level 17 of weight 2 over Rational Field
|
|
251
|
+
sage: BrandtModule(17,15)
|
|
252
|
+
Brandt module of dimension 32 of level 17*15 of weight 2 over Rational Field
|
|
253
|
+
sage: BrandtModule(3,7)
|
|
254
|
+
Brandt module of dimension 2 of level 3*7 of weight 2 over Rational Field
|
|
255
|
+
sage: BrandtModule(3,weight=2)
|
|
256
|
+
Brandt module of dimension 1 of level 3 of weight 2 over Rational Field
|
|
257
|
+
sage: BrandtModule(11, base_ring=ZZ)
|
|
258
|
+
Brandt module of dimension 2 of level 11 of weight 2 over Integer Ring
|
|
259
|
+
sage: BrandtModule(11, base_ring=QQbar)
|
|
260
|
+
Brandt module of dimension 2 of level 11 of weight 2 over Algebraic Field
|
|
261
|
+
|
|
262
|
+
The ``use_cache`` option determines whether the Brandt module returned
|
|
263
|
+
by this function is cached::
|
|
264
|
+
|
|
265
|
+
sage: BrandtModule(37) is BrandtModule(37)
|
|
266
|
+
True
|
|
267
|
+
sage: BrandtModule(37,use_cache=False) is BrandtModule(37,use_cache=False)
|
|
268
|
+
False
|
|
269
|
+
|
|
270
|
+
TESTS:
|
|
271
|
+
|
|
272
|
+
Note that `N` and `M` must be coprime::
|
|
273
|
+
|
|
274
|
+
sage: BrandtModule(3,15)
|
|
275
|
+
Traceback (most recent call last):
|
|
276
|
+
...
|
|
277
|
+
ValueError: M must be coprime to N
|
|
278
|
+
|
|
279
|
+
Only weight 2 is currently implemented::
|
|
280
|
+
|
|
281
|
+
sage: BrandtModule(3,weight=4)
|
|
282
|
+
Traceback (most recent call last):
|
|
283
|
+
...
|
|
284
|
+
NotImplementedError: weight != 2 not yet implemented
|
|
285
|
+
|
|
286
|
+
Brandt modules are cached::
|
|
287
|
+
|
|
288
|
+
sage: B = BrandtModule(3,5,2,ZZ)
|
|
289
|
+
sage: B is BrandtModule(3,5,2,ZZ)
|
|
290
|
+
True
|
|
291
|
+
"""
|
|
292
|
+
N, M, weight = Integer(N), Integer(M), Integer(weight)
|
|
293
|
+
if not N.is_prime():
|
|
294
|
+
raise NotImplementedError("Brandt modules currently only implemented when N is a prime")
|
|
295
|
+
if M < 1:
|
|
296
|
+
raise ValueError("M must be positive")
|
|
297
|
+
if M.gcd(N) != 1:
|
|
298
|
+
raise ValueError("M must be coprime to N")
|
|
299
|
+
if weight < 2:
|
|
300
|
+
raise ValueError("weight must be at least 2")
|
|
301
|
+
if base_ring not in CommutativeRings():
|
|
302
|
+
raise TypeError("base_ring must be a commutative ring")
|
|
303
|
+
key = (N, M, weight, base_ring)
|
|
304
|
+
if use_cache:
|
|
305
|
+
if key in cache: # TODO: re-enable caching!
|
|
306
|
+
return cache[key]
|
|
307
|
+
if weight != 2:
|
|
308
|
+
raise NotImplementedError("weight != 2 not yet implemented")
|
|
309
|
+
B = BrandtModule_class(*key)
|
|
310
|
+
if use_cache:
|
|
311
|
+
cache[key] = B
|
|
312
|
+
return B
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def class_number(p, r, M):
|
|
316
|
+
r"""
|
|
317
|
+
Return the class number of an order of level `N = p^r M` in the
|
|
318
|
+
quaternion algebra over `\QQ` ramified precisely at `p` and infinity.
|
|
319
|
+
|
|
320
|
+
This is an implementation of Theorem 1.12 of [Piz1980]_.
|
|
321
|
+
|
|
322
|
+
INPUT:
|
|
323
|
+
|
|
324
|
+
- ``p`` -- a prime
|
|
325
|
+
- ``r`` -- an odd positive integer (default: 1)
|
|
326
|
+
- ``M`` -- integer coprime to `q` (default: 1)
|
|
327
|
+
|
|
328
|
+
OUTPUT: integer
|
|
329
|
+
|
|
330
|
+
EXAMPLES::
|
|
331
|
+
|
|
332
|
+
sage: sage.modular.quatalg.brandt.class_number(389,1,1)
|
|
333
|
+
33
|
|
334
|
+
sage: sage.modular.quatalg.brandt.class_number(389,1,2) # TODO -- right?
|
|
335
|
+
97
|
|
336
|
+
sage: sage.modular.quatalg.brandt.class_number(389,3,1) # TODO -- right?
|
|
337
|
+
4892713
|
|
338
|
+
"""
|
|
339
|
+
N = M * p**r
|
|
340
|
+
D = prime_divisors(M)
|
|
341
|
+
s = 0
|
|
342
|
+
t = 0
|
|
343
|
+
if N % 4:
|
|
344
|
+
s = (1 - kronecker(-4, p)) / 4 * prod(1 + kronecker(-4, q) for q in D)
|
|
345
|
+
if N % 9:
|
|
346
|
+
t = (1 - kronecker(-3, p)) / 3 * prod(1 + kronecker(-3, q) for q in D)
|
|
347
|
+
h = (N / Integer(12)) * (1 - 1 / p) * prod(1 + 1 / q for q in D) + s + t
|
|
348
|
+
return Integer(h)
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def maximal_order(A):
|
|
352
|
+
"""
|
|
353
|
+
Return a maximal order in the quaternion algebra ramified
|
|
354
|
+
at `p` and infinity.
|
|
355
|
+
|
|
356
|
+
This is an implementation of Proposition 5.2 of [Piz1980]_.
|
|
357
|
+
|
|
358
|
+
INPUT:
|
|
359
|
+
|
|
360
|
+
- ``A`` -- quaternion algebra ramified precisely at `p` and infinity
|
|
361
|
+
|
|
362
|
+
OUTPUT: a maximal order in `A`
|
|
363
|
+
|
|
364
|
+
EXAMPLES::
|
|
365
|
+
|
|
366
|
+
sage: A = BrandtModule(17).quaternion_algebra()
|
|
367
|
+
|
|
368
|
+
sage: sage.modular.quatalg.brandt.maximal_order(A)
|
|
369
|
+
doctest:...: DeprecationWarning: The function maximal_order() is deprecated, use the maximal_order() method of quaternion algebras
|
|
370
|
+
See https://github.com/sagemath/sage/issues/37090 for details.
|
|
371
|
+
Order of Quaternion Algebra (-3, -17) with base ring Rational Field with basis (1/2 + 1/2*i, 1/2*j - 1/2*k, -1/3*i + 1/3*k, -k)
|
|
372
|
+
|
|
373
|
+
sage: A = QuaternionAlgebra(17,names='i,j,k')
|
|
374
|
+
sage: A.maximal_order()
|
|
375
|
+
Order of Quaternion Algebra (-3, -17) with base ring Rational Field with basis (1/2 + 1/2*i, 1/2*j - 1/2*k, -1/3*i + 1/3*k, -k)
|
|
376
|
+
"""
|
|
377
|
+
from sage.misc.superseded import deprecation
|
|
378
|
+
deprecation(37090, "The function maximal_order() is deprecated, use the maximal_order() method of quaternion algebras")
|
|
379
|
+
return A.maximal_order()
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def basis_for_left_ideal(R, gens):
|
|
383
|
+
"""
|
|
384
|
+
Return a basis for the left ideal of `R` with given generators.
|
|
385
|
+
|
|
386
|
+
INPUT:
|
|
387
|
+
|
|
388
|
+
- ``R`` -- quaternion order
|
|
389
|
+
- ``gens`` -- list of elements of `R`
|
|
390
|
+
|
|
391
|
+
OUTPUT: list of four elements of `R`
|
|
392
|
+
|
|
393
|
+
EXAMPLES::
|
|
394
|
+
|
|
395
|
+
sage: B = BrandtModule(17); A = B.quaternion_algebra(); i,j,k = A.gens()
|
|
396
|
+
sage: sage.modular.quatalg.brandt.basis_for_left_ideal(B.maximal_order(), [i+j,i-j,2*k,A(3)])
|
|
397
|
+
doctest:...: DeprecationWarning: The function basis_for_left_ideal() is deprecated, use the _left_ideal_basis() method of quaternion algebras
|
|
398
|
+
See https://github.com/sagemath/sage/issues/37090 for details.
|
|
399
|
+
[1, 1/2 + 1/2*i, j, 1/3*i + 1/2*j + 1/6*k]
|
|
400
|
+
sage: sage.modular.quatalg.brandt.basis_for_left_ideal(B.maximal_order(), [3*(i+j),3*(i-j),6*k,A(3)])
|
|
401
|
+
[3, 3/2 + 3/2*i, 3*j, i + 3/2*j + 1/2*k]
|
|
402
|
+
"""
|
|
403
|
+
from sage.misc.superseded import deprecation
|
|
404
|
+
deprecation(37090, "The function basis_for_left_ideal() is deprecated, use the _left_ideal_basis() method of quaternion algebras")
|
|
405
|
+
return R._left_ideal_basis(gens)
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
def right_order(R, basis):
|
|
409
|
+
"""
|
|
410
|
+
Given a basis for a left ideal `I`, return the right order in the
|
|
411
|
+
quaternion order `R` of elements `x` such that `I x` is contained in `I`.
|
|
412
|
+
|
|
413
|
+
INPUT:
|
|
414
|
+
|
|
415
|
+
- ``R`` -- order in quaternion algebra
|
|
416
|
+
- ``basis`` -- basis for an ideal `I`
|
|
417
|
+
|
|
418
|
+
OUTPUT: order in quaternion algebra
|
|
419
|
+
|
|
420
|
+
EXAMPLES:
|
|
421
|
+
|
|
422
|
+
We do a consistency check with the ideal equal to a maximal order::
|
|
423
|
+
|
|
424
|
+
sage: B = BrandtModule(17); basis = B.maximal_order()._left_ideal_basis([1])
|
|
425
|
+
sage: sage.modular.quatalg.brandt.right_order(B.maximal_order(), basis)
|
|
426
|
+
doctest:...: DeprecationWarning: The function right_order() is deprecated, use the _right_order_from_ideal_basis() method of quaternion algebras
|
|
427
|
+
See https://github.com/sagemath/sage/issues/37090 for details.
|
|
428
|
+
Order of Quaternion Algebra (-3, -17) with base ring Rational Field with basis (1/2 + 1/6*i + 1/3*k, 1/3*i + 2/3*k, 1/2*j + 1/2*k, k)
|
|
429
|
+
sage: basis
|
|
430
|
+
[1, 1/2 + 1/2*i, j, 1/3*i + 1/2*j + 1/6*k]
|
|
431
|
+
|
|
432
|
+
sage: B = BrandtModule(17); A = B.quaternion_algebra(); i,j,k = A.gens()
|
|
433
|
+
sage: basis = B.maximal_order()._left_ideal_basis([i*j - j])
|
|
434
|
+
sage: sage.modular.quatalg.brandt.right_order(B.maximal_order(), basis)
|
|
435
|
+
Order of Quaternion Algebra (-3, -17) with base ring Rational Field with basis (1/2 + 1/6*i + 1/3*k, 1/3*i + 2/3*k, 1/2*j + 1/2*k, k)
|
|
436
|
+
"""
|
|
437
|
+
from sage.misc.superseded import deprecation
|
|
438
|
+
deprecation(37090, "The function right_order() is deprecated, use the _right_order_from_ideal_basis() method of quaternion algebras")
|
|
439
|
+
return R._right_order_from_ideal_basis(basis)
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
def quaternion_order_with_given_level(A, level):
|
|
443
|
+
"""
|
|
444
|
+
Return an order in the quaternion algebra A with given level.
|
|
445
|
+
|
|
446
|
+
This is implemented only when the base field is the rational numbers.
|
|
447
|
+
|
|
448
|
+
INPUT:
|
|
449
|
+
|
|
450
|
+
- ``level`` -- the level of the order to be returned. Currently this
|
|
451
|
+
is only implemented when the level is divisible by at
|
|
452
|
+
most one power of a prime that ramifies in this quaternion algebra.
|
|
453
|
+
|
|
454
|
+
EXAMPLES::
|
|
455
|
+
|
|
456
|
+
sage: from sage.modular.quatalg.brandt import quaternion_order_with_given_level, maximal_order
|
|
457
|
+
sage: A.<i,j,k> = QuaternionAlgebra(5)
|
|
458
|
+
sage: level = 2 * 5 * 17
|
|
459
|
+
sage: O = quaternion_order_with_given_level(A, level)
|
|
460
|
+
doctest:...: DeprecationWarning: The function quaternion_order_with_given_level() is deprecated, use the order_with_level() method of quaternion algebras
|
|
461
|
+
See https://github.com/sagemath/sage/issues/37090 for details.
|
|
462
|
+
sage: M = A.maximal_order()
|
|
463
|
+
sage: L = O.free_module()
|
|
464
|
+
sage: N = M.free_module()
|
|
465
|
+
sage: L.index_in(N) == level/5 #check that the order has the right index in the maximal order
|
|
466
|
+
True
|
|
467
|
+
"""
|
|
468
|
+
from sage.misc.superseded import deprecation
|
|
469
|
+
deprecation(37090, "The function quaternion_order_with_given_level() is deprecated, use the order_with_level() method of quaternion algebras")
|
|
470
|
+
return A.order_with_level(level)
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
class BrandtSubmodule(HeckeSubmodule):
|
|
474
|
+
def _repr_(self):
|
|
475
|
+
"""
|
|
476
|
+
Return string representation of this Brandt submodule.
|
|
477
|
+
|
|
478
|
+
EXAMPLES::
|
|
479
|
+
|
|
480
|
+
sage: BrandtModule(11)[0]._repr_()
|
|
481
|
+
'Subspace of dimension 1 of Brandt module of dimension 2 of level 11 of weight 2 over Rational Field'
|
|
482
|
+
"""
|
|
483
|
+
return "Subspace of dimension %s of %s" % (self.dimension(), self.ambient_module())
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
class BrandtModuleElement(HeckeModuleElement):
|
|
487
|
+
def __init__(self, parent, x):
|
|
488
|
+
"""
|
|
489
|
+
EXAMPLES::
|
|
490
|
+
|
|
491
|
+
sage: B = BrandtModule(37)
|
|
492
|
+
sage: x = B([1,2,3]); x
|
|
493
|
+
(1, 2, 3)
|
|
494
|
+
sage: parent(x)
|
|
495
|
+
Brandt module of dimension 3 of level 37 of weight 2 over Rational Field
|
|
496
|
+
"""
|
|
497
|
+
if isinstance(x, HeckeModuleElement):
|
|
498
|
+
x = x.element()
|
|
499
|
+
HeckeModuleElement.__init__(self, parent, parent.free_module()(x))
|
|
500
|
+
|
|
501
|
+
def _richcmp_(self, other, op):
|
|
502
|
+
"""
|
|
503
|
+
EXAMPLES::
|
|
504
|
+
|
|
505
|
+
sage: B = BrandtModule(13,5)
|
|
506
|
+
sage: B.0
|
|
507
|
+
(1, 0, 0, 0, 0, 0)
|
|
508
|
+
sage: B.0 == B.1
|
|
509
|
+
False
|
|
510
|
+
sage: B.0 == 0
|
|
511
|
+
False
|
|
512
|
+
sage: B(0) == 0
|
|
513
|
+
True
|
|
514
|
+
sage: B.0 + 2*B.1 == 2*B.1 + B.0
|
|
515
|
+
True
|
|
516
|
+
sage: loads(dumps(B.0)) == B.0
|
|
517
|
+
True
|
|
518
|
+
"""
|
|
519
|
+
return richcmp(self.element(), other.element(), op)
|
|
520
|
+
|
|
521
|
+
def monodromy_pairing(self, x):
|
|
522
|
+
"""
|
|
523
|
+
Return the monodromy pairing of ``self`` and ``x``.
|
|
524
|
+
|
|
525
|
+
EXAMPLES::
|
|
526
|
+
|
|
527
|
+
sage: B = BrandtModule(5,13)
|
|
528
|
+
sage: B.monodromy_weights()
|
|
529
|
+
(1, 3, 1, 1, 1, 3)
|
|
530
|
+
sage: (B.0 + B.1).monodromy_pairing(B.0 + B.1)
|
|
531
|
+
4
|
|
532
|
+
|
|
533
|
+
TESTS:
|
|
534
|
+
|
|
535
|
+
One check for :issue:`12866`::
|
|
536
|
+
|
|
537
|
+
sage: Br = BrandtModule(2,7)
|
|
538
|
+
sage: g1, g2 = Br.basis()
|
|
539
|
+
sage: g = g1 - g2
|
|
540
|
+
sage: g.monodromy_pairing(g)
|
|
541
|
+
6
|
|
542
|
+
"""
|
|
543
|
+
B = self.parent()
|
|
544
|
+
w = B.monodromy_weights()
|
|
545
|
+
x = B(x).element()
|
|
546
|
+
v = self.element()
|
|
547
|
+
return sum(x[i] * v[i] * w[i] for i in range(len(v)))
|
|
548
|
+
|
|
549
|
+
def __mul__(self, right):
|
|
550
|
+
"""
|
|
551
|
+
Return the monodromy pairing of ``self`` and ``right``.
|
|
552
|
+
|
|
553
|
+
EXAMPLES::
|
|
554
|
+
|
|
555
|
+
sage: B = BrandtModule(7,10)
|
|
556
|
+
sage: B.monodromy_weights()
|
|
557
|
+
(1, 1, 1, 2, 1, 1, 2, 1, 1, 1)
|
|
558
|
+
sage: B.0 * B.0
|
|
559
|
+
1
|
|
560
|
+
sage: B.3 * B.3
|
|
561
|
+
2
|
|
562
|
+
sage: (B.0+B.3) * (B.0 + B.1 + 2*B.3)
|
|
563
|
+
5
|
|
564
|
+
"""
|
|
565
|
+
return self.monodromy_pairing(right)
|
|
566
|
+
|
|
567
|
+
def _add_(self, right):
|
|
568
|
+
"""
|
|
569
|
+
Return the sum of ``self`` and ``right``.
|
|
570
|
+
|
|
571
|
+
EXAMPLES::
|
|
572
|
+
|
|
573
|
+
sage: B = BrandtModule(11)
|
|
574
|
+
sage: B.0 + B.1 # indirect doctest
|
|
575
|
+
(1, 1)
|
|
576
|
+
"""
|
|
577
|
+
return BrandtModuleElement(self.parent(), self.element() + right.element())
|
|
578
|
+
|
|
579
|
+
def _sub_(self, right):
|
|
580
|
+
"""
|
|
581
|
+
Return the difference of ``self`` and ``right``.
|
|
582
|
+
|
|
583
|
+
EXAMPLES::
|
|
584
|
+
|
|
585
|
+
sage: B = BrandtModule(11)
|
|
586
|
+
sage: B.0 - B.1 # indirect doctest
|
|
587
|
+
(1, -1)
|
|
588
|
+
"""
|
|
589
|
+
return BrandtModuleElement(self.parent(), self.element() - right.element())
|
|
590
|
+
|
|
591
|
+
def _neg_(self):
|
|
592
|
+
"""
|
|
593
|
+
Return the opposite of ``self``.
|
|
594
|
+
|
|
595
|
+
EXAMPLES::
|
|
596
|
+
|
|
597
|
+
sage: B = BrandtModule(11)
|
|
598
|
+
sage: -B.0 # indirect doctest
|
|
599
|
+
(-1, 0)
|
|
600
|
+
"""
|
|
601
|
+
return BrandtModuleElement(self.parent(), -self.element())
|
|
602
|
+
|
|
603
|
+
|
|
604
|
+
@richcmp_method
|
|
605
|
+
class BrandtModule_class(AmbientHeckeModule):
|
|
606
|
+
"""
|
|
607
|
+
A Brandt module.
|
|
608
|
+
|
|
609
|
+
EXAMPLES::
|
|
610
|
+
|
|
611
|
+
sage: BrandtModule(3, 10)
|
|
612
|
+
Brandt module of dimension 4 of level 3*10 of weight 2 over Rational Field
|
|
613
|
+
"""
|
|
614
|
+
def __init__(self, N, M, weight, base_ring):
|
|
615
|
+
"""
|
|
616
|
+
INPUT:
|
|
617
|
+
|
|
618
|
+
- ``N`` -- ramification number (coprime to M)
|
|
619
|
+
- ``M`` -- auxiliary level
|
|
620
|
+
- ``weight`` -- integer 2
|
|
621
|
+
- ``base_ring`` -- the base ring
|
|
622
|
+
|
|
623
|
+
EXAMPLES::
|
|
624
|
+
|
|
625
|
+
sage: BrandtModule(3, 5, weight=2, base_ring=ZZ)
|
|
626
|
+
Brandt module of dimension 2 of level 3*5 of weight 2 over Integer Ring
|
|
627
|
+
"""
|
|
628
|
+
assert weight == 2
|
|
629
|
+
self.__N = N
|
|
630
|
+
self.__M = M
|
|
631
|
+
if not N.is_prime():
|
|
632
|
+
raise NotImplementedError("right now N must be prime")
|
|
633
|
+
rank = class_number(N, 1, M)
|
|
634
|
+
self.__key = (N, M, weight, base_ring)
|
|
635
|
+
AmbientHeckeModule.__init__(self, base_ring, rank, N * M, weight=2)
|
|
636
|
+
self._populate_coercion_lists_(coerce_list=[self.free_module()])
|
|
637
|
+
|
|
638
|
+
Element = BrandtModuleElement
|
|
639
|
+
|
|
640
|
+
def _submodule_class(self):
|
|
641
|
+
"""
|
|
642
|
+
Return the Python class of submodules of this ambient Brandt module.
|
|
643
|
+
|
|
644
|
+
EXAMPLES::
|
|
645
|
+
|
|
646
|
+
sage: BrandtModule(37)._submodule_class()
|
|
647
|
+
<class 'sage.modular.quatalg.brandt.BrandtSubmodule'>
|
|
648
|
+
"""
|
|
649
|
+
return BrandtSubmodule
|
|
650
|
+
|
|
651
|
+
@cached_method
|
|
652
|
+
def free_module(self):
|
|
653
|
+
"""
|
|
654
|
+
Return the underlying free module of the Brandt module.
|
|
655
|
+
|
|
656
|
+
EXAMPLES::
|
|
657
|
+
|
|
658
|
+
sage: B = BrandtModule(10007,389)
|
|
659
|
+
sage: B.free_module()
|
|
660
|
+
Vector space of dimension 325196 over Rational Field
|
|
661
|
+
"""
|
|
662
|
+
return self.base_ring() ** self.dimension()
|
|
663
|
+
|
|
664
|
+
def N(self):
|
|
665
|
+
"""
|
|
666
|
+
Return ramification level `N`.
|
|
667
|
+
|
|
668
|
+
EXAMPLES::
|
|
669
|
+
|
|
670
|
+
sage: BrandtModule(7,5,2,ZZ).N()
|
|
671
|
+
7
|
|
672
|
+
"""
|
|
673
|
+
return self.__N
|
|
674
|
+
|
|
675
|
+
def M(self):
|
|
676
|
+
"""
|
|
677
|
+
Return the auxiliary level (prime to `p` part) of the quaternion
|
|
678
|
+
order used to compute this Brandt module.
|
|
679
|
+
|
|
680
|
+
EXAMPLES::
|
|
681
|
+
|
|
682
|
+
sage: BrandtModule(7,5,2,ZZ).M()
|
|
683
|
+
5
|
|
684
|
+
"""
|
|
685
|
+
return self.__M
|
|
686
|
+
|
|
687
|
+
def character(self):
|
|
688
|
+
r"""
|
|
689
|
+
The character of this space.
|
|
690
|
+
|
|
691
|
+
Always trivial.
|
|
692
|
+
|
|
693
|
+
EXAMPLES::
|
|
694
|
+
|
|
695
|
+
sage: BrandtModule(11,5).character()
|
|
696
|
+
Dirichlet character modulo 55 of conductor 1 mapping 12 |--> 1, 46 |--> 1
|
|
697
|
+
"""
|
|
698
|
+
return TrivialCharacter(self.__N * self.__M)
|
|
699
|
+
|
|
700
|
+
def _repr_(self):
|
|
701
|
+
"""
|
|
702
|
+
Return string representation of this Brandt module.
|
|
703
|
+
|
|
704
|
+
EXAMPLES::
|
|
705
|
+
|
|
706
|
+
sage: BrandtModule(7,5,2,ZZ)._repr_()
|
|
707
|
+
'Brandt module of dimension 4 of level 7*5 of weight 2 over Integer Ring'
|
|
708
|
+
"""
|
|
709
|
+
aux = '' if self.__M == 1 else '*%s' % self.__M
|
|
710
|
+
txt = "Brandt module of dimension %s of level %s%s of weight %s over %s"
|
|
711
|
+
return txt % (self.rank(), self.__N, aux, self.weight(), self.base_ring())
|
|
712
|
+
|
|
713
|
+
def __richcmp__(self, other, op):
|
|
714
|
+
r"""
|
|
715
|
+
Compare ``self`` to ``other``.
|
|
716
|
+
|
|
717
|
+
EXAMPLES::
|
|
718
|
+
|
|
719
|
+
sage: BrandtModule(37, 5, 2, ZZ) == BrandtModule(37, 5, 2, QQ)
|
|
720
|
+
False
|
|
721
|
+
sage: BrandtModule(37, 5, 2, ZZ) == BrandtModule(37, 5, 2, ZZ)
|
|
722
|
+
True
|
|
723
|
+
sage: BrandtModule(37, 5, 2, ZZ) == loads(dumps(BrandtModule(37, 5, 2, ZZ)))
|
|
724
|
+
True
|
|
725
|
+
"""
|
|
726
|
+
if not isinstance(other, BrandtModule_class):
|
|
727
|
+
return NotImplemented
|
|
728
|
+
|
|
729
|
+
return richcmp((self.__M, self.__N, self.weight(), self.base_ring()),
|
|
730
|
+
(other.__M, other.__N, other.weight(), other.base_ring()),
|
|
731
|
+
op)
|
|
732
|
+
|
|
733
|
+
@cached_method
|
|
734
|
+
def quaternion_algebra(self):
|
|
735
|
+
r"""
|
|
736
|
+
Return the quaternion algebra `A` over `\QQ` ramified precisely at
|
|
737
|
+
`p` and infinity used to compute this Brandt module.
|
|
738
|
+
|
|
739
|
+
EXAMPLES::
|
|
740
|
+
|
|
741
|
+
sage: BrandtModule(997).quaternion_algebra()
|
|
742
|
+
Quaternion Algebra (-2, -997) with base ring Rational Field
|
|
743
|
+
sage: BrandtModule(2).quaternion_algebra()
|
|
744
|
+
Quaternion Algebra (-1, -1) with base ring Rational Field
|
|
745
|
+
sage: BrandtModule(3).quaternion_algebra()
|
|
746
|
+
Quaternion Algebra (-1, -3) with base ring Rational Field
|
|
747
|
+
sage: BrandtModule(5).quaternion_algebra()
|
|
748
|
+
Quaternion Algebra (-2, -5) with base ring Rational Field
|
|
749
|
+
sage: BrandtModule(17).quaternion_algebra()
|
|
750
|
+
Quaternion Algebra (-3, -17) with base ring Rational Field
|
|
751
|
+
"""
|
|
752
|
+
return QuaternionAlgebra(self.N())
|
|
753
|
+
|
|
754
|
+
def maximal_order(self):
|
|
755
|
+
"""
|
|
756
|
+
Return a maximal order in the quaternion algebra associated to this Brandt module.
|
|
757
|
+
|
|
758
|
+
EXAMPLES::
|
|
759
|
+
|
|
760
|
+
sage: BrandtModule(17).maximal_order()
|
|
761
|
+
Order of Quaternion Algebra (-3, -17) with base ring Rational Field with basis (1/2 + 1/2*i, 1/2*j - 1/2*k, -1/3*i + 1/3*k, -k)
|
|
762
|
+
sage: BrandtModule(17).maximal_order() is BrandtModule(17).maximal_order()
|
|
763
|
+
True
|
|
764
|
+
"""
|
|
765
|
+
return self.quaternion_algebra().maximal_order()
|
|
766
|
+
|
|
767
|
+
@cached_method
|
|
768
|
+
def order_of_level_N(self):
|
|
769
|
+
"""
|
|
770
|
+
Return an order of level `N = p^{2 r + 1} M` in the
|
|
771
|
+
quaternion algebra.
|
|
772
|
+
|
|
773
|
+
EXAMPLES::
|
|
774
|
+
|
|
775
|
+
sage: BrandtModule(7).order_of_level_N()
|
|
776
|
+
Order of Quaternion Algebra (-1, -7) with base ring Rational Field with basis (1/2 + 1/2*j, 1/2*i + 1/2*k, j, k)
|
|
777
|
+
sage: BrandtModule(7,13).order_of_level_N()
|
|
778
|
+
Order of Quaternion Algebra (-1, -7) with base ring Rational Field with basis (1/2 + 1/2*j + 12*k, 1/2*i + 9/2*k, j + 11*k, 13*k)
|
|
779
|
+
sage: BrandtModule(7,3*17).order_of_level_N()
|
|
780
|
+
Order of Quaternion Algebra (-1, -7) with base ring Rational Field with basis (1/2 + 1/2*j + 35*k, 1/2*i + 65/2*k, j + 19*k, 51*k)
|
|
781
|
+
"""
|
|
782
|
+
return self.quaternion_algebra().order_with_level(self.level())
|
|
783
|
+
|
|
784
|
+
def cyclic_submodules(self, I, p):
|
|
785
|
+
"""
|
|
786
|
+
Return a list of rescaled versions of the fractional right
|
|
787
|
+
ideals `J` such that `J` contains `I` and the quotient has
|
|
788
|
+
group structure the product of two cyclic groups of order `p`.
|
|
789
|
+
|
|
790
|
+
We emphasize again that `J` is rescaled to be integral.
|
|
791
|
+
|
|
792
|
+
INPUT:
|
|
793
|
+
|
|
794
|
+
- ``I`` -- ideal `I` in ``R = self.order_of_level_N()``
|
|
795
|
+
- ``p`` -- prime `p` coprime to ``self.level()``
|
|
796
|
+
|
|
797
|
+
OUTPUT:
|
|
798
|
+
|
|
799
|
+
list of the `p+1` fractional right R-ideals that contain I
|
|
800
|
+
such that J/I is GF(p) x GF(p).
|
|
801
|
+
|
|
802
|
+
EXAMPLES::
|
|
803
|
+
|
|
804
|
+
sage: B = BrandtModule(11)
|
|
805
|
+
sage: I = B.order_of_level_N().unit_ideal()
|
|
806
|
+
sage: B.cyclic_submodules(I, 2)
|
|
807
|
+
[Fractional ideal (2, 2*i, 3/2 + i + 1/2*j, 1 + 1/2*i + 1/2*k),
|
|
808
|
+
Fractional ideal (2, 1 + i, 1 + j, 1/2 + 1/2*i + 1/2*j + 1/2*k),
|
|
809
|
+
Fractional ideal (2, 2*i, 1/2 + i + 1/2*j, 1 + 3/2*i + 1/2*k)]
|
|
810
|
+
sage: B.cyclic_submodules(I, 3)
|
|
811
|
+
[Fractional ideal (3, 3*i, 1/2 + 1/2*j, 5/2*i + 1/2*k),
|
|
812
|
+
Fractional ideal (3, 3*i, 3/2 + 2*i + 1/2*j, 2 + 3/2*i + 1/2*k),
|
|
813
|
+
Fractional ideal (3, 3*i, 3/2 + i + 1/2*j, 1 + 3/2*i + 1/2*k),
|
|
814
|
+
Fractional ideal (3, 3*i, 5/2 + 1/2*j, 1/2*i + 1/2*k)]
|
|
815
|
+
sage: B.cyclic_submodules(I, 11)
|
|
816
|
+
Traceback (most recent call last):
|
|
817
|
+
...
|
|
818
|
+
ValueError: p must be coprime to the level
|
|
819
|
+
"""
|
|
820
|
+
if not Integer(p).is_prime():
|
|
821
|
+
raise ValueError("p must be a prime")
|
|
822
|
+
if not self.level() % p:
|
|
823
|
+
raise ValueError("p must be coprime to the level")
|
|
824
|
+
|
|
825
|
+
R = self.order_of_level_N()
|
|
826
|
+
A = R.quaternion_algebra()
|
|
827
|
+
B = R.basis()
|
|
828
|
+
V = GF(p)**4
|
|
829
|
+
|
|
830
|
+
# step 1: Compute alpha, beta, and the matrix of their action on I/pI.
|
|
831
|
+
# NOTE: Move this code to orders once we have it all working...
|
|
832
|
+
try:
|
|
833
|
+
alpha, beta = self.__cyclic_submodules[p]
|
|
834
|
+
compute = False
|
|
835
|
+
except AttributeError:
|
|
836
|
+
self.__cyclic_submodules = {}
|
|
837
|
+
compute = True
|
|
838
|
+
except KeyError:
|
|
839
|
+
compute = True
|
|
840
|
+
|
|
841
|
+
if compute:
|
|
842
|
+
d = R.free_module().basis_matrix().determinant()
|
|
843
|
+
S = None
|
|
844
|
+
for v in V:
|
|
845
|
+
if not v:
|
|
846
|
+
continue
|
|
847
|
+
alpha = sum(Integer(v[i]) * B[i] for i in range(4))
|
|
848
|
+
# If the quadratic polynomial over GF(p) given by
|
|
849
|
+
# X^2 - alpha.reduced_trace() * X + alpha.reduced_norm()
|
|
850
|
+
# is not irreducible, we try again with a new element.
|
|
851
|
+
if p == 2:
|
|
852
|
+
# special case p == 2, since there is a unique quadratic irreducible poly.
|
|
853
|
+
if alpha.reduced_trace() % 2 == 0 or alpha.reduced_norm() % 2 == 0:
|
|
854
|
+
continue
|
|
855
|
+
else:
|
|
856
|
+
# check if the discriminant is a square -- if so, poly is reducible
|
|
857
|
+
b = alpha.reduced_trace()
|
|
858
|
+
c = alpha.reduced_norm()
|
|
859
|
+
if kronecker(b * b - 4 * c, p) != -1:
|
|
860
|
+
continue
|
|
861
|
+
for w in V:
|
|
862
|
+
if not w:
|
|
863
|
+
continue
|
|
864
|
+
beta = sum(Integer(w[i]) * B[i] for i in range(4))
|
|
865
|
+
v = [A(1), alpha, beta, alpha * beta]
|
|
866
|
+
M = rational_matrix_from_rational_quaternions(v)
|
|
867
|
+
e = M.determinant()
|
|
868
|
+
if e and not (d / e).valuation(p):
|
|
869
|
+
S = A.quaternion_order(v)
|
|
870
|
+
break
|
|
871
|
+
if S is not None:
|
|
872
|
+
break
|
|
873
|
+
self.__cyclic_submodules[p] = (alpha, beta)
|
|
874
|
+
|
|
875
|
+
# right multiplication by X changes something to be written
|
|
876
|
+
# in terms of the basis for I.
|
|
877
|
+
basis = basis_for_quaternion_lattice(I.basis(), reverse=False)
|
|
878
|
+
Y = matrix(map(list, basis))
|
|
879
|
+
X = ~Y
|
|
880
|
+
|
|
881
|
+
# Compute the matrix of right multiplication by alpha acting on
|
|
882
|
+
# our fixed choice of basis for this ideal.
|
|
883
|
+
|
|
884
|
+
M_alpha = (matrix([(i * alpha).coefficient_tuple()
|
|
885
|
+
for i in basis]) * X).change_ring(GF(p))
|
|
886
|
+
M_beta = (matrix([(i * beta).coefficient_tuple()
|
|
887
|
+
for i in basis]) * X).change_ring(GF(p))
|
|
888
|
+
|
|
889
|
+
# step 2: Find j such that if f=I[j], then mod 2 we have span(I[0],alpha*I[i])
|
|
890
|
+
# has trivial intersection with span(I[j],alpha*I[j]).
|
|
891
|
+
#
|
|
892
|
+
# In terms of our matrices alpha, beta, we can now think of I/p*I
|
|
893
|
+
# as being the GF(p)^4 that M_alpha and M_beta naturally act on,
|
|
894
|
+
# and I[0], I[1], I[2], I[3] correspond to the standard basis.
|
|
895
|
+
#
|
|
896
|
+
# We try each of the standard basis vectors.
|
|
897
|
+
W0 = V.span([V.gen(0), V.gen(0) * M_alpha])
|
|
898
|
+
assert W0.dimension() == 2
|
|
899
|
+
j = None
|
|
900
|
+
for i in range(1, 4):
|
|
901
|
+
Wi = V.span([V.gen(i), V.gen(i) * M_alpha])
|
|
902
|
+
if Wi.dimension() == 2 and W0.intersection(Wi).dimension() == 0:
|
|
903
|
+
j = i
|
|
904
|
+
break
|
|
905
|
+
assert j is not None, "bug -- couldn't find basis"
|
|
906
|
+
|
|
907
|
+
# step 3: Enumerate the elements of P^1(GF(p^2)), recording each
|
|
908
|
+
# cyclic submodule of degree p.
|
|
909
|
+
answer = []
|
|
910
|
+
f = V.gen(0)
|
|
911
|
+
g = V.gen(j)
|
|
912
|
+
M2_4 = MatrixSpace(GF(p), 4)
|
|
913
|
+
M2_2 = MatrixSpace(QQ, 2, 4)
|
|
914
|
+
Yp = p * Y
|
|
915
|
+
from sage.algebras.quatalg.quaternion_algebra_cython import \
|
|
916
|
+
rational_quaternions_from_integral_matrix_and_denom
|
|
917
|
+
for v in [f + g * (a + b * M_alpha)
|
|
918
|
+
for a in GF(p) for b in GF(p)] + [g]:
|
|
919
|
+
v0 = v
|
|
920
|
+
v1 = v * M_alpha
|
|
921
|
+
v2 = v * M_beta
|
|
922
|
+
v3 = v1 * M_beta
|
|
923
|
+
W = M2_4([v0, v1, v2, v3], coerce=False)
|
|
924
|
+
if W.rank() == 2:
|
|
925
|
+
gen_mat = Yp.stack(M2_2([v0.lift() * Y, v1.lift() * Y],
|
|
926
|
+
coerce=False))
|
|
927
|
+
gen_mat, d = gen_mat._clear_denom()
|
|
928
|
+
H = gen_mat._hnf_pari(0, include_zero_rows=False)
|
|
929
|
+
gens = tuple(rational_quaternions_from_integral_matrix_and_denom(A, H, d))
|
|
930
|
+
answer.append(R.right_ideal(gens, check=False))
|
|
931
|
+
if len(answer) == p + 1:
|
|
932
|
+
break
|
|
933
|
+
return answer
|
|
934
|
+
|
|
935
|
+
def hecke_matrix(self, n, algorithm='default', sparse=False, B=None):
|
|
936
|
+
"""
|
|
937
|
+
Return the matrix of the `n`-th Hecke operator.
|
|
938
|
+
|
|
939
|
+
INPUT:
|
|
940
|
+
|
|
941
|
+
- ``n`` -- integer
|
|
942
|
+
|
|
943
|
+
- ``algorithm`` -- string (default: ``'default'``)
|
|
944
|
+
|
|
945
|
+
- ``'default'`` -- let Sage guess which algorithm is best
|
|
946
|
+
|
|
947
|
+
- ``'direct'`` -- use cyclic subideals (generally much
|
|
948
|
+
better when you want few Hecke operators and the
|
|
949
|
+
dimension is very large); uses 'theta' if n divides
|
|
950
|
+
the level.
|
|
951
|
+
|
|
952
|
+
- ``'brandt'`` -- use Brandt matrices (generally much
|
|
953
|
+
better when you want many Hecke operators and the
|
|
954
|
+
dimension is very small; bad when the dimension
|
|
955
|
+
is large)
|
|
956
|
+
|
|
957
|
+
- ``sparse`` -- boolean (default: ``False``)
|
|
958
|
+
|
|
959
|
+
- ``B`` -- integer or ``None`` (default: ``None``); in direct
|
|
960
|
+
algorithm, use theta series to this precision as an initial
|
|
961
|
+
check for equality of ideal classes.
|
|
962
|
+
|
|
963
|
+
EXAMPLES::
|
|
964
|
+
|
|
965
|
+
sage: B = BrandtModule(3,7); B.hecke_matrix(2)
|
|
966
|
+
[0 3]
|
|
967
|
+
[1 2]
|
|
968
|
+
sage: B.hecke_matrix(5, algorithm='brandt')
|
|
969
|
+
[0 6]
|
|
970
|
+
[2 4]
|
|
971
|
+
sage: t = B.hecke_matrix(11, algorithm='brandt', sparse=True); t
|
|
972
|
+
[ 6 6]
|
|
973
|
+
[ 2 10]
|
|
974
|
+
sage: type(t)
|
|
975
|
+
<class 'sage.matrix.matrix_rational_sparse.Matrix_rational_sparse'>
|
|
976
|
+
sage: B.hecke_matrix(19, algorithm='direct', B=2)
|
|
977
|
+
[ 8 12]
|
|
978
|
+
[ 4 16]
|
|
979
|
+
"""
|
|
980
|
+
n = ZZ(n)
|
|
981
|
+
if n <= 0:
|
|
982
|
+
raise IndexError("n must be positive")
|
|
983
|
+
if n not in self._hecke_matrices:
|
|
984
|
+
if algorithm == 'default':
|
|
985
|
+
try:
|
|
986
|
+
pr = len(self.__brandt_series_vectors[0][0])
|
|
987
|
+
except (AttributeError, IndexError):
|
|
988
|
+
pr = 0
|
|
989
|
+
if n <= pr:
|
|
990
|
+
# already trivially know the Hecke operator in this case
|
|
991
|
+
algorithm = 'brandt'
|
|
992
|
+
if algorithm == 'default': # still don't know
|
|
993
|
+
algorithm = 'direct'
|
|
994
|
+
|
|
995
|
+
if self.level().gcd(n) != 1:
|
|
996
|
+
algorithm = 'brandt'
|
|
997
|
+
|
|
998
|
+
if algorithm == 'direct':
|
|
999
|
+
T = self._compute_hecke_matrix(n, sparse=sparse, B=B)
|
|
1000
|
+
elif algorithm == 'brandt':
|
|
1001
|
+
T = self._compute_hecke_matrix_brandt(n, sparse=sparse)
|
|
1002
|
+
else:
|
|
1003
|
+
raise ValueError(f"unknown algorithm '{algorithm}'")
|
|
1004
|
+
T.set_immutable()
|
|
1005
|
+
self._hecke_matrices[n] = T
|
|
1006
|
+
return self._hecke_matrices[n]
|
|
1007
|
+
|
|
1008
|
+
def _compute_hecke_matrix_prime(self, p, sparse=False, B=None):
|
|
1009
|
+
"""
|
|
1010
|
+
Return matrix of the `p`-th Hecke operator on ``self``. The matrix
|
|
1011
|
+
is always computed using the direct algorithm.
|
|
1012
|
+
|
|
1013
|
+
INPUT:
|
|
1014
|
+
|
|
1015
|
+
- ``p`` -- prime number
|
|
1016
|
+
|
|
1017
|
+
- ``B`` -- integer or ``None`` (default: ``None``); in direct algorithm,
|
|
1018
|
+
use theta series to this precision as an initial check for
|
|
1019
|
+
equality of ideal classes.
|
|
1020
|
+
|
|
1021
|
+
- ``sparse`` -- boolean (default: ``False``); whether matrix should be sparse
|
|
1022
|
+
|
|
1023
|
+
EXAMPLES::
|
|
1024
|
+
|
|
1025
|
+
sage: B = BrandtModule(37)
|
|
1026
|
+
sage: t = B._compute_hecke_matrix_prime(2); t
|
|
1027
|
+
[1 1 1]
|
|
1028
|
+
[1 0 2]
|
|
1029
|
+
[1 2 0]
|
|
1030
|
+
sage: type(t)
|
|
1031
|
+
<class 'sage.matrix.matrix_rational_dense.Matrix_rational_dense'>
|
|
1032
|
+
sage: type(B._compute_hecke_matrix_prime(2,sparse=True))
|
|
1033
|
+
<class 'sage.matrix.matrix_rational_sparse.Matrix_rational_sparse'>
|
|
1034
|
+
"""
|
|
1035
|
+
return self._compute_hecke_matrix_directly(n=p, B=B, sparse=sparse)
|
|
1036
|
+
|
|
1037
|
+
def _compute_hecke_matrix_directly(self, n, B=None, sparse=False):
|
|
1038
|
+
"""
|
|
1039
|
+
Given an integer `n` coprime to the level, return the matrix of
|
|
1040
|
+
the `n`-th Hecke operator on ``self``, computed on our fixed basis
|
|
1041
|
+
by directly using the definition of the Hecke action in terms
|
|
1042
|
+
of fractional ideals.
|
|
1043
|
+
|
|
1044
|
+
INPUT:
|
|
1045
|
+
|
|
1046
|
+
- ``n`` -- integer, coprime to level
|
|
1047
|
+
|
|
1048
|
+
- ``sparse`` -- boolean (default: ``False``); whether matrix should be sparse
|
|
1049
|
+
|
|
1050
|
+
EXAMPLES::
|
|
1051
|
+
|
|
1052
|
+
sage: B = BrandtModule(37)
|
|
1053
|
+
sage: t = B._compute_hecke_matrix_directly(2); t
|
|
1054
|
+
[1 1 1]
|
|
1055
|
+
[1 0 2]
|
|
1056
|
+
[1 2 0]
|
|
1057
|
+
sage: type(t)
|
|
1058
|
+
<class 'sage.matrix.matrix_rational_dense.Matrix_rational_dense'>
|
|
1059
|
+
sage: type(B._compute_hecke_matrix_directly(2,sparse=True))
|
|
1060
|
+
<class 'sage.matrix.matrix_rational_sparse.Matrix_rational_sparse'>
|
|
1061
|
+
|
|
1062
|
+
You can't compute the Hecke operator for n not coprime to the level using this function::
|
|
1063
|
+
|
|
1064
|
+
sage: B._compute_hecke_matrix_directly(37)
|
|
1065
|
+
Traceback (most recent call last):
|
|
1066
|
+
...
|
|
1067
|
+
ValueError: n must be coprime to the level
|
|
1068
|
+
|
|
1069
|
+
The generic function (which uses theta series) does work, though::
|
|
1070
|
+
|
|
1071
|
+
sage: B.hecke_matrix(37)
|
|
1072
|
+
[1 0 0]
|
|
1073
|
+
[0 0 1]
|
|
1074
|
+
[0 1 0]
|
|
1075
|
+
|
|
1076
|
+
An example where the Hecke operator isn't symmetric::
|
|
1077
|
+
|
|
1078
|
+
sage: B = BrandtModule(43)
|
|
1079
|
+
sage: B._compute_hecke_matrix_directly(2)
|
|
1080
|
+
[1 2 0 0]
|
|
1081
|
+
[1 0 1 1]
|
|
1082
|
+
[0 1 0 2]
|
|
1083
|
+
[0 1 2 0]
|
|
1084
|
+
sage: B._compute_hecke_matrix_brandt(2)
|
|
1085
|
+
[1 2 0 0]
|
|
1086
|
+
[1 0 1 1]
|
|
1087
|
+
[0 1 0 2]
|
|
1088
|
+
[0 1 2 0]
|
|
1089
|
+
"""
|
|
1090
|
+
level = self.level()
|
|
1091
|
+
if gcd(n, level) != 1:
|
|
1092
|
+
raise ValueError("n must be coprime to the level")
|
|
1093
|
+
|
|
1094
|
+
# For rigor it does not matter at all what bound we chose.
|
|
1095
|
+
# This B is used only for the first phase of checking equality
|
|
1096
|
+
# of ideals modulo equivalence -- we always provably check
|
|
1097
|
+
# equivalence if the theta series are the same up to this
|
|
1098
|
+
# bound.
|
|
1099
|
+
if B is None:
|
|
1100
|
+
B = self.dimension() // 2 + 5
|
|
1101
|
+
|
|
1102
|
+
T = matrix(self.base_ring(), self.dimension(), sparse=sparse)
|
|
1103
|
+
C = self.right_ideals()
|
|
1104
|
+
theta_dict = self._theta_dict(B)
|
|
1105
|
+
# I think the runtime of this algorithm is now dominated by
|
|
1106
|
+
# computing theta series of ideals. The computation of
|
|
1107
|
+
# cyclic submodules is a lower order term.
|
|
1108
|
+
|
|
1109
|
+
# TODO: temporary!! -- it's not sufficiently *optimized* to be
|
|
1110
|
+
# sure this is best in these cases.
|
|
1111
|
+
# d = lcm([a.denominator() for a in self.order_of_level_N().basis()])
|
|
1112
|
+
# q = self._smallest_good_prime()
|
|
1113
|
+
# if gcd(2*d*q,n) == 1:
|
|
1114
|
+
# use_fast_alg = True
|
|
1115
|
+
# else:
|
|
1116
|
+
# use_fast_alg = False
|
|
1117
|
+
|
|
1118
|
+
use_fast_alg = False
|
|
1119
|
+
|
|
1120
|
+
last_percent = 0
|
|
1121
|
+
for r in range(len(C)):
|
|
1122
|
+
percent_done = 100 * r // len(C)
|
|
1123
|
+
if percent_done != last_percent:
|
|
1124
|
+
if not percent_done % 5:
|
|
1125
|
+
verbose("percent done: %s" % percent_done)
|
|
1126
|
+
last_percent = percent_done
|
|
1127
|
+
if use_fast_alg:
|
|
1128
|
+
v = C[r].cyclic_right_subideals(n)
|
|
1129
|
+
else:
|
|
1130
|
+
v = self.cyclic_submodules(C[r], n)
|
|
1131
|
+
for J in v:
|
|
1132
|
+
J_theta = tuple(J.theta_series_vector(B))
|
|
1133
|
+
v = theta_dict[J_theta]
|
|
1134
|
+
if len(v) == 1:
|
|
1135
|
+
T[r, v[0]] += 1
|
|
1136
|
+
else:
|
|
1137
|
+
for i in v:
|
|
1138
|
+
if C[i].is_right_equivalent(J, 0):
|
|
1139
|
+
T[r, i] += 1
|
|
1140
|
+
break
|
|
1141
|
+
return T
|
|
1142
|
+
|
|
1143
|
+
@cached_method
|
|
1144
|
+
def _theta_dict(self, B):
|
|
1145
|
+
"""
|
|
1146
|
+
Return a dictionary from theta series vectors of degree `B` to
|
|
1147
|
+
list of integers `i`, where the key is the vector of
|
|
1148
|
+
coefficients of the normalized theta series of the `i`-th right
|
|
1149
|
+
ideal, as indexed by ``self.right_ideals()``.
|
|
1150
|
+
|
|
1151
|
+
INPUT:
|
|
1152
|
+
|
|
1153
|
+
- ``B`` -- positive integer, precision of theta series vectors
|
|
1154
|
+
|
|
1155
|
+
OUTPUT: dictionary
|
|
1156
|
+
|
|
1157
|
+
EXAMPLES:
|
|
1158
|
+
|
|
1159
|
+
In this example the theta series determine the ideal classes::
|
|
1160
|
+
|
|
1161
|
+
sage: B = BrandtModule(5,11); B
|
|
1162
|
+
Brandt module of dimension 4 of level 5*11 of weight 2 over Rational Field
|
|
1163
|
+
sage: sorted(list(B._theta_dict(5).items()))
|
|
1164
|
+
[((1, 0, 0, 4, 0), [3]),
|
|
1165
|
+
((1, 0, 0, 4, 2), [2]),
|
|
1166
|
+
((1, 0, 2, 0, 6), [1]),
|
|
1167
|
+
((1, 2, 4, 0, 6), [0])]
|
|
1168
|
+
|
|
1169
|
+
In this example, the theta series does not determine the ideal class::
|
|
1170
|
+
|
|
1171
|
+
sage: sorted(list(BrandtModule(37)._theta_dict(6).items()))
|
|
1172
|
+
[((1, 0, 2, 2, 6, 4), [1, 2]), ((1, 2, 2, 4, 2, 4), [0])]
|
|
1173
|
+
"""
|
|
1174
|
+
C = self.right_ideals()
|
|
1175
|
+
theta_dict = {}
|
|
1176
|
+
for i in range(len(C)):
|
|
1177
|
+
I_theta = tuple(C[i].theta_series_vector(B))
|
|
1178
|
+
if I_theta in theta_dict:
|
|
1179
|
+
theta_dict[I_theta].append(i)
|
|
1180
|
+
else:
|
|
1181
|
+
theta_dict[I_theta] = [i]
|
|
1182
|
+
return theta_dict
|
|
1183
|
+
|
|
1184
|
+
def _compute_hecke_matrix_brandt(self, n, sparse=False):
|
|
1185
|
+
"""
|
|
1186
|
+
Return the `n`-th Hecke matrix, computed using Brandt matrices
|
|
1187
|
+
(theta series).
|
|
1188
|
+
|
|
1189
|
+
When the `n`-th Hecke operator is requested, we computed theta
|
|
1190
|
+
series to precision `2n+20`, since it only takes slightly
|
|
1191
|
+
longer, and this means that any Hecke operator `T_m` can
|
|
1192
|
+
quickly be computed, for `m<2n+20`.
|
|
1193
|
+
|
|
1194
|
+
INPUT:
|
|
1195
|
+
|
|
1196
|
+
- ``n`` -- integer, coprime to level
|
|
1197
|
+
- ``sparse`` -- boolean (default: ``False``); whether matrix should be sparse
|
|
1198
|
+
|
|
1199
|
+
EXAMPLES::
|
|
1200
|
+
|
|
1201
|
+
sage: B = BrandtModule(3,17)
|
|
1202
|
+
sage: B._compute_hecke_matrix_brandt(3)
|
|
1203
|
+
[0 1 0 0]
|
|
1204
|
+
[1 0 0 0]
|
|
1205
|
+
[0 0 0 1]
|
|
1206
|
+
[0 0 1 0]
|
|
1207
|
+
sage: B._compute_hecke_matrix_brandt(5)
|
|
1208
|
+
[4 1 1 0]
|
|
1209
|
+
[1 4 0 1]
|
|
1210
|
+
[2 0 2 2]
|
|
1211
|
+
[0 2 2 2]
|
|
1212
|
+
sage: B._compute_hecke_matrix_brandt(5).fcp()
|
|
1213
|
+
(x - 6) * (x - 3) * (x^2 - 3*x - 2)
|
|
1214
|
+
"""
|
|
1215
|
+
# we go out to 2*n+20 for efficiency, since it takes only a
|
|
1216
|
+
# little longer, but saves a lot of time if one computes
|
|
1217
|
+
# successive Hecke operators, which is a very common thing to
|
|
1218
|
+
# do.
|
|
1219
|
+
B = self._brandt_series_vectors()
|
|
1220
|
+
if len(B[0][0]) <= n:
|
|
1221
|
+
B = self._brandt_series_vectors(2 * n + 10)
|
|
1222
|
+
m = len(B)
|
|
1223
|
+
K = self.base_ring()
|
|
1224
|
+
return matrix(K, m, m, {(i, j): K(B[j][i][n])
|
|
1225
|
+
for i in range(m)
|
|
1226
|
+
for j in range(m)}, sparse=sparse)
|
|
1227
|
+
|
|
1228
|
+
@cached_method
|
|
1229
|
+
def _smallest_good_prime(self):
|
|
1230
|
+
"""
|
|
1231
|
+
Return the smallest prime number that does not divide the level.
|
|
1232
|
+
|
|
1233
|
+
EXAMPLES::
|
|
1234
|
+
|
|
1235
|
+
sage: BrandtModule(17,6)._smallest_good_prime()
|
|
1236
|
+
5
|
|
1237
|
+
"""
|
|
1238
|
+
level = self.level()
|
|
1239
|
+
p = ZZ(2)
|
|
1240
|
+
while not level % p:
|
|
1241
|
+
p = next_prime(p)
|
|
1242
|
+
return p
|
|
1243
|
+
|
|
1244
|
+
@cached_method
|
|
1245
|
+
def right_ideals(self, B=None):
|
|
1246
|
+
"""
|
|
1247
|
+
Return sorted tuple of representatives for the equivalence
|
|
1248
|
+
classes of right ideals in ``self``.
|
|
1249
|
+
|
|
1250
|
+
OUTPUT: sorted tuple of fractional ideals
|
|
1251
|
+
|
|
1252
|
+
EXAMPLES::
|
|
1253
|
+
|
|
1254
|
+
sage: B = BrandtModule(23)
|
|
1255
|
+
sage: B.right_ideals()
|
|
1256
|
+
(Fractional ideal (4, 4*i, 2 + 2*j, 2*i + 2*k),
|
|
1257
|
+
Fractional ideal (8, 8*i, 2 + 2*j, 6*i + 2*k),
|
|
1258
|
+
Fractional ideal (16, 16*i, 10 + 8*i + 2*j, 8 + 6*i + 2*k))
|
|
1259
|
+
|
|
1260
|
+
TESTS::
|
|
1261
|
+
|
|
1262
|
+
sage: B = BrandtModule(1009)
|
|
1263
|
+
sage: Is = B.right_ideals()
|
|
1264
|
+
sage: n = len(Is)
|
|
1265
|
+
sage: prod(not Is[i].is_right_equivalent(Is[j]) for i in range(n) for j in range(i))
|
|
1266
|
+
1
|
|
1267
|
+
"""
|
|
1268
|
+
# TODO: move this code to orders, along with cyclic_submodules()
|
|
1269
|
+
p = self._smallest_good_prime()
|
|
1270
|
+
R = self.order_of_level_N()
|
|
1271
|
+
I = R.unit_ideal()
|
|
1272
|
+
I = R.right_ideal([4 * x for x in I.basis()])
|
|
1273
|
+
|
|
1274
|
+
if B is None:
|
|
1275
|
+
B = self.dimension() // 2 + 5
|
|
1276
|
+
|
|
1277
|
+
ideals = [I]
|
|
1278
|
+
ideals_theta = {tuple(I.theta_series_vector(B)): [I]}
|
|
1279
|
+
new_ideals = [I]
|
|
1280
|
+
|
|
1281
|
+
newly_computed_ideals = []
|
|
1282
|
+
got_something_new = True
|
|
1283
|
+
|
|
1284
|
+
while got_something_new:
|
|
1285
|
+
got_something_new = False
|
|
1286
|
+
newly_computed_ideals = []
|
|
1287
|
+
for I in new_ideals:
|
|
1288
|
+
L = self.cyclic_submodules(I, p)
|
|
1289
|
+
for J in L:
|
|
1290
|
+
is_new = True
|
|
1291
|
+
J_theta = tuple(J.theta_series_vector(B))
|
|
1292
|
+
if J_theta in ideals_theta:
|
|
1293
|
+
for K in ideals_theta[J_theta]:
|
|
1294
|
+
if J.is_right_equivalent(K, 0):
|
|
1295
|
+
is_new = False
|
|
1296
|
+
break
|
|
1297
|
+
if is_new:
|
|
1298
|
+
newly_computed_ideals.append(J)
|
|
1299
|
+
ideals.append(J)
|
|
1300
|
+
if J_theta in ideals_theta:
|
|
1301
|
+
ideals_theta[J_theta].append(J)
|
|
1302
|
+
else:
|
|
1303
|
+
ideals_theta[J_theta] = [J]
|
|
1304
|
+
verbose("found %s of %s ideals" % (len(ideals), self.dimension()), level=2)
|
|
1305
|
+
if len(ideals) >= self.dimension():
|
|
1306
|
+
# order by basis matrix (as ideals were previously
|
|
1307
|
+
# ordered) for backward compatibility and
|
|
1308
|
+
# deterministic order of the output
|
|
1309
|
+
ideals = tuple(sorted(ideals, key=lambda x: x.basis_matrix()))
|
|
1310
|
+
self.__right_ideals = ideals
|
|
1311
|
+
return ideals
|
|
1312
|
+
got_something_new = True
|
|
1313
|
+
new_ideals = list(newly_computed_ideals)
|
|
1314
|
+
|
|
1315
|
+
return tuple(sorted(ideals))
|
|
1316
|
+
|
|
1317
|
+
@cached_method
|
|
1318
|
+
def _ideal_products(self, diagonal_only=False):
|
|
1319
|
+
"""
|
|
1320
|
+
Return all products of right ideals, which are used in computing
|
|
1321
|
+
the Brandt matrices.
|
|
1322
|
+
|
|
1323
|
+
This function is used internally by the Brandt matrices
|
|
1324
|
+
algorithms.
|
|
1325
|
+
|
|
1326
|
+
INPUT:
|
|
1327
|
+
|
|
1328
|
+
- ``diagonal_only`` -- boolean (default: ``False``); if ``True`` returns
|
|
1329
|
+
only the diagonal ideal products
|
|
1330
|
+
|
|
1331
|
+
OUTPUT: list of ideals
|
|
1332
|
+
|
|
1333
|
+
EXAMPLES::
|
|
1334
|
+
|
|
1335
|
+
sage: B = BrandtModule(37)
|
|
1336
|
+
sage: B._ideal_products()
|
|
1337
|
+
[[Fractional ideal (16, 16*i, 8 + 8*i + 8*j, 8 + 12*i + 4*k)],
|
|
1338
|
+
[Fractional ideal (32, 32*i, 8 + 24*i + 8*j, 24 + 12*i + 4*k),
|
|
1339
|
+
Fractional ideal (32, 64*i, 16 + 48*i + 16*j, 36*i + 8*j + 4*k)],
|
|
1340
|
+
[Fractional ideal (32, 32*i, 8 + 8*i + 8*j, 8 + 12*i + 4*k),
|
|
1341
|
+
Fractional ideal (64, 32 + 32*i, 16 + 16*i + 16*j, 40 + 12*i + 4*k),
|
|
1342
|
+
Fractional ideal (32, 64*i, 16 + 16*i + 16*j, 16 + 52*i + 8*j + 4*k)]]
|
|
1343
|
+
sage: B._ideal_products(diagonal_only=True)
|
|
1344
|
+
[Fractional ideal (16, 16*i, 8 + 8*i + 8*j, 8 + 12*i + 4*k),
|
|
1345
|
+
Fractional ideal (32, 64*i, 16 + 48*i + 16*j, 36*i + 8*j + 4*k),
|
|
1346
|
+
Fractional ideal (32, 64*i, 16 + 16*i + 16*j, 16 + 52*i + 8*j + 4*k)]
|
|
1347
|
+
"""
|
|
1348
|
+
L = self.right_ideals()
|
|
1349
|
+
n = len(L)
|
|
1350
|
+
if not n:
|
|
1351
|
+
return matrix(self.base_ring()[['q']], 0)
|
|
1352
|
+
|
|
1353
|
+
# 1. Compute the diagonal
|
|
1354
|
+
D = [I.multiply_by_conjugate(I) for I in L]
|
|
1355
|
+
|
|
1356
|
+
if diagonal_only:
|
|
1357
|
+
return D
|
|
1358
|
+
|
|
1359
|
+
# 2. Compute the rest of the products
|
|
1360
|
+
P = []
|
|
1361
|
+
for i in range(n):
|
|
1362
|
+
v = [L[i].multiply_by_conjugate(L[j]) for j in range(i)]
|
|
1363
|
+
v.append(D[i])
|
|
1364
|
+
P.append(v)
|
|
1365
|
+
return P
|
|
1366
|
+
|
|
1367
|
+
def _brandt_series_vectors(self, prec=None):
|
|
1368
|
+
"""
|
|
1369
|
+
Return Brandt series coefficient vectors out to precision *at least* prec.
|
|
1370
|
+
|
|
1371
|
+
EXAMPLES::
|
|
1372
|
+
|
|
1373
|
+
sage: B = BrandtModule(37, use_cache=False)
|
|
1374
|
+
sage: B._brandt_series_vectors(5)
|
|
1375
|
+
[[(1/2, 1, 1, 2, 1), (1/2, 0, 1, 1, 3), (1/2, 0, 1, 1, 3)],
|
|
1376
|
+
[(1/2, 0, 1, 1, 3), (1/2, 1, 0, 0, 3), (1/2, 0, 2, 3, 1)],
|
|
1377
|
+
[(1/2, 0, 1, 1, 3), (1/2, 0, 2, 3, 1), (1/2, 1, 0, 0, 3)]]
|
|
1378
|
+
|
|
1379
|
+
If you have computed to higher precision and ask for a lower
|
|
1380
|
+
precision, the higher precision is still returned::
|
|
1381
|
+
|
|
1382
|
+
sage: B._brandt_series_vectors(2)
|
|
1383
|
+
[[(1/2, 1, 1, 2, 1), (1/2, 0, 1, 1, 3), (1/2, 0, 1, 1, 3)],
|
|
1384
|
+
[(1/2, 0, 1, 1, 3), (1/2, 1, 0, 0, 3), (1/2, 0, 2, 3, 1)],
|
|
1385
|
+
[(1/2, 0, 1, 1, 3), (1/2, 0, 2, 3, 1), (1/2, 1, 0, 0, 3)]]
|
|
1386
|
+
"""
|
|
1387
|
+
if prec is None:
|
|
1388
|
+
try:
|
|
1389
|
+
return self.__brandt_series_vectors
|
|
1390
|
+
except AttributeError:
|
|
1391
|
+
prec = 2
|
|
1392
|
+
elif prec < 2:
|
|
1393
|
+
raise ValueError("prec must be at least 2")
|
|
1394
|
+
L = self.right_ideals()
|
|
1395
|
+
if not L:
|
|
1396
|
+
return [[]]
|
|
1397
|
+
try:
|
|
1398
|
+
if len(self.__brandt_series_vectors[0][0]) >= prec:
|
|
1399
|
+
return self.__brandt_series_vectors
|
|
1400
|
+
except AttributeError:
|
|
1401
|
+
pass
|
|
1402
|
+
|
|
1403
|
+
n = len(L)
|
|
1404
|
+
# 1. Compute the theta series
|
|
1405
|
+
theta = [[I.theta_series_vector(prec) for I in x]
|
|
1406
|
+
for x in self._ideal_products()]
|
|
1407
|
+
|
|
1408
|
+
# 2. Compute the number e_j
|
|
1409
|
+
e = [theta[j][j][1] for j in range(n)]
|
|
1410
|
+
|
|
1411
|
+
B = [[0 for _ in range(n)] for _ in range(n)]
|
|
1412
|
+
|
|
1413
|
+
# 3. Make the Brandt matrix series
|
|
1414
|
+
for i in range(n):
|
|
1415
|
+
B[i][i] = theta[i][i] / e[i]
|
|
1416
|
+
for j in range(i):
|
|
1417
|
+
B[j][i] = theta[i][j] / e[j]
|
|
1418
|
+
B[i][j] = theta[i][j] / e[i]
|
|
1419
|
+
|
|
1420
|
+
self.__brandt_series_vectors = B
|
|
1421
|
+
return B
|
|
1422
|
+
|
|
1423
|
+
def brandt_series(self, prec, var='q'):
|
|
1424
|
+
r"""
|
|
1425
|
+
Return matrix of power series `\sum T_n q^n` to the given
|
|
1426
|
+
precision.
|
|
1427
|
+
|
|
1428
|
+
Note that the Hecke operators in this series are
|
|
1429
|
+
always over `\QQ`, even if the base ring of this Brandt module
|
|
1430
|
+
is not `\QQ`.
|
|
1431
|
+
|
|
1432
|
+
INPUT:
|
|
1433
|
+
|
|
1434
|
+
- ``prec`` -- positive integer
|
|
1435
|
+
- ``var`` -- string (default: `q`)
|
|
1436
|
+
|
|
1437
|
+
OUTPUT: matrix of power series with coefficients in `\QQ`
|
|
1438
|
+
|
|
1439
|
+
EXAMPLES::
|
|
1440
|
+
|
|
1441
|
+
sage: B = BrandtModule(11)
|
|
1442
|
+
sage: B.brandt_series(2)
|
|
1443
|
+
[1/4 + q + O(q^2) 1/4 + O(q^2)]
|
|
1444
|
+
[ 1/6 + O(q^2) 1/6 + q + O(q^2)]
|
|
1445
|
+
sage: B.brandt_series(5)
|
|
1446
|
+
[1/4 + q + q^2 + 2*q^3 + 5*q^4 + O(q^5) 1/4 + 3*q^2 + 3*q^3 + 3*q^4 + O(q^5)]
|
|
1447
|
+
[ 1/6 + 2*q^2 + 2*q^3 + 2*q^4 + O(q^5) 1/6 + q + q^3 + 4*q^4 + O(q^5)]
|
|
1448
|
+
|
|
1449
|
+
|
|
1450
|
+
Asking for a smaller precision works::
|
|
1451
|
+
|
|
1452
|
+
sage: B.brandt_series(3)
|
|
1453
|
+
[1/4 + q + q^2 + O(q^3) 1/4 + 3*q^2 + O(q^3)]
|
|
1454
|
+
[ 1/6 + 2*q^2 + O(q^3) 1/6 + q + O(q^3)]
|
|
1455
|
+
sage: B.brandt_series(3,var='t')
|
|
1456
|
+
[1/4 + t + t^2 + O(t^3) 1/4 + 3*t^2 + O(t^3)]
|
|
1457
|
+
[ 1/6 + 2*t^2 + O(t^3) 1/6 + t + O(t^3)]
|
|
1458
|
+
"""
|
|
1459
|
+
A = self._brandt_series_vectors(prec)
|
|
1460
|
+
R = PowerSeriesRing(QQ, var)
|
|
1461
|
+
n = len(A[0])
|
|
1462
|
+
return matrix(R, n, n,
|
|
1463
|
+
[[R(x.list()[:prec], prec) for x in Y] for Y in A])
|
|
1464
|
+
|
|
1465
|
+
@cached_method
|
|
1466
|
+
def eisenstein_subspace(self):
|
|
1467
|
+
"""
|
|
1468
|
+
Return the 1-dimensional subspace of ``self`` on which the Hecke
|
|
1469
|
+
operators `T_p` act as `p+1` for `p` coprime to the level.
|
|
1470
|
+
|
|
1471
|
+
.. NOTE::
|
|
1472
|
+
|
|
1473
|
+
This function assumes that the base field has
|
|
1474
|
+
characteristic 0.
|
|
1475
|
+
|
|
1476
|
+
EXAMPLES::
|
|
1477
|
+
|
|
1478
|
+
sage: B = BrandtModule(11); B.eisenstein_subspace()
|
|
1479
|
+
Subspace of dimension 1 of Brandt module of dimension 2 of level 11 of weight 2 over Rational Field
|
|
1480
|
+
sage: B.eisenstein_subspace() is B.eisenstein_subspace()
|
|
1481
|
+
True
|
|
1482
|
+
sage: BrandtModule(3,11).eisenstein_subspace().basis()
|
|
1483
|
+
((1, 1),)
|
|
1484
|
+
sage: BrandtModule(7,10).eisenstein_subspace().basis()
|
|
1485
|
+
((1, 1, 1, 1/2, 1, 1, 1/2, 1, 1, 1),)
|
|
1486
|
+
sage: BrandtModule(7,10,base_ring=ZZ).eisenstein_subspace().basis()
|
|
1487
|
+
((2, 2, 2, 1, 2, 2, 1, 2, 2, 2),)
|
|
1488
|
+
"""
|
|
1489
|
+
if self.base_ring().characteristic():
|
|
1490
|
+
raise ValueError("characteristic must be 0")
|
|
1491
|
+
# cut down until we get a 1-d space using Hecke operators T_p
|
|
1492
|
+
# with p coprime to the level.
|
|
1493
|
+
V = self
|
|
1494
|
+
p = Integer(2)
|
|
1495
|
+
N = self.level()
|
|
1496
|
+
while V.dimension() >= 2:
|
|
1497
|
+
while not N % p:
|
|
1498
|
+
p = p.next_prime()
|
|
1499
|
+
A = V.T(p) - (p + 1)
|
|
1500
|
+
V = A.kernel()
|
|
1501
|
+
return V
|
|
1502
|
+
|
|
1503
|
+
def is_cuspidal(self) -> bool:
|
|
1504
|
+
r"""
|
|
1505
|
+
Return whether ``self`` is cuspidal, i.e. has no Eisenstein part.
|
|
1506
|
+
|
|
1507
|
+
EXAMPLES::
|
|
1508
|
+
|
|
1509
|
+
sage: B = BrandtModule(3, 4)
|
|
1510
|
+
sage: B.is_cuspidal()
|
|
1511
|
+
False
|
|
1512
|
+
sage: B.eisenstein_subspace()
|
|
1513
|
+
Brandt module of dimension 1 of level 3*4 of weight 2 over Rational Field
|
|
1514
|
+
"""
|
|
1515
|
+
return not self.eisenstein_subspace().dimension()
|
|
1516
|
+
|
|
1517
|
+
@cached_method
|
|
1518
|
+
def monodromy_weights(self):
|
|
1519
|
+
r"""
|
|
1520
|
+
Return the weights for the monodromy pairing on this Brandt
|
|
1521
|
+
module.
|
|
1522
|
+
|
|
1523
|
+
The weights are associated to each ideal class in our
|
|
1524
|
+
fixed choice of basis. The weight of an ideal class `[I]` is
|
|
1525
|
+
half the number of units of the right order `I`.
|
|
1526
|
+
|
|
1527
|
+
.. NOTE:: The base ring must be `\QQ` or `\ZZ`.
|
|
1528
|
+
|
|
1529
|
+
EXAMPLES::
|
|
1530
|
+
|
|
1531
|
+
sage: BrandtModule(11).monodromy_weights()
|
|
1532
|
+
(2, 3)
|
|
1533
|
+
sage: BrandtModule(37).monodromy_weights()
|
|
1534
|
+
(1, 1, 1)
|
|
1535
|
+
sage: BrandtModule(43).monodromy_weights()
|
|
1536
|
+
(2, 1, 1, 1)
|
|
1537
|
+
sage: BrandtModule(7,10).monodromy_weights()
|
|
1538
|
+
(1, 1, 1, 2, 1, 1, 2, 1, 1, 1)
|
|
1539
|
+
sage: BrandtModule(5,13).monodromy_weights()
|
|
1540
|
+
(1, 3, 1, 1, 1, 3)
|
|
1541
|
+
sage: BrandtModule(2).monodromy_weights()
|
|
1542
|
+
(12,)
|
|
1543
|
+
sage: BrandtModule(2,7).monodromy_weights()
|
|
1544
|
+
(3, 3)
|
|
1545
|
+
"""
|
|
1546
|
+
# Before normalization,
|
|
1547
|
+
#
|
|
1548
|
+
# theta(R) = 1 + e*q + ....
|
|
1549
|
+
#
|
|
1550
|
+
# where e is the number of units in the order R.
|
|
1551
|
+
#
|
|
1552
|
+
# Since the theta series may be normalized as
|
|
1553
|
+
#
|
|
1554
|
+
# c * theta(R) = a[0] + a[1]*q + ...
|
|
1555
|
+
#
|
|
1556
|
+
# we recover e = a[1]/a[0] regardless of normalization.
|
|
1557
|
+
orders = self._ideal_products(diagonal_only=True)
|
|
1558
|
+
thetas = (R.theta_series_vector(2) for R in orders)
|
|
1559
|
+
return tuple(a[1] / a[0] / 2 for a in thetas)
|
|
1560
|
+
|
|
1561
|
+
|
|
1562
|
+
# ====================
|
|
1563
|
+
# Benchmarking
|
|
1564
|
+
# ====================
|
|
1565
|
+
def benchmark_magma(levels, silent=False):
|
|
1566
|
+
"""
|
|
1567
|
+
INPUT:
|
|
1568
|
+
|
|
1569
|
+
- ``levels`` -- list of pairs `(p,M)` where `p` is a prime not
|
|
1570
|
+
dividing `M`
|
|
1571
|
+
- ``silent`` -- boolean (default: ``False``); if ``True`` suppress
|
|
1572
|
+
printing during computation
|
|
1573
|
+
|
|
1574
|
+
OUTPUT:
|
|
1575
|
+
|
|
1576
|
+
list of 4-tuples ('magma', p, M, tm), where tm is the
|
|
1577
|
+
CPU time in seconds to compute T2 using Magma
|
|
1578
|
+
|
|
1579
|
+
EXAMPLES::
|
|
1580
|
+
|
|
1581
|
+
sage: a = sage.modular.quatalg.brandt.benchmark_magma([(11,1), (37,1), (43,1), (97,1)]) # optional - magma
|
|
1582
|
+
('magma', 11, 1, ...)
|
|
1583
|
+
('magma', 37, 1, ...)
|
|
1584
|
+
('magma', 43, 1, ...)
|
|
1585
|
+
('magma', 97, 1, ...)
|
|
1586
|
+
sage: a = sage.modular.quatalg.brandt.benchmark_magma([(11,2), (37,2), (43,2), (97,2)]) # optional - magma
|
|
1587
|
+
('magma', 11, 2, ...)
|
|
1588
|
+
('magma', 37, 2, ...)
|
|
1589
|
+
('magma', 43, 2, ...)
|
|
1590
|
+
('magma', 97, 2, ...)
|
|
1591
|
+
"""
|
|
1592
|
+
ans = []
|
|
1593
|
+
from sage.interfaces.magma import magma
|
|
1594
|
+
for p, M in levels:
|
|
1595
|
+
t = magma.cputime()
|
|
1596
|
+
magma.eval('HeckeOperator(BrandtModule(%s, %s),2)' % (p, M))
|
|
1597
|
+
tm = magma.cputime(t)
|
|
1598
|
+
v = ('magma', p, M, tm)
|
|
1599
|
+
if not silent:
|
|
1600
|
+
print(v)
|
|
1601
|
+
ans.append(v)
|
|
1602
|
+
return ans
|
|
1603
|
+
|
|
1604
|
+
|
|
1605
|
+
def benchmark_sage(levels, silent=False):
|
|
1606
|
+
"""
|
|
1607
|
+
INPUT:
|
|
1608
|
+
|
|
1609
|
+
- ``levels`` -- list of pairs `(p,M)` where `p` is a prime
|
|
1610
|
+
not dividing `M`
|
|
1611
|
+
- ``silent`` -- boolean (default: ``False``); if ``True`` suppress
|
|
1612
|
+
printing during computation
|
|
1613
|
+
|
|
1614
|
+
OUTPUT:
|
|
1615
|
+
|
|
1616
|
+
list of 4-tuples ('sage', p, M, tm), where tm is the
|
|
1617
|
+
CPU time in seconds to compute T2 using Sage
|
|
1618
|
+
|
|
1619
|
+
EXAMPLES::
|
|
1620
|
+
|
|
1621
|
+
sage: a = sage.modular.quatalg.brandt.benchmark_sage([(11,1), (37,1), (43,1), (97,1)])
|
|
1622
|
+
('sage', 11, 1, ...)
|
|
1623
|
+
('sage', 37, 1, ...)
|
|
1624
|
+
('sage', 43, 1, ...)
|
|
1625
|
+
('sage', 97, 1, ...)
|
|
1626
|
+
sage: a = sage.modular.quatalg.brandt.benchmark_sage([(11,2), (37,2), (43,2), (97,2)])
|
|
1627
|
+
('sage', 11, 2, ...)
|
|
1628
|
+
('sage', 37, 2, ...)
|
|
1629
|
+
('sage', 43, 2, ...)
|
|
1630
|
+
('sage', 97, 2, ...)
|
|
1631
|
+
"""
|
|
1632
|
+
from sage.misc.timing import cputime
|
|
1633
|
+
ans = []
|
|
1634
|
+
for p, M in levels:
|
|
1635
|
+
t = cputime()
|
|
1636
|
+
BrandtModule(p, M, use_cache=False).hecke_matrix(2)
|
|
1637
|
+
tm = cputime(t)
|
|
1638
|
+
v = ('sage', p, M, tm)
|
|
1639
|
+
if not silent:
|
|
1640
|
+
print(v)
|
|
1641
|
+
ans.append(v)
|
|
1642
|
+
return ans
|