passagemath-modules 10.5.46__cp310-cp310-macosx_14_0_arm64.whl → 10.6.20__cp310-cp310-macosx_14_0_arm64.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.
Potentially problematic release.
This version of passagemath-modules might be problematic. Click here for more details.
- {passagemath_modules-10.5.46.dist-info → passagemath_modules-10.6.20.dist-info}/METADATA +49 -44
- {passagemath_modules-10.5.46.dist-info → passagemath_modules-10.6.20.dist-info}/RECORD +320 -314
- passagemath_modules.dylibs/libmpc.3.dylib +0 -0
- passagemath_modules.dylibs/libopenblasp-r0.3.29.dylib +0 -0
- sage/algebras/clifford_algebra.py +2 -2
- sage/algebras/clifford_algebra_element.cpython-310-darwin.so +0 -0
- sage/algebras/clifford_algebra_element.pyx +4 -2
- sage/algebras/exterior_algebra_groebner.cpython-310-darwin.so +0 -0
- sage/algebras/exterior_algebra_groebner.pyx +2 -0
- sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra.py +83 -5
- sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_element.cpython-310-darwin.so +0 -0
- sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_element.pxd +2 -0
- sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_element.pyx +28 -3
- sage/algebras/finite_gca.py +1 -1
- sage/algebras/lie_algebras/bgg_dual_module.py +18 -11
- sage/algebras/lie_algebras/classical_lie_algebra.py +3 -3
- sage/algebras/lie_algebras/examples.py +2 -2
- sage/algebras/lie_algebras/free_lie_algebra.py +1 -1
- sage/algebras/lie_algebras/heisenberg.py +4 -4
- sage/algebras/lie_algebras/lie_algebra.py +1 -1
- sage/algebras/lie_algebras/lie_algebra_element.cpython-310-darwin.so +0 -0
- sage/algebras/lie_algebras/lie_algebra_element.pyx +63 -27
- sage/algebras/lie_algebras/quotient.py +40 -29
- sage/algebras/lie_algebras/subalgebra.py +76 -53
- sage/algebras/lie_algebras/verma_module.py +1 -3
- sage/algebras/octonion_algebra.cpython-310-darwin.so +0 -0
- sage/algebras/octonion_algebra.pyx +1 -1
- sage/algebras/orlik_solomon.py +4 -4
- sage/algebras/orlik_terao.py +4 -4
- sage/algebras/steenrod/steenrod_algebra.py +37 -30
- sage/algebras/steenrod/steenrod_algebra_bases.py +2 -2
- sage/algebras/steenrod/steenrod_algebra_misc.py +4 -4
- sage/algebras/steenrod/steenrod_algebra_mult.py +2 -2
- sage/all__sagemath_modules.py +1 -0
- sage/calculus/integration.cpython-310-darwin.so +0 -0
- sage/calculus/integration.pyx +6 -5
- sage/calculus/interpolation.cpython-310-darwin.so +0 -0
- sage/calculus/interpolators.cpython-310-darwin.so +0 -0
- sage/calculus/ode.cpython-310-darwin.so +0 -0
- sage/calculus/ode.pxd +2 -2
- sage/calculus/ode.pyx +6 -4
- sage/calculus/riemann.cpython-310-darwin.so +0 -0
- sage/calculus/riemann.pyx +68 -48
- sage/calculus/transforms/dwt.cpython-310-darwin.so +0 -0
- sage/calculus/transforms/fft.cpython-310-darwin.so +0 -0
- sage/coding/ag_code_decoders.cpython-310-darwin.so +0 -0
- sage/coding/ag_code_decoders.pyx +31 -31
- sage/coding/binary_code.cpython-310-darwin.so +0 -0
- sage/coding/binary_code.pxd +6 -6
- sage/coding/binary_code.pyx +212 -173
- sage/coding/guruswami_sudan/utils.py +3 -5
- sage/coding/kasami_codes.cpython-310-darwin.so +0 -0
- sage/coding/kasami_codes.pyx +20 -24
- sage/coding/linear_code.py +2 -2
- sage/coding/linear_code_no_metric.py +5 -5
- sage/coding/linear_rank_metric.py +81 -19
- sage/combinat/free_module.py +22 -2
- sage/combinat/root_system/ambient_space.py +1 -1
- sage/combinat/root_system/associahedron.py +4 -4
- sage/combinat/root_system/braid_move_calculator.py +1 -1
- sage/combinat/root_system/braid_orbit.cpython-310-darwin.so +0 -0
- sage/combinat/root_system/branching_rules.py +2 -2
- sage/combinat/root_system/cartan_type.py +14 -14
- sage/combinat/root_system/coxeter_group.py +2 -2
- sage/combinat/root_system/coxeter_type.py +11 -0
- sage/combinat/root_system/extended_affine_weyl_group.py +8 -8
- sage/combinat/root_system/fundamental_group.py +2 -4
- sage/combinat/root_system/hecke_algebra_representation.py +1 -1
- sage/combinat/root_system/pieri_factors.py +2 -2
- sage/combinat/root_system/root_lattice_realization_algebras.py +1 -1
- sage/combinat/root_system/root_lattice_realizations.py +1 -1
- sage/combinat/root_system/type_folded.py +3 -3
- sage/combinat/root_system/type_reducible.py +8 -7
- sage/combinat/root_system/type_super_A.py +2 -2
- sage/combinat/root_system/weight_lattice_realizations.py +9 -8
- sage/combinat/root_system/weyl_characters.py +1 -1
- sage/crypto/__init__.py +1 -0
- sage/crypto/block_cipher/des.py +1 -1
- sage/crypto/block_cipher/miniaes.py +3 -3
- sage/crypto/block_cipher/present.py +3 -3
- sage/crypto/block_cipher/sdes.py +3 -3
- sage/crypto/boolean_function.cpython-310-darwin.so +0 -0
- sage/crypto/boolean_function.pyx +22 -23
- sage/crypto/key_exchange/diffie_hellman.py +4 -9
- sage/crypto/mq/sr.py +1 -1
- sage/crypto/public_key/blum_goldwasser.py +3 -3
- sage/crypto/sbox.cpython-310-darwin.so +0 -0
- sage/crypto/sbox.pyx +1 -1
- sage/crypto/sboxes.py +22 -0
- sage/crypto/util.py +4 -6
- sage/ext/interpreters/__init__.py +1 -1
- sage/ext/interpreters/all__sagemath_modules.py +1 -1
- sage/ext/interpreters/wrapper_cc.cpython-310-darwin.so +0 -0
- sage/ext/interpreters/wrapper_cc.pxd +5 -5
- sage/ext/interpreters/wrapper_cc.pyx +1 -1
- sage/ext/interpreters/wrapper_cdf.cpython-310-darwin.so +0 -0
- sage/ext/interpreters/wrapper_cdf.pxd +5 -7
- sage/ext/interpreters/wrapper_cdf.pyx +4 -10
- sage/ext/interpreters/wrapper_rdf.cpython-310-darwin.so +0 -0
- sage/ext/interpreters/wrapper_rdf.pxd +1 -1
- sage/ext/interpreters/wrapper_rdf.pyx +1 -1
- sage/ext/interpreters/wrapper_rr.cpython-310-darwin.so +0 -0
- sage/ext/interpreters/wrapper_rr.pxd +5 -5
- sage/ext/interpreters/wrapper_rr.pyx +1 -2
- sage/geometry/toric_lattice.py +3 -3
- sage/geometry/toric_lattice_element.cpython-310-darwin.so +0 -0
- sage/groups/additive_abelian/additive_abelian_group.py +1 -1
- sage/groups/additive_abelian/qmodnz.py +4 -4
- sage/groups/matrix_gps/coxeter_group.py +17 -4
- sage/groups/matrix_gps/group_element.cpython-310-darwin.so +0 -0
- sage/groups/misc_gps/argument_groups.py +2 -2
- sage/groups/misc_gps/imaginary_groups.py +4 -4
- sage/groups/perm_gps/partn_ref/refinement_binary.cpython-310-darwin.so +0 -0
- sage/groups/perm_gps/partn_ref/refinement_matrices.cpython-310-darwin.so +0 -0
- sage/homology/chain_complex.py +0 -2
- sage/homology/hochschild_complex.py +3 -3
- sage/homology/homology_morphism.py +6 -6
- sage/homology/homology_vector_space_with_basis.py +1 -1
- sage/libs/gsl/array.cpython-310-darwin.so +0 -0
- sage/libs/mpmath/utils.cpython-310-darwin.so +0 -0
- sage/matrix/action.cpython-310-darwin.so +0 -0
- sage/matrix/args.cpython-310-darwin.so +0 -0
- sage/matrix/args.pyx +25 -10
- sage/matrix/benchmark.py +8 -4
- sage/matrix/compute_J_ideal.py +2 -2
- sage/matrix/constructor.cpython-310-darwin.so +0 -0
- sage/matrix/echelon_matrix.cpython-310-darwin.so +0 -0
- sage/matrix/echelon_matrix.pyx +1 -1
- sage/matrix/matrix0.cpython-310-darwin.so +0 -0
- sage/matrix/matrix0.pxd +3 -3
- sage/matrix/matrix0.pyx +7 -5
- sage/matrix/matrix1.cpython-310-darwin.so +0 -0
- sage/matrix/matrix1.pyx +87 -48
- sage/matrix/matrix2.cpython-310-darwin.so +0 -0
- sage/matrix/matrix2.pxd +3 -3
- sage/matrix/matrix2.pyx +758 -75
- sage/matrix/matrix_cdv.cpython-310-darwin.so +0 -0
- sage/matrix/matrix_complex_double_dense.cpython-310-darwin.so +0 -0
- sage/matrix/matrix_complex_double_dense.pyx +1 -1
- sage/matrix/matrix_dense.cpython-310-darwin.so +0 -0
- sage/matrix/matrix_dense.pyx +2 -3
- sage/matrix/matrix_double_dense.cpython-310-darwin.so +0 -0
- sage/matrix/matrix_double_dense.pyx +11 -5
- sage/matrix/matrix_double_sparse.cpython-310-darwin.so +0 -0
- sage/matrix/matrix_generic_dense.cpython-310-darwin.so +0 -0
- sage/matrix/matrix_generic_sparse.cpython-310-darwin.so +0 -0
- sage/matrix/matrix_generic_sparse.pyx +1 -1
- sage/matrix/matrix_laurent_mpolynomial_dense.cpython-310-darwin.so +0 -0
- sage/matrix/matrix_numpy_dense.cpython-310-darwin.so +0 -0
- sage/matrix/matrix_numpy_integer_dense.cpython-310-darwin.so +0 -0
- sage/matrix/matrix_polynomial_dense.cpython-310-darwin.so +0 -0
- sage/matrix/matrix_polynomial_dense.pyx +952 -261
- sage/matrix/matrix_real_double_dense.cpython-310-darwin.so +0 -0
- sage/matrix/matrix_sparse.cpython-310-darwin.so +0 -0
- sage/matrix/matrix_sparse.pyx +2 -3
- sage/matrix/matrix_window.cpython-310-darwin.so +0 -0
- sage/matrix/matrix_window.pyx +2 -2
- sage/matrix/misc_mpfr.cpython-310-darwin.so +0 -0
- sage/matrix/operation_table.py +0 -2
- sage/matrix/special.py +4 -0
- sage/matrix/strassen.cpython-310-darwin.so +0 -0
- sage/matrix/strassen.pyx +1 -1
- sage/matroids/basis_exchange_matroid.cpython-310-darwin.so +0 -0
- sage/matroids/basis_matroid.cpython-310-darwin.so +0 -0
- sage/matroids/chow_ring.py +68 -65
- sage/matroids/chow_ring_ideal.py +41 -38
- sage/matroids/circuit_closures_matroid.cpython-310-darwin.so +0 -0
- sage/matroids/circuits_matroid.cpython-310-darwin.so +0 -0
- sage/matroids/database_matroids.py +16 -5
- sage/matroids/dual_matroid.py +2 -2
- sage/matroids/extension.cpython-310-darwin.so +0 -0
- sage/matroids/flats_matroid.cpython-310-darwin.so +0 -0
- sage/matroids/gammoid.py +1 -1
- sage/matroids/graphic_matroid.cpython-310-darwin.so +0 -0
- sage/matroids/graphic_matroid.pyx +3 -3
- sage/matroids/lean_matrix.cpython-310-darwin.so +0 -0
- sage/matroids/lean_matrix.pyx +22 -22
- sage/matroids/linear_matroid.cpython-310-darwin.so +0 -0
- sage/matroids/linear_matroid.pyx +13 -13
- sage/matroids/matroid.cpython-310-darwin.so +0 -0
- sage/matroids/matroid.pyx +15 -15
- sage/matroids/matroids_plot_helpers.py +48 -46
- sage/matroids/minor_matroid.py +2 -2
- sage/matroids/set_system.cpython-310-darwin.so +0 -0
- sage/matroids/transversal_matroid.cpython-310-darwin.so +0 -0
- sage/matroids/transversal_matroid.pyx +3 -3
- sage/matroids/union_matroid.cpython-310-darwin.so +0 -0
- sage/matroids/union_matroid.pyx +3 -0
- sage/matroids/unpickling.cpython-310-darwin.so +0 -0
- sage/matroids/utilities.py +2 -2
- sage/misc/c3.cpython-310-darwin.so +0 -0
- sage/misc/compat.py +1 -2
- sage/misc/pickle_old.cpython-310-darwin.so +0 -0
- sage/modules/diamond_cutting.py +117 -30
- sage/modules/fg_pid/fgp_module.py +3 -3
- sage/modules/filtered_vector_space.py +4 -4
- sage/modules/finite_submodule_iter.cpython-310-darwin.so +0 -0
- sage/modules/fp_graded/free_module.py +2 -2
- sage/modules/fp_graded/module.py +2 -2
- sage/modules/fp_graded/morphism.py +4 -4
- sage/modules/fp_graded/steenrod/morphism.py +1 -1
- sage/modules/free_module.py +144 -15
- sage/modules/free_module_element.cpython-310-darwin.so +0 -0
- sage/modules/free_module_element.pyx +4 -4
- sage/modules/free_module_integer.py +2 -2
- sage/modules/free_module_morphism.py +3 -3
- sage/modules/free_module_pseudohomspace.py +352 -0
- sage/modules/free_module_pseudomorphism.py +578 -0
- sage/modules/free_quadratic_module_integer_symmetric.py +24 -13
- sage/modules/matrix_morphism.py +9 -9
- sage/modules/multi_filtered_vector_space.py +4 -4
- sage/modules/ore_module.py +2208 -0
- sage/modules/ore_module_element.py +178 -0
- sage/modules/ore_module_homspace.py +147 -0
- sage/modules/ore_module_morphism.py +968 -0
- sage/modules/quotient_module.py +11 -1
- sage/modules/submodule.py +1 -1
- sage/modules/torsion_quadratic_module.py +1 -1
- sage/modules/vector_complex_double_dense.cpython-310-darwin.so +0 -0
- sage/modules/vector_double_dense.cpython-310-darwin.so +0 -0
- sage/modules/vector_integer_dense.cpython-310-darwin.so +0 -0
- sage/modules/vector_integer_sparse.cpython-310-darwin.so +0 -0
- sage/modules/vector_integer_sparse.pyx +4 -4
- sage/modules/vector_modn_dense.cpython-310-darwin.so +0 -0
- sage/modules/vector_modn_sparse.cpython-310-darwin.so +0 -0
- sage/modules/vector_numpy_dense.cpython-310-darwin.so +0 -0
- sage/modules/vector_numpy_integer_dense.cpython-310-darwin.so +0 -0
- sage/modules/vector_rational_dense.cpython-310-darwin.so +0 -0
- sage/modules/vector_rational_dense.pyx +1 -1
- sage/modules/vector_rational_sparse.cpython-310-darwin.so +0 -0
- sage/modules/vector_rational_sparse.pyx +5 -5
- sage/modules/vector_real_double_dense.cpython-310-darwin.so +0 -0
- sage/modules/vector_space_morphism.py +2 -2
- sage/modules/with_basis/cell_module.py +17 -0
- sage/modules/with_basis/indexed_element.cpython-310-darwin.so +0 -0
- sage/modules/with_basis/indexed_element.pyx +1 -1
- sage/modules/with_basis/invariant.py +1 -1
- sage/modules/with_basis/representation.py +0 -1
- sage/modules/with_basis/subquotient.py +2 -2
- sage/numerical/gauss_legendre.cpython-310-darwin.so +0 -0
- sage/probability/probability_distribution.cpython-310-darwin.so +0 -0
- sage/quadratic_forms/binary_qf.py +7 -7
- sage/quadratic_forms/bqf_class_group.py +26 -92
- sage/quadratic_forms/count_local_2.cpython-310-darwin.so +0 -0
- sage/quadratic_forms/extras.py +1 -1
- sage/quadratic_forms/quadratic_form.py +5 -4
- sage/quadratic_forms/quadratic_form__equivalence_testing.py +7 -4
- sage/quadratic_forms/quadratic_form__evaluate.cpython-310-darwin.so +0 -0
- sage/quadratic_forms/quadratic_form__local_field_invariants.py +10 -10
- sage/quadratic_forms/quadratic_form__ternary_Tornaria.py +2 -2
- sage/quadratic_forms/ternary.cpython-310-darwin.so +0 -0
- sage/quadratic_forms/ternary_qf.py +50 -83
- sage/rings/complex_conversion.cpython-310-darwin.so +0 -0
- sage/rings/complex_double.cpython-310-darwin.so +0 -0
- sage/rings/complex_double.pxd +1 -0
- sage/rings/complex_double.pyx +37 -32
- sage/rings/complex_mpc.cpython-310-darwin.so +0 -0
- sage/rings/complex_mpc.pyx +27 -23
- sage/rings/complex_mpfr.cpython-310-darwin.so +0 -0
- sage/rings/complex_mpfr.pyx +11 -9
- sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py +277 -21
- sage/rings/function_field/drinfeld_modules/drinfeld_module.py +10 -1
- sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +1 -1
- sage/rings/function_field/drinfeld_modules/homset.py +1 -2
- sage/rings/function_field/drinfeld_modules/morphism.py +2 -2
- sage/rings/function_field/hermite_form_polynomial.cpython-310-darwin.so +0 -0
- sage/rings/function_field/khuri_makdisi.cpython-310-darwin.so +0 -0
- sage/rings/function_field/khuri_makdisi.pyx +27 -25
- sage/rings/invariants/invariant_theory.py +61 -60
- sage/rings/invariants/reconstruction.py +8 -8
- sage/rings/polynomial/laurent_polynomial_mpair.cpython-310-darwin.so +0 -0
- sage/rings/polynomial/ore_function_element.py +1 -1
- sage/rings/polynomial/ore_polynomial_element.cpython-310-darwin.so +0 -0
- sage/rings/polynomial/ore_polynomial_element.pyx +8 -8
- sage/rings/polynomial/ore_polynomial_ring.py +134 -17
- sage/rings/polynomial/polynomial_real_mpfr_dense.cpython-310-darwin.so +0 -0
- sage/rings/polynomial/skew_polynomial_element.cpython-310-darwin.so +0 -0
- sage/rings/polynomial/skew_polynomial_finite_field.cpython-310-darwin.so +0 -0
- sage/rings/polynomial/skew_polynomial_finite_field.pyx +3 -4
- sage/rings/polynomial/skew_polynomial_finite_order.cpython-310-darwin.so +0 -0
- sage/rings/polynomial/skew_polynomial_finite_order.pyx +2 -5
- sage/rings/real_double_element_gsl.cpython-310-darwin.so +0 -0
- sage/rings/real_mpfr.cpython-310-darwin.so +0 -0
- sage/rings/real_mpfr.pyx +25 -7
- sage/rings/ring_extension.cpython-310-darwin.so +0 -0
- sage/rings/ring_extension.pyx +4 -2
- sage/rings/ring_extension_conversion.cpython-310-darwin.so +0 -0
- sage/rings/ring_extension_element.cpython-310-darwin.so +0 -0
- sage/rings/ring_extension_element.pyx +42 -0
- sage/rings/ring_extension_morphism.cpython-310-darwin.so +0 -0
- sage/schemes/projective/cohomology.py +2 -2
- sage/stats/basic_stats.py +9 -6
- sage/stats/distributions/dgs_misc.h +11 -4
- sage/stats/distributions/discrete_gaussian_integer.cpython-310-darwin.so +0 -0
- sage/stats/distributions/discrete_gaussian_integer.pyx +9 -7
- sage/stats/hmm/chmm.cpython-310-darwin.so +0 -0
- sage/stats/hmm/chmm.pyx +13 -13
- sage/stats/hmm/distributions.cpython-310-darwin.so +0 -0
- sage/stats/hmm/distributions.pxd +3 -3
- sage/stats/hmm/distributions.pyx +3 -3
- sage/stats/hmm/hmm.cpython-310-darwin.so +0 -0
- sage/stats/hmm/hmm.pxd +3 -3
- sage/stats/hmm/hmm.pyx +6 -6
- sage/stats/hmm/util.cpython-310-darwin.so +0 -0
- sage/stats/hmm/util.pyx +6 -6
- sage/stats/intlist.cpython-310-darwin.so +0 -0
- sage/stats/intlist.pxd +3 -3
- sage/stats/time_series.cpython-310-darwin.so +0 -0
- sage/tensor/modules/alternating_contr_tensor.py +3 -3
- sage/tensor/modules/comp.py +3 -3
- sage/tensor/modules/ext_pow_free_module.py +3 -3
- sage/tensor/modules/format_utilities.py +3 -3
- sage/tensor/modules/free_module_linear_group.py +3 -3
- sage/tensor/modules/free_module_morphism.py +0 -1
- sage/tensor/modules/tensor_free_module.py +3 -3
- sage/tensor/modules/tensor_free_submodule.py +1 -1
- sage/tensor/modules/tensor_free_submodule_basis.py +1 -1
- sage/tensor/modules/tensor_with_indices.py +5 -5
- {passagemath_modules-10.5.46.dist-info → passagemath_modules-10.6.20.dist-info}/WHEEL +0 -0
- {passagemath_modules-10.5.46.dist-info → passagemath_modules-10.6.20.dist-info}/top_level.txt +0 -0
|
@@ -31,11 +31,14 @@ AUTHORS:
|
|
|
31
31
|
|
|
32
32
|
- Vincent Neiger (2024-02-13): added basis_completion(), _is_basis_completion(),
|
|
33
33
|
_basis_completion_via_reversed_approx().
|
|
34
|
+
|
|
35
|
+
- Vincent Neiger (2025-02-16): added minimal_relation_basis(),
|
|
36
|
+
minimal_interpolation_basis().
|
|
34
37
|
"""
|
|
35
38
|
# ****************************************************************************
|
|
36
39
|
# Copyright (C) 2016 Kwankyu Lee <ekwankyu@gmail.com>
|
|
37
40
|
# Copyright (C) 2017 Johan Rosenkilde
|
|
38
|
-
# Copyright (C) 2018,2020,2021,2024 Vincent Neiger
|
|
41
|
+
# Copyright (C) 2018,2020,2021,2024,2025 Vincent Neiger
|
|
39
42
|
#
|
|
40
43
|
# Distributed under the terms of the GNU General Public License (GPL)
|
|
41
44
|
# as published by the Free Software Foundation; either version 2 of
|
|
@@ -47,7 +50,6 @@ from sage.matrix.matrix_generic_dense cimport Matrix_generic_dense
|
|
|
47
50
|
from sage.matrix.matrix2 cimport Matrix
|
|
48
51
|
from sage.rings.integer_ring import ZZ
|
|
49
52
|
|
|
50
|
-
|
|
51
53
|
cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
52
54
|
r"""
|
|
53
55
|
Dense matrix over a univariate polynomial ring over a field.
|
|
@@ -120,7 +122,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
120
122
|
EXAMPLES::
|
|
121
123
|
|
|
122
124
|
sage: pR.<x> = GF(7)[]
|
|
123
|
-
sage: M =
|
|
125
|
+
sage: M = matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]])
|
|
124
126
|
sage: M._check_shift_dimension(shifts=[1,3,2])
|
|
125
127
|
|
|
126
128
|
sage: M._check_shift_dimension(shifts=[1,3,2], row_wise=False)
|
|
@@ -146,26 +148,21 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
146
148
|
EXAMPLES::
|
|
147
149
|
|
|
148
150
|
sage: pR.<x> = GF(7)[]
|
|
149
|
-
sage: M =
|
|
151
|
+
sage: M = matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]])
|
|
150
152
|
sage: M.degree()
|
|
151
153
|
3
|
|
152
154
|
|
|
153
|
-
|
|
155
|
+
A zero matrix (including empty matrices) has degree ``-1``::
|
|
154
156
|
|
|
155
|
-
sage: M =
|
|
157
|
+
sage: M = matrix(pR, 2, 3)
|
|
156
158
|
sage: M.degree()
|
|
157
159
|
-1
|
|
158
|
-
|
|
159
|
-
For an empty matrix, the degree is not defined::
|
|
160
|
-
|
|
161
|
-
sage: M = Matrix(pR, 3, 0)
|
|
160
|
+
sage: M = matrix(pR, 3, 0)
|
|
162
161
|
sage: M.degree()
|
|
163
|
-
|
|
164
|
-
...
|
|
165
|
-
ValueError: empty matrix does not have a degree
|
|
162
|
+
-1
|
|
166
163
|
"""
|
|
167
164
|
if self.nrows() == 0 or self.ncols() == 0:
|
|
168
|
-
|
|
165
|
+
return -1
|
|
169
166
|
return max(self[i, j].degree()
|
|
170
167
|
for i in range(self.nrows()) for j in range(self.ncols()))
|
|
171
168
|
|
|
@@ -200,7 +197,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
200
197
|
EXAMPLES::
|
|
201
198
|
|
|
202
199
|
sage: pR.<x> = GF(7)[]
|
|
203
|
-
sage: M =
|
|
200
|
+
sage: M = matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]])
|
|
204
201
|
sage: M.degree_matrix()
|
|
205
202
|
[ 1 -1 0]
|
|
206
203
|
[ 3 -1 -1]
|
|
@@ -230,13 +227,13 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
230
227
|
from sage.matrix.constructor import matrix
|
|
231
228
|
zero_degree = min(shifts) - 1
|
|
232
229
|
if row_wise:
|
|
233
|
-
return matrix(
|
|
230
|
+
return matrix(ZZ, [[self[i,j].degree() + shifts[j]
|
|
234
231
|
if self[i,j] != 0 else zero_degree
|
|
235
|
-
for j in range(self.ncols()) ] for i in range(self.nrows())]
|
|
232
|
+
for j in range(self.ncols()) ] for i in range(self.nrows())])
|
|
236
233
|
else:
|
|
237
|
-
return matrix(
|
|
234
|
+
return matrix(ZZ, [[self[i,j].degree() + shifts[i]
|
|
238
235
|
if self[i,j] != 0 else zero_degree
|
|
239
|
-
for j in range(self.ncols()) ] for i in range(self.nrows())]
|
|
236
|
+
for j in range(self.ncols()) ] for i in range(self.nrows())])
|
|
240
237
|
|
|
241
238
|
def constant_matrix(self):
|
|
242
239
|
r"""
|
|
@@ -249,7 +246,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
249
246
|
|
|
250
247
|
sage: pR.<x> = GF(7)[]
|
|
251
248
|
|
|
252
|
-
sage: M =
|
|
249
|
+
sage: M = matrix([
|
|
253
250
|
....: [ x^3+5*x^2+5*x+1, 5, 6*x+4, 0],
|
|
254
251
|
....: [ 6*x^2+3*x+1, 1, 2, 0],
|
|
255
252
|
....: [2*x^3+4*x^2+6*x+4, 5*x + 1, 2*x^2+5*x+5, x^2+5*x+6]
|
|
@@ -260,7 +257,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
260
257
|
[4 1 5 6]
|
|
261
258
|
"""
|
|
262
259
|
from sage.matrix.constructor import matrix
|
|
263
|
-
return matrix([[self[i,j].constant_coefficient()
|
|
260
|
+
return matrix(self.base_ring().base_ring(), [[self[i,j].constant_coefficient()
|
|
264
261
|
for j in range(self.ncols())] for i in range(self.nrows())])
|
|
265
262
|
|
|
266
263
|
def is_constant(self):
|
|
@@ -273,16 +270,16 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
273
270
|
EXAMPLES::
|
|
274
271
|
|
|
275
272
|
sage: pR.<x> = GF(7)[]
|
|
276
|
-
sage: M =
|
|
273
|
+
sage: M = matrix([
|
|
277
274
|
....: [ x^3+5*x^2+5*x+1, 5, 6*x+4, 0],
|
|
278
275
|
....: [ 6*x^2+3*x+1, 1, 2, 0],
|
|
279
276
|
....: [2*x^3+4*x^2+6*x+4, 5*x + 1, 2*x^2+5*x+5, x^2+5*x+6]
|
|
280
277
|
....: ])
|
|
281
278
|
sage: M.is_constant()
|
|
282
279
|
False
|
|
283
|
-
sage: M =
|
|
280
|
+
sage: M = matrix(pR, [[1,5,2], [3,1,5]]); M.is_constant()
|
|
284
281
|
True
|
|
285
|
-
sage: M =
|
|
282
|
+
sage: M = matrix.zero(pR, 3, 5); M.is_constant()
|
|
286
283
|
True
|
|
287
284
|
|
|
288
285
|
.. SEEALSO::
|
|
@@ -320,7 +317,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
320
317
|
|
|
321
318
|
sage: pR.<x> = GF(7)[]
|
|
322
319
|
|
|
323
|
-
sage: M =
|
|
320
|
+
sage: M = matrix([
|
|
324
321
|
....: [ x^3+5*x^2+5*x+1, 5, 6*x+4, 0],
|
|
325
322
|
....: [ 6*x^2+3*x+1, 1, 2, 0],
|
|
326
323
|
....: [2*x^3+4*x^2+6*x+4, 5*x + 1, 2*x^2+5*x+5, x^2+5*x+6]
|
|
@@ -416,7 +413,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
416
413
|
|
|
417
414
|
sage: pR.<x> = GF(7)[]
|
|
418
415
|
|
|
419
|
-
sage: M =
|
|
416
|
+
sage: M = matrix([
|
|
420
417
|
....: [ x^3+5*x^2+5*x+1, 5, 6*x+4, 0],
|
|
421
418
|
....: [ 6*x^2+3*x+1, 1, 2, 0],
|
|
422
419
|
....: [2*x^3+4*x^2+6*x+4, 5*x + 1, 2*x^2+5*x+5, x^2+5*x+6]
|
|
@@ -507,7 +504,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
507
504
|
|
|
508
505
|
sage: pR.<x> = GF(7)[]
|
|
509
506
|
|
|
510
|
-
sage: M =
|
|
507
|
+
sage: M = matrix([
|
|
511
508
|
....: [ x^3+5*x^2+5*x+1, 5, 6*x+4, 0],
|
|
512
509
|
....: [ 6*x^2+3*x+1, 1, 2, 0],
|
|
513
510
|
....: [2*x^3+4*x^2+6*x+4, 5*x + 1, 2*x^2+5*x+5, x^2+5*x+6]
|
|
@@ -615,7 +612,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
615
612
|
|
|
616
613
|
sage: pR.<x> = GF(7)[]
|
|
617
614
|
|
|
618
|
-
sage: M =
|
|
615
|
+
sage: M = matrix([
|
|
619
616
|
....: [ x^3+5*x^2+5*x+1, 5, 6*x+4, 0],
|
|
620
617
|
....: [ 6*x^2+3*x+1, 1, 2, 0],
|
|
621
618
|
....: [2*x^3+4*x^2+6*x+4, 5*x + 1, 2*x^2+5*x+5, x^2+5*x+6]
|
|
@@ -750,7 +747,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
750
747
|
EXAMPLES::
|
|
751
748
|
|
|
752
749
|
sage: pR.<x> = GF(7)[]
|
|
753
|
-
sage: A =
|
|
750
|
+
sage: A = matrix(pR, 3, 3,
|
|
754
751
|
....: [[4*x+5, 5*x^2 + x + 1, 4*x^2 + 4],
|
|
755
752
|
....: [6*x^2 + 6*x + 6, 4*x^2 + 5*x, 4*x^2 + x + 3],
|
|
756
753
|
....: [3*x^2 + 2, 4*x + 1, x^2 + 3*x]])
|
|
@@ -791,13 +788,13 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
791
788
|
"""
|
|
792
789
|
if d <= 0:
|
|
793
790
|
raise ValueError("the precision must be positive")
|
|
791
|
+
if self.nrows() != self.ncols():
|
|
792
|
+
raise ArithmeticError("the input matrix must be square")
|
|
794
793
|
try:
|
|
795
794
|
inv_trunc = self.constant_matrix().inverse()
|
|
796
795
|
except ZeroDivisionError:
|
|
797
796
|
raise ZeroDivisionError("the constant matrix term self(0)"
|
|
798
797
|
" must be invertible")
|
|
799
|
-
except ArithmeticError:
|
|
800
|
-
raise ArithmeticError("the input matrix must be square")
|
|
801
798
|
|
|
802
799
|
# in comments below, A=self, B=inv_trunc
|
|
803
800
|
# at this point, B = A^{-1} mod x
|
|
@@ -846,9 +843,8 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
846
843
|
|
|
847
844
|
EXAMPLES::
|
|
848
845
|
|
|
849
|
-
|
|
850
846
|
sage: pR.<x> = GF(7)[]
|
|
851
|
-
sage: A =
|
|
847
|
+
sage: A = matrix(pR, 3, 3,
|
|
852
848
|
....: [[4*x+5, 5*x^2 + x + 1, 4*x^2 + 4],
|
|
853
849
|
....: [6*x^2 + 6*x + 6, 4*x^2 + 5*x, 4*x^2 + x + 3],
|
|
854
850
|
....: [3*x^2 + 2, 4*x + 1, x^2 + 3*x]])
|
|
@@ -860,7 +856,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
860
856
|
sage: B == X*A % x**4
|
|
861
857
|
True
|
|
862
858
|
|
|
863
|
-
sage: B =
|
|
859
|
+
sage: B = matrix(pR, 2, 3,
|
|
864
860
|
....: [[3*x, x^2 + x + 2, x^2 + 2*x + 3],
|
|
865
861
|
....: [ 0, 6*x^2 + 1, 1]])
|
|
866
862
|
sage: A.solve_left_series_trunc(B, 3)
|
|
@@ -895,7 +891,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
895
891
|
[5*x^2 + 2*x + 5 5*x + 5 2*x + 4]
|
|
896
892
|
[5*x^3 + 2*x + 1 2*x^2 + 2*x + 5 4*x^2]
|
|
897
893
|
|
|
898
|
-
sage: V =
|
|
894
|
+
sage: V = matrix([[3*x^2 + 4*x + 1, 4*x]])
|
|
899
895
|
sage: A[:2,:].solve_left_series_trunc(V*A[:2,:], 4) == V
|
|
900
896
|
True
|
|
901
897
|
|
|
@@ -907,16 +903,68 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
907
903
|
.. SEEALSO::
|
|
908
904
|
|
|
909
905
|
:meth:`solve_right_series_trunc` .
|
|
906
|
+
|
|
907
|
+
TESTS::
|
|
908
|
+
|
|
909
|
+
sage: ring.<x> = QQ[]
|
|
910
|
+
sage: A = matrix(ring, 0, 5)
|
|
911
|
+
sage: B = vector(ring, [1, 0, x**2+1, 0, x])
|
|
912
|
+
sage: A.solve_left_series_trunc(B, 3)
|
|
913
|
+
Traceback (most recent call last):
|
|
914
|
+
...
|
|
915
|
+
ValueError: matrix equation has no solutions
|
|
916
|
+
|
|
917
|
+
sage: B = vector(ring, 5)
|
|
918
|
+
sage: A.solve_left_series_trunc(B, 3)
|
|
919
|
+
()
|
|
920
|
+
|
|
921
|
+
sage: A = matrix(ring, 5, 0)
|
|
922
|
+
sage: B = vector(ring, 0)
|
|
923
|
+
sage: A.solve_left_series_trunc(B, 3)
|
|
924
|
+
(0, 0, 0, 0, 0)
|
|
925
|
+
|
|
926
|
+
sage: A = matrix(ring, 0, 5)
|
|
927
|
+
sage: B = matrix(ring, 2, 5, [[1, 0, x**2+1, 0, x], [0, 0, 0, 0, 0]])
|
|
928
|
+
sage: A.solve_left_series_trunc(B, 3)
|
|
929
|
+
Traceback (most recent call last):
|
|
930
|
+
...
|
|
931
|
+
ValueError: matrix equation has no solutions
|
|
932
|
+
|
|
933
|
+
sage: B = matrix(ring, 2, 5)
|
|
934
|
+
sage: A.solve_left_series_trunc(B, 3)
|
|
935
|
+
[]
|
|
936
|
+
|
|
937
|
+
sage: A = matrix(ring, 5, 0)
|
|
938
|
+
sage: B = matrix(ring, 2, 0)
|
|
939
|
+
sage: A.solve_left_series_trunc(B, 3)
|
|
940
|
+
[0 0 0 0 0]
|
|
941
|
+
[0 0 0 0 0]
|
|
910
942
|
"""
|
|
911
943
|
from sage.structure.element import Vector
|
|
944
|
+
from sage.matrix.constructor import matrix
|
|
945
|
+
from sage.matrix.constructor import vector
|
|
912
946
|
if isinstance(B, Vector):
|
|
913
947
|
if self.ncols() != B.degree():
|
|
914
948
|
raise ValueError("number of columns of self must equal "
|
|
915
949
|
"degree of right-hand side")
|
|
950
|
+
if self.ncols() == 0:
|
|
951
|
+
return vector(self.base_ring(), self.nrows())
|
|
952
|
+
if self.nrows() == 0:
|
|
953
|
+
if not B.is_zero():
|
|
954
|
+
raise ValueError("matrix equation has no solutions")
|
|
955
|
+
else:
|
|
956
|
+
return vector(self.base_ring(), 0)
|
|
916
957
|
else:
|
|
917
958
|
if self.ncols() != B.ncols():
|
|
918
959
|
raise ValueError("number of columns of self must equal "
|
|
919
960
|
"number of columns of right-hand side")
|
|
961
|
+
if self.ncols() == 0:
|
|
962
|
+
return matrix(self.base_ring(), B.nrows(), self.nrows())
|
|
963
|
+
if self.nrows() == 0:
|
|
964
|
+
if not B.is_zero():
|
|
965
|
+
raise ValueError("matrix equation has no solutions")
|
|
966
|
+
else:
|
|
967
|
+
return matrix(self.base_ring(), B.nrows(), 0)
|
|
920
968
|
|
|
921
969
|
if d <= 0:
|
|
922
970
|
raise ValueError("the precision must be positive")
|
|
@@ -941,7 +989,6 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
941
989
|
except (ZeroDivisionError,ArithmeticError):
|
|
942
990
|
# general case (possibly no solution)
|
|
943
991
|
m = self.nrows()
|
|
944
|
-
from sage.matrix.constructor import matrix
|
|
945
992
|
if isinstance(B, Vector):
|
|
946
993
|
F = matrix.block([[self],[-B.row()]])
|
|
947
994
|
s = [0]*m + [d]
|
|
@@ -995,7 +1042,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
995
1042
|
EXAMPLES::
|
|
996
1043
|
|
|
997
1044
|
sage: pR.<x> = GF(7)[]
|
|
998
|
-
sage: A =
|
|
1045
|
+
sage: A = matrix(pR, 3, 3,
|
|
999
1046
|
....: [[4*x+5, 5*x^2 + x + 1, 4*x^2 + 4],
|
|
1000
1047
|
....: [6*x^2 + 6*x + 6, 4*x^2 + 5*x, 4*x^2 + x + 3],
|
|
1001
1048
|
....: [3*x^2 + 2, 4*x + 1, x^2 + 3*x]])
|
|
@@ -1006,7 +1053,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
1006
1053
|
(2*x^3 + x^2, 5*x^3 + x^2 + 5*x + 6, 4*x^3 + 6*x^2 + 4*x)
|
|
1007
1054
|
sage: B == A*X % x**4
|
|
1008
1055
|
True
|
|
1009
|
-
sage: B =
|
|
1056
|
+
sage: B = matrix(pR, 3, 2,
|
|
1010
1057
|
....: [[5*x^2 + 6*x + 3, 4*x^2 + 6*x + 4],
|
|
1011
1058
|
....: [ x^2 + 4*x + 2, 5*x + 2],
|
|
1012
1059
|
....: [ 5*x + 3, 0]])
|
|
@@ -1044,7 +1091,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
1044
1091
|
[ x^2 + 3*x + 5 3*x^2 + 4*x + 4]
|
|
1045
1092
|
[ 5*x + 3 3*x + 2]
|
|
1046
1093
|
|
|
1047
|
-
sage: V =
|
|
1094
|
+
sage: V = matrix([[2*x^2 + 5*x + 1], [3*x^2+4]])
|
|
1048
1095
|
sage: A[:,:2].solve_right_series_trunc(A[:,:2]*V, 4) == V
|
|
1049
1096
|
True
|
|
1050
1097
|
|
|
@@ -1103,7 +1150,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
1103
1150
|
EXAMPLES::
|
|
1104
1151
|
|
|
1105
1152
|
sage: pR.<x> = GF(7)[]
|
|
1106
|
-
sage: M =
|
|
1153
|
+
sage: M = matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]])
|
|
1107
1154
|
sage: M.row_degrees()
|
|
1108
1155
|
[1, 3]
|
|
1109
1156
|
|
|
@@ -1113,31 +1160,34 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
1113
1160
|
A zero row in a polynomial matrix can be identified in the (shifted)
|
|
1114
1161
|
row degrees as the entries equal to ``min(shifts)-1``::
|
|
1115
1162
|
|
|
1116
|
-
sage: M =
|
|
1163
|
+
sage: M = matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0], [0, 0, 0]])
|
|
1117
1164
|
sage: M.row_degrees()
|
|
1118
1165
|
[1, 3, -1]
|
|
1119
1166
|
|
|
1120
1167
|
sage: M.row_degrees(shifts=[-2,1,2])
|
|
1121
1168
|
[2, 1, -3]
|
|
1122
1169
|
|
|
1123
|
-
The row degrees of
|
|
1124
|
-
|
|
1170
|
+
The row degrees of a row-empty matrix `0\times n` is an empty list,
|
|
1171
|
+
while those of a column-empty matrix `m\times 0` is a list of `m` times
|
|
1172
|
+
`-1`::
|
|
1125
1173
|
|
|
1126
|
-
sage: M =
|
|
1174
|
+
sage: M = matrix(pR, 0, 3)
|
|
1127
1175
|
sage: M.row_degrees()
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1176
|
+
[]
|
|
1177
|
+
sage: M.row_degrees(shifts=[1,2,3])
|
|
1178
|
+
[]
|
|
1131
1179
|
|
|
1132
|
-
sage: M =
|
|
1180
|
+
sage: M = matrix(pR, 3, 0)
|
|
1133
1181
|
sage: M.row_degrees()
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1182
|
+
[-1, -1, -1]
|
|
1183
|
+
sage: M.row_degrees(shifts=[])
|
|
1184
|
+
[-1, -1, -1]
|
|
1137
1185
|
"""
|
|
1138
1186
|
self._check_shift_dimension(shifts,row_wise=True)
|
|
1139
|
-
if self.
|
|
1140
|
-
|
|
1187
|
+
if self.nrows() == 0:
|
|
1188
|
+
return []
|
|
1189
|
+
if self.ncols() == 0: # shifts is None (or empty list [])
|
|
1190
|
+
return [-1]*self.nrows()
|
|
1141
1191
|
if shifts is None:
|
|
1142
1192
|
return [ max([ self[i,j].degree() for j in range(self.ncols()) ])
|
|
1143
1193
|
for i in range(self.nrows()) ]
|
|
@@ -1170,7 +1220,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
1170
1220
|
EXAMPLES::
|
|
1171
1221
|
|
|
1172
1222
|
sage: pR.<x> = GF(7)[]
|
|
1173
|
-
sage: M =
|
|
1223
|
+
sage: M = matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]])
|
|
1174
1224
|
sage: M.column_degrees()
|
|
1175
1225
|
[3, -1, 0]
|
|
1176
1226
|
|
|
@@ -1183,28 +1233,31 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
1183
1233
|
sage: M.column_degrees(shifts=[-2,1])
|
|
1184
1234
|
[4, -3, -2]
|
|
1185
1235
|
|
|
1186
|
-
The column degrees of
|
|
1187
|
-
|
|
1236
|
+
The column degrees of a column-empty matrix `m\times 0` is an empty
|
|
1237
|
+
list, while those of a row-empty matrix `0\times n` is a list of `n`
|
|
1238
|
+
times `-1`::
|
|
1188
1239
|
|
|
1189
|
-
sage: M =
|
|
1240
|
+
sage: M = matrix(pR, 3, 0)
|
|
1190
1241
|
sage: M.column_degrees()
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1242
|
+
[]
|
|
1243
|
+
sage: M.column_degrees(shifts=[1,2,3])
|
|
1244
|
+
[]
|
|
1194
1245
|
|
|
1195
|
-
sage: M =
|
|
1246
|
+
sage: M = matrix(pR, 0, 3)
|
|
1196
1247
|
sage: M.column_degrees()
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1248
|
+
[-1, -1, -1]
|
|
1249
|
+
sage: M.column_degrees(shifts=[])
|
|
1250
|
+
[-1, -1, -1]
|
|
1200
1251
|
|
|
1201
1252
|
.. SEEALSO::
|
|
1202
1253
|
|
|
1203
1254
|
The documentation of :meth:`row_degrees`.
|
|
1204
1255
|
"""
|
|
1205
1256
|
self._check_shift_dimension(shifts,row_wise=False)
|
|
1206
|
-
if self.
|
|
1207
|
-
|
|
1257
|
+
if self.nrows() == 0: # shifts is None (or empty list [])
|
|
1258
|
+
return [-1]*self.ncols()
|
|
1259
|
+
if self.ncols() == 0:
|
|
1260
|
+
return []
|
|
1208
1261
|
if shifts is None:
|
|
1209
1262
|
return [ max([ self[i,j].degree() for i in range(self.nrows()) ])
|
|
1210
1263
|
for j in range(self.ncols()) ]
|
|
@@ -1251,7 +1304,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
1251
1304
|
EXAMPLES::
|
|
1252
1305
|
|
|
1253
1306
|
sage: pR.<x> = GF(7)[]
|
|
1254
|
-
sage: M =
|
|
1307
|
+
sage: M = matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]])
|
|
1255
1308
|
sage: M.leading_matrix()
|
|
1256
1309
|
[3 0 0]
|
|
1257
1310
|
[1 0 0]
|
|
@@ -1274,31 +1327,40 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
1274
1327
|
sage: M.leading_matrix(shifts=[2,0], row_wise=False)
|
|
1275
1328
|
[3 0 1]
|
|
1276
1329
|
[1 0 0]
|
|
1330
|
+
|
|
1331
|
+
TESTS::
|
|
1332
|
+
|
|
1333
|
+
sage: M = matrix(pR, 0, 3)
|
|
1334
|
+
sage: M.leading_matrix(shifts=[1,2,3])
|
|
1335
|
+
[]
|
|
1336
|
+
|
|
1337
|
+
sage: M.leading_matrix(shifts=[], row_wise=False)
|
|
1338
|
+
[]
|
|
1277
1339
|
"""
|
|
1278
1340
|
self._check_shift_dimension(shifts,row_wise)
|
|
1279
1341
|
from sage.matrix.constructor import matrix
|
|
1280
1342
|
if row_wise:
|
|
1281
1343
|
row_degrees = self.row_degrees(shifts)
|
|
1282
1344
|
if shifts is None:
|
|
1283
|
-
return matrix([
|
|
1345
|
+
return matrix([[self[i,j].leading_coefficient()
|
|
1284
1346
|
if self[i,j].degree() == row_degrees[i] else 0
|
|
1285
|
-
for j in range(self.ncols())
|
|
1286
|
-
for i in range(self.nrows())
|
|
1287
|
-
return matrix([
|
|
1347
|
+
for j in range(self.ncols())]
|
|
1348
|
+
for i in range(self.nrows())])
|
|
1349
|
+
return matrix([[self[i,j].leading_coefficient()
|
|
1288
1350
|
if self[i,j].degree() + shifts[j] == row_degrees[i] else 0
|
|
1289
|
-
for j in range(self.ncols())
|
|
1290
|
-
for i in range(self.nrows())
|
|
1351
|
+
for j in range(self.ncols())]
|
|
1352
|
+
for i in range(self.nrows())])
|
|
1291
1353
|
else:
|
|
1292
1354
|
column_degrees = self.column_degrees(shifts)
|
|
1293
1355
|
if shifts is None:
|
|
1294
|
-
return matrix([
|
|
1356
|
+
return matrix([[self[i,j].leading_coefficient()
|
|
1295
1357
|
if self[i,j].degree() == column_degrees[j] else 0
|
|
1296
|
-
for j in range(self.ncols())
|
|
1297
|
-
for i in range(self.nrows())
|
|
1298
|
-
return matrix([
|
|
1358
|
+
for j in range(self.ncols())]
|
|
1359
|
+
for i in range(self.nrows())])
|
|
1360
|
+
return matrix([[self[i,j].leading_coefficient()
|
|
1299
1361
|
if self[i,j].degree() + shifts[i] == column_degrees[j] else 0
|
|
1300
|
-
for j in range(self.ncols())
|
|
1301
|
-
for i in range(self.nrows())
|
|
1362
|
+
for j in range(self.ncols())]
|
|
1363
|
+
for i in range(self.nrows())])
|
|
1302
1364
|
|
|
1303
1365
|
def _is_empty_popov(self, row_wise=True, include_zero_vectors=True):
|
|
1304
1366
|
r"""
|
|
@@ -1326,13 +1388,13 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
1326
1388
|
EXAMPLES::
|
|
1327
1389
|
|
|
1328
1390
|
sage: pR.<x> = GF(7)[]
|
|
1329
|
-
sage: M =
|
|
1391
|
+
sage: M = matrix(pR, 0, 0)
|
|
1330
1392
|
sage: M._is_empty_popov()
|
|
1331
1393
|
True
|
|
1332
1394
|
sage: M._is_empty_popov(include_zero_vectors=False)
|
|
1333
1395
|
True
|
|
1334
1396
|
|
|
1335
|
-
sage: M =
|
|
1397
|
+
sage: M = matrix(pR, 0, 3)
|
|
1336
1398
|
sage: M._is_empty_popov(include_zero_vectors=False)
|
|
1337
1399
|
True
|
|
1338
1400
|
sage: M._is_empty_popov(row_wise=False)
|
|
@@ -1397,7 +1459,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
1397
1459
|
EXAMPLES::
|
|
1398
1460
|
|
|
1399
1461
|
sage: pR.<x> = GF(7)[]
|
|
1400
|
-
sage: M =
|
|
1462
|
+
sage: M = matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]])
|
|
1401
1463
|
sage: M.is_reduced()
|
|
1402
1464
|
False
|
|
1403
1465
|
|
|
@@ -1411,7 +1473,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
1411
1473
|
....: include_zero_vectors=False)
|
|
1412
1474
|
False
|
|
1413
1475
|
|
|
1414
|
-
sage: M =
|
|
1476
|
+
sage: M = matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0], [0, 1, 0]])
|
|
1415
1477
|
sage: M.is_reduced(shifts=[2,0,0], row_wise=False)
|
|
1416
1478
|
True
|
|
1417
1479
|
|
|
@@ -1422,7 +1484,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
1422
1484
|
"""
|
|
1423
1485
|
self._check_shift_dimension(shifts,row_wise)
|
|
1424
1486
|
if self.ncols() == 0 or self.nrows() == 0:
|
|
1425
|
-
return self._is_empty_popov(row_wise,include_zero_vectors)
|
|
1487
|
+
return self._is_empty_popov(row_wise, include_zero_vectors)
|
|
1426
1488
|
if include_zero_vectors:
|
|
1427
1489
|
number_generators = \
|
|
1428
1490
|
[self[i,:] != 0 for i in range(self.nrows())].count(True) \
|
|
@@ -1477,7 +1539,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
1477
1539
|
EXAMPLES::
|
|
1478
1540
|
|
|
1479
1541
|
sage: pR.<x> = GF(7)[]
|
|
1480
|
-
sage: M =
|
|
1542
|
+
sage: M = matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]])
|
|
1481
1543
|
sage: M.leading_positions()
|
|
1482
1544
|
[0, 0]
|
|
1483
1545
|
|
|
@@ -1506,30 +1568,17 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
1506
1568
|
([1, -1, 0], [3, -1, 0])
|
|
1507
1569
|
|
|
1508
1570
|
The leading positions and pivot degrees of an empty matrix (`0\times n`
|
|
1509
|
-
or `m\times 0`)
|
|
1571
|
+
or `m\times 0`) are taken by convention as follows::
|
|
1510
1572
|
|
|
1511
|
-
sage: M =
|
|
1512
|
-
sage: M.leading_positions()
|
|
1513
|
-
|
|
1514
|
-
...
|
|
1515
|
-
ValueError: empty matrix does not have leading positions
|
|
1516
|
-
|
|
1517
|
-
sage: M.leading_positions(row_wise=False)
|
|
1518
|
-
Traceback (most recent call last):
|
|
1519
|
-
...
|
|
1520
|
-
ValueError: empty matrix does not have leading positions
|
|
1573
|
+
sage: M = matrix(pR, 0, 3)
|
|
1574
|
+
sage: M.leading_positions(return_degree=True)
|
|
1575
|
+
([], [])
|
|
1521
1576
|
|
|
1522
|
-
sage: M
|
|
1523
|
-
|
|
1524
|
-
Traceback (most recent call last):
|
|
1525
|
-
...
|
|
1526
|
-
ValueError: empty matrix does not have leading positions
|
|
1577
|
+
sage: M.leading_positions(shifts=[], row_wise=False, return_degree=True)
|
|
1578
|
+
([-1, -1, -1], [-1, -1, -1])
|
|
1527
1579
|
"""
|
|
1528
1580
|
self._check_shift_dimension(shifts,row_wise)
|
|
1529
1581
|
|
|
1530
|
-
if self.ncols() == 0 or self.nrows() == 0:
|
|
1531
|
-
raise ValueError('empty matrix does not have leading positions')
|
|
1532
|
-
|
|
1533
1582
|
if row_wise:
|
|
1534
1583
|
row_degrees = self.row_degrees(shifts)
|
|
1535
1584
|
if shifts is None:
|
|
@@ -1538,7 +1587,9 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
1538
1587
|
(self[i,j].degree() == row_degrees[i]) ] ))
|
|
1539
1588
|
for i in range(self.nrows()) ]
|
|
1540
1589
|
else:
|
|
1541
|
-
zero_degree=
|
|
1590
|
+
zero_degree = -1
|
|
1591
|
+
if len(shifts) > 0:
|
|
1592
|
+
zero_degree += min(shifts)
|
|
1542
1593
|
pivot_index = [ (-1 if row_degrees[i] == zero_degree else
|
|
1543
1594
|
max( [ j for j in range(self.ncols()) if
|
|
1544
1595
|
(self[i,j] != 0 and
|
|
@@ -1557,7 +1608,9 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
1557
1608
|
(self[i,j].degree() == column_degrees[j]) ] ))
|
|
1558
1609
|
for j in range(self.ncols()) ]
|
|
1559
1610
|
else:
|
|
1560
|
-
zero_degree=
|
|
1611
|
+
zero_degree = -1
|
|
1612
|
+
if len(shifts) > 0:
|
|
1613
|
+
zero_degree += min(shifts)
|
|
1561
1614
|
pivot_index = [ (-1 if column_degrees[j] == zero_degree else
|
|
1562
1615
|
max( [ i for i in range(self.nrows()) if
|
|
1563
1616
|
(self[i,j] != 0 and
|
|
@@ -1609,7 +1662,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
1609
1662
|
EXAMPLES::
|
|
1610
1663
|
|
|
1611
1664
|
sage: pR.<x> = GF(7)[]
|
|
1612
|
-
sage: M =
|
|
1665
|
+
sage: M = matrix([ [x^3+3*x^2+6*x+6, 3*x^2+3*x+6, 4*x^2+x+3],
|
|
1613
1666
|
....: [5, 1, 0 ],
|
|
1614
1667
|
....: [2*x^2+2, 2*x+5, x^2+4*x+6] ])
|
|
1615
1668
|
sage: M.is_weak_popov()
|
|
@@ -1638,7 +1691,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
1638
1691
|
|
|
1639
1692
|
Rectangular matrices are supported::
|
|
1640
1693
|
|
|
1641
|
-
sage: M =
|
|
1694
|
+
sage: M = matrix([
|
|
1642
1695
|
....: [ x^3+5*x^2+5*x+1, 5, 6*x+4, 0],
|
|
1643
1696
|
....: [ 6*x^2+3*x+1, 1, 2, 0],
|
|
1644
1697
|
....: [2*x^3+4*x^2+6*x+4, 5*x + 1, 2*x^2+5*x+5, x^2+5*x+6]
|
|
@@ -1651,7 +1704,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
1651
1704
|
|
|
1652
1705
|
Zero rows (resp. columns) can be forbidden::
|
|
1653
1706
|
|
|
1654
|
-
sage: M =
|
|
1707
|
+
sage: M = matrix([
|
|
1655
1708
|
....: [ 6*x+4, 0, 5*x+1, 0],
|
|
1656
1709
|
....: [ 2, 5*x + 1, 6*x^2+3*x+1, 0],
|
|
1657
1710
|
....: [2*x^2+5*x+5, 1, 2*x^3+4*x^2+6*x+4, 0]
|
|
@@ -1670,7 +1723,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
1670
1723
|
"""
|
|
1671
1724
|
self._check_shift_dimension(shifts,row_wise)
|
|
1672
1725
|
if self.ncols() == 0 or self.nrows() == 0:
|
|
1673
|
-
return self._is_empty_popov(row_wise)
|
|
1726
|
+
return self._is_empty_popov(row_wise, include_zero_vectors)
|
|
1674
1727
|
leading_positions = self.leading_positions(shifts, row_wise)
|
|
1675
1728
|
# here, because of the below sorting and of the convention that zero
|
|
1676
1729
|
# rows (resp. columns) are at the bottom (resp. right) of the matrix in
|
|
@@ -1749,7 +1802,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
1749
1802
|
EXAMPLES::
|
|
1750
1803
|
|
|
1751
1804
|
sage: pR.<x> = GF(7)[]
|
|
1752
|
-
sage: M =
|
|
1805
|
+
sage: M = matrix(pR, [[x^4+6*x^3+4*x+4, 3*x+6, 3 ],
|
|
1753
1806
|
....: [x^2+6*x+6, x^2+5*x+5, 2 ],
|
|
1754
1807
|
....: [3*x, 6*x+5, x+5]])
|
|
1755
1808
|
sage: M.is_popov()
|
|
@@ -1764,7 +1817,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
1764
1817
|
sage: M[:2,:].is_popov(shifts=[0,1,2])
|
|
1765
1818
|
True
|
|
1766
1819
|
|
|
1767
|
-
sage: M =
|
|
1820
|
+
sage: M = matrix(pR, [[x^4+3*x^3+x^2+2*x+6, x^3+5*x^2+5*x+1],
|
|
1768
1821
|
....: [6*x+1, x^2+4*x+1 ],
|
|
1769
1822
|
....: [6, 6 ]])
|
|
1770
1823
|
sage: M.is_popov(row_wise=False)
|
|
@@ -1775,7 +1828,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
1775
1828
|
|
|
1776
1829
|
One can forbid zero rows (or columns if not working row-wise)::
|
|
1777
1830
|
|
|
1778
|
-
sage: N =
|
|
1831
|
+
sage: N = matrix(pR, [[x^4+3*x^3+x^2+2*x+6, 6*x+1 ],
|
|
1779
1832
|
....: [5*x^2+5*x+1, x^2+4*x+1 ],
|
|
1780
1833
|
....: [0, 0 ]])
|
|
1781
1834
|
|
|
@@ -1807,7 +1860,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
1807
1860
|
# the matrix should be in weak Popov form (ordered except if
|
|
1808
1861
|
# up_to_permutation==True)
|
|
1809
1862
|
if self.ncols() == 0 or self.nrows() == 0:
|
|
1810
|
-
return self._is_empty_popov(row_wise)
|
|
1863
|
+
return self._is_empty_popov(row_wise, include_zero_vectors)
|
|
1811
1864
|
if not self.is_weak_popov(shifts,
|
|
1812
1865
|
row_wise,
|
|
1813
1866
|
not up_to_permutation,
|
|
@@ -1887,7 +1940,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
1887
1940
|
EXAMPLES::
|
|
1888
1941
|
|
|
1889
1942
|
sage: pR.<x> = GF(7)[]
|
|
1890
|
-
sage: M =
|
|
1943
|
+
sage: M = matrix(pR, [[x^4+6*x^3+4*x+4, 3*x+6, 3 ],
|
|
1891
1944
|
....: [0, x^2+5*x+5, 2 ],
|
|
1892
1945
|
....: [0, 0, x+5]])
|
|
1893
1946
|
|
|
@@ -1898,7 +1951,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
1898
1951
|
sage: M.is_hermite(row_wise=False, lower_echelon=True)
|
|
1899
1952
|
False
|
|
1900
1953
|
|
|
1901
|
-
sage: N =
|
|
1954
|
+
sage: N = matrix(pR, [[x+5, 0, 0 ],
|
|
1902
1955
|
....: [2, x^4+6*x^3+4*x+4, 0 ],
|
|
1903
1956
|
....: [3, 3*x^3+6, x^2+5*x+5]])
|
|
1904
1957
|
sage: N.is_hermite()
|
|
@@ -1928,7 +1981,22 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
1928
1981
|
.. SEEALSO::
|
|
1929
1982
|
|
|
1930
1983
|
:meth:`hermite_form` .
|
|
1984
|
+
|
|
1985
|
+
TESTS::
|
|
1986
|
+
|
|
1987
|
+
sage: M = matrix(pR, 0, 3)
|
|
1988
|
+
sage: M.is_hermite()
|
|
1989
|
+
True
|
|
1990
|
+
|
|
1991
|
+
sage: M.is_hermite(row_wise=False)
|
|
1992
|
+
True
|
|
1993
|
+
|
|
1994
|
+
sage: M.is_hermite(row_wise=False, include_zero_vectors=False)
|
|
1995
|
+
False
|
|
1931
1996
|
"""
|
|
1997
|
+
# empty matrices
|
|
1998
|
+
if self.ncols() == 0 or self.nrows() == 0:
|
|
1999
|
+
return self._is_empty_popov(row_wise, include_zero_vectors=include_zero_vectors)
|
|
1932
2000
|
# shift for lower echelon
|
|
1933
2001
|
shift = [j*(self.degree() + 1) for j in range(self.ncols())] \
|
|
1934
2002
|
if row_wise else \
|
|
@@ -2002,7 +2070,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
2002
2070
|
EXAMPLES::
|
|
2003
2071
|
|
|
2004
2072
|
sage: pR.<x> = GF(7)[]
|
|
2005
|
-
sage: M =
|
|
2073
|
+
sage: M = matrix(pR, [
|
|
2006
2074
|
....: [ 6*x+4, 5*x^3+5*x, 6*x^2+2*x+2],
|
|
2007
2075
|
....: [4*x^2+5*x+2, x^4+5*x^2+2*x+4, 4*x^3+6*x^2+6*x+5]])
|
|
2008
2076
|
sage: P, U = M.weak_popov_form(transformation=True)
|
|
@@ -2058,6 +2126,20 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
2058
2126
|
sage: M*U[:,:2] == P and (M*U[:,2]).is_zero()
|
|
2059
2127
|
True
|
|
2060
2128
|
|
|
2129
|
+
Empty matrices are supported::
|
|
2130
|
+
|
|
2131
|
+
sage: M = matrix.random(pR, 0, 3)
|
|
2132
|
+
sage: M.weak_popov_form()
|
|
2133
|
+
[]
|
|
2134
|
+
sage: M.weak_popov_form(transformation=True)
|
|
2135
|
+
([], [])
|
|
2136
|
+
sage: M.weak_popov_form(row_wise=False, transformation=True)
|
|
2137
|
+
(
|
|
2138
|
+
[1 0 0]
|
|
2139
|
+
[0 1 0]
|
|
2140
|
+
[], [0 0 1]
|
|
2141
|
+
)
|
|
2142
|
+
|
|
2061
2143
|
.. SEEALSO::
|
|
2062
2144
|
|
|
2063
2145
|
:meth:`is_weak_popov` ,
|
|
@@ -2176,8 +2258,8 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
2176
2258
|
one = R.one()
|
|
2177
2259
|
|
|
2178
2260
|
if transformation:
|
|
2179
|
-
from sage.matrix.constructor import
|
|
2180
|
-
U =
|
|
2261
|
+
from sage.matrix.constructor import matrix
|
|
2262
|
+
U = matrix.identity(R, m)
|
|
2181
2263
|
|
|
2182
2264
|
# initialise to_row and conflicts list
|
|
2183
2265
|
to_row = [[] for i in range(n)]
|
|
@@ -2287,7 +2369,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
2287
2369
|
EXAMPLES::
|
|
2288
2370
|
|
|
2289
2371
|
sage: pR.<x> = GF(7)[]
|
|
2290
|
-
sage: M =
|
|
2372
|
+
sage: M = matrix(pR, [
|
|
2291
2373
|
....: [ 6*x+4, 5*x^3+5*x, 6*x^2+2*x+2],
|
|
2292
2374
|
....: [4*x^2+5*x+2, x^4+5*x^2+2*x+4, 4*x^3+6*x^2+6*x+5]])
|
|
2293
2375
|
|
|
@@ -2653,11 +2735,11 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
2653
2735
|
EXAMPLES::
|
|
2654
2736
|
|
|
2655
2737
|
sage: pR.<x> = GF(7)[]
|
|
2656
|
-
sage: A =
|
|
2738
|
+
sage: A = matrix(pR, 3, 2,
|
|
2657
2739
|
....: [[ 3*x^3 + 3*x, 2*x^3 + 4],
|
|
2658
2740
|
....: [ 3*x^3 + 6*x + 5, 6*x^3 + 5*x^2 + 1],
|
|
2659
2741
|
....: [ 2*x^3 + 2*x + 6, 3*x^2 + 2*x + 2]])
|
|
2660
|
-
sage: B =
|
|
2742
|
+
sage: B = matrix(pR, 3, 3,
|
|
2661
2743
|
....: [[ 3, x + 3, 6],
|
|
2662
2744
|
....: [3*x^3 + 3*x + 1, 4*x^2 + 3*x, 6*x^3 + x + 4],
|
|
2663
2745
|
....: [ 4*x^2 + x + 4, 3*x^2 + 4*x, 3*x^2 + 3*x + 2]])
|
|
@@ -2748,10 +2830,10 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
2748
2830
|
Case where `B` is a square, column reduced matrix::
|
|
2749
2831
|
|
|
2750
2832
|
sage: pR.<x> = GF(7)[]
|
|
2751
|
-
sage: A =
|
|
2833
|
+
sage: A = matrix(pR, 2, 3,
|
|
2752
2834
|
....: [[3*x^3 + 3*x, 3*x^3 + 6*x + 5, 2*x^3 + 2*x + 6],
|
|
2753
2835
|
....: [2*x^3 + 4, 6*x^3 + 5*x^2 + 1, 3*x^2 + 2*x + 2]])
|
|
2754
|
-
sage: B =
|
|
2836
|
+
sage: B = matrix(pR, 3, 3,
|
|
2755
2837
|
....: [[4*x^2 + 3*x + 3, 3*x^2 + 3*x + 1, 4*x^2 + x + 4],
|
|
2756
2838
|
....: [6*x^2 + 2*x + 3, 4*x^2 + 3*x, 3*x^2 + 4*x],
|
|
2757
2839
|
....: [5*x^2 + 3*x + 6, 6*x^2 + x + 4, 3*x^2 + 3*x + 2]])
|
|
@@ -2770,7 +2852,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
2770
2852
|
ValueError: column dimension of self should be the column dimension
|
|
2771
2853
|
of the input matrix
|
|
2772
2854
|
|
|
2773
|
-
sage: B =
|
|
2855
|
+
sage: B = matrix(pR, 3, 3,
|
|
2774
2856
|
....: [[3, 3*x^3 + 3*x + 1, 4*x^2 + x + 4],
|
|
2775
2857
|
....: [x + 3, 4*x^2 + 3*x, 3*x^2 + 4*x],
|
|
2776
2858
|
....: [6, 6*x^3 + x + 4, 3*x^2 + 3*x + 2]])
|
|
@@ -2791,7 +2873,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
2791
2873
|
With a nonsingular but also non-reduced matrix, there exists a
|
|
2792
2874
|
solution, but it might not be unique::
|
|
2793
2875
|
|
|
2794
|
-
sage: B =
|
|
2876
|
+
sage: B = matrix(pR, 3, 3,
|
|
2795
2877
|
....: [[ 5, 0, 2*x + 6],
|
|
2796
2878
|
....: [ 4*x, 3*x^2 + 4*x + 5, x + 1],
|
|
2797
2879
|
....: [3*x^2 + 5*x + 2, 6*x^3 + 4*x + 6, 3]])
|
|
@@ -2809,10 +2891,10 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
2809
2891
|
sage: A == Q*B+R and all(cdegR[i] < cdegB[i] for i in range(3))
|
|
2810
2892
|
True
|
|
2811
2893
|
|
|
2812
|
-
sage: Q2 =
|
|
2894
|
+
sage: Q2 = matrix(pR, 2, 3,
|
|
2813
2895
|
....: [[6*x^2 + 3*x + 1, 4*x^2 + 3*x + 6, 5*x + 1],
|
|
2814
2896
|
....: [ x^2 + 5*x + 3, 5*x^2 + 3*x + 2, x + 2]])
|
|
2815
|
-
sage: R2 =
|
|
2897
|
+
sage: R2 = matrix(pR, 2, 3,
|
|
2816
2898
|
....: [[ 5*x, 3*x + 4, 5],
|
|
2817
2899
|
....: [4*x + 6, 5*x, 4]])
|
|
2818
2900
|
sage: A == Q2*B + R2
|
|
@@ -2850,17 +2932,47 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
2850
2932
|
remainder, in which case this method will find it via normal form
|
|
2851
2933
|
computation::
|
|
2852
2934
|
|
|
2853
|
-
sage: B =
|
|
2854
|
-
sage: A =
|
|
2935
|
+
sage: B = matrix(pR, 1, 2, [[x, x]])
|
|
2936
|
+
sage: A = matrix(pR, 1, 2, [[x, x+2]])
|
|
2855
2937
|
sage: A.right_quo_rem(B)
|
|
2856
2938
|
([1], [0 2])
|
|
2857
|
-
sage: A == 1*B +
|
|
2939
|
+
sage: A == 1*B + matrix([[0,2]])
|
|
2858
2940
|
True
|
|
2859
2941
|
|
|
2860
2942
|
.. SEEALSO::
|
|
2861
2943
|
|
|
2862
2944
|
:meth:`left_quo_rem` ,
|
|
2863
2945
|
:meth:`reduce` .
|
|
2946
|
+
|
|
2947
|
+
TESTS::
|
|
2948
|
+
|
|
2949
|
+
sage: M = matrix(pR, 0, 3)
|
|
2950
|
+
sage: Q,R = M.right_quo_rem(matrix(pR, 0, 3)); Q,R
|
|
2951
|
+
([], [])
|
|
2952
|
+
sage: Q.dimensions(), R.dimensions()
|
|
2953
|
+
((0, 0), (0, 3))
|
|
2954
|
+
|
|
2955
|
+
sage: Q,R = M.right_quo_rem(matrix.identity(pR, 3)); Q,R
|
|
2956
|
+
([], [])
|
|
2957
|
+
sage: Q.dimensions(), R.dimensions()
|
|
2958
|
+
((0, 3), (0, 3))
|
|
2959
|
+
|
|
2960
|
+
sage: Q,R = M.right_quo_rem(matrix.ones(pR, 4, 3)); Q,R
|
|
2961
|
+
([], [])
|
|
2962
|
+
sage: Q.dimensions(), R.dimensions()
|
|
2963
|
+
((0, 4), (0, 3))
|
|
2964
|
+
|
|
2965
|
+
sage: M = matrix(pR, 3, 0)
|
|
2966
|
+
sage: M.right_quo_rem(matrix(pR, 4, 0))
|
|
2967
|
+
(
|
|
2968
|
+
[0 0 0 0]
|
|
2969
|
+
[0 0 0 0]
|
|
2970
|
+
[0 0 0 0], []
|
|
2971
|
+
)
|
|
2972
|
+
|
|
2973
|
+
sage: M = matrix(pR, 0, 0)
|
|
2974
|
+
sage: M.right_quo_rem(matrix(pR, 0, 0))
|
|
2975
|
+
([], [])
|
|
2864
2976
|
"""
|
|
2865
2977
|
if self.ncols() != B.ncols():
|
|
2866
2978
|
raise ValueError("column dimension of self should be the"
|
|
@@ -2903,10 +3015,10 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
2903
3015
|
EXAMPLES::
|
|
2904
3016
|
|
|
2905
3017
|
sage: pR.<x> = GF(7)[]
|
|
2906
|
-
sage: A =
|
|
3018
|
+
sage: A = matrix(pR, 2, 3,
|
|
2907
3019
|
....: [[3*x^3 + 3*x, 3*x^3 + 6*x + 5, 2*x^3 + 2*x + 6],
|
|
2908
3020
|
....: [2*x^3 + 4, 6*x^3 + 5*x^2 + 1, 3*x^2 + 2*x + 2]])
|
|
2909
|
-
sage: B =
|
|
3021
|
+
sage: B = matrix(pR, 3, 3,
|
|
2910
3022
|
....: [[4*x^2 + 3*x + 3, 3*x^2 + 3*x + 1, 4*x^2 + x + 4],
|
|
2911
3023
|
....: [6*x^2 + 2*x + 3, 4*x^2 + 3*x, 3*x^2 + 4*x],
|
|
2912
3024
|
....: [5*x^2 + 3*x + 6, 6*x^2 + x + 4, 3*x^2 + 3*x + 2]])
|
|
@@ -2920,7 +3032,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
2920
3032
|
sage: A == Q*B+R and R.degree() < 2
|
|
2921
3033
|
True
|
|
2922
3034
|
|
|
2923
|
-
sage: B =
|
|
3035
|
+
sage: B = matrix(pR, 3, 3,
|
|
2924
3036
|
....: [[4*x + 3*x + 3, 3*x^3 + 3*x + 1, 4*x^2 + x + 4],
|
|
2925
3037
|
....: [6*x + 2*x + 3, 4*x^2 + 3*x, 3*x^2 + 4*x],
|
|
2926
3038
|
....: [6, 6*x^3 + x + 4, 3*x^2 + 3*x + 2]])
|
|
@@ -2941,7 +3053,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
2941
3053
|
# Step 0: find parameter d (delta in above reference)
|
|
2942
3054
|
cdegA = self.column_degrees() # zero columns of A --> entries -1 in cdegA
|
|
2943
3055
|
cdeg = B.column_degrees() # all nonnegative since column reduced
|
|
2944
|
-
d = max([cdegA[i]-cdeg[i]+1 for i in range(B.nrows())])
|
|
3056
|
+
d = -1 if B.nrows() == 0 else max([cdegA[i]-cdeg[i]+1 for i in range(B.nrows())])
|
|
2945
3057
|
if d<=0: # A already reduced modulo B, quotient is zero
|
|
2946
3058
|
return (self.parent().zero().__copy__(), self)
|
|
2947
3059
|
# Step 1: reverse input matrices
|
|
@@ -2981,10 +3093,10 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
2981
3093
|
EXAMPLES::
|
|
2982
3094
|
|
|
2983
3095
|
sage: pR.<x> = GF(7)[]
|
|
2984
|
-
sage: A =
|
|
3096
|
+
sage: A = matrix(pR, 2, 3,
|
|
2985
3097
|
....: [[3*x^3 + 3*x, 3*x^3 + 6*x + 5, 2*x^3 + 2*x + 6],
|
|
2986
3098
|
....: [2*x^3 + 4, 6*x^3 + 5*x^2 + 1, 3*x^2 + 2*x + 2]])
|
|
2987
|
-
sage: B =
|
|
3099
|
+
sage: B = matrix(pR, 3, 3,
|
|
2988
3100
|
....: [[4*x + 3*x + 3, 3*x^3 + 3*x + 1, 4*x^2 + x + 4],
|
|
2989
3101
|
....: [6*x + 2*x + 3, 4*x^2 + 3*x, 3*x^2 + 4*x],
|
|
2990
3102
|
....: [6, 6*x^3 + x + 4, 3*x^2 + 3*x + 2]])
|
|
@@ -3005,7 +3117,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
3005
3117
|
With a nonsingular but also non-reduced matrix, there exists a solution
|
|
3006
3118
|
and one is found by this method, but it might not be unique::
|
|
3007
3119
|
|
|
3008
|
-
sage: B =
|
|
3120
|
+
sage: B = matrix(pR, 3, 3,
|
|
3009
3121
|
....: [[ 5, 0, 2*x + 6],
|
|
3010
3122
|
....: [ 4*x, 3*x^2 + 4*x + 5, x + 1],
|
|
3011
3123
|
....: [3*x^2 + 5*x + 2, 6*x^3 + 4*x + 6, 3]])
|
|
@@ -3023,10 +3135,10 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
3023
3135
|
sage: A == Q*B+R and all(cdegR[i] < cdegB[i] for i in range(3))
|
|
3024
3136
|
True
|
|
3025
3137
|
|
|
3026
|
-
sage: Q2 =
|
|
3138
|
+
sage: Q2 = matrix(pR, 2, 3,
|
|
3027
3139
|
....: [[6*x^2 + 3*x + 1, 4*x^2 + 3*x + 6, 5*x + 1],
|
|
3028
3140
|
....: [ x^2 + 5*x + 3, 5*x^2 + 3*x + 2, x + 2]])
|
|
3029
|
-
sage: R2 =
|
|
3141
|
+
sage: R2 = matrix(pR, 2, 3,
|
|
3030
3142
|
....: [[ 5*x, 3*x + 4, 5],
|
|
3031
3143
|
....: [4*x + 6, 5*x, 4]])
|
|
3032
3144
|
sage: A == Q2*B + R2
|
|
@@ -3061,9 +3173,9 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
3061
3173
|
columns), even when there is a solution, this method might not find
|
|
3062
3174
|
it::
|
|
3063
3175
|
|
|
3064
|
-
sage: B =
|
|
3065
|
-
sage: A =
|
|
3066
|
-
sage: A == 1*B +
|
|
3176
|
+
sage: B = matrix(pR, 1, 2, [[x, x]])
|
|
3177
|
+
sage: A = matrix(pR, 1, 2, [[x, x+2]])
|
|
3178
|
+
sage: A == 1*B + matrix([[0,2]]) # a valid quo_rem
|
|
3067
3179
|
True
|
|
3068
3180
|
sage: A._right_quo_rem_solve(B)
|
|
3069
3181
|
Traceback (most recent call last):
|
|
@@ -3147,10 +3259,10 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
3147
3259
|
EXAMPLES::
|
|
3148
3260
|
|
|
3149
3261
|
sage: pR.<x> = GF(7)[]
|
|
3150
|
-
sage: B =
|
|
3262
|
+
sage: B = matrix(pR, [
|
|
3151
3263
|
....: [ 6*x+4, 5*x^3+5*x, 6*x^2+2*x+2],
|
|
3152
3264
|
....: [4*x^2+5*x+2, x^4+5*x^2+2*x+4, 4*x^3+6*x^2+6*x+5]])
|
|
3153
|
-
sage: A =
|
|
3265
|
+
sage: A = matrix(pR, 1, 3, [
|
|
3154
3266
|
....: [3*x^4+3*x^3+4*x^2+5*x+1, x^4+x^3+5*x^2+4*x+4, 4*x^4+2*x^3+x]])
|
|
3155
3267
|
|
|
3156
3268
|
sage: Q, R = A.reduce(B,return_quotient=True); R
|
|
@@ -3192,7 +3304,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
3192
3304
|
several columns, and a matrix `B` which does not have full column rank
|
|
3193
3305
|
(its column-wise Popov form has a zero column)::
|
|
3194
3306
|
|
|
3195
|
-
sage: A =
|
|
3307
|
+
sage: A = matrix(pR, 2, 2,
|
|
3196
3308
|
....: [[5*x^3 + 2*x^2 + 4*x + 1, x^3 + 4*x + 4],
|
|
3197
3309
|
....: [2*x^3 + 5*x^2 + 2*x + 4, 2*x^3 + 3*x + 2]])
|
|
3198
3310
|
sage: (Q,R) = A.reduce(B,row_wise=False, return_quotient=True); R
|
|
@@ -3311,13 +3423,13 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
3311
3423
|
computing minimal approximant bases, 2006]::
|
|
3312
3424
|
|
|
3313
3425
|
sage: order = 8; shifts = [1,1,0,0,0]
|
|
3314
|
-
sage: pmat =
|
|
3426
|
+
sage: pmat = matrix(pR, 5, 1, [
|
|
3315
3427
|
....: pR([35, 0, 41, 87, 3, 42, 22, 90]),
|
|
3316
3428
|
....: pR([80, 15, 62, 87, 14, 93, 24, 0]),
|
|
3317
3429
|
....: pR([42, 57, 90, 87, 22, 80, 71, 53]),
|
|
3318
3430
|
....: pR([37, 72, 74, 6, 5, 75, 23, 47]),
|
|
3319
3431
|
....: pR([36, 10, 74, 1, 29, 44, 87, 74])])
|
|
3320
|
-
sage: appbas =
|
|
3432
|
+
sage: appbas = matrix(pR, [
|
|
3321
3433
|
....: [x+47, 57, 58*x+44, 9*x+23, 93*x+76],
|
|
3322
3434
|
....: [ 15, x+18, 52*x+23, 15*x+58, 93*x+88],
|
|
3323
3435
|
....: [ 17, 86, x^2+77*x+16, 76*x+29, 90*x+78],
|
|
@@ -3332,7 +3444,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
3332
3444
|
not an approximant basis since its rows generate a module strictly
|
|
3333
3445
|
contained in the set of approximants for ``pmat`` at order 8::
|
|
3334
3446
|
|
|
3335
|
-
sage: M = x^8 *
|
|
3447
|
+
sage: M = x^8 * matrix.identity(pR, 5)
|
|
3336
3448
|
sage: M.is_minimal_approximant_basis(pmat, 8) # needs sage.libs.pari
|
|
3337
3449
|
False
|
|
3338
3450
|
|
|
@@ -3340,7 +3452,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
3340
3452
|
its column-wise approximant bases at order 8 are all `1\times 1`
|
|
3341
3453
|
matrices `[c x^8]` for some nonzero field element `c`::
|
|
3342
3454
|
|
|
3343
|
-
sage: M =
|
|
3455
|
+
sage: M = matrix(pR, [x^8])
|
|
3344
3456
|
sage: M.is_minimal_approximant_basis(
|
|
3345
3457
|
....: pmat, 8, row_wise=False, normal_form=True)
|
|
3346
3458
|
True
|
|
@@ -3360,7 +3472,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
3360
3472
|
ValueError: shifts length should be the column dimension
|
|
3361
3473
|
of the input matrix
|
|
3362
3474
|
|
|
3363
|
-
sage:
|
|
3475
|
+
sage: matrix(pR, [x^8]).is_minimal_approximant_basis(pmat, 8)
|
|
3364
3476
|
Traceback (most recent call last):
|
|
3365
3477
|
...
|
|
3366
3478
|
ValueError: column dimension should be the row dimension of the
|
|
@@ -3465,15 +3577,18 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
3465
3577
|
normal_form=False):
|
|
3466
3578
|
r"""
|
|
3467
3579
|
Return an approximant basis in ``shifts``-ordered weak Popov form for
|
|
3468
|
-
this polynomial matrix at order ``order``.
|
|
3580
|
+
this polynomial matrix at order ``order``. This is a direct extension
|
|
3581
|
+
of the so-called Hermite-Padé approximation, which corresponds to the
|
|
3582
|
+
case where ``self`` is a single vector.
|
|
3469
3583
|
|
|
3470
3584
|
Assuming we work row-wise, if `F` is an `m \times n` polynomial matrix
|
|
3471
|
-
and `(d_0,\ldots,d_{n-1})` are
|
|
3472
|
-
|
|
3473
|
-
|
|
3585
|
+
and `(d_0,\ldots,d_{n-1})` are integers, then an approximant basis for
|
|
3586
|
+
`F` at order `(d_0,\ldots,d_{n-1})` is a polynomial matrix whose rows
|
|
3587
|
+
form a basis of the module of approximants for `F` at order
|
|
3474
3588
|
`(d_0,\ldots,d_{n-1})`. The latter approximants are the polynomial
|
|
3475
3589
|
vectors `p` of size `m` such that the column `j` of `p F` has valuation
|
|
3476
|
-
at least `d_j`, for all `0 \le j \le n-1
|
|
3590
|
+
at least `d_j`, for all `0 \le j \le n-1` (for `j` such that `d_j \le
|
|
3591
|
+
0`, this constraint is void).
|
|
3477
3592
|
|
|
3478
3593
|
If ``normal_form`` is ``True``, then the output basis `P` is
|
|
3479
3594
|
furthermore in ``shifts``-Popov form. By default, `P` is considered
|
|
@@ -3493,7 +3608,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
3493
3608
|
|
|
3494
3609
|
INPUT:
|
|
3495
3610
|
|
|
3496
|
-
- ``order`` -- list of
|
|
3611
|
+
- ``order`` -- list of integers, or an integer
|
|
3497
3612
|
|
|
3498
3613
|
- ``shifts`` -- (default: ``None``) list of integers;
|
|
3499
3614
|
``None`` is interpreted as ``shifts=[0,...,0]``
|
|
@@ -3519,7 +3634,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
3519
3634
|
sage: pR.<x> = GF(7)[]
|
|
3520
3635
|
|
|
3521
3636
|
sage: order = [4, 3]; shifts = [-1, 2, 0]
|
|
3522
|
-
sage: F =
|
|
3637
|
+
sage: F = matrix(pR, [[5*x^3 + 4*x^2 + 4*x + 6, 5*x^2 + 4*x + 1],
|
|
3523
3638
|
....: [ 2*x^2 + 2*x + 3, 6*x^2 + 6*x + 3],
|
|
3524
3639
|
....: [4*x^3 + x + 1, 4*x^2 + 2*x + 3]])
|
|
3525
3640
|
sage: P = F.minimal_approximant_basis(order, shifts)
|
|
@@ -3543,7 +3658,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
3543
3658
|
give a single integer::
|
|
3544
3659
|
|
|
3545
3660
|
sage: (F.minimal_approximant_basis(3) ==
|
|
3546
|
-
....: F.minimal_approximant_basis([3,3], shifts=
|
|
3661
|
+
....: F.minimal_approximant_basis([3,3], shifts=[0,0,0]))
|
|
3547
3662
|
True
|
|
3548
3663
|
|
|
3549
3664
|
One can work column-wise by specifying ``row_wise=False``::
|
|
@@ -3558,6 +3673,15 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
3558
3673
|
....: 3, row_wise=False).transpose())
|
|
3559
3674
|
True
|
|
3560
3675
|
|
|
3676
|
+
Zero or negative order entries are supported, and amount to ignoring
|
|
3677
|
+
the corresponding column of ``self`` (or corresponding row, if column-wise)::
|
|
3678
|
+
|
|
3679
|
+
sage: P = F.minimal_approximant_basis([4, 0, 3], row_wise=False)
|
|
3680
|
+
sage: P == F.minimal_approximant_basis([4, -2, 3], row_wise=False)
|
|
3681
|
+
True
|
|
3682
|
+
sage: P == F[[0,2],:].minimal_approximant_basis([4,3], row_wise=False)
|
|
3683
|
+
True
|
|
3684
|
+
|
|
3561
3685
|
Errors are raised if the input dimensions are not sound::
|
|
3562
3686
|
|
|
3563
3687
|
sage: P = F.minimal_approximant_basis([4], shifts)
|
|
@@ -3570,12 +3694,9 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
3570
3694
|
...
|
|
3571
3695
|
ValueError: shifts length should be the row dimension
|
|
3572
3696
|
|
|
3573
|
-
|
|
3697
|
+
.. SEEALSO::
|
|
3574
3698
|
|
|
3575
|
-
|
|
3576
|
-
Traceback (most recent call last):
|
|
3577
|
-
...
|
|
3578
|
-
ValueError: order should consist of positive integers
|
|
3699
|
+
:meth:`minimal_interpolant_basis`, :meth:`minimal_relation_basis`
|
|
3579
3700
|
"""
|
|
3580
3701
|
m = self.nrows()
|
|
3581
3702
|
n = self.ncols()
|
|
@@ -3597,10 +3718,6 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
3597
3718
|
elif (not row_wise) and len(order) != m:
|
|
3598
3719
|
raise ValueError("order length should be the row dimension")
|
|
3599
3720
|
|
|
3600
|
-
for o in order:
|
|
3601
|
-
if o < 1:
|
|
3602
|
-
raise ValueError("order should consist of positive integers")
|
|
3603
|
-
|
|
3604
3721
|
# compute approximant basis
|
|
3605
3722
|
# if required, normalize it into shifted Popov form
|
|
3606
3723
|
if row_wise:
|
|
@@ -3611,21 +3728,18 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
3611
3728
|
# Note: -deg(P[i,i]) = shifts[i] - rdeg[i]
|
|
3612
3729
|
degree_shifts = [shifts[i] - rdeg[i] for i in range(m)]
|
|
3613
3730
|
# compute approximant basis with that list as shifts
|
|
3614
|
-
P,rdeg = self._approximant_basis_iterative(order,
|
|
3615
|
-
degree_shifts)
|
|
3731
|
+
P,rdeg = self._approximant_basis_iterative(order, degree_shifts)
|
|
3616
3732
|
# left-multiply by inverse of leading matrix
|
|
3617
3733
|
lmat = P.leading_matrix(shifts=degree_shifts)
|
|
3618
3734
|
P = lmat.inverse() * P
|
|
3619
3735
|
else:
|
|
3620
|
-
P,rdeg = self.transpose()._approximant_basis_iterative(order,
|
|
3621
|
-
shifts)
|
|
3736
|
+
P,rdeg = self.transpose()._approximant_basis_iterative(order, shifts)
|
|
3622
3737
|
if normal_form:
|
|
3623
3738
|
# compute the list "- pivot degree"
|
|
3624
3739
|
# (since weak Popov, pivot degree is rdeg-shifts entrywise)
|
|
3625
3740
|
degree_shifts = [shifts[i] - rdeg[i] for i in range(n)]
|
|
3626
3741
|
# compute approximant basis with that list as shifts
|
|
3627
|
-
P, rdeg = self.
|
|
3628
|
-
order, degree_shifts)
|
|
3742
|
+
P, rdeg = self.T._approximant_basis_iterative(order, degree_shifts)
|
|
3629
3743
|
P = P.transpose()
|
|
3630
3744
|
# right-multiply by inverse of leading matrix
|
|
3631
3745
|
lmat = P.leading_matrix(shifts=degree_shifts, row_wise=False)
|
|
@@ -3638,8 +3752,8 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
3638
3752
|
def _approximant_basis_iterative(self, order, shifts):
|
|
3639
3753
|
r"""
|
|
3640
3754
|
Return a ``shifts``-ordered weak Popov approximant basis for this
|
|
3641
|
-
polynomial matrix at order ``order``
|
|
3642
|
-
|
|
3755
|
+
polynomial matrix at order ``order`` (see
|
|
3756
|
+
:meth:`minimal_approximant_basis` for definitions).
|
|
3643
3757
|
|
|
3644
3758
|
The output basis is considered row-wise, that is, its rows are
|
|
3645
3759
|
left-approximants for the columns of ``self``. It is guaranteed that
|
|
@@ -3652,7 +3766,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
3652
3766
|
|
|
3653
3767
|
INPUT:
|
|
3654
3768
|
|
|
3655
|
-
- ``order`` -- list of
|
|
3769
|
+
- ``order`` -- list of integers
|
|
3656
3770
|
|
|
3657
3771
|
- ``shifts`` -- list of integers
|
|
3658
3772
|
|
|
@@ -3672,84 +3786,86 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
3672
3786
|
sage: pR.<x> = GF(7)[]
|
|
3673
3787
|
|
|
3674
3788
|
This method supports any number of columns or rows, as well as
|
|
3675
|
-
arbitrary shifts and orders
|
|
3789
|
+
arbitrary shifts and orders, and the returned list is the shifted row
|
|
3790
|
+
degrees of the output basis::
|
|
3676
3791
|
|
|
3677
3792
|
sage: order = [4, 1, 2]; shifts = [-3, 4]
|
|
3678
|
-
sage: pmat =
|
|
3679
|
-
....: [2*x^3 + 2*x^2 + 2*x + 3, 6,
|
|
3680
|
-
sage:
|
|
3681
|
-
|
|
3682
|
-
|
|
3793
|
+
sage: pmat = matrix(pR, [[5*x^3 + 4*x^2 + 4*x + 6, 5*x^2, 3*x^2 + 4],
|
|
3794
|
+
....: [2*x^3 + 2*x^2 + 2*x + 3, x^3 + 6, 6*x + 3]])
|
|
3795
|
+
sage: P, rdeg = pmat._approximant_basis_iterative(order, shifts)
|
|
3796
|
+
sage: P.is_minimal_approximant_basis(pmat, order, shifts)
|
|
3797
|
+
True
|
|
3798
|
+
sage: rdeg == P.row_degrees(shifts)
|
|
3683
3799
|
True
|
|
3684
3800
|
|
|
3685
|
-
|
|
3801
|
+
Zero or negative orders are supported::
|
|
3686
3802
|
|
|
3687
|
-
sage:
|
|
3803
|
+
sage: order = [4, 0, 2]; shifts = [3, -1]
|
|
3804
|
+
sage: P, rdeg = pmat._approximant_basis_iterative(order, shifts)
|
|
3805
|
+
sage: P.is_minimal_approximant_basis(pmat, order, shifts)
|
|
3806
|
+
True
|
|
3807
|
+
sage: rdeg == P.row_degrees(shifts)
|
|
3808
|
+
True
|
|
3809
|
+
sage: order = [4, -3, 2]; shifts = [3, -1]
|
|
3810
|
+
sage: P2, rdeg = pmat._approximant_basis_iterative(order, shifts)
|
|
3811
|
+
sage: P == P2
|
|
3688
3812
|
True
|
|
3689
3813
|
|
|
3690
3814
|
Approximant bases for the zero matrix are all constant unimodular
|
|
3691
3815
|
matrices; in fact, this algorithm returns the identity::
|
|
3692
3816
|
|
|
3693
|
-
sage: pmat =
|
|
3694
|
-
sage:
|
|
3695
|
-
|
|
3696
|
-
sage: rdeg == [5,0,-4] and appbas == Matrix.identity(pR, 3)
|
|
3817
|
+
sage: pmat = matrix(pR, 3, 2)
|
|
3818
|
+
sage: P,rdeg = pmat._approximant_basis_iterative([2,5], [5,0,-4])
|
|
3819
|
+
sage: rdeg == [5,0,-4] and P == matrix.identity(pR, 3)
|
|
3697
3820
|
True
|
|
3698
3821
|
"""
|
|
3699
|
-
|
|
3700
|
-
m = self.
|
|
3701
|
-
n = self.ncols()
|
|
3702
|
-
polynomial_ring = self.base_ring()
|
|
3703
|
-
X = polynomial_ring.gen()
|
|
3822
|
+
from sage.matrix.constructor import matrix # for identity
|
|
3823
|
+
m, n = self.dimensions()
|
|
3704
3824
|
|
|
3705
|
-
# '
|
|
3706
|
-
# '
|
|
3707
|
-
|
|
3708
|
-
|
|
3825
|
+
# 'rem_order': the orders that remains to be dealt with
|
|
3826
|
+
# 'rem_index': indices of orders that remains to be dealt with
|
|
3827
|
+
rem_order = [d for d in order if d > 0]
|
|
3828
|
+
rem_index = [j for j in range(n) if order[j] > 0]
|
|
3709
3829
|
|
|
3710
|
-
# initialization of the residuals (= input self)
|
|
3830
|
+
# initialization of the residuals (= input self, without columns with zero order)
|
|
3711
3831
|
# and of the approximant basis (= identity matrix)
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
residuals = self.__copy__()
|
|
3832
|
+
appbas = matrix.identity(self.base_ring(), m)
|
|
3833
|
+
residuals = self.matrix_from_columns(rem_index)
|
|
3715
3834
|
|
|
3716
|
-
# throughout the algorithm, 'rdeg'
|
|
3717
|
-
# 'appbas'
|
|
3835
|
+
# throughout the algorithm, 'rdeg' is the shifts-row degrees of 'appbas'
|
|
3718
3836
|
# --> initially, 'rdeg' is the shift-row degree of the identity matrix
|
|
3719
|
-
rdeg =
|
|
3837
|
+
rdeg = [s for s in shifts]
|
|
3720
3838
|
|
|
3721
|
-
while
|
|
3839
|
+
while rem_order:
|
|
3722
3840
|
# invariant:
|
|
3723
3841
|
# * appbas is a shifts-ordered weak Popov approximant basis for
|
|
3724
3842
|
# (self,doneorder)
|
|
3725
3843
|
# where doneorder = the already processed order, that is, the
|
|
3726
|
-
# tuple order-
|
|
3844
|
+
# tuple order-rem_order (entrywise subtraction)
|
|
3727
3845
|
# * rdeg is the shifts-row degree of appbas
|
|
3728
3846
|
# * residuals is the submatrix of columns (appbas * self)[:,j]
|
|
3729
|
-
# for all j such that
|
|
3847
|
+
# for all j such that rem_order[j] > 0
|
|
3730
3848
|
|
|
3731
3849
|
# choice for the next coefficient to be dealt with: first of the
|
|
3732
|
-
# largest entries in order
|
|
3733
|
-
# left to right)
|
|
3850
|
+
# largest entries in order
|
|
3734
3851
|
# Note: one may also consider the first one in order (--> process
|
|
3735
3852
|
# 'self' columnwise, from left column to right column, set j=0
|
|
3736
|
-
# instead of the below), but it seems to often be (
|
|
3737
|
-
|
|
3738
|
-
for ind, value in enumerate(
|
|
3739
|
-
if value ==
|
|
3853
|
+
# instead of the below), but it seems to often be (a bit) slower
|
|
3854
|
+
max_rem_order = max(rem_order)
|
|
3855
|
+
for ind, value in enumerate(rem_order):
|
|
3856
|
+
if value == max_rem_order:
|
|
3740
3857
|
j = ind
|
|
3741
3858
|
break
|
|
3742
|
-
d = order[
|
|
3859
|
+
d = order[rem_index[j]] - rem_order[j]
|
|
3743
3860
|
|
|
3744
3861
|
# coefficient = the coefficient of degree d of the column j of the
|
|
3745
3862
|
# residual matrix
|
|
3746
3863
|
# --> this is very likely nonzero and we want to make it zero, so
|
|
3747
|
-
# that this column becomes zero mod
|
|
3864
|
+
# that this column becomes zero mod x^{d+1}
|
|
3748
3865
|
coefficient = [residuals[i, j][d] for i in range(m)]
|
|
3749
3866
|
|
|
3750
3867
|
# Lambda: collect rows [i] with nonzero coefficient[i]
|
|
3751
|
-
# pi: index of the first row with smallest shift, among those in
|
|
3752
|
-
# Lambda
|
|
3868
|
+
# pi: index of the first row with smallest shift, among those in Lambda
|
|
3753
3869
|
Lambda = []
|
|
3754
3870
|
pi = -1
|
|
3755
3871
|
for i in range(m):
|
|
@@ -3764,23 +3880,383 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
3764
3880
|
scalar = -coefficient[row]/coefficient[pi]
|
|
3765
3881
|
appbas.add_multiple_of_row(row, pi, scalar)
|
|
3766
3882
|
residuals.add_multiple_of_row(row, pi, scalar)
|
|
3767
|
-
# update row pi
|
|
3883
|
+
# update row pi: multiply by x
|
|
3768
3884
|
rdeg[pi] += 1
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
#
|
|
3775
|
-
#
|
|
3776
|
-
|
|
3885
|
+
for jj in range(m):
|
|
3886
|
+
appbas[pi, jj] = appbas[pi, jj].shift(1)
|
|
3887
|
+
for jj in range(residuals.ncols()):
|
|
3888
|
+
residuals[pi, jj] = residuals[pi, jj].shift(1)
|
|
3889
|
+
|
|
3890
|
+
# Decrement rem_order[j], unless there is no more work to do in
|
|
3891
|
+
# this column (i.e. if rem_order[j] was 1) in which case
|
|
3892
|
+
# remove the column j of residual,rem_order,rem_index
|
|
3893
|
+
if rem_order[j] == 1:
|
|
3777
3894
|
residuals = residuals.delete_columns([j])
|
|
3778
|
-
|
|
3779
|
-
|
|
3895
|
+
rem_order.pop(j)
|
|
3896
|
+
rem_index.pop(j)
|
|
3780
3897
|
else:
|
|
3781
|
-
|
|
3898
|
+
rem_order[j] -= 1
|
|
3782
3899
|
return appbas, rdeg
|
|
3783
3900
|
|
|
3901
|
+
def minimal_interpolant_basis(self,
|
|
3902
|
+
points,
|
|
3903
|
+
shifts=None,
|
|
3904
|
+
row_wise=True,
|
|
3905
|
+
normal_form=False):
|
|
3906
|
+
r"""
|
|
3907
|
+
Return an interpolant basis in ``shifts``-ordered weak Popov form for
|
|
3908
|
+
this polynomial matrix with respect to the points in ``points``. This
|
|
3909
|
+
is a general form of interpolation problems usually called M-Padé
|
|
3910
|
+
approximation or vector rational interpolation.
|
|
3911
|
+
|
|
3912
|
+
Assuming we work row-wise, if `F` is an `m \times n` polynomial matrix
|
|
3913
|
+
and `a_{i,j}, 0 \le i < d_j` are elements (called points) of the base
|
|
3914
|
+
field for some integers `d_0,\ldots,d_{n-1}`, then an interpolant basis
|
|
3915
|
+
for `F` with respect to these points is a polynomial matrix whose rows
|
|
3916
|
+
form a basis of the module of interpolants for `F` with respect to the
|
|
3917
|
+
points. The latter interpolants are the polynomial vectors `p` of size
|
|
3918
|
+
`m` such that the column `j` of `p F` vanishes modulo `\mu_j = \prod_{0
|
|
3919
|
+
\le i < d_j} (x - a_{i,j})` (that is, it vanishes at all points
|
|
3920
|
+
`a_{i,j}`'s, with multiplicity in case of repeated points), for all `0
|
|
3921
|
+
\le j \le n-1`. For `j` such that `d_j \le 0`, i.e. the `j` th list of
|
|
3922
|
+
points is empty, this constraint on the column `j` is void.
|
|
3923
|
+
|
|
3924
|
+
If ``normal_form`` is ``True``, then the output basis `P` is
|
|
3925
|
+
furthermore in ``shifts``-Popov form. By default, `P` is considered
|
|
3926
|
+
row-wise, that is, its rows are left-interpolants for ``self``; if
|
|
3927
|
+
``row_wise`` is ``False`` then its columns are right-interpolants for
|
|
3928
|
+
``self``. It is guaranteed that the degree of the output basis is at
|
|
3929
|
+
most `\deg(\lcm(\mu_j, 0 \le j < n))`, independently of ``shifts``.
|
|
3930
|
+
|
|
3931
|
+
An error is raised if the input dimensions are not sound: if working
|
|
3932
|
+
row-wise (resp. column-wise), the length of ``points`` must be the
|
|
3933
|
+
number of columns (resp. rows) of ``self``, while the length of
|
|
3934
|
+
``shifts`` must be the number of rows (resp. columns) of ``self``.
|
|
3935
|
+
|
|
3936
|
+
If a single list is provided for ``points``, then it is converted into
|
|
3937
|
+
a list containing the provided list repeated the suitable number of
|
|
3938
|
+
times.
|
|
3939
|
+
|
|
3940
|
+
INPUT:
|
|
3941
|
+
|
|
3942
|
+
- ``points`` -- list of elements from the base field (or coercible into
|
|
3943
|
+
it), or list of such lists
|
|
3944
|
+
|
|
3945
|
+
- ``shifts`` -- (default: ``None``) list of integers;
|
|
3946
|
+
``None`` is interpreted as ``shifts=[0,...,0]``
|
|
3947
|
+
|
|
3948
|
+
- ``row_wise`` -- boolean (default: ``True``); if ``True`` then the
|
|
3949
|
+
output basis is considered row-wise and operates on the left of
|
|
3950
|
+
``self``. Otherwise it is column-wise and operates on the right of
|
|
3951
|
+
``self``.
|
|
3952
|
+
|
|
3953
|
+
- ``normal_form`` -- boolean (default: ``False``); if ``True`` then the
|
|
3954
|
+
output basis is in ``shifts``-Popov form
|
|
3955
|
+
|
|
3956
|
+
OUTPUT: a polynomial matrix
|
|
3957
|
+
|
|
3958
|
+
ALGORITHM:
|
|
3959
|
+
|
|
3960
|
+
The implementation is inspired from the iterative algorithms described
|
|
3961
|
+
in [Bec1992]_ and [VBB1992]_ ; for obtaining the normal form, it relies
|
|
3962
|
+
directly on Lemmas 3.3 and 4.1 in [JNSV2016]_ .
|
|
3963
|
+
|
|
3964
|
+
EXAMPLES::
|
|
3965
|
+
|
|
3966
|
+
sage: ff = GF(7)
|
|
3967
|
+
sage: xring.<x> = ff[]
|
|
3968
|
+
|
|
3969
|
+
This method supports any number of columns or rows, as well as
|
|
3970
|
+
arbitrary shifts and lists of points::
|
|
3971
|
+
|
|
3972
|
+
sage: F = matrix([[5*x^3 + 4*x^2 + 4*x + 6, 5*x^3 + 5*x^2 + 5],
|
|
3973
|
+
....: [2*x^3 + 2*x^2 + 2*x + 3, 3*x^3 + 6*x + 4],
|
|
3974
|
+
....: [ x^2 + 6*x + 3, 5*x^3 + 2*x^2 + 3*x]])
|
|
3975
|
+
sage: points = [[ff(3), ff(0), ff(6), ff(3)], [ff(1), ff(1), ff(1)]]
|
|
3976
|
+
sage: mod1 = (x - 3) * x * (x - 6) * (x - 3)
|
|
3977
|
+
sage: mod2 = (x - 1)**3
|
|
3978
|
+
|
|
3979
|
+
sage: P = F.minimal_interpolant_basis(points, shifts=[0,0,0])
|
|
3980
|
+
sage: P.is_weak_popov(ordered=True)
|
|
3981
|
+
True
|
|
3982
|
+
sage: G = P*F
|
|
3983
|
+
sage: G[:,0] % mod1 == 0 and G[:,1] % mod2 == 0
|
|
3984
|
+
True
|
|
3985
|
+
sage: P.det() == mod1 * mod2
|
|
3986
|
+
True
|
|
3987
|
+
|
|
3988
|
+
The last test highlights that for "sufficiently generic" input, the
|
|
3989
|
+
fact that the returned matrix generates the module of interpolants is
|
|
3990
|
+
equivalent to the fact that its determinant is the product of the
|
|
3991
|
+
linear factors defined by the interpolation points.
|
|
3992
|
+
|
|
3993
|
+
If shifts are not specified, they are chosen as uniform `[0,\ldots,0]`
|
|
3994
|
+
by default. Besides, if the lists of points are all identical, one can
|
|
3995
|
+
rather give a single list::
|
|
3996
|
+
|
|
3997
|
+
sage: P = F.minimal_interpolant_basis([points[0], points[0]])
|
|
3998
|
+
sage: P == F.minimal_interpolant_basis(points[0], shifts=[0,0,0])
|
|
3999
|
+
True
|
|
4000
|
+
|
|
4001
|
+
One can work column-wise by specifying ``row_wise=False``. Empty lists
|
|
4002
|
+
of points are supported, and amount to ignoring the corresponding
|
|
4003
|
+
column of ``self`` (or corresponding row, if column-wise)::
|
|
4004
|
+
|
|
4005
|
+
sage: points[1] = []
|
|
4006
|
+
sage: shifts = [-1, 2, 0]
|
|
4007
|
+
sage: Ft = F.transpose()
|
|
4008
|
+
sage: P = Ft.minimal_interpolant_basis(points, shifts=shifts, row_wise=False)
|
|
4009
|
+
sage: P == Ft[0,:].minimal_interpolant_basis([points[0]], shifts=shifts, row_wise=False)
|
|
4010
|
+
True
|
|
4011
|
+
sage: P.is_weak_popov(shifts=shifts, ordered=True, row_wise=False)
|
|
4012
|
+
True
|
|
4013
|
+
sage: G = Ft * P
|
|
4014
|
+
sage: G[0,:] % mod1 == 0 and P.det() == mod1
|
|
4015
|
+
True
|
|
4016
|
+
|
|
4017
|
+
Errors are raised if the input dimensions are not sound::
|
|
4018
|
+
|
|
4019
|
+
sage: P = F.minimal_interpolant_basis([points[0]])
|
|
4020
|
+
Traceback (most recent call last):
|
|
4021
|
+
...
|
|
4022
|
+
ValueError: points length should be the column dimension
|
|
4023
|
+
|
|
4024
|
+
sage: P = F.minimal_interpolant_basis(points, shifts=[0,0,0,0])
|
|
4025
|
+
Traceback (most recent call last):
|
|
4026
|
+
...
|
|
4027
|
+
ValueError: shifts length should be the row dimension
|
|
4028
|
+
|
|
4029
|
+
.. SEEALSO::
|
|
4030
|
+
|
|
4031
|
+
:meth:`minimal_approximant_basis`, :meth:`minimal_relation_basis`
|
|
4032
|
+
"""
|
|
4033
|
+
from sage.matrix.constructor import matrix # for identity
|
|
4034
|
+
from copy import copy
|
|
4035
|
+
|
|
4036
|
+
m = self.nrows()
|
|
4037
|
+
n = self.ncols()
|
|
4038
|
+
|
|
4039
|
+
# set default shifts / check shifts dimension
|
|
4040
|
+
if shifts is None:
|
|
4041
|
+
shifts = [0] * m if row_wise else [0] * n
|
|
4042
|
+
elif row_wise and len(shifts) != m:
|
|
4043
|
+
raise ValueError('shifts length should be the row dimension')
|
|
4044
|
+
elif (not row_wise) and len(shifts) != n:
|
|
4045
|
+
raise ValueError('shifts length should be the column dimension')
|
|
4046
|
+
|
|
4047
|
+
# deal with corner case where there is no equation to solve
|
|
4048
|
+
if row_wise and (n == 0 or len(points) == 0):
|
|
4049
|
+
return matrix.identity(self.base_ring(), m)
|
|
4050
|
+
elif (not row_wise) and (m == 0 or len(points) == 0):
|
|
4051
|
+
return matrix.identity(self.base_ring(), n)
|
|
4052
|
+
|
|
4053
|
+
# thanks to the above corner case, from here on, points is a nonempty list
|
|
4054
|
+
# if its entries are field elements, build full list of lists of points
|
|
4055
|
+
if not isinstance(points[0], list):
|
|
4056
|
+
if row_wise:
|
|
4057
|
+
points = [copy(points) for j in range(n)]
|
|
4058
|
+
else:
|
|
4059
|
+
points = [copy(points) for i in range(m)]
|
|
4060
|
+
|
|
4061
|
+
# check length of points
|
|
4062
|
+
if row_wise and len(points) != n:
|
|
4063
|
+
raise ValueError("points length should be the column dimension")
|
|
4064
|
+
elif (not row_wise) and len(points) != m:
|
|
4065
|
+
raise ValueError("points length should be the row dimension")
|
|
4066
|
+
|
|
4067
|
+
# compute interpolant basis
|
|
4068
|
+
# if required, normalize it into shifted Popov form
|
|
4069
|
+
if row_wise:
|
|
4070
|
+
P, rdeg = self._interpolant_basis_iterative(points, shifts)
|
|
4071
|
+
if normal_form:
|
|
4072
|
+
# compute the list "- pivot degree"
|
|
4073
|
+
# (since weak Popov, pivot degree is rdeg-shifts entrywise)
|
|
4074
|
+
# Note: -deg(P[i,i]) = shifts[i] - rdeg[i]
|
|
4075
|
+
degree_shifts = [shifts[i] - rdeg[i] for i in range(m)]
|
|
4076
|
+
# compute interpolant basis with that list as shifts
|
|
4077
|
+
P, rdeg = self._interpolant_basis_iterative(points, degree_shifts)
|
|
4078
|
+
# left-multiply by inverse of leading matrix
|
|
4079
|
+
lmat = P.leading_matrix(shifts=degree_shifts)
|
|
4080
|
+
P = lmat.inverse() * P
|
|
4081
|
+
else:
|
|
4082
|
+
P, rdeg = self.transpose()._interpolant_basis_iterative(points, shifts)
|
|
4083
|
+
if normal_form:
|
|
4084
|
+
# compute the list "- pivot degree"
|
|
4085
|
+
# (since weak Popov, pivot degree is rdeg-shifts entrywise)
|
|
4086
|
+
degree_shifts = [shifts[i] - rdeg[i] for i in range(n)]
|
|
4087
|
+
# compute interpolant basis with that list as shifts
|
|
4088
|
+
P, rdeg = self.T._interpolant_basis_iterative(points, degree_shifts)
|
|
4089
|
+
P = P.transpose()
|
|
4090
|
+
# right-multiply by inverse of leading matrix
|
|
4091
|
+
lmat = P.leading_matrix(shifts=degree_shifts, row_wise=False)
|
|
4092
|
+
P = P * lmat.inverse()
|
|
4093
|
+
else:
|
|
4094
|
+
P = P.transpose()
|
|
4095
|
+
|
|
4096
|
+
return P
|
|
4097
|
+
|
|
4098
|
+
def _interpolant_basis_iterative(self, points, shifts):
|
|
4099
|
+
r"""
|
|
4100
|
+
Return a ``shifts``-ordered weak Popov interpolant basis for this
|
|
4101
|
+
polynomial matrix with respect to points specified in ``points`` (see
|
|
4102
|
+
:meth:`minimal_interpolant_basis` for definitions).
|
|
4103
|
+
|
|
4104
|
+
The output basis is considered row-wise, that is, its rows are
|
|
4105
|
+
left-interpolants for the columns of ``self``.
|
|
4106
|
+
|
|
4107
|
+
The input dimensions are supposed to be sound: the length of ``points``
|
|
4108
|
+
must be the number of columns of ``self``, while the length of
|
|
4109
|
+
``shifts`` must be the number of rows of ``self``.
|
|
4110
|
+
|
|
4111
|
+
INPUT:
|
|
4112
|
+
|
|
4113
|
+
- ``points`` -- list of lists of elements from the base field (or
|
|
4114
|
+
coercible into it)
|
|
4115
|
+
|
|
4116
|
+
- ``shifts`` -- list of integers
|
|
4117
|
+
|
|
4118
|
+
OUTPUT:
|
|
4119
|
+
|
|
4120
|
+
- a polynomial matrix (the interpolant basis ``P``).
|
|
4121
|
+
|
|
4122
|
+
- a list of integers (the shifts-row degrees of ``P``).
|
|
4123
|
+
|
|
4124
|
+
ALGORITHM:
|
|
4125
|
+
|
|
4126
|
+
This is inspired from the iterative algorithms described in [Bec1992]_
|
|
4127
|
+
and [VBB1992]_ .
|
|
4128
|
+
|
|
4129
|
+
EXAMPLES::
|
|
4130
|
+
|
|
4131
|
+
sage: ff = GF(7)
|
|
4132
|
+
sage: xring.<x> = ff[]
|
|
4133
|
+
|
|
4134
|
+
This method supports any number of columns or rows, as well as
|
|
4135
|
+
arbitrary shifts and lists of points. The returned list is the shifted
|
|
4136
|
+
row degrees of the interpolation basis::
|
|
4137
|
+
|
|
4138
|
+
sage: F = matrix([[5*x^3 + 4*x^2 + 4*x + 6, 5*x^3 + 5*x^2 + 5],
|
|
4139
|
+
....: [2*x^3 + 2*x^2 + 2*x + 3, 3*x^3 + 6*x + 4],
|
|
4140
|
+
....: [ x^2 + 6*x + 3, 5*x^3 + 2*x^2 + 3*x]])
|
|
4141
|
+
sage: points = [[ff(3), ff(0), ff(6), ff(3)], [ff(1), ff(1), ff(1)]]
|
|
4142
|
+
sage: mod1 = (x - 3) * x * (x - 6) * (x - 3)
|
|
4143
|
+
sage: mod2 = (x - 1)**3
|
|
4144
|
+
|
|
4145
|
+
sage: P, rdeg = F._interpolant_basis_iterative(points, [0,0,0])
|
|
4146
|
+
sage: rdeg == P.row_degrees()
|
|
4147
|
+
True
|
|
4148
|
+
sage: P.is_weak_popov(ordered=True)
|
|
4149
|
+
True
|
|
4150
|
+
sage: G = P*F
|
|
4151
|
+
sage: G[:,0] % mod1 == 0 and G[:,1] % mod2 == 0
|
|
4152
|
+
True
|
|
4153
|
+
|
|
4154
|
+
For "sufficiently generic" input, the fact that the returned matrix
|
|
4155
|
+
generates the module of interpolants is equivalent to the fact that its
|
|
4156
|
+
determinant is the product of the linear factors defined by the
|
|
4157
|
+
interpolation points::
|
|
4158
|
+
|
|
4159
|
+
sage: P.det() == mod1 * mod2
|
|
4160
|
+
True
|
|
4161
|
+
|
|
4162
|
+
sage: points[1] = []
|
|
4163
|
+
sage: shifts = [-1, 2, 0]
|
|
4164
|
+
sage: P, rdeg = F._interpolant_basis_iterative(points, shifts)
|
|
4165
|
+
sage: rdeg == P.row_degrees(shifts=shifts)
|
|
4166
|
+
True
|
|
4167
|
+
sage: P.is_weak_popov(shifts=shifts, ordered=True)
|
|
4168
|
+
True
|
|
4169
|
+
sage: G = P*F
|
|
4170
|
+
sage: G[:,0] % mod1 == 0 and P.det() == mod1
|
|
4171
|
+
True
|
|
4172
|
+
|
|
4173
|
+
Interpolant bases for the zero matrix are all constant unimodular
|
|
4174
|
+
matrices; in fact, this algorithm returns the identity::
|
|
4175
|
+
|
|
4176
|
+
sage: F = matrix(xring, 4, 2)
|
|
4177
|
+
sage: P,rdeg = F._interpolant_basis_iterative(points, shifts)
|
|
4178
|
+
sage: rdeg == shifts and P == 1
|
|
4179
|
+
True
|
|
4180
|
+
"""
|
|
4181
|
+
from sage.matrix.constructor import matrix # for identity
|
|
4182
|
+
from copy import copy
|
|
4183
|
+
m, n = self.dimensions()
|
|
4184
|
+
|
|
4185
|
+
# 'rem_points': the points that remain to be dealt with
|
|
4186
|
+
# 'rem_index': indices of lists of points that remain to be dealt with
|
|
4187
|
+
rem_points = [copy(pts) for pts in points if len(pts) > 0]
|
|
4188
|
+
rem_index = [j for j in range(n) if len(points[j]) > 0]
|
|
4189
|
+
|
|
4190
|
+
# initialization of the residuals (= input self, without columns associated to no point)
|
|
4191
|
+
# and of the interpolant basis (= identity matrix)
|
|
4192
|
+
intbas = matrix.identity(self.base_ring(), m)
|
|
4193
|
+
residuals = self.matrix_from_columns(rem_index)
|
|
4194
|
+
|
|
4195
|
+
# throughout the algorithm, 'rdeg' is the shifts-row degrees of 'intbas'
|
|
4196
|
+
# --> initially, 'rdeg' is the shift-row degree of the identity matrix
|
|
4197
|
+
rdeg = [s for s in shifts]
|
|
4198
|
+
|
|
4199
|
+
while rem_points:
|
|
4200
|
+
# invariant:
|
|
4201
|
+
# * intbas is a shifts-ordered weak Popov interpolant basis for
|
|
4202
|
+
# self and the already processed points
|
|
4203
|
+
# * rdeg is the shifts-row degree of intbas
|
|
4204
|
+
# * residuals is the submatrix of columns
|
|
4205
|
+
# (intbas * self)[:,j] / prod_i(x - points[j][i])
|
|
4206
|
+
# for all j in rem_index and where i goes through the already
|
|
4207
|
+
# processed points from points[j]
|
|
4208
|
+
|
|
4209
|
+
# choice for the next point to be dealt with: last point of the
|
|
4210
|
+
# list points[j] that has largest length
|
|
4211
|
+
# Note: one may also consider a point of points[j] for the smallest
|
|
4212
|
+
# possible j (--> roughly, set j=0 instead of the below), but it
|
|
4213
|
+
# seems to often be (a bit) slower
|
|
4214
|
+
max_rem_points = max(len(pts) for pts in rem_points)
|
|
4215
|
+
for ind, pts in enumerate(rem_points):
|
|
4216
|
+
if len(pts) == max_rem_points:
|
|
4217
|
+
j = ind
|
|
4218
|
+
break
|
|
4219
|
+
pt = rem_points[j].pop()
|
|
4220
|
+
|
|
4221
|
+
# evals = the evaluations at pt of the column j of the residual matrix
|
|
4222
|
+
# --> this is very likely nonzero and we want to make it zero, so
|
|
4223
|
+
# that this column becomes zero mod (x - pt)
|
|
4224
|
+
evals = [residuals[i, j](pt) for i in range(m)]
|
|
4225
|
+
|
|
4226
|
+
# Lambda: collect rows [i] with nonzero evals[i]
|
|
4227
|
+
# pi: index of the first row with smallest shift, among those in Lambda
|
|
4228
|
+
Lambda = []
|
|
4229
|
+
pi = -1
|
|
4230
|
+
for i in range(m):
|
|
4231
|
+
if evals[i] != 0:
|
|
4232
|
+
Lambda.append(i)
|
|
4233
|
+
if pi < 0 or rdeg[i] < rdeg[pi]:
|
|
4234
|
+
pi = i
|
|
4235
|
+
if Lambda: # otherwise, nothing to do
|
|
4236
|
+
# update all rows in Lambda--{pi}
|
|
4237
|
+
Lambda.remove(pi)
|
|
4238
|
+
for row in Lambda:
|
|
4239
|
+
scalar = -evals[row]/evals[pi]
|
|
4240
|
+
intbas.add_multiple_of_row(row, pi, scalar)
|
|
4241
|
+
residuals.add_multiple_of_row(row, pi, scalar)
|
|
4242
|
+
# update row pi: multiply by x - pt
|
|
4243
|
+
rdeg[pi] += 1
|
|
4244
|
+
x = self.base_ring().gen()
|
|
4245
|
+
intbas.rescale_row(pi, x - pt)
|
|
4246
|
+
residuals.rescale_row(pi, x - pt)
|
|
4247
|
+
# divide residual column by x - pt
|
|
4248
|
+
for i in range(m):
|
|
4249
|
+
residuals[i, j] = residuals[i, j] // (x - pt)
|
|
4250
|
+
|
|
4251
|
+
# If rem_points[j] is now empty, there is no more work to do in
|
|
4252
|
+
# this column: remove column j of residual,rem_points,rem_index
|
|
4253
|
+
if len(rem_points[j]) == 0:
|
|
4254
|
+
residuals = residuals.delete_columns([j])
|
|
4255
|
+
rem_points.pop(j)
|
|
4256
|
+
rem_index.pop(j)
|
|
4257
|
+
|
|
4258
|
+
return intbas, rdeg
|
|
4259
|
+
|
|
3784
4260
|
def is_minimal_kernel_basis(self,
|
|
3785
4261
|
pmat,
|
|
3786
4262
|
shifts=None,
|
|
@@ -3823,22 +4299,22 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
3823
4299
|
EXAMPLES::
|
|
3824
4300
|
|
|
3825
4301
|
sage: pR.<x> = GF(97)[]
|
|
3826
|
-
sage: pmat =
|
|
4302
|
+
sage: pmat = matrix(pR, [[1], [x], [x**2]])
|
|
3827
4303
|
|
|
3828
|
-
sage: kerbas =
|
|
4304
|
+
sage: kerbas = matrix(pR, [[x,-1,0], [0,x,-1]])
|
|
3829
4305
|
sage: kerbas.is_minimal_kernel_basis(pmat)
|
|
3830
4306
|
True
|
|
3831
4307
|
|
|
3832
4308
|
A matrix in Popov form which has the right rank, all rows in the
|
|
3833
4309
|
kernel, but does not generate the kernel::
|
|
3834
4310
|
|
|
3835
|
-
sage: kerbas =
|
|
4311
|
+
sage: kerbas = matrix(pR, [[x**2,0,-1], [0,x,-1]])
|
|
3836
4312
|
sage: kerbas.is_minimal_kernel_basis(pmat)
|
|
3837
4313
|
False
|
|
3838
4314
|
|
|
3839
4315
|
Shifts and right kernel bases are supported (with ``row_wise``), and one can test whether the kernel basis is normalized in shifted-Popov form (with ``normal_form``)::
|
|
3840
4316
|
|
|
3841
|
-
sage: kerbas =
|
|
4317
|
+
sage: kerbas = matrix(pR, [[-x,-x**2], [1,0], [0,1]])
|
|
3842
4318
|
sage: kerbas.is_minimal_kernel_basis(
|
|
3843
4319
|
....: pmat.transpose(), row_wise=False,
|
|
3844
4320
|
....: normal_form=True, shifts=[0,1,2])
|
|
@@ -3945,18 +4421,18 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
3945
4421
|
EXAMPLES::
|
|
3946
4422
|
|
|
3947
4423
|
sage: pR.<x> = GF(7)[]
|
|
3948
|
-
sage: pmat =
|
|
4424
|
+
sage: pmat = matrix([[(x+1)*(x+3)], [(x+1)*(x+3)+1]])
|
|
3949
4425
|
sage: pmat.minimal_kernel_basis()
|
|
3950
4426
|
[6*x^2 + 3*x + 3 x^2 + 4*x + 3]
|
|
3951
4427
|
|
|
3952
|
-
sage: pmat =
|
|
4428
|
+
sage: pmat = matrix([[(x+1)*(x+3)], [(x+1)*(x+4)]])
|
|
3953
4429
|
sage: pmat.minimal_kernel_basis()
|
|
3954
4430
|
[6*x + 3 x + 3]
|
|
3955
4431
|
|
|
3956
4432
|
sage: pmat.minimal_kernel_basis(row_wise=False)
|
|
3957
4433
|
[]
|
|
3958
4434
|
|
|
3959
|
-
sage: pmat =
|
|
4435
|
+
sage: pmat = matrix(pR, [[1, x, x**2]])
|
|
3960
4436
|
sage: pmat.minimal_kernel_basis(row_wise=False, normal_form=True)
|
|
3961
4437
|
[x 0]
|
|
3962
4438
|
[6 x]
|
|
@@ -3970,30 +4446,30 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
3970
4446
|
|
|
3971
4447
|
Some particular cases (matrix is zero, dimension is zero, column is zero)::
|
|
3972
4448
|
|
|
3973
|
-
sage:
|
|
4449
|
+
sage: matrix(pR, 2, 1).minimal_kernel_basis()
|
|
3974
4450
|
[1 0]
|
|
3975
4451
|
[0 1]
|
|
3976
4452
|
|
|
3977
|
-
sage:
|
|
4453
|
+
sage: matrix(pR, 2, 0).minimal_kernel_basis()
|
|
3978
4454
|
[1 0]
|
|
3979
4455
|
[0 1]
|
|
3980
4456
|
|
|
3981
|
-
sage:
|
|
4457
|
+
sage: matrix(pR, 0, 2).minimal_kernel_basis()
|
|
3982
4458
|
[]
|
|
3983
4459
|
|
|
3984
|
-
sage:
|
|
4460
|
+
sage: matrix(pR, 3, 2, [[1,0],[1,0],[1,0]]).minimal_kernel_basis()
|
|
3985
4461
|
[6 1 0]
|
|
3986
4462
|
[6 0 1]
|
|
3987
4463
|
|
|
3988
|
-
sage:
|
|
4464
|
+
sage: matrix(pR, 3, 2, [[x,0],[1,0],[x+1,0]]).minimal_kernel_basis()
|
|
3989
4465
|
[6 x 0]
|
|
3990
4466
|
[6 6 1]
|
|
3991
4467
|
|
|
3992
4468
|
TESTS:
|
|
3993
4469
|
|
|
3994
|
-
We check that PR #37208 is fixed::
|
|
4470
|
+
We check that the issue in PR #37208 is fixed::
|
|
3995
4471
|
|
|
3996
|
-
sage:
|
|
4472
|
+
sage: matrix(pR, 2, 0).minimal_kernel_basis().is_sparse()
|
|
3997
4473
|
False
|
|
3998
4474
|
"""
|
|
3999
4475
|
from sage.matrix.constructor import matrix
|
|
@@ -4029,17 +4505,11 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
4029
4505
|
# orders for approximation
|
|
4030
4506
|
orders = self.column_degrees(degree_bounds)
|
|
4031
4507
|
for i in range(n): orders[i] = orders[i]+1
|
|
4032
|
-
|
|
4033
|
-
# note: minimal_approximant_basis requires orders[i] > 0
|
|
4508
|
+
# note:
|
|
4034
4509
|
# -> if d>0, then degree_bounds > 0 entry-wise and this tuple
|
|
4035
|
-
# `orders`
|
|
4036
|
-
# -> if d==0, then `orders[i]` is zero exactly when the column i
|
|
4037
|
-
#
|
|
4038
|
-
# columns which do not influence the left kernel
|
|
4039
|
-
if d == 0:
|
|
4040
|
-
for i in range(n):
|
|
4041
|
-
if orders[i] == 0:
|
|
4042
|
-
orders[i] = 1
|
|
4510
|
+
# `orders` has all entries strictly positive
|
|
4511
|
+
# -> if d==0, then `orders[i]` is zero exactly when the column i of
|
|
4512
|
+
# self is zero; such columns do not influence the left kernel
|
|
4043
4513
|
|
|
4044
4514
|
# compute approximant basis and retrieve kernel rows
|
|
4045
4515
|
P = self.minimal_approximant_basis(orders,shifts,True,normal_form)
|
|
@@ -4072,13 +4542,8 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
4072
4542
|
# note: minimal_approximant_basis requires orders[i] > 0
|
|
4073
4543
|
# -> if d>0, then degree_bounds > 0 entry-wise and this tuple
|
|
4074
4544
|
# `orders` already has all entries strictly positive
|
|
4075
|
-
# -> if d==0, then `orders[i]` is zero exactly when the row i
|
|
4076
|
-
#
|
|
4077
|
-
# rows which do not influence the right kernel
|
|
4078
|
-
if d == 0:
|
|
4079
|
-
for i in range(m):
|
|
4080
|
-
if orders[i] == 0:
|
|
4081
|
-
orders[i] = 1
|
|
4545
|
+
# -> if d==0, then `orders[i]` is zero exactly when the row i of
|
|
4546
|
+
# self is zero; such rows do not influence the right kernel
|
|
4082
4547
|
|
|
4083
4548
|
# compute approximant basis and retrieve kernel columns
|
|
4084
4549
|
P = self.minimal_approximant_basis(orders,shifts,False,normal_form)
|
|
@@ -4088,6 +4553,232 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense):
|
|
|
4088
4553
|
column_indices.append(j)
|
|
4089
4554
|
return P[:,column_indices]
|
|
4090
4555
|
|
|
4556
|
+
def minimal_relation_basis(self,
|
|
4557
|
+
mod,
|
|
4558
|
+
shifts=None,
|
|
4559
|
+
row_wise=True,
|
|
4560
|
+
normal_form=False,
|
|
4561
|
+
reduced_input=False):
|
|
4562
|
+
r"""
|
|
4563
|
+
Return a relation basis in ``shifts``-ordered weak Popov form for this
|
|
4564
|
+
polynomial matrix with respect to the module defined by ``mod``.
|
|
4565
|
+
|
|
4566
|
+
The description below uses notation `F` for ``self``, `M` for ``mod``,
|
|
4567
|
+
and `s` for the shifts ``shifts``. `F` is an `m \times n` polynomial
|
|
4568
|
+
matrix and `M` is a nonsingular `n \times n` matrix.
|
|
4569
|
+
|
|
4570
|
+
If we work row-wise (resp. column-wise), a relation basis for `F`
|
|
4571
|
+
modulo `M` is a polynomial matrix `P` whose rows (resp. columns) form a
|
|
4572
|
+
basis of the module of polynomial vectors `p` of size `m` such that `p
|
|
4573
|
+
F` (resp. `F p`) belongs to the row space (resp. column space) of `M`.
|
|
4574
|
+
Such a basis `P` is an `m \times m` nonsingular matrix, which this
|
|
4575
|
+
method computes in `s`-ordered weak Popov form.
|
|
4576
|
+
|
|
4577
|
+
If ``normal_form`` is ``True``, then the output `P` is the canonical
|
|
4578
|
+
`s`-Popov basis. If ``reduced_input`` is ``True``, then the provided
|
|
4579
|
+
`M` should be column reduced (resp. row reduced) and `F` should be
|
|
4580
|
+
reduced modulo `M`, that is, should have column (resp. row) degrees
|
|
4581
|
+
strictly bounded entry-wise by those of `M`. When those properties are
|
|
4582
|
+
known to be true, setting ``reduced_input`` to the non-default ``True``
|
|
4583
|
+
may save some computation time.
|
|
4584
|
+
|
|
4585
|
+
An error is raised if the dimensions of the input matrices or the
|
|
4586
|
+
length of the input shift are not sound. When ``reduced_input`` is the
|
|
4587
|
+
default ``False``, an error is raised if `M` is singular.
|
|
4588
|
+
|
|
4589
|
+
Here are two special cases of relation bases.
|
|
4590
|
+
|
|
4591
|
+
- minimal approximant bases, for which `M` is a diagonal of powers
|
|
4592
|
+
`x^{d_0}, \ldots, x^{d_{n-1}}` (see
|
|
4593
|
+
:meth:`minimal_approximant_basis`, which may be called directly for
|
|
4594
|
+
better performance),
|
|
4595
|
+
- minimal interpolant bases, for which `M` is a diagonal of polynomials
|
|
4596
|
+
that split into known linear factors (see
|
|
4597
|
+
:meth:`minimal_interpolant_basis`, which may be called directly for
|
|
4598
|
+
better performance).
|
|
4599
|
+
|
|
4600
|
+
INPUT:
|
|
4601
|
+
|
|
4602
|
+
- ``mod`` -- polynomial matrix, nonsingular
|
|
4603
|
+
|
|
4604
|
+
- ``shifts`` -- (default: ``None``) list of integers;
|
|
4605
|
+
``None`` is interpreted as ``shifts=[0,...,0]``
|
|
4606
|
+
|
|
4607
|
+
- ``row_wise`` -- boolean (default: ``True``). If ``True``, compute
|
|
4608
|
+
left relations for ``self`` modulo the row space of ``mod``;
|
|
4609
|
+
otherwise, compute right relations for ``self`` modulo the column
|
|
4610
|
+
space of ``mod``
|
|
4611
|
+
|
|
4612
|
+
- ``normal_form`` -- boolean (default: ``False``); if
|
|
4613
|
+
``True`` then the output basis is in ``shifts``-Popov form
|
|
4614
|
+
|
|
4615
|
+
- ``reduced_input`` -- boolean (default: ``False``). If ``True``, and
|
|
4616
|
+
working row-wise (resp. column-wise), then ``mod`` must be column
|
|
4617
|
+
(resp. row) reduced and its column (resp. row) degrees must be strict
|
|
4618
|
+
upper bounds on those of ``self`` entry-wise
|
|
4619
|
+
|
|
4620
|
+
OUTPUT: a polynomial matrix
|
|
4621
|
+
|
|
4622
|
+
EXAMPLES::
|
|
4623
|
+
|
|
4624
|
+
sage: pR.<x> = GF(7)[]
|
|
4625
|
+
|
|
4626
|
+
When M is a diagonal of powers of the variable, a relation basis is the
|
|
4627
|
+
same as an approximant basis::
|
|
4628
|
+
|
|
4629
|
+
sage: M = matrix.diagonal([x**4, x**3], sparse=False)
|
|
4630
|
+
sage: shifts = [-1, 2, 0]
|
|
4631
|
+
sage: F = matrix(pR, [[5*x^3 + 4*x^2 + 4*x + 6, 5*x^2 + 4*x + 1],
|
|
4632
|
+
....: [ 2*x^2 + 2*x + 3, 6*x^2 + 6*x + 3],
|
|
4633
|
+
....: [4*x^3 + x + 1, 4*x^2 + 2*x + 3]])
|
|
4634
|
+
sage: P_app = F.minimal_approximant_basis([4, 3], shifts, normal_form=True)
|
|
4635
|
+
sage: P_rel = F.minimal_relation_basis(M, shifts, normal_form=True)
|
|
4636
|
+
sage: P_app == P_rel
|
|
4637
|
+
True
|
|
4638
|
+
|
|
4639
|
+
If ``self`` is the identity matrix, then relation bases are simply
|
|
4640
|
+
matrices that are left-unimodularly equivalent to `M`::
|
|
4641
|
+
|
|
4642
|
+
sage: # M is both row and column reduced
|
|
4643
|
+
sage: M = x**3 + matrix([[6*x**2, 2, x], [2, 2, 6*x], [x**2, 3, 6]])
|
|
4644
|
+
sage: F1 = matrix.identity(pR, 3) # cdeg(F1) < cdeg(M)
|
|
4645
|
+
sage: P1 = F1.minimal_relation_basis(M, shifts=[0,1,2], normal_form=True)
|
|
4646
|
+
sage: P1 == M.popov_form(shifts=[0,1,2])
|
|
4647
|
+
True
|
|
4648
|
+
|
|
4649
|
+
One can consider column-wise relations; unspecified shift means taking
|
|
4650
|
+
the uniform `[0,\ldots, 0]` shift::
|
|
4651
|
+
|
|
4652
|
+
sage: F2 = matrix([[ 1, 6*x + 2, 5, 3, 5*x^2 + 3],
|
|
4653
|
+
....: [2*x^2 + 4*x + 4, 3*x + 1, 5*x, 6*x^2 + 5, 6],
|
|
4654
|
+
....: [ 5*x + 4, 3*x + 1, 2, 2*x + 2, 2*x + 1]])
|
|
4655
|
+
sage: P2 = F2.minimal_relation_basis(M, row_wise=False)
|
|
4656
|
+
sage: P2.is_weak_popov(shifts=[0]*5, row_wise=False)
|
|
4657
|
+
True
|
|
4658
|
+
sage: Q,R = (F2*P2).left_quo_rem(M) # F2*P2 = M*Q + 0
|
|
4659
|
+
sage: R == 0
|
|
4660
|
+
True
|
|
4661
|
+
|
|
4662
|
+
Unless requiring a normal form, the output basis will most often not be
|
|
4663
|
+
the canonical one::
|
|
4664
|
+
|
|
4665
|
+
sage: P2.is_popov(shifts=[0]*5, row_wise=False)
|
|
4666
|
+
False
|
|
4667
|
+
|
|
4668
|
+
By default, this supports input ``self`` that are not reduced modulo
|
|
4669
|
+
``M``, unless ``reduced_input`` is specified as ``True``::
|
|
4670
|
+
|
|
4671
|
+
sage: G1 = F1 + x * M # G1 == F1 mod M; G1 not reduced mod M
|
|
4672
|
+
sage: P1bis = G1.minimal_relation_basis(M, shifts=[0,1,2], normal_form=True)
|
|
4673
|
+
sage: P1bis == P1
|
|
4674
|
+
True
|
|
4675
|
+
sage: P = G1.minimal_relation_basis(M, shifts=[0,1,2], reduced_input=True)
|
|
4676
|
+
sage: P.is_weak_popov(shifts=[0,1,2])
|
|
4677
|
+
False
|
|
4678
|
+
|
|
4679
|
+
By default, this supports any nonsingular matrix ``M``, and nonsingularity
|
|
4680
|
+
is checked (unless ``reduced_input`` is specified as ``True``)::
|
|
4681
|
+
|
|
4682
|
+
sage: M1 = matrix([[1,x**10,x**10],[0,1,0],[0,0,1]]) * M
|
|
4683
|
+
sage: M1.is_reduced(row_wise=False) # M1 not column reduced
|
|
4684
|
+
False
|
|
4685
|
+
sage: P1bis = F1.minimal_relation_basis(M1, shifts=[0,1,2], normal_form=True)
|
|
4686
|
+
sage: P1bis == P1 # True since M and M1 have same row space
|
|
4687
|
+
True
|
|
4688
|
+
sage: P = F1.minimal_relation_basis(M1, shifts=[0,1,2], reduced_input=True)
|
|
4689
|
+
sage: P.is_weak_popov(shifts=[0,1,2])
|
|
4690
|
+
False
|
|
4691
|
+
sage: M2 = M.with_row_set_to_multiple_of_row(0, 1, x) # M2 is singular
|
|
4692
|
+
sage: F1.minimal_relation_basis(M2)
|
|
4693
|
+
Traceback (most recent call last):
|
|
4694
|
+
...
|
|
4695
|
+
ValueError: modulus matrix must be nonsingular
|
|
4696
|
+
|
|
4697
|
+
.. SEEALSO::
|
|
4698
|
+
|
|
4699
|
+
:meth:`minimal_approximant_basis`, :meth:`minimal_interpolant_basis`
|
|
4700
|
+
|
|
4701
|
+
TESTS::
|
|
4702
|
+
|
|
4703
|
+
sage: M = x**3 + matrix([[6*x**2, 2, x], [2, 2, 6*x], [x**2, 3, 6]])
|
|
4704
|
+
sage: matrix(pR, 0, 3).minimal_relation_basis(M)
|
|
4705
|
+
[]
|
|
4706
|
+
|
|
4707
|
+
sage: M = matrix(pR, 0, 0)
|
|
4708
|
+
sage: matrix(pR, 2, 0).minimal_relation_basis(M)
|
|
4709
|
+
[1 0]
|
|
4710
|
+
[0 1]
|
|
4711
|
+
|
|
4712
|
+
sage: matrix(pR, 0, 0).minimal_relation_basis(M)
|
|
4713
|
+
[]
|
|
4714
|
+
"""
|
|
4715
|
+
from sage.matrix.constructor import matrix # for matrix.block
|
|
4716
|
+
|
|
4717
|
+
m = self.nrows()
|
|
4718
|
+
n = self.ncols()
|
|
4719
|
+
|
|
4720
|
+
# set default shifts / check shifts dimension
|
|
4721
|
+
if shifts is None:
|
|
4722
|
+
shifts = [0] * m if row_wise else [0] * n
|
|
4723
|
+
elif row_wise and len(shifts) != m:
|
|
4724
|
+
raise ValueError('shifts length should be the row dimension')
|
|
4725
|
+
elif (not row_wise) and len(shifts) != n:
|
|
4726
|
+
raise ValueError('shifts length should be the column dimension')
|
|
4727
|
+
|
|
4728
|
+
# check modulus dimension
|
|
4729
|
+
if row_wise and mod.dimensions() != (n, n):
|
|
4730
|
+
raise ValueError("modulus matrix dimensions must be the column dimension of self")
|
|
4731
|
+
elif (not row_wise) and mod.dimensions() != (m, m):
|
|
4732
|
+
raise ValueError("modulus matrix dimensions must be the row dimension of self")
|
|
4733
|
+
|
|
4734
|
+
# make sure input is reduced, unless guaranteed by the user
|
|
4735
|
+
if not reduced_input:
|
|
4736
|
+
# Ensure reducedness of input mod
|
|
4737
|
+
# Say we work row-wise: we want a column reduced matrix,
|
|
4738
|
+
# left-unimodularly equivalent to mod; among the possibilities we
|
|
4739
|
+
# have at least all shifted row-wise Popov forms of mod (including
|
|
4740
|
+
# the Hermite form). Some choice of shifts might be smarter than
|
|
4741
|
+
# others, but without more information, we will fix the choice to
|
|
4742
|
+
# the uniform [0,...,0]. The informed user may want to do this step
|
|
4743
|
+
# before calling this method, with their own choice of shift.
|
|
4744
|
+
# -> check, in case, to avoid unnecessary computations
|
|
4745
|
+
if not mod.is_reduced(row_wise=(not row_wise), include_zero_vectors=False):
|
|
4746
|
+
mod = mod.popov_form(row_wise=row_wise, include_zero_vectors=False)
|
|
4747
|
+
if not mod.is_square():
|
|
4748
|
+
raise ValueError("modulus matrix must be nonsingular")
|
|
4749
|
+
|
|
4750
|
+
# Ensure self is reduced modulo mod
|
|
4751
|
+
if row_wise:
|
|
4752
|
+
self = self._right_quo_rem_reduced(mod)[1]
|
|
4753
|
+
else:
|
|
4754
|
+
self = self.T._right_quo_rem_reduced(mod.T)[1].T
|
|
4755
|
+
|
|
4756
|
+
# compute extended shift for kernel basis computation
|
|
4757
|
+
# -> for correctness, the constraint on the added part is that it
|
|
4758
|
+
# must have maximum entry at most min(shifts)
|
|
4759
|
+
# [see Lemma 4.2, Neiger-Vu, Computing Canonical Bases of Modules of
|
|
4760
|
+
# Univariate Relations, Proc. ISSAC 2017]
|
|
4761
|
+
min_shift = min(shifts, default=0)
|
|
4762
|
+
if row_wise:
|
|
4763
|
+
extended_shifts = [s - min_shift for s in shifts] + [0]*n
|
|
4764
|
+
else:
|
|
4765
|
+
extended_shifts = [s - min_shift for s in shifts] + [0]*m
|
|
4766
|
+
|
|
4767
|
+
# build matrix for kernel computation
|
|
4768
|
+
if row_wise:
|
|
4769
|
+
F = matrix.block([[self],[mod]])
|
|
4770
|
+
else:
|
|
4771
|
+
F = matrix.block([[self, mod]])
|
|
4772
|
+
|
|
4773
|
+
# compute shifted weak Popov kernel basis
|
|
4774
|
+
kbas = F.minimal_kernel_basis(shifts=extended_shifts, normal_form=normal_form, row_wise=row_wise)
|
|
4775
|
+
|
|
4776
|
+
# extract sought basis and return
|
|
4777
|
+
if row_wise:
|
|
4778
|
+
return kbas[:m,:m]
|
|
4779
|
+
else:
|
|
4780
|
+
return kbas[:n,:n]
|
|
4781
|
+
|
|
4091
4782
|
def _basis_completion_via_reversed_approx(self):
|
|
4092
4783
|
r"""
|
|
4093
4784
|
Return a Smith form-preserving nonsingular completion of a row basis of
|