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,2995 @@
|
|
1
|
+
# sage_setup: distribution = sagemath-flint
|
2
|
+
"""
|
3
|
+
Dense matrices over the rational field
|
4
|
+
|
5
|
+
EXAMPLES:
|
6
|
+
|
7
|
+
We create a 3x3 matrix with rational entries and do some
|
8
|
+
operations with it.
|
9
|
+
|
10
|
+
::
|
11
|
+
|
12
|
+
sage: a = matrix(QQ, 3,3, [1,2/3, -4/5, 1,1,1, 8,2, -3/19]); a
|
13
|
+
[ 1 2/3 -4/5]
|
14
|
+
[ 1 1 1]
|
15
|
+
[ 8 2 -3/19]
|
16
|
+
sage: a.det()
|
17
|
+
2303/285
|
18
|
+
sage: a.charpoly()
|
19
|
+
x^3 - 35/19*x^2 + 1259/285*x - 2303/285
|
20
|
+
sage: b = a^(-1); b
|
21
|
+
[ -615/2303 -426/2303 418/2303]
|
22
|
+
[ 2325/2303 1779/2303 -513/2303]
|
23
|
+
[-1710/2303 950/2303 95/2303]
|
24
|
+
sage: b.det()
|
25
|
+
285/2303
|
26
|
+
sage: a == b
|
27
|
+
False
|
28
|
+
sage: a < b
|
29
|
+
False
|
30
|
+
sage: b < a
|
31
|
+
True
|
32
|
+
sage: a > b
|
33
|
+
True
|
34
|
+
sage: a*b
|
35
|
+
[1 0 0]
|
36
|
+
[0 1 0]
|
37
|
+
[0 0 1]
|
38
|
+
|
39
|
+
TESTS::
|
40
|
+
|
41
|
+
sage: a = matrix(QQ, 2, range(4), sparse=False)
|
42
|
+
sage: TestSuite(a).run()
|
43
|
+
|
44
|
+
Test hashing::
|
45
|
+
|
46
|
+
sage: m = matrix(QQ, 2, [1/2, -1, 2, 3])
|
47
|
+
sage: hash(m)
|
48
|
+
Traceback (most recent call last):
|
49
|
+
...
|
50
|
+
TypeError: mutable matrices are unhashable
|
51
|
+
sage: m.set_immutable()
|
52
|
+
sage: hash(m)
|
53
|
+
2212268000387745777 # 64-bit
|
54
|
+
1997752305 # 32-bit
|
55
|
+
"""
|
56
|
+
|
57
|
+
# ****************************************************************************
|
58
|
+
# Copyright (C) 2004,2005,2006 William Stein <wstein@gmail.com>
|
59
|
+
# 2017 Vincent Delecroix <20100.delecroix@gmail.com>
|
60
|
+
#
|
61
|
+
# This program is free software: you can redistribute it and/or modify
|
62
|
+
# it under the terms of the GNU General Public License as published by
|
63
|
+
# the Free Software Foundation, either version 2 of the License, or
|
64
|
+
# (at your option) any later version.
|
65
|
+
# https://www.gnu.org/licenses/
|
66
|
+
# ****************************************************************************
|
67
|
+
|
68
|
+
from libc.string cimport strcpy, strlen
|
69
|
+
|
70
|
+
from sage.categories.rings import Rings
|
71
|
+
from sage.cpython.string cimport char_to_str
|
72
|
+
|
73
|
+
from sage.modules.vector_rational_dense cimport Vector_rational_dense
|
74
|
+
from sage.ext.stdsage cimport PY_NEW
|
75
|
+
from sage.misc.randstate cimport randstate, current_randstate
|
76
|
+
|
77
|
+
from cysignals.signals cimport sig_on, sig_off
|
78
|
+
from cysignals.memory cimport sig_malloc, sig_free
|
79
|
+
|
80
|
+
from sage.libs.gmp.types cimport mpz_t, mpq_t
|
81
|
+
from sage.libs.gmp.mpz cimport mpz_init, mpz_clear, mpz_cmp_si
|
82
|
+
from sage.libs.gmp.mpq cimport mpq_init, mpq_clear, mpq_set_si, mpq_mul, mpq_add, mpq_set
|
83
|
+
from sage.libs.gmp.randomize cimport (mpq_randomize_entry,
|
84
|
+
mpq_randomize_entry_as_int,
|
85
|
+
mpq_randomize_entry_recip_uniform,
|
86
|
+
mpq_randomize_entry_nonzero,
|
87
|
+
mpq_randomize_entry_as_int_nonzero,
|
88
|
+
mpq_randomize_entry_recip_uniform_nonzero)
|
89
|
+
|
90
|
+
from sage.libs.flint.fmpz cimport *
|
91
|
+
from sage.libs.flint.fmpq cimport *
|
92
|
+
from sage.libs.flint.fmpz_mat cimport *
|
93
|
+
from sage.libs.flint.fmpq_mat cimport *
|
94
|
+
|
95
|
+
cimport sage.structure.element
|
96
|
+
|
97
|
+
from sage.structure.richcmp cimport rich_to_bool
|
98
|
+
from sage.rings.rational cimport Rational
|
99
|
+
from sage.matrix.matrix cimport Matrix
|
100
|
+
from sage.matrix.args cimport SparseEntry, MatrixArgs_init
|
101
|
+
from sage.matrix.matrix_integer_dense cimport Matrix_integer_dense
|
102
|
+
from sage.structure.element cimport Element, Vector
|
103
|
+
from sage.rings.integer cimport Integer
|
104
|
+
from sage.rings.integer_ring import ZZ, IntegerRing_class
|
105
|
+
import sage.rings.abc
|
106
|
+
from sage.rings.rational_field import QQ
|
107
|
+
|
108
|
+
from sage.matrix.matrix2 import decomp_seq
|
109
|
+
from sage.misc.verbose import verbose
|
110
|
+
|
111
|
+
|
112
|
+
cdef class Matrix_rational_dense(Matrix_dense):
|
113
|
+
def __cinit__(self):
|
114
|
+
"""
|
115
|
+
Create and allocate memory for the matrix.
|
116
|
+
|
117
|
+
EXAMPLES::
|
118
|
+
|
119
|
+
sage: from sage.matrix.matrix_rational_dense import Matrix_rational_dense
|
120
|
+
sage: a = Matrix_rational_dense.__new__(Matrix_rational_dense, Mat(ZZ,3), 0,0,0)
|
121
|
+
sage: type(a)
|
122
|
+
<class 'sage.matrix.matrix_rational_dense.Matrix_rational_dense'>
|
123
|
+
|
124
|
+
.. WARNING::
|
125
|
+
|
126
|
+
This is for internal use only, or if you really know what
|
127
|
+
you're doing.
|
128
|
+
"""
|
129
|
+
sig_on()
|
130
|
+
fmpq_mat_init(self._matrix, self._nrows, self._ncols)
|
131
|
+
sig_off()
|
132
|
+
|
133
|
+
cdef inline Matrix_rational_dense _new_matrix(self, Py_ssize_t nrows, Py_ssize_t ncols):
|
134
|
+
if nrows == self._nrows and ncols == self._ncols:
|
135
|
+
parent = self._parent
|
136
|
+
else:
|
137
|
+
parent = self.matrix_space(nrows, ncols)
|
138
|
+
|
139
|
+
return Matrix_rational_dense.__new__(Matrix_rational_dense, parent, None, None, None)
|
140
|
+
|
141
|
+
def __dealloc__(self):
|
142
|
+
fmpq_mat_clear(self._matrix)
|
143
|
+
|
144
|
+
def __init__(self, parent, entries=None, copy=None, bint coerce=True):
|
145
|
+
r"""
|
146
|
+
INPUT:
|
147
|
+
|
148
|
+
- ``parent`` -- a matrix space over ``QQ``
|
149
|
+
|
150
|
+
- ``entries`` -- see :func:`matrix`
|
151
|
+
|
152
|
+
- ``copy`` -- ignored (for backwards compatibility)
|
153
|
+
|
154
|
+
- ``coerce`` -- if ``False``, assume without checking that the
|
155
|
+
entries are of type :class:`Rational`
|
156
|
+
|
157
|
+
TESTS::
|
158
|
+
|
159
|
+
sage: matrix(QQ, 2, 2, 1/4)
|
160
|
+
[1/4 0]
|
161
|
+
[ 0 1/4]
|
162
|
+
sage: matrix(QQ, 3, 1, [1/2, -3/4, 0])
|
163
|
+
[ 1/2]
|
164
|
+
[-3/4]
|
165
|
+
[ 0]
|
166
|
+
sage: matrix(QQ, 2, 2, 0.5)
|
167
|
+
[1/2 0]
|
168
|
+
[ 0 1/2]
|
169
|
+
"""
|
170
|
+
ma = MatrixArgs_init(parent, entries)
|
171
|
+
cdef Rational z
|
172
|
+
for t in ma.iter(coerce, True):
|
173
|
+
se = <SparseEntry>t
|
174
|
+
z = <Rational>se.entry
|
175
|
+
fmpq_set_mpq(fmpq_mat_entry(self._matrix, se.i, se.j), z.value)
|
176
|
+
|
177
|
+
def matrix_from_columns(self, columns):
|
178
|
+
"""
|
179
|
+
Return the matrix constructed from ``self`` using columns with indices
|
180
|
+
in the columns list.
|
181
|
+
|
182
|
+
EXAMPLES::
|
183
|
+
|
184
|
+
sage: A = matrix(QQ, 3, range(9))
|
185
|
+
sage: A
|
186
|
+
[0 1 2]
|
187
|
+
[3 4 5]
|
188
|
+
[6 7 8]
|
189
|
+
sage: A.matrix_from_columns([2,1])
|
190
|
+
[2 1]
|
191
|
+
[5 4]
|
192
|
+
[8 7]
|
193
|
+
sage: A.matrix_from_columns((2,1,0,2))
|
194
|
+
[2 1 0 2]
|
195
|
+
[5 4 3 5]
|
196
|
+
[8 7 6 8]
|
197
|
+
"""
|
198
|
+
cdef Matrix_rational_dense A
|
199
|
+
cdef Py_ssize_t k, r, col
|
200
|
+
|
201
|
+
A = self._new_matrix(self._nrows, len(columns))
|
202
|
+
k = 0
|
203
|
+
for col in columns:
|
204
|
+
if col < 0 or col >= self._ncols:
|
205
|
+
raise IndexError("column out of range")
|
206
|
+
for r in range(self._nrows):
|
207
|
+
fmpq_set(fmpq_mat_entry(A._matrix, r, k), fmpq_mat_entry(self._matrix, r, col))
|
208
|
+
k = k + 1
|
209
|
+
return A
|
210
|
+
|
211
|
+
def add_to_entry(self, Py_ssize_t i, Py_ssize_t j, elt):
|
212
|
+
r"""
|
213
|
+
Add ``elt`` to the entry at position ``(i,j)``.
|
214
|
+
|
215
|
+
EXAMPLES::
|
216
|
+
|
217
|
+
sage: m = matrix(QQ, 2, 2)
|
218
|
+
sage: m.add_to_entry(0, 0, -1/3)
|
219
|
+
sage: m
|
220
|
+
[-1/3 0]
|
221
|
+
[ 0 0]
|
222
|
+
"""
|
223
|
+
if not isinstance(elt, Rational):
|
224
|
+
elt = Rational(elt)
|
225
|
+
if i < 0:
|
226
|
+
i += self._nrows
|
227
|
+
if i < 0 or i >= self._nrows:
|
228
|
+
raise IndexError("row index out of range")
|
229
|
+
if j < 0:
|
230
|
+
j += self._ncols
|
231
|
+
if j < 0 or j >= self._ncols:
|
232
|
+
raise IndexError("column index out of range")
|
233
|
+
cdef fmpq_t tmp
|
234
|
+
fmpq_init(tmp)
|
235
|
+
fmpq_set_mpq(tmp, (<Rational>elt).value)
|
236
|
+
fmpq_add(fmpq_mat_entry(self._matrix, i, j),
|
237
|
+
fmpq_mat_entry(self._matrix, i, j),
|
238
|
+
tmp)
|
239
|
+
fmpq_clear(tmp)
|
240
|
+
|
241
|
+
cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, value):
|
242
|
+
fmpq_set_mpq(fmpq_mat_entry(self._matrix, i, j), (<Rational> value).value)
|
243
|
+
|
244
|
+
cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j):
|
245
|
+
cdef Rational x
|
246
|
+
x = Rational.__new__(Rational)
|
247
|
+
fmpq_get_mpq(x.value, fmpq_mat_entry(self._matrix, i, j))
|
248
|
+
return x
|
249
|
+
|
250
|
+
cdef bint get_is_zero_unsafe(self, Py_ssize_t i, Py_ssize_t j) except -1:
|
251
|
+
"""
|
252
|
+
Return 1 if the entry (i, j) is zero, otherwise 0.
|
253
|
+
|
254
|
+
.. WARNING::
|
255
|
+
|
256
|
+
This is very unsafe; it assumes i and j are in the right
|
257
|
+
range.
|
258
|
+
"""
|
259
|
+
return fmpq_is_zero(fmpq_mat_entry(self._matrix, i,j))
|
260
|
+
|
261
|
+
cdef _add_ui_unsafe_assuming_int(self, Py_ssize_t i, Py_ssize_t j, unsigned long int n):
|
262
|
+
# doesn't check immutability
|
263
|
+
# doesn't do bounds checks.
|
264
|
+
# assumes that self[i,j] is an integer.
|
265
|
+
cdef fmpz * entry = fmpq_numref(fmpq_mat_entry(self._matrix, i, j))
|
266
|
+
fmpz_add_ui(entry, entry, n)
|
267
|
+
|
268
|
+
cdef _sub_ui_unsafe_assuming_int(self, Py_ssize_t i, Py_ssize_t j, unsigned long int n):
|
269
|
+
# doesn't check immutability
|
270
|
+
# doesn't do bounds checks.
|
271
|
+
# assumes that self[i,j] is an integer.
|
272
|
+
cdef fmpz * entry = fmpq_numref(fmpq_mat_entry(self._matrix, i, j))
|
273
|
+
fmpz_sub_ui(entry, entry, n)
|
274
|
+
|
275
|
+
def _pickle(self):
|
276
|
+
return self._pickle_version0(), 0
|
277
|
+
|
278
|
+
def _unpickle(self, data, int version):
|
279
|
+
if version == 0:
|
280
|
+
self._unpickle_version0(data)
|
281
|
+
else:
|
282
|
+
raise RuntimeError("unknown matrix version (=%s)" % version)
|
283
|
+
|
284
|
+
cdef _pickle_version0(self):
|
285
|
+
return self._export_as_string(32)
|
286
|
+
|
287
|
+
cpdef _export_as_string(self, int base=10):
|
288
|
+
"""
|
289
|
+
Return space separated string of the entries in this matrix, in the
|
290
|
+
given base. This is optimized for speed.
|
291
|
+
|
292
|
+
INPUT:
|
293
|
+
|
294
|
+
- ``base`` -- integer (default: `10`)
|
295
|
+
|
296
|
+
EXAMPLES::
|
297
|
+
|
298
|
+
sage: m = matrix(QQ,2,3,[1,2/3,-3/4,1,-2/3,-45/17])
|
299
|
+
sage: m._export_as_string(10)
|
300
|
+
'1 2/3 -3/4 1 -2/3 -45/17'
|
301
|
+
sage: m._export_as_string(16)
|
302
|
+
'1 2/3 -3/4 1 -2/3 -2d/11'
|
303
|
+
"""
|
304
|
+
cdef Py_ssize_t i, j, len_so_far, m, n
|
305
|
+
cdef char *s
|
306
|
+
cdef char *t
|
307
|
+
cdef char *tmp
|
308
|
+
|
309
|
+
if self._nrows == 0 or self._ncols == 0:
|
310
|
+
data = ''
|
311
|
+
else:
|
312
|
+
n = self._nrows * self._ncols * 10
|
313
|
+
s = <char*> sig_malloc(n * sizeof(char))
|
314
|
+
t = s
|
315
|
+
len_so_far = 0
|
316
|
+
|
317
|
+
sig_on()
|
318
|
+
for i in range(self._nrows):
|
319
|
+
for j in range(self._ncols):
|
320
|
+
m = fmpz_sizeinbase (fmpq_mat_entry_num(self._matrix, i, j), base) + \
|
321
|
+
fmpz_sizeinbase (fmpq_mat_entry_den(self._matrix, i, j), base) + 3
|
322
|
+
if len_so_far + m + 1 >= n:
|
323
|
+
# copy to new string with double the size
|
324
|
+
n = 2*n + m + 1
|
325
|
+
tmp = <char*> sig_malloc(n * sizeof(char))
|
326
|
+
strcpy(tmp, s)
|
327
|
+
sig_free(s)
|
328
|
+
s = tmp
|
329
|
+
t = s + len_so_far
|
330
|
+
fmpq_get_str(t, base, fmpq_mat_entry(self._matrix, i, j))
|
331
|
+
m = strlen(t)
|
332
|
+
len_so_far = len_so_far + m + 1
|
333
|
+
t = t + m
|
334
|
+
t[0] = <char>32
|
335
|
+
t[1] = <char>0
|
336
|
+
t = t + 1
|
337
|
+
sig_off()
|
338
|
+
data = char_to_str(s)[:-1]
|
339
|
+
sig_free(s)
|
340
|
+
return data
|
341
|
+
|
342
|
+
cdef _unpickle_version0(self, data):
|
343
|
+
r"""
|
344
|
+
TESTS::
|
345
|
+
|
346
|
+
sage: a = random_matrix(QQ, 4, 3, num_bound=2**500, den_bound=2**500)
|
347
|
+
sage: loads(dumps(a)) == a # indirect doctest
|
348
|
+
True
|
349
|
+
"""
|
350
|
+
cdef Py_ssize_t i, j, k
|
351
|
+
data = data.split()
|
352
|
+
if len(data) != self._nrows * self._ncols:
|
353
|
+
raise RuntimeError("invalid pickle data")
|
354
|
+
k = 0
|
355
|
+
for i in range(self._nrows):
|
356
|
+
for j in range(self._ncols):
|
357
|
+
s = data[k]
|
358
|
+
k += 1
|
359
|
+
if '/' in s:
|
360
|
+
num, den = (n.encode() for n in s.split('/'))
|
361
|
+
if fmpz_set_str(fmpq_mat_entry_num(self._matrix, i, j), num, 32) or \
|
362
|
+
fmpz_set_str(fmpq_mat_entry_den(self._matrix, i, j), den, 32):
|
363
|
+
raise RuntimeError("invalid pickle data")
|
364
|
+
else:
|
365
|
+
num = s.encode()
|
366
|
+
if fmpz_set_str(fmpq_mat_entry_num(self._matrix, i, j), num, 32):
|
367
|
+
raise RuntimeError("invalid pickle data")
|
368
|
+
fmpz_one(fmpq_mat_entry_den(self._matrix, i, j))
|
369
|
+
|
370
|
+
# #######################################################################
|
371
|
+
# LEVEL 2 functionality
|
372
|
+
# x * cdef _add_
|
373
|
+
# x * cdef _mul_
|
374
|
+
# x * cdef _vector_times_matrix_
|
375
|
+
# x * cpdef _richcmp_
|
376
|
+
# x * __neg__
|
377
|
+
# * __invert__
|
378
|
+
# x * __copy__
|
379
|
+
# x * _multiply_classical
|
380
|
+
# * _list -- list of underlying elements (need not be a copy)
|
381
|
+
# * _dict -- sparse dictionary of underlying elements (need not be a copy)
|
382
|
+
# #######################################################################
|
383
|
+
|
384
|
+
cpdef _lmul_(self, Element right):
|
385
|
+
"""
|
386
|
+
EXAMPLES::
|
387
|
+
|
388
|
+
sage: a = matrix(QQ, 2, range(6))
|
389
|
+
sage: (3/4) * a
|
390
|
+
[ 0 3/4 3/2]
|
391
|
+
[ 9/4 3 15/4]
|
392
|
+
"""
|
393
|
+
cdef Matrix_rational_dense M
|
394
|
+
cdef fmpq_t x
|
395
|
+
fmpq_init(x)
|
396
|
+
fmpq_set_mpq(x, (<Rational>right).value)
|
397
|
+
M = Matrix_rational_dense.__new__(Matrix_rational_dense, self._parent, None, None, None)
|
398
|
+
fmpq_mat_scalar_mul_fmpz(M._matrix, self._matrix, fmpq_numref(x))
|
399
|
+
fmpq_mat_scalar_div_fmpz(M._matrix, M._matrix, fmpq_denref(x))
|
400
|
+
fmpq_clear(x)
|
401
|
+
return M
|
402
|
+
|
403
|
+
cpdef _add_(self, right):
|
404
|
+
"""
|
405
|
+
Add two dense matrices over QQ.
|
406
|
+
|
407
|
+
EXAMPLES::
|
408
|
+
|
409
|
+
sage: a = MatrixSpace(QQ,3)(range(9))
|
410
|
+
sage: b = MatrixSpace(QQ,3)([1/n for n in range(1,10)])
|
411
|
+
sage: a+b
|
412
|
+
[ 1 3/2 7/3]
|
413
|
+
[13/4 21/5 31/6]
|
414
|
+
[43/7 57/8 73/9]
|
415
|
+
sage: b.swap_rows(1,2)
|
416
|
+
sage: #a+b
|
417
|
+
"""
|
418
|
+
cdef Matrix_rational_dense ans
|
419
|
+
ans = Matrix_rational_dense.__new__(Matrix_rational_dense, self._parent, None, None, None)
|
420
|
+
|
421
|
+
sig_on()
|
422
|
+
fmpq_mat_add(ans._matrix, self._matrix, (<Matrix_rational_dense> right)._matrix)
|
423
|
+
sig_off()
|
424
|
+
return ans
|
425
|
+
|
426
|
+
cpdef _sub_(self, right):
|
427
|
+
"""
|
428
|
+
Subtract two dense matrices over QQ.
|
429
|
+
|
430
|
+
EXAMPLES::
|
431
|
+
|
432
|
+
sage: a = MatrixSpace(QQ,3)(range(9))
|
433
|
+
sage: b = MatrixSpace(QQ,3)([1/n for n in range(1,10)])
|
434
|
+
sage: a-b
|
435
|
+
[ -1 1/2 5/3]
|
436
|
+
[11/4 19/5 29/6]
|
437
|
+
[41/7 55/8 71/9]
|
438
|
+
"""
|
439
|
+
cdef Matrix_rational_dense ans
|
440
|
+
ans = Matrix_rational_dense.__new__(Matrix_rational_dense, self._parent, None, None, None)
|
441
|
+
|
442
|
+
sig_on()
|
443
|
+
fmpq_mat_sub(ans._matrix, self._matrix, (<Matrix_rational_dense> right)._matrix)
|
444
|
+
sig_off()
|
445
|
+
return ans
|
446
|
+
|
447
|
+
cpdef _richcmp_(self, right, int op):
|
448
|
+
r"""
|
449
|
+
TESTS::
|
450
|
+
|
451
|
+
sage: M = MatrixSpace(QQ, 1)
|
452
|
+
sage: M(1) < M(2)
|
453
|
+
True
|
454
|
+
sage: M(1/3) >= M(5/2)
|
455
|
+
False
|
456
|
+
sage: M(2) == M(2)
|
457
|
+
True
|
458
|
+
sage: M(3/4) != M(2)
|
459
|
+
True
|
460
|
+
|
461
|
+
sage: matrix(QQ, 2, 3) == matrix(QQ, 2, 3)
|
462
|
+
True
|
463
|
+
sage: matrix(QQ, 2, 2) == matrix(QQ, 2, 3)
|
464
|
+
False
|
465
|
+
sage: matrix(QQ, 2, 2) == matrix(QQ, 3, 2)
|
466
|
+
False
|
467
|
+
sage: matrix(QQ, 2, 3) == matrix(QQ, 3, 2)
|
468
|
+
False
|
469
|
+
|
470
|
+
sage: mats = [matrix(QQ, 2, 2, 1), matrix(QQ, 2, 2, -1), matrix(QQ, 2, 2, 0)]
|
471
|
+
sage: mats.sort()
|
472
|
+
sage: mats == [-1, 0, 1]
|
473
|
+
True
|
474
|
+
"""
|
475
|
+
cdef Py_ssize_t i, j
|
476
|
+
cdef int k
|
477
|
+
for i in range(self._nrows):
|
478
|
+
for j in range(self._ncols):
|
479
|
+
k = fmpq_cmp(fmpq_mat_entry(self._matrix, i, j),
|
480
|
+
fmpq_mat_entry((<Matrix_rational_dense> right)._matrix, i, j))
|
481
|
+
if k:
|
482
|
+
if k > 0:
|
483
|
+
return rich_to_bool(op, 1)
|
484
|
+
else:
|
485
|
+
return rich_to_bool(op, -1)
|
486
|
+
return rich_to_bool(op, 0)
|
487
|
+
|
488
|
+
cdef _vector_times_matrix_(self, Vector v):
|
489
|
+
r"""
|
490
|
+
Return the vector times matrix product.
|
491
|
+
|
492
|
+
INPUT:
|
493
|
+
|
494
|
+
- ``v`` -- a free module element
|
495
|
+
|
496
|
+
OUTPUT: the vector times matrix product v\*A
|
497
|
+
|
498
|
+
EXAMPLES::
|
499
|
+
|
500
|
+
sage: B = matrix(QQ,2, [1,2,3,4])
|
501
|
+
sage: V = QQ^2
|
502
|
+
sage: w = V([-1,5/2])
|
503
|
+
sage: w * B
|
504
|
+
(13/2, 8)
|
505
|
+
"""
|
506
|
+
cdef Vector_rational_dense w, ans
|
507
|
+
cdef Py_ssize_t i, j
|
508
|
+
cdef mpq_t x, y, z
|
509
|
+
|
510
|
+
M = self.row_ambient_module()
|
511
|
+
w = <Vector_rational_dense> v
|
512
|
+
ans = M.zero_vector()
|
513
|
+
|
514
|
+
mpq_init(x)
|
515
|
+
mpq_init(y)
|
516
|
+
mpq_init(z)
|
517
|
+
for i in range(self._ncols):
|
518
|
+
mpq_set_si(x, 0, 1)
|
519
|
+
for j in range(self._nrows):
|
520
|
+
fmpq_get_mpq(z, fmpq_mat_entry(self._matrix, j, i))
|
521
|
+
mpq_mul(y, w._entries[j], z)
|
522
|
+
mpq_add(x, x, y)
|
523
|
+
mpq_set(ans._entries[i], x)
|
524
|
+
mpq_clear(x)
|
525
|
+
mpq_clear(y)
|
526
|
+
mpq_clear(z)
|
527
|
+
return ans
|
528
|
+
|
529
|
+
def __neg__(self):
|
530
|
+
"""
|
531
|
+
Negate a matrix over QQ.
|
532
|
+
|
533
|
+
EXAMPLES::
|
534
|
+
|
535
|
+
sage: a = matrix(QQ, 3, [1/n for n in range(1,10)])
|
536
|
+
sage: -a
|
537
|
+
[ -1 -1/2 -1/3]
|
538
|
+
[-1/4 -1/5 -1/6]
|
539
|
+
[-1/7 -1/8 -1/9]
|
540
|
+
"""
|
541
|
+
cdef Matrix_rational_dense ans
|
542
|
+
ans = Matrix_rational_dense.__new__(Matrix_rational_dense, self._parent, None, None, None)
|
543
|
+
fmpq_mat_neg(ans._matrix, self._matrix)
|
544
|
+
return ans
|
545
|
+
|
546
|
+
def __copy__(self):
|
547
|
+
"""
|
548
|
+
Copy a matrix over QQ.
|
549
|
+
|
550
|
+
TESTS::
|
551
|
+
|
552
|
+
sage: a = matrix(QQ, 3, [1/n for n in range(1,10)])
|
553
|
+
sage: b = a.__copy__()
|
554
|
+
sage: a == b
|
555
|
+
True
|
556
|
+
sage: a is b
|
557
|
+
False
|
558
|
+
sage: b[0,0] = 5
|
559
|
+
sage: a == b
|
560
|
+
False
|
561
|
+
|
562
|
+
sage: a.subdivide(2, 1)
|
563
|
+
sage: b = a.__copy__()
|
564
|
+
sage: b.subdivisions()
|
565
|
+
([2], [1])
|
566
|
+
sage: a.subdivide(2, 2)
|
567
|
+
sage: b.subdivisions()
|
568
|
+
([2], [1])
|
569
|
+
"""
|
570
|
+
cdef Matrix_rational_dense ans
|
571
|
+
ans = Matrix_rational_dense.__new__(Matrix_rational_dense, self._parent, None, None, None)
|
572
|
+
fmpq_mat_set(ans._matrix, self._matrix)
|
573
|
+
ans._subdivisions = self._subdivisions
|
574
|
+
return ans
|
575
|
+
|
576
|
+
# #######################################################################
|
577
|
+
# LEVEL 3 functionality (Optional)
|
578
|
+
# x * cdef _sub_
|
579
|
+
# * __deepcopy__
|
580
|
+
# * __invert__
|
581
|
+
# * Matrix windows -- only if you need strassen for that base
|
582
|
+
# * Other functions (list them here):
|
583
|
+
# x * denom(self):
|
584
|
+
# x * mpz_denom(self, mpz_t d):
|
585
|
+
# x * _clear_denom(self):
|
586
|
+
# o * echelon_modular(self, height_guess=None):
|
587
|
+
# #######################################################################
|
588
|
+
def __invert__(self):
|
589
|
+
"""
|
590
|
+
EXAMPLES::
|
591
|
+
|
592
|
+
sage: a = matrix(QQ,3,range(9))
|
593
|
+
sage: a.inverse()
|
594
|
+
Traceback (most recent call last):
|
595
|
+
...
|
596
|
+
ZeroDivisionError: input matrix must be nonsingular
|
597
|
+
sage: a = matrix(QQ, 2, [1, 5, 17, 3])
|
598
|
+
sage: a.inverse()
|
599
|
+
[-3/82 5/82]
|
600
|
+
[17/82 -1/82]
|
601
|
+
sage: ~matrix(QQ, 2, 3)
|
602
|
+
Traceback (most recent call last):
|
603
|
+
...
|
604
|
+
ArithmeticError: self must be a square matrix
|
605
|
+
"""
|
606
|
+
return self.inverse()
|
607
|
+
|
608
|
+
def _invert_flint(self):
|
609
|
+
r"""
|
610
|
+
TESTS::
|
611
|
+
|
612
|
+
sage: matrix(QQ, 2, [1,2,3,4])._invert_flint()
|
613
|
+
[ -2 1]
|
614
|
+
[ 3/2 -1/2]
|
615
|
+
sage: matrix(QQ, 1)._invert_flint()
|
616
|
+
Traceback (most recent call last):
|
617
|
+
...
|
618
|
+
ZeroDivisionError: input matrix must be nonsingular
|
619
|
+
"""
|
620
|
+
cdef int ret
|
621
|
+
cdef Matrix_rational_dense ans
|
622
|
+
ans = Matrix_rational_dense.__new__(Matrix_rational_dense, self._parent, None, None, None)
|
623
|
+
sig_on()
|
624
|
+
ret = fmpq_mat_inv(ans._matrix, self._matrix)
|
625
|
+
sig_off()
|
626
|
+
if ret == 0:
|
627
|
+
raise ZeroDivisionError("input matrix must be nonsingular")
|
628
|
+
return ans
|
629
|
+
|
630
|
+
def inverse(self, algorithm=None, check_invertible=True):
|
631
|
+
"""
|
632
|
+
Return the inverse of this matrix.
|
633
|
+
|
634
|
+
INPUT:
|
635
|
+
|
636
|
+
- ``algorithm`` -- an optional specification of an algorithm. It can be one of
|
637
|
+
|
638
|
+
- ``None``: (default) uses flint
|
639
|
+
|
640
|
+
- ``'flint'``: uses flint library
|
641
|
+
|
642
|
+
- ``'pari'``: uses PARI library
|
643
|
+
|
644
|
+
- ``'iml'``: uses IML library
|
645
|
+
|
646
|
+
- ``check_invertible`` -- only used when ``algorithm=iml``; whether to
|
647
|
+
check that matrix is invertible
|
648
|
+
|
649
|
+
EXAMPLES::
|
650
|
+
|
651
|
+
sage: a = matrix(QQ,3,[1,2,5,3,2,1,1,1,1,])
|
652
|
+
sage: a.inverse()
|
653
|
+
[1/2 3/2 -4]
|
654
|
+
[ -1 -2 7]
|
655
|
+
[1/2 1/2 -2]
|
656
|
+
|
657
|
+
sage: a = matrix(QQ, 2, [1, 5, 17, 3])
|
658
|
+
sage: a.inverse(algorithm='flint')
|
659
|
+
[-3/82 5/82]
|
660
|
+
[17/82 -1/82]
|
661
|
+
sage: a.inverse(algorithm='flint') * a
|
662
|
+
[1 0]
|
663
|
+
[0 1]
|
664
|
+
|
665
|
+
sage: # needs sage.libs.iml
|
666
|
+
sage: a = matrix(QQ, 2, [-1, 5, 12, -3])
|
667
|
+
sage: a.inverse(algorithm='iml')
|
668
|
+
[1/19 5/57]
|
669
|
+
[4/19 1/57]
|
670
|
+
sage: a.inverse(algorithm='iml') * a
|
671
|
+
[1 0]
|
672
|
+
[0 1]
|
673
|
+
|
674
|
+
sage: a = matrix(QQ, 4, primes_first_n(16))
|
675
|
+
sage: a.inverse(algorithm='pari') # needs sage.libs.pari
|
676
|
+
[ 3/11 -12/55 -1/5 2/11]
|
677
|
+
[ -5/11 -2/55 3/10 -3/22]
|
678
|
+
[ -13/22 307/440 -1/10 -9/88]
|
679
|
+
[ 15/22 -37/88 0 7/88]
|
680
|
+
|
681
|
+
On singular matrices this method raises a :exc:`ZeroDivisionError`::
|
682
|
+
|
683
|
+
sage: a = matrix(QQ, 2)
|
684
|
+
sage: a.inverse(algorithm='flint')
|
685
|
+
Traceback (most recent call last):
|
686
|
+
...
|
687
|
+
ZeroDivisionError: input matrix must be nonsingular
|
688
|
+
sage: a.inverse(algorithm='iml') # needs sage.libs.iml
|
689
|
+
Traceback (most recent call last):
|
690
|
+
...
|
691
|
+
ZeroDivisionError: input matrix must be nonsingular
|
692
|
+
sage: a.inverse(algorithm='pari') # needs sage.libs.pari
|
693
|
+
Traceback (most recent call last):
|
694
|
+
...
|
695
|
+
ZeroDivisionError: input matrix must be nonsingular
|
696
|
+
|
697
|
+
TESTS::
|
698
|
+
|
699
|
+
sage: a = matrix(QQ, 2)
|
700
|
+
sage: a.inverse(algorithm="IAmNotAnAlgorithm")
|
701
|
+
Traceback (most recent call last):
|
702
|
+
...
|
703
|
+
ValueError: unknown algorithm 'IAmNotAnAlgorithm'
|
704
|
+
|
705
|
+
sage: for _ in range(30):
|
706
|
+
....: dim = randint(1, 20)
|
707
|
+
....: a = random_matrix(QQ, dim, num_bound=10, den_bound=10)
|
708
|
+
....: while a.rank() != dim: a = random_matrix(QQ, dim)
|
709
|
+
....: inv_flint = a.inverse(algorithm='flint')
|
710
|
+
....: inv_pari = a.inverse(algorithm='pari')
|
711
|
+
....: inv_iml = a.inverse(algorithm='iml')
|
712
|
+
....: assert inv_flint == inv_pari == inv_iml
|
713
|
+
"""
|
714
|
+
if self._nrows != self._ncols:
|
715
|
+
raise ArithmeticError("self must be a square matrix")
|
716
|
+
|
717
|
+
if self._nrows == 0:
|
718
|
+
return self
|
719
|
+
|
720
|
+
if algorithm is None:
|
721
|
+
algorithm = "flint"
|
722
|
+
|
723
|
+
if algorithm == "flint":
|
724
|
+
return self._invert_flint()
|
725
|
+
elif algorithm == "pari":
|
726
|
+
from cypari2.handle_error import PariError
|
727
|
+
from .matrix_rational_pari import _invert_pari
|
728
|
+
try:
|
729
|
+
return _invert_pari(self)
|
730
|
+
except PariError:
|
731
|
+
raise ZeroDivisionError("input matrix must be nonsingular")
|
732
|
+
elif algorithm == "iml":
|
733
|
+
from .matrix_integer_iml import _invert_iml
|
734
|
+
AZ, denom = self._clear_denom()
|
735
|
+
B, d = _invert_iml(AZ, check_invertible=check_invertible)
|
736
|
+
return (denom/d)*B
|
737
|
+
|
738
|
+
else:
|
739
|
+
raise ValueError("unknown algorithm '%s'" % algorithm)
|
740
|
+
|
741
|
+
def determinant(self, algorithm=None, proof=None):
|
742
|
+
"""
|
743
|
+
Return the determinant of this matrix.
|
744
|
+
|
745
|
+
INPUT:
|
746
|
+
|
747
|
+
- ``algorithm`` -- an optional specification of an algorithm. It can be one of
|
748
|
+
|
749
|
+
- ``None``: (default) uses flint
|
750
|
+
|
751
|
+
- ``'flint'``: uses flint library
|
752
|
+
|
753
|
+
- ``'pari'``: uses PARI library
|
754
|
+
|
755
|
+
- ``'integer'``: removes denominator and call determinant on the corresponding
|
756
|
+
integer matrix
|
757
|
+
|
758
|
+
- ``'generic'``: calls the generic Sage implementation
|
759
|
+
|
760
|
+
- ``proof`` -- boolean or ``None``; if ``None`` use
|
761
|
+
proof.linear_algebra(); only relevant for the padic algorithm
|
762
|
+
|
763
|
+
.. NOTE::
|
764
|
+
|
765
|
+
It would be *VERY VERY* hard for det to fail even with
|
766
|
+
proof=False.
|
767
|
+
|
768
|
+
EXAMPLES::
|
769
|
+
|
770
|
+
sage: m = matrix(QQ,3,[1,2/3,4/5, 2,2,2, 5,3,2/5])
|
771
|
+
sage: m.determinant()
|
772
|
+
-34/15
|
773
|
+
sage: m.charpoly()
|
774
|
+
x^3 - 17/5*x^2 - 122/15*x + 34/15
|
775
|
+
|
776
|
+
sage: m = matrix(QQ, 3, [(1/i)**j for i in range(2,5) for j in range(3)])
|
777
|
+
sage: m.determinant(algorithm='flint')
|
778
|
+
-1/288
|
779
|
+
|
780
|
+
sage: m = matrix(QQ, 4, [(-1)**n/n for n in range(1,17)])
|
781
|
+
sage: m.determinant(algorithm='pari') # needs sage.libs.pari
|
782
|
+
2/70945875
|
783
|
+
|
784
|
+
sage: m = matrix(QQ, 5, [1/(i+j+1) for i in range(5) for j in range(5)])
|
785
|
+
sage: m.determinant(algorithm='integer')
|
786
|
+
1/266716800000
|
787
|
+
|
788
|
+
On non-square matrices, the method raises a :exc:`ValueError`::
|
789
|
+
|
790
|
+
sage: matrix(QQ, 2, 3).determinant(algorithm='flint')
|
791
|
+
Traceback (most recent call last):
|
792
|
+
...
|
793
|
+
ValueError: non square matrix
|
794
|
+
sage: matrix(QQ, 2, 3).determinant(algorithm='pari') # needs sage.libs.pari
|
795
|
+
Traceback (most recent call last):
|
796
|
+
...
|
797
|
+
ValueError: non square matrix
|
798
|
+
sage: matrix(QQ, 2, 3).determinant(algorithm='integer')
|
799
|
+
Traceback (most recent call last):
|
800
|
+
...
|
801
|
+
ValueError: non square matrix
|
802
|
+
sage: matrix(QQ, 2, 3).determinant(algorithm='generic')
|
803
|
+
Traceback (most recent call last):
|
804
|
+
...
|
805
|
+
ValueError: non square matrix
|
806
|
+
|
807
|
+
TESTS:
|
808
|
+
|
809
|
+
Check that the four algorithms agree::
|
810
|
+
|
811
|
+
sage: # needs sage.libs.pari
|
812
|
+
sage: for _ in range(20):
|
813
|
+
....: dim = randint(0, 30)
|
814
|
+
....: m = random_matrix(QQ, dim, num_bound=10, den_bound=10)
|
815
|
+
....: det_flint = m.determinant("flint"); m._clear_cache()
|
816
|
+
....: det_pari = m.determinant("pari"); m._clear_cache()
|
817
|
+
....: det_int = m.determinant("integer"); m._clear_cache()
|
818
|
+
....: det_gen = m.determinant("generic")
|
819
|
+
....: assert det_flint == det_pari == det_int == det_gen
|
820
|
+
"""
|
821
|
+
if self._nrows != self._ncols:
|
822
|
+
raise ValueError("non square matrix")
|
823
|
+
|
824
|
+
det = self.fetch('det')
|
825
|
+
if det is not None:
|
826
|
+
return det
|
827
|
+
|
828
|
+
if algorithm is None or algorithm == "flint":
|
829
|
+
det = self._det_flint()
|
830
|
+
elif algorithm == "pari":
|
831
|
+
from .matrix_rational_pari import _det_pari
|
832
|
+
det = _det_pari(self)
|
833
|
+
elif algorithm == "integer":
|
834
|
+
A, denom = self._clear_denom()
|
835
|
+
det = Rational(A.determinant(proof=proof))
|
836
|
+
if not denom.is_one():
|
837
|
+
det = det / (denom ** self.nrows())
|
838
|
+
elif algorithm == "generic":
|
839
|
+
det = Matrix_dense.determinant(self)
|
840
|
+
else:
|
841
|
+
raise ValueError("unknown algorithm '%s'" % algorithm)
|
842
|
+
|
843
|
+
self.cache('det', det)
|
844
|
+
return det
|
845
|
+
|
846
|
+
def _det_flint(self):
|
847
|
+
r"""
|
848
|
+
Return the determinant (computed using flint).
|
849
|
+
|
850
|
+
EXAMPLES::
|
851
|
+
|
852
|
+
sage: matrix(QQ, 2, [1/3, 2/5, 3/4, 7/8])._det_flint()
|
853
|
+
-1/120
|
854
|
+
sage: matrix(QQ, 0)._det_flint()
|
855
|
+
1
|
856
|
+
sage: matrix(QQ, 1, [0])._det_flint()
|
857
|
+
0
|
858
|
+
"""
|
859
|
+
cdef Rational d = Rational.__new__(Rational)
|
860
|
+
cdef fmpq_t e
|
861
|
+
fmpq_init(e)
|
862
|
+
sig_on()
|
863
|
+
fmpq_mat_det(e, self._matrix)
|
864
|
+
fmpq_get_mpq(d.value, e)
|
865
|
+
sig_off()
|
866
|
+
return d
|
867
|
+
|
868
|
+
def denominator(self):
|
869
|
+
"""
|
870
|
+
Return the denominator of this matrix.
|
871
|
+
|
872
|
+
OUTPUT: a Sage Integer
|
873
|
+
|
874
|
+
EXAMPLES::
|
875
|
+
|
876
|
+
sage: b = matrix(QQ,2,range(6)); b[0,0]=-5007/293; b
|
877
|
+
[-5007/293 1 2]
|
878
|
+
[ 3 4 5]
|
879
|
+
sage: b.denominator()
|
880
|
+
293
|
881
|
+
|
882
|
+
sage: matrix(QQ, 2, [1/2, 1/3, 1/4, 1/5]).denominator()
|
883
|
+
60
|
884
|
+
"""
|
885
|
+
cdef Integer z = Integer.__new__(Integer)
|
886
|
+
cdef fmpz_t tmp
|
887
|
+
fmpz_init(tmp)
|
888
|
+
self.fmpz_denom(tmp)
|
889
|
+
fmpz_get_mpz(z.value, tmp)
|
890
|
+
fmpz_clear(tmp)
|
891
|
+
return z
|
892
|
+
|
893
|
+
cdef int fmpz_denom(self, fmpz_t d) except -1:
|
894
|
+
cdef Py_ssize_t i, j
|
895
|
+
sig_on()
|
896
|
+
fmpz_one(d)
|
897
|
+
for i in range(self._nrows):
|
898
|
+
for j in range(self._ncols):
|
899
|
+
fmpz_lcm(d, d, fmpq_mat_entry_den(self._matrix, i, j))
|
900
|
+
sig_off()
|
901
|
+
return 0
|
902
|
+
|
903
|
+
def _clear_denom(self):
|
904
|
+
r"""
|
905
|
+
INPUT:
|
906
|
+
|
907
|
+
- ``self`` -- a matrix
|
908
|
+
|
909
|
+
OUTPUT: ``D*self, D``
|
910
|
+
|
911
|
+
The product is a matrix over `\ZZ`.
|
912
|
+
|
913
|
+
EXAMPLES::
|
914
|
+
|
915
|
+
sage: a = matrix(QQ,2,[-1/6,-7,3,5/4]); a
|
916
|
+
[-1/6 -7]
|
917
|
+
[ 3 5/4]
|
918
|
+
sage: b, d = a._clear_denom()
|
919
|
+
sage: b
|
920
|
+
[ -2 -84]
|
921
|
+
[ 36 15]
|
922
|
+
sage: d
|
923
|
+
12
|
924
|
+
sage: b == d * a
|
925
|
+
True
|
926
|
+
"""
|
927
|
+
X = self.fetch('clear_denom')
|
928
|
+
if X is not None:
|
929
|
+
return X
|
930
|
+
|
931
|
+
cdef Py_ssize_t i, j
|
932
|
+
cdef Matrix_integer_dense A
|
933
|
+
cdef fmpz * entry
|
934
|
+
cdef fmpz_t denom
|
935
|
+
fmpz_init(denom)
|
936
|
+
self.fmpz_denom(denom)
|
937
|
+
|
938
|
+
from sage.matrix.matrix_space import MatrixSpace
|
939
|
+
MZ = MatrixSpace(ZZ, self._nrows, self._ncols, sparse=False)
|
940
|
+
A = Matrix_integer_dense.__new__(Matrix_integer_dense, MZ, None, None, None)
|
941
|
+
|
942
|
+
sig_on()
|
943
|
+
for i in range(self._nrows):
|
944
|
+
for j in range(self._ncols):
|
945
|
+
entry = fmpz_mat_entry(A._matrix, i, j)
|
946
|
+
fmpz_divexact(entry, denom, fmpq_mat_entry_den(self._matrix, i, j))
|
947
|
+
fmpz_mul(entry, entry, fmpq_mat_entry_num(self._matrix, i, j))
|
948
|
+
sig_off()
|
949
|
+
|
950
|
+
cdef Integer D = PY_NEW(Integer)
|
951
|
+
fmpz_get_mpz(D.value, denom)
|
952
|
+
fmpz_clear(denom)
|
953
|
+
X = (A, D)
|
954
|
+
self.cache('clear_denom', X)
|
955
|
+
return X
|
956
|
+
|
957
|
+
def charpoly(self, var='x', algorithm=None):
|
958
|
+
r"""
|
959
|
+
Return the characteristic polynomial of this matrix.
|
960
|
+
|
961
|
+
.. NOTE::
|
962
|
+
|
963
|
+
The characteristic polynomial is defined as `\det(xI-A)`.
|
964
|
+
|
965
|
+
INPUT:
|
966
|
+
|
967
|
+
- ``var`` -- (optional) name of the variable as a string
|
968
|
+
|
969
|
+
- ``algorithm`` -- an optional specification of an algorithm. It can be
|
970
|
+
one of:
|
971
|
+
|
972
|
+
- ``None``: (default) will use flint for small dimensions and linbox
|
973
|
+
otherwise
|
974
|
+
|
975
|
+
- ``'flint'``: uses flint library
|
976
|
+
|
977
|
+
- ``'linbox'``: uses linbox library
|
978
|
+
|
979
|
+
- ``'generic'``: uses Sage generic implementation
|
980
|
+
|
981
|
+
OUTPUT: a polynomial over the rational numbers
|
982
|
+
|
983
|
+
EXAMPLES::
|
984
|
+
|
985
|
+
sage: a = matrix(QQ, 3, [4/3, 2/5, 1/5, 4, -3/2, 0, 0, -2/3, 3/4])
|
986
|
+
sage: f = a.charpoly(); f
|
987
|
+
x^3 - 7/12*x^2 - 149/40*x + 97/30
|
988
|
+
sage: f(a)
|
989
|
+
[0 0 0]
|
990
|
+
[0 0 0]
|
991
|
+
[0 0 0]
|
992
|
+
|
993
|
+
TESTS:
|
994
|
+
|
995
|
+
The cached polynomial should be independent of the ``var``
|
996
|
+
argument (:issue:`12292`). We check (indirectly) that the
|
997
|
+
second call uses the cached value by noting that its result is
|
998
|
+
not cached::
|
999
|
+
|
1000
|
+
sage: M = MatrixSpace(QQ, 2)
|
1001
|
+
sage: A = M(range(0, 2^2))
|
1002
|
+
sage: type(A)
|
1003
|
+
<class 'sage.matrix.matrix_rational_dense.Matrix_rational_dense'>
|
1004
|
+
sage: A.charpoly('x')
|
1005
|
+
x^2 - 3*x - 2
|
1006
|
+
sage: A.charpoly('y')
|
1007
|
+
y^2 - 3*y - 2
|
1008
|
+
sage: A._cache['charpoly']
|
1009
|
+
x^2 - 3*x - 2
|
1010
|
+
|
1011
|
+
Check consistency::
|
1012
|
+
|
1013
|
+
sage: for _ in range(100): # needs sage.libs.linbox
|
1014
|
+
....: dim = randint(0, 10)
|
1015
|
+
....: m = random_matrix(QQ, dim, num_bound=8, den_bound=8)
|
1016
|
+
....: p_flint = m.charpoly(algorithm='flint'); m._clear_cache()
|
1017
|
+
....: p_linbox = m.charpoly(algorithm='linbox'); m._clear_cache()
|
1018
|
+
....: p_generic = m.charpoly(algorithm='generic')
|
1019
|
+
....: assert p_flint == p_linbox == p_generic
|
1020
|
+
"""
|
1021
|
+
poly = self.fetch('charpoly')
|
1022
|
+
if poly is not None:
|
1023
|
+
return poly.change_variable_name(var)
|
1024
|
+
|
1025
|
+
if algorithm is None:
|
1026
|
+
algorithm = 'flint' if self._nrows <= 40 else 'linbox'
|
1027
|
+
|
1028
|
+
if algorithm == 'flint' or algorithm == 'linbox':
|
1029
|
+
A, denom = self._clear_denom()
|
1030
|
+
f = A.charpoly(var, algorithm=algorithm)
|
1031
|
+
x = f.parent().gen()
|
1032
|
+
g = f(x * denom) / denom ** f.degree()
|
1033
|
+
elif algorithm == 'generic':
|
1034
|
+
g = Matrix_dense.charpoly(self, var)
|
1035
|
+
else:
|
1036
|
+
raise ValueError("no algorithm '%s'" % algorithm)
|
1037
|
+
|
1038
|
+
self.cache('charpoly', g)
|
1039
|
+
return g
|
1040
|
+
|
1041
|
+
def minpoly(self, var='x', algorithm=None):
|
1042
|
+
"""
|
1043
|
+
Return the minimal polynomial of this matrix.
|
1044
|
+
|
1045
|
+
INPUT:
|
1046
|
+
|
1047
|
+
- ``var`` -- (optional) the variable name as a string (default: ``'x'``)
|
1048
|
+
|
1049
|
+
- ``algorithm`` -- an optional specification of an algorithm. It can
|
1050
|
+
be one of
|
1051
|
+
|
1052
|
+
- ``None``: (default) will use linbox
|
1053
|
+
|
1054
|
+
- ``'linbox'``: uses the linbox library
|
1055
|
+
|
1056
|
+
- ``'generic'``: uses the generic Sage implementation
|
1057
|
+
|
1058
|
+
OUTPUT: a polynomial over the rationals
|
1059
|
+
|
1060
|
+
EXAMPLES::
|
1061
|
+
|
1062
|
+
sage: a = matrix(QQ, 3, [4/3, 2/5, 1/5, 4, -3/2, 0, 0, -2/3, 3/4])
|
1063
|
+
sage: f = a.minpoly(); f
|
1064
|
+
x^3 - 7/12*x^2 - 149/40*x + 97/30
|
1065
|
+
sage: a = Mat(ZZ,4)(range(16))
|
1066
|
+
sage: f = a.minpoly(); f.factor() # needs sage.libs.pari
|
1067
|
+
x * (x^2 - 30*x - 80)
|
1068
|
+
sage: f(a) == 0 # needs sage.libs.pari
|
1069
|
+
True
|
1070
|
+
|
1071
|
+
::
|
1072
|
+
|
1073
|
+
sage: # needs sage.libs.pari
|
1074
|
+
sage: a = matrix(QQ, 4, [1..4^2])
|
1075
|
+
sage: factor(a.minpoly())
|
1076
|
+
x * (x^2 - 34*x - 80)
|
1077
|
+
sage: factor(a.minpoly('y'))
|
1078
|
+
y * (y^2 - 34*y - 80)
|
1079
|
+
sage: factor(a.charpoly())
|
1080
|
+
x^2 * (x^2 - 34*x - 80)
|
1081
|
+
sage: b = matrix(QQ, 4, [-1, 2, 2, 0, 0, 4, 2, 2, 0, 0, -1, -2, 0, -4, 0, 4])
|
1082
|
+
sage: a = matrix(QQ, 4, [1, 1, 0,0, 0,1,0,0, 0,0,5,0, 0,0,0,5])
|
1083
|
+
sage: c = b^(-1)*a*b
|
1084
|
+
sage: factor(c.minpoly())
|
1085
|
+
(x - 5) * (x - 1)^2
|
1086
|
+
sage: factor(c.charpoly())
|
1087
|
+
(x - 5)^2 * (x - 1)^2
|
1088
|
+
|
1089
|
+
Check consistency::
|
1090
|
+
|
1091
|
+
sage: for _ in range(100): # needs sage.libs.linbox sage.libs.pari
|
1092
|
+
....: dim = randint(0, 10)
|
1093
|
+
....: m = random_matrix(QQ, dim, num_bound=8, den_bound=8)
|
1094
|
+
....: p_linbox = m.charpoly(algorithm='linbox'); m._clear_cache()
|
1095
|
+
....: p_generic = m.charpoly(algorithm='generic')
|
1096
|
+
....: assert p_linbox == p_generic
|
1097
|
+
"""
|
1098
|
+
poly = self.fetch('minpoly')
|
1099
|
+
if poly is not None:
|
1100
|
+
return poly.change_variable_name(var)
|
1101
|
+
|
1102
|
+
if algorithm is None:
|
1103
|
+
try:
|
1104
|
+
from .matrix_integer_linbox import _minpoly_linbox
|
1105
|
+
except ImportError:
|
1106
|
+
algorithm = 'generic'
|
1107
|
+
else:
|
1108
|
+
algorithm = 'linbox'
|
1109
|
+
|
1110
|
+
if algorithm == 'linbox':
|
1111
|
+
A, denom = self._clear_denom()
|
1112
|
+
f = A.minpoly(var, algorithm='linbox')
|
1113
|
+
x = f.parent().gen()
|
1114
|
+
g = f(x * denom) / denom**f.degree()
|
1115
|
+
elif algorithm == 'generic':
|
1116
|
+
g = Matrix_dense.minpoly(self, var)
|
1117
|
+
else:
|
1118
|
+
raise ValueError("no algorithm '%s'" % algorithm)
|
1119
|
+
|
1120
|
+
self.cache('minpoly', g)
|
1121
|
+
return g
|
1122
|
+
|
1123
|
+
cdef sage.structure.element.Matrix _matrix_times_matrix_(self, sage.structure.element.Matrix right):
|
1124
|
+
"""
|
1125
|
+
EXAMPLES::
|
1126
|
+
|
1127
|
+
sage: a = matrix(QQ, 3, range(9))/3
|
1128
|
+
sage: b = matrix(QQ, 3, range(1, 10))/5
|
1129
|
+
sage: a * b # indirect doctest
|
1130
|
+
[ 6/5 7/5 8/5]
|
1131
|
+
[18/5 22/5 26/5]
|
1132
|
+
[ 6 37/5 44/5]
|
1133
|
+
|
1134
|
+
sage: matrix(QQ, 2, 3) * matrix(QQ, 4, 5)
|
1135
|
+
Traceback (most recent call last):
|
1136
|
+
...
|
1137
|
+
TypeError: unsupported operand parent(s) for *: 'Full MatrixSpace of 2 by 3 dense matrices over Rational Field' and 'Full MatrixSpace of 4 by 5 dense matrices over Rational Field'
|
1138
|
+
"""
|
1139
|
+
return self._multiply_flint(right)
|
1140
|
+
|
1141
|
+
def _multiply_flint(self, Matrix_rational_dense right):
|
1142
|
+
r"""
|
1143
|
+
Multiply this matrix by ``right`` using the flint library.
|
1144
|
+
|
1145
|
+
EXAMPLES::
|
1146
|
+
|
1147
|
+
sage: n = 3
|
1148
|
+
sage: a = matrix(QQ,n,range(n^2))/3
|
1149
|
+
sage: b = matrix(QQ,n,range(1, n^2 + 1))/5
|
1150
|
+
sage: a._multiply_flint(b)
|
1151
|
+
[ 6/5 7/5 8/5]
|
1152
|
+
[18/5 22/5 26/5]
|
1153
|
+
[ 6 37/5 44/5]
|
1154
|
+
"""
|
1155
|
+
if self._nrows == right._nrows:
|
1156
|
+
# self acts on the space of right
|
1157
|
+
parent = right.parent()
|
1158
|
+
if self._ncols == right._ncols:
|
1159
|
+
# right acts on the space of self
|
1160
|
+
parent = self.parent()
|
1161
|
+
else:
|
1162
|
+
parent = self.matrix_space(self._nrows, right._ncols)
|
1163
|
+
|
1164
|
+
cdef Matrix_rational_dense ans
|
1165
|
+
ans = Matrix_rational_dense.__new__(Matrix_rational_dense, parent, None, None, None)
|
1166
|
+
|
1167
|
+
sig_on()
|
1168
|
+
fmpq_mat_mul(ans._matrix, self._matrix, (<Matrix_rational_dense> right)._matrix)
|
1169
|
+
sig_off()
|
1170
|
+
return ans
|
1171
|
+
|
1172
|
+
def _multiply_over_integers(self, Matrix_rational_dense right, algorithm='default'):
|
1173
|
+
r"""
|
1174
|
+
Multiply this matrix by right using a multimodular algorithm and
|
1175
|
+
return the result.
|
1176
|
+
|
1177
|
+
INPUT:
|
1178
|
+
|
1179
|
+
- ``self`` -- matrix over QQ
|
1180
|
+
|
1181
|
+
- ``right`` -- matrix over QQ
|
1182
|
+
|
1183
|
+
- ``algorithm``
|
1184
|
+
|
1185
|
+
- 'default': use whatever is the default for A\*B when A, B
|
1186
|
+
are over ZZ.
|
1187
|
+
|
1188
|
+
- 'multimodular': use a multimodular algorithm
|
1189
|
+
|
1190
|
+
EXAMPLES::
|
1191
|
+
|
1192
|
+
sage: a = MatrixSpace(QQ,10,5)(range(50))
|
1193
|
+
sage: b = MatrixSpace(QQ,5,12)([1/n for n in range(1,61)])
|
1194
|
+
sage: a._multiply_over_integers(b) == a._multiply_over_integers(b, algorithm='multimodular')
|
1195
|
+
True
|
1196
|
+
|
1197
|
+
::
|
1198
|
+
|
1199
|
+
sage: a = MatrixSpace(QQ,3)(range(9))
|
1200
|
+
sage: b = MatrixSpace(QQ,3)([1/n for n in range(1,10)])
|
1201
|
+
sage: c = a._multiply_over_integers(b, algorithm = 'multimodular')
|
1202
|
+
sage: c
|
1203
|
+
[ 15/28 9/20 7/18]
|
1204
|
+
[ 33/7 117/40 20/9]
|
1205
|
+
[249/28 27/5 73/18]
|
1206
|
+
sage: c == a._multiply_flint(b)
|
1207
|
+
True
|
1208
|
+
"""
|
1209
|
+
cdef Matrix_integer_dense A, B, AB
|
1210
|
+
cdef Matrix_rational_dense res
|
1211
|
+
cdef Integer D
|
1212
|
+
sig_on()
|
1213
|
+
A, A_denom = self._clear_denom()
|
1214
|
+
B, B_denom = right._clear_denom()
|
1215
|
+
if algorithm == 'default' or algorithm == 'multimodular':
|
1216
|
+
AB = A*B
|
1217
|
+
else:
|
1218
|
+
sig_off()
|
1219
|
+
raise ValueError("unknown algorithm '%s'" % algorithm)
|
1220
|
+
D = A_denom * B_denom
|
1221
|
+
if self._nrows == right._nrows:
|
1222
|
+
# self acts on the space of right
|
1223
|
+
res = Matrix_rational_dense.__new__(Matrix_rational_dense, right.parent(), 0, 0, 0)
|
1224
|
+
if self._ncols == right._ncols:
|
1225
|
+
# right acts on the space of self
|
1226
|
+
res = Matrix_rational_dense.__new__(Matrix_rational_dense, self.parent(), 0, 0, 0)
|
1227
|
+
else:
|
1228
|
+
res = Matrix_rational_dense.__new__(Matrix_rational_dense, self.matrix_space(AB._nrows, AB._ncols), 0, 0, 0)
|
1229
|
+
for i in range(res._nrows):
|
1230
|
+
for j in range(res._ncols):
|
1231
|
+
fmpz_set(fmpq_mat_entry_num(res._matrix, i, j), fmpz_mat_entry(AB._matrix,i,j))
|
1232
|
+
fmpz_set_mpz(fmpq_mat_entry_den(res._matrix, i, j), D.value)
|
1233
|
+
fmpq_canonicalise(fmpq_mat_entry(res._matrix, i, j))
|
1234
|
+
sig_off()
|
1235
|
+
return res
|
1236
|
+
|
1237
|
+
def height(self):
|
1238
|
+
"""
|
1239
|
+
Return the height of this matrix, which is the maximum of the
|
1240
|
+
absolute values of all numerators and denominators of entries in
|
1241
|
+
this matrix.
|
1242
|
+
|
1243
|
+
OUTPUT: integer
|
1244
|
+
|
1245
|
+
EXAMPLES::
|
1246
|
+
|
1247
|
+
sage: b = matrix(QQ,2,range(6)); b[0,0]=-5007/293; b
|
1248
|
+
[-5007/293 1 2]
|
1249
|
+
[ 3 4 5]
|
1250
|
+
sage: b.height()
|
1251
|
+
5007
|
1252
|
+
"""
|
1253
|
+
cdef Integer z
|
1254
|
+
cdef fmpz_t tmp
|
1255
|
+
fmpz_init(tmp)
|
1256
|
+
self.fmpz_height(tmp)
|
1257
|
+
z = PY_NEW(Integer)
|
1258
|
+
fmpz_get_mpz(z.value, tmp)
|
1259
|
+
fmpz_clear(tmp)
|
1260
|
+
return z
|
1261
|
+
|
1262
|
+
cdef int fmpz_height(self, fmpz_t h) except -1:
|
1263
|
+
cdef fmpz_t x
|
1264
|
+
cdef Py_ssize_t i, j
|
1265
|
+
sig_on()
|
1266
|
+
fmpz_init(x)
|
1267
|
+
fmpz_zero(h)
|
1268
|
+
for i in range(self._nrows):
|
1269
|
+
for j in range(self._ncols):
|
1270
|
+
fmpz_abs(x, fmpq_mat_entry_num(self._matrix, i, j))
|
1271
|
+
if fmpz_cmp(h, x) < 0:
|
1272
|
+
fmpz_set(h, x)
|
1273
|
+
fmpz_abs(x, fmpq_mat_entry_den(self._matrix, i, j))
|
1274
|
+
if fmpz_cmp(h, x) < 0:
|
1275
|
+
fmpz_set(h, x)
|
1276
|
+
fmpz_clear(x)
|
1277
|
+
sig_off()
|
1278
|
+
return 0
|
1279
|
+
|
1280
|
+
def _adjugate(self):
|
1281
|
+
"""
|
1282
|
+
Return the adjugate of this matrix.
|
1283
|
+
|
1284
|
+
Assumes ``self`` is a square matrix (checked in adjugate).
|
1285
|
+
|
1286
|
+
EXAMPLES::
|
1287
|
+
|
1288
|
+
sage: m = matrix(QQ,3,[1..9])/9; m
|
1289
|
+
[1/9 2/9 1/3]
|
1290
|
+
[4/9 5/9 2/3]
|
1291
|
+
[7/9 8/9 1]
|
1292
|
+
sage: m.adjugate() # needs sage.libs.pari
|
1293
|
+
[-1/27 2/27 -1/27]
|
1294
|
+
[ 2/27 -4/27 2/27]
|
1295
|
+
[-1/27 2/27 -1/27]
|
1296
|
+
"""
|
1297
|
+
return self.parent()(self.__pari__().matadjoint().sage())
|
1298
|
+
|
1299
|
+
def _magma_init_(self, magma):
|
1300
|
+
"""
|
1301
|
+
EXAMPLES::
|
1302
|
+
|
1303
|
+
sage: m = matrix(QQ,2,3,[1,2/3,-3/4,1,-2/3,-45/17])
|
1304
|
+
sage: m._magma_init_(magma)
|
1305
|
+
'Matrix(RationalField(),2,3,StringToIntegerSequence("204 136 -153 204 -136 -540"))/204'
|
1306
|
+
sage: magma(m) # optional - magma
|
1307
|
+
[ 1 2/3 -3/4]
|
1308
|
+
[ 1 -2/3 -45/17]
|
1309
|
+
"""
|
1310
|
+
X, d = self._clear_denom()
|
1311
|
+
s = X._magma_init_(magma).replace('IntegerRing','RationalField')
|
1312
|
+
if d != 1:
|
1313
|
+
s += '/%s' % d._magma_init_(magma)
|
1314
|
+
return s
|
1315
|
+
|
1316
|
+
def prod_of_row_sums(self, cols):
|
1317
|
+
cdef Py_ssize_t i, c
|
1318
|
+
cdef fmpq_t s, pr
|
1319
|
+
fmpq_init(s)
|
1320
|
+
fmpq_init(pr)
|
1321
|
+
|
1322
|
+
fmpq_one(pr)
|
1323
|
+
for i in range(self._nrows):
|
1324
|
+
fmpq_zero(s)
|
1325
|
+
for c in cols:
|
1326
|
+
if c < 0 or c >= self._ncols:
|
1327
|
+
raise IndexError("matrix column index out of range")
|
1328
|
+
fmpq_add(s, s, fmpq_mat_entry(self._matrix, i, c))
|
1329
|
+
fmpq_mul(pr, pr, s)
|
1330
|
+
cdef Rational ans
|
1331
|
+
ans = Rational.__new__(Rational)
|
1332
|
+
fmpq_get_mpq(ans.value, pr)
|
1333
|
+
fmpq_clear(s)
|
1334
|
+
fmpq_clear(pr)
|
1335
|
+
return ans
|
1336
|
+
|
1337
|
+
def _right_kernel_matrix(self, **kwds):
|
1338
|
+
r"""
|
1339
|
+
Return a pair that includes a matrix of basis vectors
|
1340
|
+
for the right kernel of ``self``.
|
1341
|
+
|
1342
|
+
INPUT:
|
1343
|
+
|
1344
|
+
- ``kwds`` -- these are provided for consistency with other versions
|
1345
|
+
of this method. Here they are ignored as there is no optional
|
1346
|
+
behavior available.
|
1347
|
+
|
1348
|
+
OUTPUT:
|
1349
|
+
|
1350
|
+
Returns a pair. First item is the string 'computed-iml-rational'
|
1351
|
+
that identifies the nature of the basis vectors.
|
1352
|
+
|
1353
|
+
Second item is a matrix whose rows are a basis for the right kernel,
|
1354
|
+
over the rationals, as computed by the IML library. Notice that the
|
1355
|
+
IML library returns a matrix that is in the 'pivot' format, once the
|
1356
|
+
whole matrix is multiplied by -1. So the 'computed' format is very
|
1357
|
+
close to the 'pivot' format.
|
1358
|
+
|
1359
|
+
EXAMPLES::
|
1360
|
+
|
1361
|
+
sage: A = matrix(QQ, [
|
1362
|
+
....: [1, 0, 1, -3, 1],
|
1363
|
+
....: [-5, 1, 0, 7, -3],
|
1364
|
+
....: [0, -1, -4, 6, -2],
|
1365
|
+
....: [4, -1, 0, -6, 2]])
|
1366
|
+
sage: result = A._right_kernel_matrix()
|
1367
|
+
sage: result[0] # needs sage.libs.iml
|
1368
|
+
'computed-iml-rational'
|
1369
|
+
sage: result[1]
|
1370
|
+
[-1 2 -2 -1 0]
|
1371
|
+
[ 1 2 0 0 -1]
|
1372
|
+
sage: X = result[1].transpose()
|
1373
|
+
sage: A*X == zero_matrix(QQ, 4, 2)
|
1374
|
+
True
|
1375
|
+
|
1376
|
+
Computed result is the negative of the pivot basis, which
|
1377
|
+
is just slightly more efficient to compute. ::
|
1378
|
+
|
1379
|
+
sage: A.right_kernel_matrix(basis='pivot') == -A.right_kernel_matrix(basis='computed')
|
1380
|
+
True
|
1381
|
+
|
1382
|
+
TESTS:
|
1383
|
+
|
1384
|
+
We test three trivial cases. ::
|
1385
|
+
|
1386
|
+
sage: A = matrix(QQ, 0, 2)
|
1387
|
+
sage: A._right_kernel_matrix()[1]
|
1388
|
+
[1 0]
|
1389
|
+
[0 1]
|
1390
|
+
sage: A = matrix(QQ, 2, 0)
|
1391
|
+
sage: A._right_kernel_matrix()[1].parent()
|
1392
|
+
Full MatrixSpace of 0 by 0 dense matrices over Rational Field
|
1393
|
+
sage: A = zero_matrix(QQ, 4, 3)
|
1394
|
+
sage: A._right_kernel_matrix()[1]
|
1395
|
+
[1 0 0]
|
1396
|
+
[0 1 0]
|
1397
|
+
[0 0 1]
|
1398
|
+
"""
|
1399
|
+
tm = verbose("computing right kernel matrix over the rationals for %sx%s matrix" % (self.nrows(), self.ncols()),level=1)
|
1400
|
+
# _rational_kernel_flint() gets the zero-row case wrong, fix it there
|
1401
|
+
if self.nrows()==0:
|
1402
|
+
from sage.matrix.constructor import identity_matrix
|
1403
|
+
K = identity_matrix(QQ, self.ncols())
|
1404
|
+
else:
|
1405
|
+
try:
|
1406
|
+
from .matrix_integer_iml import _rational_kernel_iml
|
1407
|
+
except ImportError:
|
1408
|
+
return self._right_kernel_matrix_over_field()
|
1409
|
+
A, _ = self._clear_denom()
|
1410
|
+
K = _rational_kernel_iml(A).transpose().change_ring(QQ)
|
1411
|
+
verbose("done computing right kernel matrix over the rationals for %sx%s matrix" % (self.nrows(), self.ncols()),level=1, t=tm)
|
1412
|
+
return 'computed-iml-rational', K
|
1413
|
+
|
1414
|
+
# ###############################################
|
1415
|
+
# Change ring
|
1416
|
+
# ###############################################
|
1417
|
+
def change_ring(self, R):
|
1418
|
+
"""
|
1419
|
+
Create the matrix over R with entries the entries of ``self`` coerced
|
1420
|
+
into R.
|
1421
|
+
|
1422
|
+
EXAMPLES::
|
1423
|
+
|
1424
|
+
sage: a = matrix(QQ,2,[1/2,-1,2,3])
|
1425
|
+
sage: a.change_ring(GF(3))
|
1426
|
+
[2 2]
|
1427
|
+
[2 0]
|
1428
|
+
sage: a.change_ring(ZZ)
|
1429
|
+
Traceback (most recent call last):
|
1430
|
+
...
|
1431
|
+
TypeError: matrix has denominators so can...t change to ZZ
|
1432
|
+
sage: b = a.change_ring(QQ['x']); b
|
1433
|
+
[1/2 -1]
|
1434
|
+
[ 2 3]
|
1435
|
+
sage: b.parent()
|
1436
|
+
Full MatrixSpace of 2 by 2 dense matrices over Univariate Polynomial Ring in x over Rational Field
|
1437
|
+
|
1438
|
+
TESTS:
|
1439
|
+
|
1440
|
+
Make sure that subdivisions are preserved when changing rings::
|
1441
|
+
|
1442
|
+
sage: a = matrix(QQ, 3, range(9))
|
1443
|
+
sage: a.subdivide(2,1); a
|
1444
|
+
[0|1 2]
|
1445
|
+
[3|4 5]
|
1446
|
+
[-+---]
|
1447
|
+
[6|7 8]
|
1448
|
+
sage: a.change_ring(ZZ).change_ring(QQ)
|
1449
|
+
[0|1 2]
|
1450
|
+
[3|4 5]
|
1451
|
+
[-+---]
|
1452
|
+
[6|7 8]
|
1453
|
+
sage: a.change_ring(GF(3))
|
1454
|
+
[0|1 2]
|
1455
|
+
[0|1 2]
|
1456
|
+
[-+---]
|
1457
|
+
[0|1 2]
|
1458
|
+
"""
|
1459
|
+
if R not in Rings():
|
1460
|
+
raise TypeError("R must be a ring")
|
1461
|
+
if R == self._base_ring:
|
1462
|
+
if self._is_immutable:
|
1463
|
+
return self
|
1464
|
+
return self.__copy__()
|
1465
|
+
if isinstance(R, IntegerRing_class):
|
1466
|
+
A, d = self._clear_denom()
|
1467
|
+
if not d.is_one():
|
1468
|
+
raise TypeError("matrix has denominators so can't change to ZZ")
|
1469
|
+
if self._subdivisions is not None:
|
1470
|
+
A.subdivide(self.subdivisions())
|
1471
|
+
return A
|
1472
|
+
|
1473
|
+
if isinstance(R, sage.rings.abc.IntegerModRing):
|
1474
|
+
try:
|
1475
|
+
from sage.matrix.matrix_modn_dense_double import MAX_MODULUS
|
1476
|
+
except ImportError:
|
1477
|
+
pass
|
1478
|
+
else:
|
1479
|
+
if R.order() < MAX_MODULUS:
|
1480
|
+
b = R.order()
|
1481
|
+
A, d = self._clear_denom()
|
1482
|
+
if not b.gcd(d).is_one():
|
1483
|
+
raise TypeError("matrix denominator not coprime to modulus")
|
1484
|
+
B = A._mod_int(b)
|
1485
|
+
C = (1/(B.base_ring()(d))) * B
|
1486
|
+
if self._subdivisions is not None:
|
1487
|
+
C.subdivide(self.subdivisions())
|
1488
|
+
return C
|
1489
|
+
|
1490
|
+
# fallback to the generic version
|
1491
|
+
return Matrix_dense.change_ring(self, R)
|
1492
|
+
|
1493
|
+
# ###############################################
|
1494
|
+
# Echelon form
|
1495
|
+
# ###############################################
|
1496
|
+
def echelonize(self, algorithm=None,
|
1497
|
+
height_guess=None, proof=None, **kwds):
|
1498
|
+
"""
|
1499
|
+
Transform the matrix ``self`` into reduced row echelon form
|
1500
|
+
in place.
|
1501
|
+
|
1502
|
+
INPUT:
|
1503
|
+
|
1504
|
+
- ``algorithm`` -- an optional specification of an algorithm. One of
|
1505
|
+
|
1506
|
+
- ``None``: (default) try to pick the best choice,
|
1507
|
+
|
1508
|
+
- ``'flint'``: use flint library
|
1509
|
+
`function <https://flintlib.org/doc/fmpq_mat.html#c.fmpq_mat_rref>`_,
|
1510
|
+
which automatically chooses between
|
1511
|
+
`classical algorithm <https://flintlib.org/doc/fmpq_mat.html#c.fmpq_mat_rref_classical>`_
|
1512
|
+
(Gaussian elimination),
|
1513
|
+
`fraction-free multimodular <https://flintlib.org/doc/fmpz_mat.html#c.fmpz_mat_rref_mul>`_,
|
1514
|
+
and `fraction-free LU decomposition <https://flintlib.org/doc/fmpz_mat.html#c.fmpz_mat_rref_fflu>`_,
|
1515
|
+
|
1516
|
+
- ``'flint:classical'``, ``'flint:multimodular'``, ``'flint:fflu'``: use the
|
1517
|
+
flint library as above, but select an algorithm explicitly,
|
1518
|
+
|
1519
|
+
- ``'padic'``: an algorithm based on the IML `p`-adic solver,
|
1520
|
+
|
1521
|
+
- ``'multimodular'``: uses a multimodular algorithm implemented in Cython
|
1522
|
+
that uses linbox modulo many primes,
|
1523
|
+
see :func:`~sage.matrix.misc.matrix_rational_echelon_form_multimodular`,
|
1524
|
+
|
1525
|
+
- ``'classical'``: just clear each column using Gauss elimination.
|
1526
|
+
|
1527
|
+
- ``height_guess``, ``**kwds`` -- all passed to the
|
1528
|
+
``'multimodular'`` algorithm; ignored by other algorithms
|
1529
|
+
|
1530
|
+
- ``proof`` -- boolean or ``None`` (default: None, see
|
1531
|
+
proof.linear_algebra or sage.structure.proof). Passed to the
|
1532
|
+
``'multimodular'`` algorithm. Note that the Sage global default is
|
1533
|
+
``proof=True``.
|
1534
|
+
|
1535
|
+
EXAMPLES::
|
1536
|
+
|
1537
|
+
sage: a = matrix(QQ, 4, range(16)); a[0,0] = 1/19; a[0,1] = 1/5; a
|
1538
|
+
[1/19 1/5 2 3]
|
1539
|
+
[ 4 5 6 7]
|
1540
|
+
[ 8 9 10 11]
|
1541
|
+
[ 12 13 14 15]
|
1542
|
+
sage: a.echelonize()
|
1543
|
+
sage: a
|
1544
|
+
[ 1 0 0 -76/157]
|
1545
|
+
[ 0 1 0 -5/157]
|
1546
|
+
[ 0 0 1 238/157]
|
1547
|
+
[ 0 0 0 0]
|
1548
|
+
|
1549
|
+
::
|
1550
|
+
|
1551
|
+
sage: # needs sage.libs.linbox
|
1552
|
+
sage: a = matrix(QQ, 4, range(16)); a[0,0] = 1/19; a[0,1] = 1/5
|
1553
|
+
sage: a.echelonize(algorithm='multimodular')
|
1554
|
+
sage: a
|
1555
|
+
[ 1 0 0 -76/157]
|
1556
|
+
[ 0 1 0 -5/157]
|
1557
|
+
[ 0 0 1 238/157]
|
1558
|
+
[ 0 0 0 0]
|
1559
|
+
|
1560
|
+
TESTS:
|
1561
|
+
|
1562
|
+
Echelonizing a matrix in place throws away the cache of
|
1563
|
+
the old matrix (:issue:`14506`)::
|
1564
|
+
|
1565
|
+
sage: for algo in ["flint", "padic", "multimodular", "classical", # needs sage.libs.linbox
|
1566
|
+
....: "flint:classical", "flint:multimodular", "flint:fflu"]:
|
1567
|
+
....: a = Matrix(QQ, [[1,2],[3,4]])
|
1568
|
+
....: _ = a.det() # fills the cache
|
1569
|
+
....: _ = a._clear_denom() # fills the cache
|
1570
|
+
....: a.echelonize(algorithm=algo)
|
1571
|
+
....: assert sorted(a._cache.keys()) == ['echelon_form', 'in_echelon_form', 'pivots', 'rank'], (algo, a._cache.keys())
|
1572
|
+
"""
|
1573
|
+
if self.fetch('in_echelon_form'):
|
1574
|
+
return # already known to be in echelon form
|
1575
|
+
self.check_mutability()
|
1576
|
+
|
1577
|
+
if algorithm is None:
|
1578
|
+
algorithm = 'flint:multimodular'
|
1579
|
+
|
1580
|
+
if algorithm in ('flint', 'flint:classical', 'flint:multimodular', 'flint:fflu'):
|
1581
|
+
pivots = self._echelonize_flint(algorithm)
|
1582
|
+
elif algorithm == 'multimodular':
|
1583
|
+
pivots = self._echelonize_multimodular(height_guess, proof, **kwds)
|
1584
|
+
elif algorithm == 'classical':
|
1585
|
+
pivots = self._echelon_in_place_classical()
|
1586
|
+
elif algorithm == 'padic':
|
1587
|
+
pivots = self._echelonize_padic()
|
1588
|
+
else:
|
1589
|
+
raise ValueError("no algorithm '%s'" % algorithm)
|
1590
|
+
|
1591
|
+
if type(pivots) is not tuple:
|
1592
|
+
raise RuntimeError("BUG: pivots must get set as a tuple. Got {} for algo {} with {}x{} matrix.".format(
|
1593
|
+
type(pivots), algorithm, self._nrows, self._ncols))
|
1594
|
+
|
1595
|
+
self.cache('in_echelon_form', True)
|
1596
|
+
self.cache('echelon_form', self)
|
1597
|
+
self.cache('pivots', pivots)
|
1598
|
+
self.cache('rank', len(pivots))
|
1599
|
+
|
1600
|
+
def echelon_form(self, algorithm=None,
|
1601
|
+
height_guess=None, proof=None, **kwds):
|
1602
|
+
r"""
|
1603
|
+
Return the echelon form of this matrix.
|
1604
|
+
|
1605
|
+
The (row) echelon form of a matrix, see :wikipedia:`Row_echelon_form`,
|
1606
|
+
is the matrix obtained by performing Gauss elimination on the rows
|
1607
|
+
of the matrix.
|
1608
|
+
|
1609
|
+
INPUT: See :meth:`echelonize` for the options.
|
1610
|
+
|
1611
|
+
EXAMPLES::
|
1612
|
+
|
1613
|
+
sage: a = matrix(QQ, 4, range(16)); a[0,0] = 1/19; a[0,1] = 1/5; a
|
1614
|
+
[1/19 1/5 2 3]
|
1615
|
+
[ 4 5 6 7]
|
1616
|
+
[ 8 9 10 11]
|
1617
|
+
[ 12 13 14 15]
|
1618
|
+
sage: a.echelon_form()
|
1619
|
+
[ 1 0 0 -76/157]
|
1620
|
+
[ 0 1 0 -5/157]
|
1621
|
+
[ 0 0 1 238/157]
|
1622
|
+
[ 0 0 0 0]
|
1623
|
+
sage: a.echelon_form(algorithm='multimodular')
|
1624
|
+
[ 1 0 0 -76/157]
|
1625
|
+
[ 0 1 0 -5/157]
|
1626
|
+
[ 0 0 1 238/157]
|
1627
|
+
[ 0 0 0 0]
|
1628
|
+
|
1629
|
+
The result is an immutable matrix, so if you want to modify the result
|
1630
|
+
then you need to make a copy. This checks that :issue:`10543` is
|
1631
|
+
fixed.::
|
1632
|
+
|
1633
|
+
sage: A = matrix(QQ, 2, range(6))
|
1634
|
+
sage: E = A.echelon_form()
|
1635
|
+
sage: E.is_mutable()
|
1636
|
+
False
|
1637
|
+
sage: F = copy(E)
|
1638
|
+
sage: F[0,0] = 50
|
1639
|
+
sage: F
|
1640
|
+
[50 0 -1]
|
1641
|
+
[ 0 1 2]
|
1642
|
+
|
1643
|
+
TESTS:
|
1644
|
+
|
1645
|
+
Check consistency::
|
1646
|
+
|
1647
|
+
sage: for _ in range(100): # needs sage.libs.linbox
|
1648
|
+
....: nrows = randint(0, 30)
|
1649
|
+
....: ncols = randint(0, 30)
|
1650
|
+
....: m = random_matrix(QQ, nrows, ncols, num_bound=10, den_bound=10)
|
1651
|
+
....: ech_flint = m.echelon_form('flint'); m._clear_cache()
|
1652
|
+
....: ech_padic = m.echelon_form('padic'); m._clear_cache()
|
1653
|
+
....: ech_multi = m.echelon_form('multimodular'); m._clear_cache()
|
1654
|
+
....: ech_class = m.echelon_form('classical')
|
1655
|
+
....: assert ech_flint == ech_padic == ech_multi == ech_class
|
1656
|
+
"""
|
1657
|
+
x = self.fetch('echelon_form')
|
1658
|
+
if x is not None:
|
1659
|
+
return x
|
1660
|
+
if self.fetch('in_echelon_form'):
|
1661
|
+
raise RuntimeError('in_echelon_form set but not echelon_form')
|
1662
|
+
|
1663
|
+
E = self.__copy__()
|
1664
|
+
E.echelonize(algorithm)
|
1665
|
+
E.set_immutable()
|
1666
|
+
self.cache('echelon_form', E)
|
1667
|
+
self.cache('pivots', E.pivots())
|
1668
|
+
self.cache('rank', len(E.pivots()))
|
1669
|
+
return E
|
1670
|
+
|
1671
|
+
def _echelonize_flint(self, algorithm: str):
|
1672
|
+
r"""
|
1673
|
+
INPUT: See :meth:`echelonize` for the options.
|
1674
|
+
Only options that use flint are allowed, passing other algorithms may
|
1675
|
+
trigger undefined behavior.
|
1676
|
+
|
1677
|
+
EXAMPLES::
|
1678
|
+
|
1679
|
+
sage: m = matrix(QQ, 4, range(16))
|
1680
|
+
sage: m._echelonize_flint("flint")
|
1681
|
+
(0, 1)
|
1682
|
+
sage: m
|
1683
|
+
[ 1 0 -1 -2]
|
1684
|
+
[ 0 1 2 3]
|
1685
|
+
[ 0 0 0 0]
|
1686
|
+
[ 0 0 0 0]
|
1687
|
+
sage: m = matrix(QQ, 4, 6, [-1,0,0,-2,-1,-2,-1,0,0,-2,-1,0,3,3,-2,0,0,3,-2,-3,1,1,-2,3])
|
1688
|
+
sage: m._echelonize_flint("flint")
|
1689
|
+
(0, 1, 2, 5)
|
1690
|
+
sage: m
|
1691
|
+
[ 1 0 0 2 1 0]
|
1692
|
+
[ 0 1 0 -4/3 1 0]
|
1693
|
+
[ 0 0 1 1 3 0]
|
1694
|
+
[ 0 0 0 0 0 1]
|
1695
|
+
"""
|
1696
|
+
self.clear_cache()
|
1697
|
+
|
1698
|
+
if fmpq_mat_is_empty(self._matrix):
|
1699
|
+
return ()
|
1700
|
+
|
1701
|
+
cdef long r
|
1702
|
+
cdef fmpz_mat_t Aclear
|
1703
|
+
cdef fmpz_t den
|
1704
|
+
|
1705
|
+
sig_on()
|
1706
|
+
|
1707
|
+
if algorithm == 'flint':
|
1708
|
+
r = fmpq_mat_rref(self._matrix, self._matrix)
|
1709
|
+
elif algorithm == 'flint:classical':
|
1710
|
+
r = fmpq_mat_rref_classical(self._matrix, self._matrix)
|
1711
|
+
else:
|
1712
|
+
# copied from fmpq_mat_rref_fraction_free
|
1713
|
+
fmpz_mat_init(Aclear, self._nrows, self._ncols)
|
1714
|
+
fmpq_mat_get_fmpz_mat_rowwise(Aclear, NULL, self._matrix)
|
1715
|
+
fmpz_init(den)
|
1716
|
+
|
1717
|
+
if algorithm == 'flint:fflu':
|
1718
|
+
r = fmpz_mat_rref_fflu(Aclear, den, Aclear)
|
1719
|
+
else:
|
1720
|
+
assert algorithm == 'flint:multimodular'
|
1721
|
+
r = fmpz_mat_rref_mul(Aclear, den, Aclear)
|
1722
|
+
|
1723
|
+
if r == 0:
|
1724
|
+
fmpq_mat_zero(self._matrix)
|
1725
|
+
else:
|
1726
|
+
fmpq_mat_set_fmpz_mat_div_fmpz(self._matrix, Aclear, den)
|
1727
|
+
|
1728
|
+
fmpz_mat_clear(Aclear)
|
1729
|
+
fmpz_clear(den)
|
1730
|
+
|
1731
|
+
sig_off()
|
1732
|
+
|
1733
|
+
# compute pivots
|
1734
|
+
cdef long i, j, k
|
1735
|
+
cdef list p = []
|
1736
|
+
k = 0
|
1737
|
+
for i in range(r):
|
1738
|
+
for j in range(k, self._ncols):
|
1739
|
+
if not fmpq_is_zero(fmpq_mat_entry(self._matrix, i, j)):
|
1740
|
+
p.append(j)
|
1741
|
+
k = j+1 # so start at next position next time
|
1742
|
+
break
|
1743
|
+
else:
|
1744
|
+
break
|
1745
|
+
return tuple(p)
|
1746
|
+
|
1747
|
+
def _echelonize_padic(self):
|
1748
|
+
"""
|
1749
|
+
Echelonize ``self`` using a `p`-adic nullspace algorithm.
|
1750
|
+
|
1751
|
+
EXAMPLES::
|
1752
|
+
|
1753
|
+
sage: # needs sage.libs.linbox
|
1754
|
+
sage: m = matrix(QQ, 4, range(16))
|
1755
|
+
sage: m._echelonize_padic()
|
1756
|
+
(0, 1)
|
1757
|
+
sage: m
|
1758
|
+
[ 1 0 -1 -2]
|
1759
|
+
[ 0 1 2 3]
|
1760
|
+
[ 0 0 0 0]
|
1761
|
+
[ 0 0 0 0]
|
1762
|
+
sage: m = matrix(QQ, 4, 6, [-1,0,0,-2,-1,-2,-1,0,0,-2,-1,0,3,3,-2,0,0,3,-2,-3,1,1,-2,3])
|
1763
|
+
sage: m._echelonize_padic()
|
1764
|
+
(0, 1, 2, 5)
|
1765
|
+
sage: m
|
1766
|
+
[ 1 0 0 2 1 0]
|
1767
|
+
[ 0 1 0 -4/3 1 0]
|
1768
|
+
[ 0 0 1 1 3 0]
|
1769
|
+
[ 0 0 0 0 0 1]
|
1770
|
+
"""
|
1771
|
+
cdef Matrix_integer_dense X
|
1772
|
+
cdef Integer d
|
1773
|
+
cdef fmpq * entry
|
1774
|
+
|
1775
|
+
A, _ = self._clear_denom()
|
1776
|
+
pivots, nonpivots, X, d = A._rational_echelon_via_solve()
|
1777
|
+
self.clear_cache()
|
1778
|
+
|
1779
|
+
# FIXME: we should always have X.nrows() == len(pivots)
|
1780
|
+
if X.nrows() != len(pivots):
|
1781
|
+
assert X.ncols() == len(pivots) == 0
|
1782
|
+
assert type(pivots) is list
|
1783
|
+
fmpq_mat_zero(self._matrix)
|
1784
|
+
return tuple(pivots)
|
1785
|
+
|
1786
|
+
cdef Py_ssize_t i,j
|
1787
|
+
for i in range(X.nrows()):
|
1788
|
+
# 1 at pivot
|
1789
|
+
fmpq_one(fmpq_mat_entry(self._matrix, i, pivots[i]))
|
1790
|
+
|
1791
|
+
# nonzero part
|
1792
|
+
for j in range(X.ncols()):
|
1793
|
+
entry = fmpq_mat_entry(self._matrix, i, nonpivots[j])
|
1794
|
+
fmpz_set(fmpq_numref(entry), fmpz_mat_entry(X._matrix, i, j))
|
1795
|
+
fmpz_set_mpz(fmpq_denref(entry), d.value)
|
1796
|
+
fmpq_canonicalise(entry)
|
1797
|
+
|
1798
|
+
# zeros on the left of the pivot
|
1799
|
+
for j in range(pivots[i]):
|
1800
|
+
fmpq_zero(fmpq_mat_entry(self._matrix, i, j))
|
1801
|
+
|
1802
|
+
# zeros on top of the other pivots
|
1803
|
+
for j in range(i):
|
1804
|
+
fmpq_zero(fmpq_mat_entry(self._matrix, j, pivots[i]))
|
1805
|
+
|
1806
|
+
# Fill in the 0-rows at the bottom.
|
1807
|
+
for i in range(len(pivots), self._nrows):
|
1808
|
+
for j in range(self._ncols):
|
1809
|
+
fmpq_zero(fmpq_mat_entry(self._matrix, i, j))
|
1810
|
+
|
1811
|
+
# FIXME: pivots should already be a tuple in all cases
|
1812
|
+
return tuple(pivots)
|
1813
|
+
|
1814
|
+
def _echelonize_multimodular(self, height_guess=None, proof=None):
|
1815
|
+
"""
|
1816
|
+
Echelonize ``self`` using multimodular recomposition.
|
1817
|
+
|
1818
|
+
REFERENCE:
|
1819
|
+
|
1820
|
+
- Chapter 7 of Stein's "Explicitly Computing Modular Forms".
|
1821
|
+
|
1822
|
+
INPUT:
|
1823
|
+
|
1824
|
+
- ``height_guess`` -- integer or ``None``
|
1825
|
+
|
1826
|
+
- ``proof`` -- boolean (default: ``None``, see
|
1827
|
+
proof.linear_algebra or sage.structure.proof); Note that the Sage
|
1828
|
+
global default is ``proof=True``
|
1829
|
+
|
1830
|
+
EXAMPLES::
|
1831
|
+
|
1832
|
+
sage: # needs sage.libs.linbox
|
1833
|
+
sage: m = matrix(QQ, 4, range(16))
|
1834
|
+
sage: m._echelonize_multimodular()
|
1835
|
+
(0, 1)
|
1836
|
+
sage: m
|
1837
|
+
[ 1 0 -1 -2]
|
1838
|
+
[ 0 1 2 3]
|
1839
|
+
[ 0 0 0 0]
|
1840
|
+
[ 0 0 0 0]
|
1841
|
+
sage: m = matrix(QQ, 4, 6, [-1,0,0,-2,-1,-2,-1,0,0,-2,-1,0,3,3,-2,0,0,3,-2,-3,1,1,-2,3])
|
1842
|
+
sage: m._echelonize_multimodular()
|
1843
|
+
(0, 1, 2, 5)
|
1844
|
+
sage: m
|
1845
|
+
[ 1 0 0 2 1 0]
|
1846
|
+
[ 0 1 0 -4/3 1 0]
|
1847
|
+
[ 0 0 1 1 3 0]
|
1848
|
+
[ 0 0 0 0 0 1]
|
1849
|
+
"""
|
1850
|
+
from sage.matrix.misc import matrix_rational_echelon_form_multimodular
|
1851
|
+
E, pivots = matrix_rational_echelon_form_multimodular(self, height_guess, proof=proof)
|
1852
|
+
self.clear_cache()
|
1853
|
+
fmpq_mat_swap(self._matrix, (<Matrix_rational_dense>E)._matrix)
|
1854
|
+
return pivots
|
1855
|
+
|
1856
|
+
cdef swap_rows_c(self, Py_ssize_t r1, Py_ssize_t r2):
|
1857
|
+
"""
|
1858
|
+
EXAMPLES::
|
1859
|
+
|
1860
|
+
sage: a = matrix(QQ,2,[1..6])
|
1861
|
+
sage: a.swap_rows(0,1) # indirect doctest
|
1862
|
+
sage: a
|
1863
|
+
[4 5 6]
|
1864
|
+
[1 2 3]
|
1865
|
+
"""
|
1866
|
+
# no bounds checking!
|
1867
|
+
cdef Py_ssize_t c
|
1868
|
+
for c in range(self._ncols):
|
1869
|
+
fmpq_swap(fmpq_mat_entry(self._matrix, r1, c),
|
1870
|
+
fmpq_mat_entry(self._matrix, r2, c))
|
1871
|
+
|
1872
|
+
cdef swap_columns_c(self, Py_ssize_t c1, Py_ssize_t c2):
|
1873
|
+
"""
|
1874
|
+
EXAMPLES::
|
1875
|
+
|
1876
|
+
sage: a = matrix(QQ,2,[1..6])
|
1877
|
+
sage: a.swap_columns(0,1) # indirect doctest
|
1878
|
+
sage: a
|
1879
|
+
[2 1 3]
|
1880
|
+
[5 4 6]
|
1881
|
+
"""
|
1882
|
+
# no bounds checking!
|
1883
|
+
for r in range(self._nrows):
|
1884
|
+
fmpq_swap(fmpq_mat_entry(self._matrix, r, c1),
|
1885
|
+
fmpq_mat_entry(self._matrix, r, c2))
|
1886
|
+
|
1887
|
+
def decomposition(self, is_diagonalizable=False, dual=False,
|
1888
|
+
algorithm=None, height_guess=None, proof=None):
|
1889
|
+
"""
|
1890
|
+
Return the decomposition of the free module on which this matrix A
|
1891
|
+
acts from the right (i.e., the action is x goes to x A), along with
|
1892
|
+
whether this matrix acts irreducibly on each factor. The factors
|
1893
|
+
are guaranteed to be sorted in the same way as the corresponding
|
1894
|
+
factors of the characteristic polynomial.
|
1895
|
+
|
1896
|
+
Let A be the matrix acting from the on the vector space V of column
|
1897
|
+
vectors. Assume that A is square. This function computes maximal
|
1898
|
+
subspaces W_1, ..., W_n corresponding to Galois conjugacy classes
|
1899
|
+
of eigenvalues of A. More precisely, let f(X) be the characteristic
|
1900
|
+
polynomial of A. This function computes the subspace
|
1901
|
+
`W_i = ker(g_(A)^n)`, where g_i(X) is an irreducible
|
1902
|
+
factor of f(X) and g_i(X) exactly divides f(X). If the optional
|
1903
|
+
parameter is_diagonalizable is True, then we let W_i = ker(g(A)),
|
1904
|
+
since then we know that ker(g(A)) = `ker(g(A)^n)`.
|
1905
|
+
|
1906
|
+
If dual is True, also returns the corresponding decomposition of V
|
1907
|
+
under the action of the transpose of A. The factors are guaranteed
|
1908
|
+
to correspond.
|
1909
|
+
|
1910
|
+
INPUT:
|
1911
|
+
|
1912
|
+
- ``is_diagonalizable`` -- ignored
|
1913
|
+
|
1914
|
+
- ``dual`` -- whether to also return decompositions for
|
1915
|
+
the dual
|
1916
|
+
|
1917
|
+
- ``algorithm`` -- an optional specification of an algorithm
|
1918
|
+
|
1919
|
+
- ``None`` -- (default) use default algorithm for computing Echelon
|
1920
|
+
forms
|
1921
|
+
|
1922
|
+
- 'multimodular': much better if the answers
|
1923
|
+
factors have small height
|
1924
|
+
|
1925
|
+
- ``height_guess`` -- positive integer; only used by
|
1926
|
+
the multimodular algorithm
|
1927
|
+
|
1928
|
+
- ``proof`` -- boolean or ``None`` (default: ``None``, see
|
1929
|
+
proof.linear_algebra or sage.structure.proof); only used by the
|
1930
|
+
multimodular algorithm. Note that the Sage global default is
|
1931
|
+
proof=True.
|
1932
|
+
|
1933
|
+
|
1934
|
+
.. NOTE::
|
1935
|
+
|
1936
|
+
IMPORTANT: If you expect that the subspaces in the answer
|
1937
|
+
are spanned by vectors with small height coordinates, use
|
1938
|
+
algorithm='multimodular' and height_guess=1; this is
|
1939
|
+
potentially much faster than the default. If you know for a
|
1940
|
+
fact the answer will be very small, use
|
1941
|
+
algorithm='multimodular', height_guess=bound on height,
|
1942
|
+
proof=False.
|
1943
|
+
|
1944
|
+
You can get very very fast decomposition with proof=False.
|
1945
|
+
|
1946
|
+
EXAMPLES::
|
1947
|
+
|
1948
|
+
sage: a = matrix(QQ,3,[1..9])
|
1949
|
+
sage: a.decomposition()
|
1950
|
+
[(Vector space of degree 3 and dimension 1 over Rational Field
|
1951
|
+
Basis matrix:
|
1952
|
+
[ 1 -2 1],
|
1953
|
+
True),
|
1954
|
+
(Vector space of degree 3 and dimension 2 over Rational Field
|
1955
|
+
Basis matrix:
|
1956
|
+
[ 1 0 -1]
|
1957
|
+
[ 0 1 2],
|
1958
|
+
True)]
|
1959
|
+
"""
|
1960
|
+
X = self._decomposition_rational(is_diagonalizable=is_diagonalizable,
|
1961
|
+
echelon_algorithm=algorithm,
|
1962
|
+
height_guess=height_guess, proof=proof)
|
1963
|
+
if not dual:
|
1964
|
+
return X
|
1965
|
+
|
1966
|
+
Y = self.transpose()._decomposition_rational(is_diagonalizable=is_diagonalizable,
|
1967
|
+
echelon_algorithm=algorithm, height_guess=height_guess, proof=proof)
|
1968
|
+
return X, Y
|
1969
|
+
|
1970
|
+
def _decomposition_rational(self, is_diagonalizable = False,
|
1971
|
+
echelon_algorithm=None,
|
1972
|
+
kernel_algorithm='default',
|
1973
|
+
**kwds):
|
1974
|
+
"""
|
1975
|
+
Return the decomposition of the free module on which this matrix A
|
1976
|
+
acts from the right (i.e., the action is x goes to x A), along with
|
1977
|
+
whether this matrix acts irreducibly on each factor. The factors
|
1978
|
+
are guaranteed to be sorted in the same way as the corresponding
|
1979
|
+
factors of the characteristic polynomial.
|
1980
|
+
|
1981
|
+
INPUT:
|
1982
|
+
|
1983
|
+
- ``self`` -- a square matrix over the rational
|
1984
|
+
numbers
|
1985
|
+
|
1986
|
+
- ``echelon_algorithm`` -- an optional algorithm to be passed to the
|
1987
|
+
method ``echelon_form``
|
1988
|
+
|
1989
|
+
- ``'multimodular'`` -- use this if the answers have
|
1990
|
+
small height
|
1991
|
+
|
1992
|
+
- ``**kwds`` -- passed on to echelon function
|
1993
|
+
|
1994
|
+
.. NOTE::
|
1995
|
+
|
1996
|
+
IMPORTANT: If you expect that the subspaces in the answer are
|
1997
|
+
spanned by vectors with small height coordinates, use
|
1998
|
+
algorithm='multimodular' and height_guess=1; this is potentially
|
1999
|
+
much faster than the default. If you know for a fact the answer
|
2000
|
+
will be very small, use algorithm='multimodular',
|
2001
|
+
height_guess=bound on height, proof=False
|
2002
|
+
|
2003
|
+
OUTPUT:
|
2004
|
+
|
2005
|
+
- ``Sequence`` -- list of tuples (V,t), where V is a
|
2006
|
+
vector spaces and t is ``True`` if and only if the charpoly of ``self`` on
|
2007
|
+
V is irreducible. The tuples are in order corresponding to the
|
2008
|
+
elements of the sorted list self.charpoly().factor().
|
2009
|
+
"""
|
2010
|
+
cdef Py_ssize_t k
|
2011
|
+
|
2012
|
+
if not self.is_square():
|
2013
|
+
raise ArithmeticError("self must be a square matrix")
|
2014
|
+
|
2015
|
+
if self.nrows() == 0:
|
2016
|
+
return decomp_seq([])
|
2017
|
+
|
2018
|
+
A, _ = self._clear_denom()
|
2019
|
+
|
2020
|
+
f = A.charpoly('x')
|
2021
|
+
E = decomp_seq([])
|
2022
|
+
|
2023
|
+
t = verbose('factoring the characteristic polynomial', level=2, caller_name='rational decomp')
|
2024
|
+
F = f.factor()
|
2025
|
+
verbose('done factoring', t=t, level=2, caller_name='rational decomp')
|
2026
|
+
|
2027
|
+
if len(F) == 1:
|
2028
|
+
V = QQ**self.nrows()
|
2029
|
+
m = F[0][1]
|
2030
|
+
return decomp_seq([(V, m==1)])
|
2031
|
+
|
2032
|
+
V = ZZ**self.nrows()
|
2033
|
+
v = V.random_element()
|
2034
|
+
num_iterates = max([0] + [f.degree() - g.degree() for g, _ in F if g.degree() > 1]) + 1
|
2035
|
+
|
2036
|
+
S = []
|
2037
|
+
|
2038
|
+
F.sort()
|
2039
|
+
for i in range(len(F)):
|
2040
|
+
g, m = F[i]
|
2041
|
+
|
2042
|
+
if g.degree() == 1:
|
2043
|
+
# Just use kernel -- much easier.
|
2044
|
+
B = A.__copy__()
|
2045
|
+
for k from 0 <= k < A.nrows():
|
2046
|
+
B[k,k] += g[0]
|
2047
|
+
if m > 1 and not is_diagonalizable:
|
2048
|
+
B = B**m
|
2049
|
+
B = B.change_ring(QQ)
|
2050
|
+
W = B.kernel(algorithm = kernel_algorithm, **kwds)
|
2051
|
+
E.append((W, m==1))
|
2052
|
+
continue
|
2053
|
+
|
2054
|
+
# General case, i.e., deg(g) > 1:
|
2055
|
+
W = None
|
2056
|
+
tries = m
|
2057
|
+
while True:
|
2058
|
+
|
2059
|
+
# Compute the complementary factor of the charpoly.
|
2060
|
+
h = f // (g**m)
|
2061
|
+
v = h.list()
|
2062
|
+
|
2063
|
+
while len(S) < tries:
|
2064
|
+
t = verbose('%s-spinning %s-th random vector' % (num_iterates, len(S)),
|
2065
|
+
level=2, caller_name='rational decomp')
|
2066
|
+
S.append(A.iterates(V.random_element(x=-10,y=10), num_iterates))
|
2067
|
+
verbose('done spinning', level=2, t=t, caller_name='rational decomp')
|
2068
|
+
|
2069
|
+
for j in range(0 if W is None else W.nrows() // g.degree(), len(S)):
|
2070
|
+
# Compute one element of the kernel of g(A)**m.
|
2071
|
+
t = verbose('compute element of kernel of g(A), for g of degree %s' % g.degree(), level=2,
|
2072
|
+
caller_name='rational decomp')
|
2073
|
+
w = S[j].linear_combination_of_rows(h.list())
|
2074
|
+
t = verbose('done computing element of kernel of g(A)', t=t,level=2, caller_name='rational decomp')
|
2075
|
+
|
2076
|
+
# Get the rest of the kernel.
|
2077
|
+
t = verbose('fill out rest of kernel',level=2, caller_name='rational decomp')
|
2078
|
+
if W is None:
|
2079
|
+
W = A.iterates(w, g.degree())
|
2080
|
+
else:
|
2081
|
+
W = W.stack(A.iterates(w, g.degree()))
|
2082
|
+
t = verbose('finished filling out more of kernel',level=2, t=t, caller_name='rational decomp')
|
2083
|
+
|
2084
|
+
if W.rank() == m * g.degree():
|
2085
|
+
W = W.change_ring(QQ)
|
2086
|
+
t = verbose('now computing row space', level=2, caller_name='rational decomp')
|
2087
|
+
W.echelonize(algorithm = echelon_algorithm, **kwds)
|
2088
|
+
E.append((W.row_space(), m==1))
|
2089
|
+
verbose('computed row space', level=2,t=t, caller_name='rational decomp')
|
2090
|
+
break
|
2091
|
+
else:
|
2092
|
+
verbose('we have not yet generated all the kernel (rank so far=%s, target rank=%s)' % (W.rank(), m*g.degree()),
|
2093
|
+
level=2, caller_name='rational decomp')
|
2094
|
+
tries += 1
|
2095
|
+
if tries > 5*m:
|
2096
|
+
raise RuntimeError("likely bug in decomposition")
|
2097
|
+
# end if
|
2098
|
+
# end while
|
2099
|
+
# end for
|
2100
|
+
return decomp_seq(E)
|
2101
|
+
|
2102
|
+
# def simple_decomposition(self, echelon_algorithm='default', **kwds):
|
2103
|
+
# """
|
2104
|
+
# Returns the decomposition of the free module on which this
|
2105
|
+
# matrix A acts from the right (i.e., the action is x goes to x
|
2106
|
+
# A), as a direct sum of simple modules.
|
2107
|
+
|
2108
|
+
# NOTE: self *must* be diagonalizable.
|
2109
|
+
|
2110
|
+
# INPUT:
|
2111
|
+
# self -- a square matrix that is assumed to be diagonalizable
|
2112
|
+
# echelon_algorithm -- 'default'
|
2113
|
+
# 'multimodular' -- use this if the answers
|
2114
|
+
# have small height
|
2115
|
+
# **kwds -- passed on to echelon function.
|
2116
|
+
|
2117
|
+
# IMPORTANT NOTE:
|
2118
|
+
# If you expect that the subspaces in the answer are spanned by vectors
|
2119
|
+
# with small height coordinates, use algorithm='multimodular' and
|
2120
|
+
# height_guess=1; this is potentially much faster than the default.
|
2121
|
+
# If you know for a fact the answer will be very small, use
|
2122
|
+
# algorithm='multimodular', height_guess=bound on height, proof=False
|
2123
|
+
|
2124
|
+
# OUTPUT:
|
2125
|
+
# Sequence -- list of tuples (V,g), where V is a subspace
|
2126
|
+
# and an irreducible polynomial g, which is the
|
2127
|
+
# charpoly (=minpoly) of self acting on V.
|
2128
|
+
# """
|
2129
|
+
# cdef Py_ssize_t k
|
2130
|
+
|
2131
|
+
# if not self.is_square():
|
2132
|
+
# raise ArithmeticError("self must be a square matrix")
|
2133
|
+
|
2134
|
+
# if self.nrows() == 0:
|
2135
|
+
# return decomp_seq([])
|
2136
|
+
|
2137
|
+
# A, _ = self._clear_denom()
|
2138
|
+
|
2139
|
+
# f = A.charpoly('x')
|
2140
|
+
# E = decomp_seq([])
|
2141
|
+
|
2142
|
+
# t = verbose('factoring the characteristic polynomial', level=2, caller_name='simple decomp')
|
2143
|
+
# F = f.factor()
|
2144
|
+
# G = [g for g, _ in F]
|
2145
|
+
# minpoly = prod(G)
|
2146
|
+
# squarefree_degree = sum([g.degree() for g in G])
|
2147
|
+
# verbose('done factoring', t=t, level=2, caller_name='simple decomp')
|
2148
|
+
|
2149
|
+
# V = ZZ**self.nrows()
|
2150
|
+
# v = V.random_element()
|
2151
|
+
# num_iterates = max([squarefree_degree - g.degree() for g in G]) + 1
|
2152
|
+
|
2153
|
+
# S = [ ]
|
2154
|
+
|
2155
|
+
# F.sort()
|
2156
|
+
# for i in range(len(F)):
|
2157
|
+
# g, m = F[i]
|
2158
|
+
|
2159
|
+
# if g.degree() == 1:
|
2160
|
+
# # Just use kernel -- much easier.
|
2161
|
+
# B = A.__copy__()
|
2162
|
+
# for k from 0 <= k < A.nrows():
|
2163
|
+
# B[k,k] += g[0]
|
2164
|
+
# if m > 1 and not is_diagonalizable:
|
2165
|
+
# B = B**m
|
2166
|
+
# W = B.change_ring(QQ).kernel()
|
2167
|
+
# for b in W.basis():
|
2168
|
+
# E.append((W.span(b), g))
|
2169
|
+
# continue
|
2170
|
+
|
2171
|
+
# # General case, i.e., deg(g) > 1:
|
2172
|
+
# W = None
|
2173
|
+
# while True:
|
2174
|
+
|
2175
|
+
# # Compute the complementary factor of the charpoly.
|
2176
|
+
# h = minpoly // g
|
2177
|
+
# v = h.list()
|
2178
|
+
|
2179
|
+
# while len(S) < m:
|
2180
|
+
# t = verbose('%s-spinning %s-th random vector'%(num_iterates, len(S)),
|
2181
|
+
# level=2, caller_name='simple decomp')
|
2182
|
+
# S.append(A.iterates(V.random_element(x=-10,y=10), num_iterates))
|
2183
|
+
# verbose('done spinning', level=2, t=t, caller_name='simple decomp')
|
2184
|
+
|
2185
|
+
# for j in range(len(S)):
|
2186
|
+
# # Compute one element of the kernel of g(A).
|
2187
|
+
# t = verbose('compute element of kernel of g(A), for g of degree %s'%g.degree(),level=2,
|
2188
|
+
# caller_name='simple decomp')
|
2189
|
+
# w = S[j].linear_combination_of_rows(h.list())
|
2190
|
+
# t = verbose('done computing element of kernel of g(A)', t=t,level=2, caller_name='simple decomp')
|
2191
|
+
|
2192
|
+
# # Get the rest of the kernel.
|
2193
|
+
# t = verbose('fill out rest of kernel',level=2, caller_name='simple decomp')
|
2194
|
+
# if W is None:
|
2195
|
+
# W = A.iterates(w, g.degree())
|
2196
|
+
# else:
|
2197
|
+
# W = W.stack(A.iterates(w, g.degree()))
|
2198
|
+
# t = verbose('finished filling out more of kernel',level=2, t=t, caller_name='simple decomp')
|
2199
|
+
|
2200
|
+
# if W.rank() == m * g.degree():
|
2201
|
+
# W = W.change_ring(QQ)
|
2202
|
+
# t = verbose('now computing row space', level=2, caller_name='simple decomp')
|
2203
|
+
# W.echelonize(algorithm = echelon_algorithm, **kwds)
|
2204
|
+
# E.append((W.row_space(), m==1))
|
2205
|
+
# verbose('computed row space', level=2,t=t, caller_name='simple decomp')
|
2206
|
+
# break
|
2207
|
+
# else:
|
2208
|
+
# verbose('we have not yet generated all the kernel (rank so far=%s, target rank=%s)'%(
|
2209
|
+
# W.rank(), m*g.degree()), level=2, caller_name='simple decomp')
|
2210
|
+
# j += 1
|
2211
|
+
# if j > 3*m:
|
2212
|
+
# raise RuntimeError("likely bug in decomposition")
|
2213
|
+
# # end if
|
2214
|
+
# #end while
|
2215
|
+
# #end for
|
2216
|
+
# return E
|
2217
|
+
|
2218
|
+
def _lift_crt_rr(self, res, mm):
|
2219
|
+
from .matrix_rational_linbox import _lift_crt_rr
|
2220
|
+
return _lift_crt_rr(self, res, mm)
|
2221
|
+
|
2222
|
+
def randomize(self, density=1, num_bound=2, den_bound=2,
|
2223
|
+
distribution=None, nonzero=False):
|
2224
|
+
"""
|
2225
|
+
Randomize ``density`` proportion of the entries of this matrix, leaving
|
2226
|
+
the rest unchanged.
|
2227
|
+
|
2228
|
+
If ``x`` and ``y`` are given, randomized entries of this matrix have
|
2229
|
+
numerators and denominators bounded by ``x`` and ``y`` and have
|
2230
|
+
density 1.
|
2231
|
+
|
2232
|
+
INPUT:
|
2233
|
+
|
2234
|
+
- ``density`` -- number between 0 and 1 (default: 1)
|
2235
|
+
|
2236
|
+
- ``num_bound`` -- numerator bound (default: 2)
|
2237
|
+
|
2238
|
+
- ``den_bound`` -- denominator bound (default: 2)
|
2239
|
+
|
2240
|
+
- ``distribution`` -- ``None`` or '1/n' (default: ``None``); if '1/n'
|
2241
|
+
then ``num_bound``, ``den_bound`` are ignored and numbers are chosen
|
2242
|
+
using the GMP function ``mpq_randomize_entry_recip_uniform``
|
2243
|
+
|
2244
|
+
OUTPUT: none; the matrix is modified in-space
|
2245
|
+
|
2246
|
+
EXAMPLES:
|
2247
|
+
|
2248
|
+
The default distribution::
|
2249
|
+
|
2250
|
+
sage: from collections import defaultdict
|
2251
|
+
sage: total_count = 0
|
2252
|
+
sage: dic = defaultdict(Integer)
|
2253
|
+
sage: def add_samples(distribution=None):
|
2254
|
+
....: global dic, total_count
|
2255
|
+
....: for _ in range(100):
|
2256
|
+
....: A = Matrix(QQ, 2, 4, 0)
|
2257
|
+
....: A.randomize(distribution=distribution)
|
2258
|
+
....: for a in A.list():
|
2259
|
+
....: dic[a] += 1
|
2260
|
+
....: total_count += 1.0
|
2261
|
+
|
2262
|
+
sage: expected = {-2: 1/9, -1: 3/18, -1/2: 1/18, 0: 3/9,
|
2263
|
+
....: 1/2: 1/18, 1: 3/18, 2: 1/9}
|
2264
|
+
sage: add_samples()
|
2265
|
+
sage: while not all(abs(dic[a]/total_count - expected[a]) < 0.001 for a in dic):
|
2266
|
+
....: add_samples()
|
2267
|
+
|
2268
|
+
The distribution ``'1/n'``::
|
2269
|
+
|
2270
|
+
sage: def mpq_randomize_entry_recip_uniform():
|
2271
|
+
....: r = 2*random() - 1
|
2272
|
+
....: if r == 0: r = 1
|
2273
|
+
....: num = int(4/(5*r))
|
2274
|
+
....: r = random()
|
2275
|
+
....: if r == 0: r = 1
|
2276
|
+
....: den = int(1/random())
|
2277
|
+
....: return Integer(num)/Integer(den)
|
2278
|
+
|
2279
|
+
sage: total_count = 0
|
2280
|
+
sage: dic = defaultdict(Integer)
|
2281
|
+
sage: dic2 = defaultdict(Integer)
|
2282
|
+
sage: add_samples('1/n')
|
2283
|
+
sage: for _ in range(8):
|
2284
|
+
....: dic2[mpq_randomize_entry_recip_uniform()] += 1
|
2285
|
+
sage: while not all(abs(dic[a] - dic2[a])/total_count < 0.005 for a in dic):
|
2286
|
+
....: add_samples('1/n')
|
2287
|
+
....: for _ in range(800):
|
2288
|
+
....: dic2[mpq_randomize_entry_recip_uniform()] += 1
|
2289
|
+
|
2290
|
+
The default can be used to obtain matrices of different rank::
|
2291
|
+
|
2292
|
+
sage: ranks = [False]*11
|
2293
|
+
sage: while not all(ranks):
|
2294
|
+
....: for dens in (0.05, 0.1, 0.2, 0.5):
|
2295
|
+
....: A = Matrix(QQ, 10, 10, 0)
|
2296
|
+
....: A.randomize(dens)
|
2297
|
+
....: ranks[A.rank()] = True
|
2298
|
+
|
2299
|
+
The default density is `6/9`::
|
2300
|
+
|
2301
|
+
sage: def add_sample(density, num_rows, num_cols):
|
2302
|
+
....: global density_sum, total_count
|
2303
|
+
....: total_count += 1.0
|
2304
|
+
....: A = Matrix(QQ, num_rows, num_cols, 0)
|
2305
|
+
....: A.randomize(density)
|
2306
|
+
....: density_sum += float(A.density())
|
2307
|
+
|
2308
|
+
sage: density_sum = 0.0
|
2309
|
+
sage: total_count = 0.0
|
2310
|
+
sage: expected_density = 6/9
|
2311
|
+
sage: add_sample(1.0, 100, 100)
|
2312
|
+
sage: while abs(density_sum/total_count - expected_density) > 0.001:
|
2313
|
+
....: add_sample(1.0, 100, 100)
|
2314
|
+
|
2315
|
+
The modified density depends on the number of columns::
|
2316
|
+
|
2317
|
+
sage: density_sum = 0.0
|
2318
|
+
sage: total_count = 0.0
|
2319
|
+
sage: expected_density = 6/9*0.5
|
2320
|
+
sage: add_sample(0.5, 100, 2)
|
2321
|
+
sage: while abs(density_sum/total_count - expected_density) > 0.001:
|
2322
|
+
....: add_sample(0.5, 100, 2)
|
2323
|
+
|
2324
|
+
sage: density_sum = 0.0
|
2325
|
+
sage: total_count = 0.0
|
2326
|
+
sage: expected_density = 6/9*(1.0 - (99/100)^50)
|
2327
|
+
sage: expected_density
|
2328
|
+
0.263...
|
2329
|
+
|
2330
|
+
sage: add_sample(0.5, 100, 100)
|
2331
|
+
sage: while abs(density_sum/total_count - expected_density) > 0.001:
|
2332
|
+
....: add_sample(0.5, 100, 100)
|
2333
|
+
|
2334
|
+
Modifying the bounds for numerator and denominator::
|
2335
|
+
|
2336
|
+
sage: num_dic = defaultdict(Integer)
|
2337
|
+
sage: den_dic = defaultdict(Integer)
|
2338
|
+
sage: while not (all(num_dic[i] for i in range(-200, 201))
|
2339
|
+
....: and all(den_dic[i] for i in range(1, 101))):
|
2340
|
+
....: a = matrix(QQ, 2, 4)
|
2341
|
+
....: a.randomize(num_bound=200, den_bound=100)
|
2342
|
+
....: for q in a.list():
|
2343
|
+
....: num_dic[q.numerator()] += 1
|
2344
|
+
....: den_dic[q.denominator()] += 1
|
2345
|
+
sage: len(num_dic)
|
2346
|
+
401
|
2347
|
+
sage: len(den_dic)
|
2348
|
+
100
|
2349
|
+
|
2350
|
+
TESTS:
|
2351
|
+
|
2352
|
+
Check that the option ``nonzero`` is meaningful (:issue:`22970`)::
|
2353
|
+
|
2354
|
+
sage: a = matrix(QQ, 10, 10, 1)
|
2355
|
+
sage: b = a.__copy__()
|
2356
|
+
sage: b.randomize(nonzero=True)
|
2357
|
+
sage: a == b
|
2358
|
+
False
|
2359
|
+
sage: any(b[i,j].is_zero() for i in range(10) for j in range(10))
|
2360
|
+
False
|
2361
|
+
|
2362
|
+
Check that :issue:`34103` is fixed::
|
2363
|
+
|
2364
|
+
sage: a = matrix(QQ, 10, 10, 1)
|
2365
|
+
sage: a.randomize(nonzero=True, distribution='1/n')
|
2366
|
+
sage: bool(a)
|
2367
|
+
True
|
2368
|
+
"""
|
2369
|
+
density = float(density)
|
2370
|
+
if density <= 0.0:
|
2371
|
+
return
|
2372
|
+
|
2373
|
+
self.check_mutability()
|
2374
|
+
self.clear_cache()
|
2375
|
+
|
2376
|
+
cdef Integer B, C
|
2377
|
+
cdef Py_ssize_t i, j, k, num_per_row
|
2378
|
+
cdef randstate rstate
|
2379
|
+
cdef mpq_t tmp
|
2380
|
+
|
2381
|
+
B = Integer(num_bound + 1)
|
2382
|
+
C = Integer(den_bound + 1)
|
2383
|
+
|
2384
|
+
mpq_init(tmp)
|
2385
|
+
|
2386
|
+
if not nonzero:
|
2387
|
+
if density >= 1.0:
|
2388
|
+
if distribution == "1/n":
|
2389
|
+
sig_on()
|
2390
|
+
for i in range(self._nrows):
|
2391
|
+
for j in range(self._ncols):
|
2392
|
+
mpq_randomize_entry_recip_uniform(tmp)
|
2393
|
+
fmpq_set_mpq(fmpq_mat_entry(self._matrix, i, j), tmp)
|
2394
|
+
sig_off()
|
2395
|
+
elif mpz_cmp_si(C.value, 2): # denom is > 1
|
2396
|
+
sig_on()
|
2397
|
+
for i in range(self._nrows):
|
2398
|
+
for j in range(self._ncols):
|
2399
|
+
mpq_randomize_entry(tmp, B.value, C.value)
|
2400
|
+
fmpq_set_mpq(fmpq_mat_entry(self._matrix, i, j), tmp)
|
2401
|
+
sig_off()
|
2402
|
+
else:
|
2403
|
+
sig_on()
|
2404
|
+
for i in range(self._nrows):
|
2405
|
+
for j in range(self._ncols):
|
2406
|
+
mpq_randomize_entry_as_int(tmp, B.value)
|
2407
|
+
fmpq_set_mpq(fmpq_mat_entry(self._matrix, i, j), tmp)
|
2408
|
+
sig_off()
|
2409
|
+
else:
|
2410
|
+
rstate = current_randstate()
|
2411
|
+
num_per_row = int(density * self._ncols)
|
2412
|
+
if distribution == "1/n":
|
2413
|
+
sig_on()
|
2414
|
+
for i in range(self._nrows):
|
2415
|
+
for j in range(num_per_row):
|
2416
|
+
k = rstate.c_random() % self._ncols
|
2417
|
+
mpq_randomize_entry_recip_uniform(tmp)
|
2418
|
+
fmpq_set_mpq(fmpq_mat_entry(self._matrix, i, k), tmp)
|
2419
|
+
sig_off()
|
2420
|
+
elif mpz_cmp_si(C.value, 2): # denom is > 1
|
2421
|
+
sig_on()
|
2422
|
+
for i in range(self._nrows):
|
2423
|
+
for j in range(num_per_row):
|
2424
|
+
k = rstate.c_random() % self._ncols
|
2425
|
+
mpq_randomize_entry(tmp, B.value, C.value)
|
2426
|
+
fmpq_set_mpq(fmpq_mat_entry(self._matrix, i, k), tmp)
|
2427
|
+
sig_off()
|
2428
|
+
else:
|
2429
|
+
sig_on()
|
2430
|
+
for i in range(self._nrows):
|
2431
|
+
for j in range(num_per_row):
|
2432
|
+
k = rstate.c_random() % self._ncols
|
2433
|
+
mpq_randomize_entry_as_int(tmp, B.value)
|
2434
|
+
fmpq_set_mpq(fmpq_mat_entry(self._matrix, i, k), tmp)
|
2435
|
+
sig_off()
|
2436
|
+
else:
|
2437
|
+
if density >= 1.0:
|
2438
|
+
if distribution == "1/n":
|
2439
|
+
sig_on()
|
2440
|
+
for i in range(self._nrows):
|
2441
|
+
for j in range(self._ncols):
|
2442
|
+
mpq_randomize_entry_recip_uniform_nonzero(tmp)
|
2443
|
+
fmpq_set_mpq(fmpq_mat_entry(self._matrix, i, j), tmp)
|
2444
|
+
sig_off()
|
2445
|
+
elif mpz_cmp_si(C.value, 2): # denom is > 1
|
2446
|
+
sig_on()
|
2447
|
+
for i in range(self._nrows):
|
2448
|
+
for j in range(self._ncols):
|
2449
|
+
mpq_randomize_entry_nonzero(tmp, B.value, C.value)
|
2450
|
+
fmpq_set_mpq(fmpq_mat_entry(self._matrix, i, j), tmp)
|
2451
|
+
sig_off()
|
2452
|
+
else:
|
2453
|
+
sig_on()
|
2454
|
+
for i in range(self._nrows):
|
2455
|
+
for j in range(self._ncols):
|
2456
|
+
mpq_randomize_entry_as_int_nonzero(tmp, B.value)
|
2457
|
+
fmpq_set_mpq(fmpq_mat_entry(self._matrix, i, j), tmp)
|
2458
|
+
sig_off()
|
2459
|
+
else:
|
2460
|
+
rstate = current_randstate()
|
2461
|
+
num_per_row = int(density * self._ncols)
|
2462
|
+
if distribution == "1/n":
|
2463
|
+
sig_on()
|
2464
|
+
for i in range(self._nrows):
|
2465
|
+
for j in range(num_per_row):
|
2466
|
+
k = rstate.c_random() % self._ncols
|
2467
|
+
mpq_randomize_entry_recip_uniform_nonzero(tmp)
|
2468
|
+
fmpq_set_mpq(fmpq_mat_entry(self._matrix, i, k), tmp)
|
2469
|
+
sig_off()
|
2470
|
+
elif mpz_cmp_si(C.value, 2): # denom is > 1
|
2471
|
+
sig_on()
|
2472
|
+
for i in range(self._nrows):
|
2473
|
+
for j in range(num_per_row):
|
2474
|
+
k = rstate.c_random() % self._ncols
|
2475
|
+
mpq_randomize_entry_nonzero(tmp, B.value, C.value)
|
2476
|
+
fmpq_set_mpq(fmpq_mat_entry(self._matrix, i, k), tmp)
|
2477
|
+
sig_off()
|
2478
|
+
else:
|
2479
|
+
sig_on()
|
2480
|
+
for i in range(self._nrows):
|
2481
|
+
for j in range(num_per_row):
|
2482
|
+
k = rstate.c_random() % self._ncols
|
2483
|
+
mpq_randomize_entry_as_int_nonzero(tmp, B.value)
|
2484
|
+
fmpq_set_mpq(fmpq_mat_entry(self._matrix, i, k), tmp)
|
2485
|
+
sig_off()
|
2486
|
+
|
2487
|
+
mpq_clear(tmp)
|
2488
|
+
|
2489
|
+
def rank(self, algorithm=None):
|
2490
|
+
"""
|
2491
|
+
Return the rank of this matrix.
|
2492
|
+
|
2493
|
+
INPUT:
|
2494
|
+
|
2495
|
+
- ``algorithm`` -- an optional specification of an algorithm. One of
|
2496
|
+
|
2497
|
+
- ``None`` -- (default) will use flint
|
2498
|
+
|
2499
|
+
- ``'flint'`` -- uses the flint library
|
2500
|
+
|
2501
|
+
- ``'pari'`` -- uses the PARI library
|
2502
|
+
|
2503
|
+
- ``'integer'`` -- eliminate denominators and calls the rank function
|
2504
|
+
on the corresponding integer matrix
|
2505
|
+
|
2506
|
+
EXAMPLES::
|
2507
|
+
|
2508
|
+
sage: matrix(QQ,3,[1..9]).rank()
|
2509
|
+
2
|
2510
|
+
sage: matrix(QQ,100,[1..100^2]).rank()
|
2511
|
+
2
|
2512
|
+
|
2513
|
+
TESTS::
|
2514
|
+
|
2515
|
+
sage: for _ in range(100): # needs sage.libs.pari
|
2516
|
+
....: dim = randint(0, 30)
|
2517
|
+
....: m = random_matrix(QQ, dim, num_bound=2, density=0.5)
|
2518
|
+
....: r_pari = m.rank('pari'); m._clear_cache()
|
2519
|
+
....: r_flint = m.rank('flint'); m._clear_cache()
|
2520
|
+
....: r_int = m.rank('integer'); m._clear_cache()
|
2521
|
+
....: assert r_pari == r_flint == r_int
|
2522
|
+
"""
|
2523
|
+
r = self.fetch('rank')
|
2524
|
+
if r is not None:
|
2525
|
+
return r
|
2526
|
+
|
2527
|
+
if algorithm is None:
|
2528
|
+
algorithm = "flint"
|
2529
|
+
|
2530
|
+
if algorithm == "flint":
|
2531
|
+
self.echelon_form(algorithm='flint')
|
2532
|
+
return self.fetch('rank')
|
2533
|
+
elif algorithm == "pari":
|
2534
|
+
from .matrix_rational_pari import _rank_pari
|
2535
|
+
r = _rank_pari(self)
|
2536
|
+
elif algorithm == "integer":
|
2537
|
+
A, _ = self._clear_denom()
|
2538
|
+
r = A.rank()
|
2539
|
+
else:
|
2540
|
+
raise ValueError("unknown algorithm %s" % algorithm)
|
2541
|
+
|
2542
|
+
self.cache('rank', r)
|
2543
|
+
return r
|
2544
|
+
|
2545
|
+
def transpose(self):
|
2546
|
+
"""
|
2547
|
+
Return the transpose of ``self``, without changing ``self``.
|
2548
|
+
|
2549
|
+
EXAMPLES:
|
2550
|
+
|
2551
|
+
We create a matrix, compute its transpose, and note that the
|
2552
|
+
original matrix is not changed.
|
2553
|
+
|
2554
|
+
::
|
2555
|
+
|
2556
|
+
sage: A = matrix(QQ, 2, 3, range(6))
|
2557
|
+
sage: type(A)
|
2558
|
+
<class 'sage.matrix.matrix_rational_dense.Matrix_rational_dense'>
|
2559
|
+
sage: B = A.transpose()
|
2560
|
+
sage: print(B)
|
2561
|
+
[0 3]
|
2562
|
+
[1 4]
|
2563
|
+
[2 5]
|
2564
|
+
sage: print(A)
|
2565
|
+
[0 1 2]
|
2566
|
+
[3 4 5]
|
2567
|
+
|
2568
|
+
``.T`` is a convenient shortcut for the transpose::
|
2569
|
+
|
2570
|
+
sage: print(A.T)
|
2571
|
+
[0 3]
|
2572
|
+
[1 4]
|
2573
|
+
[2 5]
|
2574
|
+
|
2575
|
+
::
|
2576
|
+
|
2577
|
+
sage: A.subdivide(None, 1); A
|
2578
|
+
[0|1 2]
|
2579
|
+
[3|4 5]
|
2580
|
+
sage: A.transpose()
|
2581
|
+
[0 3]
|
2582
|
+
[---]
|
2583
|
+
[1 4]
|
2584
|
+
[2 5]
|
2585
|
+
"""
|
2586
|
+
cdef Matrix_rational_dense ans
|
2587
|
+
if self._nrows == self._ncols:
|
2588
|
+
parent = self._parent
|
2589
|
+
else:
|
2590
|
+
parent = self._parent.matrix_space(self._ncols, self._nrows)
|
2591
|
+
ans = Matrix_rational_dense.__new__(Matrix_rational_dense, parent, None, None, None)
|
2592
|
+
sig_on()
|
2593
|
+
fmpq_mat_transpose(ans._matrix, self._matrix)
|
2594
|
+
sig_off()
|
2595
|
+
|
2596
|
+
if self._subdivisions is not None:
|
2597
|
+
row_divs, col_divs = self.subdivisions()
|
2598
|
+
ans.subdivide(col_divs, row_divs)
|
2599
|
+
return ans
|
2600
|
+
|
2601
|
+
def antitranspose(self):
|
2602
|
+
"""
|
2603
|
+
Return the antitranspose of ``self``, without changing ``self``.
|
2604
|
+
|
2605
|
+
EXAMPLES::
|
2606
|
+
|
2607
|
+
sage: A = matrix(QQ,2,3,range(6))
|
2608
|
+
sage: type(A)
|
2609
|
+
<class 'sage.matrix.matrix_rational_dense.Matrix_rational_dense'>
|
2610
|
+
sage: A.antitranspose()
|
2611
|
+
[5 2]
|
2612
|
+
[4 1]
|
2613
|
+
[3 0]
|
2614
|
+
sage: A
|
2615
|
+
[0 1 2]
|
2616
|
+
[3 4 5]
|
2617
|
+
|
2618
|
+
sage: A.subdivide(1,2); A
|
2619
|
+
[0 1|2]
|
2620
|
+
[---+-]
|
2621
|
+
[3 4|5]
|
2622
|
+
sage: A.antitranspose()
|
2623
|
+
[5|2]
|
2624
|
+
[-+-]
|
2625
|
+
[4|1]
|
2626
|
+
[3|0]
|
2627
|
+
"""
|
2628
|
+
if self._nrows == self._ncols:
|
2629
|
+
parent = self._parent
|
2630
|
+
else:
|
2631
|
+
parent = self._parent.matrix_space(self._ncols, self._nrows)
|
2632
|
+
|
2633
|
+
cdef Matrix_rational_dense ans
|
2634
|
+
ans = Matrix_rational_dense.__new__(Matrix_rational_dense, parent, None, None, None)
|
2635
|
+
|
2636
|
+
cdef Py_ssize_t i,j
|
2637
|
+
cdef Py_ssize_t ri,rj # reversed i and j
|
2638
|
+
sig_on()
|
2639
|
+
ri = self._nrows
|
2640
|
+
for i in range(self._nrows):
|
2641
|
+
rj = self._ncols
|
2642
|
+
ri = ri - 1
|
2643
|
+
for j in range(self._ncols):
|
2644
|
+
rj = rj - 1
|
2645
|
+
fmpq_set(fmpq_mat_entry(ans._matrix, rj, ri),
|
2646
|
+
fmpq_mat_entry(self._matrix, i, j))
|
2647
|
+
sig_off()
|
2648
|
+
|
2649
|
+
if self._subdivisions is not None:
|
2650
|
+
row_divs, col_divs = self.subdivisions()
|
2651
|
+
ans.subdivide([self._ncols - t for t in reversed(col_divs)],
|
2652
|
+
[self._nrows - t for t in reversed(row_divs)])
|
2653
|
+
return ans
|
2654
|
+
|
2655
|
+
def set_row_to_multiple_of_row(self, Py_ssize_t i, Py_ssize_t j, s):
|
2656
|
+
"""
|
2657
|
+
Set row i equal to s times row j.
|
2658
|
+
|
2659
|
+
EXAMPLES::
|
2660
|
+
|
2661
|
+
sage: a = matrix(QQ,2,3,range(6)); a
|
2662
|
+
[0 1 2]
|
2663
|
+
[3 4 5]
|
2664
|
+
sage: a.set_row_to_multiple_of_row(1,0,-3)
|
2665
|
+
sage: a
|
2666
|
+
[ 0 1 2]
|
2667
|
+
[ 0 -3 -6]
|
2668
|
+
"""
|
2669
|
+
self.check_row_bounds_and_mutability(i, j)
|
2670
|
+
cdef Py_ssize_t k
|
2671
|
+
cdef fmpq_t ss
|
2672
|
+
fmpq_init(ss)
|
2673
|
+
fmpq_set_mpq(ss, (<Rational> Rational(s)).value)
|
2674
|
+
for k in range(self._ncols):
|
2675
|
+
fmpq_mul(fmpq_mat_entry(self._matrix, i, k),
|
2676
|
+
fmpq_mat_entry(self._matrix, j, k),
|
2677
|
+
ss)
|
2678
|
+
fmpq_clear(ss)
|
2679
|
+
|
2680
|
+
def _set_row_to_negative_of_row_of_A_using_subset_of_columns(self, Py_ssize_t i, Matrix A,
|
2681
|
+
Py_ssize_t r, cols,
|
2682
|
+
cols_index=None):
|
2683
|
+
"""
|
2684
|
+
Set row i of ``self`` to -(row r of A), but where we only take the
|
2685
|
+
given column positions in that row of A. We do not zero out the
|
2686
|
+
other entries of ``self``'s row i either.
|
2687
|
+
|
2688
|
+
.. NOTE::
|
2689
|
+
|
2690
|
+
This function exists just because it is useful for modular symbols presentations.
|
2691
|
+
|
2692
|
+
INPUT:
|
2693
|
+
|
2694
|
+
- ``i`` -- integer, index into the rows of self
|
2695
|
+
|
2696
|
+
- ``A`` -- a matrix
|
2697
|
+
|
2698
|
+
- ``r`` -- integer, index into rows of A
|
2699
|
+
|
2700
|
+
- ``cols`` -- a *sorted* list of integers
|
2701
|
+
|
2702
|
+
EXAMPLES::
|
2703
|
+
|
2704
|
+
sage: a = matrix(QQ,2,3,range(6)); a
|
2705
|
+
[0 1 2]
|
2706
|
+
[3 4 5]
|
2707
|
+
sage: a._set_row_to_negative_of_row_of_A_using_subset_of_columns(0,a,1,[1,2])
|
2708
|
+
sage: a
|
2709
|
+
[-4 -5 2]
|
2710
|
+
[ 3 4 5]
|
2711
|
+
"""
|
2712
|
+
self.check_row_bounds_and_mutability(i,i)
|
2713
|
+
cdef Matrix_rational_dense _A
|
2714
|
+
if r < 0 or r >= A.nrows():
|
2715
|
+
raise IndexError("invalid row")
|
2716
|
+
cdef Py_ssize_t l = 0
|
2717
|
+
|
2718
|
+
if not A.base_ring() == QQ:
|
2719
|
+
A = A.change_ring(QQ)
|
2720
|
+
if not A.is_dense():
|
2721
|
+
A = A.dense_matrix()
|
2722
|
+
|
2723
|
+
_A = A
|
2724
|
+
for k in cols:
|
2725
|
+
entry = fmpq_mat_entry(self._matrix, i, l)
|
2726
|
+
fmpq_set(entry, fmpq_mat_entry(_A._matrix, r, k))
|
2727
|
+
fmpq_neg(entry, entry)
|
2728
|
+
l += 1
|
2729
|
+
|
2730
|
+
def _add_col_j_of_A_to_col_i_of_self(self, Py_ssize_t i,
|
2731
|
+
Matrix_rational_dense A, Py_ssize_t j):
|
2732
|
+
"""
|
2733
|
+
Unsafe technical function that very quickly adds the `j`-th column of
|
2734
|
+
A to the `i`-th column of ``self``.
|
2735
|
+
|
2736
|
+
Does not check mutability.
|
2737
|
+
"""
|
2738
|
+
if A._nrows != self._nrows:
|
2739
|
+
raise TypeError("nrows of self and A must be the same")
|
2740
|
+
cdef Py_ssize_t r
|
2741
|
+
for r in range(self._nrows):
|
2742
|
+
fmpq_add(fmpq_mat_entry(self._matrix, r, i),
|
2743
|
+
fmpq_mat_entry(self._matrix, r, i),
|
2744
|
+
fmpq_mat_entry(A._matrix, r, j))
|
2745
|
+
|
2746
|
+
# ###############################################
|
2747
|
+
# Methods using PARI library
|
2748
|
+
# ###############################################
|
2749
|
+
|
2750
|
+
def __pari__(self):
|
2751
|
+
"""
|
2752
|
+
Return pari version of this matrix.
|
2753
|
+
|
2754
|
+
EXAMPLES::
|
2755
|
+
|
2756
|
+
sage: matrix(QQ,2,[1/5,-2/3,3/4,4/9]).__pari__() # needs sage.libs.pari
|
2757
|
+
[1/5, -2/3; 3/4, 4/9]
|
2758
|
+
"""
|
2759
|
+
from .matrix_rational_pari import _pari
|
2760
|
+
return _pari(self)
|
2761
|
+
|
2762
|
+
def _det_pari(self, int flag=0):
|
2763
|
+
"""
|
2764
|
+
Return the determinant of this matrix computed using pari.
|
2765
|
+
|
2766
|
+
EXAMPLES::
|
2767
|
+
|
2768
|
+
sage: matrix(QQ,3,[1..9])._det_pari() # needs sage.libs.pari
|
2769
|
+
0
|
2770
|
+
sage: matrix(QQ,3,[1..9])._det_pari(1) # needs sage.libs.pari
|
2771
|
+
0
|
2772
|
+
sage: matrix(QQ,3,[0]+[2..9])._det_pari() # needs sage.libs.pari
|
2773
|
+
3
|
2774
|
+
"""
|
2775
|
+
from .matrix_rational_pari import _det_pari
|
2776
|
+
return _det_pari(self, flag)
|
2777
|
+
|
2778
|
+
def _rank_pari(self):
|
2779
|
+
"""
|
2780
|
+
Return the rank of this matrix computed using pari.
|
2781
|
+
|
2782
|
+
EXAMPLES::
|
2783
|
+
|
2784
|
+
sage: matrix(QQ,3,[1..9])._rank_pari() # needs sage.libs.pari
|
2785
|
+
2
|
2786
|
+
sage: matrix(QQ, 0, 0)._rank_pari() # needs sage.libs.pari
|
2787
|
+
0
|
2788
|
+
"""
|
2789
|
+
from .matrix_rational_pari import _rank_pari
|
2790
|
+
return _rank_pari(self)
|
2791
|
+
|
2792
|
+
def _multiply_pari(self, Matrix_rational_dense right):
|
2793
|
+
"""
|
2794
|
+
Return the product of ``self`` and ``right``, computed using PARI.
|
2795
|
+
|
2796
|
+
EXAMPLES::
|
2797
|
+
|
2798
|
+
sage: matrix(QQ,2,[1/5,-2/3,3/4,4/9])._multiply_pari(matrix(QQ,2,[1,2,3,4])) # needs sage.libs.pari
|
2799
|
+
[ -9/5 -34/15]
|
2800
|
+
[ 25/12 59/18]
|
2801
|
+
|
2802
|
+
We verify that 0 rows or columns works::
|
2803
|
+
|
2804
|
+
sage: x = matrix(QQ,2,0); y = matrix(QQ,0,2); x*y
|
2805
|
+
[0 0]
|
2806
|
+
[0 0]
|
2807
|
+
sage: matrix(ZZ, 0, 0) * matrix(QQ, 0, 5)
|
2808
|
+
[]
|
2809
|
+
"""
|
2810
|
+
from .matrix_rational_pari import _multiply_pari
|
2811
|
+
return _multiply_pari(self, right)
|
2812
|
+
|
2813
|
+
def _invert_pari(self):
|
2814
|
+
"""
|
2815
|
+
Return the inverse of this matrix computed using PARI.
|
2816
|
+
|
2817
|
+
EXAMPLES::
|
2818
|
+
|
2819
|
+
sage: matrix(QQ,2,[1,2,3,4])._invert_pari() # needs sage.libs.pari
|
2820
|
+
[ -2 1]
|
2821
|
+
[ 3/2 -1/2]
|
2822
|
+
sage: matrix(QQ,2,[1,2,2,4])._invert_pari() # needs sage.libs.pari
|
2823
|
+
Traceback (most recent call last):
|
2824
|
+
...
|
2825
|
+
PariError: impossible inverse in ginv: [1, 2; 2, 4]
|
2826
|
+
"""
|
2827
|
+
from .matrix_rational_pari import _invert_pari
|
2828
|
+
return _invert_pari(self)
|
2829
|
+
|
2830
|
+
def row(self, Py_ssize_t i, from_list=False):
|
2831
|
+
"""
|
2832
|
+
Return the `i`-th row of this matrix as a dense vector.
|
2833
|
+
|
2834
|
+
INPUT:
|
2835
|
+
|
2836
|
+
- ``i`` -- integer
|
2837
|
+
|
2838
|
+
- ``from_list`` -- ignored
|
2839
|
+
|
2840
|
+
EXAMPLES::
|
2841
|
+
|
2842
|
+
sage: m = matrix(QQ, 2, [1/5, -2/3, 3/4, 4/9])
|
2843
|
+
sage: m.row(0)
|
2844
|
+
(1/5, -2/3)
|
2845
|
+
sage: m.row(1)
|
2846
|
+
(3/4, 4/9)
|
2847
|
+
sage: m.row(1, from_list=True)
|
2848
|
+
(3/4, 4/9)
|
2849
|
+
sage: m.row(-2)
|
2850
|
+
(1/5, -2/3)
|
2851
|
+
|
2852
|
+
sage: m.row(2)
|
2853
|
+
Traceback (most recent call last):
|
2854
|
+
...
|
2855
|
+
IndexError: row index out of range
|
2856
|
+
sage: m.row(-3)
|
2857
|
+
Traceback (most recent call last):
|
2858
|
+
...
|
2859
|
+
IndexError: row index out of range
|
2860
|
+
"""
|
2861
|
+
if i < 0:
|
2862
|
+
i = i + self._nrows
|
2863
|
+
if i < 0 or i >= self._nrows:
|
2864
|
+
raise IndexError("row index out of range")
|
2865
|
+
|
2866
|
+
cdef Py_ssize_t j
|
2867
|
+
parent = self.row_ambient_module()
|
2868
|
+
cdef Vector_rational_dense v = Vector_rational_dense.__new__(Vector_rational_dense)
|
2869
|
+
v._init(self._ncols, parent)
|
2870
|
+
for j in range(self._ncols):
|
2871
|
+
fmpq_get_mpq(v._entries[j], fmpq_mat_entry(self._matrix, i, j))
|
2872
|
+
return v
|
2873
|
+
|
2874
|
+
def column(self, Py_ssize_t i, from_list=False):
|
2875
|
+
"""
|
2876
|
+
Return the `i`-th column of this matrix as a dense vector.
|
2877
|
+
|
2878
|
+
INPUT:
|
2879
|
+
|
2880
|
+
- ``i`` -- integer
|
2881
|
+
|
2882
|
+
- ``from_list`` -- ignored
|
2883
|
+
|
2884
|
+
EXAMPLES::
|
2885
|
+
|
2886
|
+
sage: m = matrix(QQ, 3, 2, [1/5,-2/3,3/4,4/9,-1,0])
|
2887
|
+
sage: m.column(1)
|
2888
|
+
(-2/3, 4/9, 0)
|
2889
|
+
sage: m.column(1,from_list=True)
|
2890
|
+
(-2/3, 4/9, 0)
|
2891
|
+
sage: m.column(-1)
|
2892
|
+
(-2/3, 4/9, 0)
|
2893
|
+
sage: m.column(-2)
|
2894
|
+
(1/5, 3/4, -1)
|
2895
|
+
|
2896
|
+
sage: m.column(2)
|
2897
|
+
Traceback (most recent call last):
|
2898
|
+
...
|
2899
|
+
IndexError: column index out of range
|
2900
|
+
sage: m.column(-3)
|
2901
|
+
Traceback (most recent call last):
|
2902
|
+
...
|
2903
|
+
IndexError: column index out of range
|
2904
|
+
"""
|
2905
|
+
if i < 0:
|
2906
|
+
i += self._ncols
|
2907
|
+
if i < 0 or i >= self._ncols:
|
2908
|
+
raise IndexError("column index out of range")
|
2909
|
+
|
2910
|
+
cdef Py_ssize_t j
|
2911
|
+
parent = self.column_ambient_module()
|
2912
|
+
cdef Vector_rational_dense v = Vector_rational_dense.__new__(Vector_rational_dense)
|
2913
|
+
v._init(self._nrows, parent)
|
2914
|
+
for j in range(self._nrows):
|
2915
|
+
fmpq_get_mpq(v._entries[j], fmpq_mat_entry(self._matrix, j, i))
|
2916
|
+
return v
|
2917
|
+
|
2918
|
+
# ###############################################
|
2919
|
+
# LLL
|
2920
|
+
# ###############################################
|
2921
|
+
|
2922
|
+
def BKZ(self, *args, **kwargs):
|
2923
|
+
"""
|
2924
|
+
Return the result of running Block Korkin-Zolotarev reduction on
|
2925
|
+
``self`` interpreted as a lattice.
|
2926
|
+
|
2927
|
+
The arguments ``*args`` and ``**kwargs`` are passed onto
|
2928
|
+
:meth:`sage.matrix.matrix_integer_dense.Matrix_integer_dense.BKZ`,
|
2929
|
+
see there for more details.
|
2930
|
+
|
2931
|
+
EXAMPLES::
|
2932
|
+
|
2933
|
+
sage: A = Matrix(QQ, 3, 3, [1/n for n in range(1, 10)])
|
2934
|
+
sage: A.BKZ() # needs fpylll
|
2935
|
+
[ 1/28 -1/40 -1/18]
|
2936
|
+
[ 1/28 -1/40 1/18]
|
2937
|
+
[-1/14 -1/40 0]
|
2938
|
+
|
2939
|
+
sage: A = random_matrix(QQ, 10, 10)
|
2940
|
+
sage: d = lcm(a.denom() for a in A.list())
|
2941
|
+
sage: A.BKZ() == (A * d).change_ring(ZZ).BKZ() / d # needs fpylll
|
2942
|
+
True
|
2943
|
+
"""
|
2944
|
+
A, d = self._clear_denom()
|
2945
|
+
return A.BKZ(*args, **kwargs) / d
|
2946
|
+
|
2947
|
+
def LLL(self, *args, **kwargs):
|
2948
|
+
"""
|
2949
|
+
Return an LLL reduced or approximated LLL reduced lattice for
|
2950
|
+
``self`` interpreted as a lattice.
|
2951
|
+
|
2952
|
+
The arguments ``*args`` and ``**kwargs`` are passed onto
|
2953
|
+
:meth:`sage.matrix.matrix_integer_dense.Matrix_integer_dense.LLL`,
|
2954
|
+
see there for more details.
|
2955
|
+
|
2956
|
+
EXAMPLES::
|
2957
|
+
|
2958
|
+
sage: # needs fpylll
|
2959
|
+
sage: A = Matrix(QQ, 3, 3, [1/n for n in range(1, 10)])
|
2960
|
+
sage: A.LLL()
|
2961
|
+
[ 1/28 -1/40 -1/18]
|
2962
|
+
[ 1/28 -1/40 1/18]
|
2963
|
+
[ 0 -3/40 0]
|
2964
|
+
sage: L, U = A.LLL(transformation=True)
|
2965
|
+
sage: U * A == L
|
2966
|
+
True
|
2967
|
+
sage: A = random_matrix(QQ, 10, 10)
|
2968
|
+
sage: d = lcm(a.denom() for a in A.list())
|
2969
|
+
sage: A.LLL() == (A * d).change_ring(ZZ).LLL() / d
|
2970
|
+
True
|
2971
|
+
"""
|
2972
|
+
A, d = self._clear_denom()
|
2973
|
+
if kwargs.get('transformation', False):
|
2974
|
+
L, U = A.LLL(*args, **kwargs)
|
2975
|
+
return L / d, U
|
2976
|
+
return A.LLL(*args, **kwargs) / d
|
2977
|
+
|
2978
|
+
def is_LLL_reduced(self, delta=None, eta=None):
|
2979
|
+
r"""
|
2980
|
+
Return ``True`` if this lattice is `(\delta, \eta)`-LLL reduced.
|
2981
|
+
For a definition of LLL reduction, see
|
2982
|
+
:meth:`sage.matrix.matrix_integer_dense.Matrix_integer_dense.LLL`.
|
2983
|
+
|
2984
|
+
EXAMPLES::
|
2985
|
+
|
2986
|
+
sage: # needs fpylll
|
2987
|
+
sage: A = random_matrix(QQ, 10, 10)
|
2988
|
+
sage: L = A.LLL()
|
2989
|
+
sage: A.is_LLL_reduced()
|
2990
|
+
False
|
2991
|
+
sage: L.is_LLL_reduced()
|
2992
|
+
True
|
2993
|
+
"""
|
2994
|
+
A, _ = self._clear_denom()
|
2995
|
+
return A.is_LLL_reduced(delta, eta)
|