Dense matrices over univariate polynomials over fields¶
The implementation inherits from Matrix_generic_dense but some algorithms are optimized for polynomial matrices.
AUTHORS:
- Kwankyu Lee (2016-12-15): initial version with code moved from other files. 
- Johan Rosenkilde (2017-02-07): added weak_popov_form() 
- Vincent Neiger (2018-06-13): added basic functions (row/column degrees, leading positions, leading matrix, testing reduced and canonical forms) 
- Vincent Neiger (2018-09-29): added functions for computing and for verifying minimal approximant bases 
- Vincent Neiger (2020-04-01): added functions for computing and for verifying minimal kernel bases 
- Vincent Neiger (2021-03-11): added matrix-wise basic functions for univariate polynomials (shifts, reverse, truncate, get coefficient of specified degree) 
- Vincent Neiger (2021-07-29): added popov_form(). Added more options to weak_popov_form() (column-wise, ordered, zero rows). 
- Vincent Neiger (2021-08-07): added inverse_series_trunc(), solve_{left/right}_series_trunc(), {left/right}_quo_rem(), reduce(). 
- Vincent Neiger (2024-02-13): added basis_completion(), _is_basis_completion(), _basis_completion_via_reversed_approx(). 
- Vincent Neiger (2025-02-16): added minimal_relation_basis(), minimal_interpolation_basis(). 
- class sage.matrix.matrix_polynomial_dense.Matrix_polynomial_dense[source]¶
- Bases: - Matrix_generic_dense- Dense matrix over a univariate polynomial ring over a field. - For a field \(\Bold{K}\), we consider matrices over the univariate polynomial ring \(\Bold{K}[x]\). - They are often used to represent bases of some \(\Bold{K}[x]\)-modules. In this context, there are two possible representations which are both commonly used in the literature. - Working column-wise: each column of the matrix is a vector in the basis; then, a \(\Bold{K}[x]\)-submodule of \(\Bold{K}[x]^{m}\) of rank \(n\) is represented by an \(m \times n\) matrix, whose columns span the module (via \(\Bold{K}[x]\)-linear combinations). This matrix has full rank, and \(n \leq m\). 
- Working row-wise: each row of the matrix is a vector in the basis; then, a \(\Bold{K}[x]\)-submodule of \(\Bold{K}[x]^{n}\) of rank \(m\) is represented by an \(m \times n\) matrix, whose rows span the module (via \(\Bold{K}[x]\)-linear combinations). This matrix has full rank, and \(m \leq n\). 
 - For the rest of this class description, we assume that one is working row-wise. For a given such module, all its bases are equivalent under left-multiplication by a unimodular matrix, that is, a square matrix which has determinant in \(\Bold{K}\setminus\{0\}\). - There are bases which are called reduced or minimal: their rows have the minimal degree possible among all bases of this module; here the degree of a row is the maximum of the degrees of the entries of the row. An equivalent condition is that the leading matrix of this basis has full rank (see - leading_matrix(),- reduced_form(),- is_reduced()). There is a unique minimal basis, called the Popov basis of the module, which satisfies some additional normalization condition (see- popov_form(),- is_popov()).- These notions can be extended via a more general degree measure, involving a tuple of integers which is called shift and acts as column degree shifts in the definition of row degree. Precisely, for given \(s_1,\ldots,s_n \in \ZZ\) and a row vector \([p_1 \; \cdots \; p_n] \in \Bold{K}[x]^{1 \times n}\), its shifted row degree is the maximum of \(\deg(p_j) + s_j\) for \(1 \leq j \leq n\) (see - row_degrees()). Then, reduced bases and Popov bases are defined similarly, with respect to this notion of degree.- Another important canonical basis is the Hermite basis, which is an upper triangular matrix satisfying a normalization condition similar to that for the Popov basis. In fact, if \(d\) is the largest degree appearing in the Hermite basis, then the Hermite basis coincide with the shifted Popov basis with the shifts \(((n-1)d,\ldots,2d,d,0)\). - basis_completion(row_wise=True, algorithm='approximant')[source]¶
- Return a Smith form-preserving nonsingular completion of a basis of this matrix: row-wise completing a row basis if - row_wiseis True; column-wise completing a column basis if it is False.- For a more detailed description, consider the row-wise case (the column-wise case is the same up to matrix transposition). Let \(A\) be the input matrix, \(m \times n\) over univariate polynomials \(\Bold{K}[x]\), for some field \(\Bold{K}\), and let \(r\) be the rank of \(A\), which is unknown a priori. This computes a matrix \(C\) of dimensions \((n-r) \times n\) such that stacking both matrices one above the other, say \([[A],[C]]\), gives a matrix of maximal rank \(n\) and with the same nontrivial Smith factors as \(A\). In particular, \(C\) has full row rank, and the rank of the input matrix may be recovered from the number of rows of \(C\). - As a consequence, if \(B\) is a basis of the module generated by the rows of \(A\) (for example \(B = A\) if \(A\) has full row rank), then \([[B],[C]]\) is nonsingular, and its determinant is the product of the nonzero Smith factors of \(A\) up to multiplication by a nonzero element of \(\Bold{K}\). - In particular, for \(A\) with full row rank: if the rows \(A\) can be completed into a basis of \(\Bold{K}[x]^{n}\) (or equivalently, \(A\) has unimodular column bases, or also, if the rows of \(A\) generate all polynomial vectors in the rational row space of \(A\)), then \(C\) provides such a completion. In this case, \([[A],[C]]\) is unimodular: it is invertible over \(\Bold{K}[x]\), and \(det([[A],[C]])\) is a nonzero element of the base field \(\Bold{K}\). - INPUT: - row_wise– boolean (default:- True); if- Truethen compute a row-wise completion, else compute a column-wise completion
- algorithm– (default:- 'approximant') selects the approach for computing the completion; currently supported:- 'approximant'and- 'smith'
 - OUTPUT: a matrix over the same base ring as the input matrix, which forms a completion as defined above - ALGORITHM: - 'approximant'– the approximant-based algorithm follows the ideas in [ZL2014] , based on polynomial reversals combined with the computation of a minimal kernel basis and a minimal approximant basis.
- 'smith'– the Smith form-based algorithm computes the Smith form of this matrix along with corresponding unimodular transformations, from which a completion is readily obtained.
 - EXAMPLES: - Three polynomials whose GCD is \(1\) can be completed into a unimodular matrix: - sage: ring.<x> = GF(7)[] sage: mat = matrix([[x*(x-1)*(x-2), (x-2)*(x-3)*(x-4), (x-4)*(x-5)*(x-6)]]) sage: mat [ x^3 + 4*x^2 + 2*x x^3 + 5*x^2 + 5*x + 4 x^3 + 6*x^2 + 4*x + 6] sage: rcomp = mat.basis_completion(); rcomp [ 5*x^2 + 4*x + 1 5*x^2 + 2*x 5*x^2] [ 2*x^3 + 4*x^2 2*x^3 + 6*x^2 + 2*x + 1 2*x^3 + x^2 + 3*x] sage: basis = mat.stack(rcomp); basis [ x^3 + 4*x^2 + 2*x x^3 + 5*x^2 + 5*x + 4 x^3 + 6*x^2 + 4*x + 6] [ 5*x^2 + 4*x + 1 5*x^2 + 2*x 5*x^2] [ 2*x^3 + 4*x^2 2*x^3 + 6*x^2 + 2*x + 1 2*x^3 + x^2 + 3*x] sage: basis.determinant() 6 - >>> from sage.all import * >>> ring = GF(Integer(7))['x']; (x,) = ring._first_ngens(1) >>> mat = matrix([[x*(x-Integer(1))*(x-Integer(2)), (x-Integer(2))*(x-Integer(3))*(x-Integer(4)), (x-Integer(4))*(x-Integer(5))*(x-Integer(6))]]) >>> mat [ x^3 + 4*x^2 + 2*x x^3 + 5*x^2 + 5*x + 4 x^3 + 6*x^2 + 4*x + 6] >>> rcomp = mat.basis_completion(); rcomp [ 5*x^2 + 4*x + 1 5*x^2 + 2*x 5*x^2] [ 2*x^3 + 4*x^2 2*x^3 + 6*x^2 + 2*x + 1 2*x^3 + x^2 + 3*x] >>> basis = mat.stack(rcomp); basis [ x^3 + 4*x^2 + 2*x x^3 + 5*x^2 + 5*x + 4 x^3 + 6*x^2 + 4*x + 6] [ 5*x^2 + 4*x + 1 5*x^2 + 2*x 5*x^2] [ 2*x^3 + 4*x^2 2*x^3 + 6*x^2 + 2*x + 1 2*x^3 + x^2 + 3*x] >>> basis.determinant() 6 - The following matrix has rank \(2\) and trivial Smith form. It can be completed row-wise into a \(3 \times 3\) unimodular matrix (column-wise, there is nothing to complete): - sage: mat = matrix(ring, 2, 3, \ ....: [[x^2 + 5*x + 5, 3*x^2 + x + 3, 4*x^2 + 5*x + 4], \ ....: [5*x^2 + 4*x, 3*x^2 + 4*x + 5, 5*x^2 + 5*x + 3]]) sage: rcomp = mat.basis_completion(); rcomp [ 2*x^2 + 1 4*x^2 + 3*x 2*x^2 + 3*x] sage: mat.stack(rcomp).determinant() 3 sage: mat.basis_completion(row_wise=False) [] - >>> from sage.all import * >>> mat = matrix(ring, Integer(2), Integer(3), [[x**Integer(2) + Integer(5)*x + Integer(5), Integer(3)*x**Integer(2) + x + Integer(3), Integer(4)*x**Integer(2) + Integer(5)*x + Integer(4)], [Integer(5)*x**Integer(2) + Integer(4)*x, Integer(3)*x**Integer(2) + Integer(4)*x + Integer(5), Integer(5)*x**Integer(2) + Integer(5)*x + Integer(3)]]) >>> rcomp = mat.basis_completion(); rcomp [ 2*x^2 + 1 4*x^2 + 3*x 2*x^2 + 3*x] >>> mat.stack(rcomp).determinant() 3 >>> mat.basis_completion(row_wise=False) [] - The following matrix has rank 1 and its nonzero Smith factor is \(x+3\). A row-wise completion has a single nonzero row, whereas a column-wise completion has two columns; in both cases, the Smith form is preserved: - sage: mat = matrix(ring, 3, 2, \ ....: [[ x^3 + x^2 + 5*x + 5, 2*x^3 + 2*x + 4], \ ....: [ 3*x^3 + 2*x^2 + x + 3, 6*x^3 + 5*x^2 + x + 1], \ ....: [2*x^3 + 5*x^2 + 3*x + 4, 4*x^3 + 6*x^2 + 5*x + 6]]) sage: mat.smith_form(transformation=False) [x + 3 0] [ 0 0] [ 0 0] sage: rcomp = mat.basis_completion(); rcomp [x + 1 2*x] sage: ccomp = mat.basis_completion(row_wise=False); ccomp [3*x + 1 4*x + 4] [ 2*x 5*x + 1] [ 6*x x] sage: rcomp.stack(mat).smith_form(transformation=False) [ 1 0] [ 0 x + 3] [ 0 0] [ 0 0] sage: ccomp.augment(mat).smith_form(transformation=False) [ 1 0 0 0] [ 0 1 0 0] [ 0 0 x + 3 0] - >>> from sage.all import * >>> mat = matrix(ring, Integer(3), Integer(2), [[ x**Integer(3) + x**Integer(2) + Integer(5)*x + Integer(5), Integer(2)*x**Integer(3) + Integer(2)*x + Integer(4)], [ Integer(3)*x**Integer(3) + Integer(2)*x**Integer(2) + x + Integer(3), Integer(6)*x**Integer(3) + Integer(5)*x**Integer(2) + x + Integer(1)], [Integer(2)*x**Integer(3) + Integer(5)*x**Integer(2) + Integer(3)*x + Integer(4), Integer(4)*x**Integer(3) + Integer(6)*x**Integer(2) + Integer(5)*x + Integer(6)]]) >>> mat.smith_form(transformation=False) [x + 3 0] [ 0 0] [ 0 0] >>> rcomp = mat.basis_completion(); rcomp [x + 1 2*x] >>> ccomp = mat.basis_completion(row_wise=False); ccomp [3*x + 1 4*x + 4] [ 2*x 5*x + 1] [ 6*x x] >>> rcomp.stack(mat).smith_form(transformation=False) [ 1 0] [ 0 x + 3] [ 0 0] [ 0 0] >>> ccomp.augment(mat).smith_form(transformation=False) [ 1 0 0 0] [ 0 1 0 0] [ 0 0 x + 3 0] - Here are a few more examples, similar to the above but over fields other than - GF(7):- sage: ring.<x> = QQ[] sage: mat = matrix([[x*(x-1)*(x-2), (x-2)*(x-3)*(x-4), (x-4)*(x-5)*(x-6)]]) sage: mat [ x^3 - 3*x^2 + 2*x x^3 - 9*x^2 + 26*x - 24 x^3 - 15*x^2 + 74*x - 120] sage: rcomp = mat.basis_completion(algorithm='smith'); rcomp [ -1/12*x - 1/12 -1/12*x + 5/12 0] [ 1/12 1/12 1/24*x^2 - 13/24*x + 2] sage: mat.stack(rcomp).determinant() 1 sage: mat = matrix([[x*(x-1), x*(x-2)], \ ....: [x*(x-2), x*(x-3)], \ ....: [(x-1)*(x-2), (x-1)*(x-3)]]) sage: mat.smith_form(transformation=False) [1 0] [0 x] [0 0] sage: ccomp = mat.basis_completion(row_wise=False, algorithm='smith') sage: ccomp [1/2*x - 1/2] [ 1/2*x - 1] [1/2*x - 3/2] sage: ccomp.augment(mat).smith_form(transformation=False) [ 1 0 0] [ 0 1 0] [ 0 0 1/2*x] sage: field.<a> = NumberField(x**2 - 2) sage: ring.<y> = field[] sage: mat = matrix([[3*a*y - 1, (-8*a - 1)*y - 2*a + 1]]) sage: rcomp = mat.basis_completion(algorithm='smith'); rcomp [ 39/119*a - 30/119 -99/119*a + 67/119] sage: mat.stack(rcomp).determinant() 1 - >>> from sage.all import * >>> ring = QQ['x']; (x,) = ring._first_ngens(1) >>> mat = matrix([[x*(x-Integer(1))*(x-Integer(2)), (x-Integer(2))*(x-Integer(3))*(x-Integer(4)), (x-Integer(4))*(x-Integer(5))*(x-Integer(6))]]) >>> mat [ x^3 - 3*x^2 + 2*x x^3 - 9*x^2 + 26*x - 24 x^3 - 15*x^2 + 74*x - 120] >>> rcomp = mat.basis_completion(algorithm='smith'); rcomp [ -1/12*x - 1/12 -1/12*x + 5/12 0] [ 1/12 1/12 1/24*x^2 - 13/24*x + 2] >>> mat.stack(rcomp).determinant() 1 >>> mat = matrix([[x*(x-Integer(1)), x*(x-Integer(2))], [x*(x-Integer(2)), x*(x-Integer(3))], [(x-Integer(1))*(x-Integer(2)), (x-Integer(1))*(x-Integer(3))]]) >>> mat.smith_form(transformation=False) [1 0] [0 x] [0 0] >>> ccomp = mat.basis_completion(row_wise=False, algorithm='smith') >>> ccomp [1/2*x - 1/2] [ 1/2*x - 1] [1/2*x - 3/2] >>> ccomp.augment(mat).smith_form(transformation=False) [ 1 0 0] [ 0 1 0] [ 0 0 1/2*x] >>> field = NumberField(x**Integer(2) - Integer(2), names=('a',)); (a,) = field._first_ngens(1) >>> ring = field['y']; (y,) = ring._first_ngens(1) >>> mat = matrix([[Integer(3)*a*y - Integer(1), (-Integer(8)*a - Integer(1))*y - Integer(2)*a + Integer(1)]]) >>> rcomp = mat.basis_completion(algorithm='smith'); rcomp [ 39/119*a - 30/119 -99/119*a + 67/119] >>> mat.stack(rcomp).determinant() 1 
 - coefficient_matrix(d, row_wise=True)[source]¶
- Return the constant matrix which is obtained from this matrix by taking the coefficient of its entries with degree specified by \(d\). - if \(d\) is an integer, this selects the coefficient of \(d\) for all entries; 
- if \(d\) is a list \((d_1,\ldots,d_m)\) and - row_wiseis- True, this selects the coefficient of degree \(d_i\) for all entries of the \(i\)-th row for each \(i\);
- if \(d\) is a list \((d_1,\ldots,d_n)\) and - row_wiseis- False, this selects the coefficient of degree \(d_i\) for all entries of the \(j\)-th column for each \(j\).
 - INPUT: - d– list of integers, or an integer,
- row_wise– boolean (default:- True); if- True(resp.- False) then \(d\) should be a list of length equal to the row (resp. column) dimension of this matrix
 - OUTPUT: a matrix over the base field - EXAMPLES: - sage: pR.<x> = GF(7)[] sage: M = matrix([ ....: [ x^3+5*x^2+5*x+1, 5, 6*x+4, 0], ....: [ 6*x^2+3*x+1, 1, 2, 0], ....: [2*x^3+4*x^2+6*x+4, 5*x + 1, 2*x^2+5*x+5, x^2+5*x+6] ....: ]) sage: M.coefficient_matrix(2) [5 0 0 0] [6 0 0 0] [4 0 2 1] sage: M.coefficient_matrix(0) == M.constant_matrix() True - >>> from sage.all import * >>> pR = GF(Integer(7))['x']; (x,) = pR._first_ngens(1) >>> M = matrix([ ... [ x**Integer(3)+Integer(5)*x**Integer(2)+Integer(5)*x+Integer(1), Integer(5), Integer(6)*x+Integer(4), Integer(0)], ... [ Integer(6)*x**Integer(2)+Integer(3)*x+Integer(1), Integer(1), Integer(2), Integer(0)], ... [Integer(2)*x**Integer(3)+Integer(4)*x**Integer(2)+Integer(6)*x+Integer(4), Integer(5)*x + Integer(1), Integer(2)*x**Integer(2)+Integer(5)*x+Integer(5), x**Integer(2)+Integer(5)*x+Integer(6)] ... ]) >>> M.coefficient_matrix(Integer(2)) [5 0 0 0] [6 0 0 0] [4 0 2 1] >>> M.coefficient_matrix(Integer(0)) == M.constant_matrix() True - Row-wise and column-wise coefficient extraction are available: - sage: M.coefficient_matrix([3,2,1]) [1 0 0 0] [6 0 0 0] [6 5 5 5] sage: M.coefficient_matrix([2,0,1,3], row_wise=False) [5 5 6 0] [6 1 0 0] [4 1 5 0] - >>> from sage.all import * >>> M.coefficient_matrix([Integer(3),Integer(2),Integer(1)]) [1 0 0 0] [6 0 0 0] [6 5 5 5] >>> M.coefficient_matrix([Integer(2),Integer(0),Integer(1),Integer(3)], row_wise=False) [5 5 6 0] [6 1 0 0] [4 1 5 0] - Negative degrees give zero coefficients: - sage: M.coefficient_matrix([-1,0,1,3], row_wise=False) [0 5 6 0] [0 1 0 0] [0 1 5 0] - >>> from sage.all import * >>> M.coefficient_matrix([-Integer(1),Integer(0),Integer(1),Integer(3)], row_wise=False) [0 5 6 0] [0 1 0 0] [0 1 5 0] - Length of list of degrees is checked: - sage: M.coefficient_matrix([2,1,1,2]) Traceback (most recent call last): ... ValueError: length of input degree list should be the row dimension of the input matrix sage: M.coefficient_matrix([3,2,1], row_wise=False) Traceback (most recent call last): ... ValueError: length of input degree list should be the column dimension of the input matrix - >>> from sage.all import * >>> M.coefficient_matrix([Integer(2),Integer(1),Integer(1),Integer(2)]) Traceback (most recent call last): ... ValueError: length of input degree list should be the row dimension of the input matrix >>> M.coefficient_matrix([Integer(3),Integer(2),Integer(1)], row_wise=False) Traceback (most recent call last): ... ValueError: length of input degree list should be the column dimension of the input matrix 
 - column_degrees(shifts=None)[source]¶
- Return the (shifted) column degrees of this matrix. - For a given polynomial matrix \(M = (M_{i,j})_{i,j}\) with \(m\) rows and \(n\) columns, its column degrees is the tuple \((d_1,\ldots,d_n)\) where \(d_j = \max_i(\deg(M_{i,j}))\) for \(1\leq j \leq n\). Thus, \(d_j=-1\) if the \(j\)-th column of \(M\) is zero, and \(d_j \geq 0\) otherwise. - For given shifts \(s_1,\ldots,s_m \in \ZZ\), the shifted column degrees of \(M\) is \((d_1,\ldots,d_n)\) where \(d_j = \max_i(\deg(M_{i,j})+s_i)\). Here, if the \(j\)-th column of \(M\) is zero then \(d_j = \min(s_1,\ldots,s_m)-1\); otherwise \(d_j\) is larger than this value. - INPUT: - shifts– (default:- None) list of integers;- Noneis interpreted as- shifts=[0,...,0]
 - OUTPUT: list of integers - EXAMPLES: - sage: pR.<x> = GF(7)[] sage: M = matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]]) sage: M.column_degrees() [3, -1, 0] sage: M.column_degrees(shifts=[0,2]) [5, -1, 0] - >>> from sage.all import * >>> pR = GF(Integer(7))['x']; (x,) = pR._first_ngens(1) >>> M = matrix(pR, [[Integer(3)*x+Integer(1), Integer(0), Integer(1)], [x**Integer(3)+Integer(3), Integer(0), Integer(0)]]) >>> M.column_degrees() [3, -1, 0] >>> M.column_degrees(shifts=[Integer(0),Integer(2)]) [5, -1, 0] - A zero column in a polynomial matrix can be identified in the (shifted) column degrees as the entries equal to - min(shifts)-1:- sage: M.column_degrees(shifts=[-2,1]) [4, -3, -2] - >>> from sage.all import * >>> M.column_degrees(shifts=[-Integer(2),Integer(1)]) [4, -3, -2] - The column degrees of an empty matrix (\(0\times n\) or \(m\times 0\)) is not defined: - sage: M = matrix(pR, 0, 3) sage: M.column_degrees() Traceback (most recent call last): ... ValueError: empty matrix does not have column degrees sage: M = matrix(pR, 3, 0) sage: M.column_degrees() Traceback (most recent call last): ... ValueError: empty matrix does not have column degrees - >>> from sage.all import * >>> M = matrix(pR, Integer(0), Integer(3)) >>> M.column_degrees() Traceback (most recent call last): ... ValueError: empty matrix does not have column degrees >>> M = matrix(pR, Integer(3), Integer(0)) >>> M.column_degrees() Traceback (most recent call last): ... ValueError: empty matrix does not have column degrees - See also - The documentation of - row_degrees().
 - constant_matrix()[source]¶
- Return the constant coefficient of this matrix seen as a polynomial with matrix coefficients; this is also this matrix evaluated at zero. - OUTPUT: a matrix over the base field - EXAMPLES: - sage: pR.<x> = GF(7)[] sage: M = matrix([ ....: [ x^3+5*x^2+5*x+1, 5, 6*x+4, 0], ....: [ 6*x^2+3*x+1, 1, 2, 0], ....: [2*x^3+4*x^2+6*x+4, 5*x + 1, 2*x^2+5*x+5, x^2+5*x+6] ....: ]) sage: M.constant_matrix() [1 5 4 0] [1 1 2 0] [4 1 5 6] - >>> from sage.all import * >>> pR = GF(Integer(7))['x']; (x,) = pR._first_ngens(1) >>> M = matrix([ ... [ x**Integer(3)+Integer(5)*x**Integer(2)+Integer(5)*x+Integer(1), Integer(5), Integer(6)*x+Integer(4), Integer(0)], ... [ Integer(6)*x**Integer(2)+Integer(3)*x+Integer(1), Integer(1), Integer(2), Integer(0)], ... [Integer(2)*x**Integer(3)+Integer(4)*x**Integer(2)+Integer(6)*x+Integer(4), Integer(5)*x + Integer(1), Integer(2)*x**Integer(2)+Integer(5)*x+Integer(5), x**Integer(2)+Integer(5)*x+Integer(6)] ... ]) >>> M.constant_matrix() [1 5 4 0] [1 1 2 0] [4 1 5 6] 
 - degree()[source]¶
- Return the degree of this matrix. - For a given polynomial matrix, its degree is the maximum of the degrees of all its entries. If the matrix is nonzero, this is a nonnegative integer; here, the degree of the zero matrix is -1. - OUTPUT: integer - EXAMPLES: - sage: pR.<x> = GF(7)[] sage: M = matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]]) sage: M.degree() 3 - >>> from sage.all import * >>> pR = GF(Integer(7))['x']; (x,) = pR._first_ngens(1) >>> M = matrix(pR, [[Integer(3)*x+Integer(1), Integer(0), Integer(1)], [x**Integer(3)+Integer(3), Integer(0), Integer(0)]]) >>> M.degree() 3 - The zero matrix has degree - -1:- sage: M = matrix(pR, 2, 3) sage: M.degree() -1 - >>> from sage.all import * >>> M = matrix(pR, Integer(2), Integer(3)) >>> M.degree() -1 - For an empty matrix, the degree is not defined: - sage: M = matrix(pR, 3, 0) sage: M.degree() Traceback (most recent call last): ... ValueError: empty matrix does not have a degree - >>> from sage.all import * >>> M = matrix(pR, Integer(3), Integer(0)) >>> M.degree() Traceback (most recent call last): ... ValueError: empty matrix does not have a degree 
 - degree_matrix(shifts=None, row_wise=True)[source]¶
- Return the matrix of the (shifted) degrees in this matrix. - For a given polynomial matrix \(M = (M_{i,j})_{i,j}\), its degree matrix is the matrix \((\deg(M_{i,j}))_{i,j}\) formed by the degrees of its entries. Here, the degree of the zero polynomial is \(-1\). - For given shifts \(s_1,\ldots,s_m \in \ZZ\), the shifted degree matrix of \(M\) is either \((\deg(M_{i,j})+s_j)_{i,j}\) if working row-wise, or \((\deg(M_{i,j})+s_i)_{i,j}\) if working column-wise. In the former case, \(m\) has to be the number of columns of \(M\); in the latter case, the number of its rows. Here, if \(M_{i,j}=0\) then the corresponding entry in the shifted degree matrix is \(\min(s_1,\ldots,s_m)-1\). For more on shifts and working row-wise versus column-wise, see the class documentation. - INPUT: - shifts– (default:- None) list of integers;- Noneis interpreted as- shifts=[0,...,0]
- row_wise– boolean (default:- True); if- Truethen shifts apply to the columns of the matrix and otherwise to its rows (see the class description for more details)
 - OUTPUT: integer matrix - EXAMPLES: - sage: pR.<x> = GF(7)[] sage: M = matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]]) sage: M.degree_matrix() [ 1 -1 0] [ 3 -1 -1] sage: M.degree_matrix(shifts=[0,1,2]) [ 1 -1 2] [ 3 -1 -1] - >>> from sage.all import * >>> pR = GF(Integer(7))['x']; (x,) = pR._first_ngens(1) >>> M = matrix(pR, [[Integer(3)*x+Integer(1), Integer(0), Integer(1)], [x**Integer(3)+Integer(3), Integer(0), Integer(0)]]) >>> M.degree_matrix() [ 1 -1 0] [ 3 -1 -1] >>> M.degree_matrix(shifts=[Integer(0),Integer(1),Integer(2)]) [ 1 -1 2] [ 3 -1 -1] - The zero entries in the polynomial matrix can be identified in the (shifted) degree matrix as the entries equal to - min(shifts)-1:- sage: M.degree_matrix(shifts=[-2,1,2]) [-1 -3 2] [ 1 -3 -3] - >>> from sage.all import * >>> M.degree_matrix(shifts=[-Integer(2),Integer(1),Integer(2)]) [-1 -3 2] [ 1 -3 -3] - Using - row_wise=False, the function supports shifts applied to the rows of the matrix (which, in terms of modules, means that we are working column-wise, see the class documentation):- sage: M.degree_matrix(shifts=[-1,2], row_wise=False) [ 0 -2 -1] [ 5 -2 -2] - >>> from sage.all import * >>> M.degree_matrix(shifts=[-Integer(1),Integer(2)], row_wise=False) [ 0 -2 -1] [ 5 -2 -2] 
 - hermite_form(include_zero_rows=True, transformation=False)[source]¶
- Return the Hermite form of this matrix. - See - is_hermite()for a definition of Hermite forms. If the input is a matrix \(A\), then its Hermite form is the unique matrix \(H\) in Hermite form such that \(UA = H\) for some unimodular matrix \(U\).- INPUT: - include_zero_rows– boolean (default:- True); if- False, the zero rows in the output matrix are deleted
- transformation– boolean (default:- False); if- True, return the transformation matrix
 - OUTPUT: - the Hermite normal form \(H\) of this matrix \(A\) . 
- (optional) transformation matrix \(U\) such that \(UA = H\) . 
 - EXAMPLES: - sage: M.<x> = GF(7)[] sage: A = matrix(M, 2, 3, [x, 1, 2*x, x, 1+x, 2]) sage: A.hermite_form() [ x 1 2*x] [ 0 x 5*x + 2] sage: A.hermite_form(transformation=True) ( [ x 1 2*x] [1 0] [ 0 x 5*x + 2], [6 1] ) sage: A = matrix(M, 2, 3, [x, 1, 2*x, 2*x, 2, 4*x]) sage: A.hermite_form(transformation=True, include_zero_rows=False) ([ x 1 2*x], [0 4]) sage: H, U = A.hermite_form(transformation=True, ....: include_zero_rows=True); H, U ( [ x 1 2*x] [0 4] [ 0 0 0], [5 1] ) sage: U * A == H True sage: H, U = A.hermite_form(transformation=True, ....: include_zero_rows=False) sage: U * A [ x 1 2*x] sage: U * A == H True - >>> from sage.all import * >>> M = GF(Integer(7))['x']; (x,) = M._first_ngens(1) >>> A = matrix(M, Integer(2), Integer(3), [x, Integer(1), Integer(2)*x, x, Integer(1)+x, Integer(2)]) >>> A.hermite_form() [ x 1 2*x] [ 0 x 5*x + 2] >>> A.hermite_form(transformation=True) ( [ x 1 2*x] [1 0] [ 0 x 5*x + 2], [6 1] ) >>> A = matrix(M, Integer(2), Integer(3), [x, Integer(1), Integer(2)*x, Integer(2)*x, Integer(2), Integer(4)*x]) >>> A.hermite_form(transformation=True, include_zero_rows=False) ([ x 1 2*x], [0 4]) >>> H, U = A.hermite_form(transformation=True, ... include_zero_rows=True); H, U ( [ x 1 2*x] [0 4] [ 0 0 0], [5 1] ) >>> U * A == H True >>> H, U = A.hermite_form(transformation=True, ... include_zero_rows=False) >>> U * A [ x 1 2*x] >>> U * A == H True - See also 
 - inverse_series_trunc(d)[source]¶
- Return a matrix polynomial approximation of precision - dof the inverse series of this matrix polynomial.- Here matrix polynomial means that - selfis seen as a univariate polynomial with matrix coefficients, meaning that this method has the same output as if one: 1) converts this matrix to a univariate polynomial with matrix coefficients, 2) calls- sage.rings.polynomial.polynomial_element.Polynomial.inverse_series_trunc()- on that univariate polynomial, and 3) converts back to a matrix of polynomials. - Raises a - ZeroDivisionErrorif the constant matrix of- selfis not invertible (i.e. has zero determinant); raises an- ArithmeticErrorif- selfis nonsquare; and raises a- ValueErrorif the precision- dis not positive.- INPUT: - d– positive integer
 - OUTPUT: the unique polynomial matrix \(B\) of degree less than \(d\) such that \(AB\) and \(BA\) are the identity matrix modulo \(x^d\), where \(A\) is - self.- ALGORITHM: This uses Newton iteration, performing about \(\log(d)\) polynomial matrix multiplications in size \(m \times m\) and in degree less than \(2d\), where \(m\) is the row dimension of - self.- EXAMPLES: - sage: pR.<x> = GF(7)[] sage: A = matrix(pR, 3, 3, ....: [[4*x+5, 5*x^2 + x + 1, 4*x^2 + 4], ....: [6*x^2 + 6*x + 6, 4*x^2 + 5*x, 4*x^2 + x + 3], ....: [3*x^2 + 2, 4*x + 1, x^2 + 3*x]]) sage: B = A.inverse_series_trunc(4); B [ x^3 + 5*x^2 + x + 4 x^3 + 5*x^2 + 6*x + 4 6*x^2 + 5*x + 3] [ 4*x^2 + 5*x + 6 6*x^3 + x^2 + x + 6 3*x^3 + 2*x^2 + 2] [5*x^3 + 5*x^2 + 6*x + 6 4*x^3 + 2*x^2 + 6*x + 4 6*x^3 + x^2 + 6*x + 1] sage: (B*A).truncate(4) == 1 True sage: A.inverse_series_trunc(0) Traceback (most recent call last): ... ValueError: the precision must be positive sage: A[:2,:].inverse_series_trunc(4) Traceback (most recent call last): ... ArithmeticError: the input matrix must be square sage: A[0,:] = A[0,:] - A[0,:](0) + A[1,:](0) + A[2,:](0) sage: A.inverse_series_trunc(4) Traceback (most recent call last): ... ZeroDivisionError: the constant matrix term self(0) must be invertible - >>> from sage.all import * >>> pR = GF(Integer(7))['x']; (x,) = pR._first_ngens(1) >>> A = matrix(pR, Integer(3), Integer(3), ... [[Integer(4)*x+Integer(5), Integer(5)*x**Integer(2) + x + Integer(1), Integer(4)*x**Integer(2) + Integer(4)], ... [Integer(6)*x**Integer(2) + Integer(6)*x + Integer(6), Integer(4)*x**Integer(2) + Integer(5)*x, Integer(4)*x**Integer(2) + x + Integer(3)], ... [Integer(3)*x**Integer(2) + Integer(2), Integer(4)*x + Integer(1), x**Integer(2) + Integer(3)*x]]) >>> B = A.inverse_series_trunc(Integer(4)); B [ x^3 + 5*x^2 + x + 4 x^3 + 5*x^2 + 6*x + 4 6*x^2 + 5*x + 3] [ 4*x^2 + 5*x + 6 6*x^3 + x^2 + x + 6 3*x^3 + 2*x^2 + 2] [5*x^3 + 5*x^2 + 6*x + 6 4*x^3 + 2*x^2 + 6*x + 4 6*x^3 + x^2 + 6*x + 1] >>> (B*A).truncate(Integer(4)) == Integer(1) True >>> A.inverse_series_trunc(Integer(0)) Traceback (most recent call last): ... ValueError: the precision must be positive >>> A[:Integer(2),:].inverse_series_trunc(Integer(4)) Traceback (most recent call last): ... ArithmeticError: the input matrix must be square >>> A[Integer(0),:] = A[Integer(0),:] - A[Integer(0),:](Integer(0)) + A[Integer(1),:](Integer(0)) + A[Integer(2),:](Integer(0)) >>> A.inverse_series_trunc(Integer(4)) Traceback (most recent call last): ... ZeroDivisionError: the constant matrix term self(0) must be invertible - Todo - in the current state of polynomial matrix multiplication (July 2021), it would be highly beneficial to use conversions and rely on polynomials with matrix coefficients when the matrix size is “large” and the degree “small”, see Issue #31472#comment:5. 
 - is_constant()[source]¶
- Return whether this polynomial matrix is constant, that is, all its entries are constant. - OUTPUT: boolean - EXAMPLES: - sage: pR.<x> = GF(7)[] sage: M = matrix([ ....: [ x^3+5*x^2+5*x+1, 5, 6*x+4, 0], ....: [ 6*x^2+3*x+1, 1, 2, 0], ....: [2*x^3+4*x^2+6*x+4, 5*x + 1, 2*x^2+5*x+5, x^2+5*x+6] ....: ]) sage: M.is_constant() False sage: M = matrix(pR, [[1,5,2], [3,1,5]]); M.is_constant() True sage: M = matrix.zero(pR, 3, 5); M.is_constant() True - >>> from sage.all import * >>> pR = GF(Integer(7))['x']; (x,) = pR._first_ngens(1) >>> M = matrix([ ... [ x**Integer(3)+Integer(5)*x**Integer(2)+Integer(5)*x+Integer(1), Integer(5), Integer(6)*x+Integer(4), Integer(0)], ... [ Integer(6)*x**Integer(2)+Integer(3)*x+Integer(1), Integer(1), Integer(2), Integer(0)], ... [Integer(2)*x**Integer(3)+Integer(4)*x**Integer(2)+Integer(6)*x+Integer(4), Integer(5)*x + Integer(1), Integer(2)*x**Integer(2)+Integer(5)*x+Integer(5), x**Integer(2)+Integer(5)*x+Integer(6)] ... ]) >>> M.is_constant() False >>> M = matrix(pR, [[Integer(1),Integer(5),Integer(2)], [Integer(3),Integer(1),Integer(5)]]); M.is_constant() True >>> M = matrix.zero(pR, Integer(3), Integer(5)); M.is_constant() True 
 - is_hermite(row_wise=True, lower_echelon=False, include_zero_vectors=True)[source]¶
- Return a boolean indicating whether this matrix is in Hermite form. - If working row-wise, a polynomial matrix is said to be in Hermite form if it is in row echelon form with all pivot entries monic and such that all entries above a pivot have degree less than this pivot. Being in row echelon form means that all zero rows are gathered at the bottom of the matrix, and in each nonzero row the pivot (leftmost nonzero entry) is strictly to the right of the pivot of the row just above this row. - Note that, for any integer \(d\) strictly greater than all degrees appearing in the Hermite form, then the Hermite form coincides with the shifted Popov form with the shifts \(((n-1)d,\ldots,2d,d,0)\), where \(n\) is the column dimension. - If working column-wise, a polynomial matrix is said to be in Hermite form if it is in column echelon form with all pivot entries monic and such that all entries to the left of a pivot have degree less than this pivot. Being in column echelon form means that all zero columns are gathered at the right-hand side of the matrix, and in each nonzero column the pivot (topmost nonzero entry) is strictly below the pivot of the column just to the left of this row. - Optional arguments provide support of alternative definitions, concerning the choice of upper or lower echelon forms and concerning whether zero rows (resp. columns) are allowed. - INPUT: - row_wise– boolean (default:- True);- Trueif working row-wise (see the class description)
- lower_echelon– boolean (default:- False);- Falseif working with upper triangular Hermite forms,- Trueif working with lower triangular Hermite forms.
- include_zero_vectors– boolean (default:- True);- Falseif one does not allow zero rows (resp. zero columns) in Hermite forms
 - OUTPUT: boolean - EXAMPLES: - sage: pR.<x> = GF(7)[] sage: M = matrix(pR, [[x^4+6*x^3+4*x+4, 3*x+6, 3 ], ....: [0, x^2+5*x+5, 2 ], ....: [0, 0, x+5]]) sage: M.is_hermite() True sage: M.is_hermite(row_wise=False) True sage: M.is_hermite(row_wise=False, lower_echelon=True) False sage: N = matrix(pR, [[x+5, 0, 0 ], ....: [2, x^4+6*x^3+4*x+4, 0 ], ....: [3, 3*x^3+6, x^2+5*x+5]]) sage: N.is_hermite() False sage: N.is_hermite(lower_echelon=True) True sage: N.is_hermite(row_wise=False) False sage: N.is_hermite(row_wise=False, lower_echelon=True) False - >>> from sage.all import * >>> pR = GF(Integer(7))['x']; (x,) = pR._first_ngens(1) >>> M = matrix(pR, [[x**Integer(4)+Integer(6)*x**Integer(3)+Integer(4)*x+Integer(4), Integer(3)*x+Integer(6), Integer(3) ], ... [Integer(0), x**Integer(2)+Integer(5)*x+Integer(5), Integer(2) ], ... [Integer(0), Integer(0), x+Integer(5)]]) >>> M.is_hermite() True >>> M.is_hermite(row_wise=False) True >>> M.is_hermite(row_wise=False, lower_echelon=True) False >>> N = matrix(pR, [[x+Integer(5), Integer(0), Integer(0) ], ... [Integer(2), x**Integer(4)+Integer(6)*x**Integer(3)+Integer(4)*x+Integer(4), Integer(0) ], ... [Integer(3), Integer(3)*x**Integer(3)+Integer(6), x**Integer(2)+Integer(5)*x+Integer(5)]]) >>> N.is_hermite() False >>> N.is_hermite(lower_echelon=True) True >>> N.is_hermite(row_wise=False) False >>> N.is_hermite(row_wise=False, lower_echelon=True) False - Rectangular matrices with zero rows are supported. Zero rows (resp. columns) can be forbidden, and otherwise they should be at the bottom (resp. the right-hand side) of the matrix: - sage: N[:,1:].is_hermite(lower_echelon=True) False sage: N[[1,2,0],1:].is_hermite(lower_echelon=True) True sage: N[:2,:].is_hermite(row_wise=False, lower_echelon=True) True sage: N[:2,:].is_hermite(row_wise=False, ....: lower_echelon=True, ....: include_zero_vectors=False) False - >>> from sage.all import * >>> N[:,Integer(1):].is_hermite(lower_echelon=True) False >>> N[[Integer(1),Integer(2),Integer(0)],Integer(1):].is_hermite(lower_echelon=True) True >>> N[:Integer(2),:].is_hermite(row_wise=False, lower_echelon=True) True >>> N[:Integer(2),:].is_hermite(row_wise=False, ... lower_echelon=True, ... include_zero_vectors=False) False - See also 
 - is_minimal_approximant_basis(pmat, order, shifts=None, row_wise=True, normal_form=False)[source]¶
- Return whether this matrix is an approximant basis in - shifts-ordered weak Popov form for the polynomial matrix- pmatat order- order.- If - normal_formis- True, then the polynomial matrix must furthermore be in- shifts-Popov form. An error is raised if the input dimensions are not sound. If a single integer is provided for- order, then it is interpreted as a list of repeated integers with this value. (See- minimal_approximant_basis()for definitions and more details.)- INPUT: - pmat– a polynomial matrix
- order– list of positive integers, or a positive integer
- shifts– (default:- None) list of integers;- Noneis interpreted as- shifts=[0,...,0]
- row_wise– boolean (default:- True); if- Truethen the basis considered row-wise and operates on the left of- pmat. Otherwise it is column-wise and operates on the right of- pmat.
- normal_form– boolean (default:- False); if- Truethen checks for a basis in- shifts-Popov form
 - OUTPUT: boolean - ALGORITHM: - Verification that the matrix is formed by approximants is done via a truncated matrix product; verification that the matrix is square, nonsingular and in shifted weak Popov form is done via - is_weak_popov(); verification that the matrix generates the module of approximants is done via the characterization in Theorem 2.1 of [GN2018] .- EXAMPLES: - sage: pR.<x> = GF(97)[] - >>> from sage.all import * >>> pR = GF(Integer(97))['x']; (x,) = pR._first_ngens(1) - We consider the following example from [Arne Storjohann, Notes on computing minimal approximant bases, 2006]: - sage: order = 8; shifts = [1,1,0,0,0] sage: pmat = matrix(pR, 5, 1, [ ....: pR([35, 0, 41, 87, 3, 42, 22, 90]), ....: pR([80, 15, 62, 87, 14, 93, 24, 0]), ....: pR([42, 57, 90, 87, 22, 80, 71, 53]), ....: pR([37, 72, 74, 6, 5, 75, 23, 47]), ....: pR([36, 10, 74, 1, 29, 44, 87, 74])]) sage: appbas = matrix(pR, [ ....: [x+47, 57, 58*x+44, 9*x+23, 93*x+76], ....: [ 15, x+18, 52*x+23, 15*x+58, 93*x+88], ....: [ 17, 86, x^2+77*x+16, 76*x+29, 90*x+78], ....: [ 44, 36, 3*x+42, x^2+50*x+26, 85*x+44], ....: [ 2, 22, 54*x+94, 73*x+24, x^2+2*x+25]]) sage: appbas.is_minimal_approximant_basis( # needs sage.libs.pari ....: pmat, order, shifts, row_wise=True, normal_form=True) True - >>> from sage.all import * >>> order = Integer(8); shifts = [Integer(1),Integer(1),Integer(0),Integer(0),Integer(0)] >>> pmat = matrix(pR, Integer(5), Integer(1), [ ... pR([Integer(35), Integer(0), Integer(41), Integer(87), Integer(3), Integer(42), Integer(22), Integer(90)]), ... pR([Integer(80), Integer(15), Integer(62), Integer(87), Integer(14), Integer(93), Integer(24), Integer(0)]), ... pR([Integer(42), Integer(57), Integer(90), Integer(87), Integer(22), Integer(80), Integer(71), Integer(53)]), ... pR([Integer(37), Integer(72), Integer(74), Integer(6), Integer(5), Integer(75), Integer(23), Integer(47)]), ... pR([Integer(36), Integer(10), Integer(74), Integer(1), Integer(29), Integer(44), Integer(87), Integer(74)])]) >>> appbas = matrix(pR, [ ... [x+Integer(47), Integer(57), Integer(58)*x+Integer(44), Integer(9)*x+Integer(23), Integer(93)*x+Integer(76)], ... [ Integer(15), x+Integer(18), Integer(52)*x+Integer(23), Integer(15)*x+Integer(58), Integer(93)*x+Integer(88)], ... [ Integer(17), Integer(86), x**Integer(2)+Integer(77)*x+Integer(16), Integer(76)*x+Integer(29), Integer(90)*x+Integer(78)], ... [ Integer(44), Integer(36), Integer(3)*x+Integer(42), x**Integer(2)+Integer(50)*x+Integer(26), Integer(85)*x+Integer(44)], ... [ Integer(2), Integer(22), Integer(54)*x+Integer(94), Integer(73)*x+Integer(24), x**Integer(2)+Integer(2)*x+Integer(25)]]) >>> appbas.is_minimal_approximant_basis( # needs sage.libs.pari ... pmat, order, shifts, row_wise=True, normal_form=True) True - The matrix \(x^8 \mathrm{Id}_5\) is square, nonsingular, in Popov form, and its rows are approximants for - pmatat order 8. However, it is not an approximant basis since its rows generate a module strictly contained in the set of approximants for- pmatat order 8:- sage: M = x^8 * matrix.identity(pR, 5) sage: M.is_minimal_approximant_basis(pmat, 8) # needs sage.libs.pari False - >>> from sage.all import * >>> M = x**Integer(8) * matrix.identity(pR, Integer(5)) >>> M.is_minimal_approximant_basis(pmat, Integer(8)) # needs sage.libs.pari False - Since - pmatis a single column, with nonzero constant coefficient, its column-wise approximant bases at order 8 are all \(1\times 1\) matrices \([c x^8]\) for some nonzero field element \(c\):- sage: M = matrix(pR, [x^8]) sage: M.is_minimal_approximant_basis( ....: pmat, 8, row_wise=False, normal_form=True) True - >>> from sage.all import * >>> M = matrix(pR, [x**Integer(8)]) >>> M.is_minimal_approximant_basis( ... pmat, Integer(8), row_wise=False, normal_form=True) True - Exceptions are raised if input dimensions are not sound: - sage: appbas.is_minimal_approximant_basis(pmat, [8,8], shifts) Traceback (most recent call last): ... ValueError: order length should be the column dimension of the input matrix sage: appbas.is_minimal_approximant_basis( ....: pmat, order, shifts, row_wise=False) Traceback (most recent call last): ... ValueError: shifts length should be the column dimension of the input matrix sage: matrix(pR, [x^8]).is_minimal_approximant_basis(pmat, 8) Traceback (most recent call last): ... ValueError: column dimension should be the row dimension of the input matrix - >>> from sage.all import * >>> appbas.is_minimal_approximant_basis(pmat, [Integer(8),Integer(8)], shifts) Traceback (most recent call last): ... ValueError: order length should be the column dimension of the input matrix >>> appbas.is_minimal_approximant_basis( ... pmat, order, shifts, row_wise=False) Traceback (most recent call last): ... ValueError: shifts length should be the column dimension of the input matrix >>> matrix(pR, [x**Integer(8)]).is_minimal_approximant_basis(pmat, Integer(8)) Traceback (most recent call last): ... ValueError: column dimension should be the row dimension of the input matrix - See also 
 - is_minimal_kernel_basis(pmat, shifts=None, row_wise=True, normal_form=False)[source]¶
- Return whether this matrix is a left kernel basis in - shifts-ordered weak Popov form for the polynomial matrix- pmat.- If - normal_formis- True, then the kernel basis must furthermore be in- shifts-Popov form. An error is raised if the input dimensions are not sound.- INPUT: - pmat– a polynomial matrix
- shifts– (default:- None) list of integers;- Noneis interpreted as- shifts=[0,...,0]
- row_wise– boolean (default:- True); if- Truethen the basis is considered row-wise and operates on the left of- pmat. Otherwise it is column-wise and operates on the right of- pmat.
- normal_form– boolean (default:- False); if- Truethen checks for a basis in- shifts-Popov form
 - OUTPUT: boolean - ALGORITHM: - Verification that the matrix has full rank and is in shifted weak Popov form is done via - is_weak_popov(); verification that the matrix is a left kernel basis is done by checking that the rank is correct, that the product is indeed zero, and that the matrix is saturated, i.e. it has unimodular column bases (see Lemma 6.10 of https://arxiv.org/pdf/1807.01272.pdf for details).- EXAMPLES: - sage: pR.<x> = GF(97)[] sage: pmat = matrix(pR, [[1], [x], [x**2]]) sage: kerbas = matrix(pR, [[x,-1,0], [0,x,-1]]) sage: kerbas.is_minimal_kernel_basis(pmat) True - >>> from sage.all import * >>> pR = GF(Integer(97))['x']; (x,) = pR._first_ngens(1) >>> pmat = matrix(pR, [[Integer(1)], [x], [x**Integer(2)]]) >>> kerbas = matrix(pR, [[x,-Integer(1),Integer(0)], [Integer(0),x,-Integer(1)]]) >>> kerbas.is_minimal_kernel_basis(pmat) True - A matrix in Popov form which has the right rank, all rows in the kernel, but does not generate the kernel: - sage: kerbas = matrix(pR, [[x**2,0,-1], [0,x,-1]]) sage: kerbas.is_minimal_kernel_basis(pmat) False - >>> from sage.all import * >>> kerbas = matrix(pR, [[x**Integer(2),Integer(0),-Integer(1)], [Integer(0),x,-Integer(1)]]) >>> kerbas.is_minimal_kernel_basis(pmat) False - 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):- sage: kerbas = matrix(pR, [[-x,-x**2], [1,0], [0,1]]) sage: kerbas.is_minimal_kernel_basis( ....: pmat.transpose(), row_wise=False, ....: normal_form=True, shifts=[0,1,2]) True - >>> from sage.all import * >>> kerbas = matrix(pR, [[-x,-x**Integer(2)], [Integer(1),Integer(0)], [Integer(0),Integer(1)]]) >>> kerbas.is_minimal_kernel_basis( ... pmat.transpose(), row_wise=False, ... normal_form=True, shifts=[Integer(0),Integer(1),Integer(2)]) True 
 - is_popov(shifts=None, row_wise=True, up_to_permutation=False, include_zero_vectors=True)[source]¶
- Return a boolean indicating whether this matrix is in (shifted) Popov form. - If working row-wise (resp. column-wise), a polynomial matrix is said to be in Popov form if it has no zero row above a nonzero row (resp. no zero column to the left of a nonzero column), the leading positions of its nonzero rows (resp. columns) are strictly increasing, and for each row (resp. column) the pivot entry is monic and has degree strictly larger than the other entries in its column (resp. row). - Since other conventions concerning the ordering of the rows (resp. columns) are sometimes useful, an optional argument allows one to test whether the matrix is in Popov form up to row (resp. column) permutation. For example, there is an alternative definition which replaces “leading positions strictly increasing” by “row (resp. column) degree nondecreasing, and for rows (resp. columns) of same degree, leading positions increasing”. - INPUT: - shifts– (default:- None) list of integers;- Noneis interpreted as- shifts=[0,...,0]
- row_wise– boolean (default:- True);- Trueif working row-wise (see the class description)
- up_to_permutation– (option, default:- False) boolean,- Trueif testing Popov form up to row permutation (if working row-wise).
- include_zero_vectors– boolean (default:- True);- Falseif one does not allow zero rows (resp. zero columns) in Popov forms
 - OUTPUT: boolean - REFERENCES: - For the square case, without shifts: [Pop1972] and [Kai1980] (Section 6.7.2). For the general case: [BLV2006] . - EXAMPLES: - sage: pR.<x> = GF(7)[] sage: M = matrix(pR, [[x^4+6*x^3+4*x+4, 3*x+6, 3 ], ....: [x^2+6*x+6, x^2+5*x+5, 2 ], ....: [3*x, 6*x+5, x+5]]) sage: M.is_popov() True sage: M.is_popov(shifts=[0,1,2]) True sage: M[:,:2].is_popov() False sage: M[:2,:].is_popov(shifts=[0,1,2]) True sage: M = matrix(pR, [[x^4+3*x^3+x^2+2*x+6, x^3+5*x^2+5*x+1], ....: [6*x+1, x^2+4*x+1 ], ....: [6, 6 ]]) sage: M.is_popov(row_wise=False) False sage: M.is_popov(shifts=[0,2,3], row_wise=False) True - >>> from sage.all import * >>> pR = GF(Integer(7))['x']; (x,) = pR._first_ngens(1) >>> M = matrix(pR, [[x**Integer(4)+Integer(6)*x**Integer(3)+Integer(4)*x+Integer(4), Integer(3)*x+Integer(6), Integer(3) ], ... [x**Integer(2)+Integer(6)*x+Integer(6), x**Integer(2)+Integer(5)*x+Integer(5), Integer(2) ], ... [Integer(3)*x, Integer(6)*x+Integer(5), x+Integer(5)]]) >>> M.is_popov() True >>> M.is_popov(shifts=[Integer(0),Integer(1),Integer(2)]) True >>> M[:,:Integer(2)].is_popov() False >>> M[:Integer(2),:].is_popov(shifts=[Integer(0),Integer(1),Integer(2)]) True >>> M = matrix(pR, [[x**Integer(4)+Integer(3)*x**Integer(3)+x**Integer(2)+Integer(2)*x+Integer(6), x**Integer(3)+Integer(5)*x**Integer(2)+Integer(5)*x+Integer(1)], ... [Integer(6)*x+Integer(1), x**Integer(2)+Integer(4)*x+Integer(1) ], ... [Integer(6), Integer(6) ]]) >>> M.is_popov(row_wise=False) False >>> M.is_popov(shifts=[Integer(0),Integer(2),Integer(3)], row_wise=False) True - One can forbid zero rows (or columns if not working row-wise): - sage: N = matrix(pR, [[x^4+3*x^3+x^2+2*x+6, 6*x+1 ], ....: [5*x^2+5*x+1, x^2+4*x+1 ], ....: [0, 0 ]]) sage: N.is_popov() True sage: N.is_popov(include_zero_vectors=False) False - >>> from sage.all import * >>> N = matrix(pR, [[x**Integer(4)+Integer(3)*x**Integer(3)+x**Integer(2)+Integer(2)*x+Integer(6), Integer(6)*x+Integer(1) ], ... [Integer(5)*x**Integer(2)+Integer(5)*x+Integer(1), x**Integer(2)+Integer(4)*x+Integer(1) ], ... [Integer(0), Integer(0) ]]) >>> N.is_popov() True >>> N.is_popov(include_zero_vectors=False) False - One can verify Popov form up to row permutation (or column permutation if not working row-wise): - sage: M.swap_columns(0, 1) sage: M.is_popov(shifts=[0,2,3], row_wise=False) False sage: M.is_popov(shifts=[0,2,3], row_wise=False, ....: up_to_permutation=True) True sage: N.swap_rows(0, 2) sage: N.is_popov() False sage: N.is_popov(up_to_permutation=True) True - >>> from sage.all import * >>> M.swap_columns(Integer(0), Integer(1)) >>> M.is_popov(shifts=[Integer(0),Integer(2),Integer(3)], row_wise=False) False >>> M.is_popov(shifts=[Integer(0),Integer(2),Integer(3)], row_wise=False, ... up_to_permutation=True) True >>> N.swap_rows(Integer(0), Integer(2)) >>> N.is_popov() False >>> N.is_popov(up_to_permutation=True) True 
 - is_reduced(shifts=None, row_wise=True, include_zero_vectors=True)[source]¶
- Return a boolean indicating whether this matrix is in (shifted) reduced form. - An \(m \times n\) univariate polynomial matrix \(M\) is said to be in shifted row reduced form if it has \(k\) nonzero rows with \(k \leq n\) and its shifted leading matrix has rank \(k\). Equivalently, when considering all the matrices obtained by left-multiplying \(M\) by a unimodular matrix, then the shifted row degrees of \(M\) – once sorted in nondecreasing order – is lexicographically minimal. - Similarly, \(M\) is said to be in shifted column reduced form if it has \(k\) nonzero columns with \(k \leq m\) and its shifted leading matrix has rank \(k\). - Sometimes, one forbids \(M\) to have zero rows (resp. columns) in the above definitions; an optional parameter allows one to adopt this more restrictive setting. - INPUT: - shifts– (default:- None) list of integers;- Noneis interpreted as- shifts=[0,...,0]
- row_wise– boolean (default:- True);- Trueif working row-wise (see the class description)
- include_zero_vectors– boolean (default:- True);- Falseif one does not allow zero rows in row reduced forms (resp. zero columns in column reduced forms)
 - OUTPUT: boolean - REFERENCES: - [Wol1974] (Section 2.5, without shifts) and [VBB1992] (Section 3). - EXAMPLES: - sage: pR.<x> = GF(7)[] sage: M = matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]]) sage: M.is_reduced() False sage: M.is_reduced(shifts=[0,1,2]) True sage: M.is_reduced(shifts=[2,0], row_wise=False) True sage: M.is_reduced(shifts=[2,0], row_wise=False, ....: include_zero_vectors=False) False sage: M = matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0], [0, 1, 0]]) sage: M.is_reduced(shifts=[2,0,0], row_wise=False) True - >>> from sage.all import * >>> pR = GF(Integer(7))['x']; (x,) = pR._first_ngens(1) >>> M = matrix(pR, [[Integer(3)*x+Integer(1), Integer(0), Integer(1)], [x**Integer(3)+Integer(3), Integer(0), Integer(0)]]) >>> M.is_reduced() False >>> M.is_reduced(shifts=[Integer(0),Integer(1),Integer(2)]) True >>> M.is_reduced(shifts=[Integer(2),Integer(0)], row_wise=False) True >>> M.is_reduced(shifts=[Integer(2),Integer(0)], row_wise=False, ... include_zero_vectors=False) False >>> M = matrix(pR, [[Integer(3)*x+Integer(1), Integer(0), Integer(1)], [x**Integer(3)+Integer(3), Integer(0), Integer(0)], [Integer(0), Integer(1), Integer(0)]]) >>> M.is_reduced(shifts=[Integer(2),Integer(0),Integer(0)], row_wise=False) True - See also 
 - is_weak_popov(shifts=None, row_wise=True, ordered=False, include_zero_vectors=True)[source]¶
- Return a boolean indicating whether this matrix is in (shifted) (ordered) weak Popov form. - If working row-wise (resp. column-wise), a polynomial matrix is said to be in weak Popov form if the leading positions of its nonzero rows (resp. columns) are pairwise distinct. For the ordered weak Popov form, these positions must be strictly increasing, except for the possibly repeated -1 entries which are at the end. For the shifted variants, see the class description for an introduction to shifts. - INPUT: - shifts– (default:- None) list of integers;- Noneis interpreted as- shifts=[0,...,0]
- row_wise– boolean (default:- True);- Trueif working row-wise (see the class description)
- ordered– boolean (default:- False);- Trueif checking for an ordered weak Popov form
- include_zero_vectors– boolean (default:- True);- Falseif one does not allow zero rows (resp. zero columns) in (ordered) weak Popov forms
 - OUTPUT: boolean - REFERENCES: - [Kai1980] (Section 6.7.2, square case without shifts), [MS2003] (without shifts), [BLV1999] . - EXAMPLES: - sage: pR.<x> = GF(7)[] sage: M = matrix([ [x^3+3*x^2+6*x+6, 3*x^2+3*x+6, 4*x^2+x+3], ....: [5, 1, 0 ], ....: [2*x^2+2, 2*x+5, x^2+4*x+6] ]) sage: M.is_weak_popov() True - >>> from sage.all import * >>> pR = GF(Integer(7))['x']; (x,) = pR._first_ngens(1) >>> M = matrix([ [x**Integer(3)+Integer(3)*x**Integer(2)+Integer(6)*x+Integer(6), Integer(3)*x**Integer(2)+Integer(3)*x+Integer(6), Integer(4)*x**Integer(2)+x+Integer(3)], ... [Integer(5), Integer(1), Integer(0) ], ... [Integer(2)*x**Integer(2)+Integer(2), Integer(2)*x+Integer(5), x**Integer(2)+Integer(4)*x+Integer(6)] ]) >>> M.is_weak_popov() True - One can check whether the leading positions, in addition to being pairwise distinct, are actually in increasing order: - sage: M.is_weak_popov(ordered=True) True sage: N = M.with_swapped_rows(1, 2) sage: N.is_weak_popov() True sage: N.is_weak_popov(ordered=True) False - >>> from sage.all import * >>> M.is_weak_popov(ordered=True) True >>> N = M.with_swapped_rows(Integer(1), Integer(2)) >>> N.is_weak_popov() True >>> N.is_weak_popov(ordered=True) False - Shifts and orientation (row-wise or column-wise) are supported: - sage: M.is_weak_popov(shifts=[2,3,1]) False sage: M.is_weak_popov(shifts=[0,2,0], row_wise=False, ....: ordered=True) True - >>> from sage.all import * >>> M.is_weak_popov(shifts=[Integer(2),Integer(3),Integer(1)]) False >>> M.is_weak_popov(shifts=[Integer(0),Integer(2),Integer(0)], row_wise=False, ... ordered=True) True - Rectangular matrices are supported: - sage: M = matrix([ ....: [ x^3+5*x^2+5*x+1, 5, 6*x+4, 0], ....: [ 6*x^2+3*x+1, 1, 2, 0], ....: [2*x^3+4*x^2+6*x+4, 5*x + 1, 2*x^2+5*x+5, x^2+5*x+6] ....: ]) sage: M.is_weak_popov(shifts=[0,2,1,3]) True sage: M.is_weak_popov(shifts=[0,2,1,3], ordered=True) True - >>> from sage.all import * >>> M = matrix([ ... [ x**Integer(3)+Integer(5)*x**Integer(2)+Integer(5)*x+Integer(1), Integer(5), Integer(6)*x+Integer(4), Integer(0)], ... [ Integer(6)*x**Integer(2)+Integer(3)*x+Integer(1), Integer(1), Integer(2), Integer(0)], ... [Integer(2)*x**Integer(3)+Integer(4)*x**Integer(2)+Integer(6)*x+Integer(4), Integer(5)*x + Integer(1), Integer(2)*x**Integer(2)+Integer(5)*x+Integer(5), x**Integer(2)+Integer(5)*x+Integer(6)] ... ]) >>> M.is_weak_popov(shifts=[Integer(0),Integer(2),Integer(1),Integer(3)]) True >>> M.is_weak_popov(shifts=[Integer(0),Integer(2),Integer(1),Integer(3)], ordered=True) True - Zero rows (resp. columns) can be forbidden: - sage: M = matrix([ ....: [ 6*x+4, 0, 5*x+1, 0], ....: [ 2, 5*x + 1, 6*x^2+3*x+1, 0], ....: [2*x^2+5*x+5, 1, 2*x^3+4*x^2+6*x+4, 0] ....: ]) sage: M.is_weak_popov(shifts=[2,1,0], row_wise=False, ....: ordered=True) True sage: M.is_weak_popov(shifts=[2,1,0], row_wise=False, ....: include_zero_vectors=False) False - >>> from sage.all import * >>> M = matrix([ ... [ Integer(6)*x+Integer(4), Integer(0), Integer(5)*x+Integer(1), Integer(0)], ... [ Integer(2), Integer(5)*x + Integer(1), Integer(6)*x**Integer(2)+Integer(3)*x+Integer(1), Integer(0)], ... [Integer(2)*x**Integer(2)+Integer(5)*x+Integer(5), Integer(1), Integer(2)*x**Integer(3)+Integer(4)*x**Integer(2)+Integer(6)*x+Integer(4), Integer(0)] ... ]) >>> M.is_weak_popov(shifts=[Integer(2),Integer(1),Integer(0)], row_wise=False, ... ordered=True) True >>> M.is_weak_popov(shifts=[Integer(2),Integer(1),Integer(0)], row_wise=False, ... include_zero_vectors=False) False - See also 
 - leading_matrix(shifts=None, row_wise=True)[source]¶
- Return the (shifted) leading matrix of this matrix. - Let \(M\) be a univariate polynomial matrix in \(\Bold{K}[x]^{m \times n}\). Working row-wise and without shifts, its leading matrix is the matrix in \(\Bold{K}^{m \times n}\) formed by the leading coefficients of the entries of \(M\) which reach the degree of the corresponding row. - More precisely, if working row-wise, let \(s_1,\ldots,s_n \in \ZZ\) be a shift, and let \((d_1,\ldots,d_m)\) denote the shifted row degrees of \(M\). Then, the shifted leading matrix of \(M\) is the matrix in \(\Bold{K}^{m \times n}\) whose entry \(i,j\) is the coefficient of degree \(d_i-s_j\) of the entry \(i,j\) of \(M\). - If working column-wise, let \(s_1,\ldots,s_m \in \ZZ\) be a shift, and let \((d_1,\ldots,d_n)\) denote the shifted column degrees of \(M\). Then, the shifted leading matrix of \(M\) is the matrix in \(\Bold{K}^{m \times n}\) whose entry \(i,j\) is the coefficient of degree \(d_j-s_i\) of the entry \(i,j\) of \(M\). - INPUT: - shifts– (default:- None) list of integers;- Noneis interpreted as- shifts=[0,...,0]
- row_wise– boolean (default:- True);- Trueif working row-wise (see the class description)
 - OUTPUT: a matrix over the base field - REFERENCES: - [Wol1974] (Section 2.5, without shifts) and [VBB1992] (Section 3). - EXAMPLES: - sage: pR.<x> = GF(7)[] sage: M = matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]]) sage: M.leading_matrix() [3 0 0] [1 0 0] sage: M.leading_matrix().base_ring() Finite Field of size 7 sage: M.leading_matrix(shifts=[0,1,2]) [0 0 1] [1 0 0] sage: M.leading_matrix(row_wise=False) [0 0 1] [1 0 0] sage: M.leading_matrix(shifts=[-2,1], row_wise=False) [0 0 1] [1 0 0] sage: M.leading_matrix(shifts=[2,0], row_wise=False) [3 0 1] [1 0 0] - >>> from sage.all import * >>> pR = GF(Integer(7))['x']; (x,) = pR._first_ngens(1) >>> M = matrix(pR, [[Integer(3)*x+Integer(1), Integer(0), Integer(1)], [x**Integer(3)+Integer(3), Integer(0), Integer(0)]]) >>> M.leading_matrix() [3 0 0] [1 0 0] >>> M.leading_matrix().base_ring() Finite Field of size 7 >>> M.leading_matrix(shifts=[Integer(0),Integer(1),Integer(2)]) [0 0 1] [1 0 0] >>> M.leading_matrix(row_wise=False) [0 0 1] [1 0 0] >>> M.leading_matrix(shifts=[-Integer(2),Integer(1)], row_wise=False) [0 0 1] [1 0 0] >>> M.leading_matrix(shifts=[Integer(2),Integer(0)], row_wise=False) [3 0 1] [1 0 0] 
 - leading_positions(shifts=None, row_wise=True, return_degree=False)[source]¶
- Return the (shifted) leading positions (also known as the pivot indices), and optionally the (shifted) pivot degrees of this matrix. - If working row-wise, for a given shift \(s_1,\ldots,s_n \in \ZZ\), taken as \((0,\ldots,0)\) by default, and a row vector of univariate polynomials \([p_1,\ldots,p_n]\), the leading position of this vector is the index \(j\) of the rightmost nonzero entry \(p_j\) such that \(\deg(p_j) + s_j\) is equal to the shifted row degree of the vector. Then the pivot degree of the vector is the degree \(\deg(p_j)\). - For the zero row, both the leading positions and degree are \(-1\). For a \(m \times n\) polynomial matrix, the leading positions and pivot degrees are the two lists containing the leading positions and the pivot degrees of its rows. - The definition is similar if working column-wise (instead of rightmost nonzero entry, we choose the bottommost nonzero entry). - INPUT: - shifts– (default:- None) list of integers;- Noneis interpreted as- shifts=[0,...,0]
- row_wise– boolean (default:- True);- Trueif working row-wise (see the class description)
- return_degree– boolean (default:- False);- Trueimplies that the pivot degrees are returned
 - OUTPUT: list of integers if - return_degree=False; a pair of lists of integers otherwise- REFERENCES: - [Kai1980] (Section 6.7.2, without shifts). - EXAMPLES: - sage: pR.<x> = GF(7)[] sage: M = matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]]) sage: M.leading_positions() [0, 0] sage: M.leading_positions(return_degree=True) ([0, 0], [1, 3]) sage: M.leading_positions(shifts=[0,5,2], return_degree=True) ([2, 0], [0, 3]) sage: M.leading_positions(row_wise=False, return_degree=True) ([1, -1, 0], [3, -1, 0]) sage: M.leading_positions(shifts=[1,2], row_wise=False, ....: return_degree=True) ([1, -1, 0], [3, -1, 0]) - >>> from sage.all import * >>> pR = GF(Integer(7))['x']; (x,) = pR._first_ngens(1) >>> M = matrix(pR, [[Integer(3)*x+Integer(1), Integer(0), Integer(1)], [x**Integer(3)+Integer(3), Integer(0), Integer(0)]]) >>> M.leading_positions() [0, 0] >>> M.leading_positions(return_degree=True) ([0, 0], [1, 3]) >>> M.leading_positions(shifts=[Integer(0),Integer(5),Integer(2)], return_degree=True) ([2, 0], [0, 3]) >>> M.leading_positions(row_wise=False, return_degree=True) ([1, -1, 0], [3, -1, 0]) >>> M.leading_positions(shifts=[Integer(1),Integer(2)], row_wise=False, ... return_degree=True) ([1, -1, 0], [3, -1, 0]) - In case several entries in the row (resp. column) reach the shifted row (resp. column) degree, the leading position is chosen as the rightmost (resp. bottommost) such entry: - sage: M.leading_positions(shifts=[0,5,1], return_degree=True) ([2, 0], [0, 3]) sage: M.leading_positions(shifts=[2,0], row_wise=False, ....: return_degree=True) ([1, -1, 0], [3, -1, 0]) - >>> from sage.all import * >>> M.leading_positions(shifts=[Integer(0),Integer(5),Integer(1)], return_degree=True) ([2, 0], [0, 3]) >>> M.leading_positions(shifts=[Integer(2),Integer(0)], row_wise=False, ... return_degree=True) ([1, -1, 0], [3, -1, 0]) - The leading positions and pivot degrees of an empty matrix (\(0\times n\) or \(m\times 0\)) is not defined: - sage: M = matrix(pR, 0, 3) sage: M.leading_positions() Traceback (most recent call last): ... ValueError: empty matrix does not have leading positions sage: M.leading_positions(row_wise=False) Traceback (most recent call last): ... ValueError: empty matrix does not have leading positions sage: M = matrix(pR, 3, 0) sage: M.leading_positions(row_wise=False) Traceback (most recent call last): ... ValueError: empty matrix does not have leading positions - >>> from sage.all import * >>> M = matrix(pR, Integer(0), Integer(3)) >>> M.leading_positions() Traceback (most recent call last): ... ValueError: empty matrix does not have leading positions >>> M.leading_positions(row_wise=False) Traceback (most recent call last): ... ValueError: empty matrix does not have leading positions >>> M = matrix(pR, Integer(3), Integer(0)) >>> M.leading_positions(row_wise=False) Traceback (most recent call last): ... ValueError: empty matrix does not have leading positions 
 - left_quo_rem(B)[source]¶
- Return, if it exists, the quotient and remainder \((Q,R)\) such that - selfis \(BQ+R\), where \(R\) has row degrees less than those of \(B\) entrywise.- This method directly calls - right_quo_rem()on transposed matrices, and transposes the result. See- right_quo_rem()for a complete documentation and more examples.- EXAMPLES: - sage: pR.<x> = GF(7)[] sage: A = matrix(pR, 3, 2, ....: [[ 3*x^3 + 3*x, 2*x^3 + 4], ....: [ 3*x^3 + 6*x + 5, 6*x^3 + 5*x^2 + 1], ....: [ 2*x^3 + 2*x + 6, 3*x^2 + 2*x + 2]]) sage: B = matrix(pR, 3, 3, ....: [[ 3, x + 3, 6], ....: [3*x^3 + 3*x + 1, 4*x^2 + 3*x, 6*x^3 + x + 4], ....: [ 4*x^2 + x + 4, 3*x^2 + 4*x, 3*x^2 + 3*x + 2]]) sage: Q, R = A.left_quo_rem(B); Q, R ( [2*x^2 + 4*x + 6 6*x^2 + 4*x + 1] [ 3 1] [ 3*x^2 + 5*x 2*x^2 + x + 5] [ 6 5*x^2 + 2*x + 3] [ 6*x^2 + 3*x 4*x^2 + 6*x + 1], [ 2*x + 3 6*x + 3] ) sage: rdegR = R.row_degrees(); rdegB = B.row_degrees() sage: A == B*Q+R and all(rdegR[i] < rdegB[i] for i in range(3)) True sage: A[:2,:].left_quo_rem(B) Traceback (most recent call last): ... ValueError: row dimension of self should be the row dimension of the input matrix - >>> from sage.all import * >>> pR = GF(Integer(7))['x']; (x,) = pR._first_ngens(1) >>> A = matrix(pR, Integer(3), Integer(2), ... [[ Integer(3)*x**Integer(3) + Integer(3)*x, Integer(2)*x**Integer(3) + Integer(4)], ... [ Integer(3)*x**Integer(3) + Integer(6)*x + Integer(5), Integer(6)*x**Integer(3) + Integer(5)*x**Integer(2) + Integer(1)], ... [ Integer(2)*x**Integer(3) + Integer(2)*x + Integer(6), Integer(3)*x**Integer(2) + Integer(2)*x + Integer(2)]]) >>> B = matrix(pR, Integer(3), Integer(3), ... [[ Integer(3), x + Integer(3), Integer(6)], ... [Integer(3)*x**Integer(3) + Integer(3)*x + Integer(1), Integer(4)*x**Integer(2) + Integer(3)*x, Integer(6)*x**Integer(3) + x + Integer(4)], ... [ Integer(4)*x**Integer(2) + x + Integer(4), Integer(3)*x**Integer(2) + Integer(4)*x, Integer(3)*x**Integer(2) + Integer(3)*x + Integer(2)]]) >>> Q, R = A.left_quo_rem(B); Q, R ( [2*x^2 + 4*x + 6 6*x^2 + 4*x + 1] [ 3 1] [ 3*x^2 + 5*x 2*x^2 + x + 5] [ 6 5*x^2 + 2*x + 3] [ 6*x^2 + 3*x 4*x^2 + 6*x + 1], [ 2*x + 3 6*x + 3] ) >>> rdegR = R.row_degrees(); rdegB = B.row_degrees() >>> A == B*Q+R and all(rdegR[i] < rdegB[i] for i in range(Integer(3))) True >>> A[:Integer(2),:].left_quo_rem(B) Traceback (most recent call last): ... ValueError: row dimension of self should be the row dimension of the input matrix - Rectangular or rank-deficient matrices are supported but there may be no quotient and remainder (unless the matrix has full row rank, see - right_quo_rem()):- sage: Q, R = A[:2,:].left_quo_rem(B[:2,:]); Q, R ( [ 3*x + 3 2*x + 1] [ 3*x^2 + 5*x 2*x^2 + x + 5] [ 5 0] [ 0 0], [4*x^2 + x + 2 4*x^2 + x] ) sage: rdegR = R.row_degrees(); rdegB = B[:2,:].row_degrees() sage: A[:2,:] == B[:2,:]*Q+R True sage: all([rdegR[i] < rdegB[i] for i in range(len(rdegR))]) True sage: A.left_quo_rem(B[:,:2]) Traceback (most recent call last): ... ValueError: division of these matrices does not admit a remainder with the required degree property - >>> from sage.all import * >>> Q, R = A[:Integer(2),:].left_quo_rem(B[:Integer(2),:]); Q, R ( [ 3*x + 3 2*x + 1] [ 3*x^2 + 5*x 2*x^2 + x + 5] [ 5 0] [ 0 0], [4*x^2 + x + 2 4*x^2 + x] ) >>> rdegR = R.row_degrees(); rdegB = B[:Integer(2),:].row_degrees() >>> A[:Integer(2),:] == B[:Integer(2),:]*Q+R True >>> all([rdegR[i] < rdegB[i] for i in range(len(rdegR))]) True >>> A.left_quo_rem(B[:,:Integer(2)]) Traceback (most recent call last): ... ValueError: division of these matrices does not admit a remainder with the required degree property - See also 
 - minimal_approximant_basis(order, shifts=None, row_wise=True, normal_form=False)[source]¶
- Return an approximant basis in - shifts-ordered weak Popov form for this polynomial matrix at order- order. This is a direct extension of the so-called Hermite-Padé approximation, which corresponds to the case where- selfis a single vector.- Assuming we work row-wise, if \(F\) is an \(m \times n\) polynomial matrix and \((d_0,\ldots,d_{n-1})\) are integers, then an approximant basis for \(F\) at order \((d_0,\ldots,d_{n-1})\) is a polynomial matrix whose rows form a basis of the module of approximants for \(F\) at order \((d_0,\ldots,d_{n-1})\). The latter approximants are the polynomial vectors \(p\) of size \(m\) such that the column \(j\) of \(p F\) has valuation at least \(d_j\), for all \(0 \le j \le n-1\) (for \(j\) such that \(d_j \le 0\), this constraint is void). - If - normal_formis- True, then the output basis \(P\) is furthermore in- shifts-Popov form. By default, \(P\) is considered row-wise, that is, its rows are left-approximants for- self; if- row_wiseis- Falsethen its columns are right-approximants for- self. It is guaranteed that the degree of the output basis is at most the maximum of the entries of- order, independently of- shifts.- An error is raised if the input dimensions are not sound: if working row-wise (resp. column-wise), the length of - ordermust be the number of columns (resp. rows) of- self, while the length of- shiftsmust be the number of rows (resp. columns) of- self.- If a single integer is provided for - order, then it is converted into a list of repeated integers with this value.- INPUT: - order– list of integers, or an integer
- shifts– (default:- None) list of integers;- Noneis interpreted as- shifts=[0,...,0]
- row_wise– boolean (default:- True); if- Truethen the output basis is considered row-wise and operates on the left of- self. Otherwise it is column-wise and operates on the right of- self.
- normal_form– boolean (default:- False); if- Truethen the output basis is in- shifts-Popov form
 - OUTPUT: a polynomial matrix - ALGORITHM: - The implementation is inspired from the iterative algorithms described in [VBB1992] and [BL1994] ; for obtaining the normal form, it relies directly on Lemmas 3.3 and 4.1 in [JNSV2016] . - EXAMPLES: - sage: pR.<x> = GF(7)[] sage: order = [4, 3]; shifts = [-1, 2, 0] sage: F = matrix(pR, [[5*x^3 + 4*x^2 + 4*x + 6, 5*x^2 + 4*x + 1], ....: [ 2*x^2 + 2*x + 3, 6*x^2 + 6*x + 3], ....: [4*x^3 + x + 1, 4*x^2 + 2*x + 3]]) sage: P = F.minimal_approximant_basis(order, shifts) sage: P.is_minimal_approximant_basis(F, order, shifts) True - >>> from sage.all import * >>> pR = GF(Integer(7))['x']; (x,) = pR._first_ngens(1) >>> order = [Integer(4), Integer(3)]; shifts = [-Integer(1), Integer(2), Integer(0)] >>> F = matrix(pR, [[Integer(5)*x**Integer(3) + Integer(4)*x**Integer(2) + Integer(4)*x + Integer(6), Integer(5)*x**Integer(2) + Integer(4)*x + Integer(1)], ... [ Integer(2)*x**Integer(2) + Integer(2)*x + Integer(3), Integer(6)*x**Integer(2) + Integer(6)*x + Integer(3)], ... [Integer(4)*x**Integer(3) + x + Integer(1), Integer(4)*x**Integer(2) + Integer(2)*x + Integer(3)]]) >>> P = F.minimal_approximant_basis(order, shifts) >>> P.is_minimal_approximant_basis(F, order, shifts) True - By default, the computed basis is not required to be in normal form (and will not be except in rare special cases): - sage: P.is_minimal_approximant_basis(F, order, shifts, ....: normal_form=True) False sage: P = F.minimal_approximant_basis(order, shifts, ....: normal_form=True) sage: P.is_minimal_approximant_basis(F, order, shifts, ....: normal_form=True) True - >>> from sage.all import * >>> P.is_minimal_approximant_basis(F, order, shifts, ... normal_form=True) False >>> P = F.minimal_approximant_basis(order, shifts, ... normal_form=True) >>> P.is_minimal_approximant_basis(F, order, shifts, ... normal_form=True) True - If shifts are not specified, they are chosen as uniform \([0,\ldots,0]\) by default. Besides, if the orders are all the same, one can rather give a single integer: - sage: (F.minimal_approximant_basis(3) == ....: F.minimal_approximant_basis([3,3], shifts=[0,0,0])) True - >>> from sage.all import * >>> (F.minimal_approximant_basis(Integer(3)) == ... F.minimal_approximant_basis([Integer(3),Integer(3)], shifts=[Integer(0),Integer(0),Integer(0)])) True - One can work column-wise by specifying - row_wise=False:- sage: P = F.minimal_approximant_basis([5,2,2], [0,1], ....: row_wise=False) sage: P.is_minimal_approximant_basis(F, [5,2,2], shifts=[0,1], ....: row_wise=False) True sage: (F.minimal_approximant_basis(3, row_wise=True) == ....: F.transpose().minimal_approximant_basis( ....: 3, row_wise=False).transpose()) True - >>> from sage.all import * >>> P = F.minimal_approximant_basis([Integer(5),Integer(2),Integer(2)], [Integer(0),Integer(1)], ... row_wise=False) >>> P.is_minimal_approximant_basis(F, [Integer(5),Integer(2),Integer(2)], shifts=[Integer(0),Integer(1)], ... row_wise=False) True >>> (F.minimal_approximant_basis(Integer(3), row_wise=True) == ... F.transpose().minimal_approximant_basis( ... Integer(3), row_wise=False).transpose()) True - Zero or negative order entries are supported, and amount to ignoring the corresponding column of - self(or corresponding row, if column-wise):- sage: P = F.minimal_approximant_basis([4, 0, 3], row_wise=False) sage: P == F.minimal_approximant_basis([4, -2, 3], row_wise=False) True sage: P == F[[0,2],:].minimal_approximant_basis([4,3], row_wise=False) True - >>> from sage.all import * >>> P = F.minimal_approximant_basis([Integer(4), Integer(0), Integer(3)], row_wise=False) >>> P == F.minimal_approximant_basis([Integer(4), -Integer(2), Integer(3)], row_wise=False) True >>> P == F[[Integer(0),Integer(2)],:].minimal_approximant_basis([Integer(4),Integer(3)], row_wise=False) True - Errors are raised if the input dimensions are not sound: - sage: P = F.minimal_approximant_basis([4], shifts) Traceback (most recent call last): ... ValueError: order length should be the column dimension sage: P = F.minimal_approximant_basis(order, [0,0,0,0]) Traceback (most recent call last): ... ValueError: shifts length should be the row dimension - >>> from sage.all import * >>> P = F.minimal_approximant_basis([Integer(4)], shifts) Traceback (most recent call last): ... ValueError: order length should be the column dimension >>> P = F.minimal_approximant_basis(order, [Integer(0),Integer(0),Integer(0),Integer(0)]) Traceback (most recent call last): ... ValueError: shifts length should be the row dimension 
 - minimal_interpolant_basis(points, shifts=None, row_wise=True, normal_form=False)[source]¶
- Return an interpolant basis in - shifts-ordered weak Popov form for this polynomial matrix with respect to the points in- points. This is a general form of interpolation problems usually called M-Padé approximation or vector rational interpolation.- Assuming we work row-wise, if \(F\) is an \(m \times n\) polynomial matrix and \(a_{i,j}, 0 \le i < d_j\) are elements (called points) of the base field for some integers \(d_0,\ldots,d_{n-1}\), then an interpolant basis for \(F\) with respect to these points is a polynomial matrix whose rows form a basis of the module of interpolants for \(F\) with respect to the points. The latter interpolants are the polynomial vectors \(p\) of size \(m\) such that the column \(j\) of \(p F\) vanishes modulo \(\mu_j = \prod_{0 \le i < d_j} (x - a_{i,j})\) (that is, it vanishes at all points \(a_{i,j}\)’s, with multiplicity in case of repeated points), for all \(0 \le j \le n-1\). For \(j\) such that \(d_j \le 0\), i.e. the \(j\) th list of points is empty, this constraint on the column \(j\) is void. - If - normal_formis- True, then the output basis \(P\) is furthermore in- shifts-Popov form. By default, \(P\) is considered row-wise, that is, its rows are left-interpolants for- self; if- row_wiseis- Falsethen its columns are right-interpolants for- self. It is guaranteed that the degree of the output basis is at most \(\deg(\lcm(\mu_j, 0 \le j < n))\), independently of- shifts.- An error is raised if the input dimensions are not sound: if working row-wise (resp. column-wise), the length of - pointsmust be the number of columns (resp. rows) of- self, while the length of- shiftsmust be the number of rows (resp. columns) of- self.- If a single list is provided for - points, then it is converted into a list containing the provided list repeated the suitable number of times.- INPUT: - points– list of elements from the base field (or coercible into it), or list of such lists
- shifts– (default:- None) list of integers;- Noneis interpreted as- shifts=[0,...,0]
- row_wise– boolean (default:- True); if- Truethen the output basis is considered row-wise and operates on the left of- self. Otherwise it is column-wise and operates on the right of- self.
- normal_form– boolean (default:- False); if- Truethen the output basis is in- shifts-Popov form
 - OUTPUT: a polynomial matrix - ALGORITHM: - The implementation is inspired from the iterative algorithms described in [Bec1992] and [VBB1992] ; for obtaining the normal form, it relies directly on Lemmas 3.3 and 4.1 in [JNSV2016] . - EXAMPLES: - sage: ff = GF(7) sage: xring.<x> = ff[] - >>> from sage.all import * >>> ff = GF(Integer(7)) >>> xring = ff['x']; (x,) = xring._first_ngens(1) - This method supports any number of columns or rows, as well as arbitrary shifts and lists of points: - sage: F = matrix([[5*x^3 + 4*x^2 + 4*x + 6, 5*x^3 + 5*x^2 + 5], ....: [2*x^3 + 2*x^2 + 2*x + 3, 3*x^3 + 6*x + 4], ....: [ x^2 + 6*x + 3, 5*x^3 + 2*x^2 + 3*x]]) sage: points = [[ff(3), ff(0), ff(6), ff(3)], [ff(1), ff(1), ff(1)]] sage: mod1 = (x - 3) * x * (x - 6) * (x - 3) sage: mod2 = (x - 1)**3 sage: P = F.minimal_interpolant_basis(points, shifts=[0,0,0]) sage: P.is_weak_popov(ordered=True) True sage: G = P*F sage: G[:,0] % mod1 == 0 and G[:,1] % mod2 == 0 True sage: P.det() == mod1 * mod2 True - >>> from sage.all import * >>> F = matrix([[Integer(5)*x**Integer(3) + Integer(4)*x**Integer(2) + Integer(4)*x + Integer(6), Integer(5)*x**Integer(3) + Integer(5)*x**Integer(2) + Integer(5)], ... [Integer(2)*x**Integer(3) + Integer(2)*x**Integer(2) + Integer(2)*x + Integer(3), Integer(3)*x**Integer(3) + Integer(6)*x + Integer(4)], ... [ x**Integer(2) + Integer(6)*x + Integer(3), Integer(5)*x**Integer(3) + Integer(2)*x**Integer(2) + Integer(3)*x]]) >>> points = [[ff(Integer(3)), ff(Integer(0)), ff(Integer(6)), ff(Integer(3))], [ff(Integer(1)), ff(Integer(1)), ff(Integer(1))]] >>> mod1 = (x - Integer(3)) * x * (x - Integer(6)) * (x - Integer(3)) >>> mod2 = (x - Integer(1))**Integer(3) >>> P = F.minimal_interpolant_basis(points, shifts=[Integer(0),Integer(0),Integer(0)]) >>> P.is_weak_popov(ordered=True) True >>> G = P*F >>> G[:,Integer(0)] % mod1 == Integer(0) and G[:,Integer(1)] % mod2 == Integer(0) True >>> P.det() == mod1 * mod2 True - The last test highlights that for “sufficiently generic” input, the fact that the returned matrix generates the module of interpolants is equivalent to the fact that its determinant is the product of the linear factors defined by the interpolation points. - If shifts are not specified, they are chosen as uniform \([0,\ldots,0]\) by default. Besides, if the lists of points are all identical, one can rather give a single list: - sage: P = F.minimal_interpolant_basis([points[0], points[0]]) sage: P == F.minimal_interpolant_basis(points[0], shifts=[0,0,0]) True - >>> from sage.all import * >>> P = F.minimal_interpolant_basis([points[Integer(0)], points[Integer(0)]]) >>> P == F.minimal_interpolant_basis(points[Integer(0)], shifts=[Integer(0),Integer(0),Integer(0)]) True - One can work column-wise by specifying - row_wise=False. Empty lists of points are supported, and amount to ignoring the corresponding column of- self(or corresponding row, if column-wise):- sage: points[1] = [] sage: shifts = [-1, 2, 0] sage: Ft = F.transpose() sage: P = Ft.minimal_interpolant_basis(points, shifts=shifts, row_wise=False) sage: P == Ft[0,:].minimal_interpolant_basis([points[0]], shifts=shifts, row_wise=False) True sage: P.is_weak_popov(shifts=shifts, ordered=True, row_wise=False) True sage: G = Ft * P sage: G[0,:] % mod1 == 0 and P.det() == mod1 True - >>> from sage.all import * >>> points[Integer(1)] = [] >>> shifts = [-Integer(1), Integer(2), Integer(0)] >>> Ft = F.transpose() >>> P = Ft.minimal_interpolant_basis(points, shifts=shifts, row_wise=False) >>> P == Ft[Integer(0),:].minimal_interpolant_basis([points[Integer(0)]], shifts=shifts, row_wise=False) True >>> P.is_weak_popov(shifts=shifts, ordered=True, row_wise=False) True >>> G = Ft * P >>> G[Integer(0),:] % mod1 == Integer(0) and P.det() == mod1 True - Errors are raised if the input dimensions are not sound: - sage: P = F.minimal_interpolant_basis([points[0]]) Traceback (most recent call last): ... ValueError: points length should be the column dimension sage: P = F.minimal_interpolant_basis(points, shifts=[0,0,0,0]) Traceback (most recent call last): ... ValueError: shifts length should be the row dimension - >>> from sage.all import * >>> P = F.minimal_interpolant_basis([points[Integer(0)]]) Traceback (most recent call last): ... ValueError: points length should be the column dimension >>> P = F.minimal_interpolant_basis(points, shifts=[Integer(0),Integer(0),Integer(0),Integer(0)]) Traceback (most recent call last): ... ValueError: shifts length should be the row dimension 
 - minimal_kernel_basis(shifts=None, row_wise=True, normal_form=False)[source]¶
- Return a left kernel basis in - shifts-ordered weak Popov form for this polynomial matrix.- Assuming we work row-wise, if \(F\) is an \(m \times n\) polynomial matrix, then a left kernel basis for \(F\) is a polynomial matrix whose rows form a basis of the left kernel of \(F\), which is the module of polynomial vectors \(p\) of size \(m\) such that \(p F\) is zero. - If - normal_formis- True, then the output basis \(P\) is furthermore in- shifts-Popov form. By default, \(P\) is considered row-wise, that is, its rows are left kernel vectors for- self; if- row_wiseis- Falsethen its columns are right kernel vectors for- self.- An error is raised if the input dimensions are not sound: if working row-wise (resp. column-wise), the length of - shiftsmust be the number of rows (resp. columns) of- self.- INPUT: - shifts– (default:- None) list of integers;- Noneis interpreted as- shifts=[0,...,0]
- row_wise– boolean (default:- True); if- Truethen the output basis considered row-wise and operates on the left of- self. Otherwise it is column-wise and operates on the right of- self.
- normal_form– boolean (default:- False); if- Truethen the output basis is in- shifts-Popov form
 - OUTPUT: a polynomial matrix - ALGORITHM: uses minimal approximant basis computation at a sufficiently large order so that the approximant basis contains a kernel basis as a submatrix. - EXAMPLES: - sage: pR.<x> = GF(7)[] sage: pmat = matrix([[(x+1)*(x+3)], [(x+1)*(x+3)+1]]) sage: pmat.minimal_kernel_basis() [6*x^2 + 3*x + 3 x^2 + 4*x + 3] sage: pmat = matrix([[(x+1)*(x+3)], [(x+1)*(x+4)]]) sage: pmat.minimal_kernel_basis() [6*x + 3 x + 3] sage: pmat.minimal_kernel_basis(row_wise=False) [] sage: pmat = matrix(pR, [[1, x, x**2]]) sage: pmat.minimal_kernel_basis(row_wise=False, normal_form=True) [x 0] [6 x] [0 6] sage: pmat.minimal_kernel_basis(row_wise=False, normal_form=True, ....: shifts=[0,1,2]) [ 6*x 6*x^2] [ 1 0] [ 0 1] - >>> from sage.all import * >>> pR = GF(Integer(7))['x']; (x,) = pR._first_ngens(1) >>> pmat = matrix([[(x+Integer(1))*(x+Integer(3))], [(x+Integer(1))*(x+Integer(3))+Integer(1)]]) >>> pmat.minimal_kernel_basis() [6*x^2 + 3*x + 3 x^2 + 4*x + 3] >>> pmat = matrix([[(x+Integer(1))*(x+Integer(3))], [(x+Integer(1))*(x+Integer(4))]]) >>> pmat.minimal_kernel_basis() [6*x + 3 x + 3] >>> pmat.minimal_kernel_basis(row_wise=False) [] >>> pmat = matrix(pR, [[Integer(1), x, x**Integer(2)]]) >>> pmat.minimal_kernel_basis(row_wise=False, normal_form=True) [x 0] [6 x] [0 6] >>> pmat.minimal_kernel_basis(row_wise=False, normal_form=True, ... shifts=[Integer(0),Integer(1),Integer(2)]) [ 6*x 6*x^2] [ 1 0] [ 0 1] - Some particular cases (matrix is zero, dimension is zero, column is zero): - sage: matrix(pR, 2, 1).minimal_kernel_basis() [1 0] [0 1] sage: matrix(pR, 2, 0).minimal_kernel_basis() [1 0] [0 1] sage: matrix(pR, 0, 2).minimal_kernel_basis() [] sage: matrix(pR, 3, 2, [[1,0],[1,0],[1,0]]).minimal_kernel_basis() [6 1 0] [6 0 1] sage: matrix(pR, 3, 2, [[x,0],[1,0],[x+1,0]]).minimal_kernel_basis() [6 x 0] [6 6 1] - >>> from sage.all import * >>> matrix(pR, Integer(2), Integer(1)).minimal_kernel_basis() [1 0] [0 1] >>> matrix(pR, Integer(2), Integer(0)).minimal_kernel_basis() [1 0] [0 1] >>> matrix(pR, Integer(0), Integer(2)).minimal_kernel_basis() [] >>> matrix(pR, Integer(3), Integer(2), [[Integer(1),Integer(0)],[Integer(1),Integer(0)],[Integer(1),Integer(0)]]).minimal_kernel_basis() [6 1 0] [6 0 1] >>> matrix(pR, Integer(3), Integer(2), [[x,Integer(0)],[Integer(1),Integer(0)],[x+Integer(1),Integer(0)]]).minimal_kernel_basis() [6 x 0] [6 6 1] 
 - minimal_relation_basis(mod, shifts=None, row_wise=True, normal_form=False, reduced_input=False)[source]¶
- Return a relation basis in - shifts-ordered weak Popov form for this polynomial matrix with respect to the module defined by- mod.- The description below uses notation \(F\) for - self, \(M\) for- mod, and \(s\) for the shifts- shifts. \(F\) is an \(m \times n\) polynomial matrix and \(M\) is a nonsingular \(n \times n\) matrix.- If we work row-wise (resp. column-wise), a relation basis for \(F\) modulo \(M\) is a polynomial matrix \(P\) whose rows (resp. columns) form a basis of the module of polynomial vectors \(p\) of size \(m\) such that \(p F\) (resp. \(F p\)) belongs to the row space (resp. column space) of \(M\). Such a basis \(P\) is an \(m \times m\) nonsingular matrix, which this method computes in \(s\)-ordered weak Popov form. - If - normal_formis- True, then the output \(P\) is the canonical \(s\)-Popov basis. If- reduced_inputis- True, then the provided \(M\) should be column reduced (resp. row reduced) and \(F\) should be reduced modulo \(M\), that is, should have column (resp. row) degrees strictly bounded entry-wise by those of \(M\). When those properties are known to be true, setting- reduced_inputto the non-default- Truemay save some computation time.- An error is raised if the dimensions of the input matrices or the length of the input shift are not sound. When - reduced_inputis the default- False, an error is raised if \(M\) is singular.- Here are two special cases of relation bases. - minimal approximant bases, for which \(M\) is a diagonal of powers \(x^{d_0}, \ldots, x^{d_{n-1}}\) (see - minimal_approximant_basis(), which may be called directly for better performance),
- minimal interpolant bases, for which \(M\) is a diagonal of polynomials that split into known linear factors (see - minimal_interpolant_basis(), which may be called directly for better performance).
 - INPUT: - mod– polynomial matrix, nonsingular
- shifts– (default:- None) list of integers;- Noneis interpreted as- shifts=[0,...,0]
- row_wise– boolean (default:- True). If- True, compute left relations for- selfmodulo the row space of- mod; otherwise, compute right relations for- selfmodulo the column space of- mod
- normal_form– boolean (default:- False); if- Truethen the output basis is in- shifts-Popov form
- reduced_input– boolean (default:- False). If- True, and working row-wise (resp. column-wise), then- modmust be column (resp. row) reduced and its column (resp. row) degrees must be strict upper bounds on those of- selfentry-wise
 - OUTPUT: a polynomial matrix - EXAMPLES: - sage: pR.<x> = GF(7)[] - >>> from sage.all import * >>> pR = GF(Integer(7))['x']; (x,) = pR._first_ngens(1) - When M is a diagonal of powers of the variable, a relation basis is the same as an approximant basis: - sage: M = matrix.diagonal([x**4, x**3], sparse=False) sage: shifts = [-1, 2, 0] sage: F = matrix(pR, [[5*x^3 + 4*x^2 + 4*x + 6, 5*x^2 + 4*x + 1], ....: [ 2*x^2 + 2*x + 3, 6*x^2 + 6*x + 3], ....: [4*x^3 + x + 1, 4*x^2 + 2*x + 3]]) sage: P_app = F.minimal_approximant_basis([4, 3], shifts, normal_form=True) sage: P_rel = F.minimal_relation_basis(M, shifts, normal_form=True) sage: P_app == P_rel True - >>> from sage.all import * >>> M = matrix.diagonal([x**Integer(4), x**Integer(3)], sparse=False) >>> shifts = [-Integer(1), Integer(2), Integer(0)] >>> F = matrix(pR, [[Integer(5)*x**Integer(3) + Integer(4)*x**Integer(2) + Integer(4)*x + Integer(6), Integer(5)*x**Integer(2) + Integer(4)*x + Integer(1)], ... [ Integer(2)*x**Integer(2) + Integer(2)*x + Integer(3), Integer(6)*x**Integer(2) + Integer(6)*x + Integer(3)], ... [Integer(4)*x**Integer(3) + x + Integer(1), Integer(4)*x**Integer(2) + Integer(2)*x + Integer(3)]]) >>> P_app = F.minimal_approximant_basis([Integer(4), Integer(3)], shifts, normal_form=True) >>> P_rel = F.minimal_relation_basis(M, shifts, normal_form=True) >>> P_app == P_rel True - If - selfis the identity matrix, then relation bases are simply matrices that are left-unimodularly equivalent to \(M\):- sage: # M is both row and column reduced sage: M = x**3 + matrix([[6*x**2, 2, x], [2, 2, 6*x], [x**2, 3, 6]]) sage: F1 = matrix.identity(pR, 3) # cdeg(F1) < cdeg(M) sage: P1 = F1.minimal_relation_basis(M, shifts=[0,1,2], normal_form=True) sage: P1 == M.popov_form(shifts=[0,1,2]) True - >>> from sage.all import * >>> # M is both row and column reduced >>> M = x**Integer(3) + matrix([[Integer(6)*x**Integer(2), Integer(2), x], [Integer(2), Integer(2), Integer(6)*x], [x**Integer(2), Integer(3), Integer(6)]]) >>> F1 = matrix.identity(pR, Integer(3)) # cdeg(F1) < cdeg(M) >>> P1 = F1.minimal_relation_basis(M, shifts=[Integer(0),Integer(1),Integer(2)], normal_form=True) >>> P1 == M.popov_form(shifts=[Integer(0),Integer(1),Integer(2)]) True - One can consider column-wise relations; unspecified shift means taking the uniform \([0,\ldots, 0]\) shift: - sage: F2 = matrix([[ 1, 6*x + 2, 5, 3, 5*x^2 + 3], ....: [2*x^2 + 4*x + 4, 3*x + 1, 5*x, 6*x^2 + 5, 6], ....: [ 5*x + 4, 3*x + 1, 2, 2*x + 2, 2*x + 1]]) sage: P2 = F2.minimal_relation_basis(M, row_wise=False) sage: P2.is_weak_popov(shifts=[0]*5, row_wise=False) True sage: Q,R = (F2*P2).left_quo_rem(M) # F2*P2 = M*Q + 0 sage: R == 0 True - >>> from sage.all import * >>> F2 = matrix([[ Integer(1), Integer(6)*x + Integer(2), Integer(5), Integer(3), Integer(5)*x**Integer(2) + Integer(3)], ... [Integer(2)*x**Integer(2) + Integer(4)*x + Integer(4), Integer(3)*x + Integer(1), Integer(5)*x, Integer(6)*x**Integer(2) + Integer(5), Integer(6)], ... [ Integer(5)*x + Integer(4), Integer(3)*x + Integer(1), Integer(2), Integer(2)*x + Integer(2), Integer(2)*x + Integer(1)]]) >>> P2 = F2.minimal_relation_basis(M, row_wise=False) >>> P2.is_weak_popov(shifts=[Integer(0)]*Integer(5), row_wise=False) True >>> Q,R = (F2*P2).left_quo_rem(M) # F2*P2 = M*Q + 0 >>> R == Integer(0) True - Unless requiring a normal form, the output basis will most often not be the canonical one: - sage: P2.is_popov(shifts=[0]*5, row_wise=False) False - >>> from sage.all import * >>> P2.is_popov(shifts=[Integer(0)]*Integer(5), row_wise=False) False - By default, this supports input - selfthat are not reduced modulo- M, unless- reduced_inputis specified as- True:- sage: G1 = F1 + x * M # G1 == F1 mod M; G1 not reduced mod M sage: P1bis = G1.minimal_relation_basis(M, shifts=[0,1,2], normal_form=True) sage: P1bis == P1 True sage: P = G1.minimal_relation_basis(M, shifts=[0,1,2], reduced_input=True) sage: P.is_weak_popov(shifts=[0,1,2]) False - >>> from sage.all import * >>> G1 = F1 + x * M # G1 == F1 mod M; G1 not reduced mod M >>> P1bis = G1.minimal_relation_basis(M, shifts=[Integer(0),Integer(1),Integer(2)], normal_form=True) >>> P1bis == P1 True >>> P = G1.minimal_relation_basis(M, shifts=[Integer(0),Integer(1),Integer(2)], reduced_input=True) >>> P.is_weak_popov(shifts=[Integer(0),Integer(1),Integer(2)]) False - By default, this supports any nonsingular matrix - M, and nonsingularity is checked (unless- reduced_inputis specified as- True):- sage: M1 = matrix([[1,x**10,x**10],[0,1,0],[0,0,1]]) * M sage: M1.is_reduced(row_wise=False) # M1 not column reduced False sage: P1bis = F1.minimal_relation_basis(M1, shifts=[0,1,2], normal_form=True) sage: P1bis == P1 # True since M and M1 have same row space True sage: P = F1.minimal_relation_basis(M1, shifts=[0,1,2], reduced_input=True) sage: P.is_weak_popov(shifts=[0,1,2]) False sage: M2 = M.with_row_set_to_multiple_of_row(0, 1, x) # M2 is singular sage: F1.minimal_relation_basis(M2) Traceback (most recent call last): ... ValueError: modulus matrix must be nonsingular - >>> from sage.all import * >>> M1 = matrix([[Integer(1),x**Integer(10),x**Integer(10)],[Integer(0),Integer(1),Integer(0)],[Integer(0),Integer(0),Integer(1)]]) * M >>> M1.is_reduced(row_wise=False) # M1 not column reduced False >>> P1bis = F1.minimal_relation_basis(M1, shifts=[Integer(0),Integer(1),Integer(2)], normal_form=True) >>> P1bis == P1 # True since M and M1 have same row space True >>> P = F1.minimal_relation_basis(M1, shifts=[Integer(0),Integer(1),Integer(2)], reduced_input=True) >>> P.is_weak_popov(shifts=[Integer(0),Integer(1),Integer(2)]) False >>> M2 = M.with_row_set_to_multiple_of_row(Integer(0), Integer(1), x) # M2 is singular >>> F1.minimal_relation_basis(M2) Traceback (most recent call last): ... ValueError: modulus matrix must be nonsingular 
 - popov_form(transformation=False, shifts=None, row_wise=True, include_zero_vectors=True)[source]¶
- Return the (shifted) Popov form of this matrix. - See - is_popov()for a definition of Popov forms. If the input matrix is \(A\), the (shifted) Popov form of \(A\) is the unique matrix \(P\) in (shifted) Popov form and such that \(UA = P\) for some unimodular matrix \(U\). The latter matrix is called the transformation, and the first optional argument allows one to specify whether to return this transformation. We refer to the description of- weak_popov_form()for an explanation of the option- include_zero_vectors.- INPUT: - transformation– (default:- False) if this is- True, the transformation matrix \(U\) will be returned as well
- shifts– (default:- None) list of integers;- Noneis interpreted as- shifts=[0,...,0]
- row_wise– boolean (default:- True);- Trueif working row-wise (see the class description)
- include_zero_vectors– boolean (default:- True);- Falseif zero rows (resp. zero columns) should be discarded from the Popov forms
 - OUTPUT: - A polynomial matrix which is the Popov form of - selfif- transformationis- False; otherwise two polynomial matrices which are the Popov form of- selfand the corresponding unimodular transformation.
 - ALGORITHM: - This method implements the Mulders-Storjohann algorithm of [MS2003] for transforming a weak Popov form into Popov form, straightforwardly extended to the case of shifted forms. - EXAMPLES: - sage: pR.<x> = GF(7)[] sage: M = matrix(pR, [ ....: [ 6*x+4, 5*x^3+5*x, 6*x^2+2*x+2], ....: [4*x^2+5*x+2, x^4+5*x^2+2*x+4, 4*x^3+6*x^2+6*x+5]]) sage: # needs sage.combinat sage: P, U = M.popov_form(transformation=True) sage: P [ 4 x^2 + 4*x + 1 3] [ 0 4*x + 1 x^2 + 6*x + 1] sage: U [ x 2] [5*x^2 + x + 6 3*x + 2] sage: P.is_popov() and U.is_invertible() and U*M == P True - >>> from sage.all import * >>> pR = GF(Integer(7))['x']; (x,) = pR._first_ngens(1) >>> M = matrix(pR, [ ... [ Integer(6)*x+Integer(4), Integer(5)*x**Integer(3)+Integer(5)*x, Integer(6)*x**Integer(2)+Integer(2)*x+Integer(2)], ... [Integer(4)*x**Integer(2)+Integer(5)*x+Integer(2), x**Integer(4)+Integer(5)*x**Integer(2)+Integer(2)*x+Integer(4), Integer(4)*x**Integer(3)+Integer(6)*x**Integer(2)+Integer(6)*x+Integer(5)]]) >>> # needs sage.combinat >>> P, U = M.popov_form(transformation=True) >>> P [ 4 x^2 + 4*x + 1 3] [ 0 4*x + 1 x^2 + 6*x + 1] >>> U [ x 2] [5*x^2 + x + 6 3*x + 2] >>> P.is_popov() and U.is_invertible() and U*M == P True - Demonstrating shifts and specific case of Hermite form: - sage: # needs sage.combinat sage: P = M.popov_form(shifts=[0,2,4]); P [ 4*x^2 + 3*x + 4 x^4 + 3*x^3 + 5*x^2 + 5*x + 5 0] [ 6 5*x^2 + 6*x + 5 1] sage: P.is_popov(shifts=[0,2,4]) True sage: P == M.popov_form(shifts=[-6,-4,-2]) True sage: dd = sum(M.row_degrees()) + 1 sage: M.popov_form(shifts=[2*dd,dd,0]) == M.hermite_form() True - >>> from sage.all import * >>> # needs sage.combinat >>> P = M.popov_form(shifts=[Integer(0),Integer(2),Integer(4)]); P [ 4*x^2 + 3*x + 4 x^4 + 3*x^3 + 5*x^2 + 5*x + 5 0] [ 6 5*x^2 + 6*x + 5 1] >>> P.is_popov(shifts=[Integer(0),Integer(2),Integer(4)]) True >>> P == M.popov_form(shifts=[-Integer(6),-Integer(4),-Integer(2)]) True >>> dd = sum(M.row_degrees()) + Integer(1) >>> M.popov_form(shifts=[Integer(2)*dd,dd,Integer(0)]) == M.hermite_form() True - Column-wise form is the row-wise form of the transpose: - sage: M.popov_form() == M.T.popov_form(row_wise=False).T True - >>> from sage.all import * >>> M.popov_form() == M.T.popov_form(row_wise=False).T True - Zero vectors can be discarded: - sage: M.popov_form(row_wise=False) [x + 2 6 0] [ 0 1 0] sage: # needs sage.combinat sage: P, U = M.popov_form(transformation=True, ....: row_wise=False, ....: include_zero_vectors=False) sage: P [x + 2 6] [ 0 1] sage: U [ 3*x^2 + 6*x + 3 5*x^2 + 4*x + 4 3*x^3 + 3*x^2 + 2*x + 4] [ 3 1 2*x + 1] [ 5*x + 2 2 6] sage: M*U[:,:2] == P and (M*U[:,2]).is_zero() True - >>> from sage.all import * >>> M.popov_form(row_wise=False) [x + 2 6 0] [ 0 1 0] >>> # needs sage.combinat >>> P, U = M.popov_form(transformation=True, ... row_wise=False, ... include_zero_vectors=False) >>> P [x + 2 6] [ 0 1] >>> U [ 3*x^2 + 6*x + 3 5*x^2 + 4*x + 4 3*x^3 + 3*x^2 + 2*x + 4] [ 3 1 2*x + 1] [ 5*x + 2 2 6] >>> M*U[:,:Integer(2)] == P and (M*U[:,Integer(2)]).is_zero() True - See also - is_popov(),- reduced_form(),- weak_popov_form(),- hermite_form().
 - reduce(B, shifts=None, row_wise=True, return_quotient=False)[source]¶
- Reduce - self, i.e. compute its normal form, modulo the row space of \(B\) with respect to- shifts.- If - selfis a \(k \times n\) polynomial matrix (written \(A\) below), and the input \(B\) is an \(m \times n\) polynomial matrix, this computes the normal form \(R\) of \(A\) with respect the row space of \(B\) and the monomial order defined by- shifts(written \(s\) below). This means that the \(i\) th row of \(R\) is equal to the \(i\) th row of \(A\) up to addition of an element in the row space of \(B\), and if \(J = (j_1,\ldots,j_r)\) are the \(s\)-leading positions of the \(s\)-Popov form \(P\) of \(A\), then the submatrix \(R_{*,J}\) (submatrix of \(R\) formed by its columns in \(J\)) has column degrees smaller entrywise than the column degrees of \(P_{*,J}\).- If the option - row_wiseis set to- False, the same operation is performed, but with everything considered column-wise: column space of \(B\), \(i\) th column of \(R\) and \(A\), column-wise \(s\)-leading positions and \(s\)-Popov form, and submatrices \(R_{J, *}\) and \(P_{J, *}\).- The operation above can be seen as a matrix generalization of division with remainder for univariate polynomials. If the option - return_quotientis set to- True, this method returns both the normal form \(R\) and a quotient matrix \(Q\) such that \(A = QB + R\) (or \(A = BQ + R\) if- row_wiseis- False). Whereas the remainder is unique, this quotient is not unique unless \(B\) has a trivial left kernel i.e. has full row rank (or right kernel, full column rank if- row_wiseis- False).- This method checks whether \(B\) is in \(s\)-Popov form, and if not, computes the \(s\)-Popov form \(P\) of \(B\), which can take some time. Therefore, if \(P\) is already known or is to be re-used, this method should be called directly with \(P\), yielding the same normal form \(R\) since \(P\) and \(B\) have the same row space (or column space, if - row_wiseis- False).- A - ValueErroris raised if the dimensions of the shifts and/or of the matrices are not conformal.- INPUT: - B– polynomial matrix
- shifts– (default:- None) list of integers;- Noneis interpreted as- shifts=[0,...,0]
- row_wise– boolean (default:- True);- Trueif working row-wise (see the class description)
- return_quotient– (default:- False) if this is- True, the quotient will be returned as well
 - OUTPUT: a polynomial matrix if - return_quotient=False, two polynomial matrices otherwise.- EXAMPLES: - sage: pR.<x> = GF(7)[] sage: B = matrix(pR, [ ....: [ 6*x+4, 5*x^3+5*x, 6*x^2+2*x+2], ....: [4*x^2+5*x+2, x^4+5*x^2+2*x+4, 4*x^3+6*x^2+6*x+5]]) sage: A = matrix(pR, 1, 3, [ ....: [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]]) sage: Q, R = A.reduce(B,return_quotient=True); R [3*x^4 + 3*x^3 + 4*x + 3 2*x + 2 2*x + 6] sage: A == Q*B + R True sage: P = B.popov_form(); P.leading_positions(return_degree=True) ([1, 2], [2, 2]) sage: R.degree_matrix() [4 1 1] sage: A.reduce(P) == R True sage: A.reduce(P[:,:2]) Traceback (most recent call last): ... ValueError: column dimension of self should be the column dimension of the input matrix - >>> from sage.all import * >>> pR = GF(Integer(7))['x']; (x,) = pR._first_ngens(1) >>> B = matrix(pR, [ ... [ Integer(6)*x+Integer(4), Integer(5)*x**Integer(3)+Integer(5)*x, Integer(6)*x**Integer(2)+Integer(2)*x+Integer(2)], ... [Integer(4)*x**Integer(2)+Integer(5)*x+Integer(2), x**Integer(4)+Integer(5)*x**Integer(2)+Integer(2)*x+Integer(4), Integer(4)*x**Integer(3)+Integer(6)*x**Integer(2)+Integer(6)*x+Integer(5)]]) >>> A = matrix(pR, Integer(1), Integer(3), [ ... [Integer(3)*x**Integer(4)+Integer(3)*x**Integer(3)+Integer(4)*x**Integer(2)+Integer(5)*x+Integer(1), x**Integer(4)+x**Integer(3)+Integer(5)*x**Integer(2)+Integer(4)*x+Integer(4), Integer(4)*x**Integer(4)+Integer(2)*x**Integer(3)+x]]) >>> Q, R = A.reduce(B,return_quotient=True); R [3*x^4 + 3*x^3 + 4*x + 3 2*x + 2 2*x + 6] >>> A == Q*B + R True >>> P = B.popov_form(); P.leading_positions(return_degree=True) ([1, 2], [2, 2]) >>> R.degree_matrix() [4 1 1] >>> A.reduce(P) == R True >>> A.reduce(P[:,:Integer(2)]) Traceback (most recent call last): ... ValueError: column dimension of self should be the column dimension of the input matrix - Demonstrating shifts: - sage: Qs, Rs = A.reduce(B, shifts=[0,2,4], return_quotient=True); Rs [3*x^4 + 3*x^3 + 6*x + 2 4*x^3 + 5*x 0] sage: A == Qs*B + Rs True sage: Ps = B.popov_form(shifts=[0,2,4]) sage: Ps.leading_positions(shifts=[0,2,4], return_degree=True) ([1, 2], [4, 0]) sage: Rs.degree_matrix() [ 4 3 -1] sage: A.reduce(Ps, shifts=[0,2,4]) == Rs True - >>> from sage.all import * >>> Qs, Rs = A.reduce(B, shifts=[Integer(0),Integer(2),Integer(4)], return_quotient=True); Rs [3*x^4 + 3*x^3 + 6*x + 2 4*x^3 + 5*x 0] >>> A == Qs*B + Rs True >>> Ps = B.popov_form(shifts=[Integer(0),Integer(2),Integer(4)]) >>> Ps.leading_positions(shifts=[Integer(0),Integer(2),Integer(4)], return_degree=True) ([1, 2], [4, 0]) >>> Rs.degree_matrix() [ 4 3 -1] >>> A.reduce(Ps, shifts=[Integer(0),Integer(2),Integer(4)]) == Rs True - If - return_quotientis- False, only the normal form is returned:- sage: R == A.reduce(B) and Rs == A.reduce(B, shifts=[0,2,4]) True - >>> from sage.all import * >>> R == A.reduce(B) and Rs == A.reduce(B, shifts=[Integer(0),Integer(2),Integer(4)]) True - Demonstrating column-wise normal forms, with a matrix \(A\) which has several columns, and a matrix \(B\) which does not have full column rank (its column-wise Popov form has a zero column): - sage: A = matrix(pR, 2, 2, ....: [[5*x^3 + 2*x^2 + 4*x + 1, x^3 + 4*x + 4], ....: [2*x^3 + 5*x^2 + 2*x + 4, 2*x^3 + 3*x + 2]]) sage: (Q,R) = A.reduce(B,row_wise=False, return_quotient=True); R [0 3] [0 0] sage: A == B*Q + R True sage: P = B.popov_form(row_wise=False); P [x + 2 6 0] [ 0 1 0] sage: P.leading_positions(row_wise=False, return_degree=True) ([0, 1, -1], [1, 0, -1]) sage: R.degree_matrix() [-1 0] [-1 -1] - >>> from sage.all import * >>> A = matrix(pR, Integer(2), Integer(2), ... [[Integer(5)*x**Integer(3) + Integer(2)*x**Integer(2) + Integer(4)*x + Integer(1), x**Integer(3) + Integer(4)*x + Integer(4)], ... [Integer(2)*x**Integer(3) + Integer(5)*x**Integer(2) + Integer(2)*x + Integer(4), Integer(2)*x**Integer(3) + Integer(3)*x + Integer(2)]]) >>> (Q,R) = A.reduce(B,row_wise=False, return_quotient=True); R [0 3] [0 0] >>> A == B*Q + R True >>> P = B.popov_form(row_wise=False); P [x + 2 6 0] [ 0 1 0] >>> P.leading_positions(row_wise=False, return_degree=True) ([0, 1, -1], [1, 0, -1]) >>> R.degree_matrix() [-1 0] [-1 -1] - See also 
 - reduced_form(transformation=None, shifts=None, row_wise=True, include_zero_vectors=True)[source]¶
- Return a row reduced form of this matrix (resp. a column reduced form if the optional parameter - row_wiseis set to- False).- An \(m \times n\) univariate polynomial matrix \(M\) is said to be in (shifted) row reduced form if it has \(k\) nonzero rows with \(k \leq n\) and its (shifted) leading matrix has rank \(k\). See - is_reduced()for more information.- Currently, the implementation of this method is a direct call to - weak_popov_form().- INPUT: - transformation– (default:- False) if this is- True, the transformation matrix \(U\) will be returned as well: this is a unimodular matrix over \(\Bold{K}[x]\) such that- selfequals \(UR\), where \(R\) is the output matrix.
- shifts– (default:- None) list of integers;- Noneis interpreted as- shifts=[0,...,0]
- row_wise– boolean (default:- True);- Trueif working row-wise (see the class description)
- include_zero_vectors– boolean (default:- True);- Falseif one does not allow zero rows in row reduced forms (resp. zero columns in column reduced forms)
 - OUTPUT: - A polynomial matrix \(R\) which is a reduced form of - selfif- transformation=False; otherwise two polynomial matrices \(R, U\) such that \(UA = R\) and \(R\) is reduced and \(U\) is unimodular where \(A\) is- self.
 - EXAMPLES: - sage: pR.<x> = GF(3)[] sage: A = matrix(pR, 3, [x, x^2, x^3, ....: x^2, x^1, 0, ....: x^3, x^3, x^3]) sage: R = A.reduced_form(); R [ x x^2 x^3] [ x^2 x 0] [ x^3 + 2*x x^3 + 2*x^2 0] sage: R.is_reduced() True sage: R2 = A.reduced_form(shifts=[6,3,0]); R2 [ x x^2 x^3] [ 0 2*x^2 + x 2*x^4 + x^3] [ 0 0 2*x^5 + x^4 + x^3] sage: R2.is_reduced(shifts=[6,3,0]) True sage: R2.is_reduced() False - >>> from sage.all import * >>> pR = GF(Integer(3))['x']; (x,) = pR._first_ngens(1) >>> A = matrix(pR, Integer(3), [x, x**Integer(2), x**Integer(3), ... x**Integer(2), x**Integer(1), Integer(0), ... x**Integer(3), x**Integer(3), x**Integer(3)]) >>> R = A.reduced_form(); R [ x x^2 x^3] [ x^2 x 0] [ x^3 + 2*x x^3 + 2*x^2 0] >>> R.is_reduced() True >>> R2 = A.reduced_form(shifts=[Integer(6),Integer(3),Integer(0)]); R2 [ x x^2 x^3] [ 0 2*x^2 + x 2*x^4 + x^3] [ 0 0 2*x^5 + x^4 + x^3] >>> R2.is_reduced(shifts=[Integer(6),Integer(3),Integer(0)]) True >>> R2.is_reduced() False - If the matrix is an \(n \times 1\) matrix with at least one nonzero entry, \(R\) has a single nonzero entry and that entry is a scalar multiple of the greatest-common-divisor of the entries of the matrix: - sage: A = matrix([[x*(x-1)*(x+1)], [x*(x-2)*(x+2)], [x]]) sage: R = A.reduced_form() sage: R [x] [0] [0] - >>> from sage.all import * >>> A = matrix([[x*(x-Integer(1))*(x+Integer(1))], [x*(x-Integer(2))*(x+Integer(2))], [x]]) >>> R = A.reduced_form() >>> R [x] [0] [0] - A zero matrix is already reduced: - sage: A = matrix(pR, 2, [0,0,0,0]) sage: A.reduced_form() [0 0] [0 0] - >>> from sage.all import * >>> A = matrix(pR, Integer(2), [Integer(0),Integer(0),Integer(0),Integer(0)]) >>> A.reduced_form() [0 0] [0 0] - In the following example, the original matrix is already reduced, but the output is a different matrix: currently this method is an alias for - weak_popov_form(), which is a stronger reduced form:- sage: R.<x> = QQ['x'] sage: A = matrix([[x,x,x],[0,0,x]]); A [x x x] [0 0 x] sage: A.is_reduced() True sage: W = A.reduced_form(); W [ x x x] [-x -x 0] sage: W.is_weak_popov() True - >>> from sage.all import * >>> R = QQ['x']; (x,) = R._first_ngens(1) >>> A = matrix([[x,x,x],[Integer(0),Integer(0),x]]); A [x x x] [0 0 x] >>> A.is_reduced() True >>> W = A.reduced_form(); W [ x x x] [-x -x 0] >>> W.is_weak_popov() True - The last example shows the usage of the transformation parameter: - sage: # needs sage.rings.finite_rings sage: Fq.<a> = GF(2^3) sage: pR.<x> = Fq[] sage: A = matrix(pR, [[x^2+a, x^4+a], ....: [ x^3, a*x^4]]) sage: W, U = A.reduced_form(transformation=True) sage: W, U ( [ x^2 + a x^4 + a] [1 0] [x^3 + a*x^2 + a^2 a^2], [a 1] ) sage: W.is_reduced() True sage: U*W == A True sage: U.is_invertible() True - >>> from sage.all import * >>> # needs sage.rings.finite_rings >>> Fq = GF(Integer(2)**Integer(3), names=('a',)); (a,) = Fq._first_ngens(1) >>> pR = Fq['x']; (x,) = pR._first_ngens(1) >>> A = matrix(pR, [[x**Integer(2)+a, x**Integer(4)+a], ... [ x**Integer(3), a*x**Integer(4)]]) >>> W, U = A.reduced_form(transformation=True) >>> W, U ( [ x^2 + a x^4 + a] [1 0] [x^3 + a*x^2 + a^2 a^2], [a 1] ) >>> W.is_reduced() True >>> U*W == A True >>> U.is_invertible() True - See also - is_reduced(),- weak_popov_form(),- popov_form(),- hermite_form().
 - reverse(degree=None, row_wise=True, entry_wise=False)[source]¶
- Return the matrix which is obtained from this matrix after reversing all its entries with respect to the degree as specified by - degree.- Reversing a polynomial with respect to an integer \(d\) follows the convention for univariate polynomials, in particular it uses truncation or zero-padding as necessary if \(d\) differs from the degree of this polynomial. - If - entry_wiseis- True: the input- degreeand- row_wiseare ignored, and all entries of the matrix are reversed with respect to their respective degrees.- If - entry_wiseis- False(the default):- if - degreeis an integer, all entries are reversed with respect to it;
- if - degreeis not provided, then all entries are reversed with respect to the degree of the whole matrix;
- if - degreeis a list \((d_1,\ldots,d_m)\) and- row_wiseis- True, all entries of the \(i\)-th row are reversed with respect to \(d_i\) for each \(i\);
- if - degreeis a list \((d_1,\ldots,d_n)\) and- row_wiseis- False, all entries of the \(j\)-th column are reversed with respect to \(d_j\) for each \(j\).
 - INPUT: - degree– (default:- None) a list of nonnegative integers, or a nonnegative integer
- row_wise– boolean (default:- True); if- True(resp.- False) then- degreeshould be a list of length equal to the row (resp. column) dimension of this matrix
- entry_wise– boolean (default:- False); if- Truethen the input- degreeand- row_wiseare ignored
 - OUTPUT: a polynomial matrix - EXAMPLES: - sage: pR.<x> = GF(7)[] sage: M = matrix([ ....: [ x^3+5*x^2+5*x+1, 5, 6*x+4, 0], ....: [ 6*x^2+3*x+1, 1, 2, 0], ....: [2*x^3+4*x^2+6*x+4, 5*x + 1, 2*x^2+5*x+5, x^2+5*x+6] ....: ]) sage: M.reverse() [ x^3 + 5*x^2 + 5*x + 1 5*x^3 4*x^3 + 6*x^2 0] [ x^3 + 3*x^2 + 6*x x^3 2*x^3 0] [4*x^3 + 6*x^2 + 4*x + 2 x^3 + 5*x^2 5*x^3 + 5*x^2 + 2*x 6*x^3 + 5*x^2 + x] sage: M.reverse(1) [ x + 5 5*x 4*x + 6 0] [ x + 3 x 2*x 0] [4*x + 6 x + 5 5*x + 5 6*x + 5] sage: M.reverse(0) == M.constant_matrix() True - >>> from sage.all import * >>> pR = GF(Integer(7))['x']; (x,) = pR._first_ngens(1) >>> M = matrix([ ... [ x**Integer(3)+Integer(5)*x**Integer(2)+Integer(5)*x+Integer(1), Integer(5), Integer(6)*x+Integer(4), Integer(0)], ... [ Integer(6)*x**Integer(2)+Integer(3)*x+Integer(1), Integer(1), Integer(2), Integer(0)], ... [Integer(2)*x**Integer(3)+Integer(4)*x**Integer(2)+Integer(6)*x+Integer(4), Integer(5)*x + Integer(1), Integer(2)*x**Integer(2)+Integer(5)*x+Integer(5), x**Integer(2)+Integer(5)*x+Integer(6)] ... ]) >>> M.reverse() [ x^3 + 5*x^2 + 5*x + 1 5*x^3 4*x^3 + 6*x^2 0] [ x^3 + 3*x^2 + 6*x x^3 2*x^3 0] [4*x^3 + 6*x^2 + 4*x + 2 x^3 + 5*x^2 5*x^3 + 5*x^2 + 2*x 6*x^3 + 5*x^2 + x] >>> M.reverse(Integer(1)) [ x + 5 5*x 4*x + 6 0] [ x + 3 x 2*x 0] [4*x + 6 x + 5 5*x + 5 6*x + 5] >>> M.reverse(Integer(0)) == M.constant_matrix() True - Entry-wise reversing with respect to each entry’s degree: - sage: M.reverse(entry_wise=True) [ x^3 + 5*x^2 + 5*x + 1 5 4*x + 6 0] [ x^2 + 3*x + 6 1 2 0] [4*x^3 + 6*x^2 + 4*x + 2 x + 5 5*x^2 + 5*x + 2 6*x^2 + 5*x + 1] - >>> from sage.all import * >>> M.reverse(entry_wise=True) [ x^3 + 5*x^2 + 5*x + 1 5 4*x + 6 0] [ x^2 + 3*x + 6 1 2 0] [4*x^3 + 6*x^2 + 4*x + 2 x + 5 5*x^2 + 5*x + 2 6*x^2 + 5*x + 1] - Row-wise and column-wise degree reversing are available: - sage: M.reverse([2,3,1]) [ x^2 + 5*x + 5 5*x^2 4*x^2 + 6*x 0] [x^3 + 3*x^2 + 6*x x^3 2*x^3 0] [ 4*x + 6 x + 5 5*x + 5 6*x + 5] sage: M.reverse(M.column_degrees(), row_wise=False) [ x^3 + 5*x^2 + 5*x + 1 5*x 4*x^2 + 6*x 0] [ x^3 + 3*x^2 + 6*x x 2*x^2 0] [4*x^3 + 6*x^2 + 4*x + 2 x + 5 5*x^2 + 5*x + 2 6*x^2 + 5*x + 1] - >>> from sage.all import * >>> M.reverse([Integer(2),Integer(3),Integer(1)]) [ x^2 + 5*x + 5 5*x^2 4*x^2 + 6*x 0] [x^3 + 3*x^2 + 6*x x^3 2*x^3 0] [ 4*x + 6 x + 5 5*x + 5 6*x + 5] >>> M.reverse(M.column_degrees(), row_wise=False) [ x^3 + 5*x^2 + 5*x + 1 5*x 4*x^2 + 6*x 0] [ x^3 + 3*x^2 + 6*x x 2*x^2 0] [4*x^3 + 6*x^2 + 4*x + 2 x + 5 5*x^2 + 5*x + 2 6*x^2 + 5*x + 1] - Wrong length or negativity of input degree raise errors: - sage: M.reverse([1,3,1,4]) Traceback (most recent call last): … ValueError: length of input degree list should be the row dimension of the input matrix - sage: M.reverse([5,2,1], row_wise=False) Traceback (most recent call last): … ValueError: length of input degree list should be the column dimension of the input matrix - sage: M.reverse([2,3,-1]) Traceback (most recent call last): … ValueError: degree argument must be a nonnegative integer, got -1 
 - right_quo_rem(B)[source]¶
- Return, if it exists, the quotient and remainder \((Q,R)\) such that - selfis \(QB+R\), where \(R\) has column degrees less than those of \(B\) entrywise.- If - selfis a \(k \times m\) polynomial matrix (written \(A\) below), and the input \(B\) is an \(m \times m\) polynomial matrix in column reduced form, then \((Q,R)\) is unique. Both \(Q\) and \(R\) have dimensions \(k \times m\). In this case, this method implements Newton iteration of a reversed polynomial matrix \(B\), generalizing to matrices the fast division of univariate polynomials.- If \(A\) is a \(k \times n\) polynomial matrix, and the input \(B\) is an \(m \times n\) polynomial matrix such that \(B\) has full column rank, or more generally such that the matrix equation \(A = XB\) has a rational solution, then there exists such a \((Q,R)\) but it may not be unique; the algorithm returns one such quotient and remainder. Here \(Q\) is \(k \times m\) and \(R\) is \(k \times n\). In this case, this method follows the folklore approach based on solving the matrix equation \(A = XB\) and splitting \(X\) into its polynomial part and proper fraction part. - Finally, if the matrix equation \(A = XB\) has no rational solution, this method computes the normal form \(R\) and quotient \(Q\) of the rows of \(A\) with respect to the row space of \(B\) (see - reduce()). Doing this for a well-chosen shift ensures that either \(R\) does not have column degrees less than those of \(B\), and then there is no valid quotient and remainder, or it does satisfy this degree constraint, and then this \(R\) can be returned as a remainder along with the quotient \(Q\).- A - ValueErroris raised if the dimensions of- selfand \(B\) are not conformal, or if there exists no quotient and remainder.- EXAMPLES: - Case where \(B\) is a square, column reduced matrix: - sage: pR.<x> = GF(7)[] sage: A = matrix(pR, 2, 3, ....: [[3*x^3 + 3*x, 3*x^3 + 6*x + 5, 2*x^3 + 2*x + 6], ....: [2*x^3 + 4, 6*x^3 + 5*x^2 + 1, 3*x^2 + 2*x + 2]]) sage: B = matrix(pR, 3, 3, ....: [[4*x^2 + 3*x + 3, 3*x^2 + 3*x + 1, 4*x^2 + x + 4], ....: [6*x^2 + 2*x + 3, 4*x^2 + 3*x, 3*x^2 + 4*x], ....: [5*x^2 + 3*x + 6, 6*x^2 + x + 4, 3*x^2 + 3*x + 2]]) sage: B.is_reduced(row_wise=False) True sage: Q, R = A.right_quo_rem(B); Q, R ( [ 4*x x + 2 6*x + 1] [ x + 2 6*x + 1 5*x + 4] [4*x + 3 x + 6 3*x + 4], [4*x + 2 2*x + 3 4*x + 3] ) sage: A == Q*B+R and R.degree() < 2 True sage: A[:,:2].right_quo_rem(B) Traceback (most recent call last): ... ValueError: column dimension of self should be the column dimension of the input matrix sage: B = matrix(pR, 3, 3, ....: [[3, 3*x^3 + 3*x + 1, 4*x^2 + x + 4], ....: [x + 3, 4*x^2 + 3*x, 3*x^2 + 4*x], ....: [6, 6*x^3 + x + 4, 3*x^2 + 3*x + 2]]) sage: B.is_reduced(row_wise=False) True sage: Q, R = A.right_quo_rem(B); Q, R ( [2*x^2 + 4*x + 6 3*x^2 + 5*x 6*x^2 + 3*x] [6*x^2 + 4*x + 1 2*x^2 + x + 5 4*x^2 + 6*x + 1], [ 3 6 2*x + 3] [ 1 5*x^2 + 2*x + 3 6*x + 3] ) sage: cdegR = R.column_degrees(); cdegB = B.column_degrees() sage: A == Q*B+R and all([cdegR[i] < cdegB[i] for i in range(3)]) True - >>> from sage.all import * >>> pR = GF(Integer(7))['x']; (x,) = pR._first_ngens(1) >>> A = matrix(pR, Integer(2), Integer(3), ... [[Integer(3)*x**Integer(3) + Integer(3)*x, Integer(3)*x**Integer(3) + Integer(6)*x + Integer(5), Integer(2)*x**Integer(3) + Integer(2)*x + Integer(6)], ... [Integer(2)*x**Integer(3) + Integer(4), Integer(6)*x**Integer(3) + Integer(5)*x**Integer(2) + Integer(1), Integer(3)*x**Integer(2) + Integer(2)*x + Integer(2)]]) >>> B = matrix(pR, Integer(3), Integer(3), ... [[Integer(4)*x**Integer(2) + Integer(3)*x + Integer(3), Integer(3)*x**Integer(2) + Integer(3)*x + Integer(1), Integer(4)*x**Integer(2) + x + Integer(4)], ... [Integer(6)*x**Integer(2) + Integer(2)*x + Integer(3), Integer(4)*x**Integer(2) + Integer(3)*x, Integer(3)*x**Integer(2) + Integer(4)*x], ... [Integer(5)*x**Integer(2) + Integer(3)*x + Integer(6), Integer(6)*x**Integer(2) + x + Integer(4), Integer(3)*x**Integer(2) + Integer(3)*x + Integer(2)]]) >>> B.is_reduced(row_wise=False) True >>> Q, R = A.right_quo_rem(B); Q, R ( [ 4*x x + 2 6*x + 1] [ x + 2 6*x + 1 5*x + 4] [4*x + 3 x + 6 3*x + 4], [4*x + 2 2*x + 3 4*x + 3] ) >>> A == Q*B+R and R.degree() < Integer(2) True >>> A[:,:Integer(2)].right_quo_rem(B) Traceback (most recent call last): ... ValueError: column dimension of self should be the column dimension of the input matrix >>> B = matrix(pR, Integer(3), Integer(3), ... [[Integer(3), Integer(3)*x**Integer(3) + Integer(3)*x + Integer(1), Integer(4)*x**Integer(2) + x + Integer(4)], ... [x + Integer(3), Integer(4)*x**Integer(2) + Integer(3)*x, Integer(3)*x**Integer(2) + Integer(4)*x], ... [Integer(6), Integer(6)*x**Integer(3) + x + Integer(4), Integer(3)*x**Integer(2) + Integer(3)*x + Integer(2)]]) >>> B.is_reduced(row_wise=False) True >>> Q, R = A.right_quo_rem(B); Q, R ( [2*x^2 + 4*x + 6 3*x^2 + 5*x 6*x^2 + 3*x] [6*x^2 + 4*x + 1 2*x^2 + x + 5 4*x^2 + 6*x + 1], <BLANKLINE> [ 3 6 2*x + 3] [ 1 5*x^2 + 2*x + 3 6*x + 3] ) >>> cdegR = R.column_degrees(); cdegB = B.column_degrees() >>> A == Q*B+R and all([cdegR[i] < cdegB[i] for i in range(Integer(3))]) True - With a nonsingular but also non-reduced matrix, there exists a solution, but it might not be unique: - sage: B = matrix(pR, 3, 3, ....: [[ 5, 0, 2*x + 6], ....: [ 4*x, 3*x^2 + 4*x + 5, x + 1], ....: [3*x^2 + 5*x + 2, 6*x^3 + 4*x + 6, 3]]) sage: B.det() != 0 and (not B.is_reduced(row_wise=False)) True sage: Q, R = A.right_quo_rem(B); Q, R ( [ 6*x^2 + 3*x 4*x^2 + 3*x + 1 5*x + 1] [ x^2 + 5*x + 5 5*x^2 + 3*x + 5 x + 2], [ 4*x + 5 x^2 + 2*x + 1 2] [ 6*x + 3 5*x^2 + 6 3] ) sage: cdegR = R.column_degrees(); cdegB = B.column_degrees() sage: A == Q*B+R and all(cdegR[i] < cdegB[i] for i in range(3)) True sage: Q2 = matrix(pR, 2, 3, ....: [[6*x^2 + 3*x + 1, 4*x^2 + 3*x + 6, 5*x + 1], ....: [ x^2 + 5*x + 3, 5*x^2 + 3*x + 2, x + 2]]) sage: R2 = matrix(pR, 2, 3, ....: [[ 5*x, 3*x + 4, 5], ....: [4*x + 6, 5*x, 4]]) sage: A == Q2*B + R2 True - >>> from sage.all import * >>> B = matrix(pR, Integer(3), Integer(3), ... [[ Integer(5), Integer(0), Integer(2)*x + Integer(6)], ... [ Integer(4)*x, Integer(3)*x**Integer(2) + Integer(4)*x + Integer(5), x + Integer(1)], ... [Integer(3)*x**Integer(2) + Integer(5)*x + Integer(2), Integer(6)*x**Integer(3) + Integer(4)*x + Integer(6), Integer(3)]]) >>> B.det() != Integer(0) and (not B.is_reduced(row_wise=False)) True >>> Q, R = A.right_quo_rem(B); Q, R ( [ 6*x^2 + 3*x 4*x^2 + 3*x + 1 5*x + 1] [ x^2 + 5*x + 5 5*x^2 + 3*x + 5 x + 2], <BLANKLINE> [ 4*x + 5 x^2 + 2*x + 1 2] [ 6*x + 3 5*x^2 + 6 3] ) >>> cdegR = R.column_degrees(); cdegB = B.column_degrees() >>> A == Q*B+R and all(cdegR[i] < cdegB[i] for i in range(Integer(3))) True >>> Q2 = matrix(pR, Integer(2), Integer(3), ... [[Integer(6)*x**Integer(2) + Integer(3)*x + Integer(1), Integer(4)*x**Integer(2) + Integer(3)*x + Integer(6), Integer(5)*x + Integer(1)], ... [ x**Integer(2) + Integer(5)*x + Integer(3), Integer(5)*x**Integer(2) + Integer(3)*x + Integer(2), x + Integer(2)]]) >>> R2 = matrix(pR, Integer(2), Integer(3), ... [[ Integer(5)*x, Integer(3)*x + Integer(4), Integer(5)], ... [Integer(4)*x + Integer(6), Integer(5)*x, Integer(4)]]) >>> A == Q2*B + R2 True - The same remark holds more generally for full column rank matrices: there exists a solution, but it might not be unique. However, for all other cases (rank-deficient matrix \(B\) or matrix \(B\) having strictly fewer rows than columns) there may be no solution: - sage: C = B.stack(B[1,:] + B[2,:]) # 4 x 3, full column rank sage: Q, R = A.right_quo_rem(C); Q, R ( [ 6*x^2 + 3*x 4*x^2 + 3*x + 1 5*x + 1 0] [ x^2 + 5*x + 5 5*x^2 + 3*x + 5 x + 2 0], [ 4*x + 5 x^2 + 2*x + 1 2] [ 6*x + 3 5*x^2 + 6 3] ) sage: A.right_quo_rem(B[:2,:]) # matrix 2 x 3, full row rank # needs sage.rings.finite_rings Traceback (most recent call last): ... ValueError: division of these matrices does not admit a remainder with the required degree property sage: D = copy(B); D[2,:] = B[0,:]+B[1,:] # square, singular sage: A.right_quo_rem(D) Traceback (most recent call last): ... ValueError: division of these matrices does not admit a remainder with the required degree property - >>> from sage.all import * >>> C = B.stack(B[Integer(1),:] + B[Integer(2),:]) # 4 x 3, full column rank >>> Q, R = A.right_quo_rem(C); Q, R ( [ 6*x^2 + 3*x 4*x^2 + 3*x + 1 5*x + 1 0] [ x^2 + 5*x + 5 5*x^2 + 3*x + 5 x + 2 0], <BLANKLINE> [ 4*x + 5 x^2 + 2*x + 1 2] [ 6*x + 3 5*x^2 + 6 3] ) >>> A.right_quo_rem(B[:Integer(2),:]) # matrix 2 x 3, full row rank # needs sage.rings.finite_rings Traceback (most recent call last): ... ValueError: division of these matrices does not admit a remainder with the required degree property >>> D = copy(B); D[Integer(2),:] = B[Integer(0),:]+B[Integer(1),:] # square, singular >>> A.right_quo_rem(D) Traceback (most recent call last): ... ValueError: division of these matrices does not admit a remainder with the required degree property - In the latter case (rank-deficient or strictly fewer rows than columns, with no solution to \(A = XB\)), there might still be a quotient and remainder, in which case this method will find it via normal form computation: - sage: B = matrix(pR, 1, 2, [[x, x]]) sage: A = matrix(pR, 1, 2, [[x, x+2]]) sage: A.right_quo_rem(B) ([1], [0 2]) sage: A == 1*B + matrix([[0,2]]) True - >>> from sage.all import * >>> B = matrix(pR, Integer(1), Integer(2), [[x, x]]) >>> A = matrix(pR, Integer(1), Integer(2), [[x, x+Integer(2)]]) >>> A.right_quo_rem(B) ([1], [0 2]) >>> A == Integer(1)*B + matrix([[Integer(0),Integer(2)]]) True - See also 
 - row_degrees(shifts=None)[source]¶
- Return the (shifted) row degrees of this matrix. - For a given polynomial matrix \(M = (M_{i,j})_{i,j}\) with \(m\) rows and \(n\) columns, its row degrees is the tuple \((d_1,\ldots,d_m)\) where \(d_i = \max_j(\deg(M_{i,j}))\) for \(1\leq i \leq m\). Thus, \(d_i=-1\) if the \(i\)-th row of \(M\) is zero, and \(d_i \geq 0\) otherwise. - For given shifts \(s_1,\ldots,s_n \in \ZZ\), the shifted row degrees of \(M\) is \((d_1,\ldots,d_m)\) where \(d_i = \max_j(\deg(M_{i,j})+s_j)\). Here, if the \(i\)-th row of \(M\) is zero then \(d_i =\min(s_1,\ldots,s_n)-1\); otherwise, \(d_i\) is larger than this value. - INPUT: - shifts– (default:- None) list of integers;- Noneis interpreted as- shifts=[0,...,0]
 - OUTPUT: list of integers - REFERENCES: - [Wol1974] (Section 2.5, without shifts), and [VBB1992] (Section 3). 
- Up to changes of signs, shifted row degrees coincide with the notion of defect commonly used in the rational approximation literature (see for example [Bec1992] ). 
 - EXAMPLES: - sage: pR.<x> = GF(7)[] sage: M = matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]]) sage: M.row_degrees() [1, 3] sage: M.row_degrees(shifts=[0,1,2]) [2, 3] - >>> from sage.all import * >>> pR = GF(Integer(7))['x']; (x,) = pR._first_ngens(1) >>> M = matrix(pR, [[Integer(3)*x+Integer(1), Integer(0), Integer(1)], [x**Integer(3)+Integer(3), Integer(0), Integer(0)]]) >>> M.row_degrees() [1, 3] >>> M.row_degrees(shifts=[Integer(0),Integer(1),Integer(2)]) [2, 3] - A zero row in a polynomial matrix can be identified in the (shifted) row degrees as the entries equal to - min(shifts)-1:- sage: M = matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0], [0, 0, 0]]) sage: M.row_degrees() [1, 3, -1] sage: M.row_degrees(shifts=[-2,1,2]) [2, 1, -3] - >>> from sage.all import * >>> M = matrix(pR, [[Integer(3)*x+Integer(1), Integer(0), Integer(1)], [x**Integer(3)+Integer(3), Integer(0), Integer(0)], [Integer(0), Integer(0), Integer(0)]]) >>> M.row_degrees() [1, 3, -1] >>> M.row_degrees(shifts=[-Integer(2),Integer(1),Integer(2)]) [2, 1, -3] - The row degrees of an empty matrix (\(0\times n\) or \(m\times 0\)) is not defined: - sage: M = matrix(pR, 0, 3) sage: M.row_degrees() Traceback (most recent call last): ... ValueError: empty matrix does not have row degrees sage: M = matrix(pR, 3, 0) sage: M.row_degrees() Traceback (most recent call last): ... ValueError: empty matrix does not have row degrees - >>> from sage.all import * >>> M = matrix(pR, Integer(0), Integer(3)) >>> M.row_degrees() Traceback (most recent call last): ... ValueError: empty matrix does not have row degrees >>> M = matrix(pR, Integer(3), Integer(0)) >>> M.row_degrees() Traceback (most recent call last): ... ValueError: empty matrix does not have row degrees 
 - shift(d, row_wise=True)[source]¶
- Return the matrix which is obtained from this matrix after shifting all its entries as specified by \(d\). - if \(d\) is an integer, the shift is by \(d\) for all entries; 
- if \(d\) is a list \((d_1,\ldots,d_m)\) and - row_wiseis- True, all entries of the \(i\)-th row are shifted by \(d_i\) for each \(i\);
- if \(d\) is a list \((d_1,\ldots,d_n)\) and - row_wiseis- False, all entries of the \(j\)-th column are shifted by \(d_j\) for each \(j\).
 - Shifting by \(d\) means multiplying by the variable to the power \(d\); if \(d\) is negative then terms of negative degree after shifting are discarded. - INPUT: - d– list of integers, or an integer,
- row_wise– boolean (default:- True); if- True(resp.- False) then \(d\) should be a list of length equal to the row (resp. column) dimension of this matrix
 - OUTPUT: a polynomial matrix - EXAMPLES: - sage: pR.<x> = GF(7)[] sage: M = matrix([ ....: [ x^3+5*x^2+5*x+1, 5, 6*x+4, 0], ....: [ 6*x^2+3*x+1, 1, 2, 0], ....: [2*x^3+4*x^2+6*x+4, 5*x + 1, 2*x^2+5*x+5, x^2+5*x+6] ....: ]) sage: M.shift(-2) [ x + 5 0 0 0] [ 6 0 0 0] [2*x + 4 0 2 1] - >>> from sage.all import * >>> pR = GF(Integer(7))['x']; (x,) = pR._first_ngens(1) >>> M = matrix([ ... [ x**Integer(3)+Integer(5)*x**Integer(2)+Integer(5)*x+Integer(1), Integer(5), Integer(6)*x+Integer(4), Integer(0)], ... [ Integer(6)*x**Integer(2)+Integer(3)*x+Integer(1), Integer(1), Integer(2), Integer(0)], ... [Integer(2)*x**Integer(3)+Integer(4)*x**Integer(2)+Integer(6)*x+Integer(4), Integer(5)*x + Integer(1), Integer(2)*x**Integer(2)+Integer(5)*x+Integer(5), x**Integer(2)+Integer(5)*x+Integer(6)] ... ]) >>> M.shift(-Integer(2)) [ x + 5 0 0 0] [ 6 0 0 0] [2*x + 4 0 2 1] - Row-wise and column-wise shifting are available: - sage: M.shift([-1,2,-2]) [ x^2 + 5*x + 5 0 6 0] [6*x^4 + 3*x^3 + x^2 x^2 2*x^2 0] [ 2*x + 4 0 2 1] sage: M.shift([-1,1,0,0], row_wise=False) [ x^2 + 5*x + 5 5*x 6*x + 4 0] [ 6*x + 3 x 2 0] [2*x^2 + 4*x + 6 5*x^2 + x 2*x^2 + 5*x + 5 x^2 + 5*x + 6] sage: M.shift([-d for d in M.row_degrees()]) == M.leading_matrix() True - >>> from sage.all import * >>> M.shift([-Integer(1),Integer(2),-Integer(2)]) [ x^2 + 5*x + 5 0 6 0] [6*x^4 + 3*x^3 + x^2 x^2 2*x^2 0] [ 2*x + 4 0 2 1] >>> M.shift([-Integer(1),Integer(1),Integer(0),Integer(0)], row_wise=False) [ x^2 + 5*x + 5 5*x 6*x + 4 0] [ 6*x + 3 x 2 0] [2*x^2 + 4*x + 6 5*x^2 + x 2*x^2 + 5*x + 5 x^2 + 5*x + 6] >>> M.shift([-d for d in M.row_degrees()]) == M.leading_matrix() True - Length of input shift degree list is checked: - sage: M.shift([1,3,1,4]) Traceback (most recent call last): ... ValueError: length of input shift list should be the row dimension of the input matrix sage: M.shift([5,2,-1], row_wise=False) Traceback (most recent call last): ... ValueError: length of input shift list should be the column dimension of the input matrix - >>> from sage.all import * >>> M.shift([Integer(1),Integer(3),Integer(1),Integer(4)]) Traceback (most recent call last): ... ValueError: length of input shift list should be the row dimension of the input matrix >>> M.shift([Integer(5),Integer(2),-Integer(1)], row_wise=False) Traceback (most recent call last): ... ValueError: length of input shift list should be the column dimension of the input matrix 
 - solve_left_series_trunc(B, d)[source]¶
- Try to find a solution \(X\) to the equation \(X A = B\), at precision - d.- If - selfis a matrix \(A\), then this function returns a vector or matrix \(X\) such that \(X A = B \bmod x^d\). If \(B\) is a vector then \(X\) is a vector, and if \(B\) is a matrix then \(X\) is a matrix.- Raises - ValueErrorif- dis not strictly positive, or if there is a dimension mismatch between \(A\) and \(B\), or if there is no solution to the given matrix equation at the specified precision.- INPUT: - B– a polynomial matrix or polynomial vector
- d– positive integer
 - OUTPUT: - A solution to the matrix equation, returned as polynomial matrix of degree less than - dif- Bis a polynomial matrix; a polynomial vector of degree less than- dif \(B\) is a polynomial vector.- ALGORITHM: - If \(A\) is square with invertible constant term, then the unique solution is found by calling - inverse_series_trunc()and using the Dixon & Moenck-Carter iteration. Otherwise, a left minimal approximant basis of a matrix formed by \(A\) and \(B\) is computed, for an appropriate shift which ensures that this basis reveals either a solution \(X\) or the fact that no such solution exists.- EXAMPLES: - sage: pR.<x> = GF(7)[] sage: A = matrix(pR, 3, 3, ....: [[4*x+5, 5*x^2 + x + 1, 4*x^2 + 4], ....: [6*x^2 + 6*x + 6, 4*x^2 + 5*x, 4*x^2 + x + 3], ....: [3*x^2 + 2, 4*x + 1, x^2 + 3*x]]) sage: A.is_square() and A.constant_matrix().is_invertible() True sage: B = vector([2*x^2 + 6*x + 6, 0, x + 6]) sage: X = A.solve_left_series_trunc(B, 4); X (3*x^3 + 3*x^2 + 2*x + 4, 4*x^3 + x^2 + 2*x + 6, 6*x^3 + x + 3) sage: B == X*A % x**4 True sage: B = matrix(pR, 2, 3, ....: [[3*x, x^2 + x + 2, x^2 + 2*x + 3], ....: [ 0, 6*x^2 + 1, 1]]) sage: A.solve_left_series_trunc(B, 3) [6*x^2 + 2*x + 2 4*x + 3 2*x^2 + 3*x] [3*x^2 + 4*x + 5 4*x^2 + 3 x^2 + 6*x + 3] sage: X = A.solve_left_series_trunc(B, 37); B == X*A % x**37 True - >>> from sage.all import * >>> pR = GF(Integer(7))['x']; (x,) = pR._first_ngens(1) >>> A = matrix(pR, Integer(3), Integer(3), ... [[Integer(4)*x+Integer(5), Integer(5)*x**Integer(2) + x + Integer(1), Integer(4)*x**Integer(2) + Integer(4)], ... [Integer(6)*x**Integer(2) + Integer(6)*x + Integer(6), Integer(4)*x**Integer(2) + Integer(5)*x, Integer(4)*x**Integer(2) + x + Integer(3)], ... [Integer(3)*x**Integer(2) + Integer(2), Integer(4)*x + Integer(1), x**Integer(2) + Integer(3)*x]]) >>> A.is_square() and A.constant_matrix().is_invertible() True >>> B = vector([Integer(2)*x**Integer(2) + Integer(6)*x + Integer(6), Integer(0), x + Integer(6)]) >>> X = A.solve_left_series_trunc(B, Integer(4)); X (3*x^3 + 3*x^2 + 2*x + 4, 4*x^3 + x^2 + 2*x + 6, 6*x^3 + x + 3) >>> B == X*A % x**Integer(4) True >>> B = matrix(pR, Integer(2), Integer(3), ... [[Integer(3)*x, x**Integer(2) + x + Integer(2), x**Integer(2) + Integer(2)*x + Integer(3)], ... [ Integer(0), Integer(6)*x**Integer(2) + Integer(1), Integer(1)]]) >>> A.solve_left_series_trunc(B, Integer(3)) [6*x^2 + 2*x + 2 4*x + 3 2*x^2 + 3*x] [3*x^2 + 4*x + 5 4*x^2 + 3 x^2 + 6*x + 3] >>> X = A.solve_left_series_trunc(B, Integer(37)); B == X*A % x**Integer(37) True - Dimensions of input are checked: - sage: A.solve_left_series_trunc(B[:,:2], 3) Traceback (most recent call last): ... ValueError: number of columns of self must equal number of columns of right-hand side - >>> from sage.all import * >>> A.solve_left_series_trunc(B[:,:Integer(2)], Integer(3)) Traceback (most recent call last): ... ValueError: number of columns of self must equal number of columns of right-hand side - Raises an exception when no solution: - sage: A[2:,:].solve_left_series_trunc(B, 4) Traceback (most recent call last): ... ValueError: matrix equation has no solutions sage: Ax = x*A; C = vector(pR, [1,1,1]) sage: Ax.solve_left_series_trunc(C, 5) Traceback (most recent call last): ... ValueError: matrix equation has no solutions - >>> from sage.all import * >>> A[Integer(2):,:].solve_left_series_trunc(B, Integer(4)) Traceback (most recent call last): ... ValueError: matrix equation has no solutions >>> Ax = x*A; C = vector(pR, [Integer(1),Integer(1),Integer(1)]) >>> Ax.solve_left_series_trunc(C, Integer(5)) Traceback (most recent call last): ... ValueError: matrix equation has no solutions - Supports rectangular and rank-deficient cases: - sage: A[:,:2].solve_left_series_trunc(B[:,:2], 4) [5*x^2 + 2*x + 5 5*x + 5 2*x + 4] [5*x^3 + 2*x + 1 2*x^2 + 2*x + 5 4*x^2] sage: V = matrix([[3*x^2 + 4*x + 1, 4*x]]) sage: A[:2,:].solve_left_series_trunc(V*A[:2,:], 4) == V True sage: A[1,:] = (x+1) * A[0,:]; A[2,:] = (x+5) * A[0,:] sage: B = (3*x^3+x^2+2)*A[0,:] sage: A.solve_left_series_trunc(B, 6) [4*x^2 + 6*x + 2 3*x^2 + x 0] - >>> from sage.all import * >>> A[:,:Integer(2)].solve_left_series_trunc(B[:,:Integer(2)], Integer(4)) [5*x^2 + 2*x + 5 5*x + 5 2*x + 4] [5*x^3 + 2*x + 1 2*x^2 + 2*x + 5 4*x^2] >>> V = matrix([[Integer(3)*x**Integer(2) + Integer(4)*x + Integer(1), Integer(4)*x]]) >>> A[:Integer(2),:].solve_left_series_trunc(V*A[:Integer(2),:], Integer(4)) == V True >>> A[Integer(1),:] = (x+Integer(1)) * A[Integer(0),:]; A[Integer(2),:] = (x+Integer(5)) * A[Integer(0),:] >>> B = (Integer(3)*x**Integer(3)+x**Integer(2)+Integer(2))*A[Integer(0),:] >>> A.solve_left_series_trunc(B, Integer(6)) [4*x^2 + 6*x + 2 3*x^2 + x 0] - See also 
 - solve_right_series_trunc(B, d)[source]¶
- Try to find a solution \(X\) to the equation \(A X = B\), at precision - d.- If - selfis a matrix \(A\), then this function returns a vector or matrix \(X\) such that \(A X = B \bmod x^d\). If \(B\) is a vector then \(X\) is a vector, and if \(B\) is a matrix then \(X\) is a matrix.- Raises - ValueErrorif- dis not strictly positive, or if there is a dimension mismatch between \(A\) and \(B\), or if there is no solution to the given matrix equation at the specified precision.- INPUT: - B– a polynomial matrix or polynomial vector
- d– positive integer
 - OUTPUT: - A solution to the matrix equation, returned as polynomial matrix of degree less than - dif- Bis a polynomial matrix; a polynomial vector of degree less than- dif \(B\) is a polynomial vector.- ALGORITHM: - If \(A\) is square with invertible constant term, then the unique solution is found by calling - inverse_series_trunc()and using the Dixon & Moenck-Carter iteration. Otherwise, a right minimal approximant basis of a matrix formed by \(A\) and \(B\) is computed, for an appropriate shift which ensures that this basis reveals either a solution \(X\) or the fact that no such solution exists.- EXAMPLES: - sage: pR.<x> = GF(7)[] sage: A = matrix(pR, 3, 3, ....: [[4*x+5, 5*x^2 + x + 1, 4*x^2 + 4], ....: [6*x^2 + 6*x + 6, 4*x^2 + 5*x, 4*x^2 + x + 3], ....: [3*x^2 + 2, 4*x + 1, x^2 + 3*x]]) sage: A.is_square() and A.constant_matrix().is_invertible() True sage: B = vector([2*x^2 + 6*x + 6, 0, x + 6]) sage: X = A.solve_right_series_trunc(B, 4); X (2*x^3 + x^2, 5*x^3 + x^2 + 5*x + 6, 4*x^3 + 6*x^2 + 4*x) sage: B == A*X % x**4 True sage: B = matrix(pR, 3, 2, ....: [[5*x^2 + 6*x + 3, 4*x^2 + 6*x + 4], ....: [ x^2 + 4*x + 2, 5*x + 2], ....: [ 5*x + 3, 0]]) sage: A.solve_right_series_trunc(B, 3) [ 3*x^2 + x + 1 5*x^2 + 4*x + 3] [6*x^2 + 3*x + 1 4*x + 1] [ 6*x^2 + 1 2*x^2 + x + 4] sage: X = A.solve_right_series_trunc(B, 37); B == A*X % x**37 True - >>> from sage.all import * >>> pR = GF(Integer(7))['x']; (x,) = pR._first_ngens(1) >>> A = matrix(pR, Integer(3), Integer(3), ... [[Integer(4)*x+Integer(5), Integer(5)*x**Integer(2) + x + Integer(1), Integer(4)*x**Integer(2) + Integer(4)], ... [Integer(6)*x**Integer(2) + Integer(6)*x + Integer(6), Integer(4)*x**Integer(2) + Integer(5)*x, Integer(4)*x**Integer(2) + x + Integer(3)], ... [Integer(3)*x**Integer(2) + Integer(2), Integer(4)*x + Integer(1), x**Integer(2) + Integer(3)*x]]) >>> A.is_square() and A.constant_matrix().is_invertible() True >>> B = vector([Integer(2)*x**Integer(2) + Integer(6)*x + Integer(6), Integer(0), x + Integer(6)]) >>> X = A.solve_right_series_trunc(B, Integer(4)); X (2*x^3 + x^2, 5*x^3 + x^2 + 5*x + 6, 4*x^3 + 6*x^2 + 4*x) >>> B == A*X % x**Integer(4) True >>> B = matrix(pR, Integer(3), Integer(2), ... [[Integer(5)*x**Integer(2) + Integer(6)*x + Integer(3), Integer(4)*x**Integer(2) + Integer(6)*x + Integer(4)], ... [ x**Integer(2) + Integer(4)*x + Integer(2), Integer(5)*x + Integer(2)], ... [ Integer(5)*x + Integer(3), Integer(0)]]) >>> A.solve_right_series_trunc(B, Integer(3)) [ 3*x^2 + x + 1 5*x^2 + 4*x + 3] [6*x^2 + 3*x + 1 4*x + 1] [ 6*x^2 + 1 2*x^2 + x + 4] >>> X = A.solve_right_series_trunc(B, Integer(37)); B == A*X % x**Integer(37) True - Dimensions of input are checked: - sage: A.solve_right_series_trunc(B[:2,:], 3) Traceback (most recent call last): ... ValueError: number of rows of self must equal number of rows of right-hand side - >>> from sage.all import * >>> A.solve_right_series_trunc(B[:Integer(2),:], Integer(3)) Traceback (most recent call last): ... ValueError: number of rows of self must equal number of rows of right-hand side - Raises an exception when no solution: - sage: A[:,2:].solve_right_series_trunc(B, 4) Traceback (most recent call last): ... ValueError: matrix equation has no solutions sage: Ax = x*A; C = vector(pR, [1,1,1]) sage: Ax.solve_right_series_trunc(C, 5) Traceback (most recent call last): ... ValueError: matrix equation has no solutions - >>> from sage.all import * >>> A[:,Integer(2):].solve_right_series_trunc(B, Integer(4)) Traceback (most recent call last): ... ValueError: matrix equation has no solutions >>> Ax = x*A; C = vector(pR, [Integer(1),Integer(1),Integer(1)]) >>> Ax.solve_right_series_trunc(C, Integer(5)) Traceback (most recent call last): ... ValueError: matrix equation has no solutions - Supports rectangular and rank-deficient cases: - sage: A[:2,:].solve_right_series_trunc(B[:2,:],4) [ 5*x^2 + 4*x x + 4] [ x^2 + 3*x + 5 3*x^2 + 4*x + 4] [ 5*x + 3 3*x + 2] sage: V = matrix([[2*x^2 + 5*x + 1], [3*x^2+4]]) sage: A[:,:2].solve_right_series_trunc(A[:,:2]*V, 4) == V True sage: A[:,1] = (x+1) * A[:,0]; A[:,2] = (x+5) * A[:,0] sage: B = (3*x^3+x^2+2)*A[:,0] sage: A.solve_right_series_trunc(B, 6) [4*x^2 + 6*x + 2] [ 3*x^2 + x] [ 0] - >>> from sage.all import * >>> A[:Integer(2),:].solve_right_series_trunc(B[:Integer(2),:],Integer(4)) [ 5*x^2 + 4*x x + 4] [ x^2 + 3*x + 5 3*x^2 + 4*x + 4] [ 5*x + 3 3*x + 2] >>> V = matrix([[Integer(2)*x**Integer(2) + Integer(5)*x + Integer(1)], [Integer(3)*x**Integer(2)+Integer(4)]]) >>> A[:,:Integer(2)].solve_right_series_trunc(A[:,:Integer(2)]*V, Integer(4)) == V True >>> A[:,Integer(1)] = (x+Integer(1)) * A[:,Integer(0)]; A[:,Integer(2)] = (x+Integer(5)) * A[:,Integer(0)] >>> B = (Integer(3)*x**Integer(3)+x**Integer(2)+Integer(2))*A[:,Integer(0)] >>> A.solve_right_series_trunc(B, Integer(6)) [4*x^2 + 6*x + 2] [ 3*x^2 + x] [ 0] - See also 
 - truncate(d, row_wise=True)[source]¶
- Return the matrix which is obtained from this matrix after truncating all its entries according to precisions specified by \(d\). - if \(d\) is an integer, the truncation is at precision \(d\) for all entries; 
- if \(d\) is a list \((d_1,\ldots,d_m)\) and - row_wiseis- True, all entries of the \(i\)-th row are truncated at precision \(d_i\) for each \(i\);
- if \(d\) is a list \((d_1,\ldots,d_n)\) and - row_wiseis- False, all entries of the \(j\)-th column are truncated at precision \(d_j\) for each \(j\).
 - Here the convention for univariate polynomials is to take zero for the truncation for a negative \(d\). - INPUT: - d– list of integers, or an integer,
- row_wise– boolean (default:- True); if- True(resp.- False) then \(d\) should be a list of length equal to the row (resp. column) dimension of this matrix
 - OUTPUT: a polynomial matrix - EXAMPLES: - sage: pR.<x> = GF(7)[] sage: M = matrix([ ....: [ x^3+5*x^2+5*x+1, 5, 6*x+4, 0], ....: [ 6*x^2+3*x+1, 1, 2, 0], ....: [2*x^3+4*x^2+6*x+4, 5*x + 1, 2*x^2+5*x+5, x^2+5*x+6] ....: ]) sage: M.truncate(2) [5*x + 1 5 6*x + 4 0] [3*x + 1 1 2 0] [6*x + 4 5*x + 1 5*x + 5 5*x + 6] sage: M.truncate(1) == M.constant_matrix() True - >>> from sage.all import * >>> pR = GF(Integer(7))['x']; (x,) = pR._first_ngens(1) >>> M = matrix([ ... [ x**Integer(3)+Integer(5)*x**Integer(2)+Integer(5)*x+Integer(1), Integer(5), Integer(6)*x+Integer(4), Integer(0)], ... [ Integer(6)*x**Integer(2)+Integer(3)*x+Integer(1), Integer(1), Integer(2), Integer(0)], ... [Integer(2)*x**Integer(3)+Integer(4)*x**Integer(2)+Integer(6)*x+Integer(4), Integer(5)*x + Integer(1), Integer(2)*x**Integer(2)+Integer(5)*x+Integer(5), x**Integer(2)+Integer(5)*x+Integer(6)] ... ]) >>> M.truncate(Integer(2)) [5*x + 1 5 6*x + 4 0] [3*x + 1 1 2 0] [6*x + 4 5*x + 1 5*x + 5 5*x + 6] >>> M.truncate(Integer(1)) == M.constant_matrix() True - Row-wise and column-wise truncation are available: - sage: M.truncate([3,2,1]) [5*x^2 + 5*x + 1 5 6*x + 4 0] [ 3*x + 1 1 2 0] [ 4 1 5 6] sage: M.truncate([2,1,1,2], row_wise=False) [5*x + 1 5 4 0] [3*x + 1 1 2 0] [6*x + 4 1 5 5*x + 6] - >>> from sage.all import * >>> M.truncate([Integer(3),Integer(2),Integer(1)]) [5*x^2 + 5*x + 1 5 6*x + 4 0] [ 3*x + 1 1 2 0] [ 4 1 5 6] >>> M.truncate([Integer(2),Integer(1),Integer(1),Integer(2)], row_wise=False) [5*x + 1 5 4 0] [3*x + 1 1 2 0] [6*x + 4 1 5 5*x + 6] - Length of list of truncation orders is checked: - sage: M.truncate([2,1,1,2]) Traceback (most recent call last): ... ValueError: length of input precision list should be the row dimension of the input matrix sage: M.truncate([3,2,1], row_wise=False) Traceback (most recent call last): ... ValueError: length of input precision list should be the column dimension of the input matrix - >>> from sage.all import * >>> M.truncate([Integer(2),Integer(1),Integer(1),Integer(2)]) Traceback (most recent call last): ... ValueError: length of input precision list should be the row dimension of the input matrix >>> M.truncate([Integer(3),Integer(2),Integer(1)], row_wise=False) Traceback (most recent call last): ... ValueError: length of input precision list should be the column dimension of the input matrix 
 - weak_popov_form(transformation=False, shifts=None, row_wise=True, ordered=False, include_zero_vectors=True)[source]¶
- Return a (shifted) (ordered) weak Popov form of this matrix. - See - is_weak_popov()for a definition of weak Popov forms. If the input matrix is \(A\), a weak Popov form of \(A\) is any matrix \(P\) in weak Popov form and such that \(UA = P\) for some unimodular matrix \(U\). The latter matrix is called the transformation, and the first optional argument allows one to specify whether to return this transformation.- Sometimes, one forbids weak Popov forms to have zero rows (resp. columns) in the above definitions; an optional parameter allows one to adopt this more restrictive setting. If zero rows (resp. columns) are allowed, the convention here is to place them as the bottommost rows (resp. the rightmost columns) of the output weak Popov form. - Note that, if asking for the transformation and discarding zero vectors (i.e. - transformation=Trueand- include_zero_vectors=False), then the returned transformation is still the complete unimodular matrix, including its bottommost rows (resp. rightmost columns) which correspond to zero rows (resp. columns) of the complete weak Popov form. In fact, this bottom part of the transformation yields a basis of the left (resp. right) kernel of the input matrix.- INPUT: - transformation– (default:- False) if this is- True, the transformation matrix \(U\) will be returned as well
- shifts– (default:- None) list of integers;- Noneis interpreted as- shifts=[0,...,0]
- row_wise– boolean (default:- True);- Trueif working row-wise (see the class description)
- ordered– boolean (default:- False);- Trueif seeking an ordered weak Popov form
- include_zero_vectors– boolean (default:- True);- Falseif zero rows (resp. zero columns) should be discarded from the (ordered) weak Popov forms
 - OUTPUT: - A polynomial matrix which is a weak Popov form of - selfif- transformationis- False; otherwise two polynomial matrices which are a weak Popov form of- selfand the corresponding unimodular transformation.- ALGORITHM: - This method implements the Mulders-Storjohann algorithm of [MS2003], straightforwardly extended to the case of shifted forms. - EXAMPLES: - sage: pR.<x> = GF(7)[] sage: M = matrix(pR, [ ....: [ 6*x+4, 5*x^3+5*x, 6*x^2+2*x+2], ....: [4*x^2+5*x+2, x^4+5*x^2+2*x+4, 4*x^3+6*x^2+6*x+5]]) sage: P, U = M.weak_popov_form(transformation=True) sage: P [ 4 x^2 6*x^2 + x + 2] [ 2 4*x^2 + 2*x + 4 5] sage: U [2*x^2 + 1 4*x] [ 4*x 1] sage: P.is_weak_popov() and U.is_invertible() and U*M == P True - >>> from sage.all import * >>> pR = GF(Integer(7))['x']; (x,) = pR._first_ngens(1) >>> M = matrix(pR, [ ... [ Integer(6)*x+Integer(4), Integer(5)*x**Integer(3)+Integer(5)*x, Integer(6)*x**Integer(2)+Integer(2)*x+Integer(2)], ... [Integer(4)*x**Integer(2)+Integer(5)*x+Integer(2), x**Integer(4)+Integer(5)*x**Integer(2)+Integer(2)*x+Integer(4), Integer(4)*x**Integer(3)+Integer(6)*x**Integer(2)+Integer(6)*x+Integer(5)]]) >>> P, U = M.weak_popov_form(transformation=True) >>> P [ 4 x^2 6*x^2 + x + 2] [ 2 4*x^2 + 2*x + 4 5] >>> U [2*x^2 + 1 4*x] [ 4*x 1] >>> P.is_weak_popov() and U.is_invertible() and U*M == P True - Demonstrating the - orderedoption:- sage: P.leading_positions() [2, 1] sage: PP = M.weak_popov_form(ordered=True); PP [ 2 4*x^2 + 2*x + 4 5] [ 4 x^2 6*x^2 + x + 2] sage: PP.leading_positions() [1, 2] - >>> from sage.all import * >>> P.leading_positions() [2, 1] >>> PP = M.weak_popov_form(ordered=True); PP [ 2 4*x^2 + 2*x + 4 5] [ 4 x^2 6*x^2 + x + 2] >>> PP.leading_positions() [1, 2] - Demonstrating shifts: - sage: P = M.weak_popov_form(shifts=[0,2,4]); P [ 6*x^2 + 6*x + 4 5*x^4 + 4*x^3 + 5*x^2 + 5*x 2*x + 2] [ 2 4*x^2 + 2*x + 4 5] sage: P == M.weak_popov_form(shifts=[-10,-8,-6]) True - >>> from sage.all import * >>> P = M.weak_popov_form(shifts=[Integer(0),Integer(2),Integer(4)]); P [ 6*x^2 + 6*x + 4 5*x^4 + 4*x^3 + 5*x^2 + 5*x 2*x + 2] [ 2 4*x^2 + 2*x + 4 5] >>> P == M.weak_popov_form(shifts=[-Integer(10),-Integer(8),-Integer(6)]) True - Column-wise form is the row-wise form of the transpose: - sage: M.weak_popov_form() == M.T.weak_popov_form(row_wise=False).T True - >>> from sage.all import * >>> M.weak_popov_form() == M.T.weak_popov_form(row_wise=False).T True - Zero vectors can be discarded: - sage: M.weak_popov_form(row_wise=False) [x + 4 6 0] [ 5 1 0] sage: # needs sage.combinat sage: P, U = M.weak_popov_form(transformation=True, ....: row_wise=False, ....: include_zero_vectors=False) sage: P [x + 4 6] [ 5 1] sage: U [ 5*x + 2 5*x^2 + 4*x + 4 3*x^3 + 3*x^2 + 2*x + 4] [ 1 1 2*x + 1] [ 5*x + 5 2 6] sage: M*U[:,:2] == P and (M*U[:,2]).is_zero() True - >>> from sage.all import * >>> M.weak_popov_form(row_wise=False) [x + 4 6 0] [ 5 1 0] >>> # needs sage.combinat >>> P, U = M.weak_popov_form(transformation=True, ... row_wise=False, ... include_zero_vectors=False) >>> P [x + 4 6] [ 5 1] >>> U [ 5*x + 2 5*x^2 + 4*x + 4 3*x^3 + 3*x^2 + 2*x + 4] [ 1 1 2*x + 1] [ 5*x + 5 2 6] >>> M*U[:,:Integer(2)] == P and (M*U[:,Integer(2)]).is_zero() True - See also - is_weak_popov(),- reduced_form(),- popov_form(),- hermite_form().