passagemath-schemes 10.8.1a4__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.
- 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.8.1a4.dist-info/METADATA +203 -0
- passagemath_schemes-10.8.1a4.dist-info/METADATA.bak +204 -0
- passagemath_schemes-10.8.1a4.dist-info/RECORD +312 -0
- passagemath_schemes-10.8.1a4.dist-info/WHEEL +6 -0
- passagemath_schemes-10.8.1a4.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 +9556 -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 +2578 -0
- sage/lfunctions/all.py +18 -0
- sage/lfunctions/dokchitser.py +727 -0
- sage/lfunctions/pari.py +971 -0
- sage/lfunctions/zero_sums.cpython-314t-darwin.so +0 -0
- sage/lfunctions/zero_sums.pyx +1847 -0
- sage/modular/abvar/abvar.py +5132 -0
- sage/modular/abvar/abvar_ambient_jacobian.py +414 -0
- sage/modular/abvar/abvar_newform.py +246 -0
- sage/modular/abvar/all.py +8 -0
- sage/modular/abvar/constructor.py +187 -0
- sage/modular/abvar/cuspidal_subgroup.py +371 -0
- sage/modular/abvar/finite_subgroup.py +896 -0
- sage/modular/abvar/homology.py +721 -0
- sage/modular/abvar/homspace.py +989 -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 +741 -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 +1406 -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 +361 -0
- sage/modular/arithgroup/congroup_gamma0.py +692 -0
- sage/modular/arithgroup/congroup_gamma1.py +659 -0
- sage/modular/arithgroup/congroup_gammaH.py +1491 -0
- sage/modular/arithgroup/congroup_generic.py +630 -0
- sage/modular/arithgroup/congroup_sl2z.py +266 -0
- sage/modular/arithgroup/farey_symbol.cpython-314t-darwin.so +0 -0
- sage/modular/arithgroup/farey_symbol.pyx +1067 -0
- sage/modular/arithgroup/tests.py +425 -0
- sage/modular/btquotients/all.py +4 -0
- sage/modular/btquotients/btquotient.py +3736 -0
- sage/modular/btquotients/pautomorphicform.py +2564 -0
- sage/modular/buzzard.py +100 -0
- sage/modular/congroup.py +29 -0
- sage/modular/congroup_element.py +13 -0
- sage/modular/cusps.py +1107 -0
- sage/modular/cusps_nf.py +1270 -0
- sage/modular/dims.py +571 -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 +1076 -0
- sage/modular/hecke/algebra.py +725 -0
- sage/modular/hecke/all.py +19 -0
- sage/modular/hecke/ambient_module.py +994 -0
- sage/modular/hecke/degenmap.py +119 -0
- sage/modular/hecke/element.py +302 -0
- sage/modular/hecke/hecke_operator.py +736 -0
- sage/modular/hecke/homspace.py +185 -0
- sage/modular/hecke/module.py +1744 -0
- sage/modular/hecke/morphism.py +139 -0
- sage/modular/hecke/submodule.py +970 -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 +2020 -0
- sage/modular/local_comp/all.py +2 -0
- sage/modular/local_comp/liftings.py +292 -0
- sage/modular/local_comp/local_comp.py +1070 -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 +817 -0
- sage/modular/modform/ambient_R.py +177 -0
- sage/modular/modform/ambient_eps.py +306 -0
- sage/modular/modform/ambient_g0.py +120 -0
- sage/modular/modform/ambient_g1.py +199 -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 +487 -0
- sage/modular/modform/eisenstein_submodule.py +663 -0
- sage/modular/modform/element.py +4105 -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 +127 -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 +1859 -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 +380 -0
- sage/modular/modform/weight1.py +221 -0
- sage/modular/modform_hecketriangle/abstract_ring.py +1932 -0
- sage/modular/modform_hecketriangle/abstract_space.py +2527 -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 +3349 -0
- sage/modular/modform_hecketriangle/hecke_triangle_groups.py +1426 -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 +3844 -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 +1291 -0
- sage/modular/modsym/modsym.py +400 -0
- sage/modular/modsym/modular_symbols.py +384 -0
- sage/modular/modsym/p1list_nf.py +1241 -0
- sage/modular/modsym/relation_matrix.py +591 -0
- sage/modular/modsym/relation_matrix_pyx.cpython-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 +376 -0
- sage/modular/multiple_zeta.py +2635 -0
- sage/modular/multiple_zeta_F_algebra.py +789 -0
- sage/modular/overconvergent/all.py +6 -0
- sage/modular/overconvergent/genus0.py +1879 -0
- sage/modular/overconvergent/hecke_series.py +1187 -0
- sage/modular/overconvergent/weightspace.py +776 -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 +856 -0
- sage/modular/pollack_stevens/modsym.py +1590 -0
- sage/modular/pollack_stevens/padic_lseries.py +417 -0
- sage/modular/pollack_stevens/sigma0.py +534 -0
- sage/modular/pollack_stevens/space.py +1078 -0
- sage/modular/quasimodform/all.py +3 -0
- sage/modular/quasimodform/element.py +846 -0
- sage/modular/quasimodform/ring.py +826 -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 +700 -0
- sage/schemes/curves/affine_curve.py +2924 -0
- sage/schemes/curves/all.py +33 -0
- sage/schemes/curves/closed_point.py +434 -0
- sage/schemes/curves/constructor.py +397 -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 +3203 -0
- sage/schemes/curves/weighted_projective_curve.py +106 -0
- sage/schemes/curves/zariski_vankampen.py +1931 -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 +991 -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 +1103 -0
- sage/schemes/elliptic_curves/constructor.py +1530 -0
- sage/schemes/elliptic_curves/ec_database.py +175 -0
- sage/schemes/elliptic_curves/ell_curve_isogeny.py +3971 -0
- sage/schemes/elliptic_curves/ell_egros.py +457 -0
- sage/schemes/elliptic_curves/ell_field.py +2837 -0
- sage/schemes/elliptic_curves/ell_finite_field.py +3249 -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 +4944 -0
- sage/schemes/elliptic_curves/ell_rational_field.py +7184 -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 +1663 -0
- sage/schemes/elliptic_curves/gp_simon.py +152 -0
- sage/schemes/elliptic_curves/heegner.py +7328 -0
- sage/schemes/elliptic_curves/height.py +2108 -0
- sage/schemes/elliptic_curves/hom.py +1788 -0
- sage/schemes/elliptic_curves/hom_composite.py +1084 -0
- sage/schemes/elliptic_curves/hom_fractional.py +544 -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 +681 -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 +1523 -0
- sage/schemes/elliptic_curves/isogeny_small_degree.py +2797 -0
- sage/schemes/elliptic_curves/jacobian.py +247 -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 +915 -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 +716 -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 +369 -0
- sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py +1948 -0
- sage/schemes/hyperelliptic_curves/hyperelliptic_g2.py +192 -0
- sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py +936 -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 +312 -0
- sage/schemes/hyperelliptic_curves/jacobian_g2.py +32 -0
- sage/schemes/hyperelliptic_curves/jacobian_generic.py +437 -0
- sage/schemes/hyperelliptic_curves/jacobian_homset.py +186 -0
- sage/schemes/hyperelliptic_curves/jacobian_morphism.py +878 -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 +3863 -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 +581 -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 +53 -0
- sage/schemes/riemann_surfaces/all.py +1 -0
- sage/schemes/riemann_surfaces/riemann_surface.py +4177 -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,3736 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-schemes
|
|
2
|
+
# sage.doctest: needs sage.graphs sage.libs.pari
|
|
3
|
+
r"""
|
|
4
|
+
Quotients of the Bruhat-Tits tree
|
|
5
|
+
|
|
6
|
+
This package contains all the functionality described and developed in [FM2014]_.
|
|
7
|
+
It allows for computations with fundamental domains of the Bruhat-Tits tree,
|
|
8
|
+
under the action of arithmetic groups arising from units in definite
|
|
9
|
+
quaternion algebras.
|
|
10
|
+
|
|
11
|
+
EXAMPLES:
|
|
12
|
+
|
|
13
|
+
Create the quotient attached to a maximal order of the quaternion algebra of
|
|
14
|
+
discriminant `13`, at the prime `p = 5`::
|
|
15
|
+
|
|
16
|
+
sage: Y = BruhatTitsQuotient(5, 13)
|
|
17
|
+
|
|
18
|
+
We can query for its genus, as well as get it back as a graph::
|
|
19
|
+
|
|
20
|
+
sage: Y.genus()
|
|
21
|
+
5
|
|
22
|
+
sage: Y.get_graph()
|
|
23
|
+
Multi-graph on 2 vertices
|
|
24
|
+
|
|
25
|
+
The rest of functionality can be found in the docstrings below.
|
|
26
|
+
|
|
27
|
+
AUTHORS:
|
|
28
|
+
|
|
29
|
+
- Cameron Franc and Marc Masdeu (2011): initial version
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
# ****************************************************************************
|
|
33
|
+
# Copyright (C) 2011 Cameron Franc and Marc Masdeu
|
|
34
|
+
#
|
|
35
|
+
# Distributed under the terms of the GNU General Public License (GPL)
|
|
36
|
+
# as published by the Free Software Foundation; either version 2 of
|
|
37
|
+
# the License, or (at your option) any later version.
|
|
38
|
+
#
|
|
39
|
+
# https://www.gnu.org/licenses/
|
|
40
|
+
# ****************************************************************************
|
|
41
|
+
|
|
42
|
+
from copy import copy
|
|
43
|
+
from collections import deque
|
|
44
|
+
|
|
45
|
+
from sage.arith.misc import gcd, xgcd, kronecker_symbol, fundamental_discriminant
|
|
46
|
+
from sage.matrix.constructor import Matrix
|
|
47
|
+
from sage.matrix.matrix_space import MatrixSpace
|
|
48
|
+
from sage.matrix.special import column_matrix
|
|
49
|
+
from sage.misc.cachefunc import cached_method
|
|
50
|
+
from sage.misc.latex import latex
|
|
51
|
+
from sage.misc.lazy_attribute import lazy_attribute
|
|
52
|
+
from sage.misc.lazy_import import lazy_import
|
|
53
|
+
from sage.misc.misc_c import prod
|
|
54
|
+
from sage.misc.verbose import verbose
|
|
55
|
+
from sage.modular.arithgroup.congroup_gamma0 import Gamma0_constructor as Gamma0
|
|
56
|
+
from sage.modular.arithgroup.congroup_gammaH import GammaH_constructor
|
|
57
|
+
from sage.modular.dirichlet import DirichletGroup
|
|
58
|
+
from sage.quadratic_forms.quadratic_form import QuadraticForm
|
|
59
|
+
from sage.rings.finite_rings.finite_field_constructor import GF
|
|
60
|
+
from sage.rings.finite_rings.integer_mod_ring import Zmod
|
|
61
|
+
from sage.rings.integer import Integer
|
|
62
|
+
from sage.rings.integer_ring import ZZ
|
|
63
|
+
from sage.rings.padics.precision_error import PrecisionError
|
|
64
|
+
from sage.rings.rational_field import QQ
|
|
65
|
+
from sage.structure.sage_object import SageObject
|
|
66
|
+
from sage.structure.unique_representation import UniqueRepresentation
|
|
67
|
+
lazy_import("sage.plot.colors", "rainbow")
|
|
68
|
+
|
|
69
|
+
lazy_import('sage.algebras.quatalg.quaternion_algebra', 'QuaternionAlgebra')
|
|
70
|
+
lazy_import('sage.graphs.graph', 'Graph')
|
|
71
|
+
lazy_import('sage.libs.pari', 'pari')
|
|
72
|
+
lazy_import('sage.plot.colors', 'rainbow')
|
|
73
|
+
lazy_import('sage.rings.number_field.number_field', 'NumberField')
|
|
74
|
+
lazy_import('sage.rings.padics.factory', ['Qp', 'Zp'])
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class DoubleCosetReduction(SageObject):
|
|
78
|
+
r"""
|
|
79
|
+
Edges in the Bruhat-Tits tree are represented by cosets of
|
|
80
|
+
matrices in `GL_2`. Given a matrix `x` in `GL_2`, this
|
|
81
|
+
class computes and stores the data corresponding to the
|
|
82
|
+
double coset representation of `x` in terms of a fundamental
|
|
83
|
+
domain of edges for the action of the arithmetic group `\Gamma`.
|
|
84
|
+
|
|
85
|
+
More precisely:
|
|
86
|
+
|
|
87
|
+
Initialized with an element `x` of `GL_2(\ZZ)`, finds elements
|
|
88
|
+
`\gamma` in `\Gamma`, `t` and an edge `e` such that `get=x`. It
|
|
89
|
+
stores these values as members ``gamma``, ``label`` and functions
|
|
90
|
+
``self.sign()``, ``self.t()`` and ``self.igamma()``, satisfying:
|
|
91
|
+
|
|
92
|
+
- if ``self.sign() == +1``:
|
|
93
|
+
``igamma() * edge_list[label].rep * t() == x``
|
|
94
|
+
|
|
95
|
+
- if ``self.sign() == -1``:
|
|
96
|
+
``igamma() * edge_list[label].opposite.rep * t() == x``
|
|
97
|
+
|
|
98
|
+
It also stores a member called power so that:
|
|
99
|
+
|
|
100
|
+
``p**(2*power) = gamma.reduced_norm()``
|
|
101
|
+
|
|
102
|
+
The usual decomposition `get=x` would be:
|
|
103
|
+
|
|
104
|
+
- g = gamma / (p ** power)
|
|
105
|
+
|
|
106
|
+
- e = edge_list[label]
|
|
107
|
+
|
|
108
|
+
- t' = t * p ** power
|
|
109
|
+
|
|
110
|
+
Here usual denotes that we have rescaled gamma to have unit
|
|
111
|
+
determinant, and so that the result is honestly an element
|
|
112
|
+
of the arithmetic quaternion group under consideration. In
|
|
113
|
+
practice we store integral multiples and keep track of the
|
|
114
|
+
powers of `p`.
|
|
115
|
+
|
|
116
|
+
INPUT:
|
|
117
|
+
|
|
118
|
+
- ``Y`` -- BruhatTitsQuotient object in which to work
|
|
119
|
+
- ``x`` -- Something coercible into a matrix in `GL_2(\ZZ)`. In
|
|
120
|
+
principle we should allow elements in `GL_2(\QQ_p)`, but it is
|
|
121
|
+
enough to work with integral entries
|
|
122
|
+
- ``extrapow`` -- gets added to the power attribute, and it is
|
|
123
|
+
used for the Hecke action
|
|
124
|
+
|
|
125
|
+
EXAMPLES::
|
|
126
|
+
|
|
127
|
+
sage: from sage.modular.btquotients.btquotient import DoubleCosetReduction
|
|
128
|
+
sage: Y = BruhatTitsQuotient(5, 13)
|
|
129
|
+
sage: x = Matrix(ZZ,2,2,[123,153,1231,1231])
|
|
130
|
+
sage: d = DoubleCosetReduction(Y,x)
|
|
131
|
+
sage: d.sign()
|
|
132
|
+
-1
|
|
133
|
+
sage: d.igamma()*Y._edge_list[d.label - len(Y.get_edge_list())].opposite.rep*d.t() == x
|
|
134
|
+
True
|
|
135
|
+
sage: x = Matrix(ZZ,2,2,[1423,113553,11231,12313])
|
|
136
|
+
sage: d = DoubleCosetReduction(Y,x)
|
|
137
|
+
sage: d.sign()
|
|
138
|
+
1
|
|
139
|
+
sage: d.igamma()*Y._edge_list[d.label].rep*d.t() == x
|
|
140
|
+
True
|
|
141
|
+
|
|
142
|
+
AUTHORS:
|
|
143
|
+
|
|
144
|
+
- Cameron Franc (2012-02-20)
|
|
145
|
+
- Marc Masdeu
|
|
146
|
+
"""
|
|
147
|
+
|
|
148
|
+
def __init__(self, Y, x, extrapow=0):
|
|
149
|
+
r"""
|
|
150
|
+
Initialize and compute the reduction as a double coset.
|
|
151
|
+
|
|
152
|
+
EXAMPLES::
|
|
153
|
+
|
|
154
|
+
sage: from sage.modular.btquotients.btquotient import DoubleCosetReduction
|
|
155
|
+
sage: Y = BruhatTitsQuotient(5, 13)
|
|
156
|
+
sage: x = Matrix(ZZ,2,2,[123,153,1231,1231])
|
|
157
|
+
sage: d = DoubleCosetReduction(Y,x)
|
|
158
|
+
sage: TestSuite(d).run()
|
|
159
|
+
"""
|
|
160
|
+
e1 = Y._BT.edge(x)
|
|
161
|
+
try:
|
|
162
|
+
g, label, parity = Y._cached_decomps[e1]
|
|
163
|
+
except KeyError:
|
|
164
|
+
valuation = e1.determinant().valuation(Y._p)
|
|
165
|
+
parity = valuation % 2
|
|
166
|
+
v1 = Y._BT.target(e1)
|
|
167
|
+
v = Y.fundom_rep(v1)
|
|
168
|
+
g, e = Y._find_equivalent_edge(e1, v.entering_edges,
|
|
169
|
+
valuation=valuation)
|
|
170
|
+
label = e.label
|
|
171
|
+
Y._cached_decomps[e1] = (g, label, parity)
|
|
172
|
+
|
|
173
|
+
self._parent = Y
|
|
174
|
+
self.parity = parity
|
|
175
|
+
self._num_edges = len(Y.get_edge_list())
|
|
176
|
+
self.label = label + parity * self._num_edges
|
|
177
|
+
# The label will encode whether it is an edge or its opposite !
|
|
178
|
+
self.gamma = g[0]
|
|
179
|
+
self.x = x
|
|
180
|
+
self.power = g[1] + extrapow
|
|
181
|
+
self._t_prec = -1
|
|
182
|
+
self._igamma_prec = -1
|
|
183
|
+
|
|
184
|
+
def _repr_(self):
|
|
185
|
+
r"""
|
|
186
|
+
Return the representation of ``self`` as a string.
|
|
187
|
+
|
|
188
|
+
EXAMPLES::
|
|
189
|
+
|
|
190
|
+
sage: from sage.modular.btquotients.btquotient import DoubleCosetReduction
|
|
191
|
+
sage: Y = BruhatTitsQuotient(5, 13)
|
|
192
|
+
sage: x = Matrix(ZZ,2,2,[123,153,1231,1231])
|
|
193
|
+
sage: DoubleCosetReduction(Y,x)
|
|
194
|
+
Double coset data (-1, [(4), (5), (-4), (-4)], 8)
|
|
195
|
+
"""
|
|
196
|
+
return "Double coset data (%s, %s, %s)" % (self.sign(),
|
|
197
|
+
list(self.gamma), self.label)
|
|
198
|
+
|
|
199
|
+
def __eq__(self, other):
|
|
200
|
+
"""
|
|
201
|
+
Return ``self == other``.
|
|
202
|
+
|
|
203
|
+
TESTS::
|
|
204
|
+
|
|
205
|
+
sage: from sage.modular.btquotients.btquotient import DoubleCosetReduction
|
|
206
|
+
sage: Y = BruhatTitsQuotient(5, 13)
|
|
207
|
+
sage: x = Matrix(ZZ,2,2,[123,153,1231,1231])
|
|
208
|
+
sage: d1 = DoubleCosetReduction(Y,x)
|
|
209
|
+
sage: d1 == d1
|
|
210
|
+
True
|
|
211
|
+
"""
|
|
212
|
+
if self._parent != other._parent:
|
|
213
|
+
return False
|
|
214
|
+
if self.parity != other.parity:
|
|
215
|
+
return False
|
|
216
|
+
if self._num_edges != other._num_edges:
|
|
217
|
+
return False
|
|
218
|
+
if self.label != other.label:
|
|
219
|
+
return False
|
|
220
|
+
if self.gamma != other.gamma:
|
|
221
|
+
return False
|
|
222
|
+
if self.x != other.x:
|
|
223
|
+
return False
|
|
224
|
+
if self.power != other.power:
|
|
225
|
+
return False
|
|
226
|
+
if self._t_prec != other._t_prec:
|
|
227
|
+
return False
|
|
228
|
+
return self._igamma_prec == other._igamma_prec
|
|
229
|
+
|
|
230
|
+
def __ne__(self, other):
|
|
231
|
+
"""
|
|
232
|
+
Return ``self != other``.
|
|
233
|
+
|
|
234
|
+
TESTS::
|
|
235
|
+
|
|
236
|
+
sage: from sage.modular.btquotients.btquotient import DoubleCosetReduction
|
|
237
|
+
sage: Y = BruhatTitsQuotient(5, 13)
|
|
238
|
+
sage: x = Matrix(ZZ,2,2,[123,153,1231,1231])
|
|
239
|
+
sage: d1 = DoubleCosetReduction(Y,x)
|
|
240
|
+
sage: d1 != d1
|
|
241
|
+
False
|
|
242
|
+
"""
|
|
243
|
+
return not self.__eq__(other)
|
|
244
|
+
|
|
245
|
+
def sign(self):
|
|
246
|
+
r"""
|
|
247
|
+
Return the direction of the edge.
|
|
248
|
+
|
|
249
|
+
The Bruhat-Tits quotients are directed graphs but we only store
|
|
250
|
+
half the edges (we treat them more like unordered graphs).
|
|
251
|
+
The sign tells whether the matrix self.x is equivalent to the
|
|
252
|
+
representative in the quotient (sign = +1), or to the
|
|
253
|
+
opposite of one of the representatives (sign = -1).
|
|
254
|
+
|
|
255
|
+
OUTPUT:
|
|
256
|
+
|
|
257
|
+
an int that is +1 or -1 according to the sign of ``self``
|
|
258
|
+
|
|
259
|
+
EXAMPLES::
|
|
260
|
+
|
|
261
|
+
sage: from sage.modular.btquotients.btquotient import DoubleCosetReduction
|
|
262
|
+
sage: Y = BruhatTitsQuotient(3, 11)
|
|
263
|
+
sage: x = Matrix(ZZ,2,2,[123,153,1231,1231])
|
|
264
|
+
sage: d = DoubleCosetReduction(Y,x)
|
|
265
|
+
sage: d.sign()
|
|
266
|
+
-1
|
|
267
|
+
sage: d.igamma()*Y._edge_list[d.label - len(Y.get_edge_list())].opposite.rep*d.t() == x
|
|
268
|
+
True
|
|
269
|
+
sage: x = Matrix(ZZ,2,2,[1423,113553,11231,12313])
|
|
270
|
+
sage: d = DoubleCosetReduction(Y,x)
|
|
271
|
+
sage: d.sign()
|
|
272
|
+
1
|
|
273
|
+
sage: d.igamma()*Y._edge_list[d.label].rep*d.t() == x
|
|
274
|
+
True
|
|
275
|
+
"""
|
|
276
|
+
if self.parity == 0:
|
|
277
|
+
return 1
|
|
278
|
+
else:
|
|
279
|
+
return -1
|
|
280
|
+
|
|
281
|
+
def igamma(self, embedding=None, scale=1):
|
|
282
|
+
r"""
|
|
283
|
+
Image under gamma.
|
|
284
|
+
|
|
285
|
+
Elements of the arithmetic group can be regarded as elements
|
|
286
|
+
of the global quaternion order, and hence may be represented
|
|
287
|
+
exactly. This function computes the image of such an element
|
|
288
|
+
under the local splitting and returns the corresponding `p`-adic
|
|
289
|
+
approximation.
|
|
290
|
+
|
|
291
|
+
INPUT:
|
|
292
|
+
|
|
293
|
+
- ``embedding`` -- integer; or a function (default:
|
|
294
|
+
none). If ``embedding`` is None, then the image of
|
|
295
|
+
``self.gamma`` under the local splitting associated to
|
|
296
|
+
``self.Y`` is used. If ``embedding`` is an integer, then
|
|
297
|
+
the precision of the local splitting of self.Y is raised
|
|
298
|
+
(if necessary) to be larger than this integer, and this
|
|
299
|
+
new local splitting is used. If a function is passed, then
|
|
300
|
+
map ``self.gamma`` under ``embedding``.
|
|
301
|
+
- ``scale`` -- (default: 1) scaling factor applied to the output
|
|
302
|
+
|
|
303
|
+
OUTPUT:
|
|
304
|
+
|
|
305
|
+
a 2x2 matrix with `p`-adic entries encoding the image of ``self``
|
|
306
|
+
under the local splitting
|
|
307
|
+
|
|
308
|
+
EXAMPLES::
|
|
309
|
+
|
|
310
|
+
sage: from sage.modular.btquotients.btquotient import DoubleCosetReduction
|
|
311
|
+
sage: Y = BruhatTitsQuotient(7, 11)
|
|
312
|
+
sage: d = DoubleCosetReduction(Y,Matrix(ZZ,2,2,[123,45,88,1]))
|
|
313
|
+
sage: d.igamma()
|
|
314
|
+
[6 + 6*7 + 6*7^2 + 6*7^3 + 6*7^4 + O(7^5) O(7^5)]
|
|
315
|
+
[ O(7^5) 6 + 6*7 + 6*7^2 + 6*7^3 + 6*7^4 + O(7^5)]
|
|
316
|
+
sage: d.igamma(embedding = 7)
|
|
317
|
+
[6 + 6*7 + 6*7^2 + 6*7^3 + 6*7^4 + 6*7^5 + 6*7^6 + O(7^7) O(7^7)]
|
|
318
|
+
[ O(7^7) 6 + 6*7 + 6*7^2 + 6*7^3 + 6*7^4 + 6*7^5 + 6*7^6 + O(7^7)]
|
|
319
|
+
"""
|
|
320
|
+
Y = self._parent
|
|
321
|
+
if embedding is None:
|
|
322
|
+
prec = Y._prec
|
|
323
|
+
else:
|
|
324
|
+
try:
|
|
325
|
+
# The user wants higher precision
|
|
326
|
+
prec = ZZ(embedding)
|
|
327
|
+
except TypeError:
|
|
328
|
+
# The user knows what she is doing, so let it go
|
|
329
|
+
return embedding(self.gamma)
|
|
330
|
+
if prec > self._igamma_prec:
|
|
331
|
+
self._igamma_prec = prec
|
|
332
|
+
self._cached_igamma = Y.embed_quaternion(self.gamma, exact=False,
|
|
333
|
+
prec=prec)
|
|
334
|
+
return scale * self._cached_igamma
|
|
335
|
+
|
|
336
|
+
def t(self, prec=None):
|
|
337
|
+
r"""
|
|
338
|
+
Return the 't part' of the decomposition using the rest of the data.
|
|
339
|
+
|
|
340
|
+
INPUT:
|
|
341
|
+
|
|
342
|
+
- ``prec`` -- a `p`-adic precision that t will be computed
|
|
343
|
+
to. Defaults to the default working precision of self.
|
|
344
|
+
|
|
345
|
+
OUTPUT:
|
|
346
|
+
|
|
347
|
+
a 2x2 `p`-adic matrix with entries of
|
|
348
|
+
precision ``prec`` that is the 't-part' of the decomposition of
|
|
349
|
+
self
|
|
350
|
+
|
|
351
|
+
EXAMPLES::
|
|
352
|
+
|
|
353
|
+
sage: from sage.modular.btquotients.btquotient import DoubleCosetReduction
|
|
354
|
+
sage: Y = BruhatTitsQuotient(5, 13)
|
|
355
|
+
sage: x = Matrix(ZZ,2,2,[123,153,1231,1232])
|
|
356
|
+
sage: d = DoubleCosetReduction(Y,x)
|
|
357
|
+
sage: t = d.t(20)
|
|
358
|
+
sage: t[1,0].valuation() > 0
|
|
359
|
+
True
|
|
360
|
+
"""
|
|
361
|
+
Y = self._parent
|
|
362
|
+
if prec is None:
|
|
363
|
+
prec = max([5, Y._prec])
|
|
364
|
+
if self._t_prec >= prec:
|
|
365
|
+
return self._cached_t
|
|
366
|
+
e = Y._edge_list[self.label % self._num_edges]
|
|
367
|
+
tmp_prec = prec
|
|
368
|
+
while self._t_prec < prec:
|
|
369
|
+
if self.parity == 0:
|
|
370
|
+
self._cached_t = (self.igamma(tmp_prec) * e.rep).inverse() * self.x
|
|
371
|
+
# assert self._cached_t[1, 0].valuation()>self._cached_t[1,1].valuation()
|
|
372
|
+
else:
|
|
373
|
+
self._cached_t = (self.igamma(tmp_prec) * e.opposite.rep).inverse() * self.x
|
|
374
|
+
# assert self._cached_t[1, 0].valuation()>self._cached_t[1,1].valuation()
|
|
375
|
+
tmp_prec += 1
|
|
376
|
+
self._t_prec = min([xx.precision_absolute()
|
|
377
|
+
for xx in self._cached_t.list()])
|
|
378
|
+
return self._cached_t
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
class BruhatTitsTree(SageObject, UniqueRepresentation):
|
|
382
|
+
r"""
|
|
383
|
+
An implementation of the Bruhat-Tits tree for `GL_2(\QQ_p)`.
|
|
384
|
+
|
|
385
|
+
INPUT:
|
|
386
|
+
|
|
387
|
+
- ``p`` -- a prime number. The corresponding tree is then `p+1` regular
|
|
388
|
+
|
|
389
|
+
EXAMPLES:
|
|
390
|
+
|
|
391
|
+
We create the tree for `GL_2(\QQ_5)`::
|
|
392
|
+
|
|
393
|
+
sage: from sage.modular.btquotients.btquotient import BruhatTitsTree
|
|
394
|
+
sage: p = 5
|
|
395
|
+
sage: T = BruhatTitsTree(p)
|
|
396
|
+
sage: m = Matrix(ZZ,2,2,[p**5,p**2,p**3,1+p+p*3])
|
|
397
|
+
sage: e = T.edge(m); e
|
|
398
|
+
[ 0 25]
|
|
399
|
+
[625 21]
|
|
400
|
+
sage: v0 = T.origin(e); v0
|
|
401
|
+
[ 25 0]
|
|
402
|
+
[ 21 125]
|
|
403
|
+
sage: v1 = T.target(e); v1
|
|
404
|
+
[ 25 0]
|
|
405
|
+
[ 21 625]
|
|
406
|
+
sage: T.origin(T.opposite(e)) == v1
|
|
407
|
+
True
|
|
408
|
+
sage: T.target(T.opposite(e)) == v0
|
|
409
|
+
True
|
|
410
|
+
|
|
411
|
+
A value error is raised if a prime is not passed::
|
|
412
|
+
|
|
413
|
+
sage: T = BruhatTitsTree(4)
|
|
414
|
+
Traceback (most recent call last):
|
|
415
|
+
...
|
|
416
|
+
ValueError: input (4) must be prime
|
|
417
|
+
|
|
418
|
+
AUTHORS:
|
|
419
|
+
|
|
420
|
+
- Marc Masdeu (2012-02-20)
|
|
421
|
+
"""
|
|
422
|
+
def __init__(self, p):
|
|
423
|
+
"""
|
|
424
|
+
Initialize a BruhatTitsTree object for a given prime `p`.
|
|
425
|
+
|
|
426
|
+
EXAMPLES::
|
|
427
|
+
|
|
428
|
+
sage: from sage.modular.btquotients.btquotient import BruhatTitsTree
|
|
429
|
+
sage: T = BruhatTitsTree(17)
|
|
430
|
+
sage: TestSuite(T).run()
|
|
431
|
+
"""
|
|
432
|
+
if not ZZ(p).is_prime():
|
|
433
|
+
raise ValueError(f'input ({p}) must be prime')
|
|
434
|
+
self._p = ZZ(p)
|
|
435
|
+
self._Mat_22 = MatrixSpace(ZZ, 2, 2)
|
|
436
|
+
self._mat_p001 = self._Mat_22([self._p, 0, 0, 1])
|
|
437
|
+
|
|
438
|
+
def target(self, e, normalized=False):
|
|
439
|
+
r"""
|
|
440
|
+
Return the target vertex of the edge represented by the
|
|
441
|
+
input matrix e.
|
|
442
|
+
|
|
443
|
+
INPUT:
|
|
444
|
+
|
|
445
|
+
- ``e`` -- a 2x2 matrix with integer entries
|
|
446
|
+
|
|
447
|
+
- ``normalized`` -- boolean (default: ``False``); if True
|
|
448
|
+
then the input matrix is assumed to be normalized
|
|
449
|
+
|
|
450
|
+
OUTPUT:
|
|
451
|
+
|
|
452
|
+
- ``e`` -- 2x2 integer matrix representing the target of
|
|
453
|
+
the input edge
|
|
454
|
+
|
|
455
|
+
EXAMPLES::
|
|
456
|
+
|
|
457
|
+
sage: from sage.modular.btquotients.btquotient import BruhatTitsTree
|
|
458
|
+
sage: T = BruhatTitsTree(7)
|
|
459
|
+
sage: T.target(Matrix(ZZ,2,2,[1,5,8,9]))
|
|
460
|
+
[1 0]
|
|
461
|
+
[0 1]
|
|
462
|
+
"""
|
|
463
|
+
if normalized:
|
|
464
|
+
# then the normalized target vertex is also M and we save some
|
|
465
|
+
# row reductions with a simple return
|
|
466
|
+
return e
|
|
467
|
+
else:
|
|
468
|
+
# must normalize the target vertex representative
|
|
469
|
+
return self.vertex(e)
|
|
470
|
+
|
|
471
|
+
def origin(self, e, normalized=False):
|
|
472
|
+
r"""
|
|
473
|
+
Return the origin vertex of the edge represented by the
|
|
474
|
+
input matrix e.
|
|
475
|
+
|
|
476
|
+
INPUT:
|
|
477
|
+
|
|
478
|
+
- ``e`` -- a 2x2 matrix with integer entries
|
|
479
|
+
|
|
480
|
+
- ``normalized`` -- boolean (default: ``False``); if True
|
|
481
|
+
then the input matrix M is assumed to be normalized
|
|
482
|
+
|
|
483
|
+
OUTPUT: ``e`` -- 2x2 integer matrix
|
|
484
|
+
|
|
485
|
+
EXAMPLES::
|
|
486
|
+
|
|
487
|
+
sage: from sage.modular.btquotients.btquotient import BruhatTitsTree
|
|
488
|
+
sage: T = BruhatTitsTree(7)
|
|
489
|
+
sage: T.origin(Matrix(ZZ,2,2,[1,5,8,9]))
|
|
490
|
+
[1 0]
|
|
491
|
+
[1 7]
|
|
492
|
+
"""
|
|
493
|
+
if not normalized:
|
|
494
|
+
# then normalize
|
|
495
|
+
x = copy(self.edge(e))
|
|
496
|
+
else:
|
|
497
|
+
x = copy(e)
|
|
498
|
+
x.swap_columns(0, 1)
|
|
499
|
+
x.rescale_col(0, self._p)
|
|
500
|
+
return self.vertex(x)
|
|
501
|
+
|
|
502
|
+
def edge(self, M):
|
|
503
|
+
r"""
|
|
504
|
+
Normalize a matrix to the correct normalized edge
|
|
505
|
+
representative.
|
|
506
|
+
|
|
507
|
+
INPUT:
|
|
508
|
+
|
|
509
|
+
- ``M`` -- 2x2 integer matrix
|
|
510
|
+
|
|
511
|
+
OUTPUT: ``newM`` -- 2x2 integer matrix
|
|
512
|
+
|
|
513
|
+
EXAMPLES::
|
|
514
|
+
|
|
515
|
+
sage: from sage.modular.btquotients.btquotient import BruhatTitsTree
|
|
516
|
+
sage: T = BruhatTitsTree(3)
|
|
517
|
+
sage: T.edge( Matrix(ZZ,2,2,[0,-1,3,0]) )
|
|
518
|
+
[0 1]
|
|
519
|
+
[3 0]
|
|
520
|
+
"""
|
|
521
|
+
p = self._p
|
|
522
|
+
# M_orig = M
|
|
523
|
+
|
|
524
|
+
def lift(a):
|
|
525
|
+
"""
|
|
526
|
+
Naively approximate a `p`-adic integer by a positive integer.
|
|
527
|
+
|
|
528
|
+
INPUT:
|
|
529
|
+
|
|
530
|
+
- ``a`` -- `p`-adic integer
|
|
531
|
+
|
|
532
|
+
OUTPUT: integer
|
|
533
|
+
|
|
534
|
+
EXAMPLES::
|
|
535
|
+
|
|
536
|
+
sage: x = Zp(3)(-17) # needs sage.rings.padics
|
|
537
|
+
sage: lift(x) # needs sage.rings.padics
|
|
538
|
+
3486784384
|
|
539
|
+
"""
|
|
540
|
+
try:
|
|
541
|
+
return ZZ(a.lift())
|
|
542
|
+
except AttributeError:
|
|
543
|
+
return ZZ(a)
|
|
544
|
+
|
|
545
|
+
if M.base_ring() is not ZZ:
|
|
546
|
+
M = M.apply_map(lift, R=ZZ)
|
|
547
|
+
|
|
548
|
+
v = min([M[i, j].valuation(p) for i in range(2) for j in range(2)])
|
|
549
|
+
|
|
550
|
+
if v != 0:
|
|
551
|
+
M = p ** (-v) * M
|
|
552
|
+
|
|
553
|
+
det = M.determinant()
|
|
554
|
+
if not det:
|
|
555
|
+
raise NotImplementedError("matrix must be invertible")
|
|
556
|
+
|
|
557
|
+
m00 = M[0, 0].valuation(p)
|
|
558
|
+
m01 = M[0, 1].valuation(p)
|
|
559
|
+
|
|
560
|
+
if m00 <= m01:
|
|
561
|
+
tmp = det.valuation(p) - m00
|
|
562
|
+
bigpower = p ** (1 + tmp)
|
|
563
|
+
r = M[0, 0]
|
|
564
|
+
if r != 0:
|
|
565
|
+
r /= p ** m00
|
|
566
|
+
g, s, _ = xgcd(r, bigpower)
|
|
567
|
+
r = (M[1, 0] * s) % bigpower
|
|
568
|
+
newM = self._Mat_22([p ** m00, 0, r, bigpower / p])
|
|
569
|
+
else:
|
|
570
|
+
tmp = det.valuation(p) - m01
|
|
571
|
+
bigpower = p ** tmp
|
|
572
|
+
r = M[0, 1]
|
|
573
|
+
if r != 0:
|
|
574
|
+
r /= p ** m01
|
|
575
|
+
g, s, _ = xgcd(r, bigpower)
|
|
576
|
+
r = (ZZ(M[1, 1]) * s) % bigpower
|
|
577
|
+
newM = self._Mat_22([0, p ** m01, bigpower, r])
|
|
578
|
+
newM.set_immutable()
|
|
579
|
+
# assert self.is_in_group(M_orig.inverse()*newM, as_edge = True)
|
|
580
|
+
return newM
|
|
581
|
+
|
|
582
|
+
def vertex(self, M):
|
|
583
|
+
r"""
|
|
584
|
+
Normalize a matrix to the corresponding normalized
|
|
585
|
+
vertex representative
|
|
586
|
+
|
|
587
|
+
INPUT:
|
|
588
|
+
|
|
589
|
+
- ``M`` -- 2x2 integer matrix
|
|
590
|
+
|
|
591
|
+
OUTPUT: a 2x2 integer matrix
|
|
592
|
+
|
|
593
|
+
EXAMPLES::
|
|
594
|
+
|
|
595
|
+
sage: # needs sage.rings.padics
|
|
596
|
+
sage: from sage.modular.btquotients.btquotient import BruhatTitsTree
|
|
597
|
+
sage: p = 5
|
|
598
|
+
sage: T = BruhatTitsTree(p)
|
|
599
|
+
sage: m = Matrix(ZZ,2,2,[p**5,p**2,p**3,1+p+p*3])
|
|
600
|
+
sage: e = T.edge(m)
|
|
601
|
+
sage: t = m.inverse()*e
|
|
602
|
+
sage: scaling = Qp(p,20)(t.determinant()).sqrt()
|
|
603
|
+
sage: t = 1/scaling * t
|
|
604
|
+
sage: min([t[ii,jj].valuation(p) for ii in range(2) for jj in range(2)]) >= 0
|
|
605
|
+
True
|
|
606
|
+
sage: t[1,0].valuation(p) > 0
|
|
607
|
+
True
|
|
608
|
+
"""
|
|
609
|
+
p = self._p
|
|
610
|
+
|
|
611
|
+
def lift(a):
|
|
612
|
+
try:
|
|
613
|
+
return ZZ(a.lift())
|
|
614
|
+
except AttributeError:
|
|
615
|
+
return ZZ(a)
|
|
616
|
+
|
|
617
|
+
if M.base_ring() is not ZZ:
|
|
618
|
+
M = M.apply_map(lift, R=ZZ)
|
|
619
|
+
|
|
620
|
+
v = min(M[i, j].valuation(p) for i in range(2) for j in range(2))
|
|
621
|
+
|
|
622
|
+
if v:
|
|
623
|
+
M = p ** (-v) * M
|
|
624
|
+
m00 = M[0, 0].valuation(p)
|
|
625
|
+
m01 = M[0, 1].valuation(p)
|
|
626
|
+
if m01 < m00:
|
|
627
|
+
M = copy(M)
|
|
628
|
+
M.swap_columns(0, 1)
|
|
629
|
+
m00 = m01
|
|
630
|
+
tmp = M.determinant().valuation(p) - m00
|
|
631
|
+
bigpower = p ** tmp
|
|
632
|
+
r = M[0, 0]
|
|
633
|
+
if r:
|
|
634
|
+
r /= p ** m00
|
|
635
|
+
# r = ZZ(r) % bigpower
|
|
636
|
+
g, s, _ = xgcd(r, bigpower)
|
|
637
|
+
m10 = M[1, 0] % bigpower
|
|
638
|
+
r = (m10 * s) % bigpower
|
|
639
|
+
newM = self._Mat_22([p ** m00, 0, r, bigpower])
|
|
640
|
+
newM.set_immutable()
|
|
641
|
+
# assert self.is_in_group(M_orig.inverse()*newM, as_edge=False)
|
|
642
|
+
return newM
|
|
643
|
+
|
|
644
|
+
def edges_leaving_origin(self):
|
|
645
|
+
r"""
|
|
646
|
+
Find normalized representatives for the `p+1` edges
|
|
647
|
+
leaving the origin vertex corresponding to the homothety class
|
|
648
|
+
of `\ZZ_p^2`. These are cached.
|
|
649
|
+
|
|
650
|
+
OUTPUT: list of size `p+1` of 2x2 integer matrices
|
|
651
|
+
|
|
652
|
+
EXAMPLES::
|
|
653
|
+
|
|
654
|
+
sage: from sage.modular.btquotients.btquotient import BruhatTitsTree
|
|
655
|
+
sage: T = BruhatTitsTree(3)
|
|
656
|
+
sage: T.edges_leaving_origin()
|
|
657
|
+
[
|
|
658
|
+
[0 1] [3 0] [0 1] [0 1]
|
|
659
|
+
[3 0], [0 1], [3 1], [3 2]
|
|
660
|
+
]
|
|
661
|
+
"""
|
|
662
|
+
try:
|
|
663
|
+
return self._edges_leaving_origin
|
|
664
|
+
except AttributeError:
|
|
665
|
+
p = self._p
|
|
666
|
+
self._edges_leaving_origin = [self.edge(self._Mat_22([0, -1, p, 0]))]
|
|
667
|
+
self._edges_leaving_origin.extend([self.edge(self._Mat_22([p, i, 0, 1])) for i in range(p)])
|
|
668
|
+
return self._edges_leaving_origin
|
|
669
|
+
|
|
670
|
+
def edge_between_vertices(self, v1, v2, normalized=False):
|
|
671
|
+
r"""
|
|
672
|
+
Compute the normalized matrix rep. for the edge
|
|
673
|
+
passing between two vertices.
|
|
674
|
+
|
|
675
|
+
INPUT:
|
|
676
|
+
|
|
677
|
+
- ``v1`` -- 2x2 integer matrix
|
|
678
|
+
|
|
679
|
+
- ``v2`` -- 2x2 integer matrix
|
|
680
|
+
|
|
681
|
+
- ``normalized`` -- boolean (default: ``False``); whether the
|
|
682
|
+
vertices are normalized
|
|
683
|
+
|
|
684
|
+
OUTPUT:
|
|
685
|
+
|
|
686
|
+
- 2x2 integer matrix, representing the edge from ``v1`` to
|
|
687
|
+
``v2``. If ``v1`` and ``v2`` are not at distance `1`, raise
|
|
688
|
+
a :exc:`ValueError`.
|
|
689
|
+
|
|
690
|
+
EXAMPLES::
|
|
691
|
+
|
|
692
|
+
sage: from sage.modular.btquotients.btquotient import BruhatTitsTree
|
|
693
|
+
sage: p = 7
|
|
694
|
+
sage: T = BruhatTitsTree(p)
|
|
695
|
+
sage: v1 = T.vertex(Matrix(ZZ,2,2,[p,0,0,1])); v1
|
|
696
|
+
[7 0]
|
|
697
|
+
[0 1]
|
|
698
|
+
sage: v2 = T.vertex(Matrix(ZZ,2,2,[p,1,0,1])); v2
|
|
699
|
+
[1 0]
|
|
700
|
+
[1 7]
|
|
701
|
+
sage: T.edge_between_vertices(v1,v2)
|
|
702
|
+
Traceback (most recent call last):
|
|
703
|
+
...
|
|
704
|
+
ValueError: Vertices are not adjacent.
|
|
705
|
+
|
|
706
|
+
sage: v3 = T.vertex(Matrix(ZZ,2,2,[1,0,0,1])); v3
|
|
707
|
+
[1 0]
|
|
708
|
+
[0 1]
|
|
709
|
+
sage: T.edge_between_vertices(v1,v3)
|
|
710
|
+
[0 1]
|
|
711
|
+
[1 0]
|
|
712
|
+
"""
|
|
713
|
+
if normalized:
|
|
714
|
+
v22 = v2
|
|
715
|
+
else:
|
|
716
|
+
v22 = self.vertex(v2)
|
|
717
|
+
for e in self.leaving_edges(v1):
|
|
718
|
+
if self.target(e) == v22:
|
|
719
|
+
return e
|
|
720
|
+
raise ValueError('Vertices are not adjacent.')
|
|
721
|
+
|
|
722
|
+
def leaving_edges(self, M):
|
|
723
|
+
r"""
|
|
724
|
+
Return edges leaving a vertex.
|
|
725
|
+
|
|
726
|
+
INPUT:
|
|
727
|
+
|
|
728
|
+
- ``M`` -- 2x2 integer matrix
|
|
729
|
+
|
|
730
|
+
OUTPUT: list of size `p+1` of 2x2 integer matrices
|
|
731
|
+
|
|
732
|
+
EXAMPLES::
|
|
733
|
+
|
|
734
|
+
sage: from sage.modular.btquotients.btquotient import BruhatTitsTree
|
|
735
|
+
sage: p = 7
|
|
736
|
+
sage: T = BruhatTitsTree(p)
|
|
737
|
+
sage: T.leaving_edges(Matrix(ZZ,2,2,[1,0,0,1]))
|
|
738
|
+
[
|
|
739
|
+
[0 1] [7 0] [0 1] [0 1] [0 1] [0 1] [0 1] [0 1]
|
|
740
|
+
[7 0], [0 1], [7 1], [7 4], [7 5], [7 2], [7 3], [7 6]
|
|
741
|
+
]
|
|
742
|
+
"""
|
|
743
|
+
return [self.edge(M * A) for A in self.edges_leaving_origin()]
|
|
744
|
+
|
|
745
|
+
def opposite(self, e):
|
|
746
|
+
r"""
|
|
747
|
+
This function returns the edge oriented oppositely to a
|
|
748
|
+
given edge.
|
|
749
|
+
|
|
750
|
+
INPUT:
|
|
751
|
+
|
|
752
|
+
- ``e`` -- 2x2 integer matrix
|
|
753
|
+
|
|
754
|
+
OUTPUT: 2x2 integer matrix
|
|
755
|
+
|
|
756
|
+
EXAMPLES::
|
|
757
|
+
|
|
758
|
+
sage: from sage.modular.btquotients.btquotient import BruhatTitsTree
|
|
759
|
+
sage: p = 7
|
|
760
|
+
sage: T = BruhatTitsTree(p)
|
|
761
|
+
sage: e = Matrix(ZZ,2,2,[1,0,0,1])
|
|
762
|
+
sage: T.opposite(e)
|
|
763
|
+
[0 1]
|
|
764
|
+
[7 0]
|
|
765
|
+
sage: T.opposite(T.opposite(e)) == e
|
|
766
|
+
True
|
|
767
|
+
"""
|
|
768
|
+
x = copy(e)
|
|
769
|
+
x.swap_columns(0, 1)
|
|
770
|
+
x.rescale_col(0, self._p)
|
|
771
|
+
return self.edge(x)
|
|
772
|
+
|
|
773
|
+
def entering_edges(self, v):
|
|
774
|
+
r"""
|
|
775
|
+
This function returns the edges entering a given vertex.
|
|
776
|
+
|
|
777
|
+
INPUT:
|
|
778
|
+
|
|
779
|
+
- ``v`` -- 2x2 integer matrix
|
|
780
|
+
|
|
781
|
+
OUTPUT: list of size `p+1` of 2x2 integer matrices
|
|
782
|
+
|
|
783
|
+
EXAMPLES::
|
|
784
|
+
|
|
785
|
+
sage: from sage.modular.btquotients.btquotient import BruhatTitsTree
|
|
786
|
+
sage: p = 7
|
|
787
|
+
sage: T = BruhatTitsTree(p)
|
|
788
|
+
sage: T.entering_edges(Matrix(ZZ,2,2,[1,0,0,1]))
|
|
789
|
+
[
|
|
790
|
+
[1 0] [0 1] [1 0] [1 0] [1 0] [1 0] [1 0] [1 0]
|
|
791
|
+
[0 1], [1 0], [1 1], [4 1], [5 1], [2 1], [3 1], [6 1]
|
|
792
|
+
]
|
|
793
|
+
"""
|
|
794
|
+
return [self.opposite(e) for e in self.leaving_edges(v)]
|
|
795
|
+
|
|
796
|
+
def subdivide(self, edgelist, level):
|
|
797
|
+
r"""
|
|
798
|
+
(Ordered) edges of ``self`` may be regarded as open balls in
|
|
799
|
+
`P^1(\QQ_p)`. Given a list of edges, this function return a list
|
|
800
|
+
of edges corresponding to the level-th subdivision of the
|
|
801
|
+
corresponding opens. That is, each open ball of the input is
|
|
802
|
+
broken up into `p^{\mbox{level}}` subballs of equal radius.
|
|
803
|
+
|
|
804
|
+
INPUT:
|
|
805
|
+
|
|
806
|
+
- ``edgelist`` -- list of edges
|
|
807
|
+
|
|
808
|
+
- ``level`` -- integer
|
|
809
|
+
|
|
810
|
+
OUTPUT: list of 2x2 integer matrices
|
|
811
|
+
|
|
812
|
+
EXAMPLES::
|
|
813
|
+
|
|
814
|
+
sage: from sage.modular.btquotients.btquotient import BruhatTitsTree
|
|
815
|
+
sage: p = 3
|
|
816
|
+
sage: T = BruhatTitsTree(p)
|
|
817
|
+
sage: T.subdivide([Matrix(ZZ,2,2,[p,0,0,1])],2)
|
|
818
|
+
[
|
|
819
|
+
[27 0] [0 9] [0 9] [0 3] [0 3] [0 3] [0 3] [0 3] [0 3]
|
|
820
|
+
[ 0 1], [3 1], [3 2], [9 1], [9 4], [9 7], [9 2], [9 5], [9 8]
|
|
821
|
+
]
|
|
822
|
+
"""
|
|
823
|
+
if level < 0:
|
|
824
|
+
return []
|
|
825
|
+
if level == 0:
|
|
826
|
+
return [self._Mat_22(edge) for edge in edgelist]
|
|
827
|
+
else:
|
|
828
|
+
newEgood = []
|
|
829
|
+
for edge in edgelist:
|
|
830
|
+
edge = self._Mat_22(edge)
|
|
831
|
+
origin = self.origin(edge)
|
|
832
|
+
newE = self.leaving_edges(self.target(edge))
|
|
833
|
+
newEgood.extend([e for e in newE if self.target(e) != origin])
|
|
834
|
+
return self.subdivide(newEgood, level - 1)
|
|
835
|
+
|
|
836
|
+
def get_balls(self, center=1, level=1):
|
|
837
|
+
r"""
|
|
838
|
+
Return a decomposition of `P^1(\QQ_p)` into compact
|
|
839
|
+
open balls.
|
|
840
|
+
|
|
841
|
+
Each vertex in the Bruhat-Tits tree gives a decomposition of
|
|
842
|
+
`P^1(\QQ_p)` into `p+1` open balls. Each of these balls may
|
|
843
|
+
be further subdivided, to get a finer decomposition.
|
|
844
|
+
|
|
845
|
+
This function returns the decomposition of `P^1(\QQ_p)`
|
|
846
|
+
corresponding to ``center`` into `(p+1)p^{\mbox{level}}` balls.
|
|
847
|
+
|
|
848
|
+
EXAMPLES::
|
|
849
|
+
|
|
850
|
+
sage: from sage.modular.btquotients.btquotient import BruhatTitsTree
|
|
851
|
+
sage: p = 2
|
|
852
|
+
sage: T = BruhatTitsTree(p)
|
|
853
|
+
sage: T.get_balls(Matrix(ZZ,2,2,[p,0,0,1]),1)
|
|
854
|
+
[
|
|
855
|
+
[0 1] [0 1] [8 0] [0 4] [0 2] [0 2]
|
|
856
|
+
[2 0], [2 1], [0 1], [2 1], [4 1], [4 3]
|
|
857
|
+
]
|
|
858
|
+
"""
|
|
859
|
+
return self.subdivide(self.leaving_edges(center), level)
|
|
860
|
+
|
|
861
|
+
def find_path(self, v, boundary=None):
|
|
862
|
+
r"""
|
|
863
|
+
Compute a path from a vertex to a given set of so-called
|
|
864
|
+
boundary vertices, whose interior must contain the origin
|
|
865
|
+
vertex. In the case that the boundary is not specified, it
|
|
866
|
+
computes the geodesic between the given vertex and the origin.
|
|
867
|
+
In the case that the boundary contains more than one vertex,
|
|
868
|
+
it computes the geodesic to some point of the boundary.
|
|
869
|
+
|
|
870
|
+
INPUT:
|
|
871
|
+
|
|
872
|
+
- ``v`` -- a 2x2 matrix representing a vertex ``boundary``
|
|
873
|
+
|
|
874
|
+
- a list of matrices (default: ``None``); if omitted, finds the
|
|
875
|
+
geodesic from ``v`` to the central vertex
|
|
876
|
+
|
|
877
|
+
OUTPUT:
|
|
878
|
+
|
|
879
|
+
An ordered list of vertices describing the geodesic from
|
|
880
|
+
``v`` to ``boundary``, followed by the vertex in the boundary
|
|
881
|
+
that is closest to ``v``.
|
|
882
|
+
|
|
883
|
+
EXAMPLES::
|
|
884
|
+
|
|
885
|
+
sage: from sage.modular.btquotients.btquotient import BruhatTitsTree
|
|
886
|
+
sage: p = 3
|
|
887
|
+
sage: T = BruhatTitsTree(p)
|
|
888
|
+
sage: T.find_path( Matrix(ZZ,2,2,[p^4,0,0,1]) )
|
|
889
|
+
(
|
|
890
|
+
[[81 0]
|
|
891
|
+
[ 0 1], [27 0]
|
|
892
|
+
[ 0 1], [9 0]
|
|
893
|
+
[0 1], [3 0] [1 0]
|
|
894
|
+
[0 1]] , [0 1]
|
|
895
|
+
)
|
|
896
|
+
sage: T.find_path( Matrix(ZZ,2,2,[p^3,0,134,p^2]) )
|
|
897
|
+
(
|
|
898
|
+
[[27 0]
|
|
899
|
+
[ 8 9], [27 0]
|
|
900
|
+
[ 2 3], [27 0]
|
|
901
|
+
[ 0 1], [9 0]
|
|
902
|
+
[0 1], [3 0] [1 0]
|
|
903
|
+
[0 1]] , [0 1]
|
|
904
|
+
)
|
|
905
|
+
"""
|
|
906
|
+
if boundary is None:
|
|
907
|
+
m = self._Mat_22(1)
|
|
908
|
+
m.set_immutable()
|
|
909
|
+
boundary = {m: m}
|
|
910
|
+
m = self._mat_p001
|
|
911
|
+
new_v = self.vertex(v)
|
|
912
|
+
chain = []
|
|
913
|
+
while new_v[1, 0] != 0 or new_v[0, 0].valuation(self._p) < new_v[1, 1].valuation(self._p):
|
|
914
|
+
if new_v in boundary:
|
|
915
|
+
return chain, boundary[new_v]
|
|
916
|
+
chain.append(new_v)
|
|
917
|
+
new_v = self.vertex(new_v * m)
|
|
918
|
+
|
|
919
|
+
if new_v in boundary:
|
|
920
|
+
return chain, boundary[new_v]
|
|
921
|
+
|
|
922
|
+
while True:
|
|
923
|
+
if new_v in boundary:
|
|
924
|
+
return chain, boundary[new_v]
|
|
925
|
+
chain.append(new_v)
|
|
926
|
+
new_v = self._Mat_22([new_v[0, 0] / self._p, 0, 0, 1])
|
|
927
|
+
new_v.set_immutable()
|
|
928
|
+
raise RuntimeError
|
|
929
|
+
|
|
930
|
+
def find_containing_affinoid(self, z):
|
|
931
|
+
r"""
|
|
932
|
+
Return the vertex corresponding to the affinoid in the
|
|
933
|
+
`p`-adic upper half plane that a given (unramified!) point
|
|
934
|
+
reduces to.
|
|
935
|
+
|
|
936
|
+
INPUT:
|
|
937
|
+
|
|
938
|
+
- ``z`` -- an element of an unramified extension of `\QQ_p`
|
|
939
|
+
that is not contained in `\QQ_p`
|
|
940
|
+
|
|
941
|
+
OUTPUT: a 2x2 integer matrix representing a vertex of ``self``
|
|
942
|
+
|
|
943
|
+
EXAMPLES::
|
|
944
|
+
|
|
945
|
+
sage: # needs sage.rings.padics
|
|
946
|
+
sage: from sage.modular.btquotients.btquotient import BruhatTitsTree
|
|
947
|
+
sage: T = BruhatTitsTree(5)
|
|
948
|
+
sage: K.<a> = Qq(5^2,20)
|
|
949
|
+
sage: T.find_containing_affinoid(a)
|
|
950
|
+
[1 0]
|
|
951
|
+
[0 1]
|
|
952
|
+
sage: z = 5*a+3
|
|
953
|
+
sage: v = T.find_containing_affinoid(z).inverse(); v
|
|
954
|
+
[ 1 0]
|
|
955
|
+
[-2/5 1/5]
|
|
956
|
+
|
|
957
|
+
Note that the translate of ``z`` belongs to the standard
|
|
958
|
+
affinoid. That is, it is a `p`-adic unit and its reduction
|
|
959
|
+
modulo `p` is not in `\GF{p}`::
|
|
960
|
+
|
|
961
|
+
sage: gz = (v[0,0]*z+v[0,1])/(v[1,0]*z+v[1,1]); gz # needs sage.rings.padics
|
|
962
|
+
(a + 1) + O(5^19)
|
|
963
|
+
sage: gz.valuation() == 0 # needs sage.rings.padics
|
|
964
|
+
True
|
|
965
|
+
"""
|
|
966
|
+
# Assume z belongs to some extension of QQp.
|
|
967
|
+
p = self._p
|
|
968
|
+
if z.valuation() < 0:
|
|
969
|
+
return self.vertex(self._Mat_22([0, 1, p, 0]) * self.find_containing_affinoid(1 / (p * z)))
|
|
970
|
+
a = 0
|
|
971
|
+
pn = 1
|
|
972
|
+
val = z.valuation()
|
|
973
|
+
L = [0 for _ in range(val)]
|
|
974
|
+
|
|
975
|
+
L.extend(z.expansion())
|
|
976
|
+
for n in range(len(L)):
|
|
977
|
+
if L[n] != 0:
|
|
978
|
+
if len(L[n]) > 1:
|
|
979
|
+
break
|
|
980
|
+
if len(L[n]) > 0:
|
|
981
|
+
a += pn * L[n][0]
|
|
982
|
+
pn *= p
|
|
983
|
+
return self.vertex(self._Mat_22([pn, a, 0, 1]))
|
|
984
|
+
|
|
985
|
+
def find_geodesic(self, v1, v2, normalized=True):
|
|
986
|
+
r"""
|
|
987
|
+
This function computes the geodesic between two vertices.
|
|
988
|
+
|
|
989
|
+
INPUT:
|
|
990
|
+
|
|
991
|
+
- ``v1`` -- 2x2 integer matrix representing a vertex
|
|
992
|
+
|
|
993
|
+
- ``v2`` -- 2x2 integer matrix representing a vertex
|
|
994
|
+
|
|
995
|
+
- ``normalized`` -- boolean (default: ``True``)
|
|
996
|
+
|
|
997
|
+
OUTPUT:
|
|
998
|
+
|
|
999
|
+
An ordered list of 2x2 integer matrices representing the vertices
|
|
1000
|
+
of the paths joining ``v1`` and ``v2``.
|
|
1001
|
+
|
|
1002
|
+
EXAMPLES::
|
|
1003
|
+
|
|
1004
|
+
sage: from sage.modular.btquotients.btquotient import BruhatTitsTree
|
|
1005
|
+
sage: p = 3
|
|
1006
|
+
sage: T = BruhatTitsTree(p)
|
|
1007
|
+
sage: v1 = T.vertex( Matrix(ZZ,2,2,[p^3, 0, 1, p^1]) ); v1
|
|
1008
|
+
[27 0]
|
|
1009
|
+
[ 1 3]
|
|
1010
|
+
sage: v2 = T.vertex( Matrix(ZZ,2,2,[p,2,0,p]) ); v2
|
|
1011
|
+
[1 0]
|
|
1012
|
+
[6 9]
|
|
1013
|
+
sage: T.find_geodesic(v1,v2)
|
|
1014
|
+
[
|
|
1015
|
+
[27 0] [27 0] [9 0] [3 0] [1 0] [1 0] [1 0]
|
|
1016
|
+
[ 1 3], [ 0 1], [0 1], [0 1], [0 1], [0 3], [6 9]
|
|
1017
|
+
]
|
|
1018
|
+
"""
|
|
1019
|
+
if not normalized:
|
|
1020
|
+
v1, v2 = self.vertex(v1), self.vertex(v2)
|
|
1021
|
+
gamma = v2
|
|
1022
|
+
vv = self.vertex(gamma.adjugate() * v1)
|
|
1023
|
+
chain, v0 = self.find_path(vv)
|
|
1024
|
+
return [self.vertex(gamma * x) for x in chain + [v0]]
|
|
1025
|
+
|
|
1026
|
+
def find_covering(self, z1, z2, level=0):
|
|
1027
|
+
r"""
|
|
1028
|
+
Compute a covering of `P^1(\QQ_p)` adapted to a certain
|
|
1029
|
+
geodesic in ``self``.
|
|
1030
|
+
|
|
1031
|
+
More precisely, the `p`-adic upper half plane points ``z1``
|
|
1032
|
+
and ``z2`` reduce to vertices `v_1`, `v_2`.
|
|
1033
|
+
The returned covering consists of all the edges leaving the
|
|
1034
|
+
geodesic from `v_1` to `v_2`.
|
|
1035
|
+
|
|
1036
|
+
INPUT:
|
|
1037
|
+
|
|
1038
|
+
- ``z1``, ``z2`` -- unramified algebraic points of h_p
|
|
1039
|
+
|
|
1040
|
+
OUTPUT: list of 2x2 integer matrices representing edges of self
|
|
1041
|
+
|
|
1042
|
+
EXAMPLES::
|
|
1043
|
+
|
|
1044
|
+
sage: # needs sage.rings.padics
|
|
1045
|
+
sage: from sage.modular.btquotients.btquotient import BruhatTitsTree
|
|
1046
|
+
sage: p = 3
|
|
1047
|
+
sage: K.<a> = Qq(p^2)
|
|
1048
|
+
sage: T = BruhatTitsTree(p)
|
|
1049
|
+
sage: z1 = a + a*p
|
|
1050
|
+
sage: z2 = 1 + a*p + a*p^2 - p^6
|
|
1051
|
+
sage: T.find_covering(z1,z2)
|
|
1052
|
+
[
|
|
1053
|
+
[0 1] [3 0] [0 1] [0 1] [0 1] [0 1]
|
|
1054
|
+
[3 0], [0 1], [3 2], [9 1], [9 4], [9 7]
|
|
1055
|
+
]
|
|
1056
|
+
|
|
1057
|
+
.. NOTE::
|
|
1058
|
+
|
|
1059
|
+
This function is used to compute certain Coleman integrals
|
|
1060
|
+
on `P^1`. That's why the input consists of two points of
|
|
1061
|
+
the `p`-adic upper half plane, but decomposes
|
|
1062
|
+
`P^1(\QQ_p)`. This decomposition is what allows us to
|
|
1063
|
+
represent the relevant integrand as a locally analytic
|
|
1064
|
+
function. The ``z1`` and ``z2`` appear in the integrand.
|
|
1065
|
+
"""
|
|
1066
|
+
v1 = self.find_containing_affinoid(z1)
|
|
1067
|
+
v2 = self.find_containing_affinoid(z2)
|
|
1068
|
+
vertex_set = [self._Mat_22(0)]
|
|
1069
|
+
vertex_set += self.find_geodesic(v1, v2)
|
|
1070
|
+
vertex_set += [self._Mat_22(0)]
|
|
1071
|
+
E = []
|
|
1072
|
+
for ii in range(1, len(vertex_set) - 1):
|
|
1073
|
+
vv = vertex_set[ii]
|
|
1074
|
+
# m = vv.determinant().valuation(self._p)
|
|
1075
|
+
newE = self.leaving_edges(vv)
|
|
1076
|
+
for e in newE:
|
|
1077
|
+
targ = self.target(e)
|
|
1078
|
+
if targ != vertex_set[ii - 1] and targ != vertex_set[ii + 1]:
|
|
1079
|
+
E.extend(self.subdivide([e], level))
|
|
1080
|
+
return E
|
|
1081
|
+
|
|
1082
|
+
|
|
1083
|
+
class Vertex(SageObject):
|
|
1084
|
+
r"""
|
|
1085
|
+
This is a structure to represent vertices of quotients of the
|
|
1086
|
+
Bruhat-Tits tree. It is useful to enrich the representation of
|
|
1087
|
+
the vertex as a matrix with extra data.
|
|
1088
|
+
|
|
1089
|
+
INPUT:
|
|
1090
|
+
|
|
1091
|
+
- ``p`` -- prime integer
|
|
1092
|
+
|
|
1093
|
+
- ``label`` -- integer which uniquely identifies this vertex
|
|
1094
|
+
|
|
1095
|
+
- ``rep`` -- a 2x2 matrix in reduced form representing this
|
|
1096
|
+
vertex
|
|
1097
|
+
|
|
1098
|
+
- ``leaving_edges`` -- (default: empty list) list of edges
|
|
1099
|
+
leaving this vertex
|
|
1100
|
+
|
|
1101
|
+
- ``entering_edges`` -- (default: empty list) list of edges
|
|
1102
|
+
entering this vertex
|
|
1103
|
+
|
|
1104
|
+
- ``determinant`` -- (default: ``None``) the determinant of ``rep``,
|
|
1105
|
+
if known
|
|
1106
|
+
|
|
1107
|
+
- ``valuation`` -- (default: ``None``) the valuation of the
|
|
1108
|
+
determinant of ``rep``, if known
|
|
1109
|
+
|
|
1110
|
+
EXAMPLES::
|
|
1111
|
+
|
|
1112
|
+
sage: from sage.modular.btquotients.btquotient import Vertex
|
|
1113
|
+
sage: v1 = Vertex(5,0,Matrix(ZZ,2,2,[1,2,3,18]))
|
|
1114
|
+
sage: v1.rep
|
|
1115
|
+
[ 1 2]
|
|
1116
|
+
[ 3 18]
|
|
1117
|
+
sage: v1.entering_edges
|
|
1118
|
+
[]
|
|
1119
|
+
|
|
1120
|
+
AUTHORS:
|
|
1121
|
+
|
|
1122
|
+
- Marc Masdeu (2012-02-20)
|
|
1123
|
+
"""
|
|
1124
|
+
def __init__(self, p, label, rep, leaving_edges=None,
|
|
1125
|
+
entering_edges=None, determinant=None, valuation=None):
|
|
1126
|
+
"""
|
|
1127
|
+
This initializes a structure to represent vertices of
|
|
1128
|
+
quotients of the Bruhat-Tits tree. It is useful to enrich the
|
|
1129
|
+
representation of the vertex as a matrix with extra data.
|
|
1130
|
+
|
|
1131
|
+
EXAMPLES::
|
|
1132
|
+
|
|
1133
|
+
sage: from sage.modular.btquotients.btquotient import Vertex
|
|
1134
|
+
sage: Y = BruhatTitsQuotient(5,13)
|
|
1135
|
+
sage: v1 = Vertex(5,0,Matrix(ZZ,2,2,[1,2,3,18]))
|
|
1136
|
+
sage: TestSuite(v1).run()
|
|
1137
|
+
"""
|
|
1138
|
+
if leaving_edges is None:
|
|
1139
|
+
leaving_edges = []
|
|
1140
|
+
if entering_edges is None:
|
|
1141
|
+
entering_edges = []
|
|
1142
|
+
if determinant is None:
|
|
1143
|
+
determinant = rep.determinant()
|
|
1144
|
+
if valuation is None:
|
|
1145
|
+
valuation = determinant.valuation(p)
|
|
1146
|
+
self.p = p
|
|
1147
|
+
self.label = label
|
|
1148
|
+
self.rep = rep
|
|
1149
|
+
self.rep.set_immutable()
|
|
1150
|
+
self.determinant = determinant
|
|
1151
|
+
self.valuation = valuation
|
|
1152
|
+
self.parity = valuation % 2
|
|
1153
|
+
self.leaving_edges = leaving_edges
|
|
1154
|
+
self.entering_edges = entering_edges
|
|
1155
|
+
|
|
1156
|
+
def _repr_(self):
|
|
1157
|
+
r"""
|
|
1158
|
+
Return the representation of ``self`` as a string.
|
|
1159
|
+
|
|
1160
|
+
EXAMPLES::
|
|
1161
|
+
|
|
1162
|
+
sage: X = BruhatTitsQuotient(3,5)
|
|
1163
|
+
sage: X.get_vertex_list()[0]
|
|
1164
|
+
Vertex of Bruhat-Tits tree for p = 3
|
|
1165
|
+
"""
|
|
1166
|
+
return "Vertex of Bruhat-Tits tree for p = %s" % (self.p)
|
|
1167
|
+
|
|
1168
|
+
def __eq__(self, other):
|
|
1169
|
+
"""
|
|
1170
|
+
Return ``self == other``.
|
|
1171
|
+
|
|
1172
|
+
TESTS::
|
|
1173
|
+
|
|
1174
|
+
sage: from sage.modular.btquotients.btquotient import Vertex
|
|
1175
|
+
sage: v1 = Vertex(7,0,Matrix(ZZ,2,2,[1,2,3,18]))
|
|
1176
|
+
sage: v1 == v1
|
|
1177
|
+
True
|
|
1178
|
+
"""
|
|
1179
|
+
if self.p != other.p:
|
|
1180
|
+
return False
|
|
1181
|
+
if self.label != other.label:
|
|
1182
|
+
return False
|
|
1183
|
+
if self.rep != other.rep:
|
|
1184
|
+
return False
|
|
1185
|
+
if self.determinant != other.determinant:
|
|
1186
|
+
return False
|
|
1187
|
+
if self.valuation != other.valuation:
|
|
1188
|
+
return False
|
|
1189
|
+
return self.parity == other.parity
|
|
1190
|
+
|
|
1191
|
+
def __ne__(self, other):
|
|
1192
|
+
"""
|
|
1193
|
+
Return ``self != other``.
|
|
1194
|
+
|
|
1195
|
+
TESTS::
|
|
1196
|
+
|
|
1197
|
+
sage: from sage.modular.btquotients.btquotient import Vertex
|
|
1198
|
+
sage: v1 = Vertex(7,0,Matrix(ZZ,2,2,[1,2,3,18]))
|
|
1199
|
+
sage: v1 != v1
|
|
1200
|
+
False
|
|
1201
|
+
"""
|
|
1202
|
+
return not self.__eq__(other)
|
|
1203
|
+
|
|
1204
|
+
|
|
1205
|
+
class Edge(SageObject):
|
|
1206
|
+
r"""
|
|
1207
|
+
This is a structure to represent edges of quotients of the
|
|
1208
|
+
Bruhat-Tits tree. It is useful to enrich the representation of an
|
|
1209
|
+
edge as a matrix with extra data.
|
|
1210
|
+
|
|
1211
|
+
INPUT:
|
|
1212
|
+
|
|
1213
|
+
- ``p`` -- prime integer
|
|
1214
|
+
|
|
1215
|
+
- ``label`` -- integer which uniquely identifies this edge
|
|
1216
|
+
|
|
1217
|
+
- ``rep`` -- a 2x2 matrix in reduced form representing this edge
|
|
1218
|
+
|
|
1219
|
+
- ``origin`` -- the origin vertex of ``self``
|
|
1220
|
+
|
|
1221
|
+
- ``target`` -- the target vertex of ``self``
|
|
1222
|
+
|
|
1223
|
+
- ``links`` -- (default: empty list) list of elements of
|
|
1224
|
+
`\Gamma` which identify different edges in the Bruhat-Tits tree
|
|
1225
|
+
which are equivalent to ``self``
|
|
1226
|
+
|
|
1227
|
+
- ``opposite`` -- (default: ``None``) the edge opposite to ``self``
|
|
1228
|
+
|
|
1229
|
+
- ``determinant`` -- (default: ``None``) the determinant of ``rep``,
|
|
1230
|
+
if known
|
|
1231
|
+
|
|
1232
|
+
- ``valuation`` -- (default: ``None``) the valuation of the
|
|
1233
|
+
determinant of ``rep``, if known
|
|
1234
|
+
|
|
1235
|
+
EXAMPLES::
|
|
1236
|
+
|
|
1237
|
+
sage: from sage.modular.btquotients.btquotient import Edge, Vertex
|
|
1238
|
+
sage: v1 = Vertex(7,0,Matrix(ZZ,2,2,[1,2,3,18]))
|
|
1239
|
+
sage: v2 = Vertex(7,0,Matrix(ZZ,2,2,[3,2,1,18]))
|
|
1240
|
+
sage: e1 = Edge(7,0,Matrix(ZZ,2,2,[1,2,3,18]),v1,v2)
|
|
1241
|
+
sage: e1.rep
|
|
1242
|
+
[ 1 2]
|
|
1243
|
+
[ 3 18]
|
|
1244
|
+
|
|
1245
|
+
AUTHORS:
|
|
1246
|
+
|
|
1247
|
+
- Marc Masdeu (2012-02-20)
|
|
1248
|
+
"""
|
|
1249
|
+
def __init__(self, p, label, rep, origin, target, links=None,
|
|
1250
|
+
opposite=None, determinant=None, valuation=None):
|
|
1251
|
+
"""
|
|
1252
|
+
Representation for edges of quotients of the Bruhat-Tits
|
|
1253
|
+
tree. It is useful to enrich the representation of an edge as
|
|
1254
|
+
a matrix with extra data.
|
|
1255
|
+
|
|
1256
|
+
EXAMPLES::
|
|
1257
|
+
|
|
1258
|
+
sage: from sage.modular.btquotients.btquotient import Edge
|
|
1259
|
+
sage: Y = BruhatTitsQuotient(5,11)
|
|
1260
|
+
sage: el = Y.get_edge_list()
|
|
1261
|
+
sage: e1 = el.pop()
|
|
1262
|
+
sage: e2 = Edge(5,e1.label,e1.rep,e1.origin,e1.target)
|
|
1263
|
+
sage: TestSuite(e2).run()
|
|
1264
|
+
"""
|
|
1265
|
+
if links is None:
|
|
1266
|
+
links = []
|
|
1267
|
+
if determinant is None:
|
|
1268
|
+
determinant = rep.determinant()
|
|
1269
|
+
if valuation is None:
|
|
1270
|
+
valuation = determinant.valuation(p)
|
|
1271
|
+
self.p = p
|
|
1272
|
+
self.label = label
|
|
1273
|
+
self.rep = rep
|
|
1274
|
+
self.rep.set_immutable()
|
|
1275
|
+
self.origin = origin
|
|
1276
|
+
self.target = target
|
|
1277
|
+
self.links = links
|
|
1278
|
+
self.opposite = opposite
|
|
1279
|
+
self.determinant = determinant
|
|
1280
|
+
self.valuation = valuation
|
|
1281
|
+
self.parity = valuation % 2
|
|
1282
|
+
|
|
1283
|
+
def _repr_(self):
|
|
1284
|
+
r"""
|
|
1285
|
+
Return the representation of ``self`` as a string.
|
|
1286
|
+
|
|
1287
|
+
EXAMPLES::
|
|
1288
|
+
|
|
1289
|
+
sage: X = BruhatTitsQuotient(3,5)
|
|
1290
|
+
sage: X.get_edge_list()[0]
|
|
1291
|
+
Edge of Bruhat-Tits tree for p = 3
|
|
1292
|
+
"""
|
|
1293
|
+
return "Edge of Bruhat-Tits tree for p = %s" % (self.p)
|
|
1294
|
+
|
|
1295
|
+
def __eq__(self, other):
|
|
1296
|
+
"""
|
|
1297
|
+
Return ``self == other``.
|
|
1298
|
+
|
|
1299
|
+
TESTS::
|
|
1300
|
+
|
|
1301
|
+
sage: from sage.modular.btquotients.btquotient import Edge,Vertex
|
|
1302
|
+
sage: v1 = Vertex(7,0,Matrix(ZZ,2,2,[1,2,3,18]))
|
|
1303
|
+
sage: v2 = Vertex(7,0,Matrix(ZZ,2,2,[3,2,1,18]))
|
|
1304
|
+
sage: e1 = Edge(7,0,Matrix(ZZ,2,2,[1,2,3,18]),v1,v2)
|
|
1305
|
+
sage: e1 == e1
|
|
1306
|
+
True
|
|
1307
|
+
"""
|
|
1308
|
+
if self.p != other.p:
|
|
1309
|
+
return False
|
|
1310
|
+
if self.label != other.label:
|
|
1311
|
+
return False
|
|
1312
|
+
if self.rep != other.rep:
|
|
1313
|
+
return False
|
|
1314
|
+
if self.origin != other.origin:
|
|
1315
|
+
return False
|
|
1316
|
+
if self.target != other.target:
|
|
1317
|
+
return False
|
|
1318
|
+
if self.links != other.links:
|
|
1319
|
+
return False
|
|
1320
|
+
if self.opposite != other.opposite:
|
|
1321
|
+
return False
|
|
1322
|
+
if self.determinant != other.determinant:
|
|
1323
|
+
return False
|
|
1324
|
+
if self.valuation != other.valuation:
|
|
1325
|
+
return False
|
|
1326
|
+
return self.parity == other.parity
|
|
1327
|
+
|
|
1328
|
+
def __ne__(self, other):
|
|
1329
|
+
"""
|
|
1330
|
+
Return ``self != other``.
|
|
1331
|
+
|
|
1332
|
+
TESTS::
|
|
1333
|
+
|
|
1334
|
+
sage: from sage.modular.btquotients.btquotient import Edge,Vertex
|
|
1335
|
+
sage: v1 = Vertex(7,0,Matrix(ZZ,2,2,[1,2,3,18]))
|
|
1336
|
+
sage: v2 = Vertex(7,0,Matrix(ZZ,2,2,[3,2,1,18]))
|
|
1337
|
+
sage: e1 = Edge(7,0,Matrix(ZZ,2,2,[1,2,3,18]),v1,v2)
|
|
1338
|
+
sage: e1 != e1
|
|
1339
|
+
False
|
|
1340
|
+
"""
|
|
1341
|
+
return not self.__eq__(other)
|
|
1342
|
+
|
|
1343
|
+
|
|
1344
|
+
class BruhatTitsQuotient(SageObject, UniqueRepresentation):
|
|
1345
|
+
r"""
|
|
1346
|
+
This function computes the quotient of the Bruhat-Tits tree
|
|
1347
|
+
by an arithmetic quaternionic group. The group in question is the
|
|
1348
|
+
group of norm 1 elements in an Eichler `\ZZ[1/p]`-order of some (tame)
|
|
1349
|
+
level inside of a definite quaternion algebra that is unramified
|
|
1350
|
+
at the prime `p`. Note that this routine relies in Magma in the case
|
|
1351
|
+
`p = 2` or when `N^{+} > 1`.
|
|
1352
|
+
|
|
1353
|
+
INPUT:
|
|
1354
|
+
|
|
1355
|
+
- ``p`` -- a prime number
|
|
1356
|
+
|
|
1357
|
+
- ``Nminus`` -- squarefree integer divisible by an odd number of
|
|
1358
|
+
distinct primes and relatively prime to p. This is the
|
|
1359
|
+
discriminant of the definite quaternion algebra that one is
|
|
1360
|
+
quotienting by.
|
|
1361
|
+
|
|
1362
|
+
- ``Nplus`` -- integer coprime to pNminus (default: 1). This is
|
|
1363
|
+
the tame level. It need not be squarefree! If Nplus is not 1
|
|
1364
|
+
then the user currently needs magma installed due to sage's
|
|
1365
|
+
inability to compute well with nonmaximal Eichler orders in
|
|
1366
|
+
rational (definite) quaternion algebras.
|
|
1367
|
+
|
|
1368
|
+
- ``character`` -- a Dirichlet character (default: ``None``) of modulus
|
|
1369
|
+
`pN^-N^+`
|
|
1370
|
+
|
|
1371
|
+
- ``use_magma`` -- boolean (default: ``False``); if True, uses Magma
|
|
1372
|
+
for quaternion arithmetic
|
|
1373
|
+
|
|
1374
|
+
- ``magma_session`` -- (default: ``None``) if specified, the Magma session
|
|
1375
|
+
to use
|
|
1376
|
+
|
|
1377
|
+
EXAMPLES:
|
|
1378
|
+
|
|
1379
|
+
Here is an example without a Dirichlet character::
|
|
1380
|
+
|
|
1381
|
+
sage: X = BruhatTitsQuotient(13, 19)
|
|
1382
|
+
sage: X.genus()
|
|
1383
|
+
19
|
|
1384
|
+
sage: G = X.get_graph(); G
|
|
1385
|
+
Multi-graph on 4 vertices
|
|
1386
|
+
|
|
1387
|
+
And an example with a Dirichlet character::
|
|
1388
|
+
|
|
1389
|
+
sage: f = DirichletGroup(6)[1]
|
|
1390
|
+
sage: X = BruhatTitsQuotient(3,2*5*7,character = f)
|
|
1391
|
+
sage: X.genus()
|
|
1392
|
+
5
|
|
1393
|
+
|
|
1394
|
+
.. NOTE::
|
|
1395
|
+
|
|
1396
|
+
A sage implementation of Eichler orders in rational quaternions
|
|
1397
|
+
algebras would remove the dependency on magma.
|
|
1398
|
+
|
|
1399
|
+
AUTHORS:
|
|
1400
|
+
|
|
1401
|
+
- Marc Masdeu (2012-02-20)
|
|
1402
|
+
"""
|
|
1403
|
+
@staticmethod
|
|
1404
|
+
def __classcall__(cls, p, Nminus, Nplus=1, character=None,
|
|
1405
|
+
use_magma=False, seed=None, magma_session=None):
|
|
1406
|
+
"""
|
|
1407
|
+
Ensure that a canonical BruhatTitsQuotient is created.
|
|
1408
|
+
|
|
1409
|
+
EXAMPLES::
|
|
1410
|
+
|
|
1411
|
+
sage: BruhatTitsQuotient(3,17) is BruhatTitsQuotient(3,17,1)
|
|
1412
|
+
True
|
|
1413
|
+
"""
|
|
1414
|
+
return super().__classcall__(cls, p, Nminus, Nplus,
|
|
1415
|
+
character, use_magma,
|
|
1416
|
+
seed, magma_session)
|
|
1417
|
+
|
|
1418
|
+
def __init__(self, p, Nminus, Nplus=1, character=None,
|
|
1419
|
+
use_magma=False, seed=None, magma_session=None):
|
|
1420
|
+
"""
|
|
1421
|
+
Compute the quotient of the Bruhat-Tits tree by an arithmetic
|
|
1422
|
+
quaternionic group.
|
|
1423
|
+
|
|
1424
|
+
EXAMPLES::
|
|
1425
|
+
|
|
1426
|
+
sage: Y = BruhatTitsQuotient(19,11)
|
|
1427
|
+
sage: TestSuite(Y).run()
|
|
1428
|
+
"""
|
|
1429
|
+
Nminus = Integer(Nminus)
|
|
1430
|
+
Nplus = Integer(Nplus)
|
|
1431
|
+
p = Integer(p)
|
|
1432
|
+
lev = p * Nminus
|
|
1433
|
+
self._order_is_initialized = False
|
|
1434
|
+
if character is not None:
|
|
1435
|
+
extra_level = character.conductor()
|
|
1436
|
+
if not extra_level.is_squarefree():
|
|
1437
|
+
raise ValueError("character must be of squarefree conductor")
|
|
1438
|
+
self._trivial_character = False
|
|
1439
|
+
else:
|
|
1440
|
+
G = DirichletGroup(lev * Nplus)
|
|
1441
|
+
character = G([1] * G.ngens())
|
|
1442
|
+
extra_level = 1
|
|
1443
|
+
self._trivial_character = True
|
|
1444
|
+
|
|
1445
|
+
if not p.is_prime():
|
|
1446
|
+
raise ValueError("p must be a prime")
|
|
1447
|
+
if not lev.is_squarefree():
|
|
1448
|
+
raise ValueError("level must be squarefree")
|
|
1449
|
+
if (gcd(lev, Nplus) > 1):
|
|
1450
|
+
raise ValueError("level and conductor must be coprime")
|
|
1451
|
+
|
|
1452
|
+
# if len(Nminus.factor()) % 2 != 1:
|
|
1453
|
+
# raise ValueError("Nminus should be divisible by an odd number of primes")
|
|
1454
|
+
|
|
1455
|
+
self._pN = p
|
|
1456
|
+
self._p = p
|
|
1457
|
+
self._Nminus = Nminus
|
|
1458
|
+
self._Nplus = Nplus
|
|
1459
|
+
if use_magma or self._Nplus != 1 or self._p == 2:
|
|
1460
|
+
from sage.interfaces.magma import magma
|
|
1461
|
+
try:
|
|
1462
|
+
if magma_session is None:
|
|
1463
|
+
self._magma = magma
|
|
1464
|
+
else:
|
|
1465
|
+
self._magma = magma_session
|
|
1466
|
+
self._magma(p)
|
|
1467
|
+
except RuntimeError:
|
|
1468
|
+
raise NotImplementedError('Sage does not know yet how to work with the kind of orders that you are trying to use. Try installing Magma first and set it up so that Sage can use it.')
|
|
1469
|
+
|
|
1470
|
+
# This is added for debugging, in order to have reproducible results
|
|
1471
|
+
if seed is not None:
|
|
1472
|
+
self._magma.function_call('SetSeed', seed, nvals=0)
|
|
1473
|
+
self._use_magma = True
|
|
1474
|
+
else:
|
|
1475
|
+
self._use_magma = False
|
|
1476
|
+
|
|
1477
|
+
self._BT = BruhatTitsTree(p)
|
|
1478
|
+
|
|
1479
|
+
self._prec = -1
|
|
1480
|
+
|
|
1481
|
+
self._cached_vertices = {}
|
|
1482
|
+
self._cached_edges = {}
|
|
1483
|
+
self._cached_paths = {}
|
|
1484
|
+
self._cached_decomps = {}
|
|
1485
|
+
self._cached_equivalent = {}
|
|
1486
|
+
self._CM_points = {}
|
|
1487
|
+
|
|
1488
|
+
self._V = (QQ ** 4).ambient_module().change_ring(ZZ)
|
|
1489
|
+
self._Mat_44 = MatrixSpace(ZZ, 4, 4)
|
|
1490
|
+
self._Mat_22 = MatrixSpace(ZZ, 2, 2)
|
|
1491
|
+
self._Mat_41 = MatrixSpace(ZZ, 4, 1)
|
|
1492
|
+
if extra_level == 1:
|
|
1493
|
+
self._extra_level = []
|
|
1494
|
+
else:
|
|
1495
|
+
self._extra_level = [ff[0] for ff in extra_level.factor()]
|
|
1496
|
+
self.get_extra_embedding_matrices()
|
|
1497
|
+
self._character = character
|
|
1498
|
+
self._Xv = [self._Mat_22([1, 0, 0, 0]),
|
|
1499
|
+
self._Mat_22([0, 1, 0, 0]),
|
|
1500
|
+
self._Mat_22([0, 0, 1, 0]),
|
|
1501
|
+
self._Mat_22([0, 0, 0, 1])]
|
|
1502
|
+
self._Xe = [self._Mat_22([1, 0, 0, 0]),
|
|
1503
|
+
self._Mat_22([0, 1, 0, 0]),
|
|
1504
|
+
self._Mat_22([0, 0, self._p, 0]),
|
|
1505
|
+
self._Mat_22([0, 0, 0, 1])]
|
|
1506
|
+
|
|
1507
|
+
def _cache_key(self):
|
|
1508
|
+
r"""
|
|
1509
|
+
Return a hash of ``self``, for using in caching.
|
|
1510
|
+
|
|
1511
|
+
EXAMPLES::
|
|
1512
|
+
|
|
1513
|
+
sage: X = BruhatTitsQuotient(5,13)
|
|
1514
|
+
sage: X._cache_key() == BruhatTitsQuotient(5,13)._cache_key()
|
|
1515
|
+
True
|
|
1516
|
+
sage: X._cache_key() == BruhatTitsQuotient(5,11)._cache_key()
|
|
1517
|
+
False
|
|
1518
|
+
|
|
1519
|
+
sage: Y = BruhatTitsQuotient(5,13,use_magma = True) # optional - magma
|
|
1520
|
+
sage: Y._cache_key() == X._cache_key() # optional - magma
|
|
1521
|
+
False
|
|
1522
|
+
"""
|
|
1523
|
+
return hash((self._p, self._Nminus, self._Nplus, self._character, self._use_magma))
|
|
1524
|
+
|
|
1525
|
+
__hash__ = _cache_key
|
|
1526
|
+
|
|
1527
|
+
def _repr_(self):
|
|
1528
|
+
r"""
|
|
1529
|
+
Return the representation of ``self`` as a string.
|
|
1530
|
+
|
|
1531
|
+
EXAMPLES::
|
|
1532
|
+
|
|
1533
|
+
sage: X = BruhatTitsQuotient(5,13); X
|
|
1534
|
+
Quotient of the Bruhat Tits tree of GL_2(QQ_5) with discriminant 13 and level 1
|
|
1535
|
+
"""
|
|
1536
|
+
return "Quotient of the Bruhat Tits tree of GL_2(QQ_%s) with discriminant %s and level %s" % (self.prime(), self.Nminus().factor(), self.Nplus().factor())
|
|
1537
|
+
|
|
1538
|
+
def __eq__(self, other):
|
|
1539
|
+
r"""
|
|
1540
|
+
Compare ``self`` with ``other``.
|
|
1541
|
+
|
|
1542
|
+
EXAMPLES::
|
|
1543
|
+
|
|
1544
|
+
sage: X = BruhatTitsQuotient(5,13)
|
|
1545
|
+
sage: Y = BruhatTitsQuotient(p = 5, Nminus = 13, Nplus=1,seed = 1231)
|
|
1546
|
+
sage: X == Y
|
|
1547
|
+
True
|
|
1548
|
+
"""
|
|
1549
|
+
if self._p != other._p:
|
|
1550
|
+
return False
|
|
1551
|
+
if self._Nminus != other._Nminus:
|
|
1552
|
+
return False
|
|
1553
|
+
if self._Nplus != other._Nplus:
|
|
1554
|
+
return False
|
|
1555
|
+
return self._character == other._character
|
|
1556
|
+
|
|
1557
|
+
def __ne__(self, other):
|
|
1558
|
+
r"""
|
|
1559
|
+
Compare ``self`` with ``other``.
|
|
1560
|
+
|
|
1561
|
+
EXAMPLES::
|
|
1562
|
+
|
|
1563
|
+
sage: X = BruhatTitsQuotient(5,13)
|
|
1564
|
+
sage: Y = BruhatTitsQuotient(p = 5, Nminus = 13, Nplus=1,seed = 1231)
|
|
1565
|
+
sage: X != Y
|
|
1566
|
+
False
|
|
1567
|
+
"""
|
|
1568
|
+
return not self.__eq__(other)
|
|
1569
|
+
|
|
1570
|
+
def _latex_(self):
|
|
1571
|
+
r"""
|
|
1572
|
+
Return the LaTeX representation of ``self``.
|
|
1573
|
+
|
|
1574
|
+
EXAMPLES::
|
|
1575
|
+
|
|
1576
|
+
sage: X = BruhatTitsQuotient(5,13); latex(X)
|
|
1577
|
+
X(5 \cdot 13,1)\otimes_{\Bold{Z}} \Bold{F}_{5}
|
|
1578
|
+
"""
|
|
1579
|
+
return "X(%s,%s)\\otimes_{\\Bold{Z}} \\Bold{F}_{%s}" % (latex(self.level().factor()), latex(self.Nplus().factor()), latex(self.prime()))
|
|
1580
|
+
|
|
1581
|
+
def get_vertex_dict(self):
|
|
1582
|
+
r"""
|
|
1583
|
+
This function returns the vertices of the quotient viewed as
|
|
1584
|
+
a dict.
|
|
1585
|
+
|
|
1586
|
+
OUTPUT: a Python dict with the vertices of the quotient
|
|
1587
|
+
|
|
1588
|
+
EXAMPLES::
|
|
1589
|
+
|
|
1590
|
+
sage: X = BruhatTitsQuotient(37,3)
|
|
1591
|
+
sage: X.get_vertex_dict()
|
|
1592
|
+
{[1 0]
|
|
1593
|
+
[0 1]: Vertex of Bruhat-Tits tree for p = 37, [ 1 0]
|
|
1594
|
+
[ 0 37]: Vertex of Bruhat-Tits tree for p = 37}
|
|
1595
|
+
"""
|
|
1596
|
+
try:
|
|
1597
|
+
return self._boundary
|
|
1598
|
+
except AttributeError:
|
|
1599
|
+
self._compute_quotient()
|
|
1600
|
+
return self._boundary
|
|
1601
|
+
|
|
1602
|
+
def get_vertex_list(self):
|
|
1603
|
+
r"""
|
|
1604
|
+
Return a list of the vertices of the quotient.
|
|
1605
|
+
|
|
1606
|
+
EXAMPLES::
|
|
1607
|
+
|
|
1608
|
+
sage: X = BruhatTitsQuotient(37,3)
|
|
1609
|
+
sage: X.get_vertex_list()
|
|
1610
|
+
[Vertex of Bruhat-Tits tree for p = 37, Vertex of Bruhat-Tits tree for p = 37]
|
|
1611
|
+
"""
|
|
1612
|
+
try:
|
|
1613
|
+
return self._vertex_list
|
|
1614
|
+
except AttributeError:
|
|
1615
|
+
self._compute_quotient()
|
|
1616
|
+
return self._vertex_list
|
|
1617
|
+
|
|
1618
|
+
def get_edge_list(self):
|
|
1619
|
+
r"""
|
|
1620
|
+
Return a list of ``Edge`` which represent a fundamental
|
|
1621
|
+
domain inside the Bruhat-Tits tree for the quotient.
|
|
1622
|
+
|
|
1623
|
+
EXAMPLES::
|
|
1624
|
+
|
|
1625
|
+
sage: X = BruhatTitsQuotient(37,3)
|
|
1626
|
+
sage: len(X.get_edge_list())
|
|
1627
|
+
8
|
|
1628
|
+
"""
|
|
1629
|
+
try:
|
|
1630
|
+
return self._edge_list
|
|
1631
|
+
except AttributeError:
|
|
1632
|
+
self._compute_quotient()
|
|
1633
|
+
return self._edge_list
|
|
1634
|
+
|
|
1635
|
+
def get_list(self):
|
|
1636
|
+
r"""
|
|
1637
|
+
Return a list of ``Edge`` which represent a fundamental
|
|
1638
|
+
domain inside the Bruhat-Tits tree for the quotient,
|
|
1639
|
+
together with a list of the opposite edges. This is used
|
|
1640
|
+
to work with automorphic forms.
|
|
1641
|
+
|
|
1642
|
+
EXAMPLES::
|
|
1643
|
+
|
|
1644
|
+
sage: X = BruhatTitsQuotient(37,3)
|
|
1645
|
+
sage: len(X.get_list())
|
|
1646
|
+
16
|
|
1647
|
+
"""
|
|
1648
|
+
E = self.get_edge_list()
|
|
1649
|
+
return E + [e.opposite for e in E]
|
|
1650
|
+
|
|
1651
|
+
def get_nontorsion_generators(self):
|
|
1652
|
+
r"""
|
|
1653
|
+
Use a fundamental domain in the Bruhat-Tits tree, and
|
|
1654
|
+
certain gluing data for boundary vertices, in order to compute
|
|
1655
|
+
a collection of generators for the nontorsion part
|
|
1656
|
+
of the arithmetic quaternionic group that one is quotienting by.
|
|
1657
|
+
This is analogous to using a polygonal rep. of a compact real
|
|
1658
|
+
surface to present its fundamental domain.
|
|
1659
|
+
|
|
1660
|
+
OUTPUT:
|
|
1661
|
+
|
|
1662
|
+
- A generating list of elements of an arithmetic
|
|
1663
|
+
quaternionic group.
|
|
1664
|
+
|
|
1665
|
+
EXAMPLES::
|
|
1666
|
+
|
|
1667
|
+
sage: X = BruhatTitsQuotient(3,13)
|
|
1668
|
+
sage: len(X.get_nontorsion_generators())
|
|
1669
|
+
3
|
|
1670
|
+
"""
|
|
1671
|
+
try:
|
|
1672
|
+
return list(self._nontorsion_generators)
|
|
1673
|
+
except AttributeError:
|
|
1674
|
+
self._compute_quotient()
|
|
1675
|
+
return list(self._nontorsion_generators)
|
|
1676
|
+
|
|
1677
|
+
@cached_method
|
|
1678
|
+
def get_generators(self):
|
|
1679
|
+
r"""
|
|
1680
|
+
Use a fundamental domain in the Bruhat-Tits tree, and
|
|
1681
|
+
certain gluing data for boundary vertices, in order to compute
|
|
1682
|
+
a collection of generators for the arithmetic quaternionic
|
|
1683
|
+
group that one is quotienting by. This is analogous to using a
|
|
1684
|
+
polygonal rep. of a compact real surface to present its
|
|
1685
|
+
fundamental domain.
|
|
1686
|
+
|
|
1687
|
+
OUTPUT:
|
|
1688
|
+
|
|
1689
|
+
- A generating list of elements of an arithmetic
|
|
1690
|
+
quaternionic group.
|
|
1691
|
+
|
|
1692
|
+
EXAMPLES::
|
|
1693
|
+
|
|
1694
|
+
sage: X = BruhatTitsQuotient(3,2)
|
|
1695
|
+
sage: len(X.get_generators())
|
|
1696
|
+
2
|
|
1697
|
+
"""
|
|
1698
|
+
ans = self.get_nontorsion_generators()
|
|
1699
|
+
for s in self.get_vertex_stabs():
|
|
1700
|
+
for o in s:
|
|
1701
|
+
if o[2]:
|
|
1702
|
+
ans.append(o[0])
|
|
1703
|
+
break
|
|
1704
|
+
return ans
|
|
1705
|
+
|
|
1706
|
+
def _compute_invariants(self):
|
|
1707
|
+
"""
|
|
1708
|
+
Compute certain invariants from the level data of the quotient
|
|
1709
|
+
which allow one to compute the genus of the curve.
|
|
1710
|
+
|
|
1711
|
+
Details to be found in Theorem 3.8 of [FM2014]_.
|
|
1712
|
+
|
|
1713
|
+
EXAMPLES::
|
|
1714
|
+
|
|
1715
|
+
sage: X = BruhatTitsQuotient(23,11)
|
|
1716
|
+
sage: X._compute_invariants()
|
|
1717
|
+
"""
|
|
1718
|
+
Nplus = self._Nplus
|
|
1719
|
+
lev = self._Nminus
|
|
1720
|
+
e4 = 1
|
|
1721
|
+
e3 = 1
|
|
1722
|
+
mu = Nplus
|
|
1723
|
+
for f in lev.factor():
|
|
1724
|
+
e4 *= (1 - kronecker_symbol(-4, Integer(f[0])))
|
|
1725
|
+
e3 *= (1 - kronecker_symbol(-3, Integer(f[0])))
|
|
1726
|
+
mu *= Integer(f[0]) - 1
|
|
1727
|
+
for f in Nplus.factor():
|
|
1728
|
+
if (f[1] == 1):
|
|
1729
|
+
e4 *= (1 + kronecker_symbol(-4, Integer(f[0])))
|
|
1730
|
+
e3 *= (1 + kronecker_symbol(-3, Integer(f[0])))
|
|
1731
|
+
else:
|
|
1732
|
+
if kronecker_symbol(-4, Integer(f[0])) == 1:
|
|
1733
|
+
e4 *= 2
|
|
1734
|
+
else:
|
|
1735
|
+
e4 = 0
|
|
1736
|
+
if kronecker_symbol(-3, Integer(f[0])) == 1:
|
|
1737
|
+
e3 *= 2
|
|
1738
|
+
else:
|
|
1739
|
+
e3 = 0
|
|
1740
|
+
mu *= 1 + 1 / Integer(f[0])
|
|
1741
|
+
self.e3 = e3
|
|
1742
|
+
self.e4 = e4
|
|
1743
|
+
self.mu = mu
|
|
1744
|
+
|
|
1745
|
+
@lazy_attribute
|
|
1746
|
+
def e3(self):
|
|
1747
|
+
r"""
|
|
1748
|
+
Compute the `e_3` invariant defined by the formula
|
|
1749
|
+
|
|
1750
|
+
.. MATH::
|
|
1751
|
+
|
|
1752
|
+
e_k =\prod_{\ell\mid pN^-}\left(1-\left(\frac{-3}{\ell}\right)\right)\prod_{\ell \| N^+}\left(1+\left(\frac{-3}{\ell}\right)\right)\prod_{\ell^2\mid N^+} \nu_\ell(3)
|
|
1753
|
+
|
|
1754
|
+
OUTPUT: integer
|
|
1755
|
+
|
|
1756
|
+
EXAMPLES::
|
|
1757
|
+
|
|
1758
|
+
sage: X = BruhatTitsQuotient(31,3)
|
|
1759
|
+
sage: X.e3
|
|
1760
|
+
1
|
|
1761
|
+
"""
|
|
1762
|
+
self._compute_invariants()
|
|
1763
|
+
return self.e3
|
|
1764
|
+
|
|
1765
|
+
@lazy_attribute
|
|
1766
|
+
def e4(self):
|
|
1767
|
+
r"""
|
|
1768
|
+
Compute the `e_4` invariant defined by the formula
|
|
1769
|
+
|
|
1770
|
+
.. MATH::
|
|
1771
|
+
|
|
1772
|
+
e_k =\prod_{\ell\mid pN^-}\left(1-\left(\frac{-k}{\ell}\right)\right)\prod_{\ell \| N^+}\left(1+\left(\frac{-k}{\ell}\right)\right)\prod_{\ell^2\mid N^+} \nu_\ell(k)
|
|
1773
|
+
|
|
1774
|
+
OUTPUT: integer
|
|
1775
|
+
|
|
1776
|
+
EXAMPLES::
|
|
1777
|
+
|
|
1778
|
+
sage: X = BruhatTitsQuotient(31,3)
|
|
1779
|
+
sage: X.e4
|
|
1780
|
+
2
|
|
1781
|
+
"""
|
|
1782
|
+
self._compute_invariants()
|
|
1783
|
+
return self.e4
|
|
1784
|
+
|
|
1785
|
+
@lazy_attribute
|
|
1786
|
+
def mu(self):
|
|
1787
|
+
"""
|
|
1788
|
+
Compute the mu invariant of ``self``.
|
|
1789
|
+
|
|
1790
|
+
OUTPUT: integer
|
|
1791
|
+
|
|
1792
|
+
EXAMPLES::
|
|
1793
|
+
|
|
1794
|
+
sage: X = BruhatTitsQuotient(29,3)
|
|
1795
|
+
sage: X.mu
|
|
1796
|
+
2
|
|
1797
|
+
"""
|
|
1798
|
+
self._compute_invariants()
|
|
1799
|
+
return self.mu
|
|
1800
|
+
|
|
1801
|
+
@cached_method
|
|
1802
|
+
def get_num_verts(self):
|
|
1803
|
+
r"""
|
|
1804
|
+
Return the number of vertices in the quotient using the formula
|
|
1805
|
+
`V = 2(\mu/12 + e_3/3 + e_4/4)`.
|
|
1806
|
+
|
|
1807
|
+
OUTPUT:
|
|
1808
|
+
|
|
1809
|
+
- An integer (the number of vertices)
|
|
1810
|
+
|
|
1811
|
+
EXAMPLES::
|
|
1812
|
+
|
|
1813
|
+
sage: X = BruhatTitsQuotient(29,11)
|
|
1814
|
+
sage: X.get_num_verts()
|
|
1815
|
+
4
|
|
1816
|
+
"""
|
|
1817
|
+
return 2 * Integer(self.mu / 12 + self.e3 / 3 + self.e4 / 4)
|
|
1818
|
+
|
|
1819
|
+
@cached_method
|
|
1820
|
+
def get_num_ordered_edges(self):
|
|
1821
|
+
"""
|
|
1822
|
+
Return the number of ordered edges `E` in the quotient using
|
|
1823
|
+
the formula relating the genus `g` with the number of vertices `V`
|
|
1824
|
+
and that of unordered edges `E/2`: `E = 2(g + V - 1)`.
|
|
1825
|
+
|
|
1826
|
+
OUTPUT: integer
|
|
1827
|
+
|
|
1828
|
+
EXAMPLES::
|
|
1829
|
+
|
|
1830
|
+
sage: X = BruhatTitsQuotient(3,2)
|
|
1831
|
+
sage: X.get_num_ordered_edges()
|
|
1832
|
+
2
|
|
1833
|
+
"""
|
|
1834
|
+
return 2 * (self.genus() + self.get_num_verts() - 1)
|
|
1835
|
+
|
|
1836
|
+
def genus_no_formula(self):
|
|
1837
|
+
"""
|
|
1838
|
+
Compute the genus of the quotient from the data of the
|
|
1839
|
+
quotient graph. This should agree with self.genus().
|
|
1840
|
+
|
|
1841
|
+
OUTPUT: integer
|
|
1842
|
+
|
|
1843
|
+
EXAMPLES::
|
|
1844
|
+
|
|
1845
|
+
sage: X = BruhatTitsQuotient(5,2*3*29)
|
|
1846
|
+
sage: X.genus_no_formula()
|
|
1847
|
+
17
|
|
1848
|
+
sage: X.genus_no_formula() == X.genus()
|
|
1849
|
+
True
|
|
1850
|
+
"""
|
|
1851
|
+
return ZZ(1 - len(self.get_vertex_list()) + len(self.get_edge_list()))
|
|
1852
|
+
|
|
1853
|
+
@cached_method
|
|
1854
|
+
def genus(self):
|
|
1855
|
+
r"""
|
|
1856
|
+
Compute the genus of the quotient graph using a formula
|
|
1857
|
+
This should agree with self.genus_no_formula().
|
|
1858
|
+
|
|
1859
|
+
Compute the genus of the Shimura curve
|
|
1860
|
+
corresponding to this quotient via Cerednik-Drinfeld. It is
|
|
1861
|
+
computed via a formula and not in terms of the quotient graph.
|
|
1862
|
+
|
|
1863
|
+
INPUT:
|
|
1864
|
+
|
|
1865
|
+
- ``level`` -- integer (default: ``None``); a level. By default, use that
|
|
1866
|
+
of ``self``.
|
|
1867
|
+
|
|
1868
|
+
- ``Nplus`` -- integer (default: ``None``); a conductor. By default, use
|
|
1869
|
+
that of ``self``.
|
|
1870
|
+
|
|
1871
|
+
OUTPUT: integer equal to the genus
|
|
1872
|
+
|
|
1873
|
+
EXAMPLES::
|
|
1874
|
+
|
|
1875
|
+
sage: X = BruhatTitsQuotient(3,2*5*31)
|
|
1876
|
+
sage: X.genus()
|
|
1877
|
+
21
|
|
1878
|
+
sage: X.genus() == X.genus_no_formula()
|
|
1879
|
+
True
|
|
1880
|
+
"""
|
|
1881
|
+
return self.dimension_harmonic_cocycles(2)
|
|
1882
|
+
|
|
1883
|
+
@cached_method
|
|
1884
|
+
def dimension_harmonic_cocycles(self, k, lev=None, Nplus=None,
|
|
1885
|
+
character=None):
|
|
1886
|
+
r"""
|
|
1887
|
+
Compute the dimension of the space of harmonic cocycles
|
|
1888
|
+
of weight `k` on ``self``.
|
|
1889
|
+
|
|
1890
|
+
OUTPUT: integer equal to the dimension
|
|
1891
|
+
|
|
1892
|
+
EXAMPLES::
|
|
1893
|
+
|
|
1894
|
+
sage: X = BruhatTitsQuotient(3,7)
|
|
1895
|
+
sage: [X.dimension_harmonic_cocycles(k) for k in range(2,20,2)]
|
|
1896
|
+
[1, 4, 4, 8, 8, 12, 12, 16, 16]
|
|
1897
|
+
|
|
1898
|
+
sage: X = BruhatTitsQuotient(2,5) # optional - magma
|
|
1899
|
+
sage: [X.dimension_harmonic_cocycles(k) for k in range(2,40,2)] # optional - magma
|
|
1900
|
+
[0, 1, 3, 1, 3, 5, 3, 5, 7, 5, 7, 9, 7, 9, 11, 9, 11, 13, 11]
|
|
1901
|
+
|
|
1902
|
+
sage: X = BruhatTitsQuotient(7, 2 * 3 * 5)
|
|
1903
|
+
sage: X.dimension_harmonic_cocycles(4)
|
|
1904
|
+
12
|
|
1905
|
+
sage: X = BruhatTitsQuotient(7, 2 * 3 * 5 * 11 * 13)
|
|
1906
|
+
sage: X.dimension_harmonic_cocycles(2)
|
|
1907
|
+
481
|
|
1908
|
+
sage: X.dimension_harmonic_cocycles(4)
|
|
1909
|
+
1440
|
|
1910
|
+
"""
|
|
1911
|
+
k = ZZ(k)
|
|
1912
|
+
if lev is None:
|
|
1913
|
+
lev = self._p * self._Nminus
|
|
1914
|
+
else:
|
|
1915
|
+
lev = ZZ(lev)
|
|
1916
|
+
if Nplus is None:
|
|
1917
|
+
Nplus = self._Nplus
|
|
1918
|
+
else:
|
|
1919
|
+
Nplus = ZZ(Nplus)
|
|
1920
|
+
|
|
1921
|
+
if character is None:
|
|
1922
|
+
if not self._trivial_character:
|
|
1923
|
+
character = self._character
|
|
1924
|
+
lN = lev * Nplus
|
|
1925
|
+
kernel = [r for r in lN.coprime_integers(lN)
|
|
1926
|
+
if character(r) == 1]
|
|
1927
|
+
else:
|
|
1928
|
+
character = None
|
|
1929
|
+
kernel = None
|
|
1930
|
+
|
|
1931
|
+
if k == 0:
|
|
1932
|
+
return 0
|
|
1933
|
+
|
|
1934
|
+
verbose('Computing dimension for (k,level,nplus,char) = (%s, %s, %s, %s)' % (k, lev, Nplus, character), level=2)
|
|
1935
|
+
|
|
1936
|
+
if lev == 1:
|
|
1937
|
+
return Gamma0(Nplus).dimension_cusp_forms(k=k)
|
|
1938
|
+
|
|
1939
|
+
f = lev.factor()
|
|
1940
|
+
if any(l[1] != 1 for l in f):
|
|
1941
|
+
raise NotImplementedError('The level should be squarefree for '
|
|
1942
|
+
'this function to work... Sorry!')
|
|
1943
|
+
|
|
1944
|
+
def GH(N, ker):
|
|
1945
|
+
return Gamma0(N) if character is None else GammaH_constructor(N, ker)
|
|
1946
|
+
|
|
1947
|
+
def mumu(N):
|
|
1948
|
+
p = 1
|
|
1949
|
+
for _, r in ZZ(N).factor():
|
|
1950
|
+
if r > 2:
|
|
1951
|
+
return ZZ.zero()
|
|
1952
|
+
elif r == 1:
|
|
1953
|
+
p *= -2
|
|
1954
|
+
return ZZ(p)
|
|
1955
|
+
return sum([mumu(lev // d) * GH(d * Nplus, kernel).dimension_cusp_forms(k) for d in lev.divisors()])
|
|
1956
|
+
|
|
1957
|
+
def Nplus(self):
|
|
1958
|
+
r"""
|
|
1959
|
+
Return the tame level `N^+`.
|
|
1960
|
+
|
|
1961
|
+
OUTPUT: integer equal to `N^+`
|
|
1962
|
+
|
|
1963
|
+
EXAMPLES::
|
|
1964
|
+
|
|
1965
|
+
sage: X = BruhatTitsQuotient(5,7,1)
|
|
1966
|
+
sage: X.Nplus()
|
|
1967
|
+
1
|
|
1968
|
+
"""
|
|
1969
|
+
return self._Nplus
|
|
1970
|
+
|
|
1971
|
+
def Nminus(self):
|
|
1972
|
+
r"""
|
|
1973
|
+
Return the discriminant of the relevant definite
|
|
1974
|
+
quaternion algebra.
|
|
1975
|
+
|
|
1976
|
+
OUTPUT:
|
|
1977
|
+
|
|
1978
|
+
An integer equal to `N^-`.
|
|
1979
|
+
|
|
1980
|
+
EXAMPLES::
|
|
1981
|
+
|
|
1982
|
+
sage: X = BruhatTitsQuotient(5,7)
|
|
1983
|
+
sage: X.Nminus()
|
|
1984
|
+
7
|
|
1985
|
+
"""
|
|
1986
|
+
return self._Nminus
|
|
1987
|
+
|
|
1988
|
+
@cached_method
|
|
1989
|
+
def level(self):
|
|
1990
|
+
r"""
|
|
1991
|
+
Return `p N^-`, which is the discriminant of the
|
|
1992
|
+
indefinite quaternion algebra that is uniformed by
|
|
1993
|
+
Cerednik-Drinfeld.
|
|
1994
|
+
|
|
1995
|
+
OUTPUT:
|
|
1996
|
+
|
|
1997
|
+
An integer equal to `p N^-`.
|
|
1998
|
+
|
|
1999
|
+
EXAMPLES::
|
|
2000
|
+
|
|
2001
|
+
sage: X = BruhatTitsQuotient(5,7)
|
|
2002
|
+
sage: X.level()
|
|
2003
|
+
35
|
|
2004
|
+
"""
|
|
2005
|
+
return self._Nminus * self._p
|
|
2006
|
+
|
|
2007
|
+
def prime(self):
|
|
2008
|
+
r"""
|
|
2009
|
+
Return the prime one is working with.
|
|
2010
|
+
|
|
2011
|
+
OUTPUT: integer equal to the fixed prime `p`
|
|
2012
|
+
|
|
2013
|
+
EXAMPLES::
|
|
2014
|
+
|
|
2015
|
+
sage: X = BruhatTitsQuotient(5,7)
|
|
2016
|
+
sage: X.prime()
|
|
2017
|
+
5
|
|
2018
|
+
"""
|
|
2019
|
+
return self._p
|
|
2020
|
+
|
|
2021
|
+
def get_graph(self):
|
|
2022
|
+
r"""
|
|
2023
|
+
Return the quotient graph (and compute it if needed).
|
|
2024
|
+
|
|
2025
|
+
OUTPUT: a graph representing the quotient of the Bruhat-Tits tree
|
|
2026
|
+
|
|
2027
|
+
EXAMPLES::
|
|
2028
|
+
|
|
2029
|
+
sage: X = BruhatTitsQuotient(11,5)
|
|
2030
|
+
sage: X.get_graph()
|
|
2031
|
+
Multi-graph on 2 vertices
|
|
2032
|
+
"""
|
|
2033
|
+
try:
|
|
2034
|
+
return self._S
|
|
2035
|
+
except AttributeError:
|
|
2036
|
+
self._compute_quotient()
|
|
2037
|
+
return self._S
|
|
2038
|
+
|
|
2039
|
+
def get_fundom_graph(self):
|
|
2040
|
+
r"""
|
|
2041
|
+
Return the fundamental domain (and computes it if needed).
|
|
2042
|
+
|
|
2043
|
+
OUTPUT: a fundamental domain for the action of `\Gamma`
|
|
2044
|
+
|
|
2045
|
+
EXAMPLES::
|
|
2046
|
+
|
|
2047
|
+
sage: X = BruhatTitsQuotient(11,5)
|
|
2048
|
+
sage: X.get_fundom_graph()
|
|
2049
|
+
Graph on 24 vertices
|
|
2050
|
+
"""
|
|
2051
|
+
try:
|
|
2052
|
+
return self._Sfun
|
|
2053
|
+
except AttributeError:
|
|
2054
|
+
self._compute_quotient()
|
|
2055
|
+
return self._Sfun
|
|
2056
|
+
|
|
2057
|
+
def plot(self, *args, **kwargs):
|
|
2058
|
+
r"""
|
|
2059
|
+
Plot the quotient graph.
|
|
2060
|
+
|
|
2061
|
+
OUTPUT: a plot of the quotient graph
|
|
2062
|
+
|
|
2063
|
+
EXAMPLES::
|
|
2064
|
+
|
|
2065
|
+
sage: X = BruhatTitsQuotient(7,23)
|
|
2066
|
+
sage: X.plot() # needs sage.plot
|
|
2067
|
+
Graphics object consisting of 17 graphics primitives
|
|
2068
|
+
"""
|
|
2069
|
+
S = self.get_graph()
|
|
2070
|
+
vertex_colors = {}
|
|
2071
|
+
v0 = Matrix(ZZ, 2, 2, [1, 0, 0, 1])
|
|
2072
|
+
v0.set_immutable()
|
|
2073
|
+
rainbow_color = rainbow(len(self.get_vertex_list()))
|
|
2074
|
+
for v in S.vertex_iterator():
|
|
2075
|
+
key = rainbow_color[S.get_vertex(v).label]
|
|
2076
|
+
if key in vertex_colors:
|
|
2077
|
+
vertex_colors[key].append(v)
|
|
2078
|
+
else:
|
|
2079
|
+
vertex_colors[key] = [v]
|
|
2080
|
+
|
|
2081
|
+
my_args = {}
|
|
2082
|
+
my_args['vertex_colors'] = vertex_colors
|
|
2083
|
+
my_args['color_by_label'] = True
|
|
2084
|
+
my_args['vertex_labels'] = False
|
|
2085
|
+
my_args.update(kwargs)
|
|
2086
|
+
return S.plot(*args, **my_args)
|
|
2087
|
+
|
|
2088
|
+
def plot_fundom(self, *args, **kwargs):
|
|
2089
|
+
r"""
|
|
2090
|
+
Plot a fundamental domain.
|
|
2091
|
+
|
|
2092
|
+
OUTPUT: a plot of the fundamental domain
|
|
2093
|
+
|
|
2094
|
+
EXAMPLES::
|
|
2095
|
+
|
|
2096
|
+
sage: X = BruhatTitsQuotient(7,23)
|
|
2097
|
+
sage: X.plot_fundom() # needs sage.plot
|
|
2098
|
+
Graphics object consisting of 88 graphics primitives
|
|
2099
|
+
"""
|
|
2100
|
+
S = self.get_fundom_graph()
|
|
2101
|
+
vertex_colors = {}
|
|
2102
|
+
rainbow_color = rainbow(len(self.get_vertex_list()))
|
|
2103
|
+
for v in S.vertex_iterator():
|
|
2104
|
+
key = rainbow_color[S.get_vertex(v).label]
|
|
2105
|
+
if key in vertex_colors:
|
|
2106
|
+
vertex_colors[key].append(v)
|
|
2107
|
+
else:
|
|
2108
|
+
vertex_colors[key] = [v]
|
|
2109
|
+
|
|
2110
|
+
my_args = {}
|
|
2111
|
+
my_args['vertex_colors'] = vertex_colors
|
|
2112
|
+
my_args['color_by_label'] = True
|
|
2113
|
+
my_args['vertex_labels'] = True
|
|
2114
|
+
my_args.update(kwargs)
|
|
2115
|
+
return S.plot(*args, **my_args)
|
|
2116
|
+
|
|
2117
|
+
def is_admissible(self, D) -> bool:
|
|
2118
|
+
r"""
|
|
2119
|
+
Test whether the imaginary quadratic field of
|
|
2120
|
+
discriminant `D` embeds in the quaternion algebra. It
|
|
2121
|
+
furthermore tests the Heegner hypothesis in this setting
|
|
2122
|
+
(e.g., is `p` inert in the field, etc).
|
|
2123
|
+
|
|
2124
|
+
INPUT:
|
|
2125
|
+
|
|
2126
|
+
- ``D`` -- integer whose squarefree part will define the
|
|
2127
|
+
quadratic field
|
|
2128
|
+
|
|
2129
|
+
OUTPUT: boolean describing whether the quadratic field is admissible
|
|
2130
|
+
|
|
2131
|
+
EXAMPLES::
|
|
2132
|
+
|
|
2133
|
+
sage: X = BruhatTitsQuotient(5,7)
|
|
2134
|
+
sage: [X.is_admissible(D) for D in range(-1,-20,-1)]
|
|
2135
|
+
[False, True, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, True, False]
|
|
2136
|
+
"""
|
|
2137
|
+
disc = fundamental_discriminant(D)
|
|
2138
|
+
for f in self.level().factor():
|
|
2139
|
+
if kronecker_symbol(disc, f[0]) != -1:
|
|
2140
|
+
return False
|
|
2141
|
+
return all(kronecker_symbol(disc, f[0]) == 1 for f in self._Nplus.factor())
|
|
2142
|
+
|
|
2143
|
+
def _local_splitting_map(self, prec):
|
|
2144
|
+
r"""
|
|
2145
|
+
Return an embedding of the definite quaternion algebra
|
|
2146
|
+
into the algebra of 2x2 matrices with coefficients in `\QQ_p`.
|
|
2147
|
+
|
|
2148
|
+
INPUT:
|
|
2149
|
+
|
|
2150
|
+
- ``prec`` -- integer; the precision of the splitting
|
|
2151
|
+
|
|
2152
|
+
OUTPUT: a function giving the splitting
|
|
2153
|
+
|
|
2154
|
+
EXAMPLES::
|
|
2155
|
+
|
|
2156
|
+
sage: X = BruhatTitsQuotient(11,3)
|
|
2157
|
+
sage: phi = X._local_splitting_map(10)
|
|
2158
|
+
sage: B.<i,j,k> = QuaternionAlgebra(3)
|
|
2159
|
+
sage: phi(i)**2 == QQ(i**2)*phi(B(1))
|
|
2160
|
+
True
|
|
2161
|
+
"""
|
|
2162
|
+
I, J, K = self._local_splitting(prec)
|
|
2163
|
+
|
|
2164
|
+
def phi(q):
|
|
2165
|
+
R = I.parent()
|
|
2166
|
+
v = q.coefficient_tuple()
|
|
2167
|
+
return R(v[0] + I * v[1] + J * v[2] + K * v[3])
|
|
2168
|
+
return phi
|
|
2169
|
+
|
|
2170
|
+
def _local_splitting(self, prec):
|
|
2171
|
+
r"""
|
|
2172
|
+
Find an embedding of the definite quaternion algebra
|
|
2173
|
+
into the algebra of 2x2 matrices with coefficients in `\QQ_p`.
|
|
2174
|
+
|
|
2175
|
+
INPUT:
|
|
2176
|
+
|
|
2177
|
+
- ``prec`` -- integer; the precision of the splitting
|
|
2178
|
+
|
|
2179
|
+
OUTPUT: matrices `I`, `J`, `K` giving the splitting
|
|
2180
|
+
|
|
2181
|
+
EXAMPLES::
|
|
2182
|
+
|
|
2183
|
+
sage: X = BruhatTitsQuotient(11,3)
|
|
2184
|
+
sage: phi = X._local_splitting_map(10)
|
|
2185
|
+
sage: B.<i,j,k> = QuaternionAlgebra(3)
|
|
2186
|
+
sage: phi(i)**2 == QQ(i**2)*phi(B(1))
|
|
2187
|
+
True
|
|
2188
|
+
"""
|
|
2189
|
+
assert not self._use_magma
|
|
2190
|
+
if prec <= self._prec:
|
|
2191
|
+
return self._II, self._JJ, self._KK
|
|
2192
|
+
|
|
2193
|
+
A = self.get_quaternion_algebra()
|
|
2194
|
+
|
|
2195
|
+
ZZp = Zp(self._p, prec)
|
|
2196
|
+
v = A.invariants()
|
|
2197
|
+
a = ZZp(v[0])
|
|
2198
|
+
b = ZZp(v[1])
|
|
2199
|
+
if (A.base_ring() != QQ):
|
|
2200
|
+
raise ValueError("must be rational quaternion algebra")
|
|
2201
|
+
if (A.discriminant() % self._p == 0):
|
|
2202
|
+
raise ValueError("p (=%s) must be an unramified prime" % self._p)
|
|
2203
|
+
M = MatrixSpace(ZZp, 2)
|
|
2204
|
+
|
|
2205
|
+
if a.is_square():
|
|
2206
|
+
alpha = a.sqrt()
|
|
2207
|
+
self._II = M([alpha, 0, 2 * alpha, -alpha])
|
|
2208
|
+
self._JJ = M([b, -b, b - 1, -b])
|
|
2209
|
+
else:
|
|
2210
|
+
self._II = M([0, a, 1, 0])
|
|
2211
|
+
z = 0
|
|
2212
|
+
self._JJ = 0
|
|
2213
|
+
while self._JJ == 0:
|
|
2214
|
+
c = a * z * z + b
|
|
2215
|
+
if c.is_square():
|
|
2216
|
+
x = c.sqrt()
|
|
2217
|
+
self._JJ = M([x, -a * z, z, -x])
|
|
2218
|
+
else:
|
|
2219
|
+
z += 1
|
|
2220
|
+
self._KK = self._II * self._JJ
|
|
2221
|
+
return self._II, self._JJ, self._KK
|
|
2222
|
+
|
|
2223
|
+
def _compute_embedding_matrix(self, prec, force_computation=False):
|
|
2224
|
+
r"""
|
|
2225
|
+
Return a matrix representing the embedding with the
|
|
2226
|
+
given precision.
|
|
2227
|
+
|
|
2228
|
+
INPUT:
|
|
2229
|
+
|
|
2230
|
+
- ``prec`` -- integer; the precision of the embedding matrix
|
|
2231
|
+
|
|
2232
|
+
EXAMPLES:
|
|
2233
|
+
|
|
2234
|
+
Note that the entries of the matrix are elements of Zmod::
|
|
2235
|
+
|
|
2236
|
+
sage: X = BruhatTitsQuotient(3,7)
|
|
2237
|
+
sage: A = X.get_embedding_matrix(10) # indirect doctest
|
|
2238
|
+
sage: R = A.base_ring()
|
|
2239
|
+
sage: B = X.get_eichler_order_basis()
|
|
2240
|
+
sage: R(B[0].reduced_trace()) == A[0,0]+A[3,0]
|
|
2241
|
+
True
|
|
2242
|
+
"""
|
|
2243
|
+
if self._use_magma:
|
|
2244
|
+
if not force_computation:
|
|
2245
|
+
try:
|
|
2246
|
+
return Matrix(Zmod(self._pN), 4, 4,
|
|
2247
|
+
self._cached_Iota0_matrix)
|
|
2248
|
+
except AttributeError:
|
|
2249
|
+
pass
|
|
2250
|
+
|
|
2251
|
+
Ord = self.get_eichler_order(magma=True) # force_computation = force_computation)
|
|
2252
|
+
OrdMax = self.get_maximal_order(magma=True)
|
|
2253
|
+
|
|
2254
|
+
OBasis = Ord.Basis()
|
|
2255
|
+
verbose(f'Calling magma: pMatrixRing, args = [{OrdMax}, {self._p}]')
|
|
2256
|
+
M, f, rho = self._magma.function_call('pMatrixRing', args=[OrdMax, self._p], params={'Precision': 2000}, nvals=3)
|
|
2257
|
+
v = [f.Image(OBasis[i]) for i in [1, 2, 3, 4]]
|
|
2258
|
+
|
|
2259
|
+
self._cached_Iota0_matrix = [v[kk][ii, jj].sage()
|
|
2260
|
+
for ii in range(1, 3)
|
|
2261
|
+
for jj in range(1, 3)
|
|
2262
|
+
for kk in range(4)]
|
|
2263
|
+
return Matrix(Zmod(self._pN), 4, 4, self._cached_Iota0_matrix)
|
|
2264
|
+
else:
|
|
2265
|
+
phi = self._local_splitting_map(prec)
|
|
2266
|
+
B = self.get_eichler_order_basis()
|
|
2267
|
+
return column_matrix(Zmod(self._p ** prec), 4, 4, [phi(b).list() for b in B])
|
|
2268
|
+
|
|
2269
|
+
@cached_method
|
|
2270
|
+
def get_extra_embedding_matrices(self):
|
|
2271
|
+
r"""
|
|
2272
|
+
Return a list of matrices representing the different embeddings.
|
|
2273
|
+
|
|
2274
|
+
.. NOTE::
|
|
2275
|
+
|
|
2276
|
+
The precision is very low (currently set to 5 digits),
|
|
2277
|
+
since these embeddings are only used to apply a character.
|
|
2278
|
+
|
|
2279
|
+
EXAMPLES:
|
|
2280
|
+
|
|
2281
|
+
This portion of the code is only relevant when working with a
|
|
2282
|
+
nontrivial Dirichlet character. If there is no such character
|
|
2283
|
+
then the code returns an empty list. Even if the character is
|
|
2284
|
+
not trivial it might return an empty list::
|
|
2285
|
+
|
|
2286
|
+
sage: f = DirichletGroup(6)[1]
|
|
2287
|
+
sage: X = BruhatTitsQuotient(3,2*5*7,character = f)
|
|
2288
|
+
sage: X.get_extra_embedding_matrices()
|
|
2289
|
+
[]
|
|
2290
|
+
|
|
2291
|
+
::
|
|
2292
|
+
|
|
2293
|
+
sage: f = DirichletGroup(6)[1]
|
|
2294
|
+
sage: X = BruhatTitsQuotient(5,2,3, character = f, use_magma=True) # optional - magma
|
|
2295
|
+
sage: X.get_extra_embedding_matrices() # optional - magma
|
|
2296
|
+
[
|
|
2297
|
+
[1 0 2 0]
|
|
2298
|
+
[0 0 2 0]
|
|
2299
|
+
[0 0 0 0]
|
|
2300
|
+
[1 2 2 0]
|
|
2301
|
+
]
|
|
2302
|
+
"""
|
|
2303
|
+
if not self._use_magma or len(self._extra_level) == 0:
|
|
2304
|
+
return []
|
|
2305
|
+
n_iters = 0
|
|
2306
|
+
Ord = self.get_eichler_order(magma=True)
|
|
2307
|
+
OrdMax = self.get_maximal_order(magma=True)
|
|
2308
|
+
OBasis = Ord.Basis()
|
|
2309
|
+
extra_embeddings = []
|
|
2310
|
+
success = False
|
|
2311
|
+
while not success:
|
|
2312
|
+
success = True
|
|
2313
|
+
for l in self._extra_level:
|
|
2314
|
+
success = False
|
|
2315
|
+
found = False
|
|
2316
|
+
while not found:
|
|
2317
|
+
verbose(f'Calling magma: pMatrixRing, args = [{OrdMax}, {l}]')
|
|
2318
|
+
M, f, rho = self._magma.function_call('pMatrixRing', args=[OrdMax, l], params={'Precision': 20}, nvals=3)
|
|
2319
|
+
v = [f.Image(OBasis[i]) for i in [1, 2, 3, 4]]
|
|
2320
|
+
if all(Qp(l, 5)(v[kk][2, 1].sage()).valuation() >= 1 for kk in range(4)) and not all(Qp(l, 5)(v[kk][2, 1].sage()).valuation() >= 2 for kk in range(4)):
|
|
2321
|
+
found = True
|
|
2322
|
+
success = True
|
|
2323
|
+
else:
|
|
2324
|
+
n_iters += 1
|
|
2325
|
+
verbose('Restarting magma...')
|
|
2326
|
+
self._magma.quit()
|
|
2327
|
+
self._magma = magma
|
|
2328
|
+
self._magma.function_call('SetSeed', n_iters, nvals=0)
|
|
2329
|
+
self._order_is_initialized = False
|
|
2330
|
+
self._init_order()
|
|
2331
|
+
self._compute_embedding_matrix(self._prec,
|
|
2332
|
+
force_computation=True)
|
|
2333
|
+
Ord = self.get_eichler_order(magma=True)
|
|
2334
|
+
OrdMax = self.get_maximal_order(magma=True)
|
|
2335
|
+
OBasis = Ord.Basis()
|
|
2336
|
+
extra_embeddings = []
|
|
2337
|
+
success = False
|
|
2338
|
+
break
|
|
2339
|
+
if not success:
|
|
2340
|
+
break
|
|
2341
|
+
mat = Matrix(GF(l), 4, 4, [v[kk][ii, jj].sage()
|
|
2342
|
+
for ii in range(1, 3)
|
|
2343
|
+
for jj in range(1, 3)
|
|
2344
|
+
for kk in range(4)])
|
|
2345
|
+
extra_embeddings.append(mat)
|
|
2346
|
+
return extra_embeddings
|
|
2347
|
+
|
|
2348
|
+
def _increase_precision(self, amount=1):
|
|
2349
|
+
r"""
|
|
2350
|
+
Increase the working precision.
|
|
2351
|
+
|
|
2352
|
+
INPUT:
|
|
2353
|
+
|
|
2354
|
+
- ``amount`` Integer (default: 1). The amount by which to
|
|
2355
|
+
increase the precision.
|
|
2356
|
+
|
|
2357
|
+
EXAMPLES::
|
|
2358
|
+
|
|
2359
|
+
sage: X = BruhatTitsQuotient(3,101)
|
|
2360
|
+
sage: X.get_embedding_matrix()
|
|
2361
|
+
[ O(3) 1 + O(3) 1 + O(3) 1 + O(3)]
|
|
2362
|
+
[2 + O(3) O(3) 2 + O(3) 2 + O(3)]
|
|
2363
|
+
[1 + O(3) 1 + O(3) O(3) 2 + O(3)]
|
|
2364
|
+
[1 + O(3) 2 + O(3) 2 + O(3) 2 + O(3)]
|
|
2365
|
+
sage: X._increase_precision(5)
|
|
2366
|
+
sage: X.get_embedding_matrix()[0,0]
|
|
2367
|
+
2*3^3 + 2*3^5 + O(3^6)
|
|
2368
|
+
"""
|
|
2369
|
+
if amount >= 1:
|
|
2370
|
+
self.get_embedding_matrix(prec=self._prec + amount)
|
|
2371
|
+
|
|
2372
|
+
def get_embedding_matrix(self, prec=None, exact=False):
|
|
2373
|
+
r"""
|
|
2374
|
+
Return the matrix of the embedding.
|
|
2375
|
+
|
|
2376
|
+
INPUT:
|
|
2377
|
+
|
|
2378
|
+
- ``exact`` -- boolean (default: ``False``); if ``True``, return an
|
|
2379
|
+
embedding into a matrix algebra with coefficients in a
|
|
2380
|
+
number field. Otherwise, embed into matrices over `p`-adic
|
|
2381
|
+
numbers.
|
|
2382
|
+
|
|
2383
|
+
- ``prec`` -- integer (default: ``None``); if specified, return the
|
|
2384
|
+
matrix with precision ``prec``. Otherwise, return the
|
|
2385
|
+
cached matrix (with the current working precision).
|
|
2386
|
+
|
|
2387
|
+
OUTPUT: a 4x4 matrix representing the embedding
|
|
2388
|
+
|
|
2389
|
+
EXAMPLES::
|
|
2390
|
+
|
|
2391
|
+
sage: X = BruhatTitsQuotient(7,2*3*5)
|
|
2392
|
+
sage: X.get_embedding_matrix(4)
|
|
2393
|
+
[ 1 + O(7^4) 5 + 2*7 + 3*7^3 + O(7^4) 4 + 5*7 + 6*7^2 + 6*7^3 + O(7^4) 6 + 3*7^2 + 4*7^3 + O(7^4)]
|
|
2394
|
+
[ O(7^4) O(7^4) 3 + 7 + O(7^4) 1 + 6*7 + 3*7^2 + 2*7^3 + O(7^4)]
|
|
2395
|
+
[ O(7^4) 2 + 5*7 + 6*7^3 + O(7^4) 3 + 5*7 + 6*7^2 + 6*7^3 + O(7^4) 3 + 3*7 + 3*7^2 + O(7^4)]
|
|
2396
|
+
[ 1 + O(7^4) 3 + 4*7 + 6*7^2 + 3*7^3 + O(7^4) 3 + 7 + O(7^4) 1 + 6*7 + 3*7^2 + 2*7^3 + O(7^4)]
|
|
2397
|
+
sage: X.get_embedding_matrix(3)
|
|
2398
|
+
[ 1 + O(7^4) 5 + 2*7 + 3*7^3 + O(7^4) 4 + 5*7 + 6*7^2 + 6*7^3 + O(7^4) 6 + 3*7^2 + 4*7^3 + O(7^4)]
|
|
2399
|
+
[ O(7^4) O(7^4) 3 + 7 + O(7^4) 1 + 6*7 + 3*7^2 + 2*7^3 + O(7^4)]
|
|
2400
|
+
[ O(7^4) 2 + 5*7 + 6*7^3 + O(7^4) 3 + 5*7 + 6*7^2 + 6*7^3 + O(7^4) 3 + 3*7 + 3*7^2 + O(7^4)]
|
|
2401
|
+
[ 1 + O(7^4) 3 + 4*7 + 6*7^2 + 3*7^3 + O(7^4) 3 + 7 + O(7^4) 1 + 6*7 + 3*7^2 + 2*7^3 + O(7^4)]
|
|
2402
|
+
sage: X.get_embedding_matrix(5)
|
|
2403
|
+
[ 1 + O(7^5) 5 + 2*7 + 3*7^3 + 6*7^4 + O(7^5) 4 + 5*7 + 6*7^2 + 6*7^3 + 6*7^4 + O(7^5) 6 + 3*7^2 + 4*7^3 + 5*7^4 + O(7^5)]
|
|
2404
|
+
[ O(7^5) O(7^5) 3 + 7 + O(7^5) 1 + 6*7 + 3*7^2 + 2*7^3 + 7^4 + O(7^5)]
|
|
2405
|
+
[ O(7^5) 2 + 5*7 + 6*7^3 + 5*7^4 + O(7^5) 3 + 5*7 + 6*7^2 + 6*7^3 + 6*7^4 + O(7^5) 3 + 3*7 + 3*7^2 + 5*7^4 + O(7^5)]
|
|
2406
|
+
[ 1 + O(7^5) 3 + 4*7 + 6*7^2 + 3*7^3 + O(7^5) 3 + 7 + O(7^5) 1 + 6*7 + 3*7^2 + 2*7^3 + 7^4 + O(7^5)]
|
|
2407
|
+
"""
|
|
2408
|
+
if exact is True:
|
|
2409
|
+
try:
|
|
2410
|
+
return self._Iota_exact
|
|
2411
|
+
except AttributeError:
|
|
2412
|
+
raise RuntimeError('Exact splitting not available.')
|
|
2413
|
+
else:
|
|
2414
|
+
if prec is None:
|
|
2415
|
+
prec = self._prec
|
|
2416
|
+
|
|
2417
|
+
if prec < 0:
|
|
2418
|
+
prec = 1
|
|
2419
|
+
|
|
2420
|
+
if prec == self._prec:
|
|
2421
|
+
try:
|
|
2422
|
+
return self._Iota
|
|
2423
|
+
except AttributeError:
|
|
2424
|
+
pass
|
|
2425
|
+
|
|
2426
|
+
self._pN = self._p ** prec
|
|
2427
|
+
self._R = Qp(self._p, prec=prec)
|
|
2428
|
+
|
|
2429
|
+
if prec > self._prec:
|
|
2430
|
+
verbose('self._prec = %s, prec = %s' % (self._prec, prec))
|
|
2431
|
+
Iotamod = self._compute_embedding_matrix(prec)
|
|
2432
|
+
self._Iotainv_lift = Iotamod.inverse().lift()
|
|
2433
|
+
self._Iota = Matrix(self._R, Iotamod)
|
|
2434
|
+
|
|
2435
|
+
self._prec = prec
|
|
2436
|
+
self._Iotainv = self._Mat_44(self._Iotainv_lift.apply_map(lambda x: x % self._pN))
|
|
2437
|
+
return self._Iota
|
|
2438
|
+
|
|
2439
|
+
def embed_quaternion(self, g, exact=False, prec=None):
|
|
2440
|
+
r"""
|
|
2441
|
+
Embed the quaternion element ``g`` into a matrix algebra.
|
|
2442
|
+
|
|
2443
|
+
INPUT:
|
|
2444
|
+
|
|
2445
|
+
- ``g`` -- a column vector of size `4` whose entries represent a
|
|
2446
|
+
quaternion in our basis
|
|
2447
|
+
|
|
2448
|
+
- ``exact`` -- boolean (default: ``False``); if True, tries to embed
|
|
2449
|
+
``g`` into a matrix algebra over a number field. If ``False``,
|
|
2450
|
+
the target is the matrix algebra over `\QQ_p`.
|
|
2451
|
+
|
|
2452
|
+
OUTPUT:
|
|
2453
|
+
|
|
2454
|
+
A 2x2 matrix with coefficients in `\QQ_p` if ``exact`` is
|
|
2455
|
+
False, or a number field if ``exact`` is True.
|
|
2456
|
+
|
|
2457
|
+
EXAMPLES::
|
|
2458
|
+
|
|
2459
|
+
sage: X = BruhatTitsQuotient(7,2)
|
|
2460
|
+
sage: l = X.get_units_of_order()
|
|
2461
|
+
sage: len(l)
|
|
2462
|
+
12
|
|
2463
|
+
sage: l[3] # random
|
|
2464
|
+
[-1]
|
|
2465
|
+
[ 0]
|
|
2466
|
+
[ 1]
|
|
2467
|
+
[ 1]
|
|
2468
|
+
sage: u = X.embed_quaternion(l[3]); u # random
|
|
2469
|
+
[ O(7) 3 + O(7)]
|
|
2470
|
+
[2 + O(7) 6 + O(7)]
|
|
2471
|
+
sage: X._increase_precision(5)
|
|
2472
|
+
sage: v = X.embed_quaternion(l[3]); v # random
|
|
2473
|
+
[ 7 + 3*7^2 + 7^3 + 4*7^4 + O(7^6) 3 + 7 + 3*7^2 + 7^3 + 4*7^4 + O(7^6)]
|
|
2474
|
+
[ 2 + 7 + 3*7^2 + 7^3 + 4*7^4 + O(7^6) 6 + 5*7 + 3*7^2 + 5*7^3 + 2*7^4 + 6*7^5 + O(7^6)]
|
|
2475
|
+
sage: u == v
|
|
2476
|
+
True
|
|
2477
|
+
"""
|
|
2478
|
+
if exact:
|
|
2479
|
+
return Matrix(self.get_splitting_field(), 2, 2,
|
|
2480
|
+
(self.get_embedding_matrix(exact=True) * g).list())
|
|
2481
|
+
else:
|
|
2482
|
+
A = self.get_embedding_matrix(prec=prec) * g
|
|
2483
|
+
return Matrix(self._R, 2, 2, A.list())
|
|
2484
|
+
|
|
2485
|
+
embed = embed_quaternion
|
|
2486
|
+
|
|
2487
|
+
def get_embedding(self, prec=None):
|
|
2488
|
+
r"""
|
|
2489
|
+
Return a function which embeds quaternions into a matrix
|
|
2490
|
+
algebra.
|
|
2491
|
+
|
|
2492
|
+
EXAMPLES::
|
|
2493
|
+
|
|
2494
|
+
sage: X = BruhatTitsQuotient(5,3)
|
|
2495
|
+
sage: f = X.get_embedding(prec = 4)
|
|
2496
|
+
sage: b = Matrix(ZZ,4,1,[1,2,3,4])
|
|
2497
|
+
sage: f(b)
|
|
2498
|
+
[2 + 3*5 + 2*5^2 + 4*5^3 + O(5^4) 3 + 2*5^2 + 4*5^3 + O(5^4)]
|
|
2499
|
+
[ 5 + 5^2 + 3*5^3 + O(5^4) 4 + 5 + 2*5^2 + O(5^4)]
|
|
2500
|
+
"""
|
|
2501
|
+
A = self.get_embedding_matrix(prec=prec)
|
|
2502
|
+
return lambda g: Matrix(self._R, 2, 2, (A * g).list())
|
|
2503
|
+
|
|
2504
|
+
def get_edge_stabilizers(self):
|
|
2505
|
+
r"""
|
|
2506
|
+
Compute the stabilizers in the arithmetic group of all
|
|
2507
|
+
edges in the Bruhat-Tits tree within a fundamental domain for
|
|
2508
|
+
the quotient graph. The stabilizers of an edge and its
|
|
2509
|
+
opposite are equal, and so we only store half the data.
|
|
2510
|
+
|
|
2511
|
+
OUTPUT:
|
|
2512
|
+
|
|
2513
|
+
A list of lists encoding edge stabilizers. It contains one
|
|
2514
|
+
entry for each edge. Each entry is a list of data
|
|
2515
|
+
corresponding to the group elements in the stabilizer of the
|
|
2516
|
+
edge. The data consists of: (0) a column matrix representing
|
|
2517
|
+
a quaternion, (1) the power of `p` that one needs to divide
|
|
2518
|
+
by in order to obtain a quaternion of norm 1, and hence an
|
|
2519
|
+
element of the arithmetic group `\Gamma`, (2) a boolean that
|
|
2520
|
+
is only used to compute spaces of modular forms.
|
|
2521
|
+
|
|
2522
|
+
EXAMPLES::
|
|
2523
|
+
|
|
2524
|
+
sage: X = BruhatTitsQuotient(3,2)
|
|
2525
|
+
sage: s = X.get_edge_stabilizers()
|
|
2526
|
+
sage: len(s) == X.get_num_ordered_edges()/2
|
|
2527
|
+
True
|
|
2528
|
+
sage: len(s[0])
|
|
2529
|
+
3
|
|
2530
|
+
"""
|
|
2531
|
+
try:
|
|
2532
|
+
return self._edge_stabs
|
|
2533
|
+
except AttributeError:
|
|
2534
|
+
self._edge_stabs = [self._stabilizer(e.rep, as_edge=True)
|
|
2535
|
+
for e in self.get_edge_list()]
|
|
2536
|
+
return self._edge_stabs
|
|
2537
|
+
|
|
2538
|
+
def get_stabilizers(self):
|
|
2539
|
+
r"""
|
|
2540
|
+
Compute the stabilizers in the arithmetic group of all
|
|
2541
|
+
edges in the Bruhat-Tits tree within a fundamental domain for
|
|
2542
|
+
the quotient graph. This is similar to get_edge_stabilizers, except
|
|
2543
|
+
that here we also store the stabilizers of the opposites.
|
|
2544
|
+
|
|
2545
|
+
OUTPUT:
|
|
2546
|
+
|
|
2547
|
+
A list of lists encoding edge stabilizers. It contains one
|
|
2548
|
+
entry for each edge. Each entry is a list of data
|
|
2549
|
+
corresponding to the group elements in the stabilizer of the
|
|
2550
|
+
edge. The data consists of: (0) a column matrix representing
|
|
2551
|
+
a quaternion, (1) the power of `p` that one needs to divide
|
|
2552
|
+
by in order to obtain a quaternion of norm 1, and hence an
|
|
2553
|
+
element of the arithmetic group `\Gamma`, (2) a boolean that
|
|
2554
|
+
is only used to compute spaces of modular forms.
|
|
2555
|
+
|
|
2556
|
+
EXAMPLES::
|
|
2557
|
+
|
|
2558
|
+
sage: X = BruhatTitsQuotient(3,5)
|
|
2559
|
+
sage: s = X.get_stabilizers()
|
|
2560
|
+
sage: len(s) == X.get_num_ordered_edges()
|
|
2561
|
+
True
|
|
2562
|
+
sage: gamma = X.embed_quaternion(s[1][0][0][0],prec = 20)
|
|
2563
|
+
sage: v = X.get_edge_list()[0].rep
|
|
2564
|
+
sage: X._BT.edge(gamma*v) == v
|
|
2565
|
+
True
|
|
2566
|
+
"""
|
|
2567
|
+
S = self.get_edge_stabilizers()
|
|
2568
|
+
return S + S
|
|
2569
|
+
|
|
2570
|
+
def get_vertex_stabs(self):
|
|
2571
|
+
r"""
|
|
2572
|
+
This function computes the stabilizers in the arithmetic
|
|
2573
|
+
group of all vertices in the Bruhat-Tits tree within a
|
|
2574
|
+
fundamental domain for the quotient graph.
|
|
2575
|
+
|
|
2576
|
+
OUTPUT:
|
|
2577
|
+
|
|
2578
|
+
A list of vertex stabilizers. Each vertex stabilizer is a
|
|
2579
|
+
finite cyclic subgroup, so we return generators for these
|
|
2580
|
+
subgroups.
|
|
2581
|
+
|
|
2582
|
+
EXAMPLES::
|
|
2583
|
+
|
|
2584
|
+
sage: X = BruhatTitsQuotient(13,2)
|
|
2585
|
+
sage: S = X.get_vertex_stabs()
|
|
2586
|
+
sage: gamma = X.embed_quaternion(S[0][0][0],prec = 20)
|
|
2587
|
+
sage: v = X.get_vertex_list()[0].rep
|
|
2588
|
+
sage: X._BT.vertex(gamma*v) == v
|
|
2589
|
+
True
|
|
2590
|
+
"""
|
|
2591
|
+
try:
|
|
2592
|
+
return self._vertex_stabs
|
|
2593
|
+
except AttributeError:
|
|
2594
|
+
self._vertex_stabs = [self._stabilizer(v.rep, as_edge=False)
|
|
2595
|
+
for v in self.get_vertex_list()]
|
|
2596
|
+
return self._vertex_stabs
|
|
2597
|
+
|
|
2598
|
+
def get_quaternion_algebra(self):
|
|
2599
|
+
r"""
|
|
2600
|
+
Return the underlying quaternion algebra.
|
|
2601
|
+
|
|
2602
|
+
OUTPUT: the underlying definite quaternion algebra
|
|
2603
|
+
|
|
2604
|
+
EXAMPLES::
|
|
2605
|
+
|
|
2606
|
+
sage: X = BruhatTitsQuotient(5,7)
|
|
2607
|
+
sage: X.get_quaternion_algebra()
|
|
2608
|
+
Quaternion Algebra (-1, -7) with base ring Rational Field
|
|
2609
|
+
"""
|
|
2610
|
+
try:
|
|
2611
|
+
return self._A
|
|
2612
|
+
except AttributeError:
|
|
2613
|
+
pass
|
|
2614
|
+
self._init_order()
|
|
2615
|
+
return self._A
|
|
2616
|
+
|
|
2617
|
+
def get_eichler_order(self, magma=False, force_computation=False):
|
|
2618
|
+
r"""
|
|
2619
|
+
Return the underlying Eichler order of level `N^+`.
|
|
2620
|
+
|
|
2621
|
+
OUTPUT: an Eichler order
|
|
2622
|
+
|
|
2623
|
+
EXAMPLES::
|
|
2624
|
+
|
|
2625
|
+
sage: X = BruhatTitsQuotient(5,7)
|
|
2626
|
+
sage: X.get_eichler_order()
|
|
2627
|
+
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)
|
|
2628
|
+
"""
|
|
2629
|
+
if magma:
|
|
2630
|
+
if not force_computation:
|
|
2631
|
+
try:
|
|
2632
|
+
return self._Omagma
|
|
2633
|
+
except AttributeError:
|
|
2634
|
+
pass
|
|
2635
|
+
self._init_order()
|
|
2636
|
+
return self._Omagma
|
|
2637
|
+
else:
|
|
2638
|
+
try:
|
|
2639
|
+
return self._O
|
|
2640
|
+
except AttributeError:
|
|
2641
|
+
pass
|
|
2642
|
+
self._init_order()
|
|
2643
|
+
return self._O
|
|
2644
|
+
|
|
2645
|
+
def get_maximal_order(self, magma=False, force_computation=False):
|
|
2646
|
+
r"""
|
|
2647
|
+
Return the underlying maximal order containing the
|
|
2648
|
+
Eichler order.
|
|
2649
|
+
|
|
2650
|
+
OUTPUT: a maximal order
|
|
2651
|
+
|
|
2652
|
+
EXAMPLES::
|
|
2653
|
+
|
|
2654
|
+
sage: X = BruhatTitsQuotient(5,7)
|
|
2655
|
+
sage: X.get_maximal_order()
|
|
2656
|
+
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)
|
|
2657
|
+
"""
|
|
2658
|
+
if magma:
|
|
2659
|
+
if not force_computation:
|
|
2660
|
+
try:
|
|
2661
|
+
return self._OMaxmagma
|
|
2662
|
+
except AttributeError:
|
|
2663
|
+
pass
|
|
2664
|
+
self._init_order()
|
|
2665
|
+
return self._OMaxmagma
|
|
2666
|
+
else:
|
|
2667
|
+
try:
|
|
2668
|
+
return self._OMax
|
|
2669
|
+
except AttributeError:
|
|
2670
|
+
pass
|
|
2671
|
+
self._init_order()
|
|
2672
|
+
return self._OMax
|
|
2673
|
+
|
|
2674
|
+
def get_splitting_field(self):
|
|
2675
|
+
r"""
|
|
2676
|
+
Return a quadratic field that splits the quaternion
|
|
2677
|
+
algebra attached to ``self``. Currently requires Magma.
|
|
2678
|
+
|
|
2679
|
+
EXAMPLES::
|
|
2680
|
+
|
|
2681
|
+
sage: X = BruhatTitsQuotient(5,11)
|
|
2682
|
+
sage: X.get_splitting_field()
|
|
2683
|
+
Traceback (most recent call last):
|
|
2684
|
+
...
|
|
2685
|
+
NotImplementedError: Sage does not know yet how to work with the kind of orders that you are trying to use. Try installing Magma first and set it up so that Sage can use it.
|
|
2686
|
+
|
|
2687
|
+
If we do have Magma installed, then it works::
|
|
2688
|
+
|
|
2689
|
+
sage: X = BruhatTitsQuotient(5,11,use_magma=True) # optional - magma
|
|
2690
|
+
sage: X.get_splitting_field() # optional - magma
|
|
2691
|
+
Number Field in a with defining polynomial x^2 + 11
|
|
2692
|
+
"""
|
|
2693
|
+
if not self._use_magma:
|
|
2694
|
+
raise NotImplementedError('Sage does not know yet how to work with the kind of orders that you are trying to use. Try installing Magma first and set it up so that Sage can use it.')
|
|
2695
|
+
try:
|
|
2696
|
+
return self._FF
|
|
2697
|
+
except AttributeError:
|
|
2698
|
+
pass
|
|
2699
|
+
self._compute_exact_splitting()
|
|
2700
|
+
return self._FF
|
|
2701
|
+
|
|
2702
|
+
def get_eichler_order_basis(self):
|
|
2703
|
+
r"""
|
|
2704
|
+
Return a basis for the global Eichler order.
|
|
2705
|
+
|
|
2706
|
+
OUTPUT: basis for the underlying Eichler order of level Nplus
|
|
2707
|
+
|
|
2708
|
+
EXAMPLES::
|
|
2709
|
+
|
|
2710
|
+
sage: X = BruhatTitsQuotient(7,11)
|
|
2711
|
+
sage: X.get_eichler_order_basis()
|
|
2712
|
+
[1/2 + 1/2*j, 1/2*i + 1/2*k, j, k]
|
|
2713
|
+
"""
|
|
2714
|
+
try:
|
|
2715
|
+
return self._B
|
|
2716
|
+
except AttributeError:
|
|
2717
|
+
pass
|
|
2718
|
+
self._init_order()
|
|
2719
|
+
return self._B
|
|
2720
|
+
|
|
2721
|
+
def get_eichler_order_quadform(self):
|
|
2722
|
+
r"""
|
|
2723
|
+
This function return the norm form for the underlying
|
|
2724
|
+
Eichler order of level ``Nplus``. Required for finding elements in
|
|
2725
|
+
the arithmetic subgroup Gamma.
|
|
2726
|
+
|
|
2727
|
+
OUTPUT: the norm form of the underlying Eichler order
|
|
2728
|
+
|
|
2729
|
+
EXAMPLES::
|
|
2730
|
+
|
|
2731
|
+
sage: X = BruhatTitsQuotient(7,11)
|
|
2732
|
+
sage: X.get_eichler_order_quadform()
|
|
2733
|
+
Quadratic form in 4 variables over Integer Ring with coefficients:
|
|
2734
|
+
[ 3 0 11 0 ]
|
|
2735
|
+
[ * 3 0 11 ]
|
|
2736
|
+
[ * * 11 0 ]
|
|
2737
|
+
[ * * * 11 ]
|
|
2738
|
+
"""
|
|
2739
|
+
try:
|
|
2740
|
+
return self._OQuadForm
|
|
2741
|
+
except AttributeError:
|
|
2742
|
+
pass
|
|
2743
|
+
self._init_order()
|
|
2744
|
+
return self._OQuadForm
|
|
2745
|
+
|
|
2746
|
+
def get_eichler_order_quadmatrix(self):
|
|
2747
|
+
r"""
|
|
2748
|
+
This function returns the matrix of the quadratic form of
|
|
2749
|
+
the underlying Eichler order in the fixed basis.
|
|
2750
|
+
|
|
2751
|
+
OUTPUT: a 4x4 integral matrix describing the norm form
|
|
2752
|
+
|
|
2753
|
+
EXAMPLES::
|
|
2754
|
+
|
|
2755
|
+
sage: X = BruhatTitsQuotient(7,11)
|
|
2756
|
+
sage: X.get_eichler_order_quadmatrix()
|
|
2757
|
+
[ 6 0 11 0]
|
|
2758
|
+
[ 0 6 0 11]
|
|
2759
|
+
[11 0 22 0]
|
|
2760
|
+
[ 0 11 0 22]
|
|
2761
|
+
"""
|
|
2762
|
+
try:
|
|
2763
|
+
return self._OM
|
|
2764
|
+
except AttributeError:
|
|
2765
|
+
pass
|
|
2766
|
+
self._init_order()
|
|
2767
|
+
return self._OM
|
|
2768
|
+
|
|
2769
|
+
@cached_method
|
|
2770
|
+
def get_units_of_order(self):
|
|
2771
|
+
r"""
|
|
2772
|
+
Return the units of the underlying Eichler
|
|
2773
|
+
`\ZZ`-order. This is a finite group since the order lives in a
|
|
2774
|
+
definite quaternion algebra over `\QQ`.
|
|
2775
|
+
|
|
2776
|
+
OUTPUT:
|
|
2777
|
+
|
|
2778
|
+
A list of elements of the global Eichler `\ZZ`-order of
|
|
2779
|
+
level `N^+`.
|
|
2780
|
+
|
|
2781
|
+
EXAMPLES::
|
|
2782
|
+
|
|
2783
|
+
sage: X = BruhatTitsQuotient(7,11)
|
|
2784
|
+
sage: X.get_units_of_order()
|
|
2785
|
+
[
|
|
2786
|
+
[ 0] [-2]
|
|
2787
|
+
[-2] [ 0]
|
|
2788
|
+
[ 0] [ 1]
|
|
2789
|
+
[ 1], [ 0]
|
|
2790
|
+
]
|
|
2791
|
+
"""
|
|
2792
|
+
OM = self.get_eichler_order_quadmatrix()
|
|
2793
|
+
v = pari('qfminim(%s,2,0, flag = 2)' % (OM.__pari__()))
|
|
2794
|
+
n_units = Integer(v[0].sage() / 2)
|
|
2795
|
+
v = pari('qfminim(%s,2,%s, flag = 2)' % ((OM.__pari__()), n_units))
|
|
2796
|
+
O_units = []
|
|
2797
|
+
for jj in range(n_units):
|
|
2798
|
+
vec = Matrix(ZZ, 4, 1, [v[2][ii, jj].sage() for ii in range(4)])
|
|
2799
|
+
O_units.append(vec)
|
|
2800
|
+
return O_units
|
|
2801
|
+
|
|
2802
|
+
@cached_method
|
|
2803
|
+
def _get_Up_data(self):
|
|
2804
|
+
r"""
|
|
2805
|
+
Return (compute if necessary) Up data.
|
|
2806
|
+
|
|
2807
|
+
The Up data is a vector of length `p`, and each entry consists
|
|
2808
|
+
of the corresponding data for the matrix `[p,a,0,1]` where a
|
|
2809
|
+
varies from 0 to `p-1`. The data is a tuple (acter,edge_images),
|
|
2810
|
+
with edge images being of type ``DoubleCosetReduction``.
|
|
2811
|
+
|
|
2812
|
+
EXAMPLES::
|
|
2813
|
+
|
|
2814
|
+
sage: X = BruhatTitsQuotient(3,7)
|
|
2815
|
+
sage: [o[0] for o in X._get_Up_data()]
|
|
2816
|
+
[
|
|
2817
|
+
[1/3 0] [-1/3 1/3] [-2/3 1/3]
|
|
2818
|
+
[ 0 1], [ 1 0], [ 1 0]
|
|
2819
|
+
]
|
|
2820
|
+
"""
|
|
2821
|
+
E = self.get_edge_list()
|
|
2822
|
+
vec_a = self._BT.subdivide([1], 1)
|
|
2823
|
+
return [[alpha.inverse(),
|
|
2824
|
+
[DoubleCosetReduction(self, e.rep * alpha) for e in E]
|
|
2825
|
+
+ [DoubleCosetReduction(self, e.opposite.rep * alpha)
|
|
2826
|
+
for e in E]]
|
|
2827
|
+
for alpha in vec_a]
|
|
2828
|
+
|
|
2829
|
+
@cached_method
|
|
2830
|
+
def _get_atkin_lehner_data(self, q):
|
|
2831
|
+
r"""
|
|
2832
|
+
Return (and compute if necessary) data to compute the
|
|
2833
|
+
Atkin-Lehner involution.
|
|
2834
|
+
|
|
2835
|
+
INPUT:
|
|
2836
|
+
|
|
2837
|
+
- ``q`` -- integer dividing p*Nminus*Nplus
|
|
2838
|
+
|
|
2839
|
+
EXAMPLES::
|
|
2840
|
+
|
|
2841
|
+
sage: X = BruhatTitsQuotient(3,5)
|
|
2842
|
+
sage: X._get_atkin_lehner_data(3)[0]
|
|
2843
|
+
[ 2]
|
|
2844
|
+
[ 4]
|
|
2845
|
+
[-3]
|
|
2846
|
+
[-2]
|
|
2847
|
+
"""
|
|
2848
|
+
E = self.get_edge_list()
|
|
2849
|
+
|
|
2850
|
+
nninc = -2
|
|
2851
|
+
V = []
|
|
2852
|
+
p = self._p
|
|
2853
|
+
while not V:
|
|
2854
|
+
nninc += 2
|
|
2855
|
+
V = [g for g in self._find_elements_in_order(q * self._p ** nninc)
|
|
2856
|
+
if prod([self._character(ZZ((v * Matrix(ZZ, 4, 1, g))[0, 0]))
|
|
2857
|
+
/ self._character(p ** (nninc // 2))
|
|
2858
|
+
for v in self.get_extra_embedding_matrices()]) == 1]
|
|
2859
|
+
|
|
2860
|
+
beta1 = Matrix(QQ, 4, 1, V[0])
|
|
2861
|
+
|
|
2862
|
+
success = False
|
|
2863
|
+
while not success:
|
|
2864
|
+
try:
|
|
2865
|
+
x = self.embed_quaternion(beta1)
|
|
2866
|
+
nn = x.determinant().valuation()
|
|
2867
|
+
T = [beta1,
|
|
2868
|
+
[DoubleCosetReduction(self, x.adjugate() * e.rep,
|
|
2869
|
+
extrapow=nn) for e in E]]
|
|
2870
|
+
success = True
|
|
2871
|
+
except (PrecisionError, NotImplementedError):
|
|
2872
|
+
self._increase_precision(10)
|
|
2873
|
+
return T
|
|
2874
|
+
|
|
2875
|
+
@cached_method
|
|
2876
|
+
def _get_hecke_data(self, l):
|
|
2877
|
+
r"""
|
|
2878
|
+
Return (and compute if necessary) data to compute the
|
|
2879
|
+
Hecke operator at a prime.
|
|
2880
|
+
|
|
2881
|
+
INPUT:
|
|
2882
|
+
|
|
2883
|
+
- ``l`` -- a prime l
|
|
2884
|
+
|
|
2885
|
+
EXAMPLES::
|
|
2886
|
+
|
|
2887
|
+
sage: X = BruhatTitsQuotient(3,17)
|
|
2888
|
+
sage: len(X._get_hecke_data(5))
|
|
2889
|
+
2
|
|
2890
|
+
"""
|
|
2891
|
+
E = self.get_edge_list()
|
|
2892
|
+
if (self.level() * self.Nplus()) % l == 0:
|
|
2893
|
+
Sset = []
|
|
2894
|
+
else:
|
|
2895
|
+
Sset = [self._p]
|
|
2896
|
+
BB = self._BB
|
|
2897
|
+
p = self._p
|
|
2898
|
+
T = []
|
|
2899
|
+
T0 = []
|
|
2900
|
+
V = []
|
|
2901
|
+
nninc = 0
|
|
2902
|
+
while not V:
|
|
2903
|
+
V = [g for g in self._find_elements_in_order(l * p ** nninc)
|
|
2904
|
+
if prod([self._character(ZZ((v * Matrix(ZZ, 4, 1, g))[0, 0]))
|
|
2905
|
+
/ self._character(p ** (nninc // 2))
|
|
2906
|
+
for v in self.get_extra_embedding_matrices()]) == 1]
|
|
2907
|
+
if not V:
|
|
2908
|
+
nninc += 2
|
|
2909
|
+
|
|
2910
|
+
alpha1 = V[0]
|
|
2911
|
+
alpha0 = self._conv(alpha1)
|
|
2912
|
+
|
|
2913
|
+
alpha = Matrix(QQ, 4, 1, alpha1)
|
|
2914
|
+
alphamat = self.embed_quaternion(alpha)
|
|
2915
|
+
letters = self.get_nontorsion_generators()
|
|
2916
|
+
letters += [g for g in self._find_elements_in_order(1)
|
|
2917
|
+
if prod([self._character(ZZ((v * Matrix(ZZ, 4, 1, g))[0, 0]))
|
|
2918
|
+
/ self._character(p ** (nninc // 2))
|
|
2919
|
+
for v in self.get_extra_embedding_matrices()]) == 1]
|
|
2920
|
+
|
|
2921
|
+
def enumerate_words(v, n=None):
|
|
2922
|
+
if n is None:
|
|
2923
|
+
n = []
|
|
2924
|
+
while True:
|
|
2925
|
+
add_new = True
|
|
2926
|
+
for j in range(len(n)):
|
|
2927
|
+
n[j] += 1
|
|
2928
|
+
if n[j] != len(v):
|
|
2929
|
+
add_new = False
|
|
2930
|
+
break
|
|
2931
|
+
n[j] = 0
|
|
2932
|
+
if add_new:
|
|
2933
|
+
n.append(0)
|
|
2934
|
+
yield [v[x] for x in n]
|
|
2935
|
+
|
|
2936
|
+
conv_list = [self._conv(x) for x in letters]
|
|
2937
|
+
for n_iters, wd in enumerate(enumerate_words(conv_list)):
|
|
2938
|
+
if len(T) == l + 1:
|
|
2939
|
+
break
|
|
2940
|
+
v = prod(wd)
|
|
2941
|
+
v0 = v * alpha0
|
|
2942
|
+
vinv = self.get_quaternion_algebra()(v0 ** (-1))
|
|
2943
|
+
new = True
|
|
2944
|
+
for tt in T0:
|
|
2945
|
+
r = vinv * tt
|
|
2946
|
+
r_in_order = BB * Matrix(QQ, 4, 1, r.coefficient_tuple())
|
|
2947
|
+
if all(a.is_S_integral(Sset) for a in r_in_order.list()):
|
|
2948
|
+
new = False
|
|
2949
|
+
break
|
|
2950
|
+
if new:
|
|
2951
|
+
v1 = BB * Matrix(QQ, 4, 1, v.coefficient_tuple())
|
|
2952
|
+
success = False
|
|
2953
|
+
while not success:
|
|
2954
|
+
try:
|
|
2955
|
+
x = self.embed_quaternion(v1, prec=max(self._prec, 40),
|
|
2956
|
+
exact=False) * alphamat
|
|
2957
|
+
nn = x.determinant().valuation()
|
|
2958
|
+
dcr = [DoubleCosetReduction(self, x.adjugate() * e.rep,
|
|
2959
|
+
extrapow=nn) for e in E]
|
|
2960
|
+
T.append([v1, dcr])
|
|
2961
|
+
success = True
|
|
2962
|
+
except (PrecisionError, NotImplementedError):
|
|
2963
|
+
self._increase_precision(10)
|
|
2964
|
+
alphamat = self.embed_quaternion(alpha, prec=max(self._prec, 40), exact=False)
|
|
2965
|
+
T0.append(v0)
|
|
2966
|
+
return T, alpha
|
|
2967
|
+
|
|
2968
|
+
def _find_equivalent_vertex(self, v0, V=None, valuation=None):
|
|
2969
|
+
r"""
|
|
2970
|
+
Find a vertex in ``V`` equivalent to ``v0``.
|
|
2971
|
+
|
|
2972
|
+
INPUT:
|
|
2973
|
+
|
|
2974
|
+
- ``v0`` -- a 2x2 matrix in `\ZZ_p` representing a
|
|
2975
|
+
vertex in the Bruhat-Tits tree
|
|
2976
|
+
|
|
2977
|
+
- ``V`` -- list (default: ``None``); if a list of Vertex is given,
|
|
2978
|
+
restrict the search to the vertices in ``V``. Otherwise
|
|
2979
|
+
use all the vertices in a fundamental domain.
|
|
2980
|
+
|
|
2981
|
+
- ``valuation`` -- integer (default: ``None``); the valuation
|
|
2982
|
+
of the determinant of ``v0``, if known (otherwise it is
|
|
2983
|
+
calculated)
|
|
2984
|
+
|
|
2985
|
+
OUTPUT:
|
|
2986
|
+
|
|
2987
|
+
A pair ``g``, ``v``, where ``v`` is a Vertex in ``V``
|
|
2988
|
+
equivalent to ``v0``, and ``g`` is such that `g\cdot v_0= v`.
|
|
2989
|
+
|
|
2990
|
+
EXAMPLES::
|
|
2991
|
+
|
|
2992
|
+
sage: X = BruhatTitsQuotient(3,7)
|
|
2993
|
+
sage: M = Matrix(ZZ,2,2,[1,3,2,7])
|
|
2994
|
+
sage: M.set_immutable()
|
|
2995
|
+
sage: X._find_equivalent_vertex(M)[-1] in X.get_vertex_list()
|
|
2996
|
+
True
|
|
2997
|
+
"""
|
|
2998
|
+
try:
|
|
2999
|
+
return self._cached_vertices[v0]
|
|
3000
|
+
except KeyError:
|
|
3001
|
+
pass
|
|
3002
|
+
if V is None:
|
|
3003
|
+
V = self.get_vertex_list()
|
|
3004
|
+
if valuation is None:
|
|
3005
|
+
valuation = v0.determinant().valuation(self._p)
|
|
3006
|
+
parity = valuation % 2
|
|
3007
|
+
for v in V:
|
|
3008
|
+
if v.parity != parity:
|
|
3009
|
+
continue
|
|
3010
|
+
g = self._are_equivalent(v0, v.rep, False, valuation + v.valuation)
|
|
3011
|
+
if g is not None:
|
|
3012
|
+
self._cached_vertices[v0] = (g, v)
|
|
3013
|
+
return g, v
|
|
3014
|
+
return 0, None
|
|
3015
|
+
|
|
3016
|
+
def _find_equivalent_edge(self, e0, E=None, valuation=None):
|
|
3017
|
+
r"""
|
|
3018
|
+
Find an edge in ``E`` equivalent to ``e0``.
|
|
3019
|
+
|
|
3020
|
+
INPUT:
|
|
3021
|
+
|
|
3022
|
+
- ``e0`` -- a 2x2 matrix in `\ZZ_p` representing an
|
|
3023
|
+
edge in the Bruhat-Tits tree
|
|
3024
|
+
|
|
3025
|
+
- ``E`` -- list (default: ``None``); if a list of Edge is given,
|
|
3026
|
+
restrict the search to the vertices in ``E``. Otherwise
|
|
3027
|
+
use all the edges in a fundamental domain.
|
|
3028
|
+
|
|
3029
|
+
- ``valuation`` -- integer (default: ``None``); the valuation
|
|
3030
|
+
of the determinant of ``e0``, if known (otherwise it is
|
|
3031
|
+
calculated).
|
|
3032
|
+
|
|
3033
|
+
OUTPUT:
|
|
3034
|
+
|
|
3035
|
+
A pair ``g``, ``e``, where ``e`` is an Edge in ``E``
|
|
3036
|
+
equivalent to ``e0``, and ``g`` is such that `g\cdot e_0= e`.
|
|
3037
|
+
|
|
3038
|
+
EXAMPLES::
|
|
3039
|
+
|
|
3040
|
+
sage: X = BruhatTitsQuotient(3,7)
|
|
3041
|
+
sage: M = Matrix(ZZ,2,2,[1,3,2,7])
|
|
3042
|
+
sage: M.set_immutable()
|
|
3043
|
+
sage: X._find_equivalent_edge(M)[-1] in X.get_edge_list()
|
|
3044
|
+
True
|
|
3045
|
+
"""
|
|
3046
|
+
try:
|
|
3047
|
+
return self._cached_edges[e0]
|
|
3048
|
+
except KeyError:
|
|
3049
|
+
pass
|
|
3050
|
+
if valuation is None:
|
|
3051
|
+
valuation = e0.determinant().valuation(self._p)
|
|
3052
|
+
parity = valuation % 2
|
|
3053
|
+
if E is None:
|
|
3054
|
+
if parity == 0:
|
|
3055
|
+
E = self._edge_list
|
|
3056
|
+
else:
|
|
3057
|
+
E = [e.opposite for e in self._edge_list]
|
|
3058
|
+
for e in E:
|
|
3059
|
+
if e.parity != parity:
|
|
3060
|
+
continue
|
|
3061
|
+
g = self._are_equivalent(e.rep, e0, True, valuation + e.valuation)
|
|
3062
|
+
if g is not None:
|
|
3063
|
+
self._cached_edges[e0] = (g, e)
|
|
3064
|
+
return g, e
|
|
3065
|
+
return 0, None
|
|
3066
|
+
|
|
3067
|
+
def fundom_rep(self, v1):
|
|
3068
|
+
r"""
|
|
3069
|
+
Find an equivalent vertex in the fundamental domain.
|
|
3070
|
+
|
|
3071
|
+
INPUT:
|
|
3072
|
+
|
|
3073
|
+
- ``v1`` -- a 2x2 matrix representing a normalized vertex
|
|
3074
|
+
|
|
3075
|
+
OUTPUT: a ``Vertex`` equivalent to ``v1``, in the fundamental domain
|
|
3076
|
+
|
|
3077
|
+
EXAMPLES::
|
|
3078
|
+
|
|
3079
|
+
sage: X = BruhatTitsQuotient(3,7)
|
|
3080
|
+
sage: M = Matrix(ZZ,2,2,[1,3,2,7])
|
|
3081
|
+
sage: M.set_immutable()
|
|
3082
|
+
sage: X.fundom_rep(M)
|
|
3083
|
+
Vertex of Bruhat-Tits tree for p = 3
|
|
3084
|
+
"""
|
|
3085
|
+
try:
|
|
3086
|
+
return self._cached_paths[v1]
|
|
3087
|
+
except KeyError:
|
|
3088
|
+
pass
|
|
3089
|
+
chain, v = self._BT.find_path(v1, self.get_vertex_dict())
|
|
3090
|
+
while chain:
|
|
3091
|
+
v0 = chain.pop()
|
|
3092
|
+
V = [e.target for e in v.leaving_edges]
|
|
3093
|
+
g, v = self._find_equivalent_vertex(v0, V)
|
|
3094
|
+
if v is None:
|
|
3095
|
+
print('Given vertex:', v0)
|
|
3096
|
+
print('Not equivalent to any existing vertex in the list:')
|
|
3097
|
+
if V is not None:
|
|
3098
|
+
print([ve.label for ve in V])
|
|
3099
|
+
assert 0 # what the hell is that ?
|
|
3100
|
+
self._cached_paths[v0] = v
|
|
3101
|
+
return v
|
|
3102
|
+
|
|
3103
|
+
def _find_lattice(self, v1, v2, as_edges, m):
|
|
3104
|
+
r"""
|
|
3105
|
+
Find the lattice attached to the pair ``v1``,``v2``.
|
|
3106
|
+
|
|
3107
|
+
INPUT:
|
|
3108
|
+
|
|
3109
|
+
- ``v1``, ``v2`` -- 2x2 matrices; they represent either a pair
|
|
3110
|
+
of normalized vertices or a pair of normalized edges
|
|
3111
|
+
|
|
3112
|
+
- ``as_edges`` -- boolean; if ``True``, the inputs will be
|
|
3113
|
+
considered as edges instead of vertices
|
|
3114
|
+
|
|
3115
|
+
- ``m`` -- integer; the valuation of the determinant of
|
|
3116
|
+
``v1``*``v2``
|
|
3117
|
+
|
|
3118
|
+
OUTPUT: a 4x4 integer matrix whose columns encode a lattice and a 4x4 integer matrix encoding a quadratic form
|
|
3119
|
+
|
|
3120
|
+
EXAMPLES::
|
|
3121
|
+
|
|
3122
|
+
sage: X = BruhatTitsQuotient(3,17)
|
|
3123
|
+
sage: X._find_lattice(Matrix(ZZ,2,2,[1,2,3,4]),Matrix(ZZ,2,2,[3,2,1,5]), True,0)
|
|
3124
|
+
(
|
|
3125
|
+
[1 0 0 0] [138 204 -35 102]
|
|
3126
|
+
[2 3 0 0] [204 306 -51 153]
|
|
3127
|
+
[0 0 1 0] [-35 -51 12 -34]
|
|
3128
|
+
[0 0 0 1], [102 153 -34 102]
|
|
3129
|
+
)
|
|
3130
|
+
"""
|
|
3131
|
+
if as_edges:
|
|
3132
|
+
X = self._Xe
|
|
3133
|
+
else:
|
|
3134
|
+
X = self._Xv
|
|
3135
|
+
if m + 1 > self._prec:
|
|
3136
|
+
self.get_embedding_matrix(prec=m + 1)
|
|
3137
|
+
v1adj = v1.adjugate()
|
|
3138
|
+
R = self._Mat_44
|
|
3139
|
+
vecM = [v2 * X[ii] * v1adj for ii in range(4)]
|
|
3140
|
+
M = self._Iotainv * column_matrix(4, 4, [m.list() for m in vecM])
|
|
3141
|
+
M = M.augment(R(self._pN)).transpose()
|
|
3142
|
+
E = M.echelon_form().submatrix(0, 0, 4, 4)
|
|
3143
|
+
Et = E.transpose()
|
|
3144
|
+
return Et, E * self.get_eichler_order_quadmatrix() * Et
|
|
3145
|
+
|
|
3146
|
+
def _stabilizer(self, e, as_edge=True):
|
|
3147
|
+
r"""
|
|
3148
|
+
Find the stabilizer of an edge or vertex.
|
|
3149
|
+
|
|
3150
|
+
INPUT:
|
|
3151
|
+
|
|
3152
|
+
- ``e`` -- a 2x2 matrix representing an edge or vertex
|
|
3153
|
+
|
|
3154
|
+
- ``as_edge`` -- boolean (default: ``True``); determines whether
|
|
3155
|
+
``e`` is treated as an edge or vertex
|
|
3156
|
+
|
|
3157
|
+
OUTPUT:
|
|
3158
|
+
|
|
3159
|
+
A list of data describing the (finite) stabilizing subgroup
|
|
3160
|
+
of ``e``.
|
|
3161
|
+
|
|
3162
|
+
EXAMPLES::
|
|
3163
|
+
|
|
3164
|
+
sage: X = BruhatTitsQuotient(3,7)
|
|
3165
|
+
sage: X._stabilizer(Matrix(ZZ,2,2,[3,8,2,9]))[0][2]
|
|
3166
|
+
False
|
|
3167
|
+
"""
|
|
3168
|
+
p = self._p
|
|
3169
|
+
m = e.determinant().valuation(p)
|
|
3170
|
+
twom = 2 * m
|
|
3171
|
+
E, A = self._find_lattice(e, e, as_edge, twom)
|
|
3172
|
+
n_units = len(self.get_units_of_order())
|
|
3173
|
+
# Using PARI to get the shortest vector in the lattice (via LLL)
|
|
3174
|
+
# We used to pass qfminim flag = 2
|
|
3175
|
+
mat = pari('qfminim(%s,,%s,flag = 2)' % (A.__pari__(), 2 * n_units))[2].sage().transpose()
|
|
3176
|
+
n_vecs = mat.nrows()
|
|
3177
|
+
stabs = []
|
|
3178
|
+
for jj in range(n_vecs):
|
|
3179
|
+
vect = mat.row(jj).row()
|
|
3180
|
+
vec = vect.transpose()
|
|
3181
|
+
nrd = Integer((vect * A * vec)[0, 0] / 2)
|
|
3182
|
+
if nrd == p ** twom:
|
|
3183
|
+
g, ans = self._nebentype_check(vec, twom, E, A, flag=0)
|
|
3184
|
+
if ans:
|
|
3185
|
+
x = self._conv(g.transpose())
|
|
3186
|
+
g.set_immutable()
|
|
3187
|
+
stabs.append([g, m, x != p ** m])
|
|
3188
|
+
if len(stabs) <= 1:
|
|
3189
|
+
return [[self.B_one(), 0, False]]
|
|
3190
|
+
else:
|
|
3191
|
+
return stabs
|
|
3192
|
+
|
|
3193
|
+
def _nebentype_check(self, vec, twom, E, A, flag=2):
|
|
3194
|
+
r"""
|
|
3195
|
+
Check if a quaternion maps into a subgroup of matrices
|
|
3196
|
+
determined by a nontrivial Dirichlet character (associated to
|
|
3197
|
+
self). If `N^+ = 1` then the condition is trivially satisfied.
|
|
3198
|
+
|
|
3199
|
+
INPUT:
|
|
3200
|
+
|
|
3201
|
+
- ``vec`` -- 4x1 integer matrix; it encodes the quaternion to
|
|
3202
|
+
test in the basis defined by the columns of E
|
|
3203
|
+
|
|
3204
|
+
- ``twom`` -- integer
|
|
3205
|
+
|
|
3206
|
+
- ``E`` -- 4x4 integer matrix; its columns should form a
|
|
3207
|
+
basis for an order in the quaternion algebra
|
|
3208
|
+
|
|
3209
|
+
- ``A`` -- 4x4 integer matrix; it encodes the quadratic form on the
|
|
3210
|
+
order defined by the columns of E
|
|
3211
|
+
|
|
3212
|
+
- ``flag`` -- integer (default: 0); Passed to Pari for finding
|
|
3213
|
+
minimal elements in a positive definite lattice
|
|
3214
|
+
|
|
3215
|
+
OUTPUT:
|
|
3216
|
+
|
|
3217
|
+
A pair consisting of a quaternion (represented by a 4x1 column
|
|
3218
|
+
matrix) and a boolean saying whether the quaternion is in the
|
|
3219
|
+
subgroup of `M_2(\Qp)` determined by the Dirichlet
|
|
3220
|
+
character. Note that if `N^+` is trivial then this function
|
|
3221
|
+
always outputs true.
|
|
3222
|
+
|
|
3223
|
+
EXAMPLES::
|
|
3224
|
+
|
|
3225
|
+
sage: f = DirichletGroup(6)[1]
|
|
3226
|
+
sage: X = BruhatTitsQuotient(3,2,1,f)
|
|
3227
|
+
sage: e = Matrix(ZZ,2,2,[1,2,5,7])
|
|
3228
|
+
sage: m = e.determinant().valuation(3)
|
|
3229
|
+
sage: twom = 2*m
|
|
3230
|
+
sage: E,A = X._find_lattice(e,e,True,twom)
|
|
3231
|
+
sage: X._nebentype_check(E**(-1)*Matrix(ZZ,4,1,[1,0,0,0]),twom,E,A)
|
|
3232
|
+
(
|
|
3233
|
+
[1]
|
|
3234
|
+
[0]
|
|
3235
|
+
[0]
|
|
3236
|
+
[0], True
|
|
3237
|
+
)
|
|
3238
|
+
"""
|
|
3239
|
+
if not self._use_magma or len(self._extra_level) == 0:
|
|
3240
|
+
return E * vec, True
|
|
3241
|
+
m = ZZ(twom / 2)
|
|
3242
|
+
mat = pari('qfminim(%s,,%s,flag = %s)' % (A.__pari__(), 1000, flag))[2].sage().transpose()
|
|
3243
|
+
n_vecs = mat.nrows()
|
|
3244
|
+
p = self._p
|
|
3245
|
+
pinv = Zmod(self._character.modulus())(p) ** -1
|
|
3246
|
+
for jj in range(n_vecs):
|
|
3247
|
+
vect = mat.row(jj).row()
|
|
3248
|
+
vec = vect.transpose()
|
|
3249
|
+
nrd = Integer((vect * A * vec)[0, 0] / 2)
|
|
3250
|
+
if nrd == p ** twom:
|
|
3251
|
+
g = E * vec
|
|
3252
|
+
if prod([self._character(ZZ(pinv ** m * (v * g)[0, 0]))
|
|
3253
|
+
for v in self.get_extra_embedding_matrices()]) == 1:
|
|
3254
|
+
return g, True
|
|
3255
|
+
return None, False
|
|
3256
|
+
|
|
3257
|
+
def _are_equivalent(self, v1, v2, as_edges=False, twom=None,
|
|
3258
|
+
check_parity=False):
|
|
3259
|
+
r"""
|
|
3260
|
+
Determine whether two vertices (or edges) of the
|
|
3261
|
+
Bruhat-Tits tree are equivalent under the arithmetic group in
|
|
3262
|
+
question. The computation boils down to an application of the
|
|
3263
|
+
LLL short-vector algorithm to a particular lattice; for
|
|
3264
|
+
details see [FM2014]_.
|
|
3265
|
+
|
|
3266
|
+
INPUT:
|
|
3267
|
+
|
|
3268
|
+
- ``v1``, ``v2`` -- two 2x2 integral matrices representing
|
|
3269
|
+
either vertices or edges
|
|
3270
|
+
|
|
3271
|
+
- ``as_edges`` -- boolean (default: ``False``); whether the
|
|
3272
|
+
matrices should be interpreted as edges (if ``True``), or as
|
|
3273
|
+
vertices (if ``False``)
|
|
3274
|
+
|
|
3275
|
+
- ``twom`` -- integer (default: ``None``); if specified,
|
|
3276
|
+
indicates the valuation of the determinant of ``v1``
|
|
3277
|
+
`\times` ``v2``.
|
|
3278
|
+
|
|
3279
|
+
OUTPUT:
|
|
3280
|
+
|
|
3281
|
+
If the objects are equivalent, returns an element of
|
|
3282
|
+
the arithmetic group `\Gamma` that takes ``v1`` to ``v2``.
|
|
3283
|
+
Otherwise returns ``False``.
|
|
3284
|
+
|
|
3285
|
+
EXAMPLES::
|
|
3286
|
+
|
|
3287
|
+
sage: X = BruhatTitsQuotient(7,5)
|
|
3288
|
+
sage: M1 = Matrix(ZZ,2,2,[88,3,1,1])
|
|
3289
|
+
sage: M1.set_immutable()
|
|
3290
|
+
sage: X._are_equivalent(M1,M1) == False
|
|
3291
|
+
False
|
|
3292
|
+
sage: M2 = Matrix(ZZ,2,2,[1,2,8,1]); M2.set_immutable()
|
|
3293
|
+
sage: X._are_equivalent(M1,M2, as_edges=True)
|
|
3294
|
+
sage: X._are_equivalent(M1,M2) == False
|
|
3295
|
+
False
|
|
3296
|
+
"""
|
|
3297
|
+
try:
|
|
3298
|
+
return self._cached_equivalent[(v1, v2, as_edges)]
|
|
3299
|
+
except KeyError:
|
|
3300
|
+
pass
|
|
3301
|
+
p = self._p
|
|
3302
|
+
if twom is None:
|
|
3303
|
+
twom = v1.determinant().valuation(p) + v2.determinant().valuation(p)
|
|
3304
|
+
if check_parity:
|
|
3305
|
+
if twom % 2:
|
|
3306
|
+
self._cached_equivalent[(v1, v2, as_edges)] = None
|
|
3307
|
+
return None
|
|
3308
|
+
E, A = self._find_lattice(v1, v2, as_edges, twom)
|
|
3309
|
+
# Using PARI to get the shortest vector in the lattice (via LLL)
|
|
3310
|
+
vec = pari('qfminim(%s,,1,flag = 2)' % (A.__pari__()))[2].sage()
|
|
3311
|
+
|
|
3312
|
+
vect = vec.transpose()
|
|
3313
|
+
nrd = Integer((vect * A * vec)[0, 0] / 2)
|
|
3314
|
+
if nrd == p ** twom:
|
|
3315
|
+
g, ans = self._nebentype_check(vec, twom, E, A)
|
|
3316
|
+
if ans:
|
|
3317
|
+
m = Integer(twom / 2)
|
|
3318
|
+
g.set_immutable()
|
|
3319
|
+
self._cached_equivalent[(v1, v2, as_edges)] = (g, m)
|
|
3320
|
+
return (g, m)
|
|
3321
|
+
self._cached_equivalent[(v1, v2, as_edges)] = None
|
|
3322
|
+
return None
|
|
3323
|
+
|
|
3324
|
+
def _compute_exact_splitting(self):
|
|
3325
|
+
r"""
|
|
3326
|
+
Use Magma to calculate a splitting of the order into
|
|
3327
|
+
the Matrix algebra with coefficients in an appropriate
|
|
3328
|
+
number field.
|
|
3329
|
+
|
|
3330
|
+
TESTS::
|
|
3331
|
+
|
|
3332
|
+
sage: X = BruhatTitsQuotient(3,23,use_magma=True) # optional - magma
|
|
3333
|
+
sage: X._compute_exact_splitting() # optional - magma
|
|
3334
|
+
"""
|
|
3335
|
+
# A = self.get_quaternion_algebra()
|
|
3336
|
+
R = self.get_maximal_order(magma=True)
|
|
3337
|
+
f = R.MatrixRepresentation()
|
|
3338
|
+
self._FF = NumberField(f.Codomain().BaseRing().DefiningPolynomial().sage(), 'a')
|
|
3339
|
+
allmats = []
|
|
3340
|
+
verbose('Calling magma, compute exact splitting')
|
|
3341
|
+
for kk in range(4):
|
|
3342
|
+
xseq = self._magma('%s(%s)' % (f.name(), R.gen(kk + 1).name())).ElementToSequence()
|
|
3343
|
+
allmats.append(Matrix(self._FF, 2, 2, [self._FF([QQ(xseq[ii + 1][jj + 1]) for jj in range(2)]) for ii in range(4)]))
|
|
3344
|
+
self._Iota_exact = Matrix(self._FF, 4, 4, [self._FF(allmats[kk][ii, jj]) for ii in range(2) for jj in range(2) for kk in range(4)])
|
|
3345
|
+
|
|
3346
|
+
def _init_order(self):
|
|
3347
|
+
r"""
|
|
3348
|
+
Initialize the order of the quaternion algebra. Here we
|
|
3349
|
+
possibly use Magma to split it.
|
|
3350
|
+
|
|
3351
|
+
EXAMPLES::
|
|
3352
|
+
|
|
3353
|
+
sage: X = BruhatTitsQuotient(3,23)
|
|
3354
|
+
sage: X._init_order()
|
|
3355
|
+
"""
|
|
3356
|
+
if self._order_is_initialized:
|
|
3357
|
+
return
|
|
3358
|
+
if self._use_magma:
|
|
3359
|
+
verbose('Calling magma, init_order')
|
|
3360
|
+
A = self._magma.QuaternionAlgebra(self._Nminus)
|
|
3361
|
+
g = A.gens()
|
|
3362
|
+
# We store the order because we need to split it
|
|
3363
|
+
OMaxmagma = A.QuaternionOrder(1)
|
|
3364
|
+
Omagma = OMaxmagma.Order(self._Nplus)
|
|
3365
|
+
OBasis = Omagma.Basis()
|
|
3366
|
+
self._A = QuaternionAlgebra((g[0] ** 2).sage(), (g[1] ** 2).sage())
|
|
3367
|
+
i, j, k = self._A.gens()
|
|
3368
|
+
v = [1] + list(self._A.gens())
|
|
3369
|
+
self._B = [self._A(sum([OBasis[tt + 1][rr + 1].sage() * v[rr]
|
|
3370
|
+
for rr in range(4)])) for tt in range(4)]
|
|
3371
|
+
self._O = self._A.quaternion_order(self._B)
|
|
3372
|
+
self._Omagma = Omagma
|
|
3373
|
+
self._OMaxmagma = OMaxmagma
|
|
3374
|
+
else:
|
|
3375
|
+
# Note that we can't work with non-maximal orders in sage
|
|
3376
|
+
assert self._Nplus == 1
|
|
3377
|
+
self._A = QuaternionAlgebra(self._Nminus)
|
|
3378
|
+
v = [1] + list(self._A.gens())
|
|
3379
|
+
self._O = self._A.maximal_order()
|
|
3380
|
+
self._OMax = self._O
|
|
3381
|
+
OBasis = self._O.basis()
|
|
3382
|
+
self._B = [self._A(OBasis[tt]) for tt in range(4)]
|
|
3383
|
+
|
|
3384
|
+
self._OQuadForm = QuadraticForm(self._Mat_44([(self._B[ii] * self._B[jj].conjugate()).reduced_trace() for ii in range(4) for jj in range(4)]))
|
|
3385
|
+
self._OM = self._OQuadForm.matrix()
|
|
3386
|
+
self._BB = Matrix(QQ, 4, 4, [[self._B[ii][jj] for ii in range(4)]
|
|
3387
|
+
for jj in range(4)]).inverse()
|
|
3388
|
+
self._order_is_initialized = True
|
|
3389
|
+
return
|
|
3390
|
+
|
|
3391
|
+
def B_one(self):
|
|
3392
|
+
r"""
|
|
3393
|
+
Return the coordinates of `1` in the basis for the
|
|
3394
|
+
quaternion order.
|
|
3395
|
+
|
|
3396
|
+
EXAMPLES::
|
|
3397
|
+
|
|
3398
|
+
sage: X = BruhatTitsQuotient(7,11)
|
|
3399
|
+
sage: v,pow = X.B_one()
|
|
3400
|
+
sage: X._conv(v) == 1
|
|
3401
|
+
True
|
|
3402
|
+
"""
|
|
3403
|
+
try:
|
|
3404
|
+
return self._B_one
|
|
3405
|
+
except AttributeError:
|
|
3406
|
+
O = self.get_eichler_order_basis()
|
|
3407
|
+
self._B_one = (Matrix(ZZ, 4, 1, Matrix(QQ, 4, 4, [list(x) for x in O]).transpose().inverse().column(0).list()), 0)
|
|
3408
|
+
return self._B_one
|
|
3409
|
+
|
|
3410
|
+
def _conv(self, v):
|
|
3411
|
+
r"""
|
|
3412
|
+
Return a quaternion having coordinates in the fixed
|
|
3413
|
+
basis for the order given by ``v``.
|
|
3414
|
+
|
|
3415
|
+
OUTPUT: a quaternion
|
|
3416
|
+
|
|
3417
|
+
EXAMPLES::
|
|
3418
|
+
|
|
3419
|
+
sage: X = BruhatTitsQuotient(5,7)
|
|
3420
|
+
sage: A = X.get_quaternion_algebra()
|
|
3421
|
+
sage: i,j,k = A.gens()
|
|
3422
|
+
sage: B = X.get_eichler_order_basis()
|
|
3423
|
+
sage: X._conv([1,2,3,4]) == B[0]+2*B[1]+3*B[2]+4*B[3]
|
|
3424
|
+
True
|
|
3425
|
+
"""
|
|
3426
|
+
if hasattr(v, "list"):
|
|
3427
|
+
v = v.list()
|
|
3428
|
+
B = self.get_eichler_order_basis()
|
|
3429
|
+
return sum([v[i] * B[i] for i in range(4)])
|
|
3430
|
+
|
|
3431
|
+
@cached_method
|
|
3432
|
+
def _find_elements_in_order(self, norm, trace=None, primitive=False):
|
|
3433
|
+
r"""
|
|
3434
|
+
Return elements in the order of the quaternion algebra
|
|
3435
|
+
of specified reduced norm. One may optionally choose to
|
|
3436
|
+
specify the reduced trace.
|
|
3437
|
+
|
|
3438
|
+
INPUT:
|
|
3439
|
+
|
|
3440
|
+
- ``norm`` -- integer; the required reduced norm
|
|
3441
|
+
|
|
3442
|
+
- ``trace`` -- integer (default: ``None``); if specified, returns
|
|
3443
|
+
elements only reduced trace ``trace``
|
|
3444
|
+
|
|
3445
|
+
- ``primitive`` -- boolean (default: ``False``); if ``True``, return only
|
|
3446
|
+
elements that cannot be divided by `p`
|
|
3447
|
+
|
|
3448
|
+
EXAMPLES::
|
|
3449
|
+
|
|
3450
|
+
sage: X = BruhatTitsQuotient(5,7)
|
|
3451
|
+
sage: X._find_elements_in_order(23)
|
|
3452
|
+
[[2, 9, -1, -5], [0, 8, 0, -5], [-2, 9, 1, -5], [6, 7, -3, -4], [2, 5, -1, -4], [0, 6, -1, -4], [0, 8, -1, -4], [2, 9, -1, -4], [-2, 5, 1, -4], [0, 6, 1, -4], [0, 8, 1, -4], [-2, 9, 1, -4], [-6, 7, 3, -4], [7, 6, -4, -3], [7, 6, -3, -3], [6, 7, -3, -3], [0, 8, 0, -3], [-7, 6, 3, -3], [-6, 7, 3, -3], [-7, 6, 4, -3], [0, 1, -1, -2], [0, 6, -1, -2], [0, 1, 1, -2], [0, 6, 1, -2], [9, 2, -5, -1], [6, 0, -4, -1], [8, 0, -4, -1], [5, 2, -4, -1], [9, 2, -4, -1], [1, 0, -2, -1], [6, 0, -2, -1], [0, -1, -1, -1], [-1, 0, -1, -1], [5, 2, -1, -1], [2, 5, -1, -1], [0, -1, 1, -1], [1, 0, 1, -1], [-5, 2, 1, -1], [-2, 5, 1, -1], [-6, 0, 2, -1], [-1, 0, 2, -1], [-8, 0, 4, -1], [-6, 0, 4, -1], [-9, 2, 4, -1], [-5, 2, 4, -1], [-9, 2, 5, -1], [8, 0, -5, 0], [8, 0, -3, 0]]
|
|
3453
|
+
sage: list(X._find_elements_in_order(23,1))
|
|
3454
|
+
[[1, 0, -2, -1], [1, 0, 1, -1]]
|
|
3455
|
+
"""
|
|
3456
|
+
OQuadForm = self.get_eichler_order_quadform()
|
|
3457
|
+
if norm > 10 ** 3:
|
|
3458
|
+
verbose('Warning: norm (= %s) is quite large, this may take some time!' % norm)
|
|
3459
|
+
V = OQuadForm.vectors_by_length(norm)[norm]
|
|
3460
|
+
W = V if not primitive else (v for v in V
|
|
3461
|
+
if any(vi % self._p for vi in v))
|
|
3462
|
+
return W if trace is None else (v for v in W
|
|
3463
|
+
if self._conv(v).reduced_trace() == trace)
|
|
3464
|
+
|
|
3465
|
+
def _compute_quotient(self, check=True):
|
|
3466
|
+
r"""
|
|
3467
|
+
Compute the quotient graph.
|
|
3468
|
+
|
|
3469
|
+
INPUT:
|
|
3470
|
+
|
|
3471
|
+
- ``check`` -- boolean (default: ``True``)
|
|
3472
|
+
|
|
3473
|
+
EXAMPLES::
|
|
3474
|
+
|
|
3475
|
+
sage: X = BruhatTitsQuotient(11,2)
|
|
3476
|
+
sage: X.get_graph() # indirect doctest
|
|
3477
|
+
Multi-graph on 2 vertices
|
|
3478
|
+
|
|
3479
|
+
sage: X = BruhatTitsQuotient(17,19)
|
|
3480
|
+
sage: X.get_graph() # indirect doctest
|
|
3481
|
+
Multi-graph on 4 vertices
|
|
3482
|
+
|
|
3483
|
+
The following examples require magma::
|
|
3484
|
+
|
|
3485
|
+
sage: X = BruhatTitsQuotient(5,7,12) # optional - magma
|
|
3486
|
+
sage: X.get_graph() # optional - magma
|
|
3487
|
+
Multi-graph on 24 vertices
|
|
3488
|
+
sage: len(X._edge_list) # optional - magma
|
|
3489
|
+
72
|
|
3490
|
+
|
|
3491
|
+
sage: X = BruhatTitsQuotient(2,3,5) # optional - magma
|
|
3492
|
+
sage: X.get_graph() # optional - magma
|
|
3493
|
+
Multi-graph on 4 vertices
|
|
3494
|
+
|
|
3495
|
+
sage: X = BruhatTitsQuotient(2,3,35) # optional - magma
|
|
3496
|
+
sage: X.get_graph() # optional - magma
|
|
3497
|
+
Multi-graph on 16 vertices
|
|
3498
|
+
|
|
3499
|
+
sage: X = BruhatTitsQuotient(53,11,2) # optional - magma
|
|
3500
|
+
sage: X.get_graph() # optional - magma
|
|
3501
|
+
Multi-graph on 6 vertices
|
|
3502
|
+
|
|
3503
|
+
sage: X = BruhatTitsQuotient(2,13,9) # optional - magma
|
|
3504
|
+
sage: X.get_graph() # optional - magma
|
|
3505
|
+
Multi-graph on 24 vertices
|
|
3506
|
+
|
|
3507
|
+
AUTHORS:
|
|
3508
|
+
|
|
3509
|
+
- Cameron Franc (2012-02-20)
|
|
3510
|
+
- Marc Masdeu
|
|
3511
|
+
"""
|
|
3512
|
+
nontorsion_generators = set()
|
|
3513
|
+
genus = self.genus()
|
|
3514
|
+
num_verts = 0
|
|
3515
|
+
num_edges = 0
|
|
3516
|
+
self.get_embedding_matrix(prec=3)
|
|
3517
|
+
p = self._p
|
|
3518
|
+
v0 = Vertex(p, num_verts, self._Mat_22([1, 0, 0, 1]),
|
|
3519
|
+
determinant=1, valuation=0)
|
|
3520
|
+
V = deque([v0])
|
|
3521
|
+
S = Graph(0, multiedges=True, weighted=True) # noqa:F821
|
|
3522
|
+
Sfun = Graph(0) # noqa:F821
|
|
3523
|
+
edge_list = []
|
|
3524
|
+
vertex_list = [v0]
|
|
3525
|
+
num_edges = 0
|
|
3526
|
+
num_verts += 1
|
|
3527
|
+
# total_verts = self.get_num_verts()
|
|
3528
|
+
# total_edges = genus + total_verts -1
|
|
3529
|
+
while len(V):
|
|
3530
|
+
v = V.popleft()
|
|
3531
|
+
E = self._BT.leaving_edges(v.rep)
|
|
3532
|
+
|
|
3533
|
+
verbose('V = %s, E = %s, G = %s (target = %s), lenV = %s' % (num_verts, num_edges, 1 + num_edges - num_verts, genus, len(V)))
|
|
3534
|
+
for e in E:
|
|
3535
|
+
edge_det = e.determinant()
|
|
3536
|
+
edge_valuation = edge_det.valuation(p)
|
|
3537
|
+
|
|
3538
|
+
g, e1 = self._find_equivalent_edge(e, v.leaving_edges,
|
|
3539
|
+
valuation=edge_valuation)
|
|
3540
|
+
|
|
3541
|
+
if e1 is not None: # The edge is old. We just update the links
|
|
3542
|
+
e1.links.append(g)
|
|
3543
|
+
target = self._BT.target(e)
|
|
3544
|
+
if e1.parity == 0:
|
|
3545
|
+
Sfun.add_edge(v.rep, target, label=e1.label)
|
|
3546
|
+
else:
|
|
3547
|
+
Sfun.add_edge(v.rep, target, label=e1.opposite.label)
|
|
3548
|
+
|
|
3549
|
+
Sfun.set_vertex(target, e1.target)
|
|
3550
|
+
else: # The edge is new.
|
|
3551
|
+
target = self._BT.target(e)
|
|
3552
|
+
target.set_immutable()
|
|
3553
|
+
new_det = target.determinant()
|
|
3554
|
+
new_valuation = new_det.valuation(p)
|
|
3555
|
+
# new_parity = new_valuation % 2
|
|
3556
|
+
g1, v1 = self._find_equivalent_vertex(target, V, valuation=new_valuation)
|
|
3557
|
+
if v1 is None:
|
|
3558
|
+
# The vertex is also new
|
|
3559
|
+
v1 = Vertex(p, num_verts, target, determinant=new_det,
|
|
3560
|
+
valuation=new_valuation)
|
|
3561
|
+
vertex_list.append(v1)
|
|
3562
|
+
num_verts += 1
|
|
3563
|
+
# Add the vertex to the list of pending vertices
|
|
3564
|
+
V.append(v1)
|
|
3565
|
+
else:
|
|
3566
|
+
nontorsion_generators.add(g1[0])
|
|
3567
|
+
|
|
3568
|
+
# Add the edge to the list
|
|
3569
|
+
new_e = Edge(p, num_edges, e, v, v1, determinant=edge_det,
|
|
3570
|
+
valuation=edge_valuation)
|
|
3571
|
+
new_e.links.append(self.B_one())
|
|
3572
|
+
Sfun.add_edge(v.rep, target, label=num_edges)
|
|
3573
|
+
Sfun.set_vertex(target, v1)
|
|
3574
|
+
|
|
3575
|
+
# Add the edge to the graph
|
|
3576
|
+
S.add_edge(v.rep, v1.rep, num_edges)
|
|
3577
|
+
S.set_vertex(v.rep, v)
|
|
3578
|
+
S.set_vertex(v1.rep, v1)
|
|
3579
|
+
|
|
3580
|
+
# Find the opposite edge
|
|
3581
|
+
opp = self._BT.opposite(e)
|
|
3582
|
+
# opp_det = opp.determinant()
|
|
3583
|
+
new_e_opp = Edge(p, num_edges, opp, v1, v, opposite=new_e)
|
|
3584
|
+
new_e.opposite = new_e_opp
|
|
3585
|
+
|
|
3586
|
+
if new_e.parity == 0:
|
|
3587
|
+
edge_list.append(new_e)
|
|
3588
|
+
else:
|
|
3589
|
+
edge_list.append(new_e_opp)
|
|
3590
|
+
|
|
3591
|
+
v.leaving_edges.append(new_e)
|
|
3592
|
+
v.entering_edges.append(new_e_opp)
|
|
3593
|
+
v1.entering_edges.append(new_e)
|
|
3594
|
+
v1.leaving_edges.append(new_e_opp)
|
|
3595
|
+
num_edges += 1
|
|
3596
|
+
computed_genus = Integer(1 - len(vertex_list) + num_edges)
|
|
3597
|
+
if check:
|
|
3598
|
+
if computed_genus != genus:
|
|
3599
|
+
print('You found a bug! Please report!')
|
|
3600
|
+
print('Computed genus =', computed_genus)
|
|
3601
|
+
print('Theoretical genus =', genus)
|
|
3602
|
+
raise RuntimeError
|
|
3603
|
+
if self.get_num_verts() != len(vertex_list):
|
|
3604
|
+
raise RuntimeError('Number of vertices different '
|
|
3605
|
+
'from expected.')
|
|
3606
|
+
|
|
3607
|
+
self._nontorsion_generators = nontorsion_generators
|
|
3608
|
+
self._boundary = {vv.rep: vv for vv in vertex_list}
|
|
3609
|
+
self._edge_list = edge_list
|
|
3610
|
+
self._vertex_list = vertex_list
|
|
3611
|
+
self._num_edges = num_edges
|
|
3612
|
+
self._S = S
|
|
3613
|
+
self._Sfun = Sfun
|
|
3614
|
+
|
|
3615
|
+
def harmonic_cocycle_from_elliptic_curve(self, E, prec=None):
|
|
3616
|
+
r"""
|
|
3617
|
+
Return a harmonic cocycle with the same Hecke eigenvalues as ``E``.
|
|
3618
|
+
|
|
3619
|
+
Given an elliptic curve `E` having a conductor `N` of the form `pN^-N^+`,
|
|
3620
|
+
return the harmonic cocycle over ``self`` which is attached to ``E`` via
|
|
3621
|
+
modularity. The result is only well-defined up to scaling.
|
|
3622
|
+
|
|
3623
|
+
INPUT:
|
|
3624
|
+
|
|
3625
|
+
- ``E`` -- an elliptic curve over the rational numbers
|
|
3626
|
+
|
|
3627
|
+
- ``prec`` -- (default: ``None``) if specified, the harmonic cocycle will take values
|
|
3628
|
+
in `\QQ_p` with precision ``prec``. Otherwise it will take values in `\ZZ`.
|
|
3629
|
+
|
|
3630
|
+
OUTPUT: a harmonic cocycle attached via modularity to the given elliptic curve
|
|
3631
|
+
|
|
3632
|
+
EXAMPLES::
|
|
3633
|
+
|
|
3634
|
+
sage: E = EllipticCurve('21a1')
|
|
3635
|
+
sage: X = BruhatTitsQuotient(7,3)
|
|
3636
|
+
sage: f = X.harmonic_cocycle_from_elliptic_curve(E,10)
|
|
3637
|
+
sage: T29 = f.parent().hecke_operator(29)
|
|
3638
|
+
sage: T29(f) == E.ap(29) * f
|
|
3639
|
+
True
|
|
3640
|
+
sage: E = EllipticCurve('51a1')
|
|
3641
|
+
sage: X = BruhatTitsQuotient(3,17)
|
|
3642
|
+
sage: f = X.harmonic_cocycle_from_elliptic_curve(E,20)
|
|
3643
|
+
sage: T31 = f.parent().hecke_operator(31)
|
|
3644
|
+
sage: T31(f) == E.ap(31) * f
|
|
3645
|
+
True
|
|
3646
|
+
"""
|
|
3647
|
+
from .pautomorphicform import BruhatTitsHarmonicCocycles
|
|
3648
|
+
M = BruhatTitsHarmonicCocycles(self, 2, prec=prec)
|
|
3649
|
+
q = ZZ.one()
|
|
3650
|
+
F = E.base_ring()
|
|
3651
|
+
try:
|
|
3652
|
+
N = ZZ(E.conductor())
|
|
3653
|
+
except TypeError:
|
|
3654
|
+
try:
|
|
3655
|
+
N = E.conductor().norm()
|
|
3656
|
+
except ValueError:
|
|
3657
|
+
N = E.conductor().norm(QQ)
|
|
3658
|
+
N1 = self.level() * self.Nplus()
|
|
3659
|
+
K = M.base_ring() ** M.dimension()
|
|
3660
|
+
while K.dimension() != 1:
|
|
3661
|
+
q = q.next_prime()
|
|
3662
|
+
if N % q == 0 or N1 % q == 0:
|
|
3663
|
+
continue
|
|
3664
|
+
if F == QQ:
|
|
3665
|
+
Eap = E.ap(q)
|
|
3666
|
+
else:
|
|
3667
|
+
Q = F(q).factor()[0][0]
|
|
3668
|
+
Eap = ZZ(Q.norm() + 1 - E.reduction(Q).count_points())
|
|
3669
|
+
K1 = (M.hecke_matrix(q).transpose() - Eap).right_kernel()
|
|
3670
|
+
K = K.intersection(K1)
|
|
3671
|
+
col = [ZZ(o) for o in K.matrix().list()]
|
|
3672
|
+
return sum([a * M.gen(i) for i, a in enumerate(col) if a != 0], M(0))
|
|
3673
|
+
|
|
3674
|
+
def harmonic_cocycles(self, k, prec=None, basis_matrix=None, base_field=None):
|
|
3675
|
+
r"""
|
|
3676
|
+
Compute the space of harmonic cocycles of a given even weight ``k``.
|
|
3677
|
+
|
|
3678
|
+
INPUT:
|
|
3679
|
+
|
|
3680
|
+
- ``k`` -- integer; the weight. It must be even.
|
|
3681
|
+
|
|
3682
|
+
- ``prec`` -- integer (default: ``None``); if specified, the
|
|
3683
|
+
precision for the coefficient module
|
|
3684
|
+
|
|
3685
|
+
- ``basis_matrix`` -- a matrix (default: ``None``)
|
|
3686
|
+
|
|
3687
|
+
- ``base_field`` -- a ring (default: ``None``)
|
|
3688
|
+
|
|
3689
|
+
OUTPUT: a space of harmonic cocycles
|
|
3690
|
+
|
|
3691
|
+
EXAMPLES::
|
|
3692
|
+
|
|
3693
|
+
sage: X = BruhatTitsQuotient(31,7)
|
|
3694
|
+
sage: H = X.harmonic_cocycles(2,prec=10)
|
|
3695
|
+
sage: H
|
|
3696
|
+
Space of harmonic cocycles of weight 2 on Quotient of the Bruhat Tits tree of GL_2(QQ_31) with discriminant 7 and level 1
|
|
3697
|
+
sage: H.basis()[0]
|
|
3698
|
+
Harmonic cocycle with values in Sym^0 Q_31^2
|
|
3699
|
+
"""
|
|
3700
|
+
from .pautomorphicform import BruhatTitsHarmonicCocycles
|
|
3701
|
+
return BruhatTitsHarmonicCocycles(self, k, prec=prec, basis_matrix=basis_matrix, base_field=base_field)
|
|
3702
|
+
|
|
3703
|
+
def padic_automorphic_forms(self, U, prec=None, t=None, R=None, overconvergent=False):
|
|
3704
|
+
r"""
|
|
3705
|
+
The module of (quaternionic) `p`-adic automorphic forms over ``self``.
|
|
3706
|
+
|
|
3707
|
+
INPUT:
|
|
3708
|
+
|
|
3709
|
+
- ``U`` -- a distributions module or an integer. If ``U`` is a
|
|
3710
|
+
distributions module then this creates the relevant space of
|
|
3711
|
+
automorphic forms. If ``U`` is an integer then the coefficients
|
|
3712
|
+
are the (`U-2`)nd power of the symmetric representation of
|
|
3713
|
+
`GL_2(\QQ_p)`.
|
|
3714
|
+
|
|
3715
|
+
- ``prec`` -- a precision (default: ``None``). if not ``None`` should
|
|
3716
|
+
be a positive integer
|
|
3717
|
+
|
|
3718
|
+
- ``t`` -- (default: ``None``) the number of additional moments to store. If ``None``, determine
|
|
3719
|
+
it automatically from ``prec``, ``U`` and the ``overconvergent`` flag
|
|
3720
|
+
|
|
3721
|
+
- ``R`` -- (default: ``None``) if specified, coefficient field of the automorphic forms.
|
|
3722
|
+
If not specified it defaults to the base ring of the distributions ``U``, or to `\QQ_p`
|
|
3723
|
+
with the working precision ``prec``.
|
|
3724
|
+
|
|
3725
|
+
- ``overconvergent`` -- boolean (default: ``False``); if ``True``, will construct overconvergent
|
|
3726
|
+
`p`-adic automorphic forms. Otherwise it constructs the finite dimensional space of
|
|
3727
|
+
`p`-adic automorphic forms which is isomorphic to the space of harmonic cocycles.
|
|
3728
|
+
|
|
3729
|
+
EXAMPLES::
|
|
3730
|
+
|
|
3731
|
+
sage: X = BruhatTitsQuotient(11,5)
|
|
3732
|
+
sage: X.padic_automorphic_forms(2,prec=10)
|
|
3733
|
+
Space of automorphic forms on Quotient of the Bruhat Tits tree of GL_2(QQ_11) with discriminant 5 and level 1 with values in Sym^0 Q_11^2
|
|
3734
|
+
"""
|
|
3735
|
+
from .pautomorphicform import pAdicAutomorphicForms
|
|
3736
|
+
return pAdicAutomorphicForms(self, U, prec=prec, t=t, R=R, overconvergent=overconvergent)
|