From b1e08e62e8dafd8a0026c31f386de38f9fd8d4a4 Mon Sep 17 00:00:00 2001 From: maple3142 Date: Thu, 24 Oct 2024 01:26:12 +0800 Subject: [PATCH 1/7] Improve Matrix_mod2_dense solve_right performance Use M4RI builtin `mzd_solve_left` function to solve equation. --- src/sage/libs/m4ri.pxd | 3 ++ src/sage/matrix/matrix_mod2_dense.pyx | 69 +++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/src/sage/libs/m4ri.pxd b/src/sage/libs/m4ri.pxd index 7fbdd35b856..a762a68323b 100644 --- a/src/sage/libs/m4ri.pxd +++ b/src/sage/libs/m4ri.pxd @@ -179,6 +179,9 @@ cdef extern from "m4ri/m4ri.h": # reduced row echelon form using PLUQ factorization cdef mzd_t *mzd_kernel_left_pluq(mzd_t *A, int cutoff) + # system solving + cdef int mzd_solve_left(mzd_t *A, mzd_t *B, int cutoff, int inconsistency_check) + ######################## # Bit operations ######################## diff --git a/src/sage/matrix/matrix_mod2_dense.pyx b/src/sage/matrix/matrix_mod2_dense.pyx index 07c54d6add9..0dd000d6ba9 100644 --- a/src/sage/matrix/matrix_mod2_dense.pyx +++ b/src/sage/matrix/matrix_mod2_dense.pyx @@ -1918,6 +1918,75 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse mzd_free(A) self.cache('rank', r) return r + + def _solve_right_general(self, B, check=True): + """ + Solve the matrix equation AX = B for X using the M4RI library. + + INPUT: + + - ``B`` -- a matrix + - ``check`` -- boolean (default: ``True``); whether to check if the + matrix equation has a solution + + EXAMPLES:: + + sage: A = matrix(GF(2), [[1, 0], [0, 1], [1, 1]]) + sage: A.solve_right(vector([1, 1, 0])) + (1, 1) + sage: A.solve_right(vector([1, 1, 1])) + Traceback (most recent call last): + ... + ValueError: matrix equation has no solutions + + TESTS:: + + sage: n = 128 + sage: m = 128 + sage: A = random_matrix(GF(2), n, m) + sage: B = A * random_vector(GF(2), m) + sage: A * A.solve_right(B) == B + True + sage: m = 64 + sage: A = random_matrix(GF(2), n, m) + sage: B = A * random_vector(GF(2), m) + sage: A * A.solve_right(B) == B + True + sage: m = 256 + sage: A = random_matrix(GF(2), n, m) + sage: B = A * random_vector(GF(2), m) + sage: A * A.solve_right(B) == B + True + """ + cdef Matrix_mod2_dense X # the solution + + cdef mzd_t *B_entries = (B)._entries + cdef rci_t rows = self._entries.nrows + if self._entries.nrows < self._entries.ncols: + rows = self._entries.ncols # mzd_solve_left requires ncols <= nrows + + cdef mzd_t *lhs = mzd_init(rows, self._entries.ncols) + mzd_copy(lhs, self._entries) + cdef mzd_t *rhs = mzd_init(rows, B_entries.ncols) + mzd_copy(rhs, B_entries) + + sig_on() + # although it is called mzd_solve_left, it does the same thing as solve_right + ret = mzd_solve_left(lhs, rhs, 0, check) + sig_off() + mzd_free(lhs) + + if ret == 0: + # solution is placed in rhs + X = self.new_matrix(nrows=self._entries.ncols, ncols=B_entries.ncols) + rhs.nrows = self._entries.ncols + mzd_copy(X._entries, rhs) + mzd_free(rhs) + return X + else: + mzd_free(rhs) + raise ValueError("matrix equation has no solutions") + def _right_kernel_matrix(self, **kwds): r""" From d1d21f8244f90e85373fb0866b9481ea220ec013 Mon Sep 17 00:00:00 2001 From: maple3142 Date: Thu, 24 Oct 2024 01:47:54 +0800 Subject: [PATCH 2/7] Fix code style --- src/sage/matrix/matrix_mod2_dense.pyx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/matrix/matrix_mod2_dense.pyx b/src/sage/matrix/matrix_mod2_dense.pyx index 0dd000d6ba9..deeb45cf50b 100644 --- a/src/sage/matrix/matrix_mod2_dense.pyx +++ b/src/sage/matrix/matrix_mod2_dense.pyx @@ -1918,7 +1918,7 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse mzd_free(A) self.cache('rank', r) return r - + def _solve_right_general(self, B, check=True): """ Solve the matrix equation AX = B for X using the M4RI library. @@ -1938,7 +1938,7 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse Traceback (most recent call last): ... ValueError: matrix equation has no solutions - + TESTS:: sage: n = 128 @@ -1987,7 +1987,6 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse mzd_free(rhs) raise ValueError("matrix equation has no solutions") - def _right_kernel_matrix(self, **kwds): r""" Return a pair that includes a matrix of basis vectors From 5a68faea4ad47449a1eea896253d97d671cdcd86 Mon Sep 17 00:00:00 2001 From: maple3142 Date: Thu, 24 Oct 2024 13:26:11 +0800 Subject: [PATCH 3/7] Fix Matrix_mod2_dense solve_right signal handling --- src/sage/matrix/matrix_mod2_dense.pyx | 30 ++++++++++++++------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/sage/matrix/matrix_mod2_dense.pyx b/src/sage/matrix/matrix_mod2_dense.pyx index deeb45cf50b..4cce0464dd4 100644 --- a/src/sage/matrix/matrix_mod2_dense.pyx +++ b/src/sage/matrix/matrix_mod2_dense.pyx @@ -1970,22 +1970,24 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse cdef mzd_t *rhs = mzd_init(rows, B_entries.ncols) mzd_copy(rhs, B_entries) - sig_on() - # although it is called mzd_solve_left, it does the same thing as solve_right - ret = mzd_solve_left(lhs, rhs, 0, check) - sig_off() - mzd_free(lhs) + cdef int ret + try: + sig_on() + # although it is called mzd_solve_left, it does the same thing as solve_right + ret = mzd_solve_left(lhs, rhs, 0, check) + sig_off() - if ret == 0: - # solution is placed in rhs - X = self.new_matrix(nrows=self._entries.ncols, ncols=B_entries.ncols) - rhs.nrows = self._entries.ncols - mzd_copy(X._entries, rhs) - mzd_free(rhs) - return X - else: + if ret == 0: + # solution is placed in rhs + X = self.new_matrix(nrows=self._entries.ncols, ncols=B_entries.ncols) + rhs.nrows = self._entries.ncols + mzd_copy(X._entries, rhs) + return X + else: + raise ValueError("matrix equation has no solutions") + finally: + mzd_free(lhs) mzd_free(rhs) - raise ValueError("matrix equation has no solutions") def _right_kernel_matrix(self, **kwds): r""" From 8f598195dfa260401193a8068a2450fd09f283c6 Mon Sep 17 00:00:00 2001 From: maple3142 Date: Thu, 24 Oct 2024 15:34:01 +0800 Subject: [PATCH 4/7] Fix Matrix_mod2_dense solve_right empty matrix --- src/sage/matrix/matrix_mod2_dense.pyx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/sage/matrix/matrix_mod2_dense.pyx b/src/sage/matrix/matrix_mod2_dense.pyx index 4cce0464dd4..d2f7e49c58a 100644 --- a/src/sage/matrix/matrix_mod2_dense.pyx +++ b/src/sage/matrix/matrix_mod2_dense.pyx @@ -1958,9 +1958,13 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse sage: A * A.solve_right(B) == B True """ - cdef Matrix_mod2_dense X # the solution - cdef mzd_t *B_entries = (B)._entries + + cdef Matrix_mod2_dense X # the solution + X = self.new_matrix(nrows=self._entries.ncols, ncols=B_entries.ncols) + if self._entries.nrows == 0 or self._entries.ncols == 0: + # special case: empty matrix + return X cdef rci_t rows = self._entries.nrows if self._entries.nrows < self._entries.ncols: rows = self._entries.ncols # mzd_solve_left requires ncols <= nrows @@ -1979,7 +1983,6 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse if ret == 0: # solution is placed in rhs - X = self.new_matrix(nrows=self._entries.ncols, ncols=B_entries.ncols) rhs.nrows = self._entries.ncols mzd_copy(X._entries, rhs) return X From a2486ee4b04286de737b759ec47382cc8a560840 Mon Sep 17 00:00:00 2001 From: maple3142 Date: Thu, 24 Oct 2024 15:41:12 +0800 Subject: [PATCH 5/7] Fix Matrix_mod2_dense solve_right empty matrix --- src/sage/matrix/matrix_mod2_dense.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/matrix/matrix_mod2_dense.pyx b/src/sage/matrix/matrix_mod2_dense.pyx index d2f7e49c58a..a92abdd94db 100644 --- a/src/sage/matrix/matrix_mod2_dense.pyx +++ b/src/sage/matrix/matrix_mod2_dense.pyx @@ -1962,7 +1962,7 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse cdef Matrix_mod2_dense X # the solution X = self.new_matrix(nrows=self._entries.ncols, ncols=B_entries.ncols) - if self._entries.nrows == 0 or self._entries.ncols == 0: + if self._entries.ncols == 0 or B_entries.ncols == 0: # special case: empty matrix return X cdef rci_t rows = self._entries.nrows From 88ca4ca9f9bd00c81c373ea5b360da8338c266cb Mon Sep 17 00:00:00 2001 From: maple3142 Date: Thu, 24 Oct 2024 17:31:22 +0800 Subject: [PATCH 6/7] Fix Matrix_mod2_dense solve_right empty matrix --- src/sage/matrix/matrix_mod2_dense.pyx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/matrix/matrix_mod2_dense.pyx b/src/sage/matrix/matrix_mod2_dense.pyx index a92abdd94db..0bdbd4d11d2 100644 --- a/src/sage/matrix/matrix_mod2_dense.pyx +++ b/src/sage/matrix/matrix_mod2_dense.pyx @@ -1964,6 +1964,8 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse X = self.new_matrix(nrows=self._entries.ncols, ncols=B_entries.ncols) if self._entries.ncols == 0 or B_entries.ncols == 0: # special case: empty matrix + if B != 0: + raise ValueError("matrix equation has no solutions") return X cdef rci_t rows = self._entries.nrows if self._entries.nrows < self._entries.ncols: From 9c532c5fcdbc2820d191887fa77bdb4477fcba2d Mon Sep 17 00:00:00 2001 From: maple3142 Date: Tue, 5 Nov 2024 01:45:32 +0800 Subject: [PATCH 7/7] Add special case to Matrix_mod2_dense solve_right --- src/sage/matrix/matrix_mod2_dense.pyx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/sage/matrix/matrix_mod2_dense.pyx b/src/sage/matrix/matrix_mod2_dense.pyx index 0bdbd4d11d2..055359e6fd0 100644 --- a/src/sage/matrix/matrix_mod2_dense.pyx +++ b/src/sage/matrix/matrix_mod2_dense.pyx @@ -1957,6 +1957,16 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse sage: B = A * random_vector(GF(2), m) sage: A * A.solve_right(B) == B True + sage: matrix(GF(2), 2, 0).solve_right(matrix(GF(2), 2, 2)) == matrix(GF(2), 0, 2) + True + sage: matrix(GF(2), 2, 0).solve_right(matrix(GF(2), 2, 2) + 1) + Traceback (most recent call last): + ... + ValueError: matrix equation has no solutions + sage: matrix(GF(2), 2, 0).solve_right(matrix(GF(2), 2, 2) + 1, check=False) == matrix(GF(2), 0, 2) + True + sage: matrix(GF(2), 2, 2).solve_right(matrix(GF(2), 2, 0)) == matrix(GF(2), 2, 0) + True """ cdef mzd_t *B_entries = (B)._entries @@ -1964,7 +1974,7 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse X = self.new_matrix(nrows=self._entries.ncols, ncols=B_entries.ncols) if self._entries.ncols == 0 or B_entries.ncols == 0: # special case: empty matrix - if B != 0: + if check and B != 0: raise ValueError("matrix equation has no solutions") return X cdef rci_t rows = self._entries.nrows