# Ticket #8071: trac_8071-matrix-kernels-trivially.patch

File trac_8071-matrix-kernels-trivially.patch, 18.5 KB (added by rbeezer, 11 years ago)
• ## sage/matrix/matrix2.pyx

```# HG changeset patch
# User Rob Beezer <beezer@ups.edu>
# Date 1264571203 28800
# Node ID 9d78f89061fc59a0bb1eb79718f097725df7deba
# Parent  4498ef979fc42b03037970223b0873b07d63b96b
Trac 8071: right kernels in trivial cases

diff -r 4498ef979fc4 -r 9d78f89061fc sage/matrix/matrix2.pyx```
 a """ return self.ncols() - self.rank() ###################################### # Kernel Helper Functions ###################################### def _right_kernel_trivial(self): r""" Computes the right kernel of matrices with zero rows or zero columns (or both). OUTPUT: For a matrix with zero columns, the returned kernel has degree zero and thus dimension zero.  For a matrix with zero rows the degree is the number of columns and the dimension is full (the number of columns). If the base ring is a field the result is a :meth:`sage.modules.free_module.VectorSpace`, otherwise the result is a :meth:`sage.modules.free_module.FreeModule`. TESTS: Three rows, zero columns, over a field. :: sage: m = matrix(QQ, [[],[],[]]) sage: m._right_kernel_trivial() Vector space of degree 0 and dimension 0 over Rational Field Basis matrix: [] Zero rows, three columns, over a field. :: sage: m = matrix(QQ, [[],[],[]]).transpose() sage: m._right_kernel_trivial() Vector space of dimension 3 over Rational Field Three rows, zero columns, over a principal ideal domain. :: sage: m = matrix(ZZ, [[],[],[]]) sage: m._right_kernel_trivial() Free module of degree 0 and rank 0 over Integer Ring Echelon basis matrix: [] Zero rows, three columns, over a  principal ideal domain. :: sage: m = matrix(ZZ, [[],[],[]]).transpose() sage: m._right_kernel_trivial() Ambient free module of rank 3 over the principal ideal domain Integer Ring Three rows, zero columns, over an integral domain. If modules over integral domains ever allow creation of zero submodules, this test will fail to raise an error. This is a response to Trac #8071. :: sage: m = matrix(ZZ['x'], [[],[],[]]) sage: m._right_kernel_trivial() Traceback (most recent call last): ... NotImplementedError: Cannot create kernel over Univariate Polynomial Ring in x over Integer Ring Zero rows, three columns, over an integral domain. Creating a full free module as a return value is not problematic. :: sage: m = matrix(ZZ['x'], [[],[],[]]).transpose() sage: m._right_kernel_trivial() Ambient free module of rank 3 over the integral domain Univariate Polynomial Ring in x over Integer Ring Three rows, zero columns, over a generic ring. If modules over generic rings ever allow creation of zero submodules, this test will fail to raise an error. This is a response to Trac #8071. :: sage: m = matrix(Integers(6), [[],[],[]]) sage: m._right_kernel_trivial() Traceback (most recent call last): ... NotImplementedError: Cannot create kernel over Ring of integers modulo 6 Zero rows, three columns, over a generic ring. Creating a full free module as a return value is not problematic. :: sage: m = matrix(Integers(6), [[],[],[]]).transpose() sage: m._right_kernel_trivial() Ambient free module of rank 3 over Ring of integers modulo 6 """ if self._ncols != 0 and self._nrows != 0: raise ValueError('The right kernel is not automatically trivial for %s' % self ) R = self._base_ring if self._ncols == 0:    # from a degree-0 space V = sage.modules.free_module.FreeModule(R, self._ncols) try: Z = V.zero_submodule() except AttributeError: raise NotImplementedError('Cannot create kernel over %s' % R) self.cache('right_kernel', Z) return Z elif self._nrows == 0:  # to a degree-0 space Z = sage.modules.free_module.FreeModule(R, self._ncols) self.cache('right_kernel', Z) return Z def kernel(self, *args, **kwds): r""" Return the (left) kernel of this matrix, as a vector space. This is Return the right kernel of this matrix, as a vector space. This is the space of vectors x such that self\*x=0.  A left kernel can be found with self.left_kernel() or just self.kernel(). INPUT: all additional arguments to the kernel function are passed directly onto the echelon call. By convention if self has 0 columns, the kernel is of dimension 0, whereas the kernel is whole domain if self has 0 rows. ALGORITHM: Elementary row operations do not change the right kernel, since they are left multiplication by an invertible matrix, so we instead compute the kernel of the row echelon form. When the base ring Basis matrix: [      1  4/13*zeta12^2 - 1/13      0 -2/13*zeta12^2 + 7/13] [      0                     0      1                     0] A nontrivial right kernel over a complicated base field. :: A nontrivial right kernel over a complicated base field. :: sage: K = FractionField(PolynomialRing(QQ, 2, 'x')) sage: M = MatrixSpace(K, 2)([[K.1, K.0], [K.1, K.0]]) sage: M in matrix_integer_dense class.  Timing on a 64-bit 3 GHz dual-core machine is about 3 seconds to setup and about 1 second for the kernel() call.  Timings that are one or two orders of magnitude larger indicate problems with reaching specialized derived classes. :: derived classes. :: sage: entries = [[1/(i+j+1) for i in srange(500)] for j in srange(500)] sage: a = matrix(QQ, entries) Right kernel of a matrix defined over a principal ideal domain which is not ZZ or a field. This invokes the general Smith normal form routine, rather than echelon form which is less suitable in this case. :: rather than echelon form which is less suitable in this case. :: sage: L. = NumberField(x^2 - x + 2) sage: OL = L.ring_of_integers() Echelon basis matrix: [    -1 -w + 1] With zero columns the right kernel has dimension 0. :: sage: M = matrix(QQ, [[],[],[]]) sage: M.right_kernel() Vector space of degree 0 and dimension 0 over Rational Field Basis matrix: [] With zero rows, the whole domain is the kernel, so the dimension is the number of columns. :: sage: M = matrix(QQ, [[],[],[]]).transpose() sage: M.right_kernel() Vector space of dimension 3 over Rational Field """ K = self.fetch('right_kernel') if not K is None: return K # First: obvious and easy kernels, for matrices over any ring if self._ncols == 0 or self._nrows == 0: return self._right_kernel_trivial() R = self._base_ring if self._ncols == 0:    # from a degree-0 space V = sage.modules.free_module.VectorSpace(R, self._ncols) Z = V.zero_subspace() self.cache('right_kernel', Z) return Z elif self._nrows == 0:  # to a degree-0 space Z = sage.modules.free_module.VectorSpace(R, self._ncols) self.cache('right_kernel', Z) return Z if is_IntegerRing(R): Z = self.right_kernel(*args, **kwds) self.cache('right_kernel', Z)
• ## sage/matrix/matrix_integer_dense.pyx

`diff -r 4498ef979fc4 -r 9d78f89061fc sage/matrix/matrix_integer_dense.pyx`
 a return A else: raise ValueError, "unknown algorithm '%s'"%algorithm def right_kernel(self, algorithm='default', LLL=False, proof=None, echelonize=True): r""" Return the right kernel of this matrix, as a module over the integers. This is the saturated ZZ-module spanned by all the column vectors v such that self\*v = 0. INPUT: Return the right kernel of a matrix over the integers. INPUT: -  ``algorithm`` - see the docs for ``self.kernel_matrix`` -  ``LLL`` - bool (default: False); if True the basis is an LLL reduced basis; otherwise, it is an echelon basis. -  ``proof`` - None (default: proof.linear_algebra()); if False, impacts how determinants are computed. By convention if self has 0 columns, the right kernel is of dimension 0, whereas the right kernel is the whole domain if self has 0 rows. EXAMPLES:: OUTPUT: A module over the integers is returned. This is the saturated ZZ-module spanned by all the column vectors ``v`` such that ``self*v = 0``. EXAMPLES:: sage: M = MatrixSpace(ZZ,2,4)(range(8)) sage: M.right_kernel() Free module of degree 4 and rank 2 over Integer Ring Echelon basis matrix: [ 1  0 -3  2] [ 0  1 -2  1] """ if self._ncols == 0:    # from a 0 space M = sage.modules.free_module.FreeModule(ZZ, self._ncols) return M.zero_submodule() elif self._nrows == 0:  # to a 0 space return sage.modules.free_module.FreeModule(ZZ, self._ncols) With zero columns the right kernel has dimension 0. :: sage: M = matrix(ZZ, [[],[],[]]) sage: M.right_kernel() Free module of degree 0 and rank 0 over Integer Ring Echelon basis matrix: [] With zero rows, the whole domain is the kernel, so the dimension is the number of columns. :: sage: M = matrix(ZZ, [[],[],[]]).transpose() sage: M.right_kernel() Ambient free module of rank 3 over the principal ideal domain Integer Ring """ if self._ncols == 0 or self._nrows == 0: return self._right_kernel_trivial() X = self._right_kernel_matrix(algorithm=algorithm, LLL=LLL, proof=proof) if not LLL and echelonize:
• ## sage/matrix/matrix_integer_sparse.pyx

`diff -r 4498ef979fc4 -r 9d78f89061fc sage/matrix/matrix_integer_sparse.pyx`
 a proof -- None (default: proof.linear_algebra()); if False, impacts how determinants are computed. By convention if self has 0 rows, the kernel is of dimension 0, whereas the kernel is the whole domain if self has 0 columns. EXAMPLES:: EXAMPLES: sage: M = MatrixSpace(ZZ,2,4,sparse=True)(range(8)) sage: M.right_kernel() Free module of degree 4 and rank 2 over Integer Ring Echelon basis matrix: [ 1  0 -3  2] [ 0  1 -2  1] With zero columns the right kernel has dimension 0. :: sage: M = matrix(ZZ, [[],[],[]],sparse=True) sage: M.right_kernel() Free module of degree 0 and rank 0 over Integer Ring Echelon basis matrix: [] With zero rows, the whole domain is the kernel, so the dimension is the number of columns. :: sage: M = matrix(ZZ, [[],[],[]],sparse=True).transpose() sage: M.right_kernel() Ambient free module of rank 3 over the principal ideal domain Integer Ring """ return self.dense_matrix().right_kernel(algorithm, LLL, proof, echelonize)
• ## sage/matrix/matrix_mod2_dense.pyx

`diff -r 4498ef979fc4 -r 9d78f89061fc sage/matrix/matrix_mod2_dense.pyx`
 a return r def right_kernel(self, algorithm='pluq'): """ r""" Return the right kernel of this matrix, as a vector space. This is the space of vectors x such that ``self*x=0``. A left kernel can be found with :meth:`left_kernel()` or just :meth:`kernel`. INPUT: INPUT: - ``algorithm`` - either "pluq" or "generic" By convention if self has 0 columns, the kernel is of dimension 0, whereas the kernel is whole domain if self has 0 rows. .. note:: Preference is given to left kernels in that the generic method name :meth:`kernel` returns a left kernel.  However most computations of kernels are implemented as right kernels. EXAMPLES: A trivial right kernel:: sage: A = MatrixSpace(GF(2), 2)([1,0,0,1]) sage: A.right_kernel() Vector space of degree 2 and dimension 0 over Finite Field of size 2 Basis matrix: [] Right kernel of a zero matrix:: sage: A = MatrixSpace(GF(2), 2)(0) sage: A.right_kernel() Vector space of degree 2 and dimension 2 over Finite Field of size 2 Basis matrix: [1 0] [0 1] Right kernel of a non-square matrix:: sage: A = MatrixSpace(GF(2),2,3)(range(6)) sage: A.right_kernel() Vector space of degree 3 and dimension 1 over Finite Field of size 2 Basis matrix: [1 0 1] A non-trivial kernel computation:: sage: A = random_matrix(GF(2),1000,1010) Vector space of degree 1010 and dimension 10 over Finite Field of size 2 Basis matrix: 10 x 1010 dense matrix over Finite Field of size 2 With zero columns the right kernel has dimension 0. :: sage: M = matrix(GF(2), [[],[],[]],sparse=True) sage: M.right_kernel() Vector space of degree 0 and dimension 0 over Finite Field of size 2 Basis matrix: [] With zero rows, the whole domain is the kernel, so the dimension is the number of columns. :: sage: M = matrix(GF(2), [[],[],[]],sparse=True).transpose() sage: M.right_kernel() Vector space of dimension 3 over Finite Field of size 2 """ if algorithm == 'generic': return matrix_dense.Matrix_dense.right_kernel(self) if algorithm != 'pluq': raise ValueError("Algorithm '%s' is unknown."%algorithm) cdef Matrix_mod2_dense M K = self.fetch('right_kernel') if not K is None: return K R = self._base_ring if self._ncols == 0:    # from a degree-0 space V = VectorSpace(R, self._ncols) Z = V.zero_subspace() self.cache('right_kernel', Z) return Z elif self._nrows == 0:  # to a degree-0 space Z = VectorSpace(R, self._ncols) self.cache('right_kernel', Z) return Z if self._ncols == 0 or self._nrows == 0: return self._right_kernel_trivial() cdef Matrix_mod2_dense M cdef mzd_t *A = mzd_copy(NULL, self._entries) cdef mzd_t *k = mzd_kernel_left_pluq(A, 0) # well, we don't # agree on the name else: basis = [] R = self._base_ring V = R**self._ncols W = V.submodule(basis, check=False, already_echelonized=True) self.cache('right_kernel', W)
• ## sage/matrix/matrix_rational_dense.pyx

`diff -r 4498ef979fc4 -r 9d78f89061fc sage/matrix/matrix_rational_dense.pyx`
 a mpq_clear(s) mpq_clear(pr) return _pr def right_kernel(self, algorithm='padic', **kwds): """ r""" Return the right kernel of this matrix, as a vector space over QQ. For a left kernel use self.left_kernel() or just self.kernel(). INPUT: -  ``algorithm`` - 'padic' (or 'default'): use IML's p-adic nullspace algorithm -  ``anything else`` - passed on to the generic echelon-form based algorithm. -  ``**kwds`` - passed onto to echelon form algorithm in the echelon case. Basis matrix: [   1    0   -2  3/2] [   0    1    1 -1/2] A trivial right kernel, plus left kernel (via superclass):: sage: M=Matrix(QQ,[[1/2,3],[0,1],[1,1]]) Vector space of degree 3 and dimension 1 over Rational Field Basis matrix: [   1 -5/2 -1/2] """ K = self.fetch('right_kernel') if not K is None: V = K.column_space() self.cache('right_kernel', V) return V elif self._nrows == 0 or self._ncols == 0: return self._right_kernel_trivial() else: return matrix_dense.Matrix_dense.right_kernel(self, algorithm, **kwds)