passagemath-flint 10.6.1rc10__cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (361) hide show
  1. passagemath_flint-10.6.1rc10.dist-info/METADATA +122 -0
  2. passagemath_flint-10.6.1rc10.dist-info/RECORD +361 -0
  3. passagemath_flint-10.6.1rc10.dist-info/WHEEL +6 -0
  4. passagemath_flint-10.6.1rc10.dist-info/top_level.txt +2 -0
  5. passagemath_flint.libs/libflint-aecb9cc5.so.21.0.0 +0 -0
  6. passagemath_flint.libs/libgf2x-a4cdec90.so.3.0.0 +0 -0
  7. passagemath_flint.libs/libgfortran-8f1e9814.so.5.0.0 +0 -0
  8. passagemath_flint.libs/libgmp-6e109695.so.10.5.0 +0 -0
  9. passagemath_flint.libs/libgsl-cda90e79.so.28.0.0 +0 -0
  10. passagemath_flint.libs/libmpfi-e3c25853.so.0.0.0 +0 -0
  11. passagemath_flint.libs/libmpfr-82690d50.so.6.2.1 +0 -0
  12. passagemath_flint.libs/libntl-74e7d9a3.so.44.0.1 +0 -0
  13. passagemath_flint.libs/libopenblasp-r0-6dcb67f9.3.29.so +0 -0
  14. passagemath_flint.libs/libquadmath-828275a7.so.0.0.0 +0 -0
  15. sage/all__sagemath_flint.py +29 -0
  16. sage/combinat/all__sagemath_flint.py +1 -0
  17. sage/combinat/posets/all__sagemath_flint.py +1 -0
  18. sage/combinat/posets/hasse_cython_flint.cpython-313-x86_64-linux-gnu.so +0 -0
  19. sage/combinat/posets/hasse_cython_flint.pyx +194 -0
  20. sage/data_structures/all__sagemath_flint.py +1 -0
  21. sage/data_structures/bounded_integer_sequences.cpython-313-x86_64-linux-gnu.so +0 -0
  22. sage/data_structures/bounded_integer_sequences.pxd +62 -0
  23. sage/data_structures/bounded_integer_sequences.pyx +1418 -0
  24. sage/graphs/all__sagemath_flint.py +1 -0
  25. sage/graphs/chrompoly.cpython-313-x86_64-linux-gnu.so +0 -0
  26. sage/graphs/chrompoly.pyx +555 -0
  27. sage/graphs/matchpoly.cpython-313-x86_64-linux-gnu.so +0 -0
  28. sage/graphs/matchpoly.pyx +412 -0
  29. sage/libs/all__sagemath_flint.py +17 -0
  30. sage/libs/arb/__init__.py +1 -0
  31. sage/libs/arb/acb.pxd +154 -0
  32. sage/libs/arb/acb_calc.pxd +9 -0
  33. sage/libs/arb/acb_elliptic.pxd +25 -0
  34. sage/libs/arb/acb_hypgeom.pxd +74 -0
  35. sage/libs/arb/acb_mat.pxd +62 -0
  36. sage/libs/arb/acb_modular.pxd +17 -0
  37. sage/libs/arb/acb_poly.pxd +216 -0
  38. sage/libs/arb/arb.pxd +240 -0
  39. sage/libs/arb/arb_fmpz_poly.pxd +21 -0
  40. sage/libs/arb/arb_hypgeom.pxd +83 -0
  41. sage/libs/arb/arb_wrap.h +34 -0
  42. sage/libs/arb/arf.pxd +131 -0
  43. sage/libs/arb/arith.cpython-313-x86_64-linux-gnu.so +0 -0
  44. sage/libs/arb/arith.pyx +87 -0
  45. sage/libs/arb/bernoulli.pxd +6 -0
  46. sage/libs/arb/mag.pxd +77 -0
  47. sage/libs/arb/types.pxd +37 -0
  48. sage/libs/flint/__init__.py +1 -0
  49. sage/libs/flint/acb.pxd +270 -0
  50. sage/libs/flint/acb_calc.pxd +22 -0
  51. sage/libs/flint/acb_dft.pxd +51 -0
  52. sage/libs/flint/acb_dirichlet.pxd +112 -0
  53. sage/libs/flint/acb_elliptic.pxd +42 -0
  54. sage/libs/flint/acb_hypgeom.pxd +169 -0
  55. sage/libs/flint/acb_macros.pxd +9 -0
  56. sage/libs/flint/acb_mat.pxd +136 -0
  57. sage/libs/flint/acb_mat_macros.pxd +10 -0
  58. sage/libs/flint/acb_modular.pxd +62 -0
  59. sage/libs/flint/acb_poly.pxd +251 -0
  60. sage/libs/flint/acb_poly_macros.pxd +8 -0
  61. sage/libs/flint/acb_theta.pxd +124 -0
  62. sage/libs/flint/acf.pxd +32 -0
  63. sage/libs/flint/aprcl.pxd +84 -0
  64. sage/libs/flint/arb.pxd +382 -0
  65. sage/libs/flint/arb_calc.pxd +31 -0
  66. sage/libs/flint/arb_fmpz_poly.pxd +34 -0
  67. sage/libs/flint/arb_fpwrap.pxd +215 -0
  68. sage/libs/flint/arb_hypgeom.pxd +147 -0
  69. sage/libs/flint/arb_macros.pxd +9 -0
  70. sage/libs/flint/arb_mat.pxd +140 -0
  71. sage/libs/flint/arb_mat_macros.pxd +10 -0
  72. sage/libs/flint/arb_poly.pxd +237 -0
  73. sage/libs/flint/arf.pxd +167 -0
  74. sage/libs/flint/arith.cpython-313-x86_64-linux-gnu.so +0 -0
  75. sage/libs/flint/arith.pxd +76 -0
  76. sage/libs/flint/arith.pyx +77 -0
  77. sage/libs/flint/arith_sage.cpython-313-x86_64-linux-gnu.so +0 -0
  78. sage/libs/flint/arith_sage.pyx +308 -0
  79. sage/libs/flint/bernoulli.pxd +28 -0
  80. sage/libs/flint/bool_mat.pxd +52 -0
  81. sage/libs/flint/ca.pxd +203 -0
  82. sage/libs/flint/ca_ext.pxd +34 -0
  83. sage/libs/flint/ca_field.pxd +32 -0
  84. sage/libs/flint/ca_mat.pxd +117 -0
  85. sage/libs/flint/ca_poly.pxd +104 -0
  86. sage/libs/flint/ca_vec.pxd +46 -0
  87. sage/libs/flint/calcium.pxd +27 -0
  88. sage/libs/flint/d_mat.pxd +39 -0
  89. sage/libs/flint/d_vec.pxd +32 -0
  90. sage/libs/flint/dirichlet.pxd +57 -0
  91. sage/libs/flint/dlog.pxd +53 -0
  92. sage/libs/flint/double_extras.pxd +24 -0
  93. sage/libs/flint/double_interval.pxd +36 -0
  94. sage/libs/flint/fexpr.pxd +104 -0
  95. sage/libs/flint/fexpr_builtin.pxd +20 -0
  96. sage/libs/flint/fft.pxd +66 -0
  97. sage/libs/flint/flint.pxd +36 -0
  98. sage/libs/flint/flint_ntl_wrap.h +35 -0
  99. sage/libs/flint/flint_sage.cpython-313-x86_64-linux-gnu.so +0 -0
  100. sage/libs/flint/flint_sage.pyx +163 -0
  101. sage/libs/flint/flint_wrap.h +190 -0
  102. sage/libs/flint/fmpq.pxd +137 -0
  103. sage/libs/flint/fmpq_mat.pxd +105 -0
  104. sage/libs/flint/fmpq_mat_macros.pxd +10 -0
  105. sage/libs/flint/fmpq_mpoly.pxd +165 -0
  106. sage/libs/flint/fmpq_mpoly_factor.pxd +30 -0
  107. sage/libs/flint/fmpq_poly.pxd +241 -0
  108. sage/libs/flint/fmpq_poly_macros.pxd +9 -0
  109. sage/libs/flint/fmpq_poly_sage.cpython-313-x86_64-linux-gnu.so +0 -0
  110. sage/libs/flint/fmpq_poly_sage.pxd +31 -0
  111. sage/libs/flint/fmpq_poly_sage.pyx +48 -0
  112. sage/libs/flint/fmpq_vec.pxd +27 -0
  113. sage/libs/flint/fmpz.pxd +256 -0
  114. sage/libs/flint/fmpz_extras.pxd +32 -0
  115. sage/libs/flint/fmpz_factor.pxd +42 -0
  116. sage/libs/flint/fmpz_factor_sage.cpython-313-x86_64-linux-gnu.so +0 -0
  117. sage/libs/flint/fmpz_factor_sage.pxd +4 -0
  118. sage/libs/flint/fmpz_factor_sage.pyx +29 -0
  119. sage/libs/flint/fmpz_lll.pxd +49 -0
  120. sage/libs/flint/fmpz_macros.pxd +8 -0
  121. sage/libs/flint/fmpz_mat.pxd +184 -0
  122. sage/libs/flint/fmpz_mat_macros.pxd +10 -0
  123. sage/libs/flint/fmpz_mod.pxd +46 -0
  124. sage/libs/flint/fmpz_mod_mat.pxd +71 -0
  125. sage/libs/flint/fmpz_mod_mpoly.pxd +161 -0
  126. sage/libs/flint/fmpz_mod_mpoly_factor.pxd +28 -0
  127. sage/libs/flint/fmpz_mod_poly.pxd +249 -0
  128. sage/libs/flint/fmpz_mod_poly_factor.pxd +46 -0
  129. sage/libs/flint/fmpz_mod_vec.pxd +27 -0
  130. sage/libs/flint/fmpz_mpoly.pxd +224 -0
  131. sage/libs/flint/fmpz_mpoly_factor.pxd +29 -0
  132. sage/libs/flint/fmpz_mpoly_q.pxd +57 -0
  133. sage/libs/flint/fmpz_poly.cpython-313-x86_64-linux-gnu.so +0 -0
  134. sage/libs/flint/fmpz_poly.pxd +407 -0
  135. sage/libs/flint/fmpz_poly.pyx +19 -0
  136. sage/libs/flint/fmpz_poly_factor.pxd +33 -0
  137. sage/libs/flint/fmpz_poly_macros.pxd +8 -0
  138. sage/libs/flint/fmpz_poly_mat.pxd +71 -0
  139. sage/libs/flint/fmpz_poly_q.pxd +55 -0
  140. sage/libs/flint/fmpz_poly_sage.cpython-313-x86_64-linux-gnu.so +0 -0
  141. sage/libs/flint/fmpz_poly_sage.pxd +20 -0
  142. sage/libs/flint/fmpz_poly_sage.pyx +500 -0
  143. sage/libs/flint/fmpz_vec.pxd +80 -0
  144. sage/libs/flint/fmpzi.pxd +52 -0
  145. sage/libs/flint/fq.pxd +97 -0
  146. sage/libs/flint/fq_default.pxd +84 -0
  147. sage/libs/flint/fq_default_mat.pxd +70 -0
  148. sage/libs/flint/fq_default_poly.pxd +97 -0
  149. sage/libs/flint/fq_default_poly_factor.pxd +39 -0
  150. sage/libs/flint/fq_embed.pxd +28 -0
  151. sage/libs/flint/fq_mat.pxd +83 -0
  152. sage/libs/flint/fq_nmod.pxd +95 -0
  153. sage/libs/flint/fq_nmod_embed.pxd +28 -0
  154. sage/libs/flint/fq_nmod_mat.pxd +83 -0
  155. sage/libs/flint/fq_nmod_mpoly.pxd +130 -0
  156. sage/libs/flint/fq_nmod_mpoly_factor.pxd +28 -0
  157. sage/libs/flint/fq_nmod_poly.pxd +202 -0
  158. sage/libs/flint/fq_nmod_poly_factor.pxd +47 -0
  159. sage/libs/flint/fq_nmod_vec.pxd +33 -0
  160. sage/libs/flint/fq_poly.pxd +204 -0
  161. sage/libs/flint/fq_poly_factor.pxd +47 -0
  162. sage/libs/flint/fq_vec.pxd +33 -0
  163. sage/libs/flint/fq_zech.pxd +99 -0
  164. sage/libs/flint/fq_zech_embed.pxd +28 -0
  165. sage/libs/flint/fq_zech_mat.pxd +78 -0
  166. sage/libs/flint/fq_zech_poly.pxd +198 -0
  167. sage/libs/flint/fq_zech_poly_factor.pxd +47 -0
  168. sage/libs/flint/fq_zech_vec.pxd +33 -0
  169. sage/libs/flint/gr.pxd +174 -0
  170. sage/libs/flint/gr_generic.pxd +215 -0
  171. sage/libs/flint/gr_mat.pxd +161 -0
  172. sage/libs/flint/gr_mpoly.pxd +68 -0
  173. sage/libs/flint/gr_poly.pxd +276 -0
  174. sage/libs/flint/gr_special.pxd +237 -0
  175. sage/libs/flint/gr_vec.pxd +120 -0
  176. sage/libs/flint/hypgeom.pxd +24 -0
  177. sage/libs/flint/long_extras.pxd +23 -0
  178. sage/libs/flint/mag.pxd +131 -0
  179. sage/libs/flint/mag_macros.pxd +8 -0
  180. sage/libs/flint/mpf_mat.pxd +36 -0
  181. sage/libs/flint/mpf_vec.pxd +34 -0
  182. sage/libs/flint/mpfr_mat.pxd +27 -0
  183. sage/libs/flint/mpfr_vec.pxd +25 -0
  184. sage/libs/flint/mpn_extras.pxd +41 -0
  185. sage/libs/flint/mpoly.pxd +72 -0
  186. sage/libs/flint/nf.pxd +19 -0
  187. sage/libs/flint/nf_elem.pxd +74 -0
  188. sage/libs/flint/nmod.pxd +35 -0
  189. sage/libs/flint/nmod_mat.pxd +104 -0
  190. sage/libs/flint/nmod_mpoly.pxd +144 -0
  191. sage/libs/flint/nmod_mpoly_factor.pxd +28 -0
  192. sage/libs/flint/nmod_poly.pxd +339 -0
  193. sage/libs/flint/nmod_poly_factor.pxd +44 -0
  194. sage/libs/flint/nmod_poly_linkage.pxi +710 -0
  195. sage/libs/flint/nmod_poly_mat.pxd +76 -0
  196. sage/libs/flint/nmod_vec.pxd +40 -0
  197. sage/libs/flint/ntl_interface.pxd +17 -0
  198. sage/libs/flint/padic.pxd +93 -0
  199. sage/libs/flint/padic_mat.pxd +64 -0
  200. sage/libs/flint/padic_poly.pxd +88 -0
  201. sage/libs/flint/partitions.pxd +23 -0
  202. sage/libs/flint/perm.pxd +26 -0
  203. sage/libs/flint/profiler.pxd +24 -0
  204. sage/libs/flint/qadic.pxd +77 -0
  205. sage/libs/flint/qfb.pxd +44 -0
  206. sage/libs/flint/qqbar.pxd +172 -0
  207. sage/libs/flint/qsieve.cpython-313-x86_64-linux-gnu.so +0 -0
  208. sage/libs/flint/qsieve.pxd +41 -0
  209. sage/libs/flint/qsieve.pyx +21 -0
  210. sage/libs/flint/qsieve_sage.cpython-313-x86_64-linux-gnu.so +0 -0
  211. sage/libs/flint/qsieve_sage.pyx +67 -0
  212. sage/libs/flint/thread_pool.pxd +25 -0
  213. sage/libs/flint/types.pxd +2076 -0
  214. sage/libs/flint/ulong_extras.cpython-313-x86_64-linux-gnu.so +0 -0
  215. sage/libs/flint/ulong_extras.pxd +141 -0
  216. sage/libs/flint/ulong_extras.pyx +21 -0
  217. sage/libs/flint/ulong_extras_sage.cpython-313-x86_64-linux-gnu.so +0 -0
  218. sage/libs/flint/ulong_extras_sage.pyx +21 -0
  219. sage/matrix/all__sagemath_flint.py +1 -0
  220. sage/matrix/change_ring.cpython-313-x86_64-linux-gnu.so +0 -0
  221. sage/matrix/change_ring.pyx +43 -0
  222. sage/matrix/matrix_complex_ball_dense.cpython-313-x86_64-linux-gnu.so +0 -0
  223. sage/matrix/matrix_complex_ball_dense.pxd +14 -0
  224. sage/matrix/matrix_complex_ball_dense.pyx +973 -0
  225. sage/matrix/matrix_cyclo_dense.cpython-313-x86_64-linux-gnu.so +0 -0
  226. sage/matrix/matrix_cyclo_dense.pxd +16 -0
  227. sage/matrix/matrix_cyclo_dense.pyx +1761 -0
  228. sage/matrix/matrix_integer_dense.cpython-313-x86_64-linux-gnu.so +0 -0
  229. sage/matrix/matrix_integer_dense.pxd +32 -0
  230. sage/matrix/matrix_integer_dense.pyx +5801 -0
  231. sage/matrix/matrix_integer_dense_hnf.py +1294 -0
  232. sage/matrix/matrix_integer_dense_saturation.py +346 -0
  233. sage/matrix/matrix_integer_sparse.cpython-313-x86_64-linux-gnu.so +0 -0
  234. sage/matrix/matrix_integer_sparse.pxd +9 -0
  235. sage/matrix/matrix_integer_sparse.pyx +1090 -0
  236. sage/matrix/matrix_rational_dense.cpython-313-x86_64-linux-gnu.so +0 -0
  237. sage/matrix/matrix_rational_dense.pxd +23 -0
  238. sage/matrix/matrix_rational_dense.pyx +2995 -0
  239. sage/matrix/matrix_rational_sparse.cpython-313-x86_64-linux-gnu.so +0 -0
  240. sage/matrix/matrix_rational_sparse.pxd +11 -0
  241. sage/matrix/matrix_rational_sparse.pyx +789 -0
  242. sage/matrix/misc_flint.cpython-313-x86_64-linux-gnu.so +0 -0
  243. sage/matrix/misc_flint.pyx +109 -0
  244. sage/modular/all__sagemath_flint.py +1 -0
  245. sage/modular/modform/all__sagemath_flint.py +1 -0
  246. sage/modular/modform/eis_series_cython.cpython-313-x86_64-linux-gnu.so +0 -0
  247. sage/modular/modform/eis_series_cython.pyx +226 -0
  248. sage/modular/modsym/all__sagemath_flint.py +1 -0
  249. sage/modular/modsym/apply.cpython-313-x86_64-linux-gnu.so +0 -0
  250. sage/modular/modsym/apply.pxd +6 -0
  251. sage/modular/modsym/apply.pyx +113 -0
  252. sage/modular/modsym/heilbronn.cpython-313-x86_64-linux-gnu.so +0 -0
  253. sage/modular/modsym/heilbronn.pyx +966 -0
  254. sage/modular/pollack_stevens/all__sagemath_flint.py +1 -0
  255. sage/modular/pollack_stevens/dist.cpython-313-x86_64-linux-gnu.so +0 -0
  256. sage/modular/pollack_stevens/dist.pxd +38 -0
  257. sage/modular/pollack_stevens/dist.pyx +1439 -0
  258. sage/quivers/algebra.py +691 -0
  259. sage/quivers/algebra_elements.cpython-313-x86_64-linux-gnu.so +0 -0
  260. sage/quivers/algebra_elements.pxd +97 -0
  261. sage/quivers/algebra_elements.pxi +1324 -0
  262. sage/quivers/algebra_elements.pyx +1424 -0
  263. sage/quivers/all.py +1 -0
  264. sage/quivers/ar_quiver.py +917 -0
  265. sage/quivers/homspace.py +640 -0
  266. sage/quivers/morphism.py +1282 -0
  267. sage/quivers/path_semigroup.py +1155 -0
  268. sage/quivers/paths.cpython-313-x86_64-linux-gnu.so +0 -0
  269. sage/quivers/paths.pxd +13 -0
  270. sage/quivers/paths.pyx +809 -0
  271. sage/quivers/representation.py +2975 -0
  272. sage/rings/all__sagemath_flint.py +37 -0
  273. sage/rings/cif.py +4 -0
  274. sage/rings/complex_arb.cpython-313-x86_64-linux-gnu.so +0 -0
  275. sage/rings/complex_arb.pxd +29 -0
  276. sage/rings/complex_arb.pyx +5176 -0
  277. sage/rings/complex_interval.cpython-313-x86_64-linux-gnu.so +0 -0
  278. sage/rings/complex_interval.pxd +30 -0
  279. sage/rings/complex_interval.pyx +2475 -0
  280. sage/rings/complex_interval_field.py +711 -0
  281. sage/rings/convert/all.py +1 -0
  282. sage/rings/convert/mpfi.cpython-313-x86_64-linux-gnu.so +0 -0
  283. sage/rings/convert/mpfi.pxd +6 -0
  284. sage/rings/convert/mpfi.pyx +576 -0
  285. sage/rings/factorint_flint.cpython-313-x86_64-linux-gnu.so +0 -0
  286. sage/rings/factorint_flint.pyx +99 -0
  287. sage/rings/fraction_field_FpT.cpython-313-x86_64-linux-gnu.so +0 -0
  288. sage/rings/fraction_field_FpT.pxd +28 -0
  289. sage/rings/fraction_field_FpT.pyx +2043 -0
  290. sage/rings/imaginary_unit.py +5 -0
  291. sage/rings/monomials.py +73 -0
  292. sage/rings/number_field/S_unit_solver.py +2870 -0
  293. sage/rings/number_field/all__sagemath_flint.py +7 -0
  294. sage/rings/number_field/bdd_height.py +664 -0
  295. sage/rings/number_field/class_group.py +762 -0
  296. sage/rings/number_field/galois_group.py +1307 -0
  297. sage/rings/number_field/homset.py +612 -0
  298. sage/rings/number_field/maps.py +687 -0
  299. sage/rings/number_field/morphism.py +272 -0
  300. sage/rings/number_field/number_field.py +12820 -0
  301. sage/rings/number_field/number_field_element.cpython-313-x86_64-linux-gnu.so +0 -0
  302. sage/rings/number_field/number_field_element.pxd +59 -0
  303. sage/rings/number_field/number_field_element.pyx +5735 -0
  304. sage/rings/number_field/number_field_element_quadratic.cpython-313-x86_64-linux-gnu.so +0 -0
  305. sage/rings/number_field/number_field_element_quadratic.pxd +34 -0
  306. sage/rings/number_field/number_field_element_quadratic.pyx +3185 -0
  307. sage/rings/number_field/number_field_ideal_rel.py +925 -0
  308. sage/rings/number_field/number_field_morphisms.cpython-313-x86_64-linux-gnu.so +0 -0
  309. sage/rings/number_field/number_field_morphisms.pyx +781 -0
  310. sage/rings/number_field/number_field_rel.py +2734 -0
  311. sage/rings/number_field/order.py +2981 -0
  312. sage/rings/number_field/order_ideal.py +804 -0
  313. sage/rings/number_field/selmer_group.py +715 -0
  314. sage/rings/number_field/small_primes_of_degree_one.py +242 -0
  315. sage/rings/number_field/splitting_field.py +606 -0
  316. sage/rings/number_field/structure.py +380 -0
  317. sage/rings/number_field/unit_group.py +721 -0
  318. sage/rings/padics/all__sagemath_flint.py +3 -0
  319. sage/rings/polynomial/all__sagemath_flint.py +1 -0
  320. sage/rings/polynomial/complex_roots.py +312 -0
  321. sage/rings/polynomial/evaluation_flint.cpython-313-x86_64-linux-gnu.so +0 -0
  322. sage/rings/polynomial/evaluation_flint.pxd +7 -0
  323. sage/rings/polynomial/evaluation_flint.pyx +68 -0
  324. sage/rings/polynomial/hilbert.cpython-313-x86_64-linux-gnu.so +0 -0
  325. sage/rings/polynomial/hilbert.pyx +602 -0
  326. sage/rings/polynomial/polynomial_complex_arb.cpython-313-x86_64-linux-gnu.so +0 -0
  327. sage/rings/polynomial/polynomial_complex_arb.pxd +7 -0
  328. sage/rings/polynomial/polynomial_complex_arb.pyx +963 -0
  329. sage/rings/polynomial/polynomial_integer_dense_flint.cpython-313-x86_64-linux-gnu.so +0 -0
  330. sage/rings/polynomial/polynomial_integer_dense_flint.pxd +13 -0
  331. sage/rings/polynomial/polynomial_integer_dense_flint.pyx +1881 -0
  332. sage/rings/polynomial/polynomial_number_field.cpython-313-x86_64-linux-gnu.so +0 -0
  333. sage/rings/polynomial/polynomial_number_field.pyx +345 -0
  334. sage/rings/polynomial/polynomial_rational_flint.cpython-313-x86_64-linux-gnu.so +0 -0
  335. sage/rings/polynomial/polynomial_rational_flint.pxd +20 -0
  336. sage/rings/polynomial/polynomial_rational_flint.pyx +2598 -0
  337. sage/rings/polynomial/polynomial_zmod_flint.cpython-313-x86_64-linux-gnu.so +0 -0
  338. sage/rings/polynomial/polynomial_zmod_flint.pxd +20 -0
  339. sage/rings/polynomial/polynomial_zmod_flint.pyx +1063 -0
  340. sage/rings/polynomial/real_roots.cpython-313-x86_64-linux-gnu.so +0 -0
  341. sage/rings/polynomial/real_roots.pxd +81 -0
  342. sage/rings/polynomial/real_roots.pyx +4704 -0
  343. sage/rings/polynomial/refine_root.cpython-313-x86_64-linux-gnu.so +0 -0
  344. sage/rings/polynomial/refine_root.pyx +142 -0
  345. sage/rings/polynomial/weil/all.py +4 -0
  346. sage/rings/polynomial/weil/power_sums.h +46 -0
  347. sage/rings/polynomial/weil/weil_polynomials.cpython-313-x86_64-linux-gnu.so +0 -0
  348. sage/rings/polynomial/weil/weil_polynomials.pyx +596 -0
  349. sage/rings/qqbar.py +9025 -0
  350. sage/rings/real_arb.cpython-313-x86_64-linux-gnu.so +0 -0
  351. sage/rings/real_arb.pxd +21 -0
  352. sage/rings/real_arb.pyx +4065 -0
  353. sage/rings/real_interval_absolute.cpython-313-x86_64-linux-gnu.so +0 -0
  354. sage/rings/real_interval_absolute.pyx +1073 -0
  355. sage/rings/real_mpfi.cpython-313-x86_64-linux-gnu.so +0 -0
  356. sage/rings/real_mpfi.pyx +5428 -0
  357. sage/schemes/all__sagemath_flint.py +1 -0
  358. sage/schemes/elliptic_curves/all__sagemath_flint.py +1 -0
  359. sage/schemes/elliptic_curves/descent_two_isogeny.cpython-313-x86_64-linux-gnu.so +0 -0
  360. sage/schemes/elliptic_curves/descent_two_isogeny.pyx +1387 -0
  361. sage/schemes/elliptic_curves/descent_two_isogeny_pari.pxd +5 -0
@@ -0,0 +1,2870 @@
1
+ # sage_setup: distribution = sagemath-flint
2
+ # sage.doctest: needs fpylll sage.rings.number_field sage.rings.padics sage.symbolic
3
+ r"""
4
+ Solver for the `S`-unit equation `x + y = 1`
5
+
6
+ Inspired by works of Tzanakis--de Weger, Baker--Wustholz and Smart, we use the
7
+ LLL methods to implement an algorithm that returns all `S`-unit solutions to
8
+ the equation `x + y = 1`.
9
+
10
+ EXAMPLES::
11
+
12
+ sage: from sage.rings.number_field.S_unit_solver import solve_S_unit_equation, eq_up_to_order
13
+ sage: x = polygen(ZZ, 'x')
14
+ sage: K.<xi> = NumberField(x^2 + x + 1)
15
+ sage: S = K.primes_above(3)
16
+ sage: expected = [((4, 1), (4, 0), xi + 2, -xi - 1),
17
+ ....: ((3, -1), (2, -1), 1/3*xi + 2/3, -1/3*xi + 1/3),
18
+ ....: ((1, 0), (5, 0), xi + 1, -xi),
19
+ ....: ((2, 0), (3, 1), xi, -xi + 1)]
20
+ sage: sols = solve_S_unit_equation(K, S, 200)
21
+ sage: eq_up_to_order(sols, expected)
22
+ True
23
+
24
+ .. TODO::
25
+
26
+ - Use Cython to improve timings on the sieve
27
+
28
+ REFERENCES:
29
+
30
+ - [MR2016]_
31
+ - [Sma1995]_
32
+ - [Sma1998]_
33
+ - [Yu2007]_
34
+ - [AKMRVW]_
35
+
36
+ AUTHORS:
37
+
38
+ - Alejandra Alvarado, Angelos Koutsianas, Beth Malmskog, Christopher Rasmussen,
39
+ David Roe, Christelle Vincent, Mckenzie West (2018-04-25 to 2018-11-09):
40
+ original version
41
+ """
42
+
43
+
44
+ # ****************************************************************************
45
+ # Copyright (C) 2020 Alejandra Alvarado <aalvarado2 at eiu.edu>
46
+ # Angelos Koutsianas <koutsis.jr at gmail.com>
47
+ # Beth Malmskog <beth.malmskog at gmail.com>
48
+ # Christopher Rasmussen <crasmussen at wesleyan.edu>
49
+ # Christelle Vincent <christelle.vincent at uvm.edu>
50
+ # Mckenzie West <westmr at uwec.edu>
51
+ #
52
+ # This program is free software: you can redistribute it and/or modify
53
+ # it under the terms of the GNU General Public License as published by
54
+ # the Free Software Foundation, either version 2 of the License, or
55
+ # (at your option) any later version.
56
+ # https://www.gnu.org/licenses/
57
+ # ****************************************************************************
58
+
59
+
60
+ from sage.rings.infinity import Infinity
61
+ from sage.rings.integer import Integer
62
+ from sage.rings.integer_ring import ZZ
63
+ from sage.rings.real_mpfr import RealField, RR
64
+ from sage.rings.complex_mpfr import ComplexField
65
+ from sage.functions.log import exp
66
+ from sage.rings.rational_field import QQ
67
+ from sage.rings.number_field.number_field import is_real_place, refine_embedding
68
+ from sage.rings.number_field.unit_group import UnitGroup
69
+ from sage.rings.finite_rings.integer_mod_ring import Integers
70
+ from sage.rings.finite_rings.integer_mod import mod
71
+ from sage.rings.padics.factory import Qp
72
+ from sage.combinat.combination import Combinations
73
+ from sage.rings.polynomial.polynomial_ring import polygen
74
+ from sage.misc.misc_c import prod
75
+ from sage.arith.functions import lcm
76
+ from sage.arith.misc import gcd, CRT, factorial
77
+ from sage.matrix.constructor import matrix, identity_matrix, vector, block_matrix, zero_matrix
78
+ from sage.modules.free_module_element import zero_vector
79
+ from itertools import combinations_with_replacement
80
+ from copy import copy
81
+ import itertools
82
+
83
+
84
+ def column_Log(SUK, iota, U, prec=106):
85
+ r"""
86
+ Return the log vector of ``iota``; i.e., the logs of all the valuations.
87
+
88
+ INPUT:
89
+
90
+ - ``SUK`` -- a group of `S`-units
91
+ - ``iota`` -- an element of ``K``
92
+ - ``U`` -- list of places (finite or infinite) of ``K``
93
+ - ``prec`` -- the precision of the real field (default: 106)
94
+
95
+ OUTPUT: the log vector as a list of real numbers
96
+
97
+ EXAMPLES::
98
+
99
+ sage: from sage.rings.number_field.S_unit_solver import column_Log
100
+ sage: x = polygen(ZZ, 'x')
101
+ sage: K.<xi> = NumberField(x^3 - 3)
102
+ sage: S = tuple(K.primes_above(3))
103
+ sage: SUK = UnitGroup(K, S=S)
104
+ sage: phi_complex = K.places()[1]
105
+ sage: v_fin = S[0]
106
+ sage: U = [phi_complex, v_fin]
107
+ sage: column_Log(SUK, xi^2, U) # abs tol 1e-29
108
+ [1.464816384890812968648768625966, -2.197224577336219382790490473845]
109
+
110
+ REFERENCES:
111
+
112
+ - [Sma1995]_ p. 823
113
+ """
114
+ R = RealField(prec)
115
+
116
+ return [ R(SUK.number_field().abs_val(v, iota, prec)).log() for v in U]
117
+
118
+
119
+ def c3_func(SUK, prec=106):
120
+ r"""
121
+ Return the constant `c_3` from [AKMRVW]_.
122
+
123
+ INPUT:
124
+
125
+ - ``SUK`` -- a group of `S`-units
126
+ - ``prec`` -- the precision of the real field (default: 106)
127
+
128
+ OUTPUT: the constant `c_3`, as a real number
129
+
130
+ EXAMPLES::
131
+
132
+ sage: from sage.rings.number_field.S_unit_solver import c3_func
133
+ sage: x = polygen(ZZ, 'x')
134
+ sage: K.<xi> = NumberField(x^3 - 3)
135
+ sage: SUK = UnitGroup(K, S=tuple(K.primes_above(3)))
136
+
137
+ sage: c3_func(SUK) # abs tol 1e-29
138
+ 0.4257859134798034746197327286726
139
+
140
+ .. NOTE::
141
+
142
+ The numerator should be as close to 1 as possible, especially as the rank of the `S`-units grows large
143
+
144
+ REFERENCES:
145
+
146
+ - [AKMRVW]_ :arxiv:`1903.00977`
147
+ """
148
+ R = RealField(prec)
149
+
150
+ all_places = list(SUK.primes()) + SUK.number_field().places(prec)
151
+ Possible_U = Combinations(all_places, SUK.rank())
152
+ c1 = R(1) # guarantees final c1 >= 1
153
+ for U in Possible_U:
154
+ # first, build the matrix C_{i,U}
155
+ columns_of_C = [column_Log(SUK, unit, U, prec)
156
+ for unit in SUK.fundamental_units()]
157
+ C = matrix(SUK.rank(), SUK.rank(), columns_of_C)
158
+ # Is it invertible?
159
+ if abs(C.determinant()) > 10**(-10):
160
+ poss_c1 = C.inverse().apply_map(abs).norm(Infinity)
161
+ c1 = R(max(poss_c1, c1))
162
+ return R(0.9999999) / (c1 * SUK.rank())
163
+
164
+
165
+ def c4_func(SUK, v, A, prec=106):
166
+ r"""
167
+ Return the constant `c_4` from Smart's TCDF paper, [Sma1995]_.
168
+
169
+ INPUT:
170
+
171
+ - ``SUK`` -- a group of `S`-units
172
+ - ``v`` -- a place of ``K``, finite (a fractional ideal) or infinite (element of ``SUK.number_field().places(prec)``)
173
+ - ``A`` -- the set of the product of the coefficients of the ``S``-unit equation with each root of unity of ``K``
174
+ - ``prec`` -- the precision of the real field (default: 106)
175
+
176
+ OUTPUT: the constant `c_4`, as a real number
177
+
178
+ EXAMPLES::
179
+
180
+ sage: from sage.rings.number_field.S_unit_solver import c4_func
181
+ sage: x = polygen(ZZ, 'x')
182
+ sage: K.<xi> = NumberField(x^3 - 3)
183
+ sage: SUK = UnitGroup(K, S=tuple(K.primes_above(3)))
184
+ sage: phi_real = K.places()[0]
185
+ sage: phi_complex = K.places()[1]
186
+ sage: v_fin = tuple(K.primes_above(3))[0]
187
+ sage: A = K.roots_of_unity()
188
+
189
+ sage: c4_func(SUK, phi_real, A)
190
+ 1.000000000000000000000000000000
191
+
192
+ sage: c4_func(SUK, phi_complex, A)
193
+ 1.000000000000000000000000000000
194
+
195
+ sage: c4_func(SUK, v_fin, A)
196
+ 1.000000000000000000000000000000
197
+
198
+ REFERENCES:
199
+
200
+ - [Sma1995]_ p. 824
201
+ """
202
+ return max(SUK.number_field().abs_val(v, alpha, prec) for alpha in A)
203
+
204
+
205
+ def beta_k(betas_and_ns):
206
+ r"""
207
+ Return a pair `[\beta_k,|beta_k|_v]`, where `\beta_k` has the smallest
208
+ nonzero valuation in absolute value of the list ``betas_and_ns``.
209
+
210
+ INPUT:
211
+
212
+ - ``betas_and_ns`` -- list of pairs ``[beta,val_v(beta)]`` outputted from
213
+ the function where ``beta`` is an element of ``SUK.fundamental_units()``
214
+
215
+ OUTPUT:
216
+
217
+ The pair ``[beta_k,v(beta_k)]``, where ``beta_k`` is an element of ``K``
218
+ and ``val_v(beta_k)`` is a integer.
219
+
220
+ EXAMPLES::
221
+
222
+ sage: from sage.rings.number_field.S_unit_solver import beta_k
223
+ sage: x = polygen(ZZ, 'x')
224
+ sage: K.<xi> = NumberField(x^3 - 3)
225
+ sage: SUK = UnitGroup(K, S=tuple(K.primes_above(3)))
226
+ sage: v_fin = tuple(K.primes_above(3))[0]
227
+
228
+ sage: betas = [[beta, beta.valuation(v_fin)] for beta in SUK.fundamental_units()]
229
+ sage: beta_k(betas)
230
+ [xi, 1]
231
+
232
+ REFERENCES:
233
+
234
+ - [Sma1995]_ pp. 824-825
235
+ """
236
+ for pair in betas_and_ns:
237
+ if abs( pair[1] ) != 0:
238
+ good_pair = pair
239
+ break
240
+ for pair in betas_and_ns:
241
+ if ( abs(pair[1]) != 0 and abs(pair[1]) < abs(good_pair[1]) ):
242
+ good_pair = pair
243
+ return good_pair
244
+
245
+
246
+ def mus(SUK, v):
247
+ r"""
248
+ Return a list `[\mu]`, for `\mu` defined in [AKMRVW]_.
249
+
250
+ INPUT:
251
+
252
+ - ``SUK`` -- a group of `S`-units
253
+ - ``v`` -- a finite place of ``K``
254
+
255
+ OUTPUT: list ``[mus]`` where each ``mu`` is an element of ``K``
256
+
257
+ EXAMPLES::
258
+
259
+ sage: from sage.rings.number_field.S_unit_solver import mus
260
+ sage: x = polygen(ZZ, 'x')
261
+ sage: K.<xi> = NumberField(x^3 - 3)
262
+ sage: SUK = UnitGroup(K, S=tuple(K.primes_above(3)))
263
+ sage: v_fin = tuple(K.primes_above(3))[0]
264
+
265
+ sage: mus(SUK, v_fin)
266
+ [xi^2 - 2]
267
+
268
+ REFERENCES:
269
+
270
+ - [AKMRVW]_
271
+ """
272
+ betas = SUK.fundamental_units()
273
+ beta_and_ns = [[beta,beta.valuation(v)] for beta in betas]
274
+ if all(pair[1] == 0 for pair in beta_and_ns):
275
+ return betas
276
+ else:
277
+ good_pair = beta_k(beta_and_ns)
278
+ temp = [(beta[0]**good_pair[1])*(good_pair[0]**(-beta[1])) for beta in beta_and_ns]
279
+ temp.remove(1)
280
+ return temp
281
+
282
+
283
+ def possible_mu0s(SUK, v):
284
+ r"""
285
+ Return a list `[\mu_0]` of all possible `\mu_0` values defined in [AKMRVW]_.
286
+
287
+ INPUT:
288
+
289
+ - ``SUK`` -- a group of `S`-units
290
+ - ``v`` -- a finite place of ``K``
291
+
292
+ OUTPUT: list ``[mu0s]`` where each ``mu0`` is an element of ``K``
293
+
294
+ EXAMPLES::
295
+
296
+ sage: from sage.rings.number_field.S_unit_solver import possible_mu0s
297
+ sage: x = polygen(ZZ, 'x')
298
+ sage: K.<xi> = NumberField(x^3 - 3)
299
+ sage: S = tuple(K.primes_above(3))
300
+ sage: SUK = UnitGroup(K, S=S)
301
+ sage: v_fin = S[0]
302
+
303
+ sage: possible_mu0s(SUK, v_fin)
304
+ [-1, 1]
305
+
306
+ .. NOTE::
307
+
308
+ `n_0` is the valuation of the coefficient `\alpha_d` of the `S`-unit equation such that `|\alpha_d \tau_d|_v = 1`
309
+ We have set `n_0 = 0` here since the coefficients are roots of unity
310
+ `\alpha_0` is not defined in the paper, we set it to be 1
311
+
312
+ REFERENCES:
313
+
314
+ - [AKMRVW]_
315
+ - [Sma1995]_ pp. 824-825, but we modify the definition of ``sigma`` (``sigma_tilde``) to make it easier to code
316
+ """
317
+ beta_and_ns = [[beta,beta.valuation(v)] for beta in SUK.fundamental_units()]
318
+ betak, nk = beta_k(beta_and_ns)
319
+ ns = [beta[1] for beta in beta_and_ns if beta[0] != betak]
320
+ betas = [beta[0] for beta in beta_and_ns if beta[0] != betak]
321
+ mu0s = []
322
+ for rs in combinations_with_replacement(range(abs(nk)), len(betas)):
323
+ # n_0 = valuation_v of one of the coefficients of the equation = 0 for x + y = 1 p. 824
324
+ n_rs = zip(ns, rs)
325
+ sigma_tilde = -(sum([n_r[0]*n_r[1] for n_r in n_rs]))
326
+ if sigma_tilde % nk == 0:
327
+ beta_rs = zip(betas, rs)
328
+ temp_prod = prod([beta_r[0]**beta_r[1] for beta_r in beta_rs]) * betak**(sigma_tilde/nk)
329
+ for alpha0 in SUK.roots_of_unity():
330
+ if alpha0*temp_prod not in mu0s:
331
+ mu0s.append(alpha0*temp_prod)
332
+ return mu0s
333
+
334
+
335
+ def Yu_a1_kappa1_c1(p, dK, ep):
336
+ r"""
337
+ Compute the constants a(1), kappa1, and c(1) of [Yu2007]_.
338
+
339
+ INPUT:
340
+
341
+ - ``p`` -- a rational prime number
342
+ - ``dK`` -- the absolute degree of some number field `K`
343
+ - ``ep`` -- the absolute ramification index of some prime ``frak_p`` of `K` lying above `p`
344
+
345
+ OUTPUT:
346
+
347
+ The constants a(1), kappa1, and c(1).
348
+
349
+ EXAMPLES::
350
+
351
+ sage: from sage.rings.number_field.S_unit_solver import Yu_a1_kappa1_c1
352
+ sage: Yu_a1_kappa1_c1(5, 10, 3)
353
+ (16, 20, 319)
354
+
355
+ REFERENCES:
356
+
357
+ - [Yu2007]_
358
+ """
359
+
360
+ # For readability, we compute a(1) and kappa1 first.
361
+
362
+ if p == 2:
363
+ a1 = 32
364
+ kappa1 = 40
365
+ elif p == 3:
366
+ a1 = 16
367
+ kappa1 = 20
368
+ else:
369
+ if ep >= 2:
370
+ a1 = 16
371
+ kappa1 = 20
372
+ else:
373
+ a1 = 8*(p-1)/(p-2)
374
+ kappa1 = 10
375
+
376
+ # Next we compute c(1), which has more cases to consider.
377
+
378
+ if p == 2:
379
+ c1 = 160
380
+ elif p == 3:
381
+ if dK == 1:
382
+ c1 = 537
383
+ else:
384
+ c1 = 759
385
+ elif p == 5:
386
+ if ep == 1:
387
+ c1 = 1473
388
+ else:
389
+ c1 = 319
390
+ elif p % 4 == 1:
391
+ if ep == 1:
392
+ c1 = 1473
393
+ else:
394
+ c1 = 1502
395
+ else:
396
+ # p > 5 and p % 4 == 3
397
+ if ep == 1:
398
+ if dK == 1:
399
+ c1 = 1288
400
+ else:
401
+ c1 = 1282
402
+ else:
403
+ c1 = 2190
404
+
405
+ return a1, kappa1, c1
406
+
407
+
408
+ def Yu_condition_115(K, v):
409
+ r"""
410
+ Return ``True`` or ``False``, as the number field ``K`` and the finite place ``v`` satisfy condition (1.15) of [Yu2007]_.
411
+
412
+ INPUT:
413
+
414
+ - ``K`` -- a number field
415
+ - ``v`` -- a finite place of ``K``
416
+
417
+ OUTPUT:
418
+
419
+ ``True`` if (1.15) is satisfied, otherwise ``False``.
420
+
421
+ EXAMPLES::
422
+
423
+ sage: from sage.rings.number_field.S_unit_solver import Yu_condition_115
424
+ sage: x = polygen(ZZ, 'x')
425
+ sage: K.<a> = NumberField(x^2 + 5)
426
+ sage: v2 = K.primes_above(2)[0]
427
+ sage: v11 = K.primes_above(11)[0]
428
+ sage: Yu_condition_115(K, v2)
429
+ False
430
+ sage: Yu_condition_115(K, v11)
431
+ True
432
+
433
+ REFERENCES:
434
+
435
+ - [Yu2007]_ p. 188
436
+ """
437
+
438
+ p = v.smallest_integer()
439
+ f = v.residue_class_degree()
440
+ w = K.number_of_roots_of_unity()
441
+
442
+ # Determine q.
443
+
444
+ if p == 2:
445
+ q = 3
446
+ else:
447
+ q = 2
448
+
449
+ # Check the condition.
450
+
451
+ if q == 2:
452
+ if p**f % 4 == 1:
453
+ return True
454
+ if w % 4 == 0:
455
+ return True
456
+ else:
457
+ if w % 3 == 0:
458
+ return True
459
+
460
+ return False
461
+
462
+
463
+ def Yu_modified_height(mu, n, v, prec=106):
464
+ r"""
465
+ Return the value of h(n)(mu) as appearing in [Yu2007]_ equation (1.21).
466
+
467
+ INPUT:
468
+
469
+ - ``mu`` -- an element of a field K
470
+ - ``n`` -- number of mu_j to be considered in Yu's Theorem
471
+ - ``v`` -- a place of K
472
+ - ``prec`` -- the precision of the real field
473
+
474
+ OUTPUT:
475
+
476
+ The value `h_p(mu)`.
477
+
478
+ EXAMPLES::
479
+
480
+ sage: x = polygen(ZZ, 'x')
481
+ sage: K.<a> = NumberField(x^2 + 5)
482
+ sage: v11 = K.primes_above(11)[0]
483
+ sage: from sage.rings.number_field.S_unit_solver import Yu_modified_height
484
+ sage: Yu_modified_height(a, 3, v11)
485
+ 0.8047189562170501873003796666131
486
+
487
+ If mu is a root of unity, the output is not zero. ::
488
+ sage: Yu_modified_height(-1, 3, v11)
489
+ 0.03425564675426243634374205111379
490
+
491
+ REFERENCES:
492
+
493
+ - [Yu2007]_ p. 192
494
+ """
495
+
496
+ R = RealField(prec)
497
+
498
+ K = v.number_field()
499
+ dK = K.degree()
500
+ p = v.smallest_integer()
501
+ ep = v.ramification_index()
502
+ fp = v.residue_class_degree()
503
+
504
+ a1, kappa1, c1 = Yu_a1_kappa1_c1(p, dK, ep)
505
+
506
+ h0 = mu.global_height(prec)
507
+ h1 = R( fp * R(p).log() / (kappa1 * (n + 4) * dK) )
508
+
509
+ if h0 > h1:
510
+ return h0
511
+ else:
512
+ return h1
513
+
514
+
515
+ def Omega_prime(dK, v, mu_list, prec=106):
516
+ r"""
517
+ Return the constant `\Omega'` appearing in [AKMRVW]_.
518
+
519
+ INPUT:
520
+
521
+ - ``dK`` -- the degree of a number field `K`
522
+ - ``v`` -- a finite place of `K`
523
+ - ``mu_list`` -- list of nonzero elements of `K`. It is assumed that the
524
+ sublist ``mu_list[1:]`` is multiplicatively independent
525
+ - ``prec`` -- the precision of the real field
526
+
527
+ OUTPUT: the constant `\Omega'`
528
+
529
+ EXAMPLES::
530
+
531
+ sage: from sage.rings.number_field.S_unit_solver import mus, Omega_prime
532
+ sage: x = polygen(ZZ, 'x')
533
+ sage: K.<a> = NumberField(x^3 - 3)
534
+ sage: SUK = UnitGroup(K, S=tuple(K.primes_above(6)))
535
+ sage: v = K.primes_above(3)[0]
536
+ sage: mu_list = [-1] + mus(SUK, v)
537
+ sage: dK = K.degree()
538
+ sage: Omega_prime(dK, v, mu_list)
539
+ 0.000487349679922696
540
+
541
+ REFERENCES:
542
+
543
+ - [AKMRVW]_ :arxiv:`1903.00977`
544
+ """
545
+
546
+ R = RealField(prec)
547
+
548
+ omega_prime = R(1)
549
+ for mu in mu_list[1:]:
550
+ omega_prime *= mu.global_height()
551
+
552
+ n = len(mu_list)
553
+
554
+ omega_prime *= Yu_modified_height(mu_list[0], n, v, prec)
555
+
556
+ return omega_prime
557
+
558
+
559
+ def Yu_C1_star(n, v, prec=106):
560
+ r"""
561
+ Return the constant `C_1^*` appearing in [Yu2007]_ (1.23).
562
+
563
+ INPUT:
564
+
565
+ - ``n`` -- the number of generators of a multiplicative subgroup of a field `K`
566
+ - ``v`` -- a finite place of `K` (a fractional ideal)
567
+ - ``prec`` -- the precision of the real field
568
+
569
+ OUTPUT: the constant `C_1^*` as a real number
570
+
571
+ EXAMPLES::
572
+
573
+ sage: x = polygen(ZZ, 'x')
574
+ sage: K.<a> = NumberField(x^2 + 5)
575
+ sage: v11 = K.primes_above(11)[0]
576
+ sage: from sage.rings.number_field.S_unit_solver import Yu_C1_star
577
+ sage: Yu_C1_star(1,v11)
578
+ 2.154667761574516556114215527020e6
579
+
580
+ REFERENCES:
581
+
582
+ - [Yu2007]_ p.189,193
583
+ """
584
+
585
+ R = RealField(prec)
586
+
587
+ K = v.number_field()
588
+ dK = K.absolute_degree()
589
+
590
+ p = v.smallest_integer()
591
+ ep = v.ramification_index()
592
+ fp = v.residue_class_degree()
593
+
594
+ if p == 2:
595
+ q = 3
596
+ else:
597
+ q = 2
598
+
599
+ w = K.number_of_roots_of_unity()
600
+ u = ZZ(w).valuation(q)
601
+
602
+ a_paren_1, kappa1, c_paren_1 = Yu_a1_kappa1_c1(p, dK, ep)
603
+
604
+ C1 = R(1)
605
+ C1 *= c_paren_1
606
+ C1 *= a_paren_1**n
607
+ C1 *= (n**n * (n+1)**(n+1))/factorial(n)
608
+ C1 *= p**fp/(q**u)
609
+ C1 *= ( dK / (fp * R(p).log()) )**(n+2)
610
+ C1 *= R(max( dK, exp(1) )).log()
611
+ C1 *= max( R(exp(4)*(n+1)*dK).log(), ep, fp * R(p).log() )
612
+
613
+ C1_star = R((n+1) * C1)
614
+
615
+ return C1_star
616
+
617
+
618
+ def Yu_bound(SUK, v, prec=106):
619
+ r"""
620
+ Return `c_8` such that `c_8 \geq exp(2)/\log(2)` and `ord_p (\Theta - 1) < c_8 \log B`,
621
+ where `\Theta = \prod_{j=1}^n \alpha_j^{b_j}` and `B \geq \max_j |b_j|` and `B \geq 3`.
622
+
623
+ INPUT:
624
+
625
+ - ``SUK`` -- a group of `S`-units
626
+ - ``v`` -- a finite place of `K` (a fractional ideal)
627
+ - ``prec`` -- the precision of the real field
628
+
629
+ OUTPUT: the constant `c_8` as a real number
630
+
631
+ EXAMPLES::
632
+
633
+ sage: from sage.rings.number_field.S_unit_solver import Yu_bound
634
+ sage: x = polygen(ZZ, 'x')
635
+ sage: K.<a> = NumberField(x^2 + 11)
636
+ sage: SUK = UnitGroup(K, S=tuple(K.primes_above(6)))
637
+ sage: v = K.primes_above(3)[0]
638
+ sage: Yu_bound(SUK, v)
639
+ 9.03984381033128e9
640
+
641
+ REFERENCES:
642
+
643
+ - [Sma1995]_ p. 825
644
+ - [Yu2007]_ p. 189--193 esp. Theorem 1
645
+ - [AKMRVW]_ :arxiv:`1903.00977`
646
+ """
647
+
648
+ # We are using Theorem 1 of "p-adic logarithmic forms and group varieties III" by Kunrui Yu.
649
+
650
+ # We require the assumption of (1.18): B \geq max {|b_1|,...,|b_n|,3}
651
+
652
+ # To be sure that the Lemma of Petho-de Weger is applicable in a later function, we always return a value >= exp(2)/log(2).
653
+
654
+ R = RealField(prec)
655
+ p = v.smallest_integer()
656
+
657
+ K = SUK.number_field()
658
+ dK = K.absolute_degree()
659
+
660
+ mu_free_gens = mus(SUK, v)
661
+ poss_mu0 = possible_mu0s(SUK, v)
662
+ n = 1 + len(mu_free_gens)
663
+
664
+ if Yu_condition_115(K,v):
665
+ largest_Omega_prime = R(0)
666
+ for mu0 in poss_mu0:
667
+ current_Omega_prime = Omega_prime(dK, v, [mu0] + mu_free_gens[:], prec)
668
+ largest_Omega_prime = max( current_Omega_prime, largest_Omega_prime )
669
+ C1star = Yu_C1_star(n, v, prec)
670
+ return max( exp(R(2))/R(2).log(), largest_Omega_prime * C1star)
671
+ else:
672
+ # K and v don't satisfy the theorem hypotheses, and we must move to a quadratic extension L.
673
+ # For justification of this next bound, see [AKMRVW].
674
+ x = polygen(ZZ, 'x')
675
+ if p == 2:
676
+ L_over_K = K.extension(x**2 + x + 1, 'xi0')
677
+ else:
678
+ L_over_K = K.extension(x**2 + 1, 'xi0')
679
+
680
+ # pick any prime vL over v
681
+ vL_0 = L_over_K.primes_above(v)[0]
682
+ e_vL_v = vL_0.relative_ramification_index()
683
+
684
+ # build absolute versions of L and vL
685
+
686
+ L = L_over_K.absolute_field('xi_L')
687
+ vL_gens = tuple( [L(z) for z in vL_0.gens()] )
688
+ vL = L.fractional_ideal( vL_gens )
689
+
690
+ dL = L.degree()
691
+
692
+ largest_Omega_prime = R(0)
693
+ for mu0 in poss_mu0:
694
+ current_Omega_prime = Omega_prime(dL, vL, [mu0] + mu_free_gens[:], prec)
695
+ largest_Omega_prime = max( current_Omega_prime, largest_Omega_prime )
696
+ C1star = Yu_C1_star(n, vL, prec)
697
+ return max(exp(R(2))/R(2).log(), e_vL_v * largest_Omega_prime * C1star)
698
+
699
+
700
+ def K0_func(SUK, A, prec=106):
701
+ r"""
702
+ Return the constant `K_0` from [AKMRVW]_.
703
+
704
+ INPUT:
705
+
706
+ - ``SUK`` -- a group of `S`-units
707
+ - ``A`` -- the set of the products of the coefficients of the `S`-unit equation with each root of unity of `K`
708
+ - ``prec`` -- the precision of the real field (default: 106)
709
+
710
+ OUTPUT: the constant `K_0`, a real number
711
+
712
+ EXAMPLES::
713
+
714
+ sage: from sage.rings.number_field.S_unit_solver import K0_func
715
+ sage: x = polygen(ZZ, 'x')
716
+ sage: K.<a> = NumberField(x^2 + 11)
717
+ sage: SUK = UnitGroup(K, S=tuple(K.primes_above(6)))
718
+ sage: v = K.primes_above(3)[0]
719
+ sage: K0_func(SUK, K.roots_of_unity())
720
+ 8.84763586062272e12
721
+
722
+ REFERENCES:
723
+
724
+ - [Sma1995]_ p. 824
725
+ - [AKMRVW]_ :arxiv:`1903.00977`
726
+ """
727
+ R = RealField(prec)
728
+
729
+ K0 = R(1)
730
+
731
+ c3 = c3_func(SUK, prec)
732
+
733
+ for v_l in SUK.primes():
734
+ e_l = v_l.residue_class_degree()
735
+ Norm_v_l = v_l.absolute_norm()
736
+
737
+ c5_l = c3/(e_l * R(Norm_v_l).log())
738
+
739
+ c8_l = Yu_bound(SUK, v_l, prec)
740
+
741
+ K0_l = (2 * c8_l)/(e_l * c5_l) * R(c8_l / (e_l * c5_l)).log()
742
+
743
+ K0 = max(K0, K0_l)
744
+
745
+ return K0
746
+
747
+
748
+ def c11_func(SUK, v, A, prec=106):
749
+ r"""
750
+ Return the constant `c_{11}` from Smart's TCDF paper, [Sma1995]_.
751
+
752
+ INPUT:
753
+
754
+ - ``SUK`` -- a group of `S`-units
755
+ - ``v`` -- a place of `K`, finite (a fractional ideal) or infinite (element of ``SUK.number_field().places(prec)``)
756
+ - ``A`` -- the set of the product of the coefficients of the `S`-unit equation with each root of unity of `K`
757
+ - ``prec`` -- the precision of the real field (default: 106)
758
+
759
+ OUTPUT: the constant `c_{11}`, a real number
760
+
761
+ EXAMPLES::
762
+
763
+ sage: from sage.rings.number_field.S_unit_solver import c11_func
764
+ sage: x = polygen(ZZ, 'x')
765
+ sage: K.<xi> = NumberField(x^3 - 3)
766
+ sage: SUK = UnitGroup(K, S=tuple(K.primes_above(3)))
767
+ sage: phi_real = K.places()[0]
768
+ sage: phi_complex = K.places()[1]
769
+ sage: A = K.roots_of_unity()
770
+
771
+ sage: c11_func(SUK, phi_real, A) # abs tol 1e-29
772
+ 3.255848343572896153455615423662
773
+
774
+ sage: c11_func(SUK, phi_complex, A) # abs tol 1e-29
775
+ 6.511696687145792306911230847323
776
+
777
+ REFERENCES:
778
+
779
+ - [Sma1995]_ p. 825
780
+ """
781
+ R = RealField(prec)
782
+ if is_real_place(v):
783
+ return R(4*c4_func(SUK, v, A, prec)).log() / c3_func(SUK, prec)
784
+ else:
785
+ return 2*R(4*(c4_func(SUK, v, A, prec)).sqrt()).log() / c3_func(SUK, prec)
786
+
787
+
788
+ def c13_func(SUK, v, prec=106):
789
+ r"""
790
+ Return the constant `c_{13}` from Smart's TCDF paper, [Sma1995]_.
791
+
792
+ INPUT:
793
+
794
+ - ``SUK`` -- a group of `S`-units
795
+ - ``v`` -- an infinite place of ``K`` (element of ``SUK.number_field().places(prec)``)
796
+ - ``prec`` -- the precision of the real field (default: 106)
797
+
798
+ OUTPUT: the constant `c_{13}`, as a real number
799
+
800
+ EXAMPLES::
801
+
802
+ sage: from sage.rings.number_field.S_unit_solver import c13_func
803
+ sage: x = polygen(ZZ, 'x')
804
+ sage: K.<xi> = NumberField(x^3 - 3)
805
+ sage: SUK = UnitGroup(K, S=tuple(K.primes_above(3)))
806
+ sage: phi_real = K.places()[0]
807
+ sage: phi_complex = K.places()[1]
808
+
809
+ sage: c13_func(SUK, phi_real) # abs tol 1e-29
810
+ 0.4257859134798034746197327286726
811
+
812
+ sage: c13_func(SUK, phi_complex) # abs tol 1e-29
813
+ 0.2128929567399017373098663643363
814
+
815
+ It is an error to input a finite place. ::
816
+
817
+ sage: phi_finite = K.primes_above(3)[0]
818
+ sage: c13_func(SUK, phi_finite)
819
+ Traceback (most recent call last):
820
+ ...
821
+ TypeError: Place must be infinite
822
+
823
+
824
+ REFERENCES:
825
+
826
+ - [Sma1995]_ p. 825
827
+ """
828
+ try:
829
+ v.codomain()
830
+ except AttributeError:
831
+ raise TypeError('Place must be infinite')
832
+ if is_real_place(v):
833
+ return c3_func(SUK, prec)
834
+ else:
835
+ return c3_func(SUK, prec)/2
836
+
837
+
838
+ def K1_func(SUK, v, A, prec=106):
839
+ r"""
840
+ Return the constant `K_1` from Smart's TCDF paper, [Sma1995]_.
841
+
842
+ INPUT:
843
+
844
+ - ``SUK`` -- a group of `S`-units
845
+ - ``v`` -- an infinite place of `K` (element of ``SUK.number_field().places(prec)``)
846
+ - ``A`` -- list of all products of each potential `a`, `b` in the `S`-unit
847
+ equation `ax + by + 1 = 0` with each root of unity of `K`
848
+ - ``prec`` -- the precision of the real field (default: 106)
849
+
850
+ OUTPUT: the constant `K_1`, a real number
851
+
852
+ EXAMPLES::
853
+
854
+ sage: from sage.rings.number_field.S_unit_solver import K1_func
855
+ sage: x = polygen(ZZ, 'x')
856
+ sage: K.<xi> = NumberField(x^3 - 3)
857
+ sage: SUK = UnitGroup(K, S=tuple(K.primes_above(3)))
858
+ sage: phi_real = K.places()[0]
859
+ sage: phi_complex = K.places()[1]
860
+ sage: A = K.roots_of_unity()
861
+
862
+ sage: K1_func(SUK, phi_real, A)
863
+ 4.483038368145048508970350163578e16
864
+
865
+ sage: K1_func(SUK, phi_complex, A)
866
+ 2.073346189067285101984136298965e17
867
+
868
+ REFERENCES:
869
+
870
+ - [Sma1995]_ p. 825
871
+ """
872
+ R = RealField(prec)
873
+
874
+ # [Sma1995]_ p. 825
875
+ if is_real_place(v):
876
+ c11 = R(4*c4_func(SUK, v, A, prec)).log() / c3_func(SUK, prec)
877
+ else:
878
+ c11 = 2*( R(4*(c4_func(SUK,v, A, prec)).sqrt()).log() ) / c3_func(SUK, prec)
879
+
880
+ # [Sma1995]_ p. 825
881
+ if is_real_place(v):
882
+ c12 = R(2 * c4_func(SUK, v, A, prec))
883
+ else:
884
+ c12 = R(2 * c4_func(SUK, v, A, prec).sqrt())
885
+
886
+ # [Sma1998]_ p. 225, Theorem A.1
887
+ d = SUK.number_field().degree()
888
+ t = SUK.rank()
889
+ Baker_C = R(18 * factorial(t+2) * (t+1)**(t+2) * (32*d)**(t+3) * R(2*(t+1) * d).log())
890
+
891
+ def hprime(SUK, alpha, v):
892
+ # [Sma1998]_ p. 225
893
+ return R(max(alpha.global_height(), 1/SUK.number_field().degree(), abs( v(alpha).log() ) / SUK.number_field().degree()))
894
+
895
+ # [Sma1995]_ p. 825 and [Sma1998]_ p. 225, Theorem A.1
896
+ c14 = Baker_C * prod([hprime(SUK, alpha, v) for alpha in SUK.gens_values()])
897
+
898
+ # [Sma1995]_ p. 825
899
+ c13 = c13_func(SUK,v,prec)
900
+ w = len(SUK.roots_of_unity())
901
+ c15 = (2/c13)*(c12.log()+c14*(((t+1)*w*c14/c13).log()))
902
+
903
+ return max([c11, c15])
904
+
905
+
906
+ def minimal_vector(A, y, prec=106):
907
+ r"""
908
+ INPUT:
909
+
910
+ - ``A`` -- a square `n` by `n` non-singular integer matrix whose rows generate a lattice `\mathcal L`
911
+ - ``y`` -- a row (1 by `n`) vector with integer coordinates
912
+ - ``prec`` -- precision of real field (default: 106)
913
+
914
+ OUTPUT: a lower bound for the square of
915
+
916
+ .. MATH::
917
+
918
+ \ell (\mathcal L,\vec y) =
919
+ \begin{cases}
920
+ \displaystyle\min_{\vec x\in\mathcal L}\Vert\vec x-\vec y\Vert &, \vec y\not\in\mathcal L. \\
921
+ \displaystyle\min_{0\neq\vec x\in\mathcal L}\Vert\vec x\Vert &,\vec y\in\mathcal L.
922
+ \end{cases}`
923
+
924
+ ALGORITHM:
925
+
926
+ The algorithm is based on V.9 and V.10 of [Sma1998]_
927
+
928
+ EXAMPLES::
929
+
930
+ sage: from sage.rings.number_field.S_unit_solver import minimal_vector
931
+ sage: B = matrix(ZZ, 2, [1,1,1,0])
932
+ sage: y = vector(ZZ, [2,1])
933
+ sage: minimal_vector(B, y)
934
+ 1/2
935
+
936
+ ::
937
+
938
+ sage: B = random_matrix(ZZ, 3)
939
+ sage: while not B.determinant():
940
+ ....: B = random_matrix(ZZ, 3)
941
+ sage: B # random
942
+ [-2 -1 -1]
943
+ [ 1 1 -2]
944
+ [ 6 1 -1]
945
+ sage: y = vector([1, 2, 100])
946
+ sage: minimal_vector(B, y) # random
947
+ 15/28
948
+ """
949
+ if A.is_singular():
950
+ raise ValueError('The matrix A is singular')
951
+
952
+ R = RealField(prec)
953
+
954
+ n = len(y)
955
+ c1 = 2**(n-1)
956
+ ALLL = A.LLL()
957
+ ALLLinv = ALLL.inverse()
958
+ ybrace = [ abs(R(a-a.round())) for a in y * ALLLinv if (a-a.round()) != 0]
959
+
960
+ v = ALLL.rows()[0]
961
+ if len(ybrace) == 0:
962
+ return v.dot_product(v) / c1
963
+ else:
964
+ sigma = ybrace[len(ybrace)-1]
965
+ return v.dot_product(v) * sigma / c1
966
+
967
+
968
+ def reduction_step_complex_case(place, B0, list_of_gens, torsion_gen, c13):
969
+ r"""
970
+ INPUT:
971
+
972
+ - ``place`` -- (ring morphism) an infinite place of a number field `K`
973
+ - ``B0`` -- the initial bound
974
+ - ``list_of_gens`` -- set of generators of the free part of the group
975
+ - ``torsion_gen`` -- an element of the torsion part of the group
976
+ - ``c13`` -- a positive real number
977
+
978
+ OUTPUT: a tuple consisting of:
979
+
980
+ 1. a new upper bound, an integer
981
+ 2. a boolean value, ``True`` if we have to increase precision, otherwise ``False``
982
+
983
+ .. NOTE::
984
+
985
+ The constant ``c13`` in Section 5, [AKMRVW]_
986
+ This function does handle both real and non-real infinite places.
987
+
988
+ REFERENCES:
989
+
990
+ See [Sma1998]_, [AKMRVW]_.
991
+
992
+ EXAMPLES::
993
+
994
+ sage: from sage.rings.number_field.S_unit_solver import reduction_step_complex_case
995
+ sage: x = polygen(ZZ, 'x')
996
+ sage: K.<a> = NumberField([x^3 - 2])
997
+ sage: SK = sum([K.primes_above(p) for p in [2,3,5]],[])
998
+ sage: G = [g for g in K.S_unit_group(S=SK).gens_values()
999
+ ....: if g.multiplicative_order() == Infinity]
1000
+ sage: p1 = K.places(prec=100)[1]
1001
+ sage: reduction_step_complex_case(p1, 10^5, G, -1, 2)
1002
+ (18, False)
1003
+ """
1004
+ prec = place.codomain().precision()
1005
+ R = RealField(prec)
1006
+ CF = ComplexField(prec)
1007
+ n = len(list_of_gens)
1008
+ w = torsion_gen.multiplicative_order()
1009
+
1010
+ real_part_log_gens = [ R(CF(place(g).log()).real_part()) for g in list_of_gens]
1011
+ imag_part_log_gens = [ R(CF(place(g).log()).imag_part()) for g in list_of_gens]
1012
+ real_part_log_gens += [R(0)]
1013
+ imag_part_log_gens += [2*R.pi()/w]
1014
+
1015
+ abs_log_parts = [abs(part) for part in real_part_log_gens]+[abs(part) for part in imag_part_log_gens]
1016
+ max_part_log = max(abs_log_parts)
1017
+
1018
+ npi = []
1019
+ # we collect the list of indices of log(g) which are not pure imaginary
1020
+ # if this list is empty, we have to take a special case
1021
+ for i in range(len(real_part_log_gens)):
1022
+ lg = real_part_log_gens[i]
1023
+ if abs(lg) > 2**(-place.codomain().precision()):
1024
+ npi.append(i)
1025
+ # someday make this a separate function
1026
+ if not npi:
1027
+ # this is the pure imaginary case.
1028
+ # we have only imaginary numbers
1029
+
1030
+ C = ZZ(1)
1031
+ S = n * B0**2
1032
+ T = (n+w+n*w)*B0 / 2
1033
+ finish = False
1034
+ while not finish:
1035
+ A = identity_matrix(ZZ, n+1)
1036
+ A[n] = vector([(g * C).round() for g in imag_part_log_gens])
1037
+
1038
+ if A.is_singular():
1039
+ C = ZZ(2*C)
1040
+ else:
1041
+ # We have to work with rows because of the .LLL() function
1042
+
1043
+ A = A.transpose()
1044
+
1045
+ # Note that l is the lower bound on the square of the magnitude of the shortest nonzero vector in the lattice generated by A
1046
+ l = minimal_vector(A, zero_vector(ZZ,n+1))
1047
+ # Checking hypotheses of Lemma 5.3 in our paper:
1048
+
1049
+ if l <= T**2+S:
1050
+ C = ZZ(2*C)
1051
+ # Need to check precision: must be at least two more than the number of digits in largest entry in A to ensure that we get true rounding--
1052
+ if prec < R(C*max_part_log).log()/R(2).log()+3:
1053
+ return 0, True
1054
+ else:
1055
+ # Need to check precision: must be at least two more than the number of digits in largest entry in A to ensure that we get true rounding--
1056
+ if prec < R(C*max_part_log).log()/R(2).log()+3:
1057
+ return 0, True
1058
+ else:
1059
+ Bnew = ((R(C * 2).log() - ((l**2-S).sqrt()-T)).log() / c13).round()
1060
+ finish = True
1061
+ return max(4,w,Bnew), False
1062
+ elif is_real_place(place):
1063
+ # this is the case when we are working with a real embedding, we get savings here
1064
+ C = R(1)
1065
+ S = (n-1) * B0**2
1066
+ w = place.domain().number_of_roots_of_unity()
1067
+ T = (n*B0+1)/R(2)
1068
+ finish = False
1069
+
1070
+ while not finish:
1071
+
1072
+ A = copy(identity_matrix(ZZ, n+1))
1073
+ # We redefine the imaginary parts in case any generator was negative
1074
+ new_imag_part_log_gens = [0 for i in imag_part_log_gens[:-1]]+[imag_part_log_gens[-1]]
1075
+ A[n-1] = vector([(g*C).round() for g in real_part_log_gens])
1076
+ A[n] = vector([(g*C).round() for g in new_imag_part_log_gens])
1077
+
1078
+ if A.is_singular():
1079
+ C *= 2
1080
+ else:
1081
+ # We apply Lemma 5.3 from [AKMRVW]
1082
+ A = A.transpose()
1083
+ l = minimal_vector(A, zero_vector(ZZ,n+1))
1084
+ # Note that l is the a lower bound on the square of the magnitude of the shortest nonzero vector in the lattice generated by A
1085
+ # Checking hypothesis of lemma 5.3 in [AKMRVW]
1086
+ if l <= T**2 + S:
1087
+ C *= 2
1088
+ # Need to check precision: must be at least two more than the number of digits in largest entry in A to ensure that we get true rounding--
1089
+ if prec < R(C*max_part_log).log()/R(2).log()+3:
1090
+ return 0, True
1091
+ else:
1092
+ # Need to check precision: must be at least two more than the number of digits in largest entry in A to ensure that we get true rounding--
1093
+ if prec < R(C*max_part_log).log()/R(2).log()+3:
1094
+ return 0, True
1095
+ else:
1096
+ Bnew = ((R(C * 2).log() - ((l-S).sqrt()-T).log()) / c13).round()
1097
+ finish = True
1098
+ return max(4,w,Bnew), False
1099
+
1100
+ else:
1101
+
1102
+ # the case when the real part is not 0 for all log(a_i), see Lemma 5.2 in [AKMRVW]
1103
+ C = R(1)
1104
+ S = (n-1) * B0**2
1105
+ w = place.domain().number_of_roots_of_unity()
1106
+ T = (n+w+n*w)*B0/R(2).sqrt()
1107
+ finish = False
1108
+
1109
+ # we reorder the generators to that the real part of the last non-torsion generator is not 0:
1110
+ if n-1 not in npi:
1111
+ new_last_gen_index = npi[0]
1112
+ old_last_gen_real = real_part_log_gens[n-1]
1113
+ old_last_gen_imag = imag_part_log_gens[n-1]
1114
+ real_part_log_gens[n-1] = real_part_log_gens[new_last_gen_index]
1115
+ imag_part_log_gens[n-1] = imag_part_log_gens[new_last_gen_index]
1116
+ real_part_log_gens[new_last_gen_index] = old_last_gen_real
1117
+ imag_part_log_gens[new_last_gen_index] = old_last_gen_imag
1118
+
1119
+ while not finish:
1120
+
1121
+ A = copy(identity_matrix(ZZ, n+1))
1122
+ A[n-1] = vector([(g*C).round() for g in real_part_log_gens])
1123
+ A[n] = vector([(g*C).round() for g in imag_part_log_gens])
1124
+
1125
+ if A.is_singular():
1126
+ C *= 2
1127
+ else:
1128
+ # We apply Lemma 5.2 from [AKMRVW]
1129
+ A = A.transpose()
1130
+ l = minimal_vector(A, zero_vector(ZZ,n+1))
1131
+ # Note that l is the a lower bound on the square of the magnitude of the shortest nonzero vector in the lattice generated by A
1132
+ # Checking hypothesis of lemma 5.2 in [AKMRVW]
1133
+ if l <= T**2 + S:
1134
+ C *= 2
1135
+ # Need to check precision: must be at least two more than the number of digits in largest entry in A to ensure that we get true rounding--
1136
+ if prec < R(C*max_part_log).log()/R(2).log()+3:
1137
+ return 0, True
1138
+ else:
1139
+ # Need to check precision: must be at least two more than the number of digits in largest entry in A to ensure that we get true rounding--
1140
+ if prec < R(C*max_part_log).log()/R(2).log()+3:
1141
+ return 0, True
1142
+ else:
1143
+ Bnew = ((R(C * 2).log() - ((l-S).sqrt()-T).log()) / c13).round()
1144
+ finish = True
1145
+ return max(4,w,Bnew), False
1146
+
1147
+
1148
+ def cx_LLL_bound(SUK, A, prec=106):
1149
+ r"""
1150
+ Return the maximum of all of the `K_1`'s as they are LLL-optimized for each infinite place `v`.
1151
+
1152
+ INPUT:
1153
+
1154
+ - ``SUK`` -- a group of `S`-units
1155
+ - ``A`` -- list of all products of each potential `a`, `b` in the `S`-unit
1156
+ equation `ax + by + 1 = 0` with each root of unity of `K`
1157
+ - ``prec`` -- precision of real field (default: 106)
1158
+
1159
+ OUTPUT: a bound for the exponents at the infinite place, as a real number
1160
+
1161
+ EXAMPLES::
1162
+
1163
+ sage: from sage.rings.number_field.S_unit_solver import cx_LLL_bound
1164
+ sage: x = polygen(ZZ, 'x')
1165
+ sage: K.<xi> = NumberField(x^3 - 3)
1166
+ sage: SUK = UnitGroup(K, S=tuple(K.primes_above(3)))
1167
+ sage: A = K.roots_of_unity()
1168
+
1169
+ sage: cx_LLL_bound(SUK, A) # long time
1170
+ 35
1171
+ """
1172
+ cx_LLL = 0
1173
+ # initialize a bound, a bad guess, as we iterate over the places of the number field, we will replace its value with the largest complex LLL bound we've found across the places
1174
+ for v in SUK.number_field().places(prec=prec):
1175
+ prec_v = prec
1176
+ c13_LLL = c13_func(SUK, v, prec_v)
1177
+ cx_bound = K1_func(SUK, v, A, prec_v)
1178
+ # cx_bound is the LLL bound according to this place, it will be replaced as LLL gives us smaller bounds
1179
+ new_bound, inc_prec = reduction_step_complex_case(v, cx_bound, SUK.fundamental_units(), SUK.zeta(), c13_LLL)
1180
+ while inc_prec:
1181
+ v = refine_embedding(v)
1182
+ c13_LLL = c13_func(SUK, v, prec_v)
1183
+ cx_bound = K1_func(SUK, v, A, prec_v)
1184
+ new_bound, inc_prec = reduction_step_complex_case(v, cx_bound, SUK.fundamental_units(), SUK.zeta(), c13_LLL)
1185
+ counter = 0
1186
+ while abs(cx_bound - new_bound) > .5*cx_bound and counter < 15:
1187
+ # We fear a loop that is not convergent, this is the purpose of the counter
1188
+ # Repeat complex LLL until we get essentially no change from it
1189
+ cx_bound = min(cx_bound, new_bound)
1190
+ new_bound, inc_prec = reduction_step_complex_case(v, cx_bound, SUK.fundamental_units(), SUK.zeta(), c13_LLL)
1191
+ while inc_prec:
1192
+ v = refine_embedding(v)
1193
+ c13_LLL = c13_func(SUK, v, prec_v)
1194
+ new_bound, inc_prec = reduction_step_complex_case(v, cx_bound, SUK.fundamental_units(), SUK.zeta(), c13_LLL)
1195
+ counter += 1
1196
+
1197
+ cx_bound = min(cx_bound, new_bound)
1198
+ # for this place the complex LLL bound is cx_bound
1199
+ cx_LLL = max(cx_bound, cx_LLL)
1200
+ # compare this value with the complex LLL bounds we have found for the previous places, if it is bigger, replace that bound
1201
+ return cx_LLL
1202
+
1203
+
1204
+ def log_p(a, prime, prec):
1205
+ r"""
1206
+ INPUT:
1207
+
1208
+ - ``a`` -- an element of a number field `K`
1209
+ - ``prime`` -- a prime ideal of the number field `K`
1210
+ - ``prec`` -- positive integer
1211
+
1212
+ OUTPUT:
1213
+
1214
+ An element of `K` which is congruent to the ``prime``-adic logarithm of `a`
1215
+ with respect to ``prime`` modulo ``p^prec``, where `p` is the rational
1216
+ prime below ``prime``.
1217
+
1218
+ .. NOTE::
1219
+
1220
+ Here we take into account the other primes in `K` above `p` in order to
1221
+ get coefficients with small values.
1222
+
1223
+ EXAMPLES::
1224
+
1225
+ sage: from sage.rings.number_field.S_unit_solver import log_p
1226
+ sage: x = polygen(ZZ, 'x')
1227
+ sage: K.<a> = NumberField(x^2 + 14)
1228
+ sage: p1 = K.primes_above(3)[0]
1229
+ sage: p1
1230
+ Fractional ideal (3, a + 1)
1231
+ sage: log_p(a+2, p1, 20)
1232
+ 8255385638/3*a + 15567609440/3
1233
+
1234
+ ::
1235
+
1236
+ sage: K.<a> = NumberField(x^4 + 14)
1237
+ sage: p1 = K.primes_above(5)[0]
1238
+ sage: p1
1239
+ Fractional ideal (5, a + 1)
1240
+ sage: log_p(1/(a^2-4), p1, 30)
1241
+ -42392683853751591352946/25*a^3 - 113099841599709611260219/25*a^2 -
1242
+ 8496494127064033599196/5*a - 18774052619501226990432/25
1243
+ """
1244
+ if a == 0:
1245
+ raise ValueError('a is the zero element')
1246
+
1247
+ if a.valuation(prime) != 0:
1248
+ raise ValueError('The valuation of a with respect to prime is not zero')
1249
+
1250
+ K = prime.ring()
1251
+ p = prime.smallest_integer()
1252
+
1253
+ # In order to get an approximation with small coefficients we have to take into account the other primes above p
1254
+ # with negative valuation. For example, say prime2 is another (principal ideal) prime above p, and a=(unit)(prime2)^(-k) for some unit and k
1255
+ # a positive integer, and let tilde(a):=a(prime2)^k. Then log_p(a)=log_p(tilde(a))-k(log_p(prime2)), where the series representations
1256
+ # of these two logs will have smaller coefficients.
1257
+
1258
+ primes = [(-(a.valuation(pr)),pr) for pr in K.primes_above(p) if a.valuation(pr) < 0]
1259
+ local_terms = []
1260
+
1261
+ for (val, pr) in primes:
1262
+ # for its pair in primes we find an element in K such that it is divisible only by pr and not by any other ideal above p. Then we take this element in the correct exponent
1263
+
1264
+ if pr.is_principal():
1265
+ local_terms.append(pr.gens_reduced()[0]**val)
1266
+ else:
1267
+ local_terms.append(pr.gens()[1]**val)
1268
+
1269
+ return log_p_series_part(a*prod(local_terms), prime, prec) - sum([log_p_series_part(b, prime, prec) for b in local_terms])
1270
+
1271
+
1272
+ def log_p_series_part(a, prime, prec):
1273
+ r"""
1274
+ INPUT:
1275
+
1276
+ - ``a`` -- an element of a number field `K`
1277
+ - ``prime`` -- a prime ideal of the number field `K`
1278
+ - ``prec`` -- positive integer
1279
+
1280
+ OUTPUT:
1281
+
1282
+ The ``prime``-adic logarithm of `a` and accuracy ``p^prec``, where `p` is
1283
+ the rational prime below ``prime``.
1284
+
1285
+ ALGORITHM:
1286
+
1287
+ The algorithm is based on the algorithm on page 30 of [Sma1998]_.
1288
+
1289
+ EXAMPLES::
1290
+
1291
+ sage: from sage.rings.number_field.S_unit_solver import log_p_series_part
1292
+ sage: x = polygen(ZZ, 'x')
1293
+ sage: K.<a> = NumberField(x^2 - 5)
1294
+ sage: p1 = K.primes_above(3)[0]
1295
+ sage: p1
1296
+ Fractional ideal (3)
1297
+ sage: log_p_series_part(a^2 - a + 1, p1, 30)
1298
+ 120042736778562*a + 263389019530092
1299
+
1300
+ ::
1301
+
1302
+ sage: K.<a> = NumberField(x^4 + 14)
1303
+ sage: p1 = K.primes_above(5)[0]
1304
+ sage: p1
1305
+ Fractional ideal (5, a + 1)
1306
+ sage: log_p_series_part(1/(a^2-4), p1, 30)
1307
+ 5628940883264585369224688048459896543498793204839654215019548600621221950915106576555819252366183605504671859902129729380543157757424169844382836287443485157589362653561119898762509175000557196963413830027960725069496503331353532893643983455103456070939403472988282153160667807627271637196608813155377280943180966078/1846595723557147156151786152499366687569722744011302407020455809280594038056223852568951718462474153951672335866715654153523843955513167531739386582686114545823305161128297234887329119860255600972561534713008376312342295724191173957260256352612807316114669486939448006523889489471912384033203125*a^2 + 2351432413692022254066438266577100183514828004415905040437326602004946930635942233146528817325416948515797296867947688356616798913401046136899081536181084767344346480810627200495531180794326634382675252631839139904967037478184840941275812058242995052383261849064340050686841429735092777331963400618255005895650200107/1846595723557147156151786152499366687569722744011302407020455809280594038056223852568951718462474153951672335866715654153523843955513167531739386582686114545823305161128297234887329119860255600972561534713008376312342295724191173957260256352612807316114669486939448006523889489471912384033203125
1308
+ """
1309
+ if a.valuation(prime) != 0:
1310
+ raise ValueError('The valuation of a with respect to prime is not zero')
1311
+ K = prime.ring()
1312
+ g = K.gen()
1313
+ p = prime.smallest_integer()
1314
+ f = prime.residue_class_degree()
1315
+ e = prime.absolute_ramification_index()
1316
+ q = p**f - 1
1317
+ R = RealField(prec)
1318
+
1319
+ divisor = q.divisors()
1320
+ order = min(d for d in divisor if (a**d - 1).valuation(prime) > 0)
1321
+ gamma = a**order
1322
+ t = 0
1323
+ while (gamma-1).valuation(prime) <= e:
1324
+ t += 1
1325
+ gamma = gamma**p
1326
+ prec += t
1327
+ # since later we divide by p^t, we must increase the precision by t at this point.
1328
+ m = (gamma-1).valuation(prime) / e
1329
+ n = Integer(1)
1330
+ step = 10 ** (R(prec).log()/R(10).log()).floor()
1331
+ while n < (R(n).log()/R(p).log() + prec)/m:
1332
+ n += step
1333
+ # could use smaller stepsize to get actual smallest integer n, however this seems to run faster.
1334
+ w = (R(prec).log()/R(p).log()).floor()
1335
+ gamma = sum([ZZ(gi % (p**(prec+w))) * g**i
1336
+ if gi.valuation(p) >= 0 else
1337
+ ZZ((gi * p**(-gi.valuation(p))) % (p**(prec+w-gi.valuation(p)))) * p**(gi.valuation(p)) * g**i
1338
+ for i,gi in enumerate(gamma) if gi != 0])
1339
+
1340
+ beta = 0
1341
+ delta = 1 - gamma
1342
+ for i in range(1, n+1):
1343
+ beta -= delta / i
1344
+ delta *= (1 - gamma)
1345
+ delta = sum([ZZ(di % (p**(prec+w))) * g**b
1346
+ if di.valuation(p) >= 0 else
1347
+ ZZ((di * p**(-di.valuation(p))) % (p**(prec + w - di.valuation(p)))) * p**(di.valuation(p)) * g**b
1348
+ for b,di in enumerate(delta) if di != 0])
1349
+ beta = beta / (order * p**t)
1350
+
1351
+ # we try to make the coefficients small
1352
+
1353
+ logp = 0
1354
+ for i,b in enumerate(beta.list()):
1355
+ val = b.valuation(p)
1356
+ if val < 0:
1357
+ t = b * p**(-val)
1358
+ t = ZZ(mod(t, p**(prec-val)))
1359
+ t = t * p**val
1360
+ else:
1361
+ t = ZZ(mod(b, p**prec))
1362
+ logp = logp + t * g**i
1363
+
1364
+ return logp
1365
+
1366
+
1367
+ def defining_polynomial_for_Kp(prime, prec=106):
1368
+ r"""
1369
+ INPUT:
1370
+
1371
+ - ``prime`` -- a prime ideal of a number field `K`
1372
+ - ``prec`` -- a positive natural number (default: 106)
1373
+
1374
+ OUTPUT: a polynomial with integer coefficients that is equivalent ``mod p^prec`` to a defining polynomial for the completion of `K` associated to the specified prime
1375
+
1376
+ .. NOTE::
1377
+
1378
+ `K` has to be an absolute extension
1379
+
1380
+ EXAMPLES::
1381
+
1382
+ sage: from sage.rings.number_field.S_unit_solver import defining_polynomial_for_Kp
1383
+ sage: K.<a> = QuadraticField(2)
1384
+ sage: p2 = K.prime_above(7); p2
1385
+ Fractional ideal (2*a - 1)
1386
+ sage: defining_polynomial_for_Kp(p2, 10)
1387
+ x + 266983762
1388
+
1389
+ ::
1390
+
1391
+ sage: K.<a> = QuadraticField(-6)
1392
+ sage: p2 = K.prime_above(2); p2
1393
+ Fractional ideal (2, a)
1394
+ sage: defining_polynomial_for_Kp(p2, 100)
1395
+ x^2 + 6
1396
+ sage: p5 = K.prime_above(5); p5
1397
+ Fractional ideal (5, a + 2)
1398
+ sage: defining_polynomial_for_Kp(p5, 100)
1399
+ x + 3408332191958133385114942613351834100964285496304040728906961917542037
1400
+ """
1401
+ K = prime.ring()
1402
+ if not K.is_absolute():
1403
+ raise ValueError('The number field is not an absolute extension')
1404
+
1405
+ theta = K.gen()
1406
+ f = K.defining_polynomial()
1407
+ p = prime.smallest_integer()
1408
+ e = prime.absolute_ramification_index()
1409
+
1410
+ N = prec
1411
+ while True:
1412
+ RQp = Qp(p, prec=N, type='capped-rel', print_mode='series')
1413
+
1414
+ # We factor f in Integers(p**(precision)) using the factorization in Qp
1415
+
1416
+ factors = f.change_ring(RQp).factor()
1417
+
1418
+ # We are going to find which factor of f is related to the prime ideal 'prime'
1419
+
1420
+ L = [g.change_ring(ZZ) for g, _ in factors]
1421
+ A = [g for g in L if (g(theta)).valuation(prime) >= e*N/2]
1422
+
1423
+ # We narrow down the list until only one value remains
1424
+
1425
+ if len(A) == 1:
1426
+ return A[0].change_ring(Integers(p**prec)).change_ring(ZZ)
1427
+ else:
1428
+ N += 1
1429
+
1430
+
1431
+ def embedding_to_Kp(a, prime, prec):
1432
+ r"""
1433
+ INPUT:
1434
+
1435
+ - ``a`` -- an element of a number field `K`
1436
+ - ``prime`` -- a prime ideal of `K`
1437
+ - ``prec`` -- a positive natural number
1438
+
1439
+ OUTPUT:
1440
+
1441
+ An element of `K` that is equivalent to `a` modulo ``p^(prec)`` and the generator of `K` appears with exponent less than `e \cdot f`, where `p` is the rational prime below ``prime`` and `e`, `f` are the ramification index and residue degree, respectively.
1442
+
1443
+ .. NOTE::
1444
+
1445
+ `K` has to be an absolute number field
1446
+
1447
+ EXAMPLES::
1448
+
1449
+ sage: from sage.rings.number_field.S_unit_solver import embedding_to_Kp
1450
+ sage: K.<a> = QuadraticField(17)
1451
+ sage: p = K.prime_above(13); p
1452
+ Fractional ideal (a - 2)
1453
+ sage: embedding_to_Kp(a-3, p, 15)
1454
+ -20542890112375827
1455
+
1456
+ ::
1457
+
1458
+ sage: x = polygen(ZZ, 'x')
1459
+ sage: K.<a> = NumberField(x^4 - 2)
1460
+ sage: p = K.prime_above(7); p
1461
+ Fractional ideal (-a^2 + a - 1)
1462
+ sage: embedding_to_Kp(a^3 - 3, p, 15)
1463
+ -1261985118949117459462968282807202378
1464
+ """
1465
+ K = prime.ring()
1466
+ if not K.is_absolute():
1467
+ raise ValueError('K has to be an absolute extension')
1468
+
1469
+ g = defining_polynomial_for_Kp(prime, prec).change_ring(QQ)
1470
+ gen = K.gen()
1471
+ f = K(a).lift()
1472
+
1473
+ return K( sum([b*gen**j for j,b in enumerate(f.mod(g))]) )
1474
+
1475
+
1476
+ def p_adic_LLL_bound_one_prime(prime, B0, M, M_logp, m0, c3, prec=106):
1477
+ r"""
1478
+ INPUT:
1479
+
1480
+ - ``prime`` -- a prime ideal of a number field `K`
1481
+ - ``B0`` -- the initial bound
1482
+ - ``M`` -- list of elements of `K`, the `\mu_i`'s from Lemma IX.3 of [Sma1998]_
1483
+ - ``M_logp`` -- the `p`-adic logarithm of elements in `M`
1484
+ - ``m0`` -- an element of `K`, this is `\mu_0` from Lemma IX.3 of [Sma1998]_
1485
+ - ``c3`` -- a positive real constant
1486
+ - ``prec`` -- the precision of the calculations (default: 106), i.e.,
1487
+ values are known to ``O(p^prec)``
1488
+
1489
+ OUTPUT: a pair consisting of:
1490
+
1491
+ 1. a new upper bound, an integer
1492
+ 2. a boolean value, ``True`` if we have to increase precision, otherwise ``False``
1493
+
1494
+ .. NOTE::
1495
+
1496
+ The constant `c_5` is the constant `c_5` at the page 89 of [Sma1998]_ which is equal to the constant `c_{10}` at the page 139 of [Sma1995]_.
1497
+ In this function, the `c_i` constants are in line with [Sma1998]_, but generally differ from the constants in [Sma1995]_ and other parts of this code.
1498
+
1499
+ EXAMPLES:
1500
+
1501
+ This example indicates a case where we must increase precision::
1502
+
1503
+ sage: from sage.rings.number_field.S_unit_solver import p_adic_LLL_bound_one_prime
1504
+ sage: prec = 50
1505
+ sage: x = polygen(ZZ, 'x')
1506
+ sage: K.<a> = NumberField(x^3 - 3)
1507
+ sage: S = tuple(K.primes_above(3))
1508
+ sage: SUK = UnitGroup(K, S=S)
1509
+ sage: v = S[0]
1510
+ sage: A = SUK.roots_of_unity()
1511
+ sage: K0_old = 9.4755766731093e17
1512
+ sage: Mus = [a^2 - 2]
1513
+ sage: Log_p_Mus = [185056824593551109742400*a^2
1514
+ ....: + 1389583284398773572269676*a + 717897987691852588770249]
1515
+ sage: mu0 = K(-1)
1516
+ sage: c3_value = 0.42578591347980
1517
+ sage: m0_Kv_new, increase_prec = p_adic_LLL_bound_one_prime(v, K0_old, Mus, Log_p_Mus,
1518
+ ....: mu0, c3_value, prec)
1519
+ sage: m0_Kv_new
1520
+ 0
1521
+ sage: increase_prec
1522
+ True
1523
+
1524
+ And now we increase the precision to make it all work::
1525
+
1526
+ sage: prec = 106
1527
+ sage: K0_old = 9.475576673109275443280257946930e17
1528
+ sage: Log_p_Mus = [1029563604390986737334686387890424583658678662701816*a^2
1529
+ ....: + 661450700156368458475507052066889190195530948403866*a]
1530
+ sage: c3_value = 0.4257859134798034746197327286726
1531
+ sage: m0_Kv_new, increase_prec = p_adic_LLL_bound_one_prime(v, K0_old, Mus, Log_p_Mus,
1532
+ ....: mu0, c3_value, prec)
1533
+ sage: m0_Kv_new
1534
+ 476
1535
+ sage: increase_prec
1536
+ False
1537
+ """
1538
+ if any(g.valuation(prime) != 0 for g in M+[m0]):
1539
+ raise ValueError('There is an element with nonzero valuation')
1540
+
1541
+ K = prime.ring()
1542
+ w = K.number_of_roots_of_unity()
1543
+ p = prime.smallest_integer()
1544
+ f = prime.residue_class_degree()
1545
+ e = prime.absolute_ramification_index()
1546
+ R = RealField(prec)
1547
+ c5 = c3 / (f*e*R(p).log())
1548
+ theta = K.gen()
1549
+
1550
+ # if M is empty then it is easy to give an upper bound
1551
+ if len(M) == 0:
1552
+ if m0 != 1:
1553
+ return max(4,w, R(max(R(p).log()*f*(m0-1).valuation(prime)/c3, 0)).floor()), False
1554
+ else:
1555
+ return 0, False
1556
+ # we evaluate the p-adic logarithms of m0 and we embed it in the completion of K with respect to prime
1557
+
1558
+ m0_logp = log_p(m0, prime, prec)
1559
+ m0_logp = embedding_to_Kp(m0_logp, prime, prec)
1560
+ n = len(M_logp)
1561
+ # Below we implement paragraph VI.4.2 of [Sma1998], pages 89-93
1562
+
1563
+ # we evaluate the order of discriminant of theta
1564
+
1565
+ Theta = [theta**i for i in range(K.absolute_degree())]
1566
+ ordp_Disc = (K.disc(Theta)).valuation(p)
1567
+ # We evaluate Lambda
1568
+
1569
+ c8 = min(min(a.valuation(p) for a in g) for g in M_logp)
1570
+ lam = p**c8
1571
+
1572
+ # we apply lemma VI.5 of [Sma1998] page 90
1573
+ # c6 is 0 here because we seek to solve the equation x+y=1, so our set A
1574
+ # is contained in the roots of unity of K
1575
+
1576
+ # In one very extreme case (p = 2 and all other constants as small as possible),
1577
+ # low_bound = 1/c5 is not quite enough to give strict inequality. So we add 1 to be safe.
1578
+
1579
+ low_bound = (1/c5).round() + 1
1580
+ for a in m0_logp:
1581
+ if a != 0 and c8 > a.valuation(p):
1582
+ B1 = (c8 + ordp_Disc/2) / c5
1583
+ if B1 > low_bound:
1584
+ return max(4,w,RR(B1).floor()), False
1585
+ else:
1586
+ return max(4,w,low_bound), False
1587
+
1588
+ c8 = min([a.valuation(p) for a in m0_logp] + [c8])
1589
+ B = [g/lam for g in M_logp]
1590
+ b0 = m0_logp / lam
1591
+ c9 = c8 + ordp_Disc/2
1592
+
1593
+ # We evaluate 'u' and we construct the matrix A
1594
+
1595
+ m = e * f
1596
+ u = 1
1597
+ while True:
1598
+ if prec <= u + c8:
1599
+ return 0, True
1600
+
1601
+ # We construct the matrix A as a block matrix
1602
+
1603
+ A11 = identity_matrix(ZZ, n)
1604
+ A12 = zero_matrix(ZZ, n, m)
1605
+ A21 = zero_matrix(ZZ, n, m)
1606
+ A22 = p**u * identity_matrix(ZZ, m)
1607
+ for i,b in enumerate(B):
1608
+ A21[i] = vector([mod(b[j],p**u) for j in range(m)])
1609
+ A = block_matrix( [[A11,A12], [A21.transpose(),A22]] )
1610
+
1611
+ y = zero_vector(ZZ, n+m)
1612
+ for i in range(m):
1613
+ y[i+n] = -mod(b0[i], p**u)
1614
+ # This refers to c10 from Smart
1615
+ c10squared = minimal_vector(A.transpose(), y)
1616
+ if c10squared > n * B0**2:
1617
+ B2 = (u+c9) / c5
1618
+ if B2 > low_bound:
1619
+ return max(4,w,R(B2).floor()),False
1620
+ else:
1621
+ return max(4,w,low_bound),False
1622
+ else:
1623
+ u += 1
1624
+
1625
+
1626
+ def p_adic_LLL_bound(SUK, A, prec=106):
1627
+ r"""
1628
+ Return the maximum of all of the `K_0`'s as they are LLL-optimized for each
1629
+ finite place `v`.
1630
+
1631
+ INPUT:
1632
+
1633
+ - ``SUK`` -- a group of `S`-units
1634
+ - ``A`` -- list of all products of each potential `a`, `b` in the `S`-unit
1635
+ equation `ax + by + 1 = 0` with each root of unity of `K`
1636
+ - ``prec`` -- precision for `p`-adic LLL calculations (default: 106)
1637
+
1638
+ OUTPUT:
1639
+
1640
+ A bound for the max of exponents in the case that extremal place is finite (see [Sma1995]_) as a real number
1641
+
1642
+ EXAMPLES::
1643
+
1644
+ sage: from sage.rings.number_field.S_unit_solver import p_adic_LLL_bound
1645
+ sage: x = polygen(ZZ, 'x')
1646
+ sage: K.<xi> = NumberField(x^3 - 3)
1647
+ sage: SUK = UnitGroup(K, S=tuple(K.primes_above(3)))
1648
+ sage: A = SUK.roots_of_unity()
1649
+ sage: prec = 100
1650
+ sage: p_adic_LLL_bound(SUK, A, prec)
1651
+ 89
1652
+ """
1653
+ S = SUK.primes()
1654
+ K0_old = K0_func(SUK, A, prec)
1655
+ LLL_K0_by_finite_place = []
1656
+ for i,v in enumerate(S):
1657
+ # Kv_old = K0_by_finite_place[0]
1658
+ Mus0 = possible_mu0s(SUK, v)
1659
+ Mus = mus(SUK, v)
1660
+ Log_p_Mus = [log_p(a, v, prec) for a in Mus]
1661
+ local_prec = prec
1662
+ val = 0
1663
+ for m0 in Mus0:
1664
+ m0_Kv_old = K0_old
1665
+ m0_Kv_new, increase_precision = p_adic_LLL_bound_one_prime(v, m0_Kv_old, Mus, Log_p_Mus, m0, c3_func(SUK, local_prec), local_prec)
1666
+ while increase_precision:
1667
+ local_prec *= 2
1668
+ Log_p_Mus = [log_p(a, v, local_prec) for a in Mus]
1669
+ Log_p_Mus = [embedding_to_Kp(a, v, local_prec) for a in Log_p_Mus]
1670
+ m0_Kv_new, increase_precision = p_adic_LLL_bound_one_prime(v, m0_Kv_old, Mus, Log_p_Mus, m0, c3_func(SUK, local_prec), local_prec)
1671
+
1672
+ while m0_Kv_new < m0_Kv_old:
1673
+ m0_Kv_old = m0_Kv_new
1674
+ m0_Kv_new, increase_precision = p_adic_LLL_bound_one_prime(v, m0_Kv_old, Mus, Log_p_Mus, m0, c3_func(SUK,local_prec), local_prec)
1675
+ while increase_precision:
1676
+ local_prec *= 2
1677
+ Log_p_Mus = [log_p(a, v, local_prec) for a in Mus]
1678
+ Log_p_Mus = [embedding_to_Kp(a, v, local_prec) for a in Log_p_Mus]
1679
+ m0_Kv_new,increase_precision = p_adic_LLL_bound_one_prime(v, m0_Kv_old, Mus, Log_p_Mus, m0, c3_func(SUK, local_prec), local_prec)
1680
+
1681
+ val = max(m0_Kv_old, val)
1682
+
1683
+ LLL_K0_by_finite_place.append(val)
1684
+ return max(LLL_K0_by_finite_place)
1685
+
1686
+
1687
+ def split_primes_large_lcm(SUK, bound):
1688
+ r"""
1689
+ Return a list `L` of rational primes `q` which split completely in `K` and
1690
+ which have desirable properties (see NOTE).
1691
+
1692
+ INPUT:
1693
+
1694
+ - ``SUK`` -- the `S`-unit group of an absolute number field `K`
1695
+ - ``bound`` -- positive integer
1696
+
1697
+ OUTPUT: list `L` of rational primes `q`, with the following properties:
1698
+
1699
+ - each prime `q` in `L` splits completely in `K`
1700
+ - if `Q` is a prime in `S` and `q` is the rational
1701
+ prime below `Q`, then `q` is **not** in `L`
1702
+ - the value `\lcm(\{ q-1 : q \in L \})` is greater than or equal to ``2*bound + 1``.
1703
+
1704
+ .. NOTE::
1705
+
1706
+ - A series of compatible exponent vectors for the primes in `L` will
1707
+ lift to **at most** one integer exponent vector whose entries
1708
+ `a_i` satisfy `|a_i|` is less than or equal to ``bound``.
1709
+
1710
+ - The ordering of this set is not very intelligent for the purposes
1711
+ of the later sieving processes.
1712
+
1713
+ EXAMPLES::
1714
+
1715
+ sage: from sage.rings.number_field.S_unit_solver import split_primes_large_lcm
1716
+ sage: x = polygen(ZZ, 'x')
1717
+ sage: K.<xi> = NumberField(x^3 - 3*x + 1)
1718
+ sage: S = K.primes_above(3)
1719
+ sage: SUK = UnitGroup(K, S=tuple(S))
1720
+ sage: split_primes_large_lcm(SUK, 200)
1721
+ [17, 19, 37, 53]
1722
+
1723
+ With a tiny bound, Sage may ask you to increase the bound.
1724
+
1725
+ ::
1726
+
1727
+ sage: from sage.rings.number_field.S_unit_solver import split_primes_large_lcm
1728
+ sage: K.<xi> = NumberField(x^2 + 163)
1729
+ sage: SUK = UnitGroup(K, S=tuple(K.primes_above(23)))
1730
+ sage: split_primes_large_lcm(SUK, 8)
1731
+ Traceback (most recent call last):
1732
+ ...
1733
+ ValueError: Not enough split primes found. Increase bound.
1734
+ """
1735
+
1736
+ K = SUK.number_field()
1737
+ # we recover the rational primes below S:
1738
+ S0 = set(prime_ideal.smallest_integer() for prime_ideal in SUK.primes())
1739
+ split_prime_list = K.completely_split_primes(4*bound + 4)
1740
+ lcm_list = []
1741
+ L = 1
1742
+ while L < 2*bound + 1:
1743
+ if split_prime_list == []:
1744
+ # Need More Primes!
1745
+ raise ValueError('Not enough split primes found. Increase bound.')
1746
+ q = split_prime_list.pop(0)
1747
+ # only use q if it is *not* below a prime in S -- that is,
1748
+ # only if q does *not* appear in S0.
1749
+ if q not in S0:
1750
+ L = lcm(L, q-1)
1751
+ lcm_list.append(q)
1752
+ return lcm_list
1753
+
1754
+
1755
+ def sieve_ordering(SUK, q):
1756
+ r"""
1757
+ Return ordered data for running sieve on the primes in ``SUK`` over the rational prime `q`.
1758
+
1759
+ INPUT:
1760
+
1761
+ - ``SUK`` -- the `S`-unit group of a number field `K`
1762
+ - ``q`` -- a rational prime number which splits completely in `K`
1763
+
1764
+ OUTPUT: list of tuples;
1765
+ ``[ideals_over_q, residue_fields, rho_images, product_rho_orders]``, where:
1766
+
1767
+ 1. ``ideals_over_q`` is a list of the `d = [K:\QQ]` ideals in `K` over `q`
1768
+ 2. ``residue_fields[i]`` is the residue field of ``ideals_over_q[i]``
1769
+ 3. ``rho_images[i]`` is a list of the reductions of the generators in of the `S`-unit group, modulo ``ideals_over_q[i]``
1770
+ 4. ``product_rho_orders[i]`` is the product of the multiplicative orders of the elements in ``rho_images[i]``
1771
+
1772
+ .. NOTE::
1773
+
1774
+ - The list ``ideals_over_q`` is sorted so that the product of orders is smallest for ``ideals_over_q[0]``, as this will make the later sieving steps more efficient.
1775
+ - The primes of `S` must not lie over `q`.
1776
+
1777
+ EXAMPLES::
1778
+
1779
+ sage: from sage.rings.number_field.S_unit_solver import sieve_ordering
1780
+ sage: x = polygen(ZZ, 'x')
1781
+ sage: K.<xi> = NumberField(x^3 - 3*x + 1)
1782
+ sage: SUK = K.S_unit_group(S=3)
1783
+ sage: sieve_data = list(sieve_ordering(SUK, 19))
1784
+ sage: sieve_data[0]
1785
+ (Fractional ideal (-2*xi^2 + 3),
1786
+ Fractional ideal (-xi + 3),
1787
+ Fractional ideal (2*xi + 1))
1788
+
1789
+ sage: sieve_data[1]
1790
+ (Residue field of Fractional ideal (-2*xi^2 + 3),
1791
+ Residue field of Fractional ideal (-xi + 3),
1792
+ Residue field of Fractional ideal (2*xi + 1))
1793
+
1794
+ sage: sieve_data[2]
1795
+ ([18, 9, 16, 8], [18, 7, 10, 4], [18, 3, 12, 10])
1796
+
1797
+ sage: sieve_data[3]
1798
+ (972, 972, 3888)
1799
+ """
1800
+
1801
+ K = SUK.number_field()
1802
+ rho = SUK.gens_values()
1803
+ d = K.absolute_degree()
1804
+ primes_over_q = K.primes_above(q)
1805
+ # q must split completely.
1806
+ if len(primes_over_q) != d:
1807
+ raise ValueError('The prime q is not completely split.')
1808
+
1809
+ for P in SUK.primes():
1810
+ if P in primes_over_q:
1811
+ raise ValueError('There is a prime in S over q.')
1812
+
1813
+ q_data = []
1814
+ for Qi in primes_over_q:
1815
+ resfield = Qi.residue_field()
1816
+ rho_mod_Qi = [resfield(rho_j) for rho_j in rho]
1817
+ orderprod = prod(rho_ij.multiplicative_order() for rho_ij in rho_mod_Qi)
1818
+ q_data.append([Qi, resfield, rho_mod_Qi, orderprod])
1819
+ q_data.sort(key=lambda X: [X[3],X[0],X[1],X[2]])
1820
+ # zip() will change the list of n list of length m to m tuples of length n
1821
+ return zip(*q_data)
1822
+
1823
+
1824
+ def clean_rfv_dict(rfv_dictionary):
1825
+ r"""
1826
+ Given a residue field vector dictionary, remove some impossible keys and entries.
1827
+
1828
+ INPUT:
1829
+
1830
+ - ``rfv_dictionary`` -- dictionary whose keys are exponent vectors and
1831
+ whose values are residue field vectors
1832
+
1833
+ OUTPUT: none, but it removes some keys from the input dictionary
1834
+
1835
+ .. NOTE::
1836
+
1837
+ - The keys of a residue field vector dictionary are exponent vectors modulo `q-1` for some prime `q`.
1838
+ - The values are residue field vectors. It is known that a residue field vector
1839
+ which comes from a solution to the `S`-unit equation cannot have 1 in any entry.
1840
+
1841
+ EXAMPLES:
1842
+
1843
+ In this example, we use a truncated list generated when solving the `S`-unit equation in the case that `K` is defined by the
1844
+ polynomial `x^2+x+1` and `S` consists of the primes above 3::
1845
+
1846
+ sage: from sage.rings.number_field.S_unit_solver import clean_rfv_dict
1847
+ sage: rfv_dict = {(1, 3): [3, 2], (3, 0): [6, 6], (5, 4): [3, 6], (2, 1): [4, 6],
1848
+ ....: (5, 1): [3, 1], (2, 5): [1, 5], (0, 3): [1, 6]}
1849
+ sage: len(rfv_dict)
1850
+ 7
1851
+ sage: clean_rfv_dict(rfv_dict)
1852
+ sage: len(rfv_dict)
1853
+ 4
1854
+ sage: rfv_dict
1855
+ {(1, 3): [3, 2], (2, 1): [4, 6], (3, 0): [6, 6], (5, 4): [3, 6]}
1856
+ """
1857
+
1858
+ for a, val in list(rfv_dictionary.items()):
1859
+ if 1 in val:
1860
+ rfv_dictionary.pop(a)
1861
+
1862
+
1863
+ def construct_rfv_to_ev(rfv_dictionary, q, d, verbose=False):
1864
+ r"""
1865
+ Return a reverse lookup dictionary, to find the exponent vectors associated
1866
+ to a given residue field vector.
1867
+
1868
+ INPUT:
1869
+
1870
+ - ``rfv_dictionary`` -- dictionary whose keys are exponent vectors and
1871
+ whose values are the associated residue field vectors
1872
+ - ``q`` -- a prime (assumed to split completely in the relevant number field)
1873
+ - ``d`` -- the number of primes in `K` above the rational prime `q`
1874
+ - ``verbose`` -- boolean (default: ``False``); flag to indicate more
1875
+ detailed output is desired
1876
+
1877
+ OUTPUT:
1878
+
1879
+ A dictionary ``P`` whose keys are residue field vectors and whose values are lists of all exponent vectors
1880
+ which correspond to the given residue field vector.
1881
+
1882
+ .. NOTE::
1883
+
1884
+ - For example, if ``rfv_dictionary[e0] = r0``, then ``P[r0]`` is a list which contains ``e0``.
1885
+ - During construction, some residue field vectors can be eliminated as coming from
1886
+ solutions to the `S`-unit equation. Such vectors are dropped from the keys of the dictionary ``P``.
1887
+
1888
+ EXAMPLES:
1889
+
1890
+ In this example, we use a truncated list generated when solving the `S`-unit equation in the case that `K` is defined by the
1891
+ polynomial `x^2+x+1` and `S` consists of the primes above 3::
1892
+
1893
+ sage: from sage.rings.number_field.S_unit_solver import construct_rfv_to_ev
1894
+ sage: rfv_dict = {(1, 3): [3, 2], (3, 0): [6, 6], (5, 4): [3, 6], (2, 1): [4, 6],
1895
+ ....: (4, 0): [4, 2], (1, 2): [5, 6]}
1896
+ sage: construct_rfv_to_ev(rfv_dict,7,2,False)
1897
+ {(3, 2): [(1, 3)], (4, 2): [(4, 0)], (4, 6): [(2, 1)], (5, 6): [(1, 2)]}
1898
+ """
1899
+
1900
+ # The keys in P are just the possible first entries of a residue field vector.
1901
+ # The values (all empty lists now) will be added in the next step.
1902
+
1903
+ P = {(v,) : [] for v in range(2, q)}
1904
+
1905
+ # Step 1. Populate the empty lists in P[(v,)].
1906
+ # Loop through the keys in rfv_dictionary. For each, look at the output rf_vector.
1907
+ # Find the key in P which matches the first entry of the rf_vector.
1908
+ # Dump the **rest** of the rf_vector into a pair [exp_vec, rf_vec[1:]],
1909
+ # and append this pair into the dictionary P at the key (rf_vec[0], ).
1910
+
1911
+ # Now, P[(v,)] = [ [a_0, e_0], [a_1, e_1], ...]
1912
+ #
1913
+ # The relationship between v, a_i, and e_i is as follows:
1914
+ #
1915
+ # a_i is an exponent vector, whose associated residue field vector is the
1916
+ # concatenation of v with e_i.
1917
+
1918
+ for exponent_vector in rfv_dictionary:
1919
+ residue_field_vector = rfv_dictionary[exponent_vector]
1920
+ rf_vector_start = (residue_field_vector[0], )
1921
+ rf_vector_end = residue_field_vector[1:]
1922
+ P[rf_vector_start].append([exponent_vector, rf_vector_end])
1923
+
1924
+ if verbose:
1925
+ print("Populated P. Currently it has ", len(P), "keys.")
1926
+
1927
+ # Step 2: We build a new dictionary, P_new, from P.
1928
+ #
1929
+ # This is a step that will be repeated, once for each of the d primes over q.
1930
+ #
1931
+ # P is a dictionary whose keys are tuples of length m, representing the beginning of known residue field vectors.
1932
+ #
1933
+ # For any such beginning `s`,
1934
+ #
1935
+ # P[s] = [ [a_0, e_0], [a_1, e_1], ...]
1936
+ #
1937
+ # where for any exponent vector a_i, the associated residue field vector is the concatenation s + e_i.
1938
+ #
1939
+ # The dictionary P_new is constructed from the dictionary P. The new keys will be tuples of length m + 1.
1940
+ #
1941
+ # During the construction, we look for impossible entries for S-unit solutions, and drop them from the dictionary as needed.
1942
+
1943
+ for j in range(d-1):
1944
+ if verbose:
1945
+ print("Constructing ", j, " th place of the residue field vectors, out of ", d-1, " total.")
1946
+ P_new = {}
1947
+ garbage = {}
1948
+
1949
+ # we loop over each key of P.
1950
+ for rf_vector_start in P:
1951
+
1952
+ # each key of P provides q-2 possible keys for P_new, which we introduce and assign an empty list.
1953
+ for w in range(2, q):
1954
+ new_rf_vector_start = tuple(list(rf_vector_start) + [w])
1955
+ P_new[new_rf_vector_start] = []
1956
+
1957
+ # we populate P_new[ new_rf_vector_start ] using P[rf_vector_start]
1958
+ for exponent_vector, rf_vector_end in P[rf_vector_start]:
1959
+ new_rf_vector_end = rf_vector_end[1:]
1960
+ w = rf_vector_end[0]
1961
+ new_rf_vector_start = tuple(list(rf_vector_start) + [w])
1962
+ P_new[new_rf_vector_start].append([exponent_vector, new_rf_vector_end])
1963
+
1964
+ if verbose:
1965
+ print("P_new is populated with ", len(P_new), " keys.")
1966
+
1967
+ # we now loop over the keys of P_new, looking for incompatible entries.
1968
+
1969
+ for rf_vector_start in P_new:
1970
+ # the final entry of rf_vector_start or rf_vector_complement_start must be < (q+3)/2.
1971
+ # No loss to insist that it is rf_vector_start.
1972
+ if rf_vector_start[-1] < (q+3)/2:
1973
+ # we find the complement to rf_vector_start:
1974
+ rf_vector_complement_start = tuple([ q+1-j for j in rf_vector_start])
1975
+ if P_new[ rf_vector_start ] == [] or P_new[rf_vector_complement_start] == []:
1976
+ # these can't be solutions. Mark them for deletion.
1977
+ garbage[rf_vector_start] = True
1978
+ garbage[rf_vector_complement_start] = True
1979
+
1980
+ # garbage removal
1981
+ for rf_vector_start in garbage:
1982
+ P_new.pop(rf_vector_start, 0)
1983
+
1984
+ if verbose:
1985
+ print("After removing incompatible entries, P_new is down to ", len(P_new), " keys.")
1986
+
1987
+ # Time to move on to the next dictionary.
1988
+ P = P_new.copy()
1989
+
1990
+ # Now, we just clean up P.
1991
+ for residue_field_vector in P:
1992
+ # at this instant, P[ residue_field_vector ] is a list of pairs: [ [a0,e0], ... ]
1993
+ # We only care about the exponent vectors a0,...
1994
+ P[residue_field_vector] = [a[0] for a in P[residue_field_vector]]
1995
+
1996
+ if verbose:
1997
+ print("Returning dictionary P with ", len(P), " keys.")
1998
+ return P.copy()
1999
+
2000
+
2001
+ def construct_comp_exp_vec(rfv_to_ev_dict, q):
2002
+ r"""
2003
+ Construct a dictionary associating complement vectors to residue field vectors.
2004
+
2005
+ INPUT:
2006
+
2007
+ - ``rfv_to_ev_dict`` -- dictionary whose keys are residue field vectors and
2008
+ whose values are lists of exponent vectors with the associated residue
2009
+ field vector
2010
+ - ``q`` -- the characteristic of the residue field
2011
+
2012
+ OUTPUT: a dictionary whose typical key is an exponent vector ``a``, and
2013
+ whose associated value is a list of complementary exponent vectors to ``a``
2014
+
2015
+ EXAMPLES:
2016
+
2017
+ In this example, we use the list generated when solving the `S`-unit equation in the case that `K` is defined by the
2018
+ polynomial `x^2+x+1` and `S` consists of the primes above 3
2019
+
2020
+ ::
2021
+
2022
+ sage: from sage.rings.number_field.S_unit_solver import construct_comp_exp_vec
2023
+ sage: rfv_to_ev_dict = {(6, 6): [(3, 0)], (5, 6): [(1, 2)], (5, 4): [(5, 3)],
2024
+ ....: (6, 2): [(5, 5)], (2, 5): [(0, 1)], (5, 5): [(3, 4)],
2025
+ ....: (4, 4): [(0, 2)], (6, 3): [(1, 4)], (3, 6): [(5, 4)],
2026
+ ....: (2, 2): [(0, 4)], (3, 5): [(1, 0)], (6, 4): [(1, 1)],
2027
+ ....: (3, 2): [(1, 3)], (2, 6): [(4, 5)], (4, 5): [(4, 3)],
2028
+ ....: (2, 3): [(2, 3)], (4, 2): [(4, 0)], (6, 5): [(5, 2)],
2029
+ ....: (3, 3): [(3, 2)], (5, 3): [(5, 0)], (4, 6): [(2, 1)],
2030
+ ....: (3, 4): [(3, 5)], (4, 3): [(0, 5)], (5, 2): [(3, 1)],
2031
+ ....: (2, 4): [(2, 0)]}
2032
+ sage: construct_comp_exp_vec(rfv_to_ev_dict, 7)
2033
+ {(0, 1): [(1, 4)],
2034
+ (0, 2): [(0, 2)],
2035
+ (0, 4): [(3, 0)],
2036
+ (0, 5): [(4, 3)],
2037
+ (1, 0): [(5, 0)],
2038
+ (1, 1): [(2, 0)],
2039
+ (1, 2): [(1, 3)],
2040
+ (1, 3): [(1, 2)],
2041
+ (1, 4): [(0, 1)],
2042
+ (2, 0): [(1, 1)],
2043
+ (2, 1): [(4, 0)],
2044
+ (2, 3): [(5, 2)],
2045
+ (3, 0): [(0, 4)],
2046
+ (3, 1): [(5, 4)],
2047
+ (3, 2): [(3, 4)],
2048
+ (3, 4): [(3, 2)],
2049
+ (3, 5): [(5, 3)],
2050
+ (4, 0): [(2, 1)],
2051
+ (4, 3): [(0, 5)],
2052
+ (4, 5): [(5, 5)],
2053
+ (5, 0): [(1, 0)],
2054
+ (5, 2): [(2, 3)],
2055
+ (5, 3): [(3, 5)],
2056
+ (5, 4): [(3, 1)],
2057
+ (5, 5): [(4, 5)]}
2058
+ """
2059
+
2060
+ comp_exp_vec_dict = {}
2061
+ for residue_field_vector in rfv_to_ev_dict:
2062
+ rf_vector_complement = tuple([q + 1 - j for j in residue_field_vector])
2063
+ exponent_vector_list = rfv_to_ev_dict[ residue_field_vector ][:]
2064
+ exponent_vector_complement_list = rfv_to_ev_dict[rf_vector_complement][:]
2065
+ for exponent_vector in exponent_vector_list:
2066
+ comp_exp_vec_dict[exponent_vector] = exponent_vector_complement_list
2067
+ return comp_exp_vec_dict
2068
+
2069
+
2070
+ def drop_vector(ev, p, q, complement_ev_dict):
2071
+ r"""
2072
+ Determine if the exponent vector, ``ev``, may be removed from the complement dictionary during construction.
2073
+ This will occur if ``ev`` is not compatible with an exponent vector mod `q-1`.
2074
+
2075
+ INPUT:
2076
+
2077
+ - ``ev`` -- an exponent vector modulo `p-1`
2078
+ - ``p`` -- the prime such that ``ev`` is an exponent vector modulo `p-1`
2079
+ - ``q`` -- a prime, distinct from `p`, that is a key in the ``complement_ev_dict``
2080
+ - ``complement_ev_dict`` -- dictionary of dictionaries, whose keys are primes
2081
+ ``complement_ev_dict[q]`` is a dictionary whose keys are exponent vectors modulo `q-1`
2082
+ and whose values are lists of complementary exponent vectors modulo `q-1`
2083
+
2084
+ OUTPUT: ``True`` if ``ev`` may be dropped from the complement exponent
2085
+ vector dictionary, and ``False`` if not
2086
+
2087
+ .. NOTE::
2088
+
2089
+ - If ``ev`` is not compatible with any of the vectors modulo `q-1`, then it can no longer correspond to a solution
2090
+ of the `S`-unit equation. It returns ``True`` to indicate that it should be removed.
2091
+
2092
+ EXAMPLES::
2093
+
2094
+ sage: from sage.rings.number_field.S_unit_solver import drop_vector
2095
+ sage: drop_vector((1, 2, 5), 7, 11, {11: {(1, 1, 3): [(1, 1, 3), (2, 3, 4)]}})
2096
+ True
2097
+
2098
+ ::
2099
+
2100
+ sage: P = {3: {(1, 0, 0): [(1, 0, 0), (0, 1, 0)],
2101
+ ....: (0, 1, 0): [(1, 0, 0), (0, 1, 0)]},
2102
+ ....: 7: {(0, 3, 4): [(0, 1, 2), (0, 3, 4), (0, 5, 0)],
2103
+ ....: (1, 2, 4): [(1, 0, 4), (1, 4, 2), (1, 2, 0)],
2104
+ ....: (0, 1, 2): [(0, 1, 2), (0, 3, 4), (0, 5, 0)],
2105
+ ....: (0, 5, 4): [(1, 0, 0), (1, 4, 4), (1, 2, 2)],
2106
+ ....: (1, 4, 2): [(1, 2, 4), (1, 4, 0), (1, 0, 2)],
2107
+ ....: (1, 0, 4): [(1, 2, 4), (1, 4, 0), (1, 0, 2)],
2108
+ ....: (0, 3, 2): [(1, 0, 0), (1, 4, 4), (1, 2, 2)],
2109
+ ....: (1, 0, 0): [(0, 5, 4), (0, 3, 2), (0, 1, 0)],
2110
+ ....: (1, 2, 0): [(1, 2, 4), (1, 4, 0), (1, 0, 2)],
2111
+ ....: (0, 1, 0): [(1, 0, 0), (1, 4, 4), (1, 2, 2)],
2112
+ ....: (0, 5, 0): [(0, 1, 2), (0, 3, 4), (0, 5, 0)],
2113
+ ....: (1, 2, 2): [(0, 5, 4), (0, 3, 2), (0, 1, 0)],
2114
+ ....: (1, 4, 0): [(1, 0, 4), (1, 4, 2), (1, 2, 0)],
2115
+ ....: (1, 0, 2): [(1, 0, 4), (1, 4, 2), (1, 2, 0)],
2116
+ ....: (1, 4, 4): [(0, 5, 4), (0, 3, 2), (0, 1, 0)]}}
2117
+ sage: drop_vector((0, 1, 0), 3, 7, P)
2118
+ False
2119
+ """
2120
+ # returns True if it is OK to drop exp_vec given the current comp_exp_vec dictionary associated to some q.
2121
+ # returns False otherwise
2122
+ # loop over the possible compatible vectors in the other modulus
2123
+ g = gcd(p-1, q-1)
2124
+ for compatible_exp_vec in compatible_vectors(ev, p-1, q-1, g):
2125
+ # do they appear in the other dictionary?
2126
+ if compatible_exp_vec in complement_ev_dict[q]:
2127
+ # OK, but the complements need to be compatible, too!
2128
+ ev_complement_list = complement_ev_dict[p][ev]
2129
+ for ev_comp in ev_complement_list:
2130
+ for compatible_cv in compatible_vectors(ev_comp, p-1, q-1, g):
2131
+ if compatible_cv in complement_ev_dict[q][compatible_exp_vec]:
2132
+ return False
2133
+ return True
2134
+
2135
+
2136
+ def construct_complement_dictionaries(split_primes_list, SUK, verbose=False):
2137
+ r"""
2138
+ Construct the complement exponent vector dictionaries.
2139
+
2140
+ INPUT:
2141
+
2142
+ - ``split_primes_list`` -- list of rational primes which split completely
2143
+ in the number field `K`
2144
+ - ``SUK`` -- the `S`-unit group for a number field `K`
2145
+ - ``verbose`` -- boolean to provide additional feedback (default: ``False``)
2146
+
2147
+ OUTPUT:
2148
+
2149
+ A dictionary of dictionaries. The keys coincide with the primes in
2150
+ ``split_primes_list``. For each `q`, ``comp_exp_vec[q]`` is a dictionary
2151
+ whose keys are exponent vectors modulo `q-1`, and whose values are lists of
2152
+ exponent vectors modulo `q-1`.
2153
+
2154
+ If `w` is an exponent vector in ``comp_exp_vec[q][v]``, then the residue field vectors modulo `q` for
2155
+ `v` and `w` sum to ``[1,1,...,1]``
2156
+
2157
+ .. NOTE::
2158
+
2159
+ - The data of ``comp_exp_vec`` will later be lifted to `\ZZ` to look for true `S`-Unit equation solutions.
2160
+ - During construction, the various dictionaries are compared to each other several times to
2161
+ eliminate as many mod `q` solutions as possible.
2162
+ - The authors acknowledge a helpful discussion with Norman Danner which helped formulate this code.
2163
+
2164
+ EXAMPLES::
2165
+
2166
+ sage: from sage.rings.number_field.S_unit_solver import construct_complement_dictionaries
2167
+ sage: x = polygen(ZZ, 'x')
2168
+ sage: f = x^2 + 5
2169
+ sage: H = 10
2170
+ sage: K.<xi> = NumberField(f)
2171
+ sage: SUK = K.S_unit_group(S=K.primes_above(H))
2172
+ sage: split_primes_list = [3, 7]
2173
+ sage: actual = construct_complement_dictionaries(split_primes_list, SUK)
2174
+ sage: expected = {3: {(0, 1, 0): [(0, 1, 0), (1, 0, 0)],
2175
+ ....: (1, 0, 0): [(0, 1, 0), (1, 0, 0)]},
2176
+ ....: 7: {(0, 1, 0): [(1, 0, 0), (1, 2, 2), (1, 4, 4)],
2177
+ ....: (0, 1, 2): [(0, 1, 2), (0, 3, 4), (0, 5, 0)],
2178
+ ....: (0, 3, 2): [(1, 0, 0), (1, 2, 2), (1, 4, 4)],
2179
+ ....: (0, 3, 4): [(0, 1, 2), (0, 3, 4), (0, 5, 0)],
2180
+ ....: (0, 5, 0): [(0, 1, 2), (0, 3, 4), (0, 5, 0)],
2181
+ ....: (0, 5, 4): [(1, 0, 0), (1, 2, 2), (1, 4, 4)],
2182
+ ....: (1, 0, 0): [(0, 1, 0), (0, 3, 2), (0, 5, 4)],
2183
+ ....: (1, 0, 2): [(1, 0, 4), (1, 2, 0), (1, 4, 2)],
2184
+ ....: (1, 0, 4): [(1, 0, 2), (1, 2, 4), (1, 4, 0)],
2185
+ ....: (1, 2, 0): [(1, 0, 2), (1, 2, 4), (1, 4, 0)],
2186
+ ....: (1, 2, 2): [(0, 1, 0), (0, 3, 2), (0, 5, 4)],
2187
+ ....: (1, 2, 4): [(1, 0, 4), (1, 2, 0), (1, 4, 2)],
2188
+ ....: (1, 4, 0): [(1, 0, 4), (1, 2, 0), (1, 4, 2)],
2189
+ ....: (1, 4, 2): [(1, 0, 2), (1, 2, 4), (1, 4, 0)],
2190
+ ....: (1, 4, 4): [(0, 1, 0), (0, 3, 2), (0, 5, 4)]}}
2191
+ sage: all(set(actual[p][vec]) == set(expected[p][vec])
2192
+ ....: for p in [3, 7] for vec in expected[p])
2193
+ True
2194
+ """
2195
+ # We initialize some dictionaries.
2196
+
2197
+ rho = SUK.gens_values()
2198
+ rho_length = len(rho)
2199
+ rho_images_dict = {}
2200
+ rho_orders_dict = {}
2201
+
2202
+ K = SUK.number_field()
2203
+ for q in split_primes_list:
2204
+ ideals_over_q, residue_fields, rho_images, product_rho_orders = sieve_ordering(SUK, q)
2205
+ rho_images_dict[q] = rho_images
2206
+ rho_orders_dict[q] = product_rho_orders
2207
+
2208
+ nK = K.absolute_degree()
2209
+ w0 = rho[0].multiplicative_order()
2210
+
2211
+ # We build a dictionary of dictionaries.
2212
+ # rfv_to_ev[q] is the 'mod q' residue field vector to exponent vector dictionary.
2213
+
2214
+ rfv_to_ev = {}
2215
+
2216
+ # We build a second dictionary of dictionaries.
2217
+ # comp_exp_vec[q] is the dictionary mod q which assigns to each exponent vector
2218
+ # a list of 'complementary' exponent vectors.
2219
+
2220
+ comp_exp_vec = {}
2221
+
2222
+ q0 = split_primes_list[0]
2223
+
2224
+ if verbose:
2225
+ print("Using the following primes: ", split_primes_list)
2226
+ for q in split_primes_list:
2227
+ rho_images = rho_images_dict[q]
2228
+ if verbose:
2229
+ print("q = ", q)
2230
+
2231
+ def epsilon_q(a, i):
2232
+ # a is an exponent vector
2233
+ # i is an index for one of the primes over q
2234
+ # returns the value of rho_j^a_j inside the
2235
+ # residue field of Qi. (Necessarily isomorphic to F_q.)
2236
+ # rho_images[i][j] == rho[j] modulo Q[i]
2237
+ eps_value = rho_images[i][0]**a[0]
2238
+ for j in range(1, rho_length):
2239
+ eps_value *= rho_images[i][j]**a[j]
2240
+ return eps_value
2241
+
2242
+ if verbose:
2243
+ print("The evaluation function epsilon has been defined using rho_images = ", rho_images)
2244
+ # Now, we run through the vectors in the iterator, but only keep the ones
2245
+ # which are compatible with the previously constructed dictionaries. That is,
2246
+ # in order to keep an exp_vec mod q, there must exist a compatible exp_vec mod p
2247
+ # in the keys of the rfv_to_ev[p] dictionary for each completely split prime
2248
+ # p appearing prior to q in split_primes_list.
2249
+
2250
+ if q == q0:
2251
+ # for the first prime, there is no filtering possible, and we just build the exponent vector
2252
+ # iterator.
2253
+
2254
+ # This should consist of all vectors (a0,...,a_{t-1}), where
2255
+ # a0 is in the range 0 .. w_0 - 1 and
2256
+ # aj is in the range 0 .. q - 2 (for j > 0)
2257
+ ranges = [range(w0)] + [range(q-1) for _ in range(rho_length-1)]
2258
+ ev_iterator = itertools.product(*ranges)
2259
+
2260
+ # With the iterator built, we construct the exponent vector to residue field dictionary.
2261
+
2262
+ ev_to_rfv_dict = {ev : [epsilon_q(ev, i) for i in range(nK)] for ev in ev_iterator}
2263
+
2264
+ if verbose:
2265
+ print("The residue field dictionary currently has ", len(ev_to_rfv_dict), " exponent vector keys.")
2266
+ else:
2267
+ ev_to_rfv_dict = {}
2268
+ # We use compatibility requirements to keep the size of the dictionary down.
2269
+ # Later on, we'll compare all dictionaries pairwise. But for now, we just
2270
+ # check against the first.
2271
+
2272
+ # That is, rather than loop over every possible exponent vector mod q-1,
2273
+ # we only consider those evs which are compatible with the mod q0 - 1 vectors.
2274
+
2275
+ # Loop over exponent vectors modulo q0 - 1
2276
+ g = gcd(q0-1, q-1)
2277
+ for exp_vec_mod_q0 in comp_exp_vec[q0]:
2278
+ # Loop only over exponent vectors modulo q-1 which are compatible with exp_vec_mod_q0
2279
+ for exp_vec in compatible_vectors(exp_vec_mod_q0, q0-1, q-1, g):
2280
+ # fill the dictionary with the residue field vectors using the evaluation function.
2281
+ ev_to_rfv_dict[exp_vec] = [epsilon_q(exp_vec, i) for i in range(nK)]
2282
+
2283
+ if verbose:
2284
+ print("The residue field dictionary currently has ", len(ev_to_rfv_dict), " exponent vector keys.")
2285
+ # At this point, we now have a dictionary ev_to_rfv_dict, which attaches
2286
+ # to each exponent vector a 'residue field vector,' which is a tuple of the
2287
+ # nK values epsilon_q(a,0),...,epsilon_q(a,nK-1).
2288
+
2289
+ clean_rfv_dict( ev_to_rfv_dict )
2290
+
2291
+ if verbose:
2292
+ print("clean_rfv_dict executed.")
2293
+ print("The residue field dictionary currently has ", len(ev_to_rfv_dict), " exponent vector keys.")
2294
+ # We essentially construct an inverse dictionary: one whose keys are residue field vectors,
2295
+ # and whose values are the exponent vectors that yield each key
2296
+
2297
+ rfv_to_ev[q] = construct_rfv_to_ev(ev_to_rfv_dict, q, nK, verbose=verbose)
2298
+
2299
+ if verbose:
2300
+ print("construct_rfv_to_ev executed.")
2301
+ print("The rfv_to_ev dictionary currently has ", len(rfv_to_ev[q]), "rfv keys.")
2302
+
2303
+ comp_exp_vec[q] = construct_comp_exp_vec(rfv_to_ev[q], q)
2304
+
2305
+ if verbose:
2306
+ print("construct_comp_exp_vec executed.")
2307
+ print("Size of comp_exp_vec[q]: ", len(comp_exp_vec[q]))
2308
+
2309
+ # Now that we have a new dictionary, we compare all the dictionaries pairwise,
2310
+ # looking for opportunities to remove 'impossible' solutions.
2311
+
2312
+ for p in comp_exp_vec.keys():
2313
+ if p == q:
2314
+ continue
2315
+ if verbose:
2316
+ print("Comparing dictionaries for p = ", p, "and q = ", q, ".")
2317
+
2318
+ old_size_p = len(comp_exp_vec[p])
2319
+
2320
+ if verbose:
2321
+ print("Size of comp_exp_vec[p] is: ", old_size_p, ".")
2322
+ cv_size = ((q-1)/gcd(p-1, q-1)) ** (rho_length - 1)
2323
+ print("Length of compatible_vectors: ", cv_size, ".")
2324
+ print("Product: ", old_size_p*cv_size)
2325
+
2326
+ for exp_vec in list(comp_exp_vec[p]):
2327
+ if drop_vector(exp_vec, p, q, comp_exp_vec):
2328
+ comp_exp_vec[p].pop(exp_vec)
2329
+
2330
+ if verbose:
2331
+ print("Shrunk dictionary p from ", old_size_p, " to ", len(comp_exp_vec[p]))
2332
+
2333
+ # Now, repeat, but swap p and q.
2334
+
2335
+ old_size_q = len(comp_exp_vec[q])
2336
+
2337
+ if verbose:
2338
+ print("Size of comp_exp_vec[q] is: ", old_size_q, ".")
2339
+ cv_size = ((p-1)/gcd(p-1, q-1)) ** (rho_length - 1)
2340
+ print("Length of compatible_vectors: ", cv_size, ".")
2341
+ print("Product: ", old_size_q * cv_size)
2342
+
2343
+ for exp_vec in list(comp_exp_vec[q]):
2344
+ if drop_vector(exp_vec, q, p, comp_exp_vec):
2345
+ comp_exp_vec[q].pop(exp_vec)
2346
+
2347
+ if verbose:
2348
+ print("Shrunk dictionary q from ", old_size_q, " to ", len(comp_exp_vec[q]))
2349
+
2350
+ return comp_exp_vec
2351
+
2352
+
2353
+ def compatible_vectors_check(a0, a1, g, l):
2354
+ r"""
2355
+ Given exponent vectors with respect to two moduli, determine if they are compatible.
2356
+
2357
+ INPUT:
2358
+
2359
+ - ``a0`` -- an exponent vector modulo ``m0``
2360
+ - ``a1`` -- an exponent vector modulo ``m1`` (must have the same length as ``a0``)
2361
+ - ``g`` -- the gcd of ``m0`` and ``m1``
2362
+ - ``l`` -- the length of ``a0`` and of ``a1``
2363
+
2364
+ OUTPUT: ``True`` if there is an integer exponent vector a satisfying
2365
+
2366
+ .. MATH::
2367
+
2368
+ \begin{aligned}
2369
+ a[0] &== a0[0] == a1[0]\\
2370
+ a[1:] &== a0[1:] \mod m_0\\
2371
+ a[1:] &== a1[1:] \mod m_1
2372
+ \end{aligned}
2373
+
2374
+ and ``False`` otherwise.
2375
+
2376
+ .. NOTE::
2377
+
2378
+ - Exponent vectors must agree exactly in the first coordinate.
2379
+ - If exponent vectors are different lengths, an error is raised.
2380
+
2381
+ EXAMPLES::
2382
+
2383
+ sage: from sage.rings.number_field.S_unit_solver import compatible_vectors_check
2384
+ sage: a0 = (3, 1, 8, 11)
2385
+ sage: a1 = (3, 5, 6, 13)
2386
+ sage: a2 = (5, 5, 6, 13)
2387
+ sage: compatible_vectors_check(a0, a1, gcd(12, 22), 4r)
2388
+ True
2389
+ sage: compatible_vectors_check(a0, a2, gcd(12, 22), 4r)
2390
+ False
2391
+ """
2392
+ # exponent vectors must agree exactly in the 0th coordinate.
2393
+ return a0[0] == a1[0] and all((x0 - x1) % g == 0 for x0,x1 in zip(itertools.islice(a0, 1, l), itertools.islice(a1, 1, l)))
2394
+
2395
+
2396
+ def compatible_vectors(a, m0, m1, g):
2397
+ r"""
2398
+ Given an exponent vector ``a`` modulo ``m0``, return an iterator over the exponent vectors for the modulus ``m1``, such that a lift to the lcm modulus exists.
2399
+
2400
+ INPUT:
2401
+
2402
+ - ``a`` -- an exponent vector for the modulus ``m0``
2403
+ - ``m0`` -- positive integer (specifying the modulus for ``a``)
2404
+ - ``m1`` -- positive integer (specifying the alternate modulus)
2405
+ - ``g`` -- the gcd of ``m0`` and ``m1``
2406
+
2407
+ OUTPUT: list of exponent vectors modulo ``m1`` which are compatible with ``a``
2408
+
2409
+ .. NOTE::
2410
+
2411
+ Exponent vectors must agree exactly in the 0th position in order to be
2412
+ compatible.
2413
+
2414
+ EXAMPLES::
2415
+
2416
+ sage: from sage.rings.number_field.S_unit_solver import compatible_vectors
2417
+ sage: a = (3, 1, 8, 1)
2418
+ sage: list(compatible_vectors(a, 18, 12, gcd(18,12)))
2419
+ [(3, 1, 2, 1),
2420
+ (3, 1, 2, 7),
2421
+ (3, 1, 8, 1),
2422
+ (3, 1, 8, 7),
2423
+ (3, 7, 2, 1),
2424
+ (3, 7, 2, 7),
2425
+ (3, 7, 8, 1),
2426
+ (3, 7, 8, 7)]
2427
+
2428
+ The order of the moduli matters. ::
2429
+
2430
+ sage: len(list(compatible_vectors(a, 18, 12, gcd(18,12))))
2431
+ 8
2432
+ sage: len(list(compatible_vectors(a, 12, 18, gcd(18,12))))
2433
+ 27
2434
+ """
2435
+ # recall that the 0th entry must be an exact match.
2436
+ ranges = [[a[0]]] + [range(a[i] % g, (a[i] % g) + m1, g) for i in range(1, len(a))]
2437
+ return itertools.product(*ranges)
2438
+
2439
+
2440
+ def compatible_systems(split_prime_list, complement_exp_vec_dict):
2441
+ r"""
2442
+ Given dictionaries of complement exponent vectors for various primes that
2443
+ split in `K`, compute all possible compatible systems.
2444
+
2445
+ INPUT:
2446
+
2447
+ - ``split_prime_list`` -- list of rational primes that split completely in `K`
2448
+ - ``complement_exp_vec_dict`` -- dictionary of dictionaries; the keys are
2449
+ primes from ``split_prime_list``
2450
+
2451
+ OUTPUT: list of compatible systems of exponent vectors
2452
+
2453
+ .. NOTE::
2454
+
2455
+ - For any `q` in ``split_prime_list``, ``complement_exp_vec_dict[q]`` is a dictionary whose keys are exponent vectors modulo `q-1`
2456
+ and whose values are lists of exponent vectors modulo `q-1` which are complementary to the key.
2457
+
2458
+ - An item in ``system_list`` has the form ``[ [v0, w0], [v1, w1], ..., [vk, wk] ]``, where::
2459
+
2460
+ - ``qj = split_prime_list[j]``
2461
+ - ``vj`` and ``wj`` are complementary exponent vectors modulo ``qj - 1``
2462
+ - the pairs are all simultaneously compatible.
2463
+
2464
+ - Let ``H = lcm( qj - 1 : qj in split_primes_list )``. Then for any compatible system, there is at most one pair of integer
2465
+ exponent vectors ``[v, w]`` such that::
2466
+
2467
+ - every entry of ``v`` and ``w`` is bounded in absolute value by ``H``
2468
+ - for any ``qj``, ``v`` and ``vj`` agree modulo ``(qj - 1)``
2469
+ - for any ``qj``, ``w`` and ``wj`` agree modulo ``(qj - 1)``
2470
+
2471
+ EXAMPLES::
2472
+
2473
+ sage: from sage.rings.number_field.S_unit_solver import compatible_systems
2474
+ sage: split_primes_list = [3, 7]
2475
+ sage: checking_dict = {3: {(0, 1, 0): [(1, 0, 0)]}, 7: {(0, 1, 0): [(1, 0, 0)]}}
2476
+ sage: compatible_systems(split_primes_list, checking_dict)
2477
+ [[[(0, 1, 0), (1, 0, 0)], [(0, 1, 0), (1, 0, 0)]]]
2478
+ """
2479
+ S0 = split_prime_list
2480
+
2481
+ system_list = []
2482
+ if len(S0) == 1:
2483
+ q = S0[0]
2484
+ for exponent_vector in complement_exp_vec_dict[q]:
2485
+ for complementary_vector in complement_exp_vec_dict[q][exponent_vector]:
2486
+ pair = [[exponent_vector, complementary_vector]]
2487
+ system_list.append(pair)
2488
+ elif len(S0) > 1:
2489
+ S1 = S0[:-1]
2490
+ old_systems = compatible_systems(S1, complement_exp_vec_dict)
2491
+ q = S0[-1]
2492
+ gcds = [gcd(q-1, qj-1) for qj in S1]
2493
+ for exp_vec in complement_exp_vec_dict[q]:
2494
+ l = len(exp_vec)
2495
+ for comp_vec in complement_exp_vec_dict[q][exp_vec]:
2496
+ for old_system in old_systems:
2497
+ if all((compatible_vectors_check(exp_vec, exp_vec_qj, g, l) and
2498
+ compatible_vectors_check(comp_vec, comp_vec_qj, g, l))
2499
+ for g, (exp_vec_qj, comp_vec_qj) in zip(gcds, old_system)):
2500
+ # build the new system and append it to the list.
2501
+ new_system = old_system + [[exp_vec, comp_vec]]
2502
+ system_list.append(new_system)
2503
+ return system_list
2504
+
2505
+
2506
+ def compatible_system_lift(compatible_system, split_primes_list):
2507
+ r"""
2508
+ Given a compatible system of exponent vectors and complementary exponent
2509
+ vectors, return a lift to the integers.
2510
+
2511
+ INPUT:
2512
+
2513
+ - ``compatible_system`` -- list of pairs ``[ [v0, w0], [v1, w1], .., [vk, wk] ]``
2514
+ where [vi, wi] is a pair of complementary exponent vectors modulo ``qi - 1``,
2515
+ and all pairs are compatible
2516
+ - ``split_primes_list`` -- list of primes ``[ q0, q1, .., qk ]``
2517
+
2518
+ OUTPUT: a pair of vectors ``[v, w]`` satisfying:
2519
+
2520
+ 1. ``v[0] == vi[0]`` for all ``i``
2521
+ 2. ``w[0] == wi[0]`` for all ``i``
2522
+ 3. ``v[j] == vi[j]`` modulo ``qi - 1`` for all ``i`` and all ``j > 0``
2523
+ 4. ``w[j] == wi[j]`` modulo ``qi - 1`` for all ``i`` and all `j > 0``
2524
+ 5. every entry of ``v`` and ``w`` is bounded by ``L/2`` in absolute value, where ``L`` is the least common multiple of ``{qi - 1 : qi in split_primes_list }``
2525
+
2526
+ EXAMPLES::
2527
+
2528
+ sage: from sage.rings.number_field.S_unit_solver import compatible_system_lift
2529
+ sage: split_primes_list = [3, 7]
2530
+ sage: comp_sys = [[(0, 1, 0), (0, 1, 0)], [(0, 3, 4), (0, 1, 2)]]
2531
+ sage: compatible_system_lift(comp_sys, split_primes_list)
2532
+ [(0, 3, -2), (0, 1, 2)]
2533
+ """
2534
+
2535
+ if len(split_primes_list) != len(compatible_system):
2536
+ raise ValueError("The number of primes does not match the length of the given exponent vectors.")
2537
+
2538
+ # the first entries are already determined.
2539
+ exponent_vector_lift = [ZZ(compatible_system[0][0][0])]
2540
+ complement_vector_lift = [ZZ(compatible_system[0][1][0])]
2541
+
2542
+ # fill in exponent_vector_lift
2543
+ moduli_list = [q-1 for q in split_primes_list]
2544
+ L = lcm(moduli_list)
2545
+
2546
+ t = len(compatible_system[0][0])
2547
+ for i in range(1,t):
2548
+ exp_coord_residues = [pair[0][i] for pair in compatible_system]
2549
+ comp_coord_residues = [pair[1][i] for pair in compatible_system]
2550
+
2551
+ ev_lift_coordinate = CRT(exp_coord_residues, moduli_list)
2552
+ cv_lift_coordinate = CRT(comp_coord_residues, moduli_list)
2553
+
2554
+ # these values lie in the range [0, L-1], so we must shift them if they are bigger than L/2.
2555
+
2556
+ if ev_lift_coordinate > L/2:
2557
+ ev_lift_coordinate -= L
2558
+ if cv_lift_coordinate > L/2:
2559
+ cv_lift_coordinate -= L
2560
+
2561
+ exponent_vector_lift.append(ev_lift_coordinate)
2562
+ complement_vector_lift.append(cv_lift_coordinate)
2563
+
2564
+ return [tuple(exponent_vector_lift), tuple(complement_vector_lift)]
2565
+
2566
+
2567
+ def solutions_from_systems(SUK, bound, cs_list, split_primes_list):
2568
+ r"""
2569
+ Lift compatible systems to the integers and return the `S`-unit equation
2570
+ solutions that the lifts yield.
2571
+
2572
+ INPUT:
2573
+
2574
+ - ``SUK`` -- the group of `S`-units where we search for solutions
2575
+ - ``bound`` -- a bound for the entries of all entries of all lifts
2576
+ - ``cs_list`` -- list of compatible systems of exponent vectors modulo
2577
+ `q-1` for various primes `q`
2578
+ - ``split_primes_list`` -- list of primes giving the moduli of the exponent
2579
+ vectors in ``cs_list``
2580
+
2581
+ OUTPUT:
2582
+
2583
+ A list of solutions to the S-unit equation. Each solution is a list:
2584
+
2585
+ 1. an exponent vector over the integers, ``ev``
2586
+ 2. an exponent vector over the integers, ``cv``
2587
+ 3. the S-unit corresponding to ``ev``, ``iota_exp``
2588
+ 4. the S-unit corresponding to ``cv``, ``iota_comp``
2589
+
2590
+ .. NOTE::
2591
+
2592
+ - Every entry of ``ev`` is less than or equal to bound in absolute value
2593
+ - every entry of ``cv`` is less than or equal to bound in absolute value
2594
+ - ``iota_exp + iota_comp == 1``
2595
+
2596
+ EXAMPLES:
2597
+
2598
+ Given a single compatible system, a solution can be found. ::
2599
+
2600
+ sage: from sage.rings.number_field.S_unit_solver import solutions_from_systems
2601
+ sage: x = polygen(ZZ, 'x')
2602
+ sage: K.<xi> = NumberField(x^2 - 15)
2603
+ sage: SUK = K.S_unit_group(S=K.primes_above(2))
2604
+ sage: split_primes_list = [7, 17]
2605
+ sage: a_compatible_system = [[[(0, 0, 5), (0, 0, 5)], [(0, 0, 15), (0, 0, 15)]]]
2606
+ sage: solutions_from_systems(SUK, 20, a_compatible_system, split_primes_list)
2607
+ [((0, 0, -1), (0, 0, -1), 1/2, 1/2)]
2608
+ """
2609
+ solutions = []
2610
+
2611
+ for system in cs_list:
2612
+ ev, cv = compatible_system_lift(system, split_primes_list)
2613
+ if all(abs(x) <= bound for x in ev[1:] + cv[1:]):
2614
+ # the entries are all below the bound, so there is nothing left to do
2615
+ # except construct the elements and see if they are solutions to
2616
+ # the S-unit equation
2617
+ iota_exp = SUK.exp( ev )
2618
+ iota_comp = SUK.exp( cv )
2619
+ if iota_exp + iota_comp == 1:
2620
+ sol = ( ev, cv, iota_exp, iota_comp )
2621
+ solutions.append( sol )
2622
+
2623
+ return solutions
2624
+
2625
+
2626
+ def clean_sfs(sfs_list):
2627
+ r"""
2628
+ Given a list of `S`-unit equation solutions, remove trivial redundancies.
2629
+
2630
+ INPUT:
2631
+
2632
+ - ``sfs_list`` -- list of solutions to the `S`-unit equation
2633
+
2634
+ OUTPUT: list of solutions to the `S`-unit equation
2635
+
2636
+ .. NOTE::
2637
+
2638
+ The function looks for cases where `x + y = 1` and `y + x = 1` appear
2639
+ as separate solutions, and removes one.
2640
+
2641
+ EXAMPLES:
2642
+
2643
+ The function is not dependent on the number field and removes redundancies in any list. ::
2644
+
2645
+ sage: from sage.rings.number_field.S_unit_solver import clean_sfs
2646
+ sage: sols = [((1, 0, 0), (0, 0, 1), -1, 2), ((0, 0, 1), (1, 0, 0), 2, -1)]
2647
+ sage: clean_sfs( sols )
2648
+ [((1, 0, 0), (0, 0, 1), -1, 2)]
2649
+ """
2650
+ # given the output from solutions_from_systems,
2651
+ # look for trivial redundancies: swapping exp_vec, comp_vec, particularly.
2652
+ new_sfs = []
2653
+ for entry in sfs_list:
2654
+ swapped_entry = (entry[1], entry[0], entry[3], entry[2])
2655
+ if entry not in new_sfs and swapped_entry not in new_sfs:
2656
+ new_sfs.append(entry)
2657
+ return new_sfs
2658
+
2659
+
2660
+ def sieve_below_bound(K, S, bound=10, bump=10, split_primes_list=[], verbose=False):
2661
+ r"""
2662
+ Return all solutions to the `S`-unit equation `x + y = 1` over `K` with
2663
+ exponents below the given bound.
2664
+
2665
+ INPUT:
2666
+
2667
+ - ``K`` -- a number field (an absolute extension of the rationals)
2668
+ - ``S`` -- list of finite primes of `K`
2669
+ - ``bound`` -- positive integer upper bound for exponents, solutions with
2670
+ exponents having absolute value below this bound will be found (default: 10)
2671
+ - ``bump`` -- positive integer by which the minimum LCM will be increased
2672
+ if not enough split primes are found in sieving step (default: 10)
2673
+ - ``split_primes_list`` -- list of rational primes that split completely in
2674
+ the extension `K/\QQ`, used for sieving. For complete list of solutions
2675
+ should have lcm of `\{(p_i-1)\} for primes `p_i` greater than bound
2676
+ (default: ``[]``).
2677
+ - ``verbose`` -- an optional parameter allowing the user to print
2678
+ information during the sieving process (default: ``False``)
2679
+
2680
+ OUTPUT:
2681
+
2682
+ A list of tuples `[(A_1, B_1, x_1, y_1), (A_2, B_2, x_2, y_2), \dots (A_n, B_n, x_n, y_n)]` such that:
2683
+
2684
+ 1. The first two entries are tuples `A_i = (a_0, a_1, \dots, a_t)` and `B_i = (b_0, b_1, \dots, b_t)` of exponents.
2685
+ 2. The last two entries are `S`-units `x_i` and `y_i` in `K` with `x_i + y_i = 1`.
2686
+ 3. If the default generators for the `S`-units of `K` are `(\rho_0, \rho_1, \dots, \rho_t)`,
2687
+ then these satisfy `x_i = \prod(\rho_i)^{(a_i)}` and `y_i = \prod(\rho_i)^{(b_i)}`.
2688
+
2689
+ EXAMPLES::
2690
+
2691
+ sage: from sage.rings.number_field.S_unit_solver import sieve_below_bound, eq_up_to_order
2692
+ sage: x = polygen(ZZ, 'x')
2693
+ sage: K.<xi> = NumberField(x^2 + x + 1)
2694
+ sage: SUK = UnitGroup(K, S=tuple(K.primes_above(3)))
2695
+ sage: S = SUK.primes()
2696
+ sage: sols = sieve_below_bound(K, S, 10)
2697
+ sage: expected = [((3, -1), (2, -1), 1/3*xi + 2/3, -1/3*xi + 1/3),
2698
+ ....: ((4, 1), (4, 0), xi + 2, -xi - 1),
2699
+ ....: ((2, 0), (3, 1), xi, -xi + 1),
2700
+ ....: ((1, 0), (5, 0), xi + 1, -xi)]
2701
+ sage: eq_up_to_order(sols, expected)
2702
+ True
2703
+ """
2704
+ SUK = UnitGroup(K, S=tuple(S))
2705
+ initial_bound = bound
2706
+
2707
+ while not split_primes_list:
2708
+ try:
2709
+ split_primes_list = split_primes_large_lcm(SUK, initial_bound)
2710
+ except ValueError:
2711
+ initial_bound += bump
2712
+ print("Couldn't find enough split primes. Bumping to ", initial_bound)
2713
+
2714
+ if not K.is_absolute():
2715
+ raise ValueError("K must be an absolute extension.")
2716
+
2717
+ complement_exp_vec_dict = construct_complement_dictionaries(split_primes_list, SUK, verbose=verbose)
2718
+
2719
+ cs_list = compatible_systems(split_primes_list, complement_exp_vec_dict)
2720
+
2721
+ sfs_list = solutions_from_systems(SUK, bound, cs_list, split_primes_list)
2722
+
2723
+ S_unit_solutions = clean_sfs(sfs_list)
2724
+
2725
+ return S_unit_solutions
2726
+
2727
+
2728
+ def solve_S_unit_equation(K, S, prec=106, include_exponents=True, include_bound=False, proof=None, verbose=False):
2729
+ r"""
2730
+ Return all solutions to the `S`-unit equation `x + y = 1` over `K`.
2731
+
2732
+ INPUT:
2733
+
2734
+ - ``K`` -- a number field (an absolute extension of the rationals)
2735
+ - ``S`` -- list of finite primes of `K`
2736
+ - ``prec`` -- precision used for computations in real, complex, and `p`-adic
2737
+ fields (default: 106)
2738
+ - ``include_exponents`` -- whether to include the exponent vectors in the
2739
+ returned value (default: ``True``)
2740
+ - ``include_bound`` -- whether to return the final computed bound
2741
+ (default: ``False``)
2742
+ - ``verbose`` -- whether to print information during the sieving step
2743
+ (default: ``False``)
2744
+
2745
+ OUTPUT:
2746
+
2747
+ A list of tuples `[(A_1, B_1, x_1, y_1), (A_2, B_2, x_2, y_2), \dots (A_n, B_n, x_n, y_n)]` such that:
2748
+
2749
+ 1. The first two entries are tuples `A_i = (a_0, a_1, \dots, a_t)` and `B_i = (b_0, b_1, \dots, b_t)` of exponents. These will be omitted if ``include_exponents`` is ``False``.
2750
+ 2. The last two entries are `S`-units `x_i` and `y_i` in `K` with `x_i + y_i = 1`.
2751
+ 3. If the default generators for the `S`-units of `K` are `(\rho_0, \rho_1, \dots, \rho_t)``, then these satisfy `x_i = \prod(\rho_i)^{(a_i)}` and `y_i = \prod(\rho_i)^{(b_i)}`.
2752
+
2753
+ If ``include_bound``, will return a pair ``(sols, bound)`` where ``sols`` is as above and ``bound`` is the bound used for the entries in the exponent vectors.
2754
+
2755
+ EXAMPLES::
2756
+
2757
+ sage: from sage.rings.number_field.S_unit_solver import solve_S_unit_equation, eq_up_to_order
2758
+ sage: x = polygen(ZZ, 'x')
2759
+ sage: K.<xi> = NumberField(x^2 + x + 1)
2760
+ sage: S = K.primes_above(3)
2761
+ sage: sols = solve_S_unit_equation(K, S, 200)
2762
+ sage: expected = [((4, 1), (4, 0), xi + 2, -xi - 1),
2763
+ ....: ((3, -1), (2, -1), 1/3*xi + 2/3, -1/3*xi + 1/3),
2764
+ ....: ((1, 0), (5, 0), xi + 1, -xi),
2765
+ ....: ((2, 0), (3, 1), xi, -xi + 1)]
2766
+ sage: eq_up_to_order(sols, expected)
2767
+ True
2768
+
2769
+ In order to see the bound as well, use the optional parameter ``include_bound``::
2770
+
2771
+ sage: solutions, bound = solve_S_unit_equation(K, S, 100, include_bound=True)
2772
+ sage: bound
2773
+ 6
2774
+
2775
+ You can omit the exponent vectors::
2776
+
2777
+ sage: sols = solve_S_unit_equation(K, S, 200, include_exponents=False)
2778
+ sage: expected = [(xi + 2, -xi - 1), (1/3*xi + 2/3, -1/3*xi + 1/3),
2779
+ ....: (-xi, xi + 1), (-xi + 1, xi)]
2780
+ sage: set(frozenset(a) for a in sols) == set(frozenset(b) for b in expected)
2781
+ True
2782
+
2783
+ It is an error to use values in S that are not primes in K::
2784
+
2785
+ sage: solve_S_unit_equation(K, [3], 200)
2786
+ Traceback (most recent call last):
2787
+ ...
2788
+ ValueError: S must consist only of prime ideals,
2789
+ or a single element from which a prime ideal can be constructed.
2790
+
2791
+ We check the case that the rank is 0::
2792
+
2793
+ sage: K.<xi> = NumberField(x^2 + x + 1)
2794
+ sage: solve_S_unit_equation(K, [])
2795
+ [((1,), (5,), xi + 1, -xi)]
2796
+ """
2797
+
2798
+ # Checks to make sure inputs are legal
2799
+ # K must be an absolute extension:
2800
+ if not K.is_absolute():
2801
+ raise ValueError("K must be an absolute extension.")
2802
+ # S must be a finite set of primes
2803
+ try:
2804
+ SUK = UnitGroup(K, proof=proof, S=tuple(S))
2805
+ except Exception:
2806
+ raise ValueError("S must consist only of prime ideals, or a single element from which a prime ideal can be constructed.")
2807
+
2808
+ # Gather the roots of unity of the number field
2809
+ A = K.roots_of_unity()
2810
+ w = K.number_of_roots_of_unity()
2811
+ if SUK.rank() == 0:
2812
+ # Since the rank is 0, K is imaginary quadratic and S is empty
2813
+ # Only possibilities are combinations of roots of unity
2814
+ # and this can only occur when there are 6 roots of unity, when
2815
+ # (1+sqrt(-3))/2 + (1-sqrt(-3))/2 = 1 is the unique solution.
2816
+ if len(A) == 6:
2817
+ S_unit_solutions = [((ZZ(1),), (ZZ(5),), A[0], A[-2])]
2818
+ else:
2819
+ S_unit_solutions = []
2820
+ else:
2821
+ # First find a bound using the LLL reduction method
2822
+ # That bound must exceed both 4 and w. (See [AKMRVW].)
2823
+ all_LLL_bounds = [4, w]
2824
+ all_LLL_bounds += [cx_LLL_bound(SUK, A, prec)]
2825
+ if S:
2826
+ # only need p-adic bound when S nonempty
2827
+ all_LLL_bounds.append(p_adic_LLL_bound(SUK, A, prec))
2828
+
2829
+ # Take the largest of all of the bounds we found
2830
+ final_LLL_bound = max(all_LLL_bounds)
2831
+
2832
+ if verbose:
2833
+ print("The LLL bound is: ", final_LLL_bound)
2834
+
2835
+ # Use the sieve to more easily find all bounds
2836
+ S_unit_solutions = sieve_below_bound(K, list(S), final_LLL_bound, verbose=verbose)
2837
+
2838
+ if not include_exponents:
2839
+ S_unit_solutions = [sol[2:] for sol in S_unit_solutions]
2840
+ if include_bound:
2841
+ return S_unit_solutions, final_LLL_bound
2842
+ else:
2843
+ return S_unit_solutions
2844
+
2845
+
2846
+ def eq_up_to_order(A, B):
2847
+ """
2848
+ If ``A`` and ``B`` are lists of four-tuples ``[a0,a1,a2,a3]`` and ``[b0,b1,b2,b3]``,
2849
+ check that there is some reordering so that either ``ai=bi`` for all ``i`` or
2850
+ ``a0==b1``, ``a1==b0``, ``a2==b3``, ``a3==b2``.
2851
+
2852
+ The entries must be hashable.
2853
+
2854
+ EXAMPLES::
2855
+
2856
+ sage: from sage.rings.number_field.S_unit_solver import eq_up_to_order
2857
+ sage: L = [(1,2,3,4), (5,6,7,8)]
2858
+ sage: L1 = [L[1], L[0]]
2859
+ sage: L2 = [(2,1,4,3), (6,5,8,7)]
2860
+ sage: eq_up_to_order(L, L1)
2861
+ True
2862
+ sage: eq_up_to_order(L, L2)
2863
+ True
2864
+ sage: eq_up_to_order(L, [(1,2,4,3), (5,6,8,7)])
2865
+ False
2866
+ """
2867
+ # does not look very optimal
2868
+ Adup = set(A + [(a[1],a[0],a[3],a[2]) for a in A])
2869
+ Bdup = set(B + [(b[1],b[0],b[3],b[2]) for b in B])
2870
+ return Adup == Bdup