Skip to content

Commit 0b88463

Browse files
author
Release Manager
committed
gh-pr-34968: compute matrix kernels modulo composites Fixes #34862. URL: #34968 Reported by: Lorenz Panny Reviewer(s): Lorenz Panny, Travis Scrimshaw
2 parents a10cf50 + c028ed7 commit 0b88463

File tree

3 files changed

+74
-15
lines changed

3 files changed

+74
-15
lines changed

src/sage/geometry/polyhedron/ppl_lattice_polytope.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -855,8 +855,8 @@ def base_projection_matrix(self, fiber):
855855
sage: poly = LatticePolytope_PPL((-9,-6,-1,-1),(0,0,0,1),(0,0,1,0),(0,1,0,0),(1,0,0,0))
856856
sage: fiber = next(poly.fibration_generator(2))
857857
sage: poly.base_projection_matrix(fiber)
858-
[0 0 1 0]
859-
[0 0 0 1]
858+
[ 0 0 -1 0]
859+
[ 0 0 0 -1]
860860
861861
Note that the basis choice in :meth:`base_projection` for the
862862
quotient is usually different::
@@ -866,7 +866,7 @@ def base_projection_matrix(self, fiber):
866866
sage: [ proj(p) for p in poly.integral_points() ]
867867
[(-1, -1), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 1), (1, 0)]
868868
sage: [ proj_matrix*p for p in poly.integral_points() ]
869-
[(-1, -1), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 1), (1, 0)]
869+
[(1, 1), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, -1), (-1, 0)]
870870
"""
871871
return matrix(ZZ, fiber.vertices()).right_kernel_matrix()
872872

src/sage/matrix/matrix2.pyx

Lines changed: 62 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3670,7 +3670,7 @@ cdef class Matrix(Matrix1):
36703670

36713671
def _right_kernel_matrix_over_number_field(self):
36723672
r"""
3673-
Returns a pair that includes a matrix of basis vectors
3673+
Return a pair that includes a matrix of basis vectors
36743674
for the right kernel of ``self``.
36753675

36763676
OUTPUT:
@@ -3724,7 +3724,7 @@ cdef class Matrix(Matrix1):
37243724

37253725
def _right_kernel_matrix_over_field(self, *args, **kwds):
37263726
r"""
3727-
Returns a pair that includes a matrix of basis vectors
3727+
Return a pair that includes a matrix of basis vectors
37283728
for the right kernel of ``self``.
37293729

37303730
OUTPUT:
@@ -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+
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+
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.
@@ -4372,10 +4405,6 @@ cdef class Matrix(Matrix1):
43724405
Traceback (most recent call last):
43734406
...
43744407
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'.
43794408
sage: matrix(QQ, 2, 2).right_kernel_matrix(algorithm='pluq')
43804409
Traceback (most recent call last):
43814410
...
@@ -4430,13 +4459,15 @@ cdef class Matrix(Matrix1):
44304459
# Determine the basis format of independent spanning set to return
44314460
basis = kwds.pop('basis', None)
44324461
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']:
44354464
raise ValueError("matrix kernel basis format '%s' not recognized" % basis )
44364465
elif basis == 'pivot' and R not in _Fields:
44374466
raise ValueError('pivot basis only available over a field, not over %s' % R)
44384467
elif basis == 'LLL' and not is_IntegerRing(R):
44394468
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'
44404471

44414472
# Determine proof keyword for integer matrices
44424473
proof = kwds.pop('proof', None)
@@ -4484,6 +4515,9 @@ cdef class Matrix(Matrix1):
44844515
if M is None and R.is_integral_domain():
44854516
format, M = self._right_kernel_matrix_over_domain()
44864517

4518+
if M is None and isinstance(R, sage.rings.abc.IntegerModRing):
4519+
format, M = self._right_kernel_matrix_over_integer_mod_ring()
4520+
44874521
if M is None:
44884522
raise NotImplementedError("Cannot compute a matrix kernel over %s" % R)
44894523

@@ -4523,6 +4557,24 @@ cdef class Matrix(Matrix1):
45234557
else:
45244558
return M
45254559

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+
45264578
def right_kernel(self, *args, **kwds):
45274579
r"""
45284580
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)