passagemath-flint 10.6.1rc10__cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- passagemath_flint-10.6.1rc10.dist-info/METADATA +122 -0
- passagemath_flint-10.6.1rc10.dist-info/RECORD +360 -0
- passagemath_flint-10.6.1rc10.dist-info/WHEEL +6 -0
- passagemath_flint-10.6.1rc10.dist-info/top_level.txt +2 -0
- passagemath_flint.libs/libflint-3701249d.so.21.0.0 +0 -0
- passagemath_flint.libs/libgf2x-fbd36f80.so.3.0.0 +0 -0
- passagemath_flint.libs/libgfortran-8a9a71bc.so.5.0.0 +0 -0
- passagemath_flint.libs/libgmp-93ebf16a.so.10.5.0 +0 -0
- passagemath_flint.libs/libgsl-e3525837.so.28.0.0 +0 -0
- passagemath_flint.libs/libmpfi-ad12a86d.so.0.0.0 +0 -0
- passagemath_flint.libs/libmpfr-e0f11cf3.so.6.2.1 +0 -0
- passagemath_flint.libs/libntl-1004113e.so.44.0.1 +0 -0
- passagemath_flint.libs/libopenblasp-r0-4c5b64b1.3.29.so +0 -0
- sage/all__sagemath_flint.py +29 -0
- sage/combinat/all__sagemath_flint.py +1 -0
- sage/combinat/posets/all__sagemath_flint.py +1 -0
- sage/combinat/posets/hasse_cython_flint.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/posets/hasse_cython_flint.pyx +194 -0
- sage/data_structures/all__sagemath_flint.py +1 -0
- sage/data_structures/bounded_integer_sequences.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/data_structures/bounded_integer_sequences.pxd +62 -0
- sage/data_structures/bounded_integer_sequences.pyx +1418 -0
- sage/graphs/all__sagemath_flint.py +1 -0
- sage/graphs/chrompoly.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/chrompoly.pyx +555 -0
- sage/graphs/matchpoly.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/matchpoly.pyx +412 -0
- sage/libs/all__sagemath_flint.py +17 -0
- sage/libs/arb/__init__.py +1 -0
- sage/libs/arb/acb.pxd +154 -0
- sage/libs/arb/acb_calc.pxd +9 -0
- sage/libs/arb/acb_elliptic.pxd +25 -0
- sage/libs/arb/acb_hypgeom.pxd +74 -0
- sage/libs/arb/acb_mat.pxd +62 -0
- sage/libs/arb/acb_modular.pxd +17 -0
- sage/libs/arb/acb_poly.pxd +216 -0
- sage/libs/arb/arb.pxd +240 -0
- sage/libs/arb/arb_fmpz_poly.pxd +21 -0
- sage/libs/arb/arb_hypgeom.pxd +83 -0
- sage/libs/arb/arb_wrap.h +34 -0
- sage/libs/arb/arf.pxd +131 -0
- sage/libs/arb/arith.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/libs/arb/arith.pyx +87 -0
- sage/libs/arb/bernoulli.pxd +6 -0
- sage/libs/arb/mag.pxd +77 -0
- sage/libs/arb/types.pxd +37 -0
- sage/libs/flint/__init__.py +1 -0
- sage/libs/flint/acb.pxd +270 -0
- sage/libs/flint/acb_calc.pxd +22 -0
- sage/libs/flint/acb_dft.pxd +51 -0
- sage/libs/flint/acb_dirichlet.pxd +112 -0
- sage/libs/flint/acb_elliptic.pxd +42 -0
- sage/libs/flint/acb_hypgeom.pxd +169 -0
- sage/libs/flint/acb_macros.pxd +9 -0
- sage/libs/flint/acb_mat.pxd +136 -0
- sage/libs/flint/acb_mat_macros.pxd +10 -0
- sage/libs/flint/acb_modular.pxd +62 -0
- sage/libs/flint/acb_poly.pxd +251 -0
- sage/libs/flint/acb_poly_macros.pxd +8 -0
- sage/libs/flint/acb_theta.pxd +124 -0
- sage/libs/flint/acf.pxd +32 -0
- sage/libs/flint/aprcl.pxd +84 -0
- sage/libs/flint/arb.pxd +382 -0
- sage/libs/flint/arb_calc.pxd +31 -0
- sage/libs/flint/arb_fmpz_poly.pxd +34 -0
- sage/libs/flint/arb_fpwrap.pxd +215 -0
- sage/libs/flint/arb_hypgeom.pxd +147 -0
- sage/libs/flint/arb_macros.pxd +9 -0
- sage/libs/flint/arb_mat.pxd +140 -0
- sage/libs/flint/arb_mat_macros.pxd +10 -0
- sage/libs/flint/arb_poly.pxd +237 -0
- sage/libs/flint/arf.pxd +167 -0
- sage/libs/flint/arith.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/libs/flint/arith.pxd +76 -0
- sage/libs/flint/arith.pyx +77 -0
- sage/libs/flint/arith_sage.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/libs/flint/arith_sage.pyx +308 -0
- sage/libs/flint/bernoulli.pxd +28 -0
- sage/libs/flint/bool_mat.pxd +52 -0
- sage/libs/flint/ca.pxd +203 -0
- sage/libs/flint/ca_ext.pxd +34 -0
- sage/libs/flint/ca_field.pxd +32 -0
- sage/libs/flint/ca_mat.pxd +117 -0
- sage/libs/flint/ca_poly.pxd +104 -0
- sage/libs/flint/ca_vec.pxd +46 -0
- sage/libs/flint/calcium.pxd +27 -0
- sage/libs/flint/d_mat.pxd +39 -0
- sage/libs/flint/d_vec.pxd +32 -0
- sage/libs/flint/dirichlet.pxd +57 -0
- sage/libs/flint/dlog.pxd +53 -0
- sage/libs/flint/double_extras.pxd +24 -0
- sage/libs/flint/double_interval.pxd +36 -0
- sage/libs/flint/fexpr.pxd +104 -0
- sage/libs/flint/fexpr_builtin.pxd +20 -0
- sage/libs/flint/fft.pxd +66 -0
- sage/libs/flint/flint.pxd +36 -0
- sage/libs/flint/flint_ntl_wrap.h +35 -0
- sage/libs/flint/flint_sage.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/libs/flint/flint_sage.pyx +163 -0
- sage/libs/flint/flint_wrap.h +190 -0
- sage/libs/flint/fmpq.pxd +137 -0
- sage/libs/flint/fmpq_mat.pxd +105 -0
- sage/libs/flint/fmpq_mat_macros.pxd +10 -0
- sage/libs/flint/fmpq_mpoly.pxd +165 -0
- sage/libs/flint/fmpq_mpoly_factor.pxd +30 -0
- sage/libs/flint/fmpq_poly.pxd +241 -0
- sage/libs/flint/fmpq_poly_macros.pxd +9 -0
- sage/libs/flint/fmpq_poly_sage.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/libs/flint/fmpq_poly_sage.pxd +31 -0
- sage/libs/flint/fmpq_poly_sage.pyx +48 -0
- sage/libs/flint/fmpq_vec.pxd +27 -0
- sage/libs/flint/fmpz.pxd +256 -0
- sage/libs/flint/fmpz_extras.pxd +32 -0
- sage/libs/flint/fmpz_factor.pxd +42 -0
- sage/libs/flint/fmpz_factor_sage.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/libs/flint/fmpz_factor_sage.pxd +4 -0
- sage/libs/flint/fmpz_factor_sage.pyx +29 -0
- sage/libs/flint/fmpz_lll.pxd +49 -0
- sage/libs/flint/fmpz_macros.pxd +8 -0
- sage/libs/flint/fmpz_mat.pxd +184 -0
- sage/libs/flint/fmpz_mat_macros.pxd +10 -0
- sage/libs/flint/fmpz_mod.pxd +46 -0
- sage/libs/flint/fmpz_mod_mat.pxd +71 -0
- sage/libs/flint/fmpz_mod_mpoly.pxd +161 -0
- sage/libs/flint/fmpz_mod_mpoly_factor.pxd +28 -0
- sage/libs/flint/fmpz_mod_poly.pxd +249 -0
- sage/libs/flint/fmpz_mod_poly_factor.pxd +46 -0
- sage/libs/flint/fmpz_mod_vec.pxd +27 -0
- sage/libs/flint/fmpz_mpoly.pxd +224 -0
- sage/libs/flint/fmpz_mpoly_factor.pxd +29 -0
- sage/libs/flint/fmpz_mpoly_q.pxd +57 -0
- sage/libs/flint/fmpz_poly.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/libs/flint/fmpz_poly.pxd +407 -0
- sage/libs/flint/fmpz_poly.pyx +19 -0
- sage/libs/flint/fmpz_poly_factor.pxd +33 -0
- sage/libs/flint/fmpz_poly_macros.pxd +8 -0
- sage/libs/flint/fmpz_poly_mat.pxd +71 -0
- sage/libs/flint/fmpz_poly_q.pxd +55 -0
- sage/libs/flint/fmpz_poly_sage.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/libs/flint/fmpz_poly_sage.pxd +20 -0
- sage/libs/flint/fmpz_poly_sage.pyx +500 -0
- sage/libs/flint/fmpz_vec.pxd +80 -0
- sage/libs/flint/fmpzi.pxd +52 -0
- sage/libs/flint/fq.pxd +97 -0
- sage/libs/flint/fq_default.pxd +84 -0
- sage/libs/flint/fq_default_mat.pxd +70 -0
- sage/libs/flint/fq_default_poly.pxd +97 -0
- sage/libs/flint/fq_default_poly_factor.pxd +39 -0
- sage/libs/flint/fq_embed.pxd +28 -0
- sage/libs/flint/fq_mat.pxd +83 -0
- sage/libs/flint/fq_nmod.pxd +95 -0
- sage/libs/flint/fq_nmod_embed.pxd +28 -0
- sage/libs/flint/fq_nmod_mat.pxd +83 -0
- sage/libs/flint/fq_nmod_mpoly.pxd +130 -0
- sage/libs/flint/fq_nmod_mpoly_factor.pxd +28 -0
- sage/libs/flint/fq_nmod_poly.pxd +202 -0
- sage/libs/flint/fq_nmod_poly_factor.pxd +47 -0
- sage/libs/flint/fq_nmod_vec.pxd +33 -0
- sage/libs/flint/fq_poly.pxd +204 -0
- sage/libs/flint/fq_poly_factor.pxd +47 -0
- sage/libs/flint/fq_vec.pxd +33 -0
- sage/libs/flint/fq_zech.pxd +99 -0
- sage/libs/flint/fq_zech_embed.pxd +28 -0
- sage/libs/flint/fq_zech_mat.pxd +78 -0
- sage/libs/flint/fq_zech_poly.pxd +198 -0
- sage/libs/flint/fq_zech_poly_factor.pxd +47 -0
- sage/libs/flint/fq_zech_vec.pxd +33 -0
- sage/libs/flint/gr.pxd +174 -0
- sage/libs/flint/gr_generic.pxd +215 -0
- sage/libs/flint/gr_mat.pxd +161 -0
- sage/libs/flint/gr_mpoly.pxd +68 -0
- sage/libs/flint/gr_poly.pxd +276 -0
- sage/libs/flint/gr_special.pxd +237 -0
- sage/libs/flint/gr_vec.pxd +120 -0
- sage/libs/flint/hypgeom.pxd +24 -0
- sage/libs/flint/long_extras.pxd +23 -0
- sage/libs/flint/mag.pxd +131 -0
- sage/libs/flint/mag_macros.pxd +8 -0
- sage/libs/flint/mpf_mat.pxd +36 -0
- sage/libs/flint/mpf_vec.pxd +34 -0
- sage/libs/flint/mpfr_mat.pxd +27 -0
- sage/libs/flint/mpfr_vec.pxd +25 -0
- sage/libs/flint/mpn_extras.pxd +41 -0
- sage/libs/flint/mpoly.pxd +72 -0
- sage/libs/flint/nf.pxd +19 -0
- sage/libs/flint/nf_elem.pxd +74 -0
- sage/libs/flint/nmod.pxd +35 -0
- sage/libs/flint/nmod_mat.pxd +104 -0
- sage/libs/flint/nmod_mpoly.pxd +144 -0
- sage/libs/flint/nmod_mpoly_factor.pxd +28 -0
- sage/libs/flint/nmod_poly.pxd +339 -0
- sage/libs/flint/nmod_poly_factor.pxd +44 -0
- sage/libs/flint/nmod_poly_linkage.pxi +710 -0
- sage/libs/flint/nmod_poly_mat.pxd +76 -0
- sage/libs/flint/nmod_vec.pxd +40 -0
- sage/libs/flint/ntl_interface.pxd +17 -0
- sage/libs/flint/padic.pxd +93 -0
- sage/libs/flint/padic_mat.pxd +64 -0
- sage/libs/flint/padic_poly.pxd +88 -0
- sage/libs/flint/partitions.pxd +23 -0
- sage/libs/flint/perm.pxd +26 -0
- sage/libs/flint/profiler.pxd +24 -0
- sage/libs/flint/qadic.pxd +77 -0
- sage/libs/flint/qfb.pxd +44 -0
- sage/libs/flint/qqbar.pxd +172 -0
- sage/libs/flint/qsieve.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/libs/flint/qsieve.pxd +41 -0
- sage/libs/flint/qsieve.pyx +21 -0
- sage/libs/flint/qsieve_sage.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/libs/flint/qsieve_sage.pyx +67 -0
- sage/libs/flint/thread_pool.pxd +25 -0
- sage/libs/flint/types.pxd +2076 -0
- sage/libs/flint/ulong_extras.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/libs/flint/ulong_extras.pxd +141 -0
- sage/libs/flint/ulong_extras.pyx +21 -0
- sage/libs/flint/ulong_extras_sage.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/libs/flint/ulong_extras_sage.pyx +21 -0
- sage/matrix/all__sagemath_flint.py +1 -0
- sage/matrix/change_ring.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/matrix/change_ring.pyx +43 -0
- sage/matrix/matrix_complex_ball_dense.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/matrix/matrix_complex_ball_dense.pxd +14 -0
- sage/matrix/matrix_complex_ball_dense.pyx +973 -0
- sage/matrix/matrix_cyclo_dense.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/matrix/matrix_cyclo_dense.pxd +16 -0
- sage/matrix/matrix_cyclo_dense.pyx +1761 -0
- sage/matrix/matrix_integer_dense.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/matrix/matrix_integer_dense.pxd +32 -0
- sage/matrix/matrix_integer_dense.pyx +5801 -0
- sage/matrix/matrix_integer_dense_hnf.py +1294 -0
- sage/matrix/matrix_integer_dense_saturation.py +346 -0
- sage/matrix/matrix_integer_sparse.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/matrix/matrix_integer_sparse.pxd +9 -0
- sage/matrix/matrix_integer_sparse.pyx +1090 -0
- sage/matrix/matrix_rational_dense.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/matrix/matrix_rational_dense.pxd +23 -0
- sage/matrix/matrix_rational_dense.pyx +2995 -0
- sage/matrix/matrix_rational_sparse.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/matrix/matrix_rational_sparse.pxd +11 -0
- sage/matrix/matrix_rational_sparse.pyx +789 -0
- sage/matrix/misc_flint.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/matrix/misc_flint.pyx +109 -0
- sage/modular/all__sagemath_flint.py +1 -0
- sage/modular/modform/all__sagemath_flint.py +1 -0
- sage/modular/modform/eis_series_cython.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/modular/modform/eis_series_cython.pyx +226 -0
- sage/modular/modsym/all__sagemath_flint.py +1 -0
- sage/modular/modsym/apply.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/modular/modsym/apply.pxd +6 -0
- sage/modular/modsym/apply.pyx +113 -0
- sage/modular/modsym/heilbronn.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/modular/modsym/heilbronn.pyx +966 -0
- sage/modular/pollack_stevens/all__sagemath_flint.py +1 -0
- sage/modular/pollack_stevens/dist.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/modular/pollack_stevens/dist.pxd +38 -0
- sage/modular/pollack_stevens/dist.pyx +1439 -0
- sage/quivers/algebra.py +691 -0
- sage/quivers/algebra_elements.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/quivers/algebra_elements.pxd +97 -0
- sage/quivers/algebra_elements.pxi +1324 -0
- sage/quivers/algebra_elements.pyx +1424 -0
- sage/quivers/all.py +1 -0
- sage/quivers/ar_quiver.py +917 -0
- sage/quivers/homspace.py +640 -0
- sage/quivers/morphism.py +1282 -0
- sage/quivers/path_semigroup.py +1155 -0
- sage/quivers/paths.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/quivers/paths.pxd +13 -0
- sage/quivers/paths.pyx +809 -0
- sage/quivers/representation.py +2975 -0
- sage/rings/all__sagemath_flint.py +37 -0
- sage/rings/cif.py +4 -0
- sage/rings/complex_arb.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/rings/complex_arb.pxd +29 -0
- sage/rings/complex_arb.pyx +5176 -0
- sage/rings/complex_interval.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/rings/complex_interval.pxd +30 -0
- sage/rings/complex_interval.pyx +2475 -0
- sage/rings/complex_interval_field.py +711 -0
- sage/rings/convert/all.py +1 -0
- sage/rings/convert/mpfi.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/rings/convert/mpfi.pxd +6 -0
- sage/rings/convert/mpfi.pyx +576 -0
- sage/rings/factorint_flint.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/rings/factorint_flint.pyx +99 -0
- sage/rings/fraction_field_FpT.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/rings/fraction_field_FpT.pxd +28 -0
- sage/rings/fraction_field_FpT.pyx +2043 -0
- sage/rings/imaginary_unit.py +5 -0
- sage/rings/monomials.py +73 -0
- sage/rings/number_field/S_unit_solver.py +2870 -0
- sage/rings/number_field/all__sagemath_flint.py +7 -0
- sage/rings/number_field/bdd_height.py +664 -0
- sage/rings/number_field/class_group.py +762 -0
- sage/rings/number_field/galois_group.py +1307 -0
- sage/rings/number_field/homset.py +612 -0
- sage/rings/number_field/maps.py +687 -0
- sage/rings/number_field/morphism.py +272 -0
- sage/rings/number_field/number_field.py +12820 -0
- sage/rings/number_field/number_field_element.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/rings/number_field/number_field_element.pxd +59 -0
- sage/rings/number_field/number_field_element.pyx +5735 -0
- sage/rings/number_field/number_field_element_quadratic.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/rings/number_field/number_field_element_quadratic.pxd +34 -0
- sage/rings/number_field/number_field_element_quadratic.pyx +3185 -0
- sage/rings/number_field/number_field_ideal_rel.py +925 -0
- sage/rings/number_field/number_field_morphisms.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/rings/number_field/number_field_morphisms.pyx +781 -0
- sage/rings/number_field/number_field_rel.py +2734 -0
- sage/rings/number_field/order.py +2981 -0
- sage/rings/number_field/order_ideal.py +804 -0
- sage/rings/number_field/selmer_group.py +715 -0
- sage/rings/number_field/small_primes_of_degree_one.py +242 -0
- sage/rings/number_field/splitting_field.py +606 -0
- sage/rings/number_field/structure.py +380 -0
- sage/rings/number_field/unit_group.py +721 -0
- sage/rings/padics/all__sagemath_flint.py +3 -0
- sage/rings/polynomial/all__sagemath_flint.py +1 -0
- sage/rings/polynomial/complex_roots.py +312 -0
- sage/rings/polynomial/evaluation_flint.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/evaluation_flint.pxd +7 -0
- sage/rings/polynomial/evaluation_flint.pyx +68 -0
- sage/rings/polynomial/hilbert.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/hilbert.pyx +602 -0
- sage/rings/polynomial/polynomial_complex_arb.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/polynomial_complex_arb.pxd +7 -0
- sage/rings/polynomial/polynomial_complex_arb.pyx +963 -0
- sage/rings/polynomial/polynomial_integer_dense_flint.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/polynomial_integer_dense_flint.pxd +13 -0
- sage/rings/polynomial/polynomial_integer_dense_flint.pyx +1881 -0
- sage/rings/polynomial/polynomial_number_field.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/polynomial_number_field.pyx +345 -0
- sage/rings/polynomial/polynomial_rational_flint.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/polynomial_rational_flint.pxd +20 -0
- sage/rings/polynomial/polynomial_rational_flint.pyx +2598 -0
- sage/rings/polynomial/polynomial_zmod_flint.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/polynomial_zmod_flint.pxd +20 -0
- sage/rings/polynomial/polynomial_zmod_flint.pyx +1063 -0
- sage/rings/polynomial/real_roots.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/real_roots.pxd +81 -0
- sage/rings/polynomial/real_roots.pyx +4704 -0
- sage/rings/polynomial/refine_root.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/refine_root.pyx +142 -0
- sage/rings/polynomial/weil/all.py +4 -0
- sage/rings/polynomial/weil/power_sums.h +46 -0
- sage/rings/polynomial/weil/weil_polynomials.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/weil/weil_polynomials.pyx +596 -0
- sage/rings/qqbar.py +9025 -0
- sage/rings/real_arb.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/rings/real_arb.pxd +21 -0
- sage/rings/real_arb.pyx +4065 -0
- sage/rings/real_interval_absolute.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/rings/real_interval_absolute.pyx +1073 -0
- sage/rings/real_mpfi.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/rings/real_mpfi.pyx +5428 -0
- sage/schemes/all__sagemath_flint.py +1 -0
- sage/schemes/elliptic_curves/all__sagemath_flint.py +1 -0
- sage/schemes/elliptic_curves/descent_two_isogeny.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/schemes/elliptic_curves/descent_two_isogeny.pyx +1387 -0
- sage/schemes/elliptic_curves/descent_two_isogeny_pari.pxd +5 -0
@@ -0,0 +1,1761 @@
|
|
1
|
+
# sage_setup: distribution = sagemath-flint
|
2
|
+
# distutils: language = c++
|
3
|
+
# distutils: libraries = NTL_LIBRARIES
|
4
|
+
# distutils: extra_compile_args = NTL_CFLAGS
|
5
|
+
# distutils: include_dirs = NTL_INCDIR
|
6
|
+
# distutils: library_dirs = NTL_LIBDIR
|
7
|
+
# distutils: extra_link_args = NTL_LIBEXTRA
|
8
|
+
# sage.doctest: needs sagemath-linbox
|
9
|
+
"""
|
10
|
+
Matrices over Cyclotomic Fields
|
11
|
+
|
12
|
+
The underlying matrix for a Matrix_cyclo_dense object is stored as
|
13
|
+
follows: given an n x m matrix over a cyclotomic field of degree d, we
|
14
|
+
store a d x (nm) matrix over QQ, each column of which corresponds to
|
15
|
+
an element of the original matrix. This can be retrieved via the
|
16
|
+
_rational_matrix method. Here is an example illustrating this:
|
17
|
+
|
18
|
+
EXAMPLES::
|
19
|
+
|
20
|
+
sage: F.<zeta> = CyclotomicField(5)
|
21
|
+
sage: M = Matrix(F, 2, 3, [zeta, 3, zeta**4+5, (zeta+1)**4, 0, 1])
|
22
|
+
sage: M
|
23
|
+
[ zeta 3 -zeta^3 - zeta^2 - zeta + 4]
|
24
|
+
[3*zeta^3 + 5*zeta^2 + 3*zeta 0 1]
|
25
|
+
|
26
|
+
sage: M._rational_matrix()
|
27
|
+
[ 0 3 4 0 0 1]
|
28
|
+
[ 1 0 -1 3 0 0]
|
29
|
+
[ 0 0 -1 5 0 0]
|
30
|
+
[ 0 0 -1 3 0 0]
|
31
|
+
|
32
|
+
|
33
|
+
AUTHORS:
|
34
|
+
* William Stein
|
35
|
+
* Craig Citro
|
36
|
+
"""
|
37
|
+
|
38
|
+
#*****************************************************************************
|
39
|
+
# Copyright (C) 2008 William Stein <wstein@gmail.com>
|
40
|
+
#
|
41
|
+
# This program is free software: you can redistribute it and/or modify
|
42
|
+
# it under the terms of the GNU General Public License as published by
|
43
|
+
# the Free Software Foundation, either version 2 of the License, or
|
44
|
+
# (at your option) any later version.
|
45
|
+
# https://www.gnu.org/licenses/
|
46
|
+
#*****************************************************************************
|
47
|
+
|
48
|
+
from cysignals.signals cimport sig_on, sig_off
|
49
|
+
|
50
|
+
include "sage/libs/ntl/decl.pxi"
|
51
|
+
|
52
|
+
from sage.structure.element cimport Element
|
53
|
+
from sage.misc.randstate cimport randstate, current_randstate
|
54
|
+
from sage.libs.gmp.randomize cimport *
|
55
|
+
|
56
|
+
from sage.libs.flint.types cimport fmpz_t
|
57
|
+
from sage.libs.flint.fmpz cimport fmpz_init, fmpz_clear, fmpz_set_mpz, fmpz_one, fmpz_get_mpz, fmpz_add, fmpz_mul, fmpz_sub, fmpz_mul_si, fmpz_mul_si, fmpz_mul_si, fmpz_divexact, fmpz_lcm
|
58
|
+
from sage.libs.flint.fmpq cimport fmpq_is_zero, fmpq_set_mpq, fmpq_canonicalise
|
59
|
+
from sage.libs.flint.fmpq_mat cimport fmpq_mat_entry_num, fmpq_mat_entry_den, fmpq_mat_entry
|
60
|
+
|
61
|
+
from sage.matrix.args cimport MatrixArgs_init
|
62
|
+
from sage.matrix.constructor import matrix
|
63
|
+
from sage.matrix.matrix_space import MatrixSpace
|
64
|
+
from sage.matrix.matrix cimport Matrix
|
65
|
+
from sage.matrix import matrix_dense
|
66
|
+
from sage.structure.element cimport Matrix as baseMatrix
|
67
|
+
|
68
|
+
from sage.arith.misc import binomial
|
69
|
+
from sage.rings.rational_field import QQ
|
70
|
+
from sage.rings.integer_ring import ZZ
|
71
|
+
from sage.rings.real_mpfr import create_RealNumber as RealNumber
|
72
|
+
from sage.rings.integer cimport Integer
|
73
|
+
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
|
74
|
+
from sage.rings.number_field.number_field_element cimport NumberFieldElement
|
75
|
+
from sage.rings.number_field.number_field_element_quadratic cimport NumberFieldElement_quadratic
|
76
|
+
|
77
|
+
from sage.misc.verbose import verbose
|
78
|
+
|
79
|
+
|
80
|
+
cdef class Matrix_cyclo_dense(Matrix_dense):
|
81
|
+
def __cinit__(self):
|
82
|
+
"""
|
83
|
+
EXAMPLES::
|
84
|
+
|
85
|
+
sage: from sage.matrix.matrix_cyclo_dense import Matrix_cyclo_dense
|
86
|
+
sage: A = Matrix_cyclo_dense.__new__(Matrix_cyclo_dense, MatrixSpace(CyclotomicField(3),2), [0,1,2,3], True, True)
|
87
|
+
sage: type(A)
|
88
|
+
<class 'sage.matrix.matrix_cyclo_dense.Matrix_cyclo_dense'>
|
89
|
+
|
90
|
+
Note that the entries of A haven't even been set yet above; that doesn't
|
91
|
+
happen until ``__init__`` is called::
|
92
|
+
|
93
|
+
sage: A[0,0]
|
94
|
+
Traceback (most recent call last):
|
95
|
+
...
|
96
|
+
ValueError: matrix entries not yet initialized
|
97
|
+
"""
|
98
|
+
self._degree = self._base_ring.degree()
|
99
|
+
self._n = int(self._base_ring._n())
|
100
|
+
|
101
|
+
def __init__(self, parent, entries=None, copy=None, bint coerce=True):
|
102
|
+
"""
|
103
|
+
Initialize a newly created cyclotomic matrix.
|
104
|
+
|
105
|
+
INPUT:
|
106
|
+
|
107
|
+
- ``parent`` -- a matrix space over a cyclotomic number field
|
108
|
+
|
109
|
+
- ``entries`` -- see :func:`matrix`
|
110
|
+
|
111
|
+
- ``copy`` -- ignored (for backwards compatibility)
|
112
|
+
|
113
|
+
- ``coerce`` -- if ``False``, assume without checking that the
|
114
|
+
entries lie in the base ring
|
115
|
+
|
116
|
+
EXAMPLES:
|
117
|
+
|
118
|
+
This function is called implicitly when you create new
|
119
|
+
cyclotomic dense matrices::
|
120
|
+
|
121
|
+
sage: W.<a> = CyclotomicField(100)
|
122
|
+
sage: A = matrix(2, 3, [1, 1/a, 1-a,a, -2/3*a, a^19])
|
123
|
+
sage: A
|
124
|
+
[ 1 -a^39 + a^29 - a^19 + a^9 -a + 1]
|
125
|
+
[ a -2/3*a a^19]
|
126
|
+
sage: TestSuite(A).run()
|
127
|
+
|
128
|
+
TESTS::
|
129
|
+
|
130
|
+
sage: matrix(W, 2, 1, a)
|
131
|
+
Traceback (most recent call last):
|
132
|
+
...
|
133
|
+
TypeError: nonzero scalar matrix must be square
|
134
|
+
|
135
|
+
We call __init__ explicitly below::
|
136
|
+
|
137
|
+
sage: from sage.matrix.matrix_cyclo_dense import Matrix_cyclo_dense
|
138
|
+
sage: A = Matrix_cyclo_dense.__new__(Matrix_cyclo_dense, MatrixSpace(CyclotomicField(3),2), [0,1,2,3], True, True)
|
139
|
+
sage: A.__init__(MatrixSpace(CyclotomicField(3),2), [0,1,2,3], True, True)
|
140
|
+
sage: A
|
141
|
+
[0 1]
|
142
|
+
[2 3]
|
143
|
+
"""
|
144
|
+
ma = MatrixArgs_init(parent, entries)
|
145
|
+
cdef list L = []
|
146
|
+
for x in ma.iter(coerce):
|
147
|
+
L += x.list()
|
148
|
+
|
149
|
+
QQspace = MatrixSpace(QQ, self._nrows * self._ncols, self._degree)
|
150
|
+
QQmat = Matrix_rational_dense(QQspace, L, False, False)
|
151
|
+
self._matrix = QQmat.transpose()
|
152
|
+
|
153
|
+
cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, value):
|
154
|
+
"""
|
155
|
+
Set the ij-th entry of ``self``.
|
156
|
+
|
157
|
+
WARNING: This function does no bounds checking whatsoever, as
|
158
|
+
the name suggests. It also assumes certain facts about the
|
159
|
+
internal representation of cyclotomic fields. This is intended
|
160
|
+
for internal use only.
|
161
|
+
|
162
|
+
EXAMPLES::
|
163
|
+
|
164
|
+
sage: K.<z> = CyclotomicField(11) ; M = Matrix(K,2,range(4))
|
165
|
+
sage: M[0,1] = z ; M
|
166
|
+
[0 z]
|
167
|
+
[2 3]
|
168
|
+
|
169
|
+
sage: K.<z> = CyclotomicField(3) ; M = Matrix(K,2,range(4))
|
170
|
+
sage: M[1,1] = z+1 ; M
|
171
|
+
[ 0 1]
|
172
|
+
[ 2 z + 1]
|
173
|
+
|
174
|
+
TESTS:
|
175
|
+
|
176
|
+
Since separate code exists for each quadratic field, we need
|
177
|
+
doctests for each.::
|
178
|
+
|
179
|
+
sage: K.<z> = CyclotomicField(4) ; M = Matrix(K,2,range(4))
|
180
|
+
sage: M[1,1] = z+1 ; M
|
181
|
+
[ 0 1]
|
182
|
+
[ 2 z + 1]
|
183
|
+
sage: K.<z> = CyclotomicField(6) ; M = Matrix(K,2,range(4))
|
184
|
+
sage: M[1,1] = z+1 ; M
|
185
|
+
[ 0 1]
|
186
|
+
[ 2 z + 1]
|
187
|
+
"""
|
188
|
+
# NEW FAST VERSION -- makes assumptions about how the
|
189
|
+
# cyclotomic field is implemented.
|
190
|
+
cdef Py_ssize_t k, c
|
191
|
+
cdef NumberFieldElement v
|
192
|
+
cdef NumberFieldElement_quadratic qv
|
193
|
+
cdef mpz_t numer, denom
|
194
|
+
cdef fmpz_t ftmp
|
195
|
+
|
196
|
+
# The i,j entry is the (i * self._ncols + j)'th column.
|
197
|
+
c = i * self._ncols + j
|
198
|
+
|
199
|
+
if self._degree == 2:
|
200
|
+
# Must be coded differently, since elements of
|
201
|
+
# quadratic number fields are stored differently.
|
202
|
+
qv = <NumberFieldElement_quadratic> value
|
203
|
+
if self._n == 4:
|
204
|
+
fmpz_set_mpz(fmpq_mat_entry_num(self._matrix._matrix, 0, c),
|
205
|
+
qv.a)
|
206
|
+
fmpz_set_mpz(fmpq_mat_entry_den(self._matrix._matrix, 0, c),
|
207
|
+
qv.denom)
|
208
|
+
fmpq_canonicalise(fmpq_mat_entry(self._matrix._matrix, 0, c))
|
209
|
+
|
210
|
+
fmpz_set_mpz(fmpq_mat_entry_num(self._matrix._matrix, 1, c),
|
211
|
+
qv.b)
|
212
|
+
fmpz_set_mpz(fmpq_mat_entry_den(self._matrix._matrix, 1, c),
|
213
|
+
qv.denom)
|
214
|
+
fmpq_canonicalise(fmpq_mat_entry(self._matrix._matrix, 1, c))
|
215
|
+
elif self._n == 3:
|
216
|
+
# mat[0,c] = (x.a + x.b) / x.denom
|
217
|
+
fmpz_set_mpz(fmpq_mat_entry_num(self._matrix._matrix, 0, c),
|
218
|
+
qv.a)
|
219
|
+
|
220
|
+
# NOTE: it would be convenient here to have fmpz_add_mpz
|
221
|
+
fmpz_init(ftmp)
|
222
|
+
fmpz_set_mpz(ftmp, qv.b)
|
223
|
+
fmpz_add(fmpq_mat_entry_num(self._matrix._matrix, 0, c),
|
224
|
+
fmpq_mat_entry_num(self._matrix._matrix, 0, c),
|
225
|
+
ftmp)
|
226
|
+
fmpz_clear(ftmp)
|
227
|
+
|
228
|
+
fmpz_set_mpz(fmpq_mat_entry_den(self._matrix._matrix, 0, c),
|
229
|
+
qv.denom)
|
230
|
+
fmpq_canonicalise(fmpq_mat_entry(self._matrix._matrix, 0, c))
|
231
|
+
|
232
|
+
# mat[1,c] = (2 x.b) / x.denom
|
233
|
+
fmpz_set_mpz(fmpq_mat_entry_num(self._matrix._matrix, 1, c),
|
234
|
+
qv.b)
|
235
|
+
fmpz_mul_si(fmpq_mat_entry_num(self._matrix._matrix, 1, c),
|
236
|
+
fmpq_mat_entry_num(self._matrix._matrix, 1, c), 2)
|
237
|
+
fmpz_set_mpz(fmpq_mat_entry_den(self._matrix._matrix, 1, c),
|
238
|
+
qv.denom)
|
239
|
+
fmpq_canonicalise(fmpq_mat_entry(self._matrix._matrix, 1, c))
|
240
|
+
else: # self._n is 6
|
241
|
+
fmpz_set_mpz(fmpq_mat_entry_num(self._matrix._matrix, 0, c),
|
242
|
+
qv.a)
|
243
|
+
|
244
|
+
# NOTE: it would be convenient here to have fmpz_add_mpz
|
245
|
+
fmpz_init(ftmp)
|
246
|
+
fmpz_set_mpz(ftmp, qv.b)
|
247
|
+
fmpz_sub(fmpq_mat_entry_num(self._matrix._matrix, 0, c),
|
248
|
+
fmpq_mat_entry_num(self._matrix._matrix, 0, c),
|
249
|
+
ftmp)
|
250
|
+
fmpz_clear(ftmp)
|
251
|
+
|
252
|
+
fmpz_set_mpz(fmpq_mat_entry_den(self._matrix._matrix, 0, c),
|
253
|
+
qv.denom)
|
254
|
+
fmpq_canonicalise(fmpq_mat_entry(self._matrix._matrix, 0, c))
|
255
|
+
|
256
|
+
fmpz_set_mpz(fmpq_mat_entry_num(self._matrix._matrix, 1, c),
|
257
|
+
qv.b)
|
258
|
+
fmpz_mul_si(fmpq_mat_entry_num(self._matrix._matrix, 1, c),
|
259
|
+
fmpq_mat_entry_num(self._matrix._matrix, 1, c), 2)
|
260
|
+
fmpz_set_mpz(fmpq_mat_entry_den(self._matrix._matrix, 1, c),
|
261
|
+
qv.denom)
|
262
|
+
fmpq_canonicalise(fmpq_mat_entry(self._matrix._matrix, 1, c))
|
263
|
+
return
|
264
|
+
|
265
|
+
v = value
|
266
|
+
|
267
|
+
mpz_init(numer)
|
268
|
+
mpz_init(denom)
|
269
|
+
|
270
|
+
v._ntl_denom_as_mpz(denom)
|
271
|
+
for k in range(self._degree):
|
272
|
+
v._ntl_coeff_as_mpz(numer, k)
|
273
|
+
fmpz_set_mpz(fmpq_mat_entry_num(self._matrix._matrix, k, c), numer)
|
274
|
+
fmpz_set_mpz(fmpq_mat_entry_den(self._matrix._matrix, k, c), denom)
|
275
|
+
fmpq_canonicalise(fmpq_mat_entry(self._matrix._matrix, k, c))
|
276
|
+
|
277
|
+
mpz_clear(numer)
|
278
|
+
mpz_clear(denom)
|
279
|
+
|
280
|
+
cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j):
|
281
|
+
"""
|
282
|
+
Get the ij-th of ``self``.
|
283
|
+
|
284
|
+
WARNING: As the name suggests, expect segfaults if i,j are out
|
285
|
+
of bounds!! This is for internal use only.
|
286
|
+
|
287
|
+
EXAMPLES::
|
288
|
+
|
289
|
+
sage: W.<a> = CyclotomicField(5)
|
290
|
+
sage: A = matrix(2, 3, [9939208341, 1/a, 1-a,a, -2/3*a, a^19])
|
291
|
+
|
292
|
+
This implicitly calls get_unsafe::
|
293
|
+
|
294
|
+
sage: A[0,0]
|
295
|
+
9939208341
|
296
|
+
|
297
|
+
TESTS:
|
298
|
+
|
299
|
+
Since separate code exists for each quadratic field, we need
|
300
|
+
doctests for each.::
|
301
|
+
|
302
|
+
sage: K.<z> = CyclotomicField(3) ; M = Matrix(K,2,range(4))
|
303
|
+
sage: M[1,1] = z+1 ; M[1,1]
|
304
|
+
z + 1
|
305
|
+
sage: (M[1,1] - M[0,1])**3
|
306
|
+
1
|
307
|
+
sage: K.<z> = CyclotomicField(4) ; M = Matrix(K,2,range(4))
|
308
|
+
sage: M[1,1] = z+1 ; M[1,1]
|
309
|
+
z + 1
|
310
|
+
sage: (M[1,1] - M[0,1])**4
|
311
|
+
1
|
312
|
+
sage: K.<z> = CyclotomicField(6) ; M = Matrix(K,2,range(4))
|
313
|
+
sage: M[1,1] = z+1 ; M[1,1]
|
314
|
+
z + 1
|
315
|
+
sage: (M[1,1] - M[0,1])**6
|
316
|
+
1
|
317
|
+
"""
|
318
|
+
cdef Py_ssize_t k, c
|
319
|
+
cdef NumberFieldElement x
|
320
|
+
cdef NumberFieldElement_quadratic xq
|
321
|
+
cdef mpz_t tmp
|
322
|
+
cdef fmpz_t denom, ftmp
|
323
|
+
cdef ZZ_c coeff
|
324
|
+
|
325
|
+
if self._matrix is None:
|
326
|
+
raise ValueError("matrix entries not yet initialized")
|
327
|
+
|
328
|
+
c = i * self._ncols + j
|
329
|
+
mpz_init(tmp)
|
330
|
+
|
331
|
+
if self._degree == 2:
|
332
|
+
fmpz_init(ftmp)
|
333
|
+
xq = self._base_ring(0)
|
334
|
+
if self._n == 4:
|
335
|
+
fmpz_mul(ftmp, fmpq_mat_entry_num(self._matrix._matrix, 0, c),
|
336
|
+
fmpq_mat_entry_den(self._matrix._matrix, 1, c))
|
337
|
+
fmpz_get_mpz(xq.a, ftmp)
|
338
|
+
fmpz_mul(ftmp, fmpq_mat_entry_num(self._matrix._matrix, 1, c),
|
339
|
+
fmpq_mat_entry_den(self._matrix._matrix, 0, c))
|
340
|
+
fmpz_get_mpz(xq.b, ftmp)
|
341
|
+
fmpz_mul(ftmp, fmpq_mat_entry_den(self._matrix._matrix, 0, c),
|
342
|
+
fmpq_mat_entry_den(self._matrix._matrix, 1, c))
|
343
|
+
fmpz_get_mpz(xq.denom, ftmp)
|
344
|
+
|
345
|
+
else: # n is 3 or 6
|
346
|
+
fmpz_mul(ftmp, fmpq_mat_entry_num(self._matrix._matrix, 0, c),
|
347
|
+
fmpq_mat_entry_den(self._matrix._matrix, 1, c))
|
348
|
+
fmpz_mul_si(ftmp, ftmp, 2)
|
349
|
+
fmpz_get_mpz(xq.a, ftmp)
|
350
|
+
fmpz_mul(ftmp, fmpq_mat_entry_den(self._matrix._matrix, 0, c),
|
351
|
+
fmpq_mat_entry_num(self._matrix._matrix, 1, c))
|
352
|
+
fmpz_get_mpz(tmp, ftmp)
|
353
|
+
if self._n == 3:
|
354
|
+
mpz_sub(xq.a, xq.a, tmp)
|
355
|
+
else: # n == 6
|
356
|
+
mpz_add(xq.a, xq.a, tmp)
|
357
|
+
|
358
|
+
fmpz_mul(ftmp, fmpq_mat_entry_den(self._matrix._matrix, 0, c),
|
359
|
+
fmpq_mat_entry_num(self._matrix._matrix, 1, c))
|
360
|
+
fmpz_get_mpz(xq.b, ftmp)
|
361
|
+
|
362
|
+
fmpz_mul(ftmp, fmpq_mat_entry_den(self._matrix._matrix, 0, c),
|
363
|
+
fmpq_mat_entry_den(self._matrix._matrix, 1, c))
|
364
|
+
fmpz_get_mpz(xq.denom, ftmp)
|
365
|
+
mpz_mul_si(xq.denom, xq.denom, 2)
|
366
|
+
|
367
|
+
xq._reduce_c_()
|
368
|
+
mpz_clear(tmp)
|
369
|
+
fmpz_clear(ftmp)
|
370
|
+
return xq
|
371
|
+
|
372
|
+
x = self._base_ring(0)
|
373
|
+
fmpz_init(denom)
|
374
|
+
fmpz_init(ftmp)
|
375
|
+
fmpz_one(denom)
|
376
|
+
|
377
|
+
# Get the least common multiple of the denominators in
|
378
|
+
# this column.
|
379
|
+
for k in range(self._degree):
|
380
|
+
fmpz_lcm(denom, denom, fmpq_mat_entry_den(self._matrix._matrix, k, c))
|
381
|
+
|
382
|
+
for k in range(self._degree):
|
383
|
+
# set each entry of x to a*denom/b where a/b is the
|
384
|
+
# k,c entry of _matrix.
|
385
|
+
fmpz_mul(ftmp, fmpq_mat_entry_num(self._matrix._matrix, k, c), denom)
|
386
|
+
fmpz_divexact(ftmp, ftmp, fmpq_mat_entry_den(self._matrix._matrix, k, c))
|
387
|
+
# Now set k-th entry of x's numerator to tmp
|
388
|
+
fmpz_get_mpz(tmp, ftmp)
|
389
|
+
mpz_to_ZZ(&coeff, tmp)
|
390
|
+
ZZX_SetCoeff(x._numerator, k, coeff)
|
391
|
+
|
392
|
+
# Set the denominator of x to denom.
|
393
|
+
fmpz_get_mpz(tmp, denom)
|
394
|
+
mpz_to_ZZ(&x._denominator, tmp)
|
395
|
+
fmpz_clear(denom)
|
396
|
+
mpz_clear(tmp)
|
397
|
+
fmpz_clear(ftmp)
|
398
|
+
|
399
|
+
return x
|
400
|
+
|
401
|
+
cdef bint get_is_zero_unsafe(self, Py_ssize_t i, Py_ssize_t j) except -1:
|
402
|
+
r"""
|
403
|
+
Return 1 if the entry ``(i, j)`` is zero, otherwise 0.
|
404
|
+
|
405
|
+
EXAMPLES::
|
406
|
+
|
407
|
+
sage: K.<z> = CyclotomicField(3)
|
408
|
+
sage: A = matrix(K, 4, 3, [0, -z, -2, -2*z + 2, 2*z, z, z, 1-z, 2+3*z, z, 1+z, 0])
|
409
|
+
sage: A.zero_pattern_matrix() # indirect doctest
|
410
|
+
[1 0 0]
|
411
|
+
[0 0 0]
|
412
|
+
[0 0 0]
|
413
|
+
[0 0 1]
|
414
|
+
"""
|
415
|
+
cdef int a
|
416
|
+
for a in range(self._degree):
|
417
|
+
if not self._matrix.get_is_zero_unsafe(a, j+i*self._ncols):
|
418
|
+
return False
|
419
|
+
return True
|
420
|
+
|
421
|
+
def _pickle(self):
|
422
|
+
"""
|
423
|
+
Used for pickling matrices. This function returns the
|
424
|
+
underlying data and pickle version.
|
425
|
+
|
426
|
+
OUTPUT:
|
427
|
+
|
428
|
+
- data; output of pickle
|
429
|
+
- version; integer
|
430
|
+
|
431
|
+
EXAMPLES::
|
432
|
+
|
433
|
+
sage: K.<z> = CyclotomicField(3)
|
434
|
+
sage: w = matrix(K, 3, 3, [0, -z, -2, -2*z + 2, 2*z, z, z, 1-z, 2+3*z])
|
435
|
+
sage: w._pickle()
|
436
|
+
(('0 0 -2 2 0 0 0 1 2 0 -1 0 -2 2 1 1 -1 3', 0), 0)
|
437
|
+
"""
|
438
|
+
data = self._matrix._pickle()
|
439
|
+
return data, 0
|
440
|
+
|
441
|
+
def _unpickle(self, data, int version):
|
442
|
+
"""
|
443
|
+
Called when unpickling matrices.
|
444
|
+
|
445
|
+
INPUT:
|
446
|
+
|
447
|
+
- ``data`` -- string
|
448
|
+
- ``version`` -- integer
|
449
|
+
|
450
|
+
This modifies ``self``.
|
451
|
+
|
452
|
+
EXAMPLES::
|
453
|
+
|
454
|
+
sage: K.<z> = CyclotomicField(3)
|
455
|
+
sage: w = matrix(K, 3, 3, [0, -z, -2, -2*z + 2, 2*z, z, z, 1-z, 2+3*z])
|
456
|
+
sage: data, version = w._pickle()
|
457
|
+
sage: k = w.parent()(0)
|
458
|
+
sage: k._unpickle(data, version)
|
459
|
+
sage: k == w
|
460
|
+
True
|
461
|
+
"""
|
462
|
+
#self.check_mutability()
|
463
|
+
if version == 0:
|
464
|
+
self._matrix = Matrix_rational_dense(MatrixSpace(QQ, self._degree, self._nrows*self._ncols), None, False, False)
|
465
|
+
self._matrix._unpickle(*data) # data is (data, matrix_QQ_version)
|
466
|
+
else:
|
467
|
+
raise RuntimeError("unknown matrix version (=%s)" % version)
|
468
|
+
|
469
|
+
########################################################################
|
470
|
+
# LEVEL 2 functionality
|
471
|
+
# x * cdef _add_
|
472
|
+
# x * cdef _sub_
|
473
|
+
# * cdef _mul_
|
474
|
+
# x * cdef _lmul_ -- scalar multiplication
|
475
|
+
# x * cpdef _richcmp_
|
476
|
+
# x * __neg__
|
477
|
+
# * __invert__
|
478
|
+
# x * __copy__
|
479
|
+
# * _multiply_classical
|
480
|
+
# * _list -- list of underlying elements (need not be a copy)
|
481
|
+
# * _dict -- sparse dictionary of underlying elements (need not be a copy)
|
482
|
+
########################################################################
|
483
|
+
|
484
|
+
cpdef _add_(self, right):
|
485
|
+
"""
|
486
|
+
Return the sum of two dense cyclotomic matrices.
|
487
|
+
|
488
|
+
INPUT:
|
489
|
+
|
490
|
+
- ``self``, ``right`` -- dense cyclotomic matrices with the same
|
491
|
+
parents
|
492
|
+
|
493
|
+
OUTPUT: a dense cyclotomic matrix
|
494
|
+
|
495
|
+
EXAMPLES::
|
496
|
+
|
497
|
+
sage: W.<z> = CyclotomicField(5)
|
498
|
+
sage: A = matrix(2, 3, [1,z,z^2,z^3,z^4,2/3*z]); B = matrix(2, 3, [-1,2*z,3*z^2,5*z+1,z^4,1/3*z])
|
499
|
+
sage: A + B
|
500
|
+
[ 0 3*z 4*z^2]
|
501
|
+
[ z^3 + 5*z + 1 -2*z^3 - 2*z^2 - 2*z - 2 z]
|
502
|
+
|
503
|
+
Verify directly that the above output is correct::
|
504
|
+
|
505
|
+
sage: (A+B).list() == [A.list()[i]+B.list()[i] for i in range(6)]
|
506
|
+
True
|
507
|
+
"""
|
508
|
+
cdef Matrix_cyclo_dense A = Matrix_cyclo_dense.__new__(Matrix_cyclo_dense, self.parent(), None, None, None)
|
509
|
+
# Fix the redundancy here.
|
510
|
+
A._matrix = self._matrix + (<Matrix_cyclo_dense>right)._matrix
|
511
|
+
return A
|
512
|
+
|
513
|
+
cpdef _sub_(self, right):
|
514
|
+
"""
|
515
|
+
Return the difference of two dense cyclotomic matrices.
|
516
|
+
|
517
|
+
INPUT:
|
518
|
+
|
519
|
+
- ``self``, ``right`` -- dense cyclotomic matrices with the same
|
520
|
+
parent
|
521
|
+
|
522
|
+
OUTPUT: a dense cyclotomic matrix
|
523
|
+
|
524
|
+
EXAMPLES::
|
525
|
+
|
526
|
+
sage: W.<z> = CyclotomicField(5)
|
527
|
+
sage: A = matrix(2, 3, [1,z,z^2,z^3,z^4,2/3*z]); B = matrix(2, 3, [-1,2*z,3*z^2,5*z+1,z^4,1/3*z])
|
528
|
+
sage: A - B
|
529
|
+
[ 2 -z -2*z^2]
|
530
|
+
[z^3 - 5*z - 1 0 1/3*z]
|
531
|
+
|
532
|
+
Verify directly that the above output is correct::
|
533
|
+
|
534
|
+
sage: (A-B).list() == [A.list()[i]-B.list()[i] for i in range(6)]
|
535
|
+
True
|
536
|
+
"""
|
537
|
+
cdef Matrix_cyclo_dense A = Matrix_cyclo_dense.__new__(Matrix_cyclo_dense, self.parent(), None, None, None)
|
538
|
+
A._matrix = self._matrix - (<Matrix_cyclo_dense>right)._matrix
|
539
|
+
return A
|
540
|
+
|
541
|
+
cpdef _lmul_(self, Element right):
|
542
|
+
"""
|
543
|
+
Multiply a dense cyclotomic matrix by a scalar.
|
544
|
+
|
545
|
+
INPUT:
|
546
|
+
|
547
|
+
- ``right`` -- scalar in the base cyclotomic field
|
548
|
+
|
549
|
+
EXAMPLES::
|
550
|
+
|
551
|
+
sage: W.<z> = CyclotomicField(5)
|
552
|
+
sage: A = matrix(2, 3, [1,z,z^2,z^3,z^4,2/3*z])
|
553
|
+
sage: (1 + z/3)*A
|
554
|
+
[ 1/3*z + 1 1/3*z^2 + z 1/3*z^3 + z^2]
|
555
|
+
[2/3*z^3 - 1/3*z^2 - 1/3*z - 1/3 -z^3 - z^2 - z - 2/3 2/9*z^2 + 2/3*z]
|
556
|
+
|
557
|
+
Verify directly that the above output is correct::
|
558
|
+
|
559
|
+
sage: ((1+z/3)*A).list() == [(1+z/3)*x for x in A.list()]
|
560
|
+
True
|
561
|
+
"""
|
562
|
+
if right.is_one():
|
563
|
+
return self
|
564
|
+
elif right.is_zero():
|
565
|
+
return self.parent().zero()
|
566
|
+
|
567
|
+
# Create a new matrix object but with the _matrix attribute not initialized:
|
568
|
+
cdef Matrix_cyclo_dense A = Matrix_cyclo_dense.__new__(Matrix_cyclo_dense,
|
569
|
+
self.parent(), None, None, None)
|
570
|
+
|
571
|
+
if right.is_rational():
|
572
|
+
A._matrix = self._matrix._lmul_(right._rational_())
|
573
|
+
else:
|
574
|
+
# Multiply by nontrivial element of the cyclotomic number field
|
575
|
+
# We do this by finding the matrix of this element, then left
|
576
|
+
# multiplying by it. We have to tweak the matrix some to
|
577
|
+
# get the right basis, etc.
|
578
|
+
T = right.matrix().transpose()
|
579
|
+
A._matrix = T * self._matrix
|
580
|
+
return A
|
581
|
+
|
582
|
+
cdef _matrix_times_matrix_(self, baseMatrix right):
|
583
|
+
"""
|
584
|
+
Return the product of two cyclotomic dense matrices.
|
585
|
+
|
586
|
+
INPUT:
|
587
|
+
|
588
|
+
- ``self``, ``right`` -- cyclotomic dense matrices with compatible
|
589
|
+
parents (same base ring, and compatible dimensions for matrix
|
590
|
+
multiplication)
|
591
|
+
|
592
|
+
OUTPUT: cyclotomic dense matrix
|
593
|
+
|
594
|
+
ALGORITHM:
|
595
|
+
|
596
|
+
Use a multimodular algorithm that involves multiplying the two matrices
|
597
|
+
modulo split primes.
|
598
|
+
|
599
|
+
EXAMPLES::
|
600
|
+
|
601
|
+
sage: W.<z> = CyclotomicField(5)
|
602
|
+
sage: A = matrix(3, 3, [1,z,z^2,z^3,z^4,2/3*z,-3*z,z,2+z]); B = matrix(3, 3, [-1,2*z,3*z^2,5*z+1,z^4,1/3*z,2-z,3-z,5-z])
|
603
|
+
sage: A*B
|
604
|
+
[ -z^3 + 7*z^2 + z - 1 -z^3 + 3*z^2 + 2*z + 1 -z^3 + 25/3*z^2]
|
605
|
+
[-2*z^3 - 5/3*z^2 + 1/3*z + 4 -z^3 - 8/3*z^2 - 2 -2/3*z^2 + 10/3*z + 10/3]
|
606
|
+
[ 4*z^2 + 4*z + 4 -7*z^2 + z + 7 -9*z^3 - 2/3*z^2 + 3*z + 10]
|
607
|
+
|
608
|
+
Verify that the answer above is consistent with what the
|
609
|
+
generic sparse matrix multiply gives (which is a different
|
610
|
+
implementation).::
|
611
|
+
|
612
|
+
sage: A*B == A.sparse_matrix()*B.sparse_matrix()
|
613
|
+
True
|
614
|
+
|
615
|
+
sage: N1 = Matrix(CyclotomicField(6), 1, [1])
|
616
|
+
sage: cf6 = CyclotomicField(6) ; z6 = cf6.0
|
617
|
+
sage: N2 = Matrix(CyclotomicField(6), 1, 5, [0,1,z6,-z6,-z6+1])
|
618
|
+
sage: N1*N2
|
619
|
+
[ 0 1 zeta6 -zeta6 -zeta6 + 1]
|
620
|
+
sage: N1 = Matrix(CyclotomicField(6), 1, [-1])
|
621
|
+
sage: N1*N2
|
622
|
+
[ 0 -1 -zeta6 zeta6 zeta6 - 1]
|
623
|
+
|
624
|
+
Verify that a degenerate case bug reported at :issue:`5974` is fixed.
|
625
|
+
|
626
|
+
sage: K.<zeta6>=CyclotomicField(6); matrix(K,1,2) * matrix(K,2,[0, 1, 0, -2*zeta6, 0, 0, 1, -2*zeta6 + 1])
|
627
|
+
[0 0 0 0]
|
628
|
+
|
629
|
+
TESTS:
|
630
|
+
|
631
|
+
This is from :issue:`8666`::
|
632
|
+
|
633
|
+
sage: K.<zeta4> = CyclotomicField(4)
|
634
|
+
sage: m = matrix(K, [125])
|
635
|
+
sage: n = matrix(K, [186])
|
636
|
+
sage: m*n
|
637
|
+
[23250]
|
638
|
+
sage: (-m)*n
|
639
|
+
[-23250]
|
640
|
+
"""
|
641
|
+
try:
|
642
|
+
from .matrix_cyclo_linbox import _matrix_times_matrix_
|
643
|
+
except ImportError:
|
644
|
+
return Matrix._matrix_times_matrix_(self, right)
|
645
|
+
else:
|
646
|
+
return _matrix_times_matrix_(self, right)
|
647
|
+
|
648
|
+
cdef long _hash_(self) except -1:
|
649
|
+
"""
|
650
|
+
Return hash of an immutable matrix.
|
651
|
+
|
652
|
+
This raises a :exc:`TypeError` if input matrix is mutable.
|
653
|
+
|
654
|
+
EXAMPLES:
|
655
|
+
|
656
|
+
This is called implicitly by the hash function.::
|
657
|
+
|
658
|
+
sage: W.<z> = CyclotomicField(5)
|
659
|
+
sage: A = matrix(W, 2, 2, [1,z,-z,1+z/2])
|
660
|
+
|
661
|
+
The matrix must be immutable.::
|
662
|
+
|
663
|
+
sage: hash(A)
|
664
|
+
Traceback (most recent call last):
|
665
|
+
...
|
666
|
+
TypeError: mutable matrices are unhashable
|
667
|
+
sage: A.set_immutable()
|
668
|
+
|
669
|
+
Yes, this works::
|
670
|
+
|
671
|
+
sage: hash(A) # random
|
672
|
+
3107179158321342168
|
673
|
+
|
674
|
+
::
|
675
|
+
|
676
|
+
sage: W.<z> = CyclotomicField(5)
|
677
|
+
sage: A = matrix(W, 2, 2, [1,2/3*z+z^2,-z,1+z/2])
|
678
|
+
sage: hash(A)
|
679
|
+
Traceback (most recent call last):
|
680
|
+
...
|
681
|
+
TypeError: mutable matrices are unhashable
|
682
|
+
sage: A.set_immutable()
|
683
|
+
sage: A.__hash__() # random
|
684
|
+
2347601038649299176
|
685
|
+
"""
|
686
|
+
return hash(self._matrix)
|
687
|
+
|
688
|
+
cpdef _richcmp_(self, right, int op):
|
689
|
+
"""
|
690
|
+
Implement comparison of two cyclotomic matrices with
|
691
|
+
identical parents.
|
692
|
+
|
693
|
+
INPUT:
|
694
|
+
|
695
|
+
- ``self``, ``right`` -- matrices with same parent
|
696
|
+
|
697
|
+
OUTPUT: boolean
|
698
|
+
|
699
|
+
EXAMPLES::
|
700
|
+
|
701
|
+
sage: W.<z> = CyclotomicField(5)
|
702
|
+
sage: A = matrix(W, 2, 2, [1,z,-z,1+z/2])
|
703
|
+
|
704
|
+
These implicitly call richcmp::
|
705
|
+
|
706
|
+
sage: A == 5
|
707
|
+
False
|
708
|
+
sage: A < 100
|
709
|
+
True
|
710
|
+
|
711
|
+
This function is called implicitly when comparisons with matrices
|
712
|
+
are done::
|
713
|
+
|
714
|
+
sage: W.<z> = CyclotomicField(5)
|
715
|
+
sage: A = matrix(W, 2, 2, [1,2/3*z+z^2,-z,1+z/2])
|
716
|
+
sage: A == A
|
717
|
+
True
|
718
|
+
sage: A < 2*A
|
719
|
+
True
|
720
|
+
sage: A >= 2*A
|
721
|
+
False
|
722
|
+
"""
|
723
|
+
return self._matrix._richcmp_((<Matrix_cyclo_dense>right)._matrix, op)
|
724
|
+
|
725
|
+
def __copy__(self):
|
726
|
+
"""
|
727
|
+
Make a copy of this matrix.
|
728
|
+
|
729
|
+
EXAMPLES:
|
730
|
+
|
731
|
+
We create a cyclotomic matrix::
|
732
|
+
|
733
|
+
sage: W.<z> = CyclotomicField(5)
|
734
|
+
sage: A = matrix(W, 2, 2, [1,2/3*z+z^2,-z,1+z/2])
|
735
|
+
|
736
|
+
We make a copy of A::
|
737
|
+
|
738
|
+
sage: C = A.__copy__()
|
739
|
+
|
740
|
+
We make another reference to A::
|
741
|
+
|
742
|
+
sage: B = A
|
743
|
+
|
744
|
+
Changing this reference changes A itself::
|
745
|
+
|
746
|
+
sage: B[0,0] = 10
|
747
|
+
sage: A[0,0]
|
748
|
+
10
|
749
|
+
|
750
|
+
Changing the copy does not change A::
|
751
|
+
|
752
|
+
sage: C[0,0] = 20
|
753
|
+
sage: C[0,0]
|
754
|
+
20
|
755
|
+
sage: A[0,0]
|
756
|
+
10
|
757
|
+
"""
|
758
|
+
cdef Matrix_cyclo_dense A = Matrix_cyclo_dense.__new__(Matrix_cyclo_dense, self.parent(), None, None, None)
|
759
|
+
A._matrix = self._matrix.__copy__()
|
760
|
+
return A
|
761
|
+
|
762
|
+
def __neg__(self):
|
763
|
+
"""
|
764
|
+
Return the negative of this matrix.
|
765
|
+
|
766
|
+
OUTPUT: matrix
|
767
|
+
|
768
|
+
EXAMPLES::
|
769
|
+
|
770
|
+
sage: W.<z> = CyclotomicField(5)
|
771
|
+
sage: A = matrix(W, 2, 2, [1,2/3*z+z^2,-z,1+z/2])
|
772
|
+
sage: -A
|
773
|
+
[ -1 -z^2 - 2/3*z]
|
774
|
+
[ z -1/2*z - 1]
|
775
|
+
sage: A.__neg__()
|
776
|
+
[ -1 -z^2 - 2/3*z]
|
777
|
+
[ z -1/2*z - 1]
|
778
|
+
"""
|
779
|
+
cdef Matrix_cyclo_dense A = Matrix_cyclo_dense.__new__(Matrix_cyclo_dense, self.parent(), None, None, None)
|
780
|
+
A._matrix = -self._matrix
|
781
|
+
return A
|
782
|
+
|
783
|
+
########################################################################
|
784
|
+
# LEVEL 3 functionality (Optional)
|
785
|
+
# * __deepcopy__
|
786
|
+
# * __invert__
|
787
|
+
# * Matrix windows -- only if you need strassen for that base
|
788
|
+
# * Other functions (list them here):
|
789
|
+
# * Specialized echelon form
|
790
|
+
# * tensor product
|
791
|
+
########################################################################
|
792
|
+
|
793
|
+
def set_immutable(self):
|
794
|
+
"""
|
795
|
+
Change this matrix so that it is immutable.
|
796
|
+
|
797
|
+
EXAMPLES::
|
798
|
+
|
799
|
+
sage: W.<z> = CyclotomicField(5)
|
800
|
+
sage: A = matrix(W, 2, 2, [1,2/3*z+z^2,-z,1+z/2])
|
801
|
+
sage: A[0,0] = 10
|
802
|
+
sage: A.set_immutable()
|
803
|
+
sage: A[0,0] = 20
|
804
|
+
Traceback (most recent call last):
|
805
|
+
...
|
806
|
+
ValueError: matrix is immutable; please change a copy instead (i.e., use copy(M) to change a copy of M).
|
807
|
+
|
808
|
+
Note that there is no function to set a matrix to be mutable
|
809
|
+
again, since such a function would violate the whole point.
|
810
|
+
Instead make a copy, which is always mutable by default.::
|
811
|
+
|
812
|
+
sage: A.set_mutable()
|
813
|
+
Traceback (most recent call last):
|
814
|
+
...
|
815
|
+
AttributeError: 'sage.matrix.matrix_cyclo_dense.Matrix_cyclo_dense' object has no attribute 'set_mutable'...
|
816
|
+
sage: B = A.__copy__()
|
817
|
+
sage: B[0,0] = 20
|
818
|
+
sage: B[0,0]
|
819
|
+
20
|
820
|
+
"""
|
821
|
+
self._matrix.set_immutable()
|
822
|
+
matrix_dense.Matrix_dense.set_immutable(self)
|
823
|
+
|
824
|
+
def _rational_matrix(self):
|
825
|
+
"""
|
826
|
+
Return the underlying rational matrix corresponding to ``self``.
|
827
|
+
|
828
|
+
EXAMPLES::
|
829
|
+
|
830
|
+
sage: Matrix(CyclotomicField(7),4,4,range(16))._rational_matrix()
|
831
|
+
[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]
|
832
|
+
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
|
833
|
+
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
|
834
|
+
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
|
835
|
+
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
|
836
|
+
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
|
837
|
+
sage: Matrix(CyclotomicField(7),4,4,[CyclotomicField(7).gen(0)**i for i in range(16)])._rational_matrix()
|
838
|
+
[ 1 0 0 0 0 0 -1 1 0 0 0 0 0 -1 1 0]
|
839
|
+
[ 0 1 0 0 0 0 -1 0 1 0 0 0 0 -1 0 1]
|
840
|
+
[ 0 0 1 0 0 0 -1 0 0 1 0 0 0 -1 0 0]
|
841
|
+
[ 0 0 0 1 0 0 -1 0 0 0 1 0 0 -1 0 0]
|
842
|
+
[ 0 0 0 0 1 0 -1 0 0 0 0 1 0 -1 0 0]
|
843
|
+
[ 0 0 0 0 0 1 -1 0 0 0 0 0 1 -1 0 0]
|
844
|
+
"""
|
845
|
+
return self._matrix
|
846
|
+
|
847
|
+
def denominator(self):
|
848
|
+
"""
|
849
|
+
Return the denominator of the entries of this matrix.
|
850
|
+
|
851
|
+
OUTPUT: integer; the smallest integer `d` so that ``d * self`` has
|
852
|
+
entries in the ring of integers
|
853
|
+
|
854
|
+
EXAMPLES::
|
855
|
+
|
856
|
+
sage: W.<z> = CyclotomicField(5)
|
857
|
+
sage: A = matrix(W, 2, 2, [-2/7,2/3*z+z^2,-z,1+z/19]); A
|
858
|
+
[ -2/7 z^2 + 2/3*z]
|
859
|
+
[ -z 1/19*z + 1]
|
860
|
+
sage: d = A.denominator(); d
|
861
|
+
399
|
862
|
+
"""
|
863
|
+
return self._matrix.denominator()
|
864
|
+
|
865
|
+
def coefficient_bound(self):
|
866
|
+
r"""
|
867
|
+
Return an upper bound for the (complex) absolute values of all
|
868
|
+
entries of ``self`` with respect to all embeddings.
|
869
|
+
|
870
|
+
Use ``self.height()`` for a sharper bound.
|
871
|
+
|
872
|
+
This is computed using just the Cauchy-Schwarz inequality, i.e.,
|
873
|
+
we use the fact that ::
|
874
|
+
|
875
|
+
\left| \sum_i a_i\zeta^i \right| \leq \sum_i |a_i|,
|
876
|
+
|
877
|
+
as `|\zeta| = 1`.
|
878
|
+
|
879
|
+
EXAMPLES::
|
880
|
+
|
881
|
+
sage: W.<z> = CyclotomicField(5)
|
882
|
+
sage: A = matrix(W, 2, 2, [1+z, 0, 9*z+7, -3 + 4*z]); A
|
883
|
+
[ z + 1 0]
|
884
|
+
[9*z + 7 4*z - 3]
|
885
|
+
sage: A.coefficient_bound()
|
886
|
+
16
|
887
|
+
|
888
|
+
The above bound is just `9 + 7`, coming from the lower left entry.
|
889
|
+
A better bound would be the following::
|
890
|
+
|
891
|
+
sage: (A[1,0]).abs()
|
892
|
+
12.997543663...
|
893
|
+
"""
|
894
|
+
cdef Py_ssize_t i, j
|
895
|
+
|
896
|
+
bound = 0
|
897
|
+
for i from 0 <= i < self._matrix._ncols:
|
898
|
+
|
899
|
+
n = 0
|
900
|
+
for j from 0 <= j < self._matrix._nrows:
|
901
|
+
n += self._matrix[j, i].abs()
|
902
|
+
if bound < n:
|
903
|
+
bound = n
|
904
|
+
|
905
|
+
return bound
|
906
|
+
|
907
|
+
def height(self):
|
908
|
+
r"""
|
909
|
+
Return the height of ``self``.
|
910
|
+
|
911
|
+
If we let `a_{ij}` be the `i,j` entry of self, then we define
|
912
|
+
the height of ``self`` to be
|
913
|
+
|
914
|
+
`\max_v \max_{i,j} |a_{ij}|_v`,
|
915
|
+
|
916
|
+
where `v` runs over all complex embeddings of ``self.base_ring()``.
|
917
|
+
|
918
|
+
EXAMPLES::
|
919
|
+
|
920
|
+
sage: W.<z> = CyclotomicField(5)
|
921
|
+
sage: A = matrix(W, 2, 2, [1+z, 0, 9*z+7, -3 + 4*z]); A
|
922
|
+
[ z + 1 0]
|
923
|
+
[9*z + 7 4*z - 3]
|
924
|
+
sage: A.height()
|
925
|
+
12.997543663...
|
926
|
+
sage: (A[1,0]).abs()
|
927
|
+
12.997543663...
|
928
|
+
"""
|
929
|
+
cdef Py_ssize_t i, j
|
930
|
+
|
931
|
+
emb = self._base_ring.complex_embeddings()
|
932
|
+
|
933
|
+
ht = 0
|
934
|
+
for i from 0 <= i < self._nrows:
|
935
|
+
for j from 0 <= j < self._ncols:
|
936
|
+
t = max([ x.norm().sqrt() for x in [ f(self.get_unsafe(i,j)) for f in emb ] ])
|
937
|
+
if t > ht:
|
938
|
+
ht = t
|
939
|
+
|
940
|
+
return ht
|
941
|
+
|
942
|
+
cdef _randomize_rational_column_unsafe(Matrix_cyclo_dense self,
|
943
|
+
Py_ssize_t col, mpz_t nump1, mpz_t denp1, distribution=None):
|
944
|
+
"""
|
945
|
+
Randomizes all entries in column ``col``. This is a helper method
|
946
|
+
used in the implementation of dense matrices over cyclotomic fields.
|
947
|
+
|
948
|
+
INPUT:
|
949
|
+
|
950
|
+
- ``col`` -- integer indicating the column; must be coercible to
|
951
|
+
``int``, and this must lie between 0 (inclusive) and
|
952
|
+
``self._ncols`` (exclusive), since no bounds-checking is performed
|
953
|
+
- ``nump1`` -- integer; numerator bound plus one
|
954
|
+
- ``denp1`` -- integer; denominator bound plus one
|
955
|
+
- ``distribution`` -- ``None`` or '1/n' (default: ``None``); if '1/n'
|
956
|
+
then ``num_bound``, ``den_bound`` are ignored and numbers are chosen
|
957
|
+
using the GMP function ``mpq_randomize_entry_recip_uniform``
|
958
|
+
- ``nonzero`` -- boolean (default: ``False``); whether the new entries
|
959
|
+
are forced to be nonzero
|
960
|
+
|
961
|
+
OUTPUT: none, the matrix is modified in-space
|
962
|
+
|
963
|
+
WARNING:
|
964
|
+
|
965
|
+
This method is quite unsafe. It's called from the method
|
966
|
+
``randomize``, but probably shouldn't be called from another method
|
967
|
+
without first carefully reading the source code!
|
968
|
+
|
969
|
+
TESTS:
|
970
|
+
|
971
|
+
The following doctests are all indirect::
|
972
|
+
|
973
|
+
sage: MS = MatrixSpace(CyclotomicField(10), 4, 4)
|
974
|
+
sage: A = MS.random_element(); A # random
|
975
|
+
[ -2*zeta10^3 + 2*zeta10^2 - zeta10 zeta10^3 + 2*zeta10^2 - zeta10 + 1 0 -2*zeta10^3 + zeta10^2 - 2*zeta10 + 2]
|
976
|
+
[ 0 -zeta10^3 + 2*zeta10^2 - zeta10 -zeta10^3 + 1 zeta10^3 + zeta10]
|
977
|
+
[ 1/2*zeta10^2 -2*zeta10^2 + 2 -1/2*zeta10^3 + 1/2*zeta10^2 + 2 2*zeta10^3 - zeta10^2 - 2]
|
978
|
+
[ 1 zeta10^2 + 2 2*zeta10^2 2*zeta10 - 2]
|
979
|
+
sage: A.parent() is MS
|
980
|
+
True
|
981
|
+
|
982
|
+
::
|
983
|
+
|
984
|
+
sage: B = MS.random_element(density=0.5)
|
985
|
+
sage: all(a in (-2, -1, -1/2, 0, 1/2, 1, 2) for a in B._rational_matrix().list())
|
986
|
+
True
|
987
|
+
sage: while set(B._rational_matrix().list()) != set((-2, -1, -1/2, 0, 1/2, 1, 2)):
|
988
|
+
....: B = MS.random_element(density=0.5)
|
989
|
+
|
990
|
+
::
|
991
|
+
|
992
|
+
sage: C = MS.random_element(density=0.5, num_bound=20, den_bound=20)
|
993
|
+
sage: all(abs(a.denominator()) <= 20 and abs(a.numerator()) <= 20 for a in C._rational_matrix().list())
|
994
|
+
True
|
995
|
+
sage: while not (any(abs(a.denominator()) == 20 for a in C._rational_matrix().list())
|
996
|
+
....: and any(abs(a.numerator()) == 20 for a in C._rational_matrix().list())):
|
997
|
+
....: C = MS.random_element(density=0.5, num_bound=20, den_bound=20)
|
998
|
+
"""
|
999
|
+
cdef Py_ssize_t i
|
1000
|
+
cdef Matrix_rational_dense mat = self._matrix
|
1001
|
+
cdef mpq_t tmp
|
1002
|
+
|
1003
|
+
sig_on()
|
1004
|
+
mpq_init(tmp)
|
1005
|
+
if distribution == "1/n":
|
1006
|
+
for i in range(mat._nrows):
|
1007
|
+
mpq_randomize_entry_recip_uniform(tmp)
|
1008
|
+
fmpq_set_mpq(fmpq_mat_entry(mat._matrix, i, col), tmp)
|
1009
|
+
elif mpz_cmp_si(denp1, 2): # denom is > 1
|
1010
|
+
for i in range(mat._nrows):
|
1011
|
+
mpq_randomize_entry(tmp, nump1, denp1)
|
1012
|
+
fmpq_set_mpq(fmpq_mat_entry(mat._matrix, i, col), tmp)
|
1013
|
+
else:
|
1014
|
+
for i in range(mat._nrows):
|
1015
|
+
mpq_randomize_entry_as_int(tmp, nump1)
|
1016
|
+
fmpq_set_mpq(fmpq_mat_entry(mat._matrix, i, col), tmp)
|
1017
|
+
mpq_clear(tmp)
|
1018
|
+
sig_off()
|
1019
|
+
|
1020
|
+
def randomize(self, density=1, num_bound=2, den_bound=2,
|
1021
|
+
distribution=None, nonzero=False, *args, **kwds):
|
1022
|
+
r"""
|
1023
|
+
Randomize the entries of ``self``.
|
1024
|
+
|
1025
|
+
Choose rational numbers according to ``distribution``, whose
|
1026
|
+
numerators are bounded by ``num_bound`` and whose denominators are
|
1027
|
+
bounded by ``den_bound``.
|
1028
|
+
|
1029
|
+
EXAMPLES::
|
1030
|
+
|
1031
|
+
sage: A = Matrix(CyclotomicField(5),2,2,range(4)) ; A
|
1032
|
+
[0 1]
|
1033
|
+
[2 3]
|
1034
|
+
sage: A.randomize()
|
1035
|
+
sage: A # random output
|
1036
|
+
[ 1/2*zeta5^2 + zeta5 1/2]
|
1037
|
+
[ -zeta5^2 + 2*zeta5 -2*zeta5^3 + 2*zeta5^2 + 2]
|
1038
|
+
"""
|
1039
|
+
# Problem 1:
|
1040
|
+
# We cannot simply call the ``randomize`` code in ``matrix2.pyx`` on
|
1041
|
+
# the underlying matrix, since this is a d x (mn) matrix, where d is
|
1042
|
+
# the degree of the field extension, which leads to an overly dense
|
1043
|
+
# matrix.
|
1044
|
+
#
|
1045
|
+
# Problem 2:
|
1046
|
+
# We cannot simply copy the code from ``matrix2.pyx``, since the
|
1047
|
+
# ``random_element`` method for cyclotomic fields does not support
|
1048
|
+
# the arguments ``num_bound`` and ``den_bound``, which are support by
|
1049
|
+
# the rational field.
|
1050
|
+
#
|
1051
|
+
# Proposed solution:
|
1052
|
+
# Randomly select a proportion of ``density`` of the elements in the
|
1053
|
+
# matrix over the cyclotomic field, that is, this many columns in the
|
1054
|
+
# underlying rational matrix. Then, for each element in that column,
|
1055
|
+
# randomize it to a rational number, applying the arguments
|
1056
|
+
# ``num_bound`` and ``den_bound``.
|
1057
|
+
|
1058
|
+
density = float(density)
|
1059
|
+
if density <= 0:
|
1060
|
+
return
|
1061
|
+
if density > 1:
|
1062
|
+
density = 1
|
1063
|
+
|
1064
|
+
self.check_mutability()
|
1065
|
+
self.clear_cache()
|
1066
|
+
|
1067
|
+
cdef Py_ssize_t col, i, k, num
|
1068
|
+
cdef randstate rstate = current_randstate()
|
1069
|
+
cdef Integer B, C
|
1070
|
+
cdef bint col_is_zero
|
1071
|
+
|
1072
|
+
B = Integer(num_bound+1)
|
1073
|
+
C = Integer(den_bound+1)
|
1074
|
+
|
1075
|
+
if nonzero:
|
1076
|
+
if density >= 1:
|
1077
|
+
for col in range(self._matrix._ncols):
|
1078
|
+
col_is_zero = True
|
1079
|
+
while col_is_zero:
|
1080
|
+
self._randomize_rational_column_unsafe(col, B.value,
|
1081
|
+
C.value, distribution)
|
1082
|
+
# Check whether the new column is nonzero
|
1083
|
+
for i in range(self._degree):
|
1084
|
+
if not fmpq_is_zero(fmpq_mat_entry(self._matrix._matrix, i, col)):
|
1085
|
+
col_is_zero = False
|
1086
|
+
break
|
1087
|
+
else:
|
1088
|
+
num = int(self._nrows * self._ncols * density)
|
1089
|
+
for k in range(num):
|
1090
|
+
col = rstate.c_random() % self._matrix._ncols
|
1091
|
+
col_is_zero = True
|
1092
|
+
while col_is_zero:
|
1093
|
+
self._randomize_rational_column_unsafe(col, B.value,
|
1094
|
+
C.value, distribution)
|
1095
|
+
# Check whether the new column is nonzero
|
1096
|
+
for i in range(self._degree):
|
1097
|
+
if not fmpq_is_zero(fmpq_mat_entry(self._matrix._matrix, i, col)):
|
1098
|
+
col_is_zero = False
|
1099
|
+
break
|
1100
|
+
else:
|
1101
|
+
if density >= 1:
|
1102
|
+
for col in range(self._matrix._ncols):
|
1103
|
+
self._randomize_rational_column_unsafe(col, B.value,
|
1104
|
+
C.value, distribution)
|
1105
|
+
else:
|
1106
|
+
num = int(self._nrows * self._ncols * density)
|
1107
|
+
for k in range(num):
|
1108
|
+
col = rstate.c_random() % self._matrix._ncols
|
1109
|
+
self._randomize_rational_column_unsafe(col, B.value,
|
1110
|
+
C.value, distribution)
|
1111
|
+
|
1112
|
+
def _charpoly_bound(self):
|
1113
|
+
"""
|
1114
|
+
Determine a bound for the coefficients of the characteristic
|
1115
|
+
polynomial of ``self``.
|
1116
|
+
|
1117
|
+
We use the bound in Lemma 2.2 of:
|
1118
|
+
|
1119
|
+
Dumas, J-G. "Bounds on the coefficients of characteristic
|
1120
|
+
and minimal polynomials." J. Inequal. Pure Appl. Math. 8
|
1121
|
+
(2007), no. 2.
|
1122
|
+
|
1123
|
+
This bound only applies for ``self._nrows >= 4``, so in all
|
1124
|
+
smaller cases, we just use a naive bound.
|
1125
|
+
|
1126
|
+
EXAMPLES::
|
1127
|
+
|
1128
|
+
sage: A = Matrix(CyclotomicField(7),3,3,range(9))
|
1129
|
+
sage: A._charpoly_bound()
|
1130
|
+
2048
|
1131
|
+
sage: A.charpoly()
|
1132
|
+
x^3 - 12*x^2 - 18*x
|
1133
|
+
|
1134
|
+
An example from the above paper, where the bound is sharp::
|
1135
|
+
|
1136
|
+
sage: B = Matrix(CyclotomicField(7), 5,5, [1,1,1,1,1,1,1,-1,-1,-1,1,-1,1,-1,-1,1,-1,-1,1,-1,1,-1,-1,-1,1])
|
1137
|
+
sage: B._charpoly_bound()
|
1138
|
+
80
|
1139
|
+
sage: B.charpoly()
|
1140
|
+
x^5 - 5*x^4 + 40*x^2 - 80*x + 48
|
1141
|
+
"""
|
1142
|
+
cdef Py_ssize_t i
|
1143
|
+
|
1144
|
+
# should we even bother with this check, or just say in
|
1145
|
+
# the docstring that we assume it's square?
|
1146
|
+
if self._nrows != self._ncols:
|
1147
|
+
raise ArithmeticError("self must be a square matrix")
|
1148
|
+
|
1149
|
+
if self.is_zero():
|
1150
|
+
return 1
|
1151
|
+
|
1152
|
+
B = self.coefficient_bound()
|
1153
|
+
|
1154
|
+
# TODO: should charpoly just hardcode the return value for
|
1155
|
+
# self.nrows() < 4?
|
1156
|
+
|
1157
|
+
# this bound is only valid for n >= 4, use naive bounds
|
1158
|
+
# in other cases.
|
1159
|
+
if self._nrows <= 3:
|
1160
|
+
return max(1, 3*B, 6*B**2, 4*B**3)
|
1161
|
+
|
1162
|
+
# This is 2*e^(1-(2(7\gamma-4))/(13(3-2\gamma))), where \gamma
|
1163
|
+
# is Euler's constant.
|
1164
|
+
delta = RealNumber('5.418236')
|
1165
|
+
# This is an approximation to 1/2. :)
|
1166
|
+
half = RealNumber('0.5')
|
1167
|
+
|
1168
|
+
D = (((1+2*delta*self._nrows*(B**2)).sqrt()-1)/(delta*B**2)).ceil()
|
1169
|
+
|
1170
|
+
# TODO: we don't check anything about overflows anywhere here;
|
1171
|
+
# should we?
|
1172
|
+
|
1173
|
+
# i = 0 case
|
1174
|
+
M = ((self._nrows * B**2)**(self._nrows * half)).ceil()
|
1175
|
+
|
1176
|
+
for i from 1 <= i < D:
|
1177
|
+
val = binomial(self._nrows, i) * \
|
1178
|
+
(((self._nrows-i)*B**2)**((self._nrows-i)*half)).ceil()
|
1179
|
+
if val > M:
|
1180
|
+
M = val
|
1181
|
+
|
1182
|
+
return M
|
1183
|
+
|
1184
|
+
def charpoly(self, var='x', algorithm='multimodular', proof=None):
|
1185
|
+
r"""
|
1186
|
+
Return the characteristic polynomial of self, as a polynomial
|
1187
|
+
over the base ring.
|
1188
|
+
|
1189
|
+
INPUT:
|
1190
|
+
|
1191
|
+
- ``algorithm`` -- options:
|
1192
|
+
|
1193
|
+
- ``'multimodular'`` (default): reduce modulo primes, compute
|
1194
|
+
charpoly mod p, and lift (very fast)
|
1195
|
+
- ``'pari'``: use pari (quite slow; comparable to Magma v2.14 though)
|
1196
|
+
- ``'hessenberg'``: put matrix in Hessenberg form (double dog slow)
|
1197
|
+
|
1198
|
+
- ``proof`` -- boolean (default: ``None``); proof flag determined by
|
1199
|
+
global linalg proof
|
1200
|
+
|
1201
|
+
OUTPUT: polynomial
|
1202
|
+
|
1203
|
+
EXAMPLES::
|
1204
|
+
|
1205
|
+
sage: K.<z> = CyclotomicField(5)
|
1206
|
+
sage: a = matrix(K, 3, [1,z,1+z^2, z/3,1,2,3,z^2,1-z])
|
1207
|
+
sage: f = a.charpoly(); f
|
1208
|
+
x^3 + (z - 3)*x^2 + (-16/3*z^2 - 2*z)*x - 2/3*z^3 + 16/3*z^2 - 5*z + 5/3
|
1209
|
+
sage: f(a)
|
1210
|
+
[0 0 0]
|
1211
|
+
[0 0 0]
|
1212
|
+
[0 0 0]
|
1213
|
+
sage: a.charpoly(algorithm='pari')
|
1214
|
+
x^3 + (z - 3)*x^2 + (-16/3*z^2 - 2*z)*x - 2/3*z^3 + 16/3*z^2 - 5*z + 5/3
|
1215
|
+
sage: a.charpoly(algorithm='hessenberg')
|
1216
|
+
x^3 + (z - 3)*x^2 + (-16/3*z^2 - 2*z)*x - 2/3*z^3 + 16/3*z^2 - 5*z + 5/3
|
1217
|
+
|
1218
|
+
sage: Matrix(K, 1, [0]).charpoly()
|
1219
|
+
x
|
1220
|
+
sage: Matrix(K, 1, [5]).charpoly(var='y')
|
1221
|
+
y - 5
|
1222
|
+
|
1223
|
+
sage: Matrix(CyclotomicField(13),3).charpoly()
|
1224
|
+
x^3
|
1225
|
+
sage: Matrix(CyclotomicField(13),3).charpoly()[2].parent()
|
1226
|
+
Cyclotomic Field of order 13 and degree 12
|
1227
|
+
|
1228
|
+
TESTS::
|
1229
|
+
|
1230
|
+
sage: Matrix(CyclotomicField(10),0).charpoly()
|
1231
|
+
1
|
1232
|
+
"""
|
1233
|
+
key = 'charpoly-%s-%s' % (algorithm, proof)
|
1234
|
+
f = self.fetch(key)
|
1235
|
+
if f is not None:
|
1236
|
+
return f.change_variable_name(var)
|
1237
|
+
|
1238
|
+
if self.nrows() != self.ncols():
|
1239
|
+
raise TypeError("self must be square")
|
1240
|
+
|
1241
|
+
if self.is_zero():
|
1242
|
+
R = PolynomialRing(self.base_ring(), name=var)
|
1243
|
+
f = R.gen(0)**self.nrows()
|
1244
|
+
self.cache(key, f)
|
1245
|
+
return f
|
1246
|
+
|
1247
|
+
if self.nrows() == 1:
|
1248
|
+
R = PolynomialRing(self.base_ring(), name=var)
|
1249
|
+
f = R.gen(0) - self[0,0]
|
1250
|
+
self.cache(key, f)
|
1251
|
+
return f
|
1252
|
+
|
1253
|
+
if algorithm == 'multimodular':
|
1254
|
+
f = self._charpoly_multimodular(var, proof=proof)
|
1255
|
+
elif algorithm == 'pari':
|
1256
|
+
paripoly = self.__pari__().charpoly()
|
1257
|
+
f = self.base_ring()[var](paripoly)
|
1258
|
+
elif algorithm == 'hessenberg':
|
1259
|
+
f = self._charpoly_hessenberg(var)
|
1260
|
+
else:
|
1261
|
+
raise ValueError("unknown algorithm '%s'" % algorithm)
|
1262
|
+
self.cache(key, f)
|
1263
|
+
return f
|
1264
|
+
|
1265
|
+
def _charpoly_mod(self, p):
|
1266
|
+
"""
|
1267
|
+
Return the characteristic polynomial of self*denom modulo all
|
1268
|
+
primes over `p`.
|
1269
|
+
|
1270
|
+
This is used internally by the multimodular charpoly algorithm.
|
1271
|
+
|
1272
|
+
INPUT:
|
1273
|
+
|
1274
|
+
- ``p`` -- a prime that splits completely
|
1275
|
+
|
1276
|
+
OUTPUT: matrix over GF(p) whose columns correspond to the entries
|
1277
|
+
of all the characteristic polynomials of the reduction of ``self``
|
1278
|
+
modulo all the primes over `p`
|
1279
|
+
|
1280
|
+
EXAMPLES::
|
1281
|
+
|
1282
|
+
sage: W.<z> = CyclotomicField(5)
|
1283
|
+
sage: A = matrix(W, 2, 2, [1+z, 0, 9*z+7, -3 + 4*z]); A
|
1284
|
+
[ z + 1 0]
|
1285
|
+
[9*z + 7 4*z - 3]
|
1286
|
+
sage: A._charpoly_mod(11)
|
1287
|
+
[8 2 1]
|
1288
|
+
[1 6 0]
|
1289
|
+
[4 0 0]
|
1290
|
+
[0 0 0]
|
1291
|
+
"""
|
1292
|
+
tm = verbose("Computing characteristic polynomial of cyclotomic matrix modulo %s." % p)
|
1293
|
+
# Reduce self modulo all primes over p
|
1294
|
+
R, _ = self._reductions(p)
|
1295
|
+
# Compute the characteristic polynomial of each reduced matrix
|
1296
|
+
F = [A.charpoly('x') for A in R]
|
1297
|
+
# Put the characteristic polynomials together as the rows of a mod-p matrix
|
1298
|
+
k = R[0].base_ring()
|
1299
|
+
S = matrix(k, len(F), self.nrows() + 1, [f.list() for f in F])
|
1300
|
+
# multiply by inverse of reduction matrix to lift
|
1301
|
+
_, L = self._reduction_matrix(p)
|
1302
|
+
X = L * S
|
1303
|
+
# Now the columns of the matrix X define the entries of the
|
1304
|
+
# charpoly modulo p.
|
1305
|
+
verbose("Finished computing charpoly mod %s." % p, tm)
|
1306
|
+
return X
|
1307
|
+
|
1308
|
+
def _charpoly_multimodular(self, var='x', proof=None):
|
1309
|
+
"""
|
1310
|
+
Compute the characteristic polynomial of ``self`` using a
|
1311
|
+
multimodular algorithm.
|
1312
|
+
|
1313
|
+
INPUT:
|
1314
|
+
|
1315
|
+
- ``proof`` -- boolean (default: global flag); if ``False``, compute
|
1316
|
+
using primes `p_i` until the lift modulo all primes up to `p_i` is
|
1317
|
+
the same as the lift modulo all primes up to `p_{i+3}` or the bound
|
1318
|
+
is reached
|
1319
|
+
|
1320
|
+
EXAMPLES::
|
1321
|
+
|
1322
|
+
sage: K.<z> = CyclotomicField(3)
|
1323
|
+
sage: A = matrix(3, [-z, 2*z + 1, 1/2*z + 2, 1, -1/2, 2*z + 2, -2*z - 2, -2*z - 2, 2*z - 1])
|
1324
|
+
sage: A._charpoly_multimodular()
|
1325
|
+
x^3 + (-z + 3/2)*x^2 + (17/2*z + 9/2)*x - 9/2*z - 23/2
|
1326
|
+
sage: A._charpoly_multimodular('T')
|
1327
|
+
T^3 + (-z + 3/2)*T^2 + (17/2*z + 9/2)*T - 9/2*z - 23/2
|
1328
|
+
sage: A._charpoly_multimodular('T', proof=False)
|
1329
|
+
T^3 + (-z + 3/2)*T^2 + (17/2*z + 9/2)*T - 9/2*z - 23/2
|
1330
|
+
|
1331
|
+
TESTS:
|
1332
|
+
|
1333
|
+
We test a degenerate case::
|
1334
|
+
|
1335
|
+
sage: A = matrix(CyclotomicField(1),2,[1,2,3,4]); A.charpoly()
|
1336
|
+
x^2 - 5*x - 2
|
1337
|
+
"""
|
1338
|
+
from .matrix_cyclo_linbox import _charpoly_multimodular
|
1339
|
+
return _charpoly_multimodular(self, var, proof)
|
1340
|
+
|
1341
|
+
def _reductions(self, p):
|
1342
|
+
"""
|
1343
|
+
Compute the reductions modulo all primes over p of denom*self,
|
1344
|
+
where denom is the denominator of ``self``.
|
1345
|
+
|
1346
|
+
INPUT:
|
1347
|
+
|
1348
|
+
- ``p`` -- a prime that splits completely in the base cyclotomic field
|
1349
|
+
|
1350
|
+
OUTPUT:
|
1351
|
+
|
1352
|
+
- ``list`` -- of r distinct matrices modulo p, where r is
|
1353
|
+
the degree of the cyclotomic base field
|
1354
|
+
- ``denom`` -- integer
|
1355
|
+
|
1356
|
+
EXAMPLES::
|
1357
|
+
|
1358
|
+
sage: K.<z> = CyclotomicField(3)
|
1359
|
+
sage: w = matrix(K, 2, 3, [0, -z/5, -2/3, -2*z + 2, 2*z, z])
|
1360
|
+
sage: R, d = w._reductions(7)
|
1361
|
+
sage: R[0]
|
1362
|
+
[0 2 4]
|
1363
|
+
[1 1 4]
|
1364
|
+
sage: R[1]
|
1365
|
+
[0 1 4]
|
1366
|
+
[5 4 2]
|
1367
|
+
sage: d
|
1368
|
+
15
|
1369
|
+
"""
|
1370
|
+
# Get matrix that defines the linear reduction maps modulo
|
1371
|
+
# each prime of the base ring over p.
|
1372
|
+
T, _ = self._reduction_matrix(p)
|
1373
|
+
# Clear denominator and get matrix over the integers suitable
|
1374
|
+
# for reduction.
|
1375
|
+
A, denom = self._matrix._clear_denom()
|
1376
|
+
# Actually reduce the matrix over the integers modulo the
|
1377
|
+
# prime p.
|
1378
|
+
B = A._mod_int(p)
|
1379
|
+
# Now multiply, which computes from B all the reductions of
|
1380
|
+
# self*denom modulo each of the primes over p.
|
1381
|
+
R = T * B
|
1382
|
+
# Finally compute the actual reductions by extracting them
|
1383
|
+
# from R (note that the rows of R define the reductions).
|
1384
|
+
ans = R._matrices_from_rows(self._nrows, self._ncols)
|
1385
|
+
return ans, denom
|
1386
|
+
|
1387
|
+
def _reduction_matrix(self, p):
|
1388
|
+
"""
|
1389
|
+
INPUT:
|
1390
|
+
|
1391
|
+
- ``p`` -- a prime that splits completely in the base field
|
1392
|
+
|
1393
|
+
OUTPUT:
|
1394
|
+
|
1395
|
+
- Matrix over GF(p) whose action from the left gives the map from O_K
|
1396
|
+
to GF(p) x ... x GF(p) given by reducing modulo all the primes over p
|
1397
|
+
- inverse of this matrix
|
1398
|
+
|
1399
|
+
EXAMPLES::
|
1400
|
+
|
1401
|
+
sage: K.<z> = CyclotomicField(3)
|
1402
|
+
sage: w = matrix(K, 2, 3, [0, -z/5, -2/3, -2*z + 2, 2*z, z])
|
1403
|
+
sage: A, B = w._reduction_matrix(7)
|
1404
|
+
sage: A
|
1405
|
+
[1 4]
|
1406
|
+
[1 2]
|
1407
|
+
sage: B
|
1408
|
+
[6 2]
|
1409
|
+
[4 3]
|
1410
|
+
|
1411
|
+
The reduction matrix is used to calculate the reductions mod primes
|
1412
|
+
above p. ::
|
1413
|
+
|
1414
|
+
sage: K.<z> = CyclotomicField(5)
|
1415
|
+
sage: A = matrix(K, 2, 2, [1, z, z^2+1, 5*z^3]); A
|
1416
|
+
[ 1 z]
|
1417
|
+
[z^2 + 1 5*z^3]
|
1418
|
+
sage: T, S = A._reduction_matrix(11)
|
1419
|
+
sage: T * A._rational_matrix().change_ring(GF(11))
|
1420
|
+
[ 1 9 5 4]
|
1421
|
+
[ 1 5 4 9]
|
1422
|
+
[ 1 4 6 1]
|
1423
|
+
[ 1 3 10 3]
|
1424
|
+
|
1425
|
+
The rows of this product are the (flattened) matrices mod each prime above p::
|
1426
|
+
|
1427
|
+
sage: roots = [r for r, e in K.defining_polynomial().change_ring(GF(11)).roots()]; roots
|
1428
|
+
[9, 5, 4, 3]
|
1429
|
+
sage: [r^2+1 for r in roots]
|
1430
|
+
[5, 4, 6, 10]
|
1431
|
+
sage: [5*r^3 for r in roots]
|
1432
|
+
[4, 9, 1, 3]
|
1433
|
+
|
1434
|
+
The reduction matrix is cached::
|
1435
|
+
|
1436
|
+
sage: w._reduction_matrix(7) is w._reduction_matrix(7)
|
1437
|
+
True
|
1438
|
+
"""
|
1439
|
+
cache = self.fetch('reduction_matrices')
|
1440
|
+
if cache is None:
|
1441
|
+
cache = {}
|
1442
|
+
self.cache('reduction_matrices', cache)
|
1443
|
+
try:
|
1444
|
+
return cache[p]
|
1445
|
+
except KeyError:
|
1446
|
+
pass
|
1447
|
+
K = self.base_ring()
|
1448
|
+
phi = K.defining_polynomial()
|
1449
|
+
from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF
|
1450
|
+
from sage.matrix.constructor import matrix
|
1451
|
+
F = GF(p)
|
1452
|
+
aa = [a for a, _ in phi.change_ring(F).roots()]
|
1453
|
+
n = K.degree()
|
1454
|
+
if len(aa) != n:
|
1455
|
+
raise ValueError("the prime p (=%s) must split completely but doesn't" % p)
|
1456
|
+
T = matrix(F, n)
|
1457
|
+
for i in range(n):
|
1458
|
+
a = aa[i]
|
1459
|
+
b = 1
|
1460
|
+
for j in range(n):
|
1461
|
+
T[i,j] = b
|
1462
|
+
b *= a
|
1463
|
+
T.set_immutable()
|
1464
|
+
ans = (T, T**(-1))
|
1465
|
+
cache[p] = ans
|
1466
|
+
return ans
|
1467
|
+
|
1468
|
+
def echelon_form(self, algorithm=None, height_guess=None):
|
1469
|
+
"""
|
1470
|
+
Find the echelon form of ``self``, using the specified algorithm.
|
1471
|
+
|
1472
|
+
The result is cached for each algorithm separately.
|
1473
|
+
|
1474
|
+
EXAMPLES::
|
1475
|
+
|
1476
|
+
sage: W.<z> = CyclotomicField(3)
|
1477
|
+
sage: A = matrix(W, 2, 3, [1+z, 2/3, 9*z+7, -3 + 4*z, z, -7*z]); A
|
1478
|
+
[ z + 1 2/3 9*z + 7]
|
1479
|
+
[4*z - 3 z -7*z]
|
1480
|
+
sage: A.echelon_form()
|
1481
|
+
[ 1 0 -192/97*z - 361/97]
|
1482
|
+
[ 0 1 1851/97*z + 1272/97]
|
1483
|
+
sage: A.echelon_form(algorithm='classical')
|
1484
|
+
[ 1 0 -192/97*z - 361/97]
|
1485
|
+
[ 0 1 1851/97*z + 1272/97]
|
1486
|
+
|
1487
|
+
We verify that the result is cached and that the caches are separate::
|
1488
|
+
|
1489
|
+
sage: A.echelon_form() is A.echelon_form()
|
1490
|
+
True
|
1491
|
+
sage: A.echelon_form() is A.echelon_form(algorithm='classical')
|
1492
|
+
False
|
1493
|
+
|
1494
|
+
TESTS::
|
1495
|
+
|
1496
|
+
sage: W.<z> = CyclotomicField(13)
|
1497
|
+
sage: A = Matrix(W, 2,3, [10^30*(1-z)^13, 1, 2, 3, 4, z])
|
1498
|
+
sage: B = Matrix(W, 2,3, [(1-z)^13, 1, 2, 3, 4, z])
|
1499
|
+
sage: A.echelon_form() == A.echelon_form('classical') # long time (4s on sage.math, 2011)
|
1500
|
+
True
|
1501
|
+
sage: B.echelon_form() == B.echelon_form('classical')
|
1502
|
+
True
|
1503
|
+
|
1504
|
+
A degenerate case with the degree 1 cyclotomic field::
|
1505
|
+
|
1506
|
+
sage: A = matrix(CyclotomicField(1),2,3,[1,2,3,4,5,6])
|
1507
|
+
sage: A.echelon_form()
|
1508
|
+
[ 1 0 -1]
|
1509
|
+
[ 0 1 2]
|
1510
|
+
|
1511
|
+
A case that checks the bug in :issue:`3500`::
|
1512
|
+
|
1513
|
+
sage: cf4 = CyclotomicField(4) ; z4 = cf4.0
|
1514
|
+
sage: A = Matrix(cf4, 1, 2, [-z4, 1])
|
1515
|
+
sage: A.echelon_form()
|
1516
|
+
[ 1 zeta4]
|
1517
|
+
|
1518
|
+
Verify that the matrix on :issue:`10281` works::
|
1519
|
+
|
1520
|
+
sage: K.<rho> = CyclotomicField(106)
|
1521
|
+
sage: coeffs = [(18603/107*rho^51 - 11583/107*rho^50 - 19907/107*rho^49 - 13588/107*rho^48 - 8722/107*rho^47 + 2857/107*rho^46 - 19279/107*rho^45 - 16666/107*rho^44 - 11327/107*rho^43 + 3802/107*rho^42 + 18998/107*rho^41 - 10798/107*rho^40 + 16210/107*rho^39 - 13768/107*rho^38 + 15063/107*rho^37 - 14433/107*rho^36 - 19434/107*rho^35 - 12606/107*rho^34 + 3786/107*rho^33 - 17996/107*rho^32 + 12341/107*rho^31 - 15656/107*rho^30 - 19092/107*rho^29 + 8382/107*rho^28 - 18147/107*rho^27 + 14024/107*rho^26 + 18751/107*rho^25 - 8301/107*rho^24 - 20112/107*rho^23 - 14483/107*rho^22 + 4715/107*rho^21 + 20065/107*rho^20 + 15293/107*rho^19 + 10072/107*rho^18 + 4775/107*rho^17 - 953/107*rho^16 - 19782/107*rho^15 - 16020/107*rho^14 + 5633/107*rho^13 - 17618/107*rho^12 - 18187/107*rho^11 + 7492/107*rho^10 + 19165/107*rho^9 - 9988/107*rho^8 - 20042/107*rho^7 + 10109/107*rho^6 - 17677/107*rho^5 - 17723/107*rho^4 - 12489/107*rho^3 - 6321/107*rho^2 - 4082/107*rho - 1378/107, 1, 4*rho + 1), (0, 1, rho + 4)]
|
1522
|
+
sage: m = matrix(2, coeffs)
|
1523
|
+
sage: a = m.echelon_form(algorithm='classical')
|
1524
|
+
sage: b = m.echelon_form(algorithm='multimodular') # long time (5s on sage.math, 2012)
|
1525
|
+
sage: a == b # long time (depends on previous)
|
1526
|
+
True
|
1527
|
+
"""
|
1528
|
+
key = 'echelon_form-%s' % algorithm
|
1529
|
+
E = self.fetch(key)
|
1530
|
+
if E is not None:
|
1531
|
+
return E
|
1532
|
+
|
1533
|
+
if self._nrows == 0:
|
1534
|
+
E = self.__copy__()
|
1535
|
+
self.cache(key, E)
|
1536
|
+
self.cache('pivots', ())
|
1537
|
+
return E
|
1538
|
+
|
1539
|
+
if algorithm is None:
|
1540
|
+
try:
|
1541
|
+
from .matrix_cyclo_linbox import _echelon_form_multimodular
|
1542
|
+
except ImportError:
|
1543
|
+
algorithm = 'classical'
|
1544
|
+
else:
|
1545
|
+
algorithm = 'multimodular'
|
1546
|
+
|
1547
|
+
if algorithm == 'multimodular':
|
1548
|
+
E = self._echelon_form_multimodular(height_guess=height_guess)
|
1549
|
+
elif algorithm == 'classical':
|
1550
|
+
E = (self*self.denominator())._echelon_classical()
|
1551
|
+
else:
|
1552
|
+
raise ValueError("unknown algorithm '%s'" % algorithm)
|
1553
|
+
|
1554
|
+
self.cache(key, E)
|
1555
|
+
return E
|
1556
|
+
|
1557
|
+
def _echelon_form_multimodular(self, num_primes=10, height_guess=None):
|
1558
|
+
"""
|
1559
|
+
Use a multimodular algorithm to find the echelon form of ``self``.
|
1560
|
+
|
1561
|
+
INPUT:
|
1562
|
+
|
1563
|
+
- ``num_primes`` -- number of primes to work modulo
|
1564
|
+
|
1565
|
+
- ``height_guess`` -- guess for the height of the echelon form of self
|
1566
|
+
|
1567
|
+
OUTPUT: matrix in reduced row echelon form
|
1568
|
+
|
1569
|
+
EXAMPLES::
|
1570
|
+
|
1571
|
+
sage: W.<z> = CyclotomicField(3)
|
1572
|
+
sage: A = matrix(W, 2, 3, [1+z, 2/3, 9*z+7, -3 + 4*z, z, -7*z]); A
|
1573
|
+
[ z + 1 2/3 9*z + 7]
|
1574
|
+
[4*z - 3 z -7*z]
|
1575
|
+
sage: A._echelon_form_multimodular(10)
|
1576
|
+
[ 1 0 -192/97*z - 361/97]
|
1577
|
+
[ 0 1 1851/97*z + 1272/97]
|
1578
|
+
|
1579
|
+
TESTS:
|
1580
|
+
|
1581
|
+
We test a degenerate case::
|
1582
|
+
|
1583
|
+
sage: A = matrix(CyclotomicField(5),0); A
|
1584
|
+
[]
|
1585
|
+
sage: A._echelon_form_multimodular(10)
|
1586
|
+
[]
|
1587
|
+
sage: A.pivots()
|
1588
|
+
()
|
1589
|
+
|
1590
|
+
sage: A = matrix(CyclotomicField(13), 2, 3, [5, 1, 2, 46307, 46307*4, 46307])
|
1591
|
+
sage: A._echelon_form_multimodular()
|
1592
|
+
[ 1 0 7/19]
|
1593
|
+
[ 0 1 3/19]
|
1594
|
+
"""
|
1595
|
+
from .matrix_cyclo_linbox import _echelon_form_multimodular
|
1596
|
+
return _echelon_form_multimodular(self, num_primes, height_guess)
|
1597
|
+
|
1598
|
+
def _echelon_form_one_prime(self, p):
|
1599
|
+
"""
|
1600
|
+
Find the echelon form of ``self`` mod the primes dividing p. Return
|
1601
|
+
the rational matrix representing this lift. If the pivots of the
|
1602
|
+
reductions mod the primes over p are different, then no such lift
|
1603
|
+
exists, and we raise a :exc:`ValueError`. If this happens, then the
|
1604
|
+
denominator of the echelon form of ``self`` is divisible by p. (Note
|
1605
|
+
that the converse need not be true.)
|
1606
|
+
|
1607
|
+
INPUT:
|
1608
|
+
|
1609
|
+
- ``p`` -- a prime that splits completely in the cyclotomic base field
|
1610
|
+
|
1611
|
+
OUTPUT: tuple of
|
1612
|
+
|
1613
|
+
- ``matrix`` -- Lift via CRT of the echelon forms of ``self`` modulo
|
1614
|
+
each of the primes over p.
|
1615
|
+
- ``tuple`` -- the tuple of pivots for the echelon form of ``self`` mod the
|
1616
|
+
primes dividing p
|
1617
|
+
|
1618
|
+
EXAMPLES::
|
1619
|
+
|
1620
|
+
sage: W.<z> = CyclotomicField(3)
|
1621
|
+
sage: A = matrix(W, 2, 3, [1+z, 2/3, 9*z+7, -3 + 4*z, z, -7*z]); A
|
1622
|
+
[ z + 1 2/3 9*z + 7]
|
1623
|
+
[4*z - 3 z -7*z]
|
1624
|
+
sage: A._echelon_form_one_prime(7)
|
1625
|
+
(
|
1626
|
+
[1 0 4 0 1 2]
|
1627
|
+
[0 0 3 0 0 4], (0, 1)
|
1628
|
+
)
|
1629
|
+
sage: Matrix(W,2,3,[2*z+3,0,1,0,1,0])._echelon_form_one_prime(7)
|
1630
|
+
Traceback (most recent call last):
|
1631
|
+
...
|
1632
|
+
ValueError: echelon form mod 7 not defined
|
1633
|
+
"""
|
1634
|
+
cdef Py_ssize_t i
|
1635
|
+
|
1636
|
+
# Initialize variables
|
1637
|
+
ls, _ = self._reductions(p)
|
1638
|
+
|
1639
|
+
# Find our first echelon form, and the associated list
|
1640
|
+
# of pivots
|
1641
|
+
ech_ls = [ls[0].echelon_form()]
|
1642
|
+
pivot_ls = ech_ls[0].pivots()
|
1643
|
+
# If we've found the identity matrix, we're all done.
|
1644
|
+
if self._nrows == self._ncols == len(pivot_ls):
|
1645
|
+
return (self.parent().identity_matrix(), range(self._nrows))
|
1646
|
+
|
1647
|
+
# For each reduction of self (i.e. for each prime of
|
1648
|
+
# self.base_ring() over p), compute the echelon form, and
|
1649
|
+
# keep track of all reductions which have the largest
|
1650
|
+
# number of pivots seen so far.
|
1651
|
+
for i from 1 <= i < len(ls):
|
1652
|
+
ech = ls[i].echelon_form()
|
1653
|
+
|
1654
|
+
# This should only occur when p divides the denominator
|
1655
|
+
# of the echelon form of self.
|
1656
|
+
if ech.pivots() != pivot_ls:
|
1657
|
+
raise ValueError("echelon form mod %s not defined" % p)
|
1658
|
+
|
1659
|
+
ech_ls.append(ech)
|
1660
|
+
|
1661
|
+
# Now, just lift back to ZZ and return it.
|
1662
|
+
|
1663
|
+
# TODO: coercion going on here
|
1664
|
+
reduction = matrix(ZZ, len(ech_ls), self._nrows * self._ncols,
|
1665
|
+
[ [y.lift() for y in E.list()] for E in ech_ls])
|
1666
|
+
|
1667
|
+
# TODO: more coercion happening here
|
1668
|
+
_, Finv = self._reduction_matrix(p)
|
1669
|
+
|
1670
|
+
lifted_matrix = Finv * reduction
|
1671
|
+
|
1672
|
+
return (lifted_matrix, pivot_ls)
|
1673
|
+
|
1674
|
+
def tensor_product(self, A, subdivide=True):
|
1675
|
+
r"""
|
1676
|
+
Return the tensor product of two matrices.
|
1677
|
+
|
1678
|
+
INPUT:
|
1679
|
+
|
1680
|
+
- ``A`` -- a matrix
|
1681
|
+
- ``subdivide`` -- boolean (default: ``True``); whether or not to return
|
1682
|
+
natural subdivisions with the matrix
|
1683
|
+
|
1684
|
+
OUTPUT:
|
1685
|
+
|
1686
|
+
Replace each element of ``self`` by a copy of ``A``, but first
|
1687
|
+
create a scalar multiple of ``A`` by the element it replaces.
|
1688
|
+
So if ``self`` is an `m\times n` matrix and ``A`` is a
|
1689
|
+
`p\times q` matrix, then the tensor product is an `mp\times nq`
|
1690
|
+
matrix. By default, the matrix will be subdivided into
|
1691
|
+
submatrices of size `p\times q`.
|
1692
|
+
|
1693
|
+
EXAMPLES::
|
1694
|
+
|
1695
|
+
sage: C = CyclotomicField(12)
|
1696
|
+
sage: M = matrix.random(C, 3, 3)
|
1697
|
+
sage: N = matrix.random(C, 50, 50)
|
1698
|
+
sage: M.tensor_product(M) == super(type(M), M).tensor_product(M)
|
1699
|
+
True
|
1700
|
+
sage: N = matrix.random(C, 15, 20)
|
1701
|
+
sage: M.tensor_product(N) == super(type(M), M).tensor_product(N)
|
1702
|
+
True
|
1703
|
+
|
1704
|
+
TESTS::
|
1705
|
+
|
1706
|
+
sage: Mp = matrix.random(C, 2,3)
|
1707
|
+
sage: Np = matrix.random(C, 4,5)
|
1708
|
+
sage: subdiv = super(type(Mp),Mp).tensor_product(Np).subdivisions()
|
1709
|
+
sage: Mp.tensor_product(Np).subdivisions() == subdiv
|
1710
|
+
True
|
1711
|
+
|
1712
|
+
Check that `m \times 0` and `0 \times m` matrices work
|
1713
|
+
(:issue:`22769`)::
|
1714
|
+
|
1715
|
+
sage: m1 = matrix(C, 1, 0, [])
|
1716
|
+
sage: m2 = matrix(C, 2, 2, [1, 2, 3, 4])
|
1717
|
+
sage: m1.tensor_product(m2).dimensions()
|
1718
|
+
(2, 0)
|
1719
|
+
sage: m2.tensor_product(m1).dimensions()
|
1720
|
+
(2, 0)
|
1721
|
+
sage: m3 = matrix(C, 0, 3, [])
|
1722
|
+
sage: m3.tensor_product(m2).dimensions()
|
1723
|
+
(0, 6)
|
1724
|
+
sage: m2.tensor_product(m3).dimensions()
|
1725
|
+
(0, 6)
|
1726
|
+
"""
|
1727
|
+
if not isinstance(A, Matrix):
|
1728
|
+
raise TypeError('tensor product requires a second matrix, not {0}'.format(A))
|
1729
|
+
|
1730
|
+
if A.base_ring() is not self.base_ring():
|
1731
|
+
return super(Matrix_cyclo_dense, self).tensor_product(A, subdivide)
|
1732
|
+
|
1733
|
+
cdef Matrix_cyclo_dense M
|
1734
|
+
l = []
|
1735
|
+
R = self.base_ring()
|
1736
|
+
X = R._generator_matrix()
|
1737
|
+
d = self._degree
|
1738
|
+
MS = MatrixSpace(QQ, d, d)
|
1739
|
+
for c in self._matrix.columns():
|
1740
|
+
v = c.list()
|
1741
|
+
for n in range(d-1):
|
1742
|
+
c = c * X
|
1743
|
+
v += c.list()
|
1744
|
+
rmul = MS([v[d*i+j] for j in range(d) for i in range(d)]) # We take the transpose
|
1745
|
+
l.append(rmul * A._rational_matrix())
|
1746
|
+
|
1747
|
+
nr = self.nrows()
|
1748
|
+
nc = self.ncols()
|
1749
|
+
Anr = A.nrows()
|
1750
|
+
Anc = A.ncols()
|
1751
|
+
P = MatrixSpace(R, nr*Anr, nc*Anc)
|
1752
|
+
M = Matrix_cyclo_dense.__new__(Matrix_cyclo_dense, P,
|
1753
|
+
None, None, None)
|
1754
|
+
MS = MatrixSpace(QQ, d, P.nrows()*P.ncols())
|
1755
|
+
ret = [[l[mr*nc+mc][i,r*Anc+c] for mr in range(nr) for r in range(Anr)
|
1756
|
+
for mc in range(nc) for c in range(Anc)]
|
1757
|
+
for i in range(d)]
|
1758
|
+
M._matrix = MS(ret)
|
1759
|
+
if subdivide:
|
1760
|
+
M.subdivide([Anr*i for i in range(1,nr)], [Anc*i for i in range(1,nc)])
|
1761
|
+
return M
|