Skip to content

Commit 9925b45

Browse files
committed
compute matrix kernels modulo composites
1 parent c000c95 commit 9925b45

File tree

2 files changed

+69
-6
lines changed

2 files changed

+69
-6
lines changed

src/sage/matrix/matrix2.pyx

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3868,6 +3868,38 @@ cdef class Matrix(Matrix1):
38683868
% (self.nrows(), self.ncols()), level=1, t=tm)
38693869
return 'computed-smith-form', self.new_matrix(nrows=len(basis), ncols=self._ncols, entries=basis)
38703870

3871+
def _right_kernel_matrix_over_integer_mod_ring(self):
3872+
r"""
3873+
Returns a pair that includes a matrix of basis vectors
3874+
for the right kernel of ``self``.
3875+
3876+
OUTPUT:
3877+
3878+
Returns a pair. First item is the string 'computed-pari-matkermod'
3879+
that identifies the nature of the basis vectors.
3880+
3881+
Second item is a matrix whose rows are a basis for the right kernel,
3882+
over the integer modular ring, as computed by :pari:`matkermod`.
3883+
3884+
EXAMPLES::
3885+
3886+
sage: A = matrix(Zmod(24480), [[1,2,3,4,5],[7,7,7,7,7]])
3887+
sage: result = A._right_kernel_matrix_over_integer_mod_ring()
3888+
sage: result[0]
3889+
'computed-pari-matkermod'
3890+
sage: P = result[1]; P
3891+
[ 1 24478 1 0 0]
3892+
[ 2 24477 0 1 0]
3893+
[ 3 24476 0 0 1]
3894+
sage: A*P.transpose() == 0
3895+
True
3896+
"""
3897+
R = self.base_ring()
3898+
pariM = self.change_ring(ZZ).__pari__()
3899+
basis = list(pariM.matkermod(R.characteristic()))
3900+
from sage.matrix.matrix_space import MatrixSpace
3901+
return 'computed-pari-matkermod', MatrixSpace(R, len(basis), ncols=self._ncols)(basis)
3902+
38713903
def right_kernel_matrix(self, *args, **kwds):
38723904
r"""
38733905
Returns a matrix whose rows form a basis
@@ -3888,10 +3920,11 @@ cdef class Matrix(Matrix1):
38883920
over the rationals and integers
38893921
- 'pluq' - PLUQ matrix factorization for matrices mod 2
38903922

3891-
- ``basis`` - default: 'echelon' - a keyword that describes
3923+
- ``basis`` - default: 'default' - a keyword that describes
38923924
the format of the basis returned. Allowable values are:
38933925

3894-
- 'echelon': the basis matrix is returned in echelon form
3926+
- 'default': uses 'echelon' over fields; 'computed' otherwise.
3927+
- 'echelon': the basis matrix is returned in echelon form.
38953928
- 'pivot' : each basis vector is computed from the reduced
38963929
row-echelon form of ``self`` by placing a single one in a
38973930
non-pivot column and zeros in the remaining non-pivot columns.
@@ -4430,13 +4463,15 @@ cdef class Matrix(Matrix1):
44304463
# Determine the basis format of independent spanning set to return
44314464
basis = kwds.pop('basis', None)
44324465
if basis is None:
4433-
basis = 'echelon'
4434-
elif basis not in ['computed', 'echelon', 'pivot', 'LLL']:
4466+
basis = 'default'
4467+
elif basis not in ['default', 'computed', 'echelon', 'pivot', 'LLL']:
44354468
raise ValueError("matrix kernel basis format '%s' not recognized" % basis )
44364469
elif basis == 'pivot' and R not in _Fields:
44374470
raise ValueError('pivot basis only available over a field, not over %s' % R)
44384471
elif basis == 'LLL' and not is_IntegerRing(R):
44394472
raise ValueError('LLL-reduced basis only available over the integers, not over %s' % R)
4473+
if basis == 'default':
4474+
basis = 'echelon' if R in _Fields else 'computed'
44404475

44414476
# Determine proof keyword for integer matrices
44424477
proof = kwds.pop('proof', None)
@@ -4484,6 +4519,9 @@ cdef class Matrix(Matrix1):
44844519
if M is None and R.is_integral_domain():
44854520
format, M = self._right_kernel_matrix_over_domain()
44864521

4522+
if M is None and isinstance(R, sage.rings.abc.IntegerModRing):
4523+
format, M = self._right_kernel_matrix_over_integer_mod_ring()
4524+
44874525
if M is None:
44884526
raise NotImplementedError("Cannot compute a matrix kernel over %s" % R)
44894527

@@ -4523,6 +4561,24 @@ cdef class Matrix(Matrix1):
45234561
else:
45244562
return M
45254563

4564+
def left_kernel_matrix(self, *args, **kwds):
4565+
r"""
4566+
Returns a matrix whose rows form a basis for the left kernel
4567+
of ``self``.
4568+
4569+
This method is a thin wrapper around :meth:`right_kernel_matrix`.
4570+
For supported parameters and input/output formats, see there.
4571+
4572+
EXAMPLES::
4573+
4574+
sage: M = matrix([[1,2],[3,4],[5,6]])
4575+
sage: K = M.left_kernel_matrix(); K
4576+
[ 1 -2 1]
4577+
sage: K * M
4578+
[0 0]
4579+
"""
4580+
return self.transpose().right_kernel_matrix(*args, **kwds)
4581+
45264582
def right_kernel(self, *args, **kwds):
45274583
r"""
45284584
Returns the right kernel of this matrix, as a vector space or

src/sage/matrix/matrix_modn_dense_template.pxi

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1840,7 +1840,11 @@ cdef class Matrix_modn_dense_template(Matrix_dense):
18401840
def right_kernel_matrix(self, algorithm='linbox', basis='echelon'):
18411841
r"""
18421842
Returns a matrix whose rows form a basis for the right kernel
1843-
of ``self``, where ``self`` is a matrix over a (small) finite field.
1843+
of ``self``.
1844+
1845+
If the base ring is the ring of integers modulo a composite,
1846+
the keyword arguments are ignored and the computation is
1847+
delegated to :meth:`Matrix_dense.right_kernel_matrix`.
18441848
18451849
INPUT:
18461850
@@ -1894,7 +1898,10 @@ cdef class Matrix_modn_dense_template(Matrix_dense):
18941898
[0 0 0 0]
18951899
"""
18961900
if self.fetch('in_echelon_form') is None:
1897-
self = self.echelon_form(algorithm=algorithm)
1901+
try:
1902+
self = self.echelon_form(algorithm=algorithm)
1903+
except NotImplementedError: # composite modulus
1904+
return Matrix_dense.right_kernel_matrix(self)
18981905

18991906
cdef Py_ssize_t r = self.rank()
19001907
cdef Py_ssize_t nrows = self._nrows

0 commit comments

Comments
 (0)