passagemath-flint 10.6.1rc10__cp310-cp310-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-310-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-310-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-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/chrompoly.pyx +555 -0
- sage/graphs/matchpoly.cpython-310-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-310-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-310-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-310-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-310-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-310-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-310-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-310-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-310-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-310-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-310-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-310-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-310-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-310-aarch64-linux-gnu.so +0 -0
- sage/matrix/change_ring.pyx +43 -0
- sage/matrix/matrix_complex_ball_dense.cpython-310-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-310-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-310-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-310-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-310-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-310-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-310-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-310-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-310-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-310-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-310-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-310-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-310-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-310-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-310-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-310-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-310-aarch64-linux-gnu.so +0 -0
- sage/rings/factorint_flint.pyx +99 -0
- sage/rings/fraction_field_FpT.cpython-310-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-310-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-310-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-310-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-310-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-310-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/hilbert.pyx +602 -0
- sage/rings/polynomial/polynomial_complex_arb.cpython-310-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-310-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-310-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/polynomial_number_field.pyx +345 -0
- sage/rings/polynomial/polynomial_rational_flint.cpython-310-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-310-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-310-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-310-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-310-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-310-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-310-aarch64-linux-gnu.so +0 -0
- sage/rings/real_interval_absolute.pyx +1073 -0
- sage/rings/real_mpfi.cpython-310-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-310-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,1294 @@
|
|
1
|
+
# sage_setup: distribution = sagemath-flint
|
2
|
+
# sage.doctest: needs sage.libs.pari
|
3
|
+
"""
|
4
|
+
Modular algorithm to compute Hermite normal forms of integer matrices
|
5
|
+
|
6
|
+
AUTHORS:
|
7
|
+
|
8
|
+
- Clement Pernet and William Stein (2008-02-07): initial version
|
9
|
+
"""
|
10
|
+
|
11
|
+
from copy import copy
|
12
|
+
|
13
|
+
from sage.arith.misc import CRT_list, previous_prime
|
14
|
+
from sage.matrix.constructor import identity_matrix, matrix, random_matrix
|
15
|
+
from sage.misc.timing import cputime
|
16
|
+
from sage.misc.verbose import verbose
|
17
|
+
from sage.rings.integer import Integer
|
18
|
+
from sage.rings.integer_ring import ZZ
|
19
|
+
from sage.rings.real_mpfr import RR
|
20
|
+
|
21
|
+
|
22
|
+
def max_det_prime(n):
|
23
|
+
"""
|
24
|
+
Return the largest prime so that it is reasonably efficient to
|
25
|
+
compute modulo that prime with n x n matrices in LinBox.
|
26
|
+
|
27
|
+
INPUT:
|
28
|
+
|
29
|
+
- ``n`` -- positive integer
|
30
|
+
|
31
|
+
OUTPUT: a prime number
|
32
|
+
|
33
|
+
EXAMPLES::
|
34
|
+
|
35
|
+
sage: from sage.matrix.matrix_integer_dense_hnf import max_det_prime
|
36
|
+
sage: max_det_prime(10000)
|
37
|
+
8388593
|
38
|
+
sage: max_det_prime(1000)
|
39
|
+
8388593
|
40
|
+
sage: max_det_prime(10)
|
41
|
+
8388593
|
42
|
+
"""
|
43
|
+
# See #14032: LinBox now uses a constant bound of 2^23.
|
44
|
+
# This is the largest prime less than that bound.
|
45
|
+
return Integer(8388593)
|
46
|
+
|
47
|
+
|
48
|
+
def det_from_modp_and_divisor(A, d, p, z_mod, moduli, z_so_far=ZZ(1), N_so_far=ZZ(1)):
|
49
|
+
"""
|
50
|
+
This is used for internal purposes for computing determinants
|
51
|
+
quickly (with the hybrid `p`-adic / multimodular algorithm).
|
52
|
+
|
53
|
+
INPUT:
|
54
|
+
|
55
|
+
- ``A`` -- a square matrix
|
56
|
+
- ``d`` -- a divisor of the determinant of A
|
57
|
+
- ``p`` -- a prime
|
58
|
+
- ``z_mod`` -- values of det/d (mod ...)
|
59
|
+
- ``moduli`` -- the moduli so far
|
60
|
+
- ``z_so_far`` -- for a modulus p in the list moduli,
|
61
|
+
(z_so_far mod p) is the determinant of A modulo p
|
62
|
+
- ``N_so_far`` -- N_so_far is the product over the primes in the list moduli
|
63
|
+
|
64
|
+
OUTPUT:
|
65
|
+
|
66
|
+
- A triple (det bound, new z_so_far, new N_so_far).
|
67
|
+
|
68
|
+
EXAMPLES::
|
69
|
+
|
70
|
+
sage: a = matrix(ZZ, 3, [6, 1, 2, -56, -2, -1, -11, 2, -3])
|
71
|
+
sage: factor(a.det())
|
72
|
+
-1 * 13 * 29
|
73
|
+
sage: d = 13
|
74
|
+
sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
|
75
|
+
sage: matrix_integer_dense_hnf.det_from_modp_and_divisor(a, d, 97, [], [])
|
76
|
+
(-377, -29, 97)
|
77
|
+
sage: a.det()
|
78
|
+
-377
|
79
|
+
"""
|
80
|
+
tm = verbose("Multimodular stage of det calculation -- using p = %s" % p, level=2)
|
81
|
+
z = A.mod(p).det() / d
|
82
|
+
z = z.lift()
|
83
|
+
z_mod.append(z)
|
84
|
+
moduli.append(p)
|
85
|
+
z = CRT_list([z_so_far, z], [N_so_far, p])
|
86
|
+
N = N_so_far*p
|
87
|
+
|
88
|
+
if z > N // 2:
|
89
|
+
z -= N
|
90
|
+
verbose("Finished multimodular det for p = %s" % p, tm, level=2)
|
91
|
+
return (d * z, z, N)
|
92
|
+
|
93
|
+
|
94
|
+
def det_given_divisor(A, d, proof=True, stabilize=2):
|
95
|
+
"""
|
96
|
+
Given a divisor d of the determinant of A, compute the determinant of A.
|
97
|
+
|
98
|
+
INPUT:
|
99
|
+
|
100
|
+
- ``A`` -- square integer matrix
|
101
|
+
- ``d`` -- nonzero integer that is assumed to divide the determinant of A
|
102
|
+
- ``proof`` -- boolean (default: ``True``); compute det modulo enough primes
|
103
|
+
so that the determinant is computed provably correctly (via the
|
104
|
+
Hadamard bound). It would be VERY hard for ``det()`` to fail even
|
105
|
+
when ``proof`` is ``False``.
|
106
|
+
- ``stabilize`` -- integer (default: 2); if proof = False, then compute
|
107
|
+
the determinant modulo `p` until ``stabilize`` successive modulo
|
108
|
+
determinant computations stabilize.
|
109
|
+
|
110
|
+
OUTPUT: integer; determinant
|
111
|
+
|
112
|
+
EXAMPLES::
|
113
|
+
|
114
|
+
sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
|
115
|
+
sage: a = matrix(ZZ,3,[-1, -1, -1, -20, 4, 1, -1, 1, 2])
|
116
|
+
sage: matrix_integer_dense_hnf.det_given_divisor(a, 3)
|
117
|
+
-30
|
118
|
+
sage: matrix_integer_dense_hnf.det_given_divisor(a, 3, proof=False)
|
119
|
+
-30
|
120
|
+
sage: matrix_integer_dense_hnf.det_given_divisor(a, 3, proof=False, stabilize=1)
|
121
|
+
-30
|
122
|
+
sage: a.det()
|
123
|
+
-30
|
124
|
+
|
125
|
+
Here we illustrate proof=False giving a wrong answer::
|
126
|
+
|
127
|
+
sage: p = matrix_integer_dense_hnf.max_det_prime(2)
|
128
|
+
sage: q = previous_prime(p)
|
129
|
+
sage: a = matrix(ZZ, 2, [p, 0, 0, q])
|
130
|
+
sage: p * q
|
131
|
+
70368442188091
|
132
|
+
sage: matrix_integer_dense_hnf.det_given_divisor(a, 1, proof=False, stabilize=2)
|
133
|
+
0
|
134
|
+
|
135
|
+
This still works, because we do not work modulo primes that divide
|
136
|
+
the determinant bound, which is found using a `p`-adic algorithm::
|
137
|
+
|
138
|
+
sage: a.det(proof=False, stabilize=2)
|
139
|
+
70368442188091
|
140
|
+
|
141
|
+
3 primes is enough::
|
142
|
+
|
143
|
+
sage: matrix_integer_dense_hnf.det_given_divisor(a, 1, proof=False, stabilize=3)
|
144
|
+
70368442188091
|
145
|
+
sage: matrix_integer_dense_hnf.det_given_divisor(a, 1, proof=False, stabilize=5)
|
146
|
+
70368442188091
|
147
|
+
sage: matrix_integer_dense_hnf.det_given_divisor(a, 1, proof=True)
|
148
|
+
70368442188091
|
149
|
+
|
150
|
+
TESTS::
|
151
|
+
|
152
|
+
sage: m = diagonal_matrix(ZZ, 68, [2]*66 + [1,1])
|
153
|
+
sage: m.det()
|
154
|
+
73786976294838206464
|
155
|
+
"""
|
156
|
+
p = max_det_prime(A.nrows())
|
157
|
+
z_mod = []
|
158
|
+
moduli = []
|
159
|
+
assert d != 0
|
160
|
+
z_so_far = 1
|
161
|
+
N_so_far = 1
|
162
|
+
if proof:
|
163
|
+
N = 1
|
164
|
+
B = (2 * 10**A.hadamard_bound()) // d + 1
|
165
|
+
dd = d
|
166
|
+
# bad verbose statement, since computing the log overflows!
|
167
|
+
est = int(RR(B).log() / RR(p).log()) + 1
|
168
|
+
cnt = 1
|
169
|
+
verbose("Multimodular det -- need to use about %s primes." % est,
|
170
|
+
level=1)
|
171
|
+
while N < B:
|
172
|
+
if d % p != 0:
|
173
|
+
tm = cputime()
|
174
|
+
dd, z_so_far, N_so_far = det_from_modp_and_divisor(A, d, p, z_mod, moduli, z_so_far, N_so_far)
|
175
|
+
N *= p
|
176
|
+
verbose("computed det mod p=%s which is %s (of about %s)" % (p, cnt, est), tm)
|
177
|
+
p = previous_prime(p)
|
178
|
+
cnt += 1
|
179
|
+
return dd
|
180
|
+
else:
|
181
|
+
val = []
|
182
|
+
while True:
|
183
|
+
if d % p:
|
184
|
+
tm = cputime()
|
185
|
+
dd, z_so_far, N_so_far = det_from_modp_and_divisor(A, d, p, z_mod, moduli, z_so_far, N_so_far)
|
186
|
+
verbose("computed det mod %s" % p, tm)
|
187
|
+
val.append(dd)
|
188
|
+
if len(val) >= stabilize and len(set(val[-stabilize:])) == 1:
|
189
|
+
return val[-1]
|
190
|
+
p = previous_prime(p)
|
191
|
+
|
192
|
+
|
193
|
+
def det_padic(A, proof=True, stabilize=2):
|
194
|
+
"""
|
195
|
+
Return the determinant of A, computed using a `p`-adic/multimodular
|
196
|
+
algorithm.
|
197
|
+
|
198
|
+
INPUT:
|
199
|
+
|
200
|
+
- ``A`` -- a square matrix
|
201
|
+
|
202
|
+
- ``proof`` -- boolean
|
203
|
+
|
204
|
+
- ``stabilize`` -- (default: 2) if proof False, number of successive primes so that
|
205
|
+
CRT det must stabilize
|
206
|
+
|
207
|
+
EXAMPLES::
|
208
|
+
|
209
|
+
sage: import sage.matrix.matrix_integer_dense_hnf as h
|
210
|
+
sage: a = matrix(ZZ, 3, [1..9])
|
211
|
+
sage: h.det_padic(a)
|
212
|
+
0
|
213
|
+
sage: a = matrix(ZZ, 3, [1,2,5,-7,8,10,192,5,18])
|
214
|
+
sage: h.det_padic(a)
|
215
|
+
-3669
|
216
|
+
sage: a.determinant(algorithm='ntl')
|
217
|
+
-3669
|
218
|
+
"""
|
219
|
+
if not A.is_square():
|
220
|
+
raise ValueError("A must be a square matrix")
|
221
|
+
r = A.rank()
|
222
|
+
if r < A.nrows():
|
223
|
+
return ZZ.zero()
|
224
|
+
v = random_matrix(ZZ, A.nrows(), 1)
|
225
|
+
d = A._solve_right_nonsingular_square(v, check_rank=False).denominator()
|
226
|
+
return det_given_divisor(A, d, proof=proof, stabilize=stabilize)
|
227
|
+
|
228
|
+
|
229
|
+
def double_det(A, b, c, proof):
|
230
|
+
"""
|
231
|
+
Compute the determinants of the stacked integer matrices
|
232
|
+
A.stack(b) and A.stack(c).
|
233
|
+
|
234
|
+
INPUT:
|
235
|
+
|
236
|
+
- ``A`` -- an (n-1) x n matrix
|
237
|
+
- ``b`` -- a 1 x n matrix
|
238
|
+
- ``c`` -- a 1 x n matrix
|
239
|
+
- ``proof`` -- whether or not to compute the det modulo enough times to
|
240
|
+
provably compute the determinant
|
241
|
+
|
242
|
+
OUTPUT: a pair of two integers
|
243
|
+
|
244
|
+
EXAMPLES::
|
245
|
+
|
246
|
+
sage: from sage.matrix.matrix_integer_dense_hnf import double_det
|
247
|
+
sage: A = matrix(ZZ, 2, 3, [1,2,3, 4,-2,5])
|
248
|
+
sage: b = matrix(ZZ, 1, 3, [1,-2,5])
|
249
|
+
sage: c = matrix(ZZ, 1, 3, [8,2,10])
|
250
|
+
sage: A.stack(b).det()
|
251
|
+
-48
|
252
|
+
sage: A.stack(c).det()
|
253
|
+
42
|
254
|
+
sage: double_det(A, b, c, False)
|
255
|
+
(-48, 42)
|
256
|
+
"""
|
257
|
+
# We use the "two for the price of one" algorithm, which I made up. (William Stein)
|
258
|
+
|
259
|
+
# This is a clever trick! First we transpose everything. Then
|
260
|
+
# we use that if [A|b]*v = c then [A|c]*w = b with w easy to write down!
|
261
|
+
# In fact w is got from v by dividing all entries by -v[n], where n is the
|
262
|
+
# number of rows of v, and *also* dividing the last entry of w by v[n] again.
|
263
|
+
# See this as an algebra exercise where you have to think of matrix vector
|
264
|
+
# multiply as "linear combination of columns".
|
265
|
+
A = A.transpose()
|
266
|
+
b = b.transpose()
|
267
|
+
c = c.transpose()
|
268
|
+
t = verbose('starting double det')
|
269
|
+
B = A.augment(b)
|
270
|
+
v = B.solve_right(-c)
|
271
|
+
|
272
|
+
db = det_given_divisor(B, v.denominator(), proof=proof)
|
273
|
+
|
274
|
+
n = v.nrows()
|
275
|
+
vn = v[n - 1, 0]
|
276
|
+
w = (-1 / vn) * v
|
277
|
+
w[n - 1] = w[n - 1] / vn
|
278
|
+
dc = det_given_divisor(A.augment(c), w.denominator(), proof=proof)
|
279
|
+
|
280
|
+
verbose('finished double det', t)
|
281
|
+
|
282
|
+
return (db, dc)
|
283
|
+
|
284
|
+
|
285
|
+
def add_column_fallback(B, a, proof):
|
286
|
+
"""
|
287
|
+
Simplistic version of add_column, in case the powerful clever one
|
288
|
+
fails (e.g., B is singular).
|
289
|
+
|
290
|
+
INPUT:
|
291
|
+
|
292
|
+
- ``B`` -- a square matrix (may be singular)
|
293
|
+
- ``a`` -- an n x 1 matrix, where B has n rows
|
294
|
+
- ``proof`` -- boolean; whether to prove result correct
|
295
|
+
|
296
|
+
OUTPUT: x; a vector such that ``H' = H_B.augment(x)`` is the HNF of
|
297
|
+
``A = B.augment(a)``
|
298
|
+
|
299
|
+
EXAMPLES::
|
300
|
+
|
301
|
+
sage: B = matrix(ZZ,3, [-1, -1, 1, -3, 8, -2, -1, -1, -1])
|
302
|
+
sage: a = matrix(ZZ,3,1, [1,2,3])
|
303
|
+
sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
|
304
|
+
sage: matrix_integer_dense_hnf.add_column_fallback(B, a, True)
|
305
|
+
[-3]
|
306
|
+
[-7]
|
307
|
+
[-2]
|
308
|
+
sage: matrix_integer_dense_hnf.add_column_fallback(B, a, False)
|
309
|
+
[-3]
|
310
|
+
[-7]
|
311
|
+
[-2]
|
312
|
+
sage: B.augment(a).hermite_form()
|
313
|
+
[ 1 1 1 -3]
|
314
|
+
[ 0 11 1 -7]
|
315
|
+
[ 0 0 2 -2]
|
316
|
+
"""
|
317
|
+
tt = verbose('add column fallback...')
|
318
|
+
W = B.augment(matrix(ZZ,B.nrows(),a.list()))
|
319
|
+
H, _ = hnf(W, proof)
|
320
|
+
C = H.matrix_from_columns([H.ncols()-1])
|
321
|
+
verbose('finished add column fallback', tt)
|
322
|
+
return C
|
323
|
+
|
324
|
+
|
325
|
+
def solve_system_with_difficult_last_row(B, a):
|
326
|
+
"""
|
327
|
+
Solve ``B*x = a`` when the last row of `B` contains huge entries using
|
328
|
+
a clever trick that reduces the problem to solve ``C*x = a`` where `C`
|
329
|
+
is `B` but with the last row replaced by something small, along
|
330
|
+
with one easy null space computation. The latter are both solved
|
331
|
+
`p`-adically.
|
332
|
+
|
333
|
+
INPUT:
|
334
|
+
|
335
|
+
- ``B`` -- a square n x n nonsingular matrix with painful big bottom row
|
336
|
+
- ``a`` -- an n x 1 column matrix
|
337
|
+
|
338
|
+
OUTPUT: the unique solution to ``B*x = a``
|
339
|
+
|
340
|
+
EXAMPLES::
|
341
|
+
|
342
|
+
sage: from sage.matrix.matrix_integer_dense_hnf import solve_system_with_difficult_last_row
|
343
|
+
sage: B = matrix(ZZ, 3, [1,2,4, 3,-4,7, 939082,2930982,132902384098234])
|
344
|
+
sage: a = matrix(ZZ,3,1, [1,2,5])
|
345
|
+
sage: z = solve_system_with_difficult_last_row(B, a)
|
346
|
+
sage: z
|
347
|
+
[ 106321906985474/132902379815497]
|
348
|
+
[132902385037291/1329023798154970]
|
349
|
+
[ -5221794/664511899077485]
|
350
|
+
sage: B*z
|
351
|
+
[1]
|
352
|
+
[2]
|
353
|
+
[5]
|
354
|
+
"""
|
355
|
+
# Here's how:
|
356
|
+
# 1. We make a copy of B but with the last *nasty* row of B replaced
|
357
|
+
# by a random very nice row.
|
358
|
+
C = copy(B)
|
359
|
+
while True:
|
360
|
+
C[C.nrows()-1] = random_matrix(ZZ,1,C.ncols()).row(0)
|
361
|
+
# 2. Then we find the unique solution to C * x = a
|
362
|
+
try:
|
363
|
+
x = C.solve_right(a)
|
364
|
+
except ValueError:
|
365
|
+
verbose("Try difficult solve again with different random vector")
|
366
|
+
else:
|
367
|
+
break
|
368
|
+
|
369
|
+
# 3. We next delete the last row of B and find a basis vector k
|
370
|
+
# for the 1-dimensional kernel.
|
371
|
+
D = B.matrix_from_rows(range(C.nrows()-1))
|
372
|
+
from .matrix_integer_iml import _rational_kernel_iml
|
373
|
+
N = _rational_kernel_iml(D)
|
374
|
+
if N.ncols() != 1:
|
375
|
+
verbose("Try difficult solve again with different random vector")
|
376
|
+
return solve_system_with_difficult_last_row(B, a)
|
377
|
+
|
378
|
+
k = N.matrix_from_columns([0])
|
379
|
+
|
380
|
+
# 4. The sought for solution z to B*z = a is some linear combination
|
381
|
+
#
|
382
|
+
# z = x + alpha*k
|
383
|
+
#
|
384
|
+
# of x and k, where k is the above fixed basis for the kernel of D.
|
385
|
+
# Setting w to be the last row of B, this column vector z satisfies
|
386
|
+
#
|
387
|
+
# w * z = a'
|
388
|
+
#
|
389
|
+
# where a' is the last entry of a. Thus
|
390
|
+
#
|
391
|
+
# w * (x + alpha*k) = a'
|
392
|
+
#
|
393
|
+
# so w * x + alpha*w*k = a'
|
394
|
+
# so alpha*w*k = a' - w*x.
|
395
|
+
|
396
|
+
w = B[-1] # last row of B
|
397
|
+
a_prime = a[-1]
|
398
|
+
lhs = w*k
|
399
|
+
rhs = a_prime - w * x
|
400
|
+
|
401
|
+
if lhs[0] == 0:
|
402
|
+
verbose("Try difficult solve again with different random vector")
|
403
|
+
return solve_system_with_difficult_last_row(B, a)
|
404
|
+
|
405
|
+
alpha = rhs[0] / lhs[0]
|
406
|
+
z = x + alpha*k
|
407
|
+
return z
|
408
|
+
|
409
|
+
|
410
|
+
def add_column(B, H_B, a, proof):
|
411
|
+
"""
|
412
|
+
The add column procedure.
|
413
|
+
|
414
|
+
INPUT:
|
415
|
+
|
416
|
+
- ``B`` -- a square matrix (may be singular)
|
417
|
+
- ``H_B`` -- the Hermite normal form of B
|
418
|
+
- ``a`` -- an n x 1 matrix, where B has n rows
|
419
|
+
- ``proof`` -- boolean; whether to prove result correct, in case we use fallback method
|
420
|
+
|
421
|
+
OUTPUT:
|
422
|
+
|
423
|
+
- x -- a vector such that H' = H_B.augment(x) is the HNF of A = B.augment(a)
|
424
|
+
|
425
|
+
EXAMPLES::
|
426
|
+
|
427
|
+
sage: B = matrix(ZZ, 3, 3, [1,2,5, 0,-5,3, 1,1,2])
|
428
|
+
sage: H_B = B.echelon_form()
|
429
|
+
sage: a = matrix(ZZ, 3, 1, [1,8,-2])
|
430
|
+
sage: import sage.matrix.matrix_integer_dense_hnf as hnf
|
431
|
+
sage: x = hnf.add_column(B, H_B, a, True); x
|
432
|
+
[18]
|
433
|
+
[ 3]
|
434
|
+
[23]
|
435
|
+
sage: H_B.augment(x)
|
436
|
+
[ 1 0 17 18]
|
437
|
+
[ 0 1 3 3]
|
438
|
+
[ 0 0 18 23]
|
439
|
+
sage: B.augment(a).echelon_form()
|
440
|
+
[ 1 0 17 18]
|
441
|
+
[ 0 1 3 3]
|
442
|
+
[ 0 0 18 23]
|
443
|
+
"""
|
444
|
+
verbose('starting add_column')
|
445
|
+
|
446
|
+
if B.rank() < B.nrows():
|
447
|
+
return add_column_fallback(B, a, proof)
|
448
|
+
else:
|
449
|
+
z = solve_system_with_difficult_last_row(B, a)
|
450
|
+
|
451
|
+
zd, d = z._clear_denom()
|
452
|
+
x = H_B * zd
|
453
|
+
if d != 1:
|
454
|
+
for i in range(x.nrows()):
|
455
|
+
x[i, 0] = x[i, 0] / d
|
456
|
+
|
457
|
+
return x
|
458
|
+
|
459
|
+
|
460
|
+
def add_row(A, b, pivots, include_zero_rows):
|
461
|
+
"""
|
462
|
+
The add row procedure.
|
463
|
+
|
464
|
+
INPUT:
|
465
|
+
|
466
|
+
- ``A`` -- a matrix in Hermite normal form with n column
|
467
|
+
- ``b`` -- an n x 1 row matrix
|
468
|
+
- ``pivots`` -- sorted list of integers; the pivot positions of A
|
469
|
+
|
470
|
+
OUTPUT:
|
471
|
+
|
472
|
+
- ``H`` -- the Hermite normal form of A.stack(b)
|
473
|
+
- ``new_pivots`` -- the pivot columns of H
|
474
|
+
|
475
|
+
EXAMPLES::
|
476
|
+
|
477
|
+
sage: import sage.matrix.matrix_integer_dense_hnf as hnf
|
478
|
+
sage: A = matrix(ZZ, 2, 3, [-21, -7, 5, 1,20,-7])
|
479
|
+
sage: b = matrix(ZZ, 1,3, [-1,1,-1])
|
480
|
+
sage: hnf.add_row(A, b, A.pivots(), True)
|
481
|
+
(
|
482
|
+
[ 1 6 29]
|
483
|
+
[ 0 7 28]
|
484
|
+
[ 0 0 46], [0, 1, 2]
|
485
|
+
)
|
486
|
+
sage: A.stack(b).echelon_form()
|
487
|
+
[ 1 6 29]
|
488
|
+
[ 0 7 28]
|
489
|
+
[ 0 0 46]
|
490
|
+
"""
|
491
|
+
t = verbose('add hnf row')
|
492
|
+
H, pivs = A._add_row_and_maintain_echelon_form(b.row(0), pivots)
|
493
|
+
if include_zero_rows and H.nrows() != A.nrows() + 1:
|
494
|
+
H = H.matrix_from_rows(range(A.nrows() + 1))
|
495
|
+
verbose('finished add hnf row', t)
|
496
|
+
return H, pivs
|
497
|
+
|
498
|
+
|
499
|
+
def pivots_of_hnf_matrix(H):
|
500
|
+
"""
|
501
|
+
Return the pivot columns of a matrix H assumed to be in HNF.
|
502
|
+
|
503
|
+
INPUT:
|
504
|
+
|
505
|
+
- ``H`` -- a matrix that must be HNF
|
506
|
+
|
507
|
+
OUTPUT: list of pivots
|
508
|
+
|
509
|
+
EXAMPLES::
|
510
|
+
|
511
|
+
sage: H = matrix(ZZ, 3, 5, [1, 0, 0, 45, -36, 0, 1, 0, 131, -107, 0, 0, 0, 178, -145]); H
|
512
|
+
[ 1 0 0 45 -36]
|
513
|
+
[ 0 1 0 131 -107]
|
514
|
+
[ 0 0 0 178 -145]
|
515
|
+
sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
|
516
|
+
sage: matrix_integer_dense_hnf.pivots_of_hnf_matrix(H)
|
517
|
+
[0, 1, 3]
|
518
|
+
"""
|
519
|
+
pivots = []
|
520
|
+
r = -1
|
521
|
+
for j in range(H.ncols()):
|
522
|
+
# Find first nonzero position (counting from bottom) in the j-th column
|
523
|
+
for i in reversed(range(H.nrows())):
|
524
|
+
if H[i, j]:
|
525
|
+
if i > r:
|
526
|
+
pivots.append(j)
|
527
|
+
r = i
|
528
|
+
else:
|
529
|
+
break
|
530
|
+
return pivots
|
531
|
+
|
532
|
+
|
533
|
+
def hnf_square(A, proof):
|
534
|
+
"""
|
535
|
+
INPUT:
|
536
|
+
|
537
|
+
- ``A`` -- a nonsingular n x n matrix over the integers
|
538
|
+
|
539
|
+
OUTPUT: the Hermite normal form of A
|
540
|
+
|
541
|
+
EXAMPLES::
|
542
|
+
|
543
|
+
sage: import sage.matrix.matrix_integer_dense_hnf as hnf
|
544
|
+
sage: A = matrix(ZZ, 3, [-21, -7, 5, 1,20,-7, -1,1,-1])
|
545
|
+
sage: hnf.hnf_square(A, False)
|
546
|
+
[ 1 6 29]
|
547
|
+
[ 0 7 28]
|
548
|
+
[ 0 0 46]
|
549
|
+
sage: A.echelon_form()
|
550
|
+
[ 1 6 29]
|
551
|
+
[ 0 7 28]
|
552
|
+
[ 0 0 46]
|
553
|
+
"""
|
554
|
+
n = A.nrows()
|
555
|
+
m = A.ncols()
|
556
|
+
if n != m:
|
557
|
+
raise ValueError("A must be square.")
|
558
|
+
|
559
|
+
# Small cases -- do not use this algorithm
|
560
|
+
if n <= 3:
|
561
|
+
return A.echelon_form(algorithm='pari')
|
562
|
+
|
563
|
+
if A.rank() < A.nrows():
|
564
|
+
raise ValueError("matrix must have full rank")
|
565
|
+
|
566
|
+
t = verbose("starting slicings")
|
567
|
+
B = A.matrix_from_rows(range(m-2)).matrix_from_columns(range(n-1))
|
568
|
+
c = A.matrix_from_rows([m-2]).matrix_from_columns(range(n-1))
|
569
|
+
d = A.matrix_from_rows([m-1]).matrix_from_columns(range(n-1))
|
570
|
+
b = A.matrix_from_columns([n-1]).matrix_from_rows(range(m-2))
|
571
|
+
verbose("done slicing", t)
|
572
|
+
|
573
|
+
try:
|
574
|
+
d1, d2 = double_det(B, c, d, proof=proof)
|
575
|
+
except (ValueError, ZeroDivisionError):
|
576
|
+
d1 = B.stack(c).det(proof=proof)
|
577
|
+
d2 = B.stack(d).det(proof=proof)
|
578
|
+
|
579
|
+
g, k, l = d1._xgcd(d2, minimal=True)
|
580
|
+
|
581
|
+
W = B.stack(k * c + l * d)
|
582
|
+
verbose("submatrix det: g=%s" % g)
|
583
|
+
CUTOFF = 2147483647 # 2^31-1
|
584
|
+
if g == 0:
|
585
|
+
# Big trouble -- matrix is not invertible
|
586
|
+
# Since we have no good conditioning code at present,
|
587
|
+
# in this case we just fall back to using pari.
|
588
|
+
H = W.echelon_form(algorithm='pari')
|
589
|
+
elif 2 * g > CUTOFF:
|
590
|
+
# Unlikely that g will be large on even slightly random input
|
591
|
+
# if it is, we fallback to the traditional algorithm.
|
592
|
+
# A nasty example is A = n*random_matrix(ZZ,m), where
|
593
|
+
# this algorithm gets killed. This is not random input though.
|
594
|
+
f = W.gcd()
|
595
|
+
g = g / (f**W.nrows())
|
596
|
+
if 2 * g <= CUTOFF:
|
597
|
+
verbose("Found common factor of %s -- dividing out; get new g = %s" % (f, g))
|
598
|
+
W0 = (W / f).change_ring(ZZ)
|
599
|
+
H = W0._hnf_mod(2 * g)
|
600
|
+
H *= f
|
601
|
+
else:
|
602
|
+
verbose("Falling back to PARI HNF since input matrix is ill conditioned for p-adic hnf algorithm.")
|
603
|
+
# We need more clever preconditioning?
|
604
|
+
# It is important to *not* just do the submatrix, since
|
605
|
+
# the whole rest of the algorithm will likely be very slow in
|
606
|
+
# weird cases where the det is large.
|
607
|
+
# E.g., matrix all of whose rows but 1 are multiplied by some
|
608
|
+
# fixed scalar n.
|
609
|
+
raise NotImplementedError("fallback to PARI")
|
610
|
+
# H = W.hermite_form(algorithm='pari')
|
611
|
+
else:
|
612
|
+
H = W._hnf_mod(2 * g)
|
613
|
+
|
614
|
+
x = add_column(W, H, b.stack(matrix(1, 1,
|
615
|
+
[k*A[m-2,m-1] + l*A[m-1,m-1]])),
|
616
|
+
proof)
|
617
|
+
Hprime = H.augment(x)
|
618
|
+
pivots = pivots_of_hnf_matrix(Hprime)
|
619
|
+
|
620
|
+
Hprime, pivots = add_row(Hprime, A.matrix_from_rows([m - 2]),
|
621
|
+
pivots, include_zero_rows=False)
|
622
|
+
Hprime, pivots = add_row(Hprime, A.matrix_from_rows([m - 1]),
|
623
|
+
pivots, include_zero_rows=False)
|
624
|
+
return Hprime.matrix_from_rows(range(m))
|
625
|
+
|
626
|
+
|
627
|
+
def interleave_matrices(A, B, cols1, cols2):
|
628
|
+
"""
|
629
|
+
INPUT:
|
630
|
+
|
631
|
+
- A, B -- matrices with the same number of rows
|
632
|
+
- cols1, cols2 -- disjoint lists of integers
|
633
|
+
|
634
|
+
OUTPUT:
|
635
|
+
|
636
|
+
construct a new matrix C by sticking the columns
|
637
|
+
of A at the positions specified by cols1 and the
|
638
|
+
columns of B at the positions specified by cols2.
|
639
|
+
|
640
|
+
EXAMPLES::
|
641
|
+
|
642
|
+
sage: A = matrix(ZZ, 2, [1,2,3,4]); B = matrix(ZZ, 2, [-1,5,2,3])
|
643
|
+
sage: A
|
644
|
+
[1 2]
|
645
|
+
[3 4]
|
646
|
+
sage: B
|
647
|
+
[-1 5]
|
648
|
+
[ 2 3]
|
649
|
+
sage: import sage.matrix.matrix_integer_dense_hnf as hnf
|
650
|
+
sage: hnf.interleave_matrices(A, B, [1,3], [0,2])
|
651
|
+
[-1 1 5 2]
|
652
|
+
[ 2 3 3 4]
|
653
|
+
"""
|
654
|
+
D = A.augment(B)
|
655
|
+
w = cols1 + cols2
|
656
|
+
v = [w.index(i) for i in range(len(cols1) + len(cols2))]
|
657
|
+
return D.matrix_from_columns(v)
|
658
|
+
|
659
|
+
|
660
|
+
def probable_pivot_rows(A):
|
661
|
+
"""
|
662
|
+
Return rows of A that are very likely to be pivots.
|
663
|
+
|
664
|
+
This really finds the pivots of A modulo a random prime.
|
665
|
+
|
666
|
+
INPUT:
|
667
|
+
|
668
|
+
- ``A`` -- a matrix
|
669
|
+
|
670
|
+
OUTPUT: a tuple of integers
|
671
|
+
|
672
|
+
EXAMPLES::
|
673
|
+
|
674
|
+
sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
|
675
|
+
sage: a = matrix(ZZ,3,[0, -1, -1, 0, -20, 1, 0, 1, 2])
|
676
|
+
sage: a
|
677
|
+
[ 0 -1 -1]
|
678
|
+
[ 0 -20 1]
|
679
|
+
[ 0 1 2]
|
680
|
+
sage: matrix_integer_dense_hnf.probable_pivot_rows(a)
|
681
|
+
(0, 1)
|
682
|
+
"""
|
683
|
+
return probable_pivot_columns(A.transpose())
|
684
|
+
|
685
|
+
|
686
|
+
def probable_pivot_columns(A):
|
687
|
+
"""
|
688
|
+
INPUT:
|
689
|
+
|
690
|
+
- ``A`` -- a matrix
|
691
|
+
|
692
|
+
OUTPUT: a tuple of integers
|
693
|
+
|
694
|
+
EXAMPLES::
|
695
|
+
|
696
|
+
sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
|
697
|
+
sage: a = matrix(ZZ,3,[0, -1, -1, 0, -20, 1, 0, 1, 2])
|
698
|
+
sage: a
|
699
|
+
[ 0 -1 -1]
|
700
|
+
[ 0 -20 1]
|
701
|
+
[ 0 1 2]
|
702
|
+
sage: matrix_integer_dense_hnf.probable_pivot_columns(a)
|
703
|
+
(1, 2)
|
704
|
+
"""
|
705
|
+
p = ZZ.random_element(10007, 46000).next_prime()
|
706
|
+
return A._reduce(p).pivots()
|
707
|
+
|
708
|
+
|
709
|
+
def ones(H, pivots):
|
710
|
+
"""
|
711
|
+
Find all 1 pivot columns of the matrix H in Hermite form, along
|
712
|
+
with the corresponding rows, and also the non 1 pivot columns and
|
713
|
+
non-pivot rows. Here a 1 pivot column is a pivot column so that
|
714
|
+
the leading bottom entry is 1.
|
715
|
+
|
716
|
+
INPUT:
|
717
|
+
|
718
|
+
- ``H`` -- matrix in Hermite form
|
719
|
+
- ``pivots`` -- list of integers (all pivot positions of H)
|
720
|
+
|
721
|
+
OUTPUT:
|
722
|
+
|
723
|
+
4-tuple of integer lists: onecol, onerow, non_oneol, non_onerow
|
724
|
+
|
725
|
+
EXAMPLES::
|
726
|
+
|
727
|
+
sage: H = matrix(ZZ, 3, 5, [1, 0, 0, 45, -36, 0, 1, 0, 131, -107, 0, 0, 0, 178, -145]); H
|
728
|
+
[ 1 0 0 45 -36]
|
729
|
+
[ 0 1 0 131 -107]
|
730
|
+
[ 0 0 0 178 -145]
|
731
|
+
sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
|
732
|
+
sage: matrix_integer_dense_hnf.ones(H, [0,1,3])
|
733
|
+
([0, 1], [0, 1], [2], [2])
|
734
|
+
"""
|
735
|
+
# Find the "onecol" pivot columns of H, i.e., the columns
|
736
|
+
# that contain exactly one "1" entry and all other entries 0.
|
737
|
+
onecol = []
|
738
|
+
onerow = []
|
739
|
+
i = 0
|
740
|
+
for c in pivots:
|
741
|
+
if H[i, c] == 1:
|
742
|
+
onecol.append(c)
|
743
|
+
onerow.append(i)
|
744
|
+
i += 1
|
745
|
+
onecol_set = set(onecol)
|
746
|
+
non_onerow = [j for j in range(len(pivots)) if j not in onerow]
|
747
|
+
non_onecol = [j for j in range(H.ncols()) if j not in onecol_set][:len(non_onerow)]
|
748
|
+
return onecol, onerow, non_onecol, non_onerow
|
749
|
+
|
750
|
+
|
751
|
+
def extract_ones_data(H, pivots):
|
752
|
+
"""
|
753
|
+
Compute ones data and corresponding submatrices of H.
|
754
|
+
|
755
|
+
This is used to optimized the :func:`add_row` function.
|
756
|
+
|
757
|
+
INPUT:
|
758
|
+
|
759
|
+
- ``H`` -- a matrix in HNF
|
760
|
+
- ``pivots`` -- list of all pivot column positions of H
|
761
|
+
|
762
|
+
OUTPUT:
|
763
|
+
|
764
|
+
C, D, E, onecol, onerow, non_onecol, non_onerow
|
765
|
+
where onecol, onerow, non_onecol, non_onerow are as for
|
766
|
+
the ones function, and C, D, E are matrices:
|
767
|
+
|
768
|
+
- ``C`` -- submatrix of all non-onecol columns and onecol rows
|
769
|
+
- ``D`` -- all non-onecol columns and other rows
|
770
|
+
- ``E`` -- inverse of D
|
771
|
+
|
772
|
+
If D is not invertible or there are 0 or more than 2 non onecols,
|
773
|
+
then C, D, and E are set to None.
|
774
|
+
|
775
|
+
EXAMPLES::
|
776
|
+
|
777
|
+
sage: H = matrix(ZZ, 3, 4, [1, 0, 0, 7, 0, 1, 5, 2, 0, 0, 6, 6])
|
778
|
+
sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
|
779
|
+
sage: matrix_integer_dense_hnf.extract_ones_data(H, [0,1,2])
|
780
|
+
(
|
781
|
+
[0]
|
782
|
+
[5], [6], [1/6], [0, 1], [0, 1], [2], [2]
|
783
|
+
)
|
784
|
+
|
785
|
+
Here we get None's since the (2,2) position submatrix is not invertible.
|
786
|
+
sage: H = matrix(ZZ, 3, 5, [1, 0, 0, 45, -36, 0, 1, 0, 131, -107, 0, 0, 0, 178, -145]); H
|
787
|
+
[ 1 0 0 45 -36]
|
788
|
+
[ 0 1 0 131 -107]
|
789
|
+
[ 0 0 0 178 -145]
|
790
|
+
sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
|
791
|
+
sage: matrix_integer_dense_hnf.extract_ones_data(H, [0,1,3])
|
792
|
+
(None, None, None, [0, 1], [0, 1], [2], [2])
|
793
|
+
"""
|
794
|
+
onecol, onerow, non_onecol, non_onerow = ones(H, pivots)
|
795
|
+
verbose('extract_ones -- got submatrix of size %s' % len(non_onecol))
|
796
|
+
if len(non_onecol) in [1, 2]:
|
797
|
+
# Extract submatrix of all non-onecol columns and onecol rows
|
798
|
+
C = H.matrix_from_rows_and_columns(onerow, non_onecol)
|
799
|
+
# Extract submatrix of all non-onecol columns and other rows
|
800
|
+
D = H.matrix_from_rows_and_columns(non_onerow, non_onecol).transpose()
|
801
|
+
tt = verbose("extract ones -- INVERT %s x %s" % (len(non_onerow), len(non_onecol)), level=1)
|
802
|
+
try:
|
803
|
+
E = D**(-1)
|
804
|
+
except ZeroDivisionError:
|
805
|
+
C = D = E = None
|
806
|
+
verbose("done inverting", tt, level=1)
|
807
|
+
return C, D, E, onecol, onerow, non_onecol, non_onerow
|
808
|
+
else:
|
809
|
+
return None, None, None, onecol, onerow, non_onecol, non_onerow
|
810
|
+
|
811
|
+
|
812
|
+
def is_in_hnf_form(H, pivots):
|
813
|
+
"""
|
814
|
+
Return whether the matrix ``H`` is in Hermite normal form
|
815
|
+
with given pivot columns.
|
816
|
+
|
817
|
+
INPUT:
|
818
|
+
|
819
|
+
- ``H`` -- matrix
|
820
|
+
- ``pivots`` -- sorted list of integers
|
821
|
+
|
822
|
+
OUTPUT: boolean
|
823
|
+
|
824
|
+
EXAMPLES::
|
825
|
+
|
826
|
+
sage: a = matrix(ZZ,3,5,[-2, -6, -3, -17, -1, 2, -1, -1, -2, -1, -2, -2, -6, 9, 2])
|
827
|
+
sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
|
828
|
+
sage: matrix_integer_dense_hnf.is_in_hnf_form(a,range(3))
|
829
|
+
False
|
830
|
+
sage: e = a.hermite_form(); p = a.pivots()
|
831
|
+
sage: matrix_integer_dense_hnf.is_in_hnf_form(e, p)
|
832
|
+
True
|
833
|
+
"""
|
834
|
+
tt = verbose('testing if matrix is in HNF')
|
835
|
+
r = 0
|
836
|
+
pivots_set = set(pivots)
|
837
|
+
for j in range(H.ncols()):
|
838
|
+
if j in pivots_set:
|
839
|
+
for i in range(r + 1, H.nrows()):
|
840
|
+
if H[i, j]:
|
841
|
+
verbose('not HNF because nonzeros below pivot position',tt)
|
842
|
+
return False
|
843
|
+
for i in range(r):
|
844
|
+
if H[i, j] < 0 or H[i, j] >= H[r, j]:
|
845
|
+
verbose('not HNF because negative or too big above pivot position',tt)
|
846
|
+
return False
|
847
|
+
r += 1
|
848
|
+
else:
|
849
|
+
for i in range(r, H.nrows()):
|
850
|
+
if H[i, j]:
|
851
|
+
verbose('not HNF nonzero in wrong place in nonpivot column',tt)
|
852
|
+
return False
|
853
|
+
verbose('done verifying in HNF -- yes', tt)
|
854
|
+
return True
|
855
|
+
|
856
|
+
|
857
|
+
def probable_hnf(A, include_zero_rows, proof):
|
858
|
+
"""
|
859
|
+
Return the HNF of A or raise an exception if something involving
|
860
|
+
the randomized nature of the algorithm goes wrong along the way.
|
861
|
+
|
862
|
+
Calling this function again a few times should result it in it
|
863
|
+
working, at least if proof=True.
|
864
|
+
|
865
|
+
INPUT:
|
866
|
+
|
867
|
+
- ``A`` -- a matrix
|
868
|
+
- ``include_zero_rows`` -- boolean
|
869
|
+
- ``proof`` -- boolean
|
870
|
+
|
871
|
+
OUTPUT:
|
872
|
+
|
873
|
+
the Hermite normal form of A.
|
874
|
+
cols -- pivot columns
|
875
|
+
|
876
|
+
EXAMPLES::
|
877
|
+
|
878
|
+
sage: a = matrix(ZZ,4,3,[-1, -1, -1, -20, 4, 1, -1, 1, 2,1,2,3])
|
879
|
+
sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
|
880
|
+
sage: matrix_integer_dense_hnf.probable_hnf(a, True, True)
|
881
|
+
(
|
882
|
+
[1 0 0]
|
883
|
+
[0 1 0]
|
884
|
+
[0 0 1]
|
885
|
+
[0 0 0], [0, 1, 2]
|
886
|
+
)
|
887
|
+
sage: matrix_integer_dense_hnf.probable_hnf(a, False, True)
|
888
|
+
(
|
889
|
+
[1 0 0]
|
890
|
+
[0 1 0]
|
891
|
+
[0 0 1], [0, 1, 2]
|
892
|
+
)
|
893
|
+
sage: matrix_integer_dense_hnf.probable_hnf(a, False, False)
|
894
|
+
(
|
895
|
+
[1 0 0]
|
896
|
+
[0 1 0]
|
897
|
+
[0 0 1], [0, 1, 2]
|
898
|
+
)
|
899
|
+
"""
|
900
|
+
# Find left-most full rank submatrix by working modulo a prime
|
901
|
+
rows = list(probable_pivot_rows(A))
|
902
|
+
B = A.matrix_from_rows(rows)
|
903
|
+
cols = list(probable_pivot_columns(B))
|
904
|
+
C = B.matrix_from_columns(cols)
|
905
|
+
# Now C is a submatrix of A that has full rank and is square.
|
906
|
+
|
907
|
+
# We compute the HNF of C, which is a square nonsingular matrix.
|
908
|
+
try:
|
909
|
+
H = hnf_square(C, proof=proof)
|
910
|
+
except NotImplementedError:
|
911
|
+
# raise
|
912
|
+
# this signals that we must fallback to PARI
|
913
|
+
verbose("generic random modular HNF algorithm failed -- we fall back to PARI")
|
914
|
+
H = A.hermite_form(algorithm='pari', include_zero_rows=include_zero_rows, proof=proof)
|
915
|
+
return H, H.pivots()
|
916
|
+
|
917
|
+
# The transformation matrix to HNF is the unique
|
918
|
+
# matrix U such that U * C = H, i.e., U = H*C^(-1).
|
919
|
+
|
920
|
+
if len(cols) < B.ncols():
|
921
|
+
# We compute the HNF of B by multiplying the matrix D
|
922
|
+
# got from the columns not in C by U:
|
923
|
+
# We want to compute X = U*D. But U = H*C^(-1),
|
924
|
+
# so X = U*D = H*C^(-1)*D.
|
925
|
+
# So C*H^(-1)*X = D
|
926
|
+
|
927
|
+
# find y s.t C*y = D
|
928
|
+
# H^(-1)*X = y ===> X = H*y
|
929
|
+
#
|
930
|
+
cols_set = set(cols)
|
931
|
+
cols2 = [i for i in range(B.ncols()) if i not in cols_set]
|
932
|
+
D = B.matrix_from_columns(cols2)
|
933
|
+
Y = C.solve_right(D)
|
934
|
+
H2 = H * Y
|
935
|
+
H2 = H2.change_ring(ZZ)
|
936
|
+
|
937
|
+
# The HNF of B is got by assembling together
|
938
|
+
# the matrices H and H2.
|
939
|
+
H = interleave_matrices(H, H2, cols, cols2)
|
940
|
+
|
941
|
+
pivots = pivots_of_hnf_matrix(H)
|
942
|
+
|
943
|
+
# Now H is the HNF of the matrix B.
|
944
|
+
# Finally we add all remaining rows of A to H using
|
945
|
+
# the add_row function.
|
946
|
+
|
947
|
+
C, D, E, onecol, onerow, non_onecol, non_onerow = extract_ones_data(H, cols)
|
948
|
+
if not proof and len(non_onecol) == 0:
|
949
|
+
# Identity matrix -- done
|
950
|
+
verbose("hnf -- got identity matrix -- early abort (0)")
|
951
|
+
if include_zero_rows:
|
952
|
+
H = pad_zeros(H, A.nrows())
|
953
|
+
return H, pivots
|
954
|
+
|
955
|
+
rows_set = set(rows)
|
956
|
+
for i in range(A.nrows()):
|
957
|
+
if i not in rows_set:
|
958
|
+
v = A.matrix_from_rows([i])
|
959
|
+
if v == 0:
|
960
|
+
continue
|
961
|
+
if E is None:
|
962
|
+
H, pivots = add_row(H, v, pivots, include_zero_rows=False)
|
963
|
+
C, D, E, onecol, onerow, non_onecol, non_onerow = extract_ones_data(H, pivots)
|
964
|
+
if not proof and len(non_onecol) == 0:
|
965
|
+
# Identity matrix -- done
|
966
|
+
verbose("hnf -- got identity matrix -- early abort (1)")
|
967
|
+
if include_zero_rows:
|
968
|
+
H = pad_zeros(H, A.nrows())
|
969
|
+
return H, pivots
|
970
|
+
else:
|
971
|
+
z = A.matrix_from_rows_and_columns([i], non_onecol)
|
972
|
+
w = A.matrix_from_rows_and_columns([i], onecol)
|
973
|
+
tt = verbose("checking denom (%s x %s)" % (D.nrows(),
|
974
|
+
D.ncols()))
|
975
|
+
Y = (z - w * C).transpose()
|
976
|
+
k = E * Y
|
977
|
+
verbose("done checking denom",tt)
|
978
|
+
if k.denominator() != 1:
|
979
|
+
H, pivots = add_row(H, v, pivots, include_zero_rows=False)
|
980
|
+
D = H.matrix_from_rows_and_columns(non_onerow, non_onecol).transpose()
|
981
|
+
nn = ones(H, pivots)
|
982
|
+
if not proof and len(nn[2]) == 0:
|
983
|
+
verbose("hnf -- got identity matrix -- early abort (2)")
|
984
|
+
if include_zero_rows:
|
985
|
+
H = pad_zeros(H, A.nrows())
|
986
|
+
return H, pivots
|
987
|
+
|
988
|
+
if include_zero_rows:
|
989
|
+
H = pad_zeros(H, A.nrows())
|
990
|
+
return H, pivots
|
991
|
+
|
992
|
+
|
993
|
+
def pad_zeros(A, nrows):
|
994
|
+
"""
|
995
|
+
Add zeros to the bottom of A so that the resulting matrix has nrows.
|
996
|
+
|
997
|
+
INPUT:
|
998
|
+
|
999
|
+
- ``A`` -- a matrix
|
1000
|
+
- ``nrows`` -- integer that is at least as big as the number of rows of A
|
1001
|
+
|
1002
|
+
OUTPUT: a matrix with nrows rows
|
1003
|
+
|
1004
|
+
EXAMPLES::
|
1005
|
+
|
1006
|
+
sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
|
1007
|
+
sage: a = matrix(ZZ, 2, 4, [1, 0, 0, 7, 0, 1, 5, 2])
|
1008
|
+
sage: matrix_integer_dense_hnf.pad_zeros(a, 4)
|
1009
|
+
[1 0 0 7]
|
1010
|
+
[0 1 5 2]
|
1011
|
+
[0 0 0 0]
|
1012
|
+
[0 0 0 0]
|
1013
|
+
sage: matrix_integer_dense_hnf.pad_zeros(a, 2)
|
1014
|
+
[1 0 0 7]
|
1015
|
+
[0 1 5 2]
|
1016
|
+
"""
|
1017
|
+
nz = nrows - A.nrows()
|
1018
|
+
if nz == 0:
|
1019
|
+
return A
|
1020
|
+
if nz < 0:
|
1021
|
+
return A.matrix_from_rows(range(nrows))
|
1022
|
+
return A.stack(matrix(ZZ, nz, A.ncols()))
|
1023
|
+
|
1024
|
+
|
1025
|
+
def hnf(A, include_zero_rows=True, proof=True):
|
1026
|
+
"""
|
1027
|
+
Return the Hermite Normal Form of a general integer matrix A,
|
1028
|
+
along with the pivot columns.
|
1029
|
+
|
1030
|
+
INPUT:
|
1031
|
+
|
1032
|
+
- ``A`` -- an n x m matrix A over the integers
|
1033
|
+
- ``include_zero_rows`` -- boolean (default: ``True``); whether or not to include zero
|
1034
|
+
rows in the output matrix
|
1035
|
+
- ``proof`` -- whether or not to prove the result correct
|
1036
|
+
|
1037
|
+
OUTPUT: tuple of:
|
1038
|
+
|
1039
|
+
- ``matrix`` -- the Hermite normal form of A
|
1040
|
+
- ``pivots`` -- the pivot column positions of A
|
1041
|
+
|
1042
|
+
EXAMPLES::
|
1043
|
+
|
1044
|
+
sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
|
1045
|
+
sage: a = matrix(ZZ,3,5,[-2, -6, -3, -17, -1, 2, -1, -1, -2, -1, -2, -2, -6, 9, 2])
|
1046
|
+
sage: matrix_integer_dense_hnf.hnf(a)
|
1047
|
+
(
|
1048
|
+
[ 2 0 26 -75 -10]
|
1049
|
+
[ 0 1 27 -73 -9]
|
1050
|
+
[ 0 0 37 -106 -13], [0, 1, 2]
|
1051
|
+
)
|
1052
|
+
sage: matrix_integer_dense_hnf.hnf(a.transpose())
|
1053
|
+
(
|
1054
|
+
[1 0 0]
|
1055
|
+
[0 1 0]
|
1056
|
+
[0 0 1]
|
1057
|
+
[0 0 0]
|
1058
|
+
[0 0 0], [0, 1, 2]
|
1059
|
+
)
|
1060
|
+
sage: matrix_integer_dense_hnf.hnf(a.transpose(), include_zero_rows=False)
|
1061
|
+
(
|
1062
|
+
[1 0 0]
|
1063
|
+
[0 1 0]
|
1064
|
+
[0 0 1], [0, 1, 2]
|
1065
|
+
)
|
1066
|
+
"""
|
1067
|
+
if A.nrows() <= 1:
|
1068
|
+
np = A.nonzero_positions()
|
1069
|
+
if not np:
|
1070
|
+
pivots = []
|
1071
|
+
if not include_zero_rows:
|
1072
|
+
A = A.new_matrix(0) # 0 rows
|
1073
|
+
else:
|
1074
|
+
i,j = np[0]
|
1075
|
+
if A[i,j] < 0:
|
1076
|
+
A = -A
|
1077
|
+
pivots = [j]
|
1078
|
+
return A, pivots
|
1079
|
+
|
1080
|
+
if not proof:
|
1081
|
+
H, pivots = probable_hnf(A, include_zero_rows=include_zero_rows,
|
1082
|
+
proof=False)
|
1083
|
+
if not include_zero_rows and len(pivots) > H.nrows():
|
1084
|
+
return H.matrix_from_rows(range(len(pivots))), pivots
|
1085
|
+
|
1086
|
+
while True:
|
1087
|
+
try:
|
1088
|
+
H, pivots = probable_hnf(A, include_zero_rows=include_zero_rows,
|
1089
|
+
proof=True)
|
1090
|
+
except ValueError:
|
1091
|
+
verbose("The attempt failed since the pivots must have been wrong. We try again.")
|
1092
|
+
continue
|
1093
|
+
|
1094
|
+
if is_in_hnf_form(H, pivots):
|
1095
|
+
if not include_zero_rows and len(pivots) > H.nrows():
|
1096
|
+
H = H.matrix_from_rows(range(len(pivots)))
|
1097
|
+
return H, pivots
|
1098
|
+
verbose("After attempt the return matrix is not in HNF form since pivots must have been wrong. We try again.")
|
1099
|
+
|
1100
|
+
|
1101
|
+
def hnf_with_transformation(A, proof=True):
|
1102
|
+
"""
|
1103
|
+
Compute the HNF H of A along with a transformation matrix U
|
1104
|
+
such that U*A = H.
|
1105
|
+
|
1106
|
+
INPUT:
|
1107
|
+
|
1108
|
+
- ``A`` -- an n x m matrix A over the integers
|
1109
|
+
- ``proof`` -- whether or not to prove the result correct
|
1110
|
+
|
1111
|
+
OUTPUT: tuple of:
|
1112
|
+
|
1113
|
+
- ``matrix`` -- the Hermite normal form H of A
|
1114
|
+
- ``U`` -- a unimodular matrix such that U * A = H
|
1115
|
+
|
1116
|
+
EXAMPLES::
|
1117
|
+
|
1118
|
+
sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
|
1119
|
+
sage: A = matrix(ZZ, 2, [1, -5, -10, 1, 3, 197]); A
|
1120
|
+
[ 1 -5 -10]
|
1121
|
+
[ 1 3 197]
|
1122
|
+
sage: H, U = matrix_integer_dense_hnf.hnf_with_transformation(A)
|
1123
|
+
sage: H
|
1124
|
+
[ 1 3 197]
|
1125
|
+
[ 0 8 207]
|
1126
|
+
sage: U
|
1127
|
+
[ 0 1]
|
1128
|
+
[-1 1]
|
1129
|
+
sage: U*A
|
1130
|
+
[ 1 3 197]
|
1131
|
+
[ 0 8 207]
|
1132
|
+
"""
|
1133
|
+
# All we do is augment the input matrix with the identity matrix of the appropriate rank on the right.
|
1134
|
+
C = A.augment(identity_matrix(ZZ, A.nrows()))
|
1135
|
+
H, _ = hnf(C, include_zero_rows=True, proof=proof)
|
1136
|
+
U = H.matrix_from_columns(range(A.ncols(), H.ncols()))
|
1137
|
+
H2 = H.matrix_from_columns(range(A.ncols()))
|
1138
|
+
return H2, U
|
1139
|
+
|
1140
|
+
|
1141
|
+
def hnf_with_transformation_tests(n=10, m=5, trials=10):
|
1142
|
+
"""
|
1143
|
+
Use this to randomly test that hnf with transformation matrix
|
1144
|
+
is working.
|
1145
|
+
|
1146
|
+
EXAMPLES::
|
1147
|
+
|
1148
|
+
sage: from sage.matrix.matrix_integer_dense_hnf import hnf_with_transformation_tests
|
1149
|
+
sage: hnf_with_transformation_tests(n=15, m=10, trials=10)
|
1150
|
+
0 1 2 3 4 5 6 7 8 9
|
1151
|
+
"""
|
1152
|
+
for i in range(trials):
|
1153
|
+
print(i, end=" ")
|
1154
|
+
A = random_matrix(ZZ, n, m)
|
1155
|
+
H, U = hnf_with_transformation(A)
|
1156
|
+
assert H == U * A
|
1157
|
+
H, U = hnf_with_transformation(A, proof=False)
|
1158
|
+
assert H == U * A
|
1159
|
+
|
1160
|
+
|
1161
|
+
# -----------------------------------------------------------------------------
|
1162
|
+
# Code for testing and benchmarking
|
1163
|
+
# ------------------------------------------------------------------------------
|
1164
|
+
|
1165
|
+
|
1166
|
+
def benchmark_hnf(nrange, bits=4):
|
1167
|
+
"""
|
1168
|
+
Run benchmark program.
|
1169
|
+
|
1170
|
+
EXAMPLES::
|
1171
|
+
|
1172
|
+
sage: import sage.matrix.matrix_integer_dense_hnf as hnf
|
1173
|
+
sage: hnf.benchmark_hnf([10,25],32)
|
1174
|
+
('sage', 10, 32, ...),
|
1175
|
+
('sage', 25, 32, ...),
|
1176
|
+
"""
|
1177
|
+
b = 2**bits
|
1178
|
+
for n in nrange:
|
1179
|
+
a = random_matrix(ZZ, n, x=-b, y=b)
|
1180
|
+
t = cputime()
|
1181
|
+
h, _ = hnf(a, proof=False)
|
1182
|
+
tm = cputime(t)
|
1183
|
+
print('%s,' % (('sage', n, bits, tm),))
|
1184
|
+
|
1185
|
+
|
1186
|
+
def benchmark_magma_hnf(nrange, bits=4):
|
1187
|
+
"""
|
1188
|
+
EXAMPLES::
|
1189
|
+
|
1190
|
+
sage: import sage.matrix.matrix_integer_dense_hnf as hnf
|
1191
|
+
sage: hnf.benchmark_magma_hnf([50,100],32) # optional - magma
|
1192
|
+
('magma', 50, 32, ...),
|
1193
|
+
('magma', 100, 32, ...),
|
1194
|
+
"""
|
1195
|
+
from sage.interfaces.magma import magma
|
1196
|
+
b = 2**bits
|
1197
|
+
for n in nrange:
|
1198
|
+
a = magma('MatrixAlgebra(IntegerRing(),%s)![Random(%s,%s) : i in [1..%s]]' % (n, -b, b, n**2))
|
1199
|
+
t = magma.cputime()
|
1200
|
+
a.EchelonForm()
|
1201
|
+
tm = magma.cputime(t)
|
1202
|
+
print('%s,' % (('magma', n, bits, tm),))
|
1203
|
+
|
1204
|
+
|
1205
|
+
def sanity_checks(times=50, n=8, m=5, proof=True, stabilize=2,
|
1206
|
+
check_using_magma=True):
|
1207
|
+
"""
|
1208
|
+
Run random sanity checks on the modular `p`-adic HNF with tall and wide matrices
|
1209
|
+
both dense and sparse.
|
1210
|
+
|
1211
|
+
INPUT:
|
1212
|
+
|
1213
|
+
- ``times`` -- number of times to randomly try matrices with each shape
|
1214
|
+
- ``n`` -- number of rows
|
1215
|
+
- ``m`` -- number of columns
|
1216
|
+
- ``proof`` -- test with proof true
|
1217
|
+
- ``stabilize`` -- parameter to pass to hnf algorithm when proof is False
|
1218
|
+
- ``check_using_magma`` -- if ``True`` use Magma instead of PARI to check
|
1219
|
+
correctness of computed HNF's. Since PARI's HNF is buggy and slow (as of
|
1220
|
+
2008-02-16 non-pivot entries sometimes are not normalized to be
|
1221
|
+
nonnegative) the default is Magma.
|
1222
|
+
|
1223
|
+
EXAMPLES::
|
1224
|
+
|
1225
|
+
sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
|
1226
|
+
sage: matrix_integer_dense_hnf.sanity_checks(times=5, check_using_magma=False)
|
1227
|
+
small 8 x 5
|
1228
|
+
0 1 2 3 4 (done)
|
1229
|
+
big 8 x 5
|
1230
|
+
0 1 2 3 4 (done)
|
1231
|
+
small 5 x 8
|
1232
|
+
0 1 2 3 4 (done)
|
1233
|
+
big 5 x 8
|
1234
|
+
0 1 2 3 4 (done)
|
1235
|
+
sparse 8 x 5
|
1236
|
+
0 1 2 3 4 (done)
|
1237
|
+
sparse 5 x 8
|
1238
|
+
0 1 2 3 4 (done)
|
1239
|
+
ill conditioned -- 1000*A -- 8 x 5
|
1240
|
+
0 1 2 3 4 (done)
|
1241
|
+
ill conditioned -- 1000*A but one row -- 8 x 5
|
1242
|
+
0 1 2 3 4 (done)
|
1243
|
+
"""
|
1244
|
+
if check_using_magma:
|
1245
|
+
from sage.interfaces.magma import magma
|
1246
|
+
|
1247
|
+
def __do_check(v):
|
1248
|
+
"""
|
1249
|
+
This is used internally by the sanity check code.
|
1250
|
+
"""
|
1251
|
+
for i, a in enumerate(v):
|
1252
|
+
global sanity
|
1253
|
+
sanity = a
|
1254
|
+
print(i, end=" ")
|
1255
|
+
if check_using_magma:
|
1256
|
+
if magma(hnf(a)[0]) != magma(a).EchelonForm():
|
1257
|
+
print("bug computing hnf of a matrix")
|
1258
|
+
print('a = matrix(ZZ, %s, %s, %s)' % (a.nrows(), a.ncols(),
|
1259
|
+
a.list()))
|
1260
|
+
return
|
1261
|
+
else:
|
1262
|
+
if hnf(a)[0] != a.echelon_form(algorithm='pari'):
|
1263
|
+
print("bug computing hnf of a matrix")
|
1264
|
+
print('a = matrix(ZZ, %s, %s, %s)' % (a.nrows(), a.ncols(),
|
1265
|
+
a.list()))
|
1266
|
+
return
|
1267
|
+
print(" (done)")
|
1268
|
+
print("small %s x %s" % (n, m))
|
1269
|
+
__do_check([random_matrix(ZZ, n, m, x=-1, y=1) for _ in range(times)])
|
1270
|
+
print("big %s x %s" % (n, m))
|
1271
|
+
__do_check([random_matrix(ZZ, n, m, x=-2**32, y=2**32)
|
1272
|
+
for _ in range(times)])
|
1273
|
+
|
1274
|
+
print("small %s x %s" % (m, n))
|
1275
|
+
__do_check([random_matrix(ZZ, m, n, x=-1, y=1) for _ in range(times)])
|
1276
|
+
print("big %s x %s" % (m, n))
|
1277
|
+
__do_check([random_matrix(ZZ, m, n, x=-2**32,y=2**32)
|
1278
|
+
for _ in range(times)])
|
1279
|
+
|
1280
|
+
print("sparse %s x %s" % (n, m))
|
1281
|
+
__do_check([random_matrix(ZZ, n, m, density=0.1) for _ in range(times)])
|
1282
|
+
print("sparse %s x %s" % (m, n))
|
1283
|
+
__do_check([random_matrix(ZZ, m, n, density=0.1) for _ in range(times)])
|
1284
|
+
|
1285
|
+
print("ill conditioned -- 1000*A -- %s x %s" % (n, m))
|
1286
|
+
__do_check([1000*random_matrix(ZZ, n, m, x=-1, y=1) for _ in range(times)])
|
1287
|
+
|
1288
|
+
print("ill conditioned -- 1000*A but one row -- %s x %s" % (n, m))
|
1289
|
+
v = []
|
1290
|
+
for _ in range(times):
|
1291
|
+
a = 1000 * random_matrix(ZZ, n, m, x=-1, y=1)
|
1292
|
+
a[a.nrows() - 1] = a[a.nrows() - 1] / 1000
|
1293
|
+
v.append(a)
|
1294
|
+
__do_check(v)
|