Skip to content

Commit 3dd31ba

Browse files
author
Release Manager
committed
gh-36059: Speed up the creation of submatrices of `Matrix_modn_dense_template` matrices <!-- ^^^^^ Please provide a concise, informative and self-explanatory title. Don't put issue numbers in there, do this in the PR body below. For example, instead of "Fixes #1234" use "Introduce new method to calculate 1+1" --> <!-- Describe your changes here in detail --> Modified the method `submatrix()` to do a systematic call to `memcpy()`. Overrode methods `matrix_from_rows()`, `matrix_from_columns()` and `matrix_from_rows_and_columns()`. <!-- Why is this change required? What problem does it solve? --> In the current version of `submatrix()`, only the case when the submatrix has as many columns as the matrix, relies on the contiguous memory storage of `Matrix_modn_dense_template` and call `memcpy()`. In fact, this can be applied to any set of arguments given to the method and enables better performances. Overriding the other methods mentioned above allows to avoid calling `get_unsafe()` and `set_unsafe()` that are doing time-consuming casts/conversions that are unnecessary in this case. <!-- If this PR resolves an open issue, please link to it here. For example "Fixes #12345". --> <!-- If your change requires a documentation PR, please link it appropriately. --> ### 📝 Checklist <!-- Put an `x` in all the boxes that apply. --> <!-- If your change requires a documentation PR, please link it appropriately --> <!-- If you're unsure about any of these, don't hesitate to ask. We're here to help! --> <!-- Feel free to remove irrelevant items. --> - [x] The title is concise, informative, and self-explanatory. - [x] The description explains in detail what this PR is about. - [ ] I have linked a relevant issue or discussion. - [x] I have created tests covering the changes. - [ ] I have updated the documentation accordingly. ### ⌛ Dependencies <!-- List all open PRs that this PR logically depends on - #12345: short description why this is a dependency - #34567: ... --> <!-- If you're unsure about any of these, don't hesitate to ask. We're here to help! --> URL: #36059 Reported by: Marie Bonboire Reviewer(s): Vincent Neiger
2 parents 18ad7f8 + a52a33f commit 3dd31ba

File tree

3 files changed

+165
-40
lines changed

3 files changed

+165
-40
lines changed

src/sage/matrix/matrix1.pyx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2176,9 +2176,7 @@ cdef class Matrix(Matrix0):
21762176
[5 4]
21772177
21782178
For example here we take from row 1 columns 2 then 0 twice, and do
2179-
this 3 times.
2180-
2181-
::
2179+
this 3 times::
21822180
21832181
sage: A.matrix_from_rows_and_columns([1,1,1],[2,0,0])
21842182
[5 3 3]

src/sage/matrix/matrix_integer_dense.pyx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1614,7 +1614,7 @@ cdef class Matrix_integer_dense(Matrix_dense):
16141614
return self._mod_two()
16151615
elif p < MAX_MODULUS_FLOAT:
16161616
res_f = Matrix_modn_dense_float.__new__(Matrix_modn_dense_float,
1617-
matrix_space.MatrixSpace(IntegerModRing(p), self._nrows, self._ncols, sparse=False), None, None, None)
1617+
matrix_space.MatrixSpace(IntegerModRing(p), self._nrows, self._ncols, sparse=False), None, None, None, zeroed_alloc=False)
16181618
for i from 0 <= i < self._nrows:
16191619
res_row_f = res_f._matrix[i]
16201620
for j from 0 <= j < self._ncols:
@@ -1623,7 +1623,7 @@ cdef class Matrix_integer_dense(Matrix_dense):
16231623

16241624
elif p < MAX_MODULUS_DOUBLE:
16251625
res_d = Matrix_modn_dense_double.__new__(Matrix_modn_dense_double,
1626-
matrix_space.MatrixSpace(IntegerModRing(p), self._nrows, self._ncols, sparse=False), None, None, None)
1626+
matrix_space.MatrixSpace(IntegerModRing(p), self._nrows, self._ncols, sparse=False), None, None, None, zeroed_alloc=False)
16271627
for i from 0 <= i < self._nrows:
16281628
res_row_d = res_d._matrix[i]
16291629
for j from 0 <= j < self._ncols:
@@ -1649,11 +1649,11 @@ cdef class Matrix_integer_dense(Matrix_dense):
16491649
if p < MAX_MODULUS_FLOAT:
16501650
res.append( Matrix_modn_dense_float.__new__(Matrix_modn_dense_float,
16511651
matrix_space.MatrixSpace(IntegerModRing(p), self._nrows, self._ncols, sparse=False),
1652-
None, None, None) )
1652+
None, None, None, zeroed_alloc=False) )
16531653
elif p < MAX_MODULUS_DOUBLE:
16541654
res.append( Matrix_modn_dense_double.__new__(Matrix_modn_dense_double,
16551655
matrix_space.MatrixSpace(IntegerModRing(p), self._nrows, self._ncols, sparse=False),
1656-
None, None, None) )
1656+
None, None, None, zeroed_alloc=False) )
16571657
else:
16581658
raise ValueError("p=%d too big."%p)
16591659

src/sage/matrix/matrix_modn_dense_template.pxi

Lines changed: 160 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ We test corner cases for multiplication::
9090
from libc.stdint cimport uint64_t
9191
from cpython.bytes cimport *
9292

93-
from cysignals.memory cimport check_malloc, check_allocarray, sig_malloc, sig_free
93+
from cysignals.memory cimport check_malloc, check_allocarray, check_calloc, sig_malloc, sig_free
9494
from cysignals.signals cimport sig_check, sig_on, sig_off
9595

9696
from sage.libs.gmp.mpz cimport *
@@ -123,7 +123,7 @@ from sage.structure.proof.proof import get_flag as get_proof_flag
123123
from sage.structure.richcmp cimport rich_to_bool
124124
from sage.misc.randstate cimport randstate, current_randstate
125125
import sage.matrix.matrix_space as matrix_space
126-
from .args cimport MatrixArgs_init
126+
from .args cimport SparseEntry, MatrixArgs_init
127127

128128

129129
from sage.cpython.string cimport char_to_str
@@ -441,15 +441,18 @@ cpdef __matrix_from_rows_of_matrices(X):
441441

442442

443443
cdef class Matrix_modn_dense_template(Matrix_dense):
444-
def __cinit__(self):
444+
def __cinit__(self, *args, bint zeroed_alloc=True, **kwds):
445445
cdef long p = self._base_ring.characteristic()
446446
self.p = p
447447
if p >= MAX_MODULUS:
448448
raise OverflowError("p (=%s) must be < %s."%(p, MAX_MODULUS))
449449

450-
self._entries = <celement *>check_allocarray(self._nrows * self._ncols, sizeof(celement))
450+
if zeroed_alloc:
451+
self._entries = <celement *>check_calloc(self._nrows * self._ncols, sizeof(celement))
452+
else:
453+
self._entries = <celement *>check_allocarray(self._nrows * self._ncols, sizeof(celement))
454+
451455
self._matrix = <celement **>check_allocarray(self._nrows, sizeof(celement*))
452-
453456
cdef unsigned int k
454457
cdef Py_ssize_t i
455458
k = 0
@@ -518,27 +521,28 @@ cdef class Matrix_modn_dense_template(Matrix_dense):
518521
"""
519522
ma = MatrixArgs_init(parent, entries)
520523
cdef long i, j
521-
it = ma.iter(False)
524+
it = ma.iter(convert=False, sparse=True)
522525
R = ma.base
523526
p = R.characteristic()
524-
for i in range(ma.nrows):
525-
v = self._matrix[i]
526-
for j in range(ma.ncols):
527-
x = next(it)
528-
if type(x) is int:
529-
tmp = (<long>x) % p
530-
v[j] = tmp + (tmp<0)*p
531-
elif type(x) is IntegerMod_int and (<IntegerMod_int>x)._parent is R:
532-
v[j] = <celement>(<IntegerMod_int>x).ivalue
533-
elif type(x) is Integer:
534-
if coerce:
535-
v[j] = mpz_fdiv_ui((<Integer>x).value, p)
536-
else:
537-
v[j] = mpz_get_ui((<Integer>x).value)
538-
elif coerce:
539-
v[j] = R(x)
527+
528+
for t in it:
529+
se = <SparseEntry>t
530+
x = se.entry
531+
v = self._matrix[se.i]
532+
if type(x) is int:
533+
tmp = (<long>x) % p
534+
v[se.j] = tmp + (tmp<0)*p
535+
elif type(x) is IntegerMod_int and (<IntegerMod_int>x)._parent is R:
536+
v[se.j] = <celement>(<IntegerMod_int>x).ivalue
537+
elif type(x) is Integer:
538+
if coerce:
539+
v[se.j] = mpz_fdiv_ui((<Integer>x).value, p)
540540
else:
541-
v[j] = <celement>x
541+
v[se.j] = mpz_get_ui((<Integer>x).value)
542+
elif coerce:
543+
v[se.j] = R(x)
544+
else:
545+
v[se.j] = <celement>x
542546

543547
cdef long _hash_(self) except -1:
544548
"""
@@ -786,7 +790,7 @@ cdef class Matrix_modn_dense_template(Matrix_dense):
786790
cdef Matrix_modn_dense_template M
787791
cdef celement p = self.p
788792

789-
M = self.__class__.__new__(self.__class__, self._parent,None,None,None)
793+
M = self.__class__.__new__(self.__class__, self._parent,None,None,None, zeroed_alloc=False)
790794

791795
sig_on()
792796
for i in range(self._nrows*self._ncols):
@@ -825,7 +829,7 @@ cdef class Matrix_modn_dense_template(Matrix_dense):
825829
cdef celement p = self.p
826830
cdef celement a = left
827831

828-
M = self.__class__.__new__(self.__class__, self._parent,None,None,None)
832+
M = self.__class__.__new__(self.__class__, self._parent,None,None,None,zeroed_alloc=False)
829833

830834
sig_on()
831835
for i in range(self._nrows*self._ncols):
@@ -844,7 +848,7 @@ cdef class Matrix_modn_dense_template(Matrix_dense):
844848
False
845849
"""
846850
cdef Matrix_modn_dense_template A
847-
A = self.__class__.__new__(self.__class__, self._parent, 0, 0, 0)
851+
A = self.__class__.__new__(self.__class__,self._parent,None,None,None,zeroed_alloc=False)
848852
memcpy(A._entries, self._entries, sizeof(celement)*self._nrows*self._ncols)
849853
if self._subdivisions is not None:
850854
A.subdivide(*self.subdivisions())
@@ -883,7 +887,7 @@ cdef class Matrix_modn_dense_template(Matrix_dense):
883887
cdef celement k, p
884888
cdef Matrix_modn_dense_template M
885889

886-
M = self.__class__.__new__(self.__class__, self._parent,None,None,None)
890+
M = self.__class__.__new__(self.__class__, self._parent,None,None,None,zeroed_alloc=False)
887891
p = self.p
888892
cdef celement* other_ent = (<Matrix_modn_dense_template>right)._entries
889893

@@ -920,7 +924,7 @@ cdef class Matrix_modn_dense_template(Matrix_dense):
920924
cdef celement k, p
921925
cdef Matrix_modn_dense_template M
922926

923-
M = self.__class__.__new__(self.__class__, self._parent, None, None, None)
927+
M = self.__class__.__new__(self.__class__, self._parent, None, None, None, zeroed_alloc=False)
924928
p = self.p
925929
cdef celement* other_ent = (<Matrix_modn_dense_template>right)._entries
926930

@@ -3012,14 +3016,21 @@ cdef class Matrix_modn_dense_template(Matrix_dense):
30123016
if nrows == -1:
30133017
nrows = self._nrows - row
30143018

3015-
if col != 0 or ncols != self._ncols:
3016-
return self.matrix_from_rows_and_columns(range(row, row+nrows), range(col, col+ncols))
3017-
30183019
if nrows < 0 or row < 0 or row + nrows > self._nrows:
30193020
raise IndexError("rows out of range")
3021+
if ncols < 0 or col < 0 or col + ncols > self._ncols:
3022+
raise IndexError("columns out of range")
3023+
3024+
cdef Matrix_modn_dense_template M = self.new_matrix(nrows=nrows, ncols=ncols)
3025+
3026+
if col == 0 and ncols == self._ncols:
3027+
memcpy(M._entries, self._matrix[row], sizeof(celement)*ncols*nrows)
3028+
return M
3029+
3030+
cdef Py_ssize_t i,r
3031+
for i,r in enumerate(range(row, row+nrows)) :
3032+
memcpy(M._matrix[i], self._matrix[r]+col, sizeof(celement)*ncols)
30203033

3021-
cdef Matrix_modn_dense_template M = self.new_matrix(nrows=nrows, ncols=self._ncols)
3022-
memcpy(M._entries, self._entries+row*ncols, sizeof(celement)*ncols*nrows)
30233034
return M
30243035

30253036
def _matrices_from_rows(self, Py_ssize_t nrows, Py_ssize_t ncols):
@@ -3065,6 +3076,122 @@ cdef class Matrix_modn_dense_template(Matrix_dense):
30653076
ans.append(M)
30663077
return ans
30673078

3079+
def matrix_from_columns(self, columns):
3080+
"""
3081+
Return the matrix constructed from self using columns with indices
3082+
in the columns list.
3083+
3084+
EXAMPLES::
3085+
3086+
sage: M = MatrixSpace(Integers(8),3,3)
3087+
sage: A = M(range(9)); A
3088+
[0 1 2]
3089+
[3 4 5]
3090+
[6 7 0]
3091+
sage: A.matrix_from_columns([2,1])
3092+
[2 1]
3093+
[5 4]
3094+
[0 7]
3095+
"""
3096+
cdef Py_ssize_t ncols = len(columns)
3097+
3098+
# Construct new matrix
3099+
cdef Matrix_modn_dense_template A = self.new_matrix(ncols=ncols)
3100+
cdef Py_ssize_t i, j, col
3101+
for j, col in enumerate(columns):
3102+
if col < 0 or col >= self._ncols:
3103+
raise IndexError("column index out of range")
3104+
for i in range(self._nrows):
3105+
A._matrix[i][j] = self._matrix[i][col]
3106+
3107+
return A
3108+
3109+
def matrix_from_rows(self, rows):
3110+
"""
3111+
Return the matrix constructed from self using rows with indices in
3112+
the rows list.
3113+
3114+
EXAMPLES::
3115+
3116+
sage: M = MatrixSpace(Integers(8),3,3)
3117+
sage: A = M(range(9)); A
3118+
[0 1 2]
3119+
[3 4 5]
3120+
[6 7 0]
3121+
sage: A.matrix_from_rows([2,1])
3122+
[6 7 0]
3123+
[3 4 5]
3124+
"""
3125+
cdef Py_ssize_t nrows = len(rows)
3126+
3127+
# Construct new matrix
3128+
cdef Matrix_modn_dense_template A = self.new_matrix(nrows=nrows)
3129+
3130+
cdef Py_ssize_t i, row
3131+
for i, row in enumerate(rows):
3132+
if row < 0 or row >= self._nrows:
3133+
raise IndexError("row index out of range")
3134+
memcpy(A._matrix[i], self._matrix[row], sizeof(celement)*self._ncols)
3135+
3136+
return A
3137+
3138+
def matrix_from_rows_and_columns(self, rows, columns):
3139+
"""
3140+
Return the matrix constructed from self from the given rows and
3141+
columns.
3142+
3143+
EXAMPLES::
3144+
3145+
sage: M = MatrixSpace(Integers(8),3,3)
3146+
sage: A = M(range(9)); A
3147+
[0 1 2]
3148+
[3 4 5]
3149+
[6 7 0]
3150+
sage: A.matrix_from_rows_and_columns([1], [0,2])
3151+
[3 5]
3152+
sage: A.matrix_from_rows_and_columns([1,2], [1,2])
3153+
[4 5]
3154+
[7 0]
3155+
3156+
Note that row and column indices can be reordered or repeated::
3157+
3158+
sage: A.matrix_from_rows_and_columns([2,1], [2,1])
3159+
[0 7]
3160+
[5 4]
3161+
3162+
For example here we take from row 1 columns 2 then 0 twice, and do
3163+
this 3 times::
3164+
3165+
sage: A.matrix_from_rows_and_columns([1,1,1],[2,0,0])
3166+
[5 3 3]
3167+
[5 3 3]
3168+
[5 3 3]
3169+
3170+
AUTHORS:
3171+
3172+
- Jaap Spies (2006-02-18)
3173+
3174+
- Didier Deshommes: some Pyrex speedups implemented
3175+
"""
3176+
cdef Py_ssize_t ncols = len(columns)
3177+
cdef Py_ssize_t nrows = len(rows)
3178+
3179+
# Check whether column indices are valid
3180+
cdef Py_ssize_t i, j, row, col
3181+
for col in columns:
3182+
if col < 0 or col >= self._ncols:
3183+
raise IndexError("column index out of range")
3184+
3185+
# Construct new matrix
3186+
cdef Matrix_modn_dense_template A = self.new_matrix(nrows=nrows, ncols=ncols)
3187+
for i, row in enumerate(rows):
3188+
if row < 0 or row >= self._nrows:
3189+
raise IndexError("row index out of range")
3190+
for j, col in enumerate(columns):
3191+
A._matrix[i][j] = self._matrix[row][col]
3192+
3193+
return A
3194+
30683195
def __bool__(self):
30693196
"""
30703197
Test whether this matrix is zero.

0 commit comments

Comments
 (0)