@@ -3670,7 +3670,7 @@ cdef class Matrix(Matrix1):
3670
3670
3671
3671
def _right_kernel_matrix_over_number_field(self):
3672
3672
r"""
3673
- Returns a pair that includes a matrix of basis vectors
3673
+ Return a pair that includes a matrix of basis vectors
3674
3674
for the right kernel of ``self``.
3675
3675
3676
3676
OUTPUT:
@@ -3724,7 +3724,7 @@ cdef class Matrix(Matrix1):
3724
3724
3725
3725
def _right_kernel_matrix_over_field(self, *args, **kwds):
3726
3726
r"""
3727
- Returns a pair that includes a matrix of basis vectors
3727
+ Return a pair that includes a matrix of basis vectors
3728
3728
for the right kernel of ``self``.
3729
3729
3730
3730
OUTPUT:
@@ -3868,6 +3868,38 @@ cdef class Matrix(Matrix1):
3868
3868
% (self.nrows(), self.ncols()), level=1, t=tm)
3869
3869
return 'computed-smith-form', self.new_matrix(nrows=len(basis), ncols=self._ncols, entries=basis)
3870
3870
3871
+ def _right_kernel_matrix_over_integer_mod_ring(self):
3872
+ r"""
3873
+ Return 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
+
3871
3903
def right_kernel_matrix(self, *args, **kwds):
3872
3904
r"""
3873
3905
Returns a matrix whose rows form a basis
@@ -3888,10 +3920,11 @@ cdef class Matrix(Matrix1):
3888
3920
over the rationals and integers
3889
3921
- 'pluq' - PLUQ matrix factorization for matrices mod 2
3890
3922
3891
- - ``basis`` - default: 'echelon ' - a keyword that describes
3923
+ - ``basis`` - default: 'default ' - a keyword that describes
3892
3924
the format of the basis returned. Allowable values are:
3893
3925
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.
3895
3928
- 'pivot' : each basis vector is computed from the reduced
3896
3929
row-echelon form of ``self`` by placing a single one in a
3897
3930
non-pivot column and zeros in the remaining non-pivot columns.
@@ -4372,10 +4405,6 @@ cdef class Matrix(Matrix1):
4372
4405
Traceback (most recent call last):
4373
4406
...
4374
4407
ValueError: 'pari' matrix kernel algorithm only available over non-trivial number fields and the integers, not over Rational Field
4375
- sage: matrix(Integers(6), 2, 2).right_kernel_matrix(algorithm='generic')
4376
- Traceback (most recent call last):
4377
- ...
4378
- NotImplementedError: Echelon form not implemented over 'Ring of integers modulo 6'.
4379
4408
sage: matrix(QQ, 2, 2).right_kernel_matrix(algorithm='pluq')
4380
4409
Traceback (most recent call last):
4381
4410
...
@@ -4430,13 +4459,15 @@ cdef class Matrix(Matrix1):
4430
4459
# Determine the basis format of independent spanning set to return
4431
4460
basis = kwds.pop('basis', None)
4432
4461
if basis is None:
4433
- basis = 'echelon '
4434
- elif basis not in ['computed', 'echelon', 'pivot', 'LLL']:
4462
+ basis = 'default '
4463
+ elif basis not in ['default', ' computed', 'echelon', 'pivot', 'LLL']:
4435
4464
raise ValueError("matrix kernel basis format '%s' not recognized" % basis )
4436
4465
elif basis == 'pivot' and R not in _Fields:
4437
4466
raise ValueError('pivot basis only available over a field, not over %s' % R)
4438
4467
elif basis == 'LLL' and not is_IntegerRing(R):
4439
4468
raise ValueError('LLL-reduced basis only available over the integers, not over %s' % R)
4469
+ if basis == 'default':
4470
+ basis = 'echelon' if R in _Fields else 'computed'
4440
4471
4441
4472
# Determine proof keyword for integer matrices
4442
4473
proof = kwds.pop('proof', None)
@@ -4484,6 +4515,9 @@ cdef class Matrix(Matrix1):
4484
4515
if M is None and R.is_integral_domain():
4485
4516
format, M = self._right_kernel_matrix_over_domain()
4486
4517
4518
+ if M is None and isinstance(R, sage.rings.abc.IntegerModRing):
4519
+ format, M = self._right_kernel_matrix_over_integer_mod_ring()
4520
+
4487
4521
if M is None:
4488
4522
raise NotImplementedError("Cannot compute a matrix kernel over %s" % R)
4489
4523
@@ -4523,6 +4557,24 @@ cdef class Matrix(Matrix1):
4523
4557
else:
4524
4558
return M
4525
4559
4560
+ def left_kernel_matrix(self, *args, **kwds):
4561
+ r"""
4562
+ Returns a matrix whose rows form a basis for the left kernel
4563
+ of ``self``.
4564
+
4565
+ This method is a thin wrapper around :meth:`right_kernel_matrix`.
4566
+ For supported parameters and input/output formats, see there.
4567
+
4568
+ EXAMPLES::
4569
+
4570
+ sage: M = matrix([[1,2],[3,4],[5,6]])
4571
+ sage: K = M.left_kernel_matrix(); K
4572
+ [ 1 -2 1]
4573
+ sage: K * M
4574
+ [0 0]
4575
+ """
4576
+ return self.transpose().right_kernel_matrix(*args, **kwds)
4577
+
4526
4578
def right_kernel(self, *args, **kwds):
4527
4579
r"""
4528
4580
Returns the right kernel of this matrix, as a vector space or
0 commit comments