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.

Files changed (320) hide show
  1. {passagemath_modules-10.5.46.dist-info → passagemath_modules-10.6.20.dist-info}/METADATA +49 -44
  2. {passagemath_modules-10.5.46.dist-info → passagemath_modules-10.6.20.dist-info}/RECORD +320 -314
  3. passagemath_modules.dylibs/libmpc.3.dylib +0 -0
  4. passagemath_modules.dylibs/libopenblasp-r0.3.29.dylib +0 -0
  5. sage/algebras/clifford_algebra.py +2 -2
  6. sage/algebras/clifford_algebra_element.cpython-310-darwin.so +0 -0
  7. sage/algebras/clifford_algebra_element.pyx +4 -2
  8. sage/algebras/exterior_algebra_groebner.cpython-310-darwin.so +0 -0
  9. sage/algebras/exterior_algebra_groebner.pyx +2 -0
  10. sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra.py +83 -5
  11. sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_element.cpython-310-darwin.so +0 -0
  12. sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_element.pxd +2 -0
  13. sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_element.pyx +28 -3
  14. sage/algebras/finite_gca.py +1 -1
  15. sage/algebras/lie_algebras/bgg_dual_module.py +18 -11
  16. sage/algebras/lie_algebras/classical_lie_algebra.py +3 -3
  17. sage/algebras/lie_algebras/examples.py +2 -2
  18. sage/algebras/lie_algebras/free_lie_algebra.py +1 -1
  19. sage/algebras/lie_algebras/heisenberg.py +4 -4
  20. sage/algebras/lie_algebras/lie_algebra.py +1 -1
  21. sage/algebras/lie_algebras/lie_algebra_element.cpython-310-darwin.so +0 -0
  22. sage/algebras/lie_algebras/lie_algebra_element.pyx +63 -27
  23. sage/algebras/lie_algebras/quotient.py +40 -29
  24. sage/algebras/lie_algebras/subalgebra.py +76 -53
  25. sage/algebras/lie_algebras/verma_module.py +1 -3
  26. sage/algebras/octonion_algebra.cpython-310-darwin.so +0 -0
  27. sage/algebras/octonion_algebra.pyx +1 -1
  28. sage/algebras/orlik_solomon.py +4 -4
  29. sage/algebras/orlik_terao.py +4 -4
  30. sage/algebras/steenrod/steenrod_algebra.py +37 -30
  31. sage/algebras/steenrod/steenrod_algebra_bases.py +2 -2
  32. sage/algebras/steenrod/steenrod_algebra_misc.py +4 -4
  33. sage/algebras/steenrod/steenrod_algebra_mult.py +2 -2
  34. sage/all__sagemath_modules.py +1 -0
  35. sage/calculus/integration.cpython-310-darwin.so +0 -0
  36. sage/calculus/integration.pyx +6 -5
  37. sage/calculus/interpolation.cpython-310-darwin.so +0 -0
  38. sage/calculus/interpolators.cpython-310-darwin.so +0 -0
  39. sage/calculus/ode.cpython-310-darwin.so +0 -0
  40. sage/calculus/ode.pxd +2 -2
  41. sage/calculus/ode.pyx +6 -4
  42. sage/calculus/riemann.cpython-310-darwin.so +0 -0
  43. sage/calculus/riemann.pyx +68 -48
  44. sage/calculus/transforms/dwt.cpython-310-darwin.so +0 -0
  45. sage/calculus/transforms/fft.cpython-310-darwin.so +0 -0
  46. sage/coding/ag_code_decoders.cpython-310-darwin.so +0 -0
  47. sage/coding/ag_code_decoders.pyx +31 -31
  48. sage/coding/binary_code.cpython-310-darwin.so +0 -0
  49. sage/coding/binary_code.pxd +6 -6
  50. sage/coding/binary_code.pyx +212 -173
  51. sage/coding/guruswami_sudan/utils.py +3 -5
  52. sage/coding/kasami_codes.cpython-310-darwin.so +0 -0
  53. sage/coding/kasami_codes.pyx +20 -24
  54. sage/coding/linear_code.py +2 -2
  55. sage/coding/linear_code_no_metric.py +5 -5
  56. sage/coding/linear_rank_metric.py +81 -19
  57. sage/combinat/free_module.py +22 -2
  58. sage/combinat/root_system/ambient_space.py +1 -1
  59. sage/combinat/root_system/associahedron.py +4 -4
  60. sage/combinat/root_system/braid_move_calculator.py +1 -1
  61. sage/combinat/root_system/braid_orbit.cpython-310-darwin.so +0 -0
  62. sage/combinat/root_system/branching_rules.py +2 -2
  63. sage/combinat/root_system/cartan_type.py +14 -14
  64. sage/combinat/root_system/coxeter_group.py +2 -2
  65. sage/combinat/root_system/coxeter_type.py +11 -0
  66. sage/combinat/root_system/extended_affine_weyl_group.py +8 -8
  67. sage/combinat/root_system/fundamental_group.py +2 -4
  68. sage/combinat/root_system/hecke_algebra_representation.py +1 -1
  69. sage/combinat/root_system/pieri_factors.py +2 -2
  70. sage/combinat/root_system/root_lattice_realization_algebras.py +1 -1
  71. sage/combinat/root_system/root_lattice_realizations.py +1 -1
  72. sage/combinat/root_system/type_folded.py +3 -3
  73. sage/combinat/root_system/type_reducible.py +8 -7
  74. sage/combinat/root_system/type_super_A.py +2 -2
  75. sage/combinat/root_system/weight_lattice_realizations.py +9 -8
  76. sage/combinat/root_system/weyl_characters.py +1 -1
  77. sage/crypto/__init__.py +1 -0
  78. sage/crypto/block_cipher/des.py +1 -1
  79. sage/crypto/block_cipher/miniaes.py +3 -3
  80. sage/crypto/block_cipher/present.py +3 -3
  81. sage/crypto/block_cipher/sdes.py +3 -3
  82. sage/crypto/boolean_function.cpython-310-darwin.so +0 -0
  83. sage/crypto/boolean_function.pyx +22 -23
  84. sage/crypto/key_exchange/diffie_hellman.py +4 -9
  85. sage/crypto/mq/sr.py +1 -1
  86. sage/crypto/public_key/blum_goldwasser.py +3 -3
  87. sage/crypto/sbox.cpython-310-darwin.so +0 -0
  88. sage/crypto/sbox.pyx +1 -1
  89. sage/crypto/sboxes.py +22 -0
  90. sage/crypto/util.py +4 -6
  91. sage/ext/interpreters/__init__.py +1 -1
  92. sage/ext/interpreters/all__sagemath_modules.py +1 -1
  93. sage/ext/interpreters/wrapper_cc.cpython-310-darwin.so +0 -0
  94. sage/ext/interpreters/wrapper_cc.pxd +5 -5
  95. sage/ext/interpreters/wrapper_cc.pyx +1 -1
  96. sage/ext/interpreters/wrapper_cdf.cpython-310-darwin.so +0 -0
  97. sage/ext/interpreters/wrapper_cdf.pxd +5 -7
  98. sage/ext/interpreters/wrapper_cdf.pyx +4 -10
  99. sage/ext/interpreters/wrapper_rdf.cpython-310-darwin.so +0 -0
  100. sage/ext/interpreters/wrapper_rdf.pxd +1 -1
  101. sage/ext/interpreters/wrapper_rdf.pyx +1 -1
  102. sage/ext/interpreters/wrapper_rr.cpython-310-darwin.so +0 -0
  103. sage/ext/interpreters/wrapper_rr.pxd +5 -5
  104. sage/ext/interpreters/wrapper_rr.pyx +1 -2
  105. sage/geometry/toric_lattice.py +3 -3
  106. sage/geometry/toric_lattice_element.cpython-310-darwin.so +0 -0
  107. sage/groups/additive_abelian/additive_abelian_group.py +1 -1
  108. sage/groups/additive_abelian/qmodnz.py +4 -4
  109. sage/groups/matrix_gps/coxeter_group.py +17 -4
  110. sage/groups/matrix_gps/group_element.cpython-310-darwin.so +0 -0
  111. sage/groups/misc_gps/argument_groups.py +2 -2
  112. sage/groups/misc_gps/imaginary_groups.py +4 -4
  113. sage/groups/perm_gps/partn_ref/refinement_binary.cpython-310-darwin.so +0 -0
  114. sage/groups/perm_gps/partn_ref/refinement_matrices.cpython-310-darwin.so +0 -0
  115. sage/homology/chain_complex.py +0 -2
  116. sage/homology/hochschild_complex.py +3 -3
  117. sage/homology/homology_morphism.py +6 -6
  118. sage/homology/homology_vector_space_with_basis.py +1 -1
  119. sage/libs/gsl/array.cpython-310-darwin.so +0 -0
  120. sage/libs/mpmath/utils.cpython-310-darwin.so +0 -0
  121. sage/matrix/action.cpython-310-darwin.so +0 -0
  122. sage/matrix/args.cpython-310-darwin.so +0 -0
  123. sage/matrix/args.pyx +25 -10
  124. sage/matrix/benchmark.py +8 -4
  125. sage/matrix/compute_J_ideal.py +2 -2
  126. sage/matrix/constructor.cpython-310-darwin.so +0 -0
  127. sage/matrix/echelon_matrix.cpython-310-darwin.so +0 -0
  128. sage/matrix/echelon_matrix.pyx +1 -1
  129. sage/matrix/matrix0.cpython-310-darwin.so +0 -0
  130. sage/matrix/matrix0.pxd +3 -3
  131. sage/matrix/matrix0.pyx +7 -5
  132. sage/matrix/matrix1.cpython-310-darwin.so +0 -0
  133. sage/matrix/matrix1.pyx +87 -48
  134. sage/matrix/matrix2.cpython-310-darwin.so +0 -0
  135. sage/matrix/matrix2.pxd +3 -3
  136. sage/matrix/matrix2.pyx +758 -75
  137. sage/matrix/matrix_cdv.cpython-310-darwin.so +0 -0
  138. sage/matrix/matrix_complex_double_dense.cpython-310-darwin.so +0 -0
  139. sage/matrix/matrix_complex_double_dense.pyx +1 -1
  140. sage/matrix/matrix_dense.cpython-310-darwin.so +0 -0
  141. sage/matrix/matrix_dense.pyx +2 -3
  142. sage/matrix/matrix_double_dense.cpython-310-darwin.so +0 -0
  143. sage/matrix/matrix_double_dense.pyx +11 -5
  144. sage/matrix/matrix_double_sparse.cpython-310-darwin.so +0 -0
  145. sage/matrix/matrix_generic_dense.cpython-310-darwin.so +0 -0
  146. sage/matrix/matrix_generic_sparse.cpython-310-darwin.so +0 -0
  147. sage/matrix/matrix_generic_sparse.pyx +1 -1
  148. sage/matrix/matrix_laurent_mpolynomial_dense.cpython-310-darwin.so +0 -0
  149. sage/matrix/matrix_numpy_dense.cpython-310-darwin.so +0 -0
  150. sage/matrix/matrix_numpy_integer_dense.cpython-310-darwin.so +0 -0
  151. sage/matrix/matrix_polynomial_dense.cpython-310-darwin.so +0 -0
  152. sage/matrix/matrix_polynomial_dense.pyx +952 -261
  153. sage/matrix/matrix_real_double_dense.cpython-310-darwin.so +0 -0
  154. sage/matrix/matrix_sparse.cpython-310-darwin.so +0 -0
  155. sage/matrix/matrix_sparse.pyx +2 -3
  156. sage/matrix/matrix_window.cpython-310-darwin.so +0 -0
  157. sage/matrix/matrix_window.pyx +2 -2
  158. sage/matrix/misc_mpfr.cpython-310-darwin.so +0 -0
  159. sage/matrix/operation_table.py +0 -2
  160. sage/matrix/special.py +4 -0
  161. sage/matrix/strassen.cpython-310-darwin.so +0 -0
  162. sage/matrix/strassen.pyx +1 -1
  163. sage/matroids/basis_exchange_matroid.cpython-310-darwin.so +0 -0
  164. sage/matroids/basis_matroid.cpython-310-darwin.so +0 -0
  165. sage/matroids/chow_ring.py +68 -65
  166. sage/matroids/chow_ring_ideal.py +41 -38
  167. sage/matroids/circuit_closures_matroid.cpython-310-darwin.so +0 -0
  168. sage/matroids/circuits_matroid.cpython-310-darwin.so +0 -0
  169. sage/matroids/database_matroids.py +16 -5
  170. sage/matroids/dual_matroid.py +2 -2
  171. sage/matroids/extension.cpython-310-darwin.so +0 -0
  172. sage/matroids/flats_matroid.cpython-310-darwin.so +0 -0
  173. sage/matroids/gammoid.py +1 -1
  174. sage/matroids/graphic_matroid.cpython-310-darwin.so +0 -0
  175. sage/matroids/graphic_matroid.pyx +3 -3
  176. sage/matroids/lean_matrix.cpython-310-darwin.so +0 -0
  177. sage/matroids/lean_matrix.pyx +22 -22
  178. sage/matroids/linear_matroid.cpython-310-darwin.so +0 -0
  179. sage/matroids/linear_matroid.pyx +13 -13
  180. sage/matroids/matroid.cpython-310-darwin.so +0 -0
  181. sage/matroids/matroid.pyx +15 -15
  182. sage/matroids/matroids_plot_helpers.py +48 -46
  183. sage/matroids/minor_matroid.py +2 -2
  184. sage/matroids/set_system.cpython-310-darwin.so +0 -0
  185. sage/matroids/transversal_matroid.cpython-310-darwin.so +0 -0
  186. sage/matroids/transversal_matroid.pyx +3 -3
  187. sage/matroids/union_matroid.cpython-310-darwin.so +0 -0
  188. sage/matroids/union_matroid.pyx +3 -0
  189. sage/matroids/unpickling.cpython-310-darwin.so +0 -0
  190. sage/matroids/utilities.py +2 -2
  191. sage/misc/c3.cpython-310-darwin.so +0 -0
  192. sage/misc/compat.py +1 -2
  193. sage/misc/pickle_old.cpython-310-darwin.so +0 -0
  194. sage/modules/diamond_cutting.py +117 -30
  195. sage/modules/fg_pid/fgp_module.py +3 -3
  196. sage/modules/filtered_vector_space.py +4 -4
  197. sage/modules/finite_submodule_iter.cpython-310-darwin.so +0 -0
  198. sage/modules/fp_graded/free_module.py +2 -2
  199. sage/modules/fp_graded/module.py +2 -2
  200. sage/modules/fp_graded/morphism.py +4 -4
  201. sage/modules/fp_graded/steenrod/morphism.py +1 -1
  202. sage/modules/free_module.py +144 -15
  203. sage/modules/free_module_element.cpython-310-darwin.so +0 -0
  204. sage/modules/free_module_element.pyx +4 -4
  205. sage/modules/free_module_integer.py +2 -2
  206. sage/modules/free_module_morphism.py +3 -3
  207. sage/modules/free_module_pseudohomspace.py +352 -0
  208. sage/modules/free_module_pseudomorphism.py +578 -0
  209. sage/modules/free_quadratic_module_integer_symmetric.py +24 -13
  210. sage/modules/matrix_morphism.py +9 -9
  211. sage/modules/multi_filtered_vector_space.py +4 -4
  212. sage/modules/ore_module.py +2208 -0
  213. sage/modules/ore_module_element.py +178 -0
  214. sage/modules/ore_module_homspace.py +147 -0
  215. sage/modules/ore_module_morphism.py +968 -0
  216. sage/modules/quotient_module.py +11 -1
  217. sage/modules/submodule.py +1 -1
  218. sage/modules/torsion_quadratic_module.py +1 -1
  219. sage/modules/vector_complex_double_dense.cpython-310-darwin.so +0 -0
  220. sage/modules/vector_double_dense.cpython-310-darwin.so +0 -0
  221. sage/modules/vector_integer_dense.cpython-310-darwin.so +0 -0
  222. sage/modules/vector_integer_sparse.cpython-310-darwin.so +0 -0
  223. sage/modules/vector_integer_sparse.pyx +4 -4
  224. sage/modules/vector_modn_dense.cpython-310-darwin.so +0 -0
  225. sage/modules/vector_modn_sparse.cpython-310-darwin.so +0 -0
  226. sage/modules/vector_numpy_dense.cpython-310-darwin.so +0 -0
  227. sage/modules/vector_numpy_integer_dense.cpython-310-darwin.so +0 -0
  228. sage/modules/vector_rational_dense.cpython-310-darwin.so +0 -0
  229. sage/modules/vector_rational_dense.pyx +1 -1
  230. sage/modules/vector_rational_sparse.cpython-310-darwin.so +0 -0
  231. sage/modules/vector_rational_sparse.pyx +5 -5
  232. sage/modules/vector_real_double_dense.cpython-310-darwin.so +0 -0
  233. sage/modules/vector_space_morphism.py +2 -2
  234. sage/modules/with_basis/cell_module.py +17 -0
  235. sage/modules/with_basis/indexed_element.cpython-310-darwin.so +0 -0
  236. sage/modules/with_basis/indexed_element.pyx +1 -1
  237. sage/modules/with_basis/invariant.py +1 -1
  238. sage/modules/with_basis/representation.py +0 -1
  239. sage/modules/with_basis/subquotient.py +2 -2
  240. sage/numerical/gauss_legendre.cpython-310-darwin.so +0 -0
  241. sage/probability/probability_distribution.cpython-310-darwin.so +0 -0
  242. sage/quadratic_forms/binary_qf.py +7 -7
  243. sage/quadratic_forms/bqf_class_group.py +26 -92
  244. sage/quadratic_forms/count_local_2.cpython-310-darwin.so +0 -0
  245. sage/quadratic_forms/extras.py +1 -1
  246. sage/quadratic_forms/quadratic_form.py +5 -4
  247. sage/quadratic_forms/quadratic_form__equivalence_testing.py +7 -4
  248. sage/quadratic_forms/quadratic_form__evaluate.cpython-310-darwin.so +0 -0
  249. sage/quadratic_forms/quadratic_form__local_field_invariants.py +10 -10
  250. sage/quadratic_forms/quadratic_form__ternary_Tornaria.py +2 -2
  251. sage/quadratic_forms/ternary.cpython-310-darwin.so +0 -0
  252. sage/quadratic_forms/ternary_qf.py +50 -83
  253. sage/rings/complex_conversion.cpython-310-darwin.so +0 -0
  254. sage/rings/complex_double.cpython-310-darwin.so +0 -0
  255. sage/rings/complex_double.pxd +1 -0
  256. sage/rings/complex_double.pyx +37 -32
  257. sage/rings/complex_mpc.cpython-310-darwin.so +0 -0
  258. sage/rings/complex_mpc.pyx +27 -23
  259. sage/rings/complex_mpfr.cpython-310-darwin.so +0 -0
  260. sage/rings/complex_mpfr.pyx +11 -9
  261. sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py +277 -21
  262. sage/rings/function_field/drinfeld_modules/drinfeld_module.py +10 -1
  263. sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +1 -1
  264. sage/rings/function_field/drinfeld_modules/homset.py +1 -2
  265. sage/rings/function_field/drinfeld_modules/morphism.py +2 -2
  266. sage/rings/function_field/hermite_form_polynomial.cpython-310-darwin.so +0 -0
  267. sage/rings/function_field/khuri_makdisi.cpython-310-darwin.so +0 -0
  268. sage/rings/function_field/khuri_makdisi.pyx +27 -25
  269. sage/rings/invariants/invariant_theory.py +61 -60
  270. sage/rings/invariants/reconstruction.py +8 -8
  271. sage/rings/polynomial/laurent_polynomial_mpair.cpython-310-darwin.so +0 -0
  272. sage/rings/polynomial/ore_function_element.py +1 -1
  273. sage/rings/polynomial/ore_polynomial_element.cpython-310-darwin.so +0 -0
  274. sage/rings/polynomial/ore_polynomial_element.pyx +8 -8
  275. sage/rings/polynomial/ore_polynomial_ring.py +134 -17
  276. sage/rings/polynomial/polynomial_real_mpfr_dense.cpython-310-darwin.so +0 -0
  277. sage/rings/polynomial/skew_polynomial_element.cpython-310-darwin.so +0 -0
  278. sage/rings/polynomial/skew_polynomial_finite_field.cpython-310-darwin.so +0 -0
  279. sage/rings/polynomial/skew_polynomial_finite_field.pyx +3 -4
  280. sage/rings/polynomial/skew_polynomial_finite_order.cpython-310-darwin.so +0 -0
  281. sage/rings/polynomial/skew_polynomial_finite_order.pyx +2 -5
  282. sage/rings/real_double_element_gsl.cpython-310-darwin.so +0 -0
  283. sage/rings/real_mpfr.cpython-310-darwin.so +0 -0
  284. sage/rings/real_mpfr.pyx +25 -7
  285. sage/rings/ring_extension.cpython-310-darwin.so +0 -0
  286. sage/rings/ring_extension.pyx +4 -2
  287. sage/rings/ring_extension_conversion.cpython-310-darwin.so +0 -0
  288. sage/rings/ring_extension_element.cpython-310-darwin.so +0 -0
  289. sage/rings/ring_extension_element.pyx +42 -0
  290. sage/rings/ring_extension_morphism.cpython-310-darwin.so +0 -0
  291. sage/schemes/projective/cohomology.py +2 -2
  292. sage/stats/basic_stats.py +9 -6
  293. sage/stats/distributions/dgs_misc.h +11 -4
  294. sage/stats/distributions/discrete_gaussian_integer.cpython-310-darwin.so +0 -0
  295. sage/stats/distributions/discrete_gaussian_integer.pyx +9 -7
  296. sage/stats/hmm/chmm.cpython-310-darwin.so +0 -0
  297. sage/stats/hmm/chmm.pyx +13 -13
  298. sage/stats/hmm/distributions.cpython-310-darwin.so +0 -0
  299. sage/stats/hmm/distributions.pxd +3 -3
  300. sage/stats/hmm/distributions.pyx +3 -3
  301. sage/stats/hmm/hmm.cpython-310-darwin.so +0 -0
  302. sage/stats/hmm/hmm.pxd +3 -3
  303. sage/stats/hmm/hmm.pyx +6 -6
  304. sage/stats/hmm/util.cpython-310-darwin.so +0 -0
  305. sage/stats/hmm/util.pyx +6 -6
  306. sage/stats/intlist.cpython-310-darwin.so +0 -0
  307. sage/stats/intlist.pxd +3 -3
  308. sage/stats/time_series.cpython-310-darwin.so +0 -0
  309. sage/tensor/modules/alternating_contr_tensor.py +3 -3
  310. sage/tensor/modules/comp.py +3 -3
  311. sage/tensor/modules/ext_pow_free_module.py +3 -3
  312. sage/tensor/modules/format_utilities.py +3 -3
  313. sage/tensor/modules/free_module_linear_group.py +3 -3
  314. sage/tensor/modules/free_module_morphism.py +0 -1
  315. sage/tensor/modules/tensor_free_module.py +3 -3
  316. sage/tensor/modules/tensor_free_submodule.py +1 -1
  317. sage/tensor/modules/tensor_free_submodule_basis.py +1 -1
  318. sage/tensor/modules/tensor_with_indices.py +5 -5
  319. {passagemath_modules-10.5.46.dist-info → passagemath_modules-10.6.20.dist-info}/WHEEL +0 -0
  320. {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 = Matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]])
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 = Matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]])
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
- The zero matrix has degree ``-1``::
155
+ A zero matrix (including empty matrices) has degree ``-1``::
154
156
 
155
- sage: M = Matrix(pR, 2, 3)
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
- Traceback (most recent call last):
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
- raise ValueError('empty matrix does not have a degree')
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 = Matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]])
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( ZZ, [[ self[i,j].degree() + shifts[j]
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( ZZ, [[ self[i,j].degree() + shifts[i]
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 = Matrix([
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 = Matrix([
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 = Matrix(pR, [[1,5,2], [3,1,5]]); M.is_constant()
280
+ sage: M = matrix(pR, [[1,5,2], [3,1,5]]); M.is_constant()
284
281
  True
285
- sage: M = Matrix.zero(pR, 3, 5); M.is_constant()
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 = Matrix([
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 = Matrix([
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 = Matrix([
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 = Matrix([
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 = Matrix(pR, 3, 3,
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 = Matrix(pR, 3, 3,
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 = Matrix(pR, 2, 3,
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 = Matrix([[3*x^2 + 4*x + 1, 4*x]])
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 = Matrix(pR, 3, 3,
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 = Matrix(pR, 3, 2,
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 = Matrix([[2*x^2 + 5*x + 1], [3*x^2+4]])
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 = Matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]])
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 = Matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0], [0, 0, 0]])
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 an empty matrix (`0\times n` or `m\times 0`) is
1124
- not defined::
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 = Matrix(pR, 0, 3)
1174
+ sage: M = matrix(pR, 0, 3)
1127
1175
  sage: M.row_degrees()
1128
- Traceback (most recent call last):
1129
- ...
1130
- ValueError: empty matrix does not have row degrees
1176
+ []
1177
+ sage: M.row_degrees(shifts=[1,2,3])
1178
+ []
1131
1179
 
1132
- sage: M = Matrix(pR, 3, 0)
1180
+ sage: M = matrix(pR, 3, 0)
1133
1181
  sage: M.row_degrees()
1134
- Traceback (most recent call last):
1135
- ...
1136
- ValueError: empty matrix does not have row degrees
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.ncols() == 0 or self.nrows() == 0:
1140
- raise ValueError('empty matrix does not have row degrees')
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 = Matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]])
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 an empty matrix (`0\times n` or `m\times 0`) is
1187
- not defined::
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 = Matrix(pR, 0, 3)
1240
+ sage: M = matrix(pR, 3, 0)
1190
1241
  sage: M.column_degrees()
1191
- Traceback (most recent call last):
1192
- ...
1193
- ValueError: empty matrix does not have column degrees
1242
+ []
1243
+ sage: M.column_degrees(shifts=[1,2,3])
1244
+ []
1194
1245
 
1195
- sage: M = Matrix(pR, 3, 0)
1246
+ sage: M = matrix(pR, 0, 3)
1196
1247
  sage: M.column_degrees()
1197
- Traceback (most recent call last):
1198
- ...
1199
- ValueError: empty matrix does not have column degrees
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.ncols() == 0 or self.nrows() == 0:
1207
- raise ValueError('empty matrix does not have column degrees')
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 = Matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]])
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([ [ self[i,j].leading_coefficient()
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([ [ self[i,j].leading_coefficient()
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([ [ self[i,j].leading_coefficient()
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([ [ self[i,j].leading_coefficient()
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 = Matrix(pR, 0, 0)
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 = Matrix(pR, 0, 3)
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 = Matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]])
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 = Matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0], [0, 1, 0]])
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 = Matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]])
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`) is not defined::
1571
+ or `m\times 0`) are taken by convention as follows::
1510
1572
 
1511
- sage: M = Matrix(pR, 0, 3)
1512
- sage: M.leading_positions()
1513
- Traceback (most recent call last):
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 = Matrix(pR, 3, 0)
1523
- sage: M.leading_positions(row_wise=False)
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=min(shifts) - 1
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=min(shifts) - 1
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 = Matrix([ [x^3+3*x^2+6*x+6, 3*x^2+3*x+6, 4*x^2+x+3],
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 = Matrix([
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 = Matrix([
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 = Matrix(pR, [[x^4+6*x^3+4*x+4, 3*x+6, 3 ],
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 = Matrix(pR, [[x^4+3*x^3+x^2+2*x+6, x^3+5*x^2+5*x+1],
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 = Matrix(pR, [[x^4+3*x^3+x^2+2*x+6, 6*x+1 ],
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 = Matrix(pR, [[x^4+6*x^3+4*x+4, 3*x+6, 3 ],
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 = Matrix(pR, [[x+5, 0, 0 ],
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 = Matrix(pR, [
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 identity_matrix
2180
- U = identity_matrix(R, m)
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 = Matrix(pR, [
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 = Matrix(pR, 3, 2,
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 = Matrix(pR, 3, 3,
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 = Matrix(pR, 2, 3,
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 = Matrix(pR, 3, 3,
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 = Matrix(pR, 3, 3,
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 = Matrix(pR, 3, 3,
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 = Matrix(pR, 2, 3,
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 = Matrix(pR, 2, 3,
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 = Matrix(pR, 1, 2, [[x, x]])
2854
- sage: A = Matrix(pR, 1, 2, [[x, x+2]])
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 + Matrix([[0,2]])
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 = Matrix(pR, 2, 3,
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 = Matrix(pR, 3, 3,
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 = Matrix(pR, 3, 3,
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 = Matrix(pR, 2, 3,
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 = Matrix(pR, 3, 3,
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 = Matrix(pR, 3, 3,
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 = Matrix(pR, 2, 3,
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 = Matrix(pR, 2, 3,
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 = Matrix(pR, 1, 2, [[x, x]])
3065
- sage: A = Matrix(pR, 1, 2, [[x, x+2]])
3066
- sage: A == 1*B + Matrix([[0,2]]) # a valid quo_rem
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 = Matrix(pR, [
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 = Matrix(pR, 1, 3, [
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 = Matrix(pR, 2, 2,
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 = Matrix(pR, 5, 1, [
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 = Matrix(pR, [
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 * Matrix.identity(pR, 5)
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 = Matrix(pR, [x^8])
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: Matrix(pR, [x^8]).is_minimal_approximant_basis(pmat, 8)
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 positive integers, then an approximant
3472
- basis for `F` at order `(d_0,\ldots,d_{n-1})` is a polynomial matrix
3473
- whose rows form a basis of the module of approximants for `F` at order
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 positive integers, or a positive integer
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 = Matrix(pR, [[5*x^3 + 4*x^2 + 4*x + 6, 5*x^2 + 4*x + 1],
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=None))
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
- An error is raised if order does not contain only positive integers::
3697
+ .. SEEALSO::
3574
3698
 
3575
- sage: P = F.minimal_approximant_basis([1,0], shifts)
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.transpose()._approximant_basis_iterative(
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
- (see :meth:`minimal_approximant_basis` for definitions).
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 positive integers
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 = Matrix(pR, [[5*x^3 + 4*x^2 + 4*x + 6, 5, 4],
3679
- ....: [2*x^3 + 2*x^2 + 2*x + 3, 6, 6*x + 3]])
3680
- sage: appbas, rdeg = pmat._approximant_basis_iterative(order,
3681
- ....: shifts)
3682
- sage: appbas.is_minimal_approximant_basis(pmat, order, shifts)
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
- The returned list is the shifted row degrees of ``appbas``::
3801
+ Zero or negative orders are supported::
3686
3802
 
3687
- sage: rdeg == appbas.row_degrees(shifts)
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 = Matrix(pR, 3, 2)
3694
- sage: appbas,rdeg = pmat._approximant_basis_iterative([2,5],
3695
- ....: [5,0,-4])
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
- # Define parameters and perform some sanity checks
3700
- m = self.nrows()
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
- # 'rest_order': the orders that remains to be dealt with
3706
- # 'rest_index': indices of orders that remains to be dealt with
3707
- rest_order = list(order)
3708
- rest_index = list(range(n))
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
- from sage.matrix.constructor import identity_matrix
3713
- appbas = identity_matrix(polynomial_ring, m)
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' will be the shifts-row degrees of
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 = list(shifts)
3837
+ rdeg = [s for s in shifts]
3720
3838
 
3721
- while rest_order:
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-rest_order (entrywise subtraction)
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 rest_order[j] > 0
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 (--> process 'self' degree-wise, and
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 (barely) slower
3737
- max_rest_order = max(rest_order)
3738
- for ind, value in enumerate(rest_order):
3739
- if value == max_rest_order:
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[rest_index[j]] - rest_order[j]
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 X^{d+1}
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
- appbas.rescale_row(pi, X)
3770
- residuals.rescale_row(pi, X)
3771
-
3772
- # Decrement rest_order[j], unless there is no more work to do in
3773
- # this column, i.e. if rest_order[j] was 1:
3774
- # in this case remove the column j of
3775
- # residual,rest_order,rest_index
3776
- if rest_order[j] == 1:
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
- rest_order.pop(j)
3779
- rest_index.pop(j)
3895
+ rem_order.pop(j)
3896
+ rem_index.pop(j)
3780
3897
  else:
3781
- rest_order[j] -= 1
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 = Matrix(pR, [[1], [x], [x**2]])
4302
+ sage: pmat = matrix(pR, [[1], [x], [x**2]])
3827
4303
 
3828
- sage: kerbas = Matrix(pR, [[x,-1,0], [0,x,-1]])
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 = Matrix(pR, [[x**2,0,-1], [0,x,-1]])
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 = Matrix(pR, [[-x,-x**2], [1,0], [0,1]])
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 = Matrix([[(x+1)*(x+3)], [(x+1)*(x+3)+1]])
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 = Matrix([[(x+1)*(x+3)], [(x+1)*(x+4)]])
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 = Matrix(pR, [[1, x, x**2]])
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: Matrix(pR, 2, 1).minimal_kernel_basis()
4449
+ sage: matrix(pR, 2, 1).minimal_kernel_basis()
3974
4450
  [1 0]
3975
4451
  [0 1]
3976
4452
 
3977
- sage: Matrix(pR, 2, 0).minimal_kernel_basis()
4453
+ sage: matrix(pR, 2, 0).minimal_kernel_basis()
3978
4454
  [1 0]
3979
4455
  [0 1]
3980
4456
 
3981
- sage: Matrix(pR, 0, 2).minimal_kernel_basis()
4457
+ sage: matrix(pR, 0, 2).minimal_kernel_basis()
3982
4458
  []
3983
4459
 
3984
- sage: Matrix(pR, 3, 2, [[1,0],[1,0],[1,0]]).minimal_kernel_basis()
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: Matrix(pR, 3, 2, [[x,0],[1,0],[x+1,0]]).minimal_kernel_basis()
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: Matrix(pR, 2, 0).minimal_kernel_basis().is_sparse()
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` already has all entries strictly positive
4036
- # -> if d==0, then `orders[i]` is zero exactly when the column i
4037
- # of self is zero; we may as well take orders[i] == 1 for such
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
- # of self is zero; we may as well take orders[i] == 1 for such
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