passagemath-schemes 10.6.38__cp314-cp314t-macosx_13_0_arm64.whl

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

Potentially problematic release.


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

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