passagemath-flint 10.6.1rc10__cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- passagemath_flint-10.6.1rc10.dist-info/METADATA +122 -0
- passagemath_flint-10.6.1rc10.dist-info/RECORD +360 -0
- passagemath_flint-10.6.1rc10.dist-info/WHEEL +6 -0
- passagemath_flint-10.6.1rc10.dist-info/top_level.txt +2 -0
- passagemath_flint.libs/libflint-3701249d.so.21.0.0 +0 -0
- passagemath_flint.libs/libgf2x-fbd36f80.so.3.0.0 +0 -0
- passagemath_flint.libs/libgfortran-8a9a71bc.so.5.0.0 +0 -0
- passagemath_flint.libs/libgmp-93ebf16a.so.10.5.0 +0 -0
- passagemath_flint.libs/libgsl-e3525837.so.28.0.0 +0 -0
- passagemath_flint.libs/libmpfi-ad12a86d.so.0.0.0 +0 -0
- passagemath_flint.libs/libmpfr-e0f11cf3.so.6.2.1 +0 -0
- passagemath_flint.libs/libntl-1004113e.so.44.0.1 +0 -0
- passagemath_flint.libs/libopenblasp-r0-4c5b64b1.3.29.so +0 -0
- sage/all__sagemath_flint.py +29 -0
- sage/combinat/all__sagemath_flint.py +1 -0
- sage/combinat/posets/all__sagemath_flint.py +1 -0
- sage/combinat/posets/hasse_cython_flint.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/combinat/posets/hasse_cython_flint.pyx +194 -0
- sage/data_structures/all__sagemath_flint.py +1 -0
- sage/data_structures/bounded_integer_sequences.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/data_structures/bounded_integer_sequences.pxd +62 -0
- sage/data_structures/bounded_integer_sequences.pyx +1418 -0
- sage/graphs/all__sagemath_flint.py +1 -0
- sage/graphs/chrompoly.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/graphs/chrompoly.pyx +555 -0
- sage/graphs/matchpoly.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/graphs/matchpoly.pyx +412 -0
- sage/libs/all__sagemath_flint.py +17 -0
- sage/libs/arb/__init__.py +1 -0
- sage/libs/arb/acb.pxd +154 -0
- sage/libs/arb/acb_calc.pxd +9 -0
- sage/libs/arb/acb_elliptic.pxd +25 -0
- sage/libs/arb/acb_hypgeom.pxd +74 -0
- sage/libs/arb/acb_mat.pxd +62 -0
- sage/libs/arb/acb_modular.pxd +17 -0
- sage/libs/arb/acb_poly.pxd +216 -0
- sage/libs/arb/arb.pxd +240 -0
- sage/libs/arb/arb_fmpz_poly.pxd +21 -0
- sage/libs/arb/arb_hypgeom.pxd +83 -0
- sage/libs/arb/arb_wrap.h +34 -0
- sage/libs/arb/arf.pxd +131 -0
- sage/libs/arb/arith.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/libs/arb/arith.pyx +87 -0
- sage/libs/arb/bernoulli.pxd +6 -0
- sage/libs/arb/mag.pxd +77 -0
- sage/libs/arb/types.pxd +37 -0
- sage/libs/flint/__init__.py +1 -0
- sage/libs/flint/acb.pxd +270 -0
- sage/libs/flint/acb_calc.pxd +22 -0
- sage/libs/flint/acb_dft.pxd +51 -0
- sage/libs/flint/acb_dirichlet.pxd +112 -0
- sage/libs/flint/acb_elliptic.pxd +42 -0
- sage/libs/flint/acb_hypgeom.pxd +169 -0
- sage/libs/flint/acb_macros.pxd +9 -0
- sage/libs/flint/acb_mat.pxd +136 -0
- sage/libs/flint/acb_mat_macros.pxd +10 -0
- sage/libs/flint/acb_modular.pxd +62 -0
- sage/libs/flint/acb_poly.pxd +251 -0
- sage/libs/flint/acb_poly_macros.pxd +8 -0
- sage/libs/flint/acb_theta.pxd +124 -0
- sage/libs/flint/acf.pxd +32 -0
- sage/libs/flint/aprcl.pxd +84 -0
- sage/libs/flint/arb.pxd +382 -0
- sage/libs/flint/arb_calc.pxd +31 -0
- sage/libs/flint/arb_fmpz_poly.pxd +34 -0
- sage/libs/flint/arb_fpwrap.pxd +215 -0
- sage/libs/flint/arb_hypgeom.pxd +147 -0
- sage/libs/flint/arb_macros.pxd +9 -0
- sage/libs/flint/arb_mat.pxd +140 -0
- sage/libs/flint/arb_mat_macros.pxd +10 -0
- sage/libs/flint/arb_poly.pxd +237 -0
- sage/libs/flint/arf.pxd +167 -0
- sage/libs/flint/arith.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/libs/flint/arith.pxd +76 -0
- sage/libs/flint/arith.pyx +77 -0
- sage/libs/flint/arith_sage.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/libs/flint/arith_sage.pyx +308 -0
- sage/libs/flint/bernoulli.pxd +28 -0
- sage/libs/flint/bool_mat.pxd +52 -0
- sage/libs/flint/ca.pxd +203 -0
- sage/libs/flint/ca_ext.pxd +34 -0
- sage/libs/flint/ca_field.pxd +32 -0
- sage/libs/flint/ca_mat.pxd +117 -0
- sage/libs/flint/ca_poly.pxd +104 -0
- sage/libs/flint/ca_vec.pxd +46 -0
- sage/libs/flint/calcium.pxd +27 -0
- sage/libs/flint/d_mat.pxd +39 -0
- sage/libs/flint/d_vec.pxd +32 -0
- sage/libs/flint/dirichlet.pxd +57 -0
- sage/libs/flint/dlog.pxd +53 -0
- sage/libs/flint/double_extras.pxd +24 -0
- sage/libs/flint/double_interval.pxd +36 -0
- sage/libs/flint/fexpr.pxd +104 -0
- sage/libs/flint/fexpr_builtin.pxd +20 -0
- sage/libs/flint/fft.pxd +66 -0
- sage/libs/flint/flint.pxd +36 -0
- sage/libs/flint/flint_ntl_wrap.h +35 -0
- sage/libs/flint/flint_sage.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/libs/flint/flint_sage.pyx +163 -0
- sage/libs/flint/flint_wrap.h +190 -0
- sage/libs/flint/fmpq.pxd +137 -0
- sage/libs/flint/fmpq_mat.pxd +105 -0
- sage/libs/flint/fmpq_mat_macros.pxd +10 -0
- sage/libs/flint/fmpq_mpoly.pxd +165 -0
- sage/libs/flint/fmpq_mpoly_factor.pxd +30 -0
- sage/libs/flint/fmpq_poly.pxd +241 -0
- sage/libs/flint/fmpq_poly_macros.pxd +9 -0
- sage/libs/flint/fmpq_poly_sage.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/libs/flint/fmpq_poly_sage.pxd +31 -0
- sage/libs/flint/fmpq_poly_sage.pyx +48 -0
- sage/libs/flint/fmpq_vec.pxd +27 -0
- sage/libs/flint/fmpz.pxd +256 -0
- sage/libs/flint/fmpz_extras.pxd +32 -0
- sage/libs/flint/fmpz_factor.pxd +42 -0
- sage/libs/flint/fmpz_factor_sage.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/libs/flint/fmpz_factor_sage.pxd +4 -0
- sage/libs/flint/fmpz_factor_sage.pyx +29 -0
- sage/libs/flint/fmpz_lll.pxd +49 -0
- sage/libs/flint/fmpz_macros.pxd +8 -0
- sage/libs/flint/fmpz_mat.pxd +184 -0
- sage/libs/flint/fmpz_mat_macros.pxd +10 -0
- sage/libs/flint/fmpz_mod.pxd +46 -0
- sage/libs/flint/fmpz_mod_mat.pxd +71 -0
- sage/libs/flint/fmpz_mod_mpoly.pxd +161 -0
- sage/libs/flint/fmpz_mod_mpoly_factor.pxd +28 -0
- sage/libs/flint/fmpz_mod_poly.pxd +249 -0
- sage/libs/flint/fmpz_mod_poly_factor.pxd +46 -0
- sage/libs/flint/fmpz_mod_vec.pxd +27 -0
- sage/libs/flint/fmpz_mpoly.pxd +224 -0
- sage/libs/flint/fmpz_mpoly_factor.pxd +29 -0
- sage/libs/flint/fmpz_mpoly_q.pxd +57 -0
- sage/libs/flint/fmpz_poly.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/libs/flint/fmpz_poly.pxd +407 -0
- sage/libs/flint/fmpz_poly.pyx +19 -0
- sage/libs/flint/fmpz_poly_factor.pxd +33 -0
- sage/libs/flint/fmpz_poly_macros.pxd +8 -0
- sage/libs/flint/fmpz_poly_mat.pxd +71 -0
- sage/libs/flint/fmpz_poly_q.pxd +55 -0
- sage/libs/flint/fmpz_poly_sage.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/libs/flint/fmpz_poly_sage.pxd +20 -0
- sage/libs/flint/fmpz_poly_sage.pyx +500 -0
- sage/libs/flint/fmpz_vec.pxd +80 -0
- sage/libs/flint/fmpzi.pxd +52 -0
- sage/libs/flint/fq.pxd +97 -0
- sage/libs/flint/fq_default.pxd +84 -0
- sage/libs/flint/fq_default_mat.pxd +70 -0
- sage/libs/flint/fq_default_poly.pxd +97 -0
- sage/libs/flint/fq_default_poly_factor.pxd +39 -0
- sage/libs/flint/fq_embed.pxd +28 -0
- sage/libs/flint/fq_mat.pxd +83 -0
- sage/libs/flint/fq_nmod.pxd +95 -0
- sage/libs/flint/fq_nmod_embed.pxd +28 -0
- sage/libs/flint/fq_nmod_mat.pxd +83 -0
- sage/libs/flint/fq_nmod_mpoly.pxd +130 -0
- sage/libs/flint/fq_nmod_mpoly_factor.pxd +28 -0
- sage/libs/flint/fq_nmod_poly.pxd +202 -0
- sage/libs/flint/fq_nmod_poly_factor.pxd +47 -0
- sage/libs/flint/fq_nmod_vec.pxd +33 -0
- sage/libs/flint/fq_poly.pxd +204 -0
- sage/libs/flint/fq_poly_factor.pxd +47 -0
- sage/libs/flint/fq_vec.pxd +33 -0
- sage/libs/flint/fq_zech.pxd +99 -0
- sage/libs/flint/fq_zech_embed.pxd +28 -0
- sage/libs/flint/fq_zech_mat.pxd +78 -0
- sage/libs/flint/fq_zech_poly.pxd +198 -0
- sage/libs/flint/fq_zech_poly_factor.pxd +47 -0
- sage/libs/flint/fq_zech_vec.pxd +33 -0
- sage/libs/flint/gr.pxd +174 -0
- sage/libs/flint/gr_generic.pxd +215 -0
- sage/libs/flint/gr_mat.pxd +161 -0
- sage/libs/flint/gr_mpoly.pxd +68 -0
- sage/libs/flint/gr_poly.pxd +276 -0
- sage/libs/flint/gr_special.pxd +237 -0
- sage/libs/flint/gr_vec.pxd +120 -0
- sage/libs/flint/hypgeom.pxd +24 -0
- sage/libs/flint/long_extras.pxd +23 -0
- sage/libs/flint/mag.pxd +131 -0
- sage/libs/flint/mag_macros.pxd +8 -0
- sage/libs/flint/mpf_mat.pxd +36 -0
- sage/libs/flint/mpf_vec.pxd +34 -0
- sage/libs/flint/mpfr_mat.pxd +27 -0
- sage/libs/flint/mpfr_vec.pxd +25 -0
- sage/libs/flint/mpn_extras.pxd +41 -0
- sage/libs/flint/mpoly.pxd +72 -0
- sage/libs/flint/nf.pxd +19 -0
- sage/libs/flint/nf_elem.pxd +74 -0
- sage/libs/flint/nmod.pxd +35 -0
- sage/libs/flint/nmod_mat.pxd +104 -0
- sage/libs/flint/nmod_mpoly.pxd +144 -0
- sage/libs/flint/nmod_mpoly_factor.pxd +28 -0
- sage/libs/flint/nmod_poly.pxd +339 -0
- sage/libs/flint/nmod_poly_factor.pxd +44 -0
- sage/libs/flint/nmod_poly_linkage.pxi +710 -0
- sage/libs/flint/nmod_poly_mat.pxd +76 -0
- sage/libs/flint/nmod_vec.pxd +40 -0
- sage/libs/flint/ntl_interface.pxd +17 -0
- sage/libs/flint/padic.pxd +93 -0
- sage/libs/flint/padic_mat.pxd +64 -0
- sage/libs/flint/padic_poly.pxd +88 -0
- sage/libs/flint/partitions.pxd +23 -0
- sage/libs/flint/perm.pxd +26 -0
- sage/libs/flint/profiler.pxd +24 -0
- sage/libs/flint/qadic.pxd +77 -0
- sage/libs/flint/qfb.pxd +44 -0
- sage/libs/flint/qqbar.pxd +172 -0
- sage/libs/flint/qsieve.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/libs/flint/qsieve.pxd +41 -0
- sage/libs/flint/qsieve.pyx +21 -0
- sage/libs/flint/qsieve_sage.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/libs/flint/qsieve_sage.pyx +67 -0
- sage/libs/flint/thread_pool.pxd +25 -0
- sage/libs/flint/types.pxd +2076 -0
- sage/libs/flint/ulong_extras.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/libs/flint/ulong_extras.pxd +141 -0
- sage/libs/flint/ulong_extras.pyx +21 -0
- sage/libs/flint/ulong_extras_sage.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/libs/flint/ulong_extras_sage.pyx +21 -0
- sage/matrix/all__sagemath_flint.py +1 -0
- sage/matrix/change_ring.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/matrix/change_ring.pyx +43 -0
- sage/matrix/matrix_complex_ball_dense.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/matrix/matrix_complex_ball_dense.pxd +14 -0
- sage/matrix/matrix_complex_ball_dense.pyx +973 -0
- sage/matrix/matrix_cyclo_dense.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/matrix/matrix_cyclo_dense.pxd +16 -0
- sage/matrix/matrix_cyclo_dense.pyx +1761 -0
- sage/matrix/matrix_integer_dense.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/matrix/matrix_integer_dense.pxd +32 -0
- sage/matrix/matrix_integer_dense.pyx +5801 -0
- sage/matrix/matrix_integer_dense_hnf.py +1294 -0
- sage/matrix/matrix_integer_dense_saturation.py +346 -0
- sage/matrix/matrix_integer_sparse.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/matrix/matrix_integer_sparse.pxd +9 -0
- sage/matrix/matrix_integer_sparse.pyx +1090 -0
- sage/matrix/matrix_rational_dense.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/matrix/matrix_rational_dense.pxd +23 -0
- sage/matrix/matrix_rational_dense.pyx +2995 -0
- sage/matrix/matrix_rational_sparse.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/matrix/matrix_rational_sparse.pxd +11 -0
- sage/matrix/matrix_rational_sparse.pyx +789 -0
- sage/matrix/misc_flint.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/matrix/misc_flint.pyx +109 -0
- sage/modular/all__sagemath_flint.py +1 -0
- sage/modular/modform/all__sagemath_flint.py +1 -0
- sage/modular/modform/eis_series_cython.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/modular/modform/eis_series_cython.pyx +226 -0
- sage/modular/modsym/all__sagemath_flint.py +1 -0
- sage/modular/modsym/apply.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/modular/modsym/apply.pxd +6 -0
- sage/modular/modsym/apply.pyx +113 -0
- sage/modular/modsym/heilbronn.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/modular/modsym/heilbronn.pyx +966 -0
- sage/modular/pollack_stevens/all__sagemath_flint.py +1 -0
- sage/modular/pollack_stevens/dist.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/modular/pollack_stevens/dist.pxd +38 -0
- sage/modular/pollack_stevens/dist.pyx +1439 -0
- sage/quivers/algebra.py +691 -0
- sage/quivers/algebra_elements.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/quivers/algebra_elements.pxd +97 -0
- sage/quivers/algebra_elements.pxi +1324 -0
- sage/quivers/algebra_elements.pyx +1424 -0
- sage/quivers/all.py +1 -0
- sage/quivers/ar_quiver.py +917 -0
- sage/quivers/homspace.py +640 -0
- sage/quivers/morphism.py +1282 -0
- sage/quivers/path_semigroup.py +1155 -0
- sage/quivers/paths.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/quivers/paths.pxd +13 -0
- sage/quivers/paths.pyx +809 -0
- sage/quivers/representation.py +2975 -0
- sage/rings/all__sagemath_flint.py +37 -0
- sage/rings/cif.py +4 -0
- sage/rings/complex_arb.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/rings/complex_arb.pxd +29 -0
- sage/rings/complex_arb.pyx +5176 -0
- sage/rings/complex_interval.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/rings/complex_interval.pxd +30 -0
- sage/rings/complex_interval.pyx +2475 -0
- sage/rings/complex_interval_field.py +711 -0
- sage/rings/convert/all.py +1 -0
- sage/rings/convert/mpfi.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/rings/convert/mpfi.pxd +6 -0
- sage/rings/convert/mpfi.pyx +576 -0
- sage/rings/factorint_flint.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/rings/factorint_flint.pyx +99 -0
- sage/rings/fraction_field_FpT.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/rings/fraction_field_FpT.pxd +28 -0
- sage/rings/fraction_field_FpT.pyx +2043 -0
- sage/rings/imaginary_unit.py +5 -0
- sage/rings/monomials.py +73 -0
- sage/rings/number_field/S_unit_solver.py +2870 -0
- sage/rings/number_field/all__sagemath_flint.py +7 -0
- sage/rings/number_field/bdd_height.py +664 -0
- sage/rings/number_field/class_group.py +762 -0
- sage/rings/number_field/galois_group.py +1307 -0
- sage/rings/number_field/homset.py +612 -0
- sage/rings/number_field/maps.py +687 -0
- sage/rings/number_field/morphism.py +272 -0
- sage/rings/number_field/number_field.py +12820 -0
- sage/rings/number_field/number_field_element.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/rings/number_field/number_field_element.pxd +59 -0
- sage/rings/number_field/number_field_element.pyx +5735 -0
- sage/rings/number_field/number_field_element_quadratic.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/rings/number_field/number_field_element_quadratic.pxd +34 -0
- sage/rings/number_field/number_field_element_quadratic.pyx +3185 -0
- sage/rings/number_field/number_field_ideal_rel.py +925 -0
- sage/rings/number_field/number_field_morphisms.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/rings/number_field/number_field_morphisms.pyx +781 -0
- sage/rings/number_field/number_field_rel.py +2734 -0
- sage/rings/number_field/order.py +2981 -0
- sage/rings/number_field/order_ideal.py +804 -0
- sage/rings/number_field/selmer_group.py +715 -0
- sage/rings/number_field/small_primes_of_degree_one.py +242 -0
- sage/rings/number_field/splitting_field.py +606 -0
- sage/rings/number_field/structure.py +380 -0
- sage/rings/number_field/unit_group.py +721 -0
- sage/rings/padics/all__sagemath_flint.py +3 -0
- sage/rings/polynomial/all__sagemath_flint.py +1 -0
- sage/rings/polynomial/complex_roots.py +312 -0
- sage/rings/polynomial/evaluation_flint.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/evaluation_flint.pxd +7 -0
- sage/rings/polynomial/evaluation_flint.pyx +68 -0
- sage/rings/polynomial/hilbert.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/hilbert.pyx +602 -0
- sage/rings/polynomial/polynomial_complex_arb.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/polynomial_complex_arb.pxd +7 -0
- sage/rings/polynomial/polynomial_complex_arb.pyx +963 -0
- sage/rings/polynomial/polynomial_integer_dense_flint.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/polynomial_integer_dense_flint.pxd +13 -0
- sage/rings/polynomial/polynomial_integer_dense_flint.pyx +1881 -0
- sage/rings/polynomial/polynomial_number_field.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/polynomial_number_field.pyx +345 -0
- sage/rings/polynomial/polynomial_rational_flint.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/polynomial_rational_flint.pxd +20 -0
- sage/rings/polynomial/polynomial_rational_flint.pyx +2598 -0
- sage/rings/polynomial/polynomial_zmod_flint.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/polynomial_zmod_flint.pxd +20 -0
- sage/rings/polynomial/polynomial_zmod_flint.pyx +1063 -0
- sage/rings/polynomial/real_roots.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/real_roots.pxd +81 -0
- sage/rings/polynomial/real_roots.pyx +4704 -0
- sage/rings/polynomial/refine_root.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/refine_root.pyx +142 -0
- sage/rings/polynomial/weil/all.py +4 -0
- sage/rings/polynomial/weil/power_sums.h +46 -0
- sage/rings/polynomial/weil/weil_polynomials.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/weil/weil_polynomials.pyx +596 -0
- sage/rings/qqbar.py +9025 -0
- sage/rings/real_arb.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/rings/real_arb.pxd +21 -0
- sage/rings/real_arb.pyx +4065 -0
- sage/rings/real_interval_absolute.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/rings/real_interval_absolute.pyx +1073 -0
- sage/rings/real_mpfi.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/rings/real_mpfi.pyx +5428 -0
- sage/schemes/all__sagemath_flint.py +1 -0
- sage/schemes/elliptic_curves/all__sagemath_flint.py +1 -0
- sage/schemes/elliptic_curves/descent_two_isogeny.cpython-312-aarch64-linux-gnu.so +0 -0
- sage/schemes/elliptic_curves/descent_two_isogeny.pyx +1387 -0
- 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
|