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