passagemath-linbox 10.6.43__cp313-cp313-macosx_13_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- passagemath_linbox/.dylibs/libfflas.1.dylib +0 -0
- passagemath_linbox/.dylibs/libffpack.1.dylib +0 -0
- passagemath_linbox/.dylibs/libflint.22.0.dylib +0 -0
- passagemath_linbox/.dylibs/libgd.3.dylib +0 -0
- passagemath_linbox/.dylibs/libgfortran.5.dylib +0 -0
- passagemath_linbox/.dylibs/libgivaro.9.dylib +0 -0
- passagemath_linbox/.dylibs/libgmp.10.dylib +0 -0
- passagemath_linbox/.dylibs/libgmpxx.4.dylib +0 -0
- passagemath_linbox/.dylibs/libiml.0.dylib +0 -0
- passagemath_linbox/.dylibs/liblinbox.0.dylib +0 -0
- passagemath_linbox/.dylibs/libm4ri.1.dylib +0 -0
- passagemath_linbox/.dylibs/libm4rie.1.dylib +0 -0
- passagemath_linbox/.dylibs/libmpfr.6.dylib +0 -0
- passagemath_linbox/.dylibs/libopenblasp-r0.3.29.dylib +0 -0
- passagemath_linbox/.dylibs/libpng16.16.dylib +0 -0
- passagemath_linbox/.dylibs/libquadmath.0.dylib +0 -0
- passagemath_linbox/.dylibs/libz.1.3.1.dylib +0 -0
- passagemath_linbox/__init__.py +3 -0
- passagemath_linbox-10.6.43.dist-info/METADATA +100 -0
- passagemath_linbox-10.6.43.dist-info/RECORD +75 -0
- passagemath_linbox-10.6.43.dist-info/WHEEL +6 -0
- passagemath_linbox-10.6.43.dist-info/top_level.txt +3 -0
- sage/all__sagemath_linbox.py +2 -0
- sage/geometry/all__sagemath_linbox.py +1 -0
- sage/geometry/integral_points.pxi +1426 -0
- sage/geometry/integral_points_integer_dense.cpython-313-darwin.so +0 -0
- sage/geometry/integral_points_integer_dense.pyx +7 -0
- sage/libs/all__sagemath_linbox.py +1 -0
- sage/libs/iml.pxd +10 -0
- sage/libs/linbox/__init__.py +1 -0
- sage/libs/linbox/conversion.pxd +185 -0
- sage/libs/linbox/fflas.pxd +189 -0
- sage/libs/linbox/givaro.pxd +109 -0
- sage/libs/linbox/linbox.pxd +219 -0
- sage/libs/linbox/linbox_flint_interface.cpython-313-darwin.so +0 -0
- sage/libs/linbox/linbox_flint_interface.pxd +18 -0
- sage/libs/linbox/linbox_flint_interface.pyx +192 -0
- sage/libs/m4ri.pxd +198 -0
- sage/libs/m4rie.pxd +204 -0
- sage/matrix/all__sagemath_linbox.py +1 -0
- sage/matrix/matrix_cyclo_linbox.cpython-313-darwin.so +0 -0
- sage/matrix/matrix_cyclo_linbox.pyx +361 -0
- sage/matrix/matrix_gf2e_dense.cpython-313-darwin.so +0 -0
- sage/matrix/matrix_gf2e_dense.pxd +15 -0
- sage/matrix/matrix_gf2e_dense.pyx +1573 -0
- sage/matrix/matrix_integer_iml.cpython-313-darwin.so +0 -0
- sage/matrix/matrix_integer_iml.pyx +316 -0
- sage/matrix/matrix_integer_linbox.cpython-313-darwin.so +0 -0
- sage/matrix/matrix_integer_linbox.pxd +5 -0
- sage/matrix/matrix_integer_linbox.pyx +358 -0
- sage/matrix/matrix_integer_sparse_linbox.cpython-313-darwin.so +0 -0
- sage/matrix/matrix_integer_sparse_linbox.pyx +465 -0
- sage/matrix/matrix_mod2_dense.cpython-313-darwin.so +0 -0
- sage/matrix/matrix_mod2_dense.pxd +14 -0
- sage/matrix/matrix_mod2_dense.pyx +2789 -0
- sage/matrix/matrix_modn_dense_double.cpython-313-darwin.so +0 -0
- sage/matrix/matrix_modn_dense_double.pyx +179 -0
- sage/matrix/matrix_modn_dense_float.cpython-313-darwin.so +0 -0
- sage/matrix/matrix_modn_dense_float.pyx +154 -0
- sage/matrix/matrix_modn_sparse.cpython-313-darwin.so +0 -0
- sage/matrix/matrix_modn_sparse.pyx +871 -0
- sage/matrix/matrix_rational_linbox.cpython-313-darwin.so +0 -0
- sage/matrix/matrix_rational_linbox.pyx +36 -0
- sage/matrix/misc.cpython-313-darwin.so +0 -0
- sage/matrix/misc.pyx +418 -0
- sage/modules/all__sagemath_linbox.py +1 -0
- sage/modules/numpy_util.cpython-313-darwin.so +0 -0
- sage/modules/numpy_util.pxd +10 -0
- sage/modules/numpy_util.pyx +136 -0
- sage/modules/vector_mod2_dense.cpython-313-darwin.so +0 -0
- sage/modules/vector_mod2_dense.pxd +11 -0
- sage/modules/vector_mod2_dense.pyx +547 -0
- sage/rings/all__sagemath_linbox.py +1 -0
- sage/rings/finite_rings/all__sagemath_linbox.py +1 -0
- sage/rings/polynomial/all__sagemath_linbox.py +1 -0
|
@@ -0,0 +1,871 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-linbox
|
|
2
|
+
r"""
|
|
3
|
+
Sparse matrices over `\ZZ/n\ZZ` for `n` small
|
|
4
|
+
|
|
5
|
+
This is a compiled implementation of sparse matrices over
|
|
6
|
+
`\ZZ/n\ZZ` for `n` small.
|
|
7
|
+
|
|
8
|
+
.. TODO::
|
|
9
|
+
|
|
10
|
+
move vectors into a Cython vector class - add _add_ and _mul_ methods.
|
|
11
|
+
|
|
12
|
+
EXAMPLES::
|
|
13
|
+
|
|
14
|
+
sage: a = matrix(Integers(37),3,3,range(9),sparse=True); a
|
|
15
|
+
[0 1 2]
|
|
16
|
+
[3 4 5]
|
|
17
|
+
[6 7 8]
|
|
18
|
+
sage: type(a)
|
|
19
|
+
<class 'sage.matrix.matrix_modn_sparse.Matrix_modn_sparse'>
|
|
20
|
+
sage: parent(a)
|
|
21
|
+
Full MatrixSpace of 3 by 3 sparse matrices over Ring of integers modulo 37
|
|
22
|
+
sage: a^2
|
|
23
|
+
[15 18 21]
|
|
24
|
+
[ 5 17 29]
|
|
25
|
+
[32 16 0]
|
|
26
|
+
sage: a+a
|
|
27
|
+
[ 0 2 4]
|
|
28
|
+
[ 6 8 10]
|
|
29
|
+
[12 14 16]
|
|
30
|
+
sage: b = a.new_matrix(2,3,range(6)); b
|
|
31
|
+
[0 1 2]
|
|
32
|
+
[3 4 5]
|
|
33
|
+
sage: a*b
|
|
34
|
+
Traceback (most recent call last):
|
|
35
|
+
...
|
|
36
|
+
TypeError: unsupported operand parent(s) for *: 'Full MatrixSpace of 3 by 3 sparse matrices over Ring of integers modulo 37' and 'Full MatrixSpace of 2 by 3 sparse matrices over Ring of integers modulo 37'
|
|
37
|
+
sage: b*a
|
|
38
|
+
[15 18 21]
|
|
39
|
+
[ 5 17 29]
|
|
40
|
+
|
|
41
|
+
::
|
|
42
|
+
|
|
43
|
+
sage: TestSuite(a).run()
|
|
44
|
+
sage: TestSuite(b).run()
|
|
45
|
+
|
|
46
|
+
::
|
|
47
|
+
|
|
48
|
+
sage: a.echelonize(); a
|
|
49
|
+
[ 1 0 36]
|
|
50
|
+
[ 0 1 2]
|
|
51
|
+
[ 0 0 0]
|
|
52
|
+
sage: b.echelonize(); b
|
|
53
|
+
[ 1 0 36]
|
|
54
|
+
[ 0 1 2]
|
|
55
|
+
sage: a.pivots()
|
|
56
|
+
(0, 1)
|
|
57
|
+
sage: b.pivots()
|
|
58
|
+
(0, 1)
|
|
59
|
+
sage: a.rank()
|
|
60
|
+
2
|
|
61
|
+
sage: b.rank()
|
|
62
|
+
2
|
|
63
|
+
sage: a[2,2] = 5
|
|
64
|
+
sage: a.rank()
|
|
65
|
+
3
|
|
66
|
+
|
|
67
|
+
TESTS::
|
|
68
|
+
|
|
69
|
+
sage: matrix(Integers(37),0,0,sparse=True).inverse()
|
|
70
|
+
[]
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
# ****************************************************************************
|
|
74
|
+
# Copyright (C) 2006 William Stein <wstein@gmail.com>
|
|
75
|
+
#
|
|
76
|
+
# This program is free software: you can redistribute it and/or modify
|
|
77
|
+
# it under the terms of the GNU General Public License as published by
|
|
78
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
79
|
+
# (at your option) any later version.
|
|
80
|
+
# https://www.gnu.org/licenses/
|
|
81
|
+
# ****************************************************************************
|
|
82
|
+
|
|
83
|
+
from libc.stdint cimport uint64_t
|
|
84
|
+
from libc.limits cimport UINT_MAX
|
|
85
|
+
|
|
86
|
+
from cysignals.memory cimport check_calloc, sig_free
|
|
87
|
+
from cysignals.signals cimport sig_on, sig_off
|
|
88
|
+
|
|
89
|
+
cimport sage.libs.linbox.givaro as givaro
|
|
90
|
+
cimport sage.libs.linbox.linbox as linbox
|
|
91
|
+
|
|
92
|
+
from sage.arith.misc import is_prime
|
|
93
|
+
from sage.data_structures.binary_search cimport *
|
|
94
|
+
from sage.ext.stdsage cimport PY_NEW
|
|
95
|
+
from sage.libs.flint.fmpz cimport fmpz_get_mpz, fmpz_set_mpz
|
|
96
|
+
from sage.libs.flint.fmpz_mat cimport fmpz_mat_entry
|
|
97
|
+
from sage.libs.gmp.mpz cimport mpz_set
|
|
98
|
+
from sage.libs.linbox.conversion cimport (get_method,
|
|
99
|
+
METHOD_DEFAULT,
|
|
100
|
+
METHOD_DENSE_ELIMINATION,
|
|
101
|
+
METHOD_SPARSE_ELIMINATION,
|
|
102
|
+
METHOD_BLACKBOX,
|
|
103
|
+
METHOD_WIEDEMANN,
|
|
104
|
+
new_linbox_matrix_modn_sparse,
|
|
105
|
+
new_linbox_matrix_integer_sparse,
|
|
106
|
+
new_linbox_vector_integer_dense,
|
|
107
|
+
new_sage_vector_integer_dense)
|
|
108
|
+
from sage.matrix.args cimport SparseEntry, MatrixArgs_init
|
|
109
|
+
from sage.matrix.matrix2 import Matrix as Matrix2
|
|
110
|
+
from sage.matrix.matrix_dense cimport Matrix_dense
|
|
111
|
+
from sage.matrix.matrix_integer_dense cimport Matrix_integer_dense
|
|
112
|
+
from sage.matrix.matrix_sparse cimport Matrix_sparse
|
|
113
|
+
from sage.misc.verbose import verbose, get_verbose
|
|
114
|
+
from sage.modules.vector_integer_dense cimport Vector_integer_dense
|
|
115
|
+
from sage.modules.vector_integer_sparse cimport *
|
|
116
|
+
from sage.modules.vector_modn_sparse cimport *
|
|
117
|
+
from sage.rings.fast_arith cimport arith_int
|
|
118
|
+
from sage.rings.finite_rings.integer_mod cimport IntegerMod_int, IntegerMod_abstract
|
|
119
|
+
from sage.rings.integer cimport Integer
|
|
120
|
+
from sage.rings.integer_ring import ZZ
|
|
121
|
+
from sage.rings.rational_field import QQ
|
|
122
|
+
from sage.structure.element cimport Matrix
|
|
123
|
+
|
|
124
|
+
################
|
|
125
|
+
# TODO: change this to use extern cdef's methods.
|
|
126
|
+
cdef arith_int ai
|
|
127
|
+
ai = arith_int()
|
|
128
|
+
################
|
|
129
|
+
|
|
130
|
+
# The 46341 below is because the mod-n sparse code still uses
|
|
131
|
+
# int's, even on 64-bit computers. Improving this is
|
|
132
|
+
# Github Issue #12679.
|
|
133
|
+
MAX_MODULUS = 46341
|
|
134
|
+
|
|
135
|
+
cdef class Matrix_modn_sparse(Matrix_sparse):
|
|
136
|
+
def __cinit__(self):
|
|
137
|
+
nr = self._nrows
|
|
138
|
+
nc = self._ncols
|
|
139
|
+
cdef int p = self._base_ring.order()
|
|
140
|
+
self.p = p
|
|
141
|
+
|
|
142
|
+
self.rows = <c_vector_modint*>check_calloc(nr, sizeof(c_vector_modint))
|
|
143
|
+
|
|
144
|
+
cdef Py_ssize_t i
|
|
145
|
+
for i in range(nr):
|
|
146
|
+
init_c_vector_modint(&self.rows[i], p, nc, 0)
|
|
147
|
+
|
|
148
|
+
def __dealloc__(self):
|
|
149
|
+
cdef Py_ssize_t i
|
|
150
|
+
if self.rows:
|
|
151
|
+
for i in range(self._nrows):
|
|
152
|
+
clear_c_vector_modint(&self.rows[i])
|
|
153
|
+
sig_free(self.rows)
|
|
154
|
+
|
|
155
|
+
def __init__(self, parent, entries=None, copy=None, bint coerce=True):
|
|
156
|
+
r"""
|
|
157
|
+
Create a sparse matrix over the integers modulo ``n``.
|
|
158
|
+
|
|
159
|
+
INPUT:
|
|
160
|
+
|
|
161
|
+
- ``parent`` -- a matrix space over the integers modulo ``n``
|
|
162
|
+
|
|
163
|
+
- ``entries`` -- see :func:`matrix`
|
|
164
|
+
|
|
165
|
+
- ``copy`` -- ignored (for backwards compatibility)
|
|
166
|
+
|
|
167
|
+
- ``coerce`` -- if ``False``, assume without checking that the
|
|
168
|
+
entries lie in the base ring
|
|
169
|
+
"""
|
|
170
|
+
ma = MatrixArgs_init(parent, entries)
|
|
171
|
+
for t in ma.iter(coerce, True):
|
|
172
|
+
se = <SparseEntry>t
|
|
173
|
+
z = se.entry
|
|
174
|
+
if z:
|
|
175
|
+
set_entry(&self.rows[se.i], se.j, z)
|
|
176
|
+
|
|
177
|
+
cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, value):
|
|
178
|
+
set_entry(&self.rows[i], j, (<IntegerMod_int> value).ivalue)
|
|
179
|
+
|
|
180
|
+
cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j):
|
|
181
|
+
cdef IntegerMod_int n
|
|
182
|
+
n = IntegerMod_int.__new__(IntegerMod_int)
|
|
183
|
+
IntegerMod_abstract.__init__(n, self._base_ring)
|
|
184
|
+
n.ivalue = get_entry(&self.rows[i], j)
|
|
185
|
+
return n
|
|
186
|
+
|
|
187
|
+
cdef bint get_is_zero_unsafe(self, Py_ssize_t i, Py_ssize_t j) except -1:
|
|
188
|
+
"""
|
|
189
|
+
Return 1 if the entry ``(i, j)`` is zero, otherwise 0.
|
|
190
|
+
|
|
191
|
+
EXAMPLES::
|
|
192
|
+
|
|
193
|
+
sage: M = matrix(GF(13), [[0,1,0],[0,0,0]], sparse=True)
|
|
194
|
+
sage: M.zero_pattern_matrix() # indirect doctest
|
|
195
|
+
[1 0 1]
|
|
196
|
+
[1 1 1]
|
|
197
|
+
"""
|
|
198
|
+
return is_entry_zero_unsafe(&self.rows[i], j)
|
|
199
|
+
|
|
200
|
+
def _dict(self):
|
|
201
|
+
"""
|
|
202
|
+
Unsafe version of the dict method, mainly for internal use. This
|
|
203
|
+
may return the dict of elements, but as an *unsafe* reference to
|
|
204
|
+
the underlying dict of the object. It might be dangerous if you
|
|
205
|
+
change entries of the returned dict.
|
|
206
|
+
|
|
207
|
+
EXAMPLES::
|
|
208
|
+
|
|
209
|
+
sage: MS = MatrixSpace(GF(13), 50, 50, sparse=True)
|
|
210
|
+
sage: m = MS._random_nonzero_element(density=0.002)
|
|
211
|
+
sage: d = m._dict()
|
|
212
|
+
sage: for i in range(50):
|
|
213
|
+
....: for j in range(50):
|
|
214
|
+
....: if m[i, j] != 0:
|
|
215
|
+
....: assert m[i, j] == d[i, j]
|
|
216
|
+
....: else:
|
|
217
|
+
....: assert (i, j) not in d
|
|
218
|
+
|
|
219
|
+
TESTS::
|
|
220
|
+
|
|
221
|
+
sage: [i, j] = list(d.keys())[0]
|
|
222
|
+
sage: parent(m._dict()[i, j])
|
|
223
|
+
Finite Field of size 13
|
|
224
|
+
"""
|
|
225
|
+
d = self.fetch('dict')
|
|
226
|
+
if d is not None:
|
|
227
|
+
return d
|
|
228
|
+
|
|
229
|
+
cdef Py_ssize_t i, j
|
|
230
|
+
d = {}
|
|
231
|
+
cdef IntegerMod_int n
|
|
232
|
+
for i from 0 <= i < self._nrows:
|
|
233
|
+
for j from 0 <= j < self.rows[i].num_nonzero:
|
|
234
|
+
n = IntegerMod_int.__new__(IntegerMod_int)
|
|
235
|
+
IntegerMod_abstract.__init__(n, self._base_ring)
|
|
236
|
+
n.ivalue = self.rows[i].entries[j]
|
|
237
|
+
d[(int(i),int(self.rows[i].positions[j]))] = n
|
|
238
|
+
self.cache('dict', d)
|
|
239
|
+
return d
|
|
240
|
+
|
|
241
|
+
def _pickle(self):
|
|
242
|
+
"""
|
|
243
|
+
TESTS::
|
|
244
|
+
|
|
245
|
+
sage: M = Matrix( GF(2), [[1,1,1,1,0,0,0,0,0,0]], sparse=True )
|
|
246
|
+
sage: loads(dumps(M))
|
|
247
|
+
[1 1 1 1 0 0 0 0 0 0]
|
|
248
|
+
sage: loads(dumps(M)) == M
|
|
249
|
+
True
|
|
250
|
+
"""
|
|
251
|
+
return self._dict(), 1
|
|
252
|
+
|
|
253
|
+
def _unpickle(self, data, version):
|
|
254
|
+
if version == 1:
|
|
255
|
+
self.__init__(self.parent(), data, coerce=False)
|
|
256
|
+
else:
|
|
257
|
+
raise ValueError("unknown matrix format")
|
|
258
|
+
|
|
259
|
+
cdef Matrix _matrix_times_matrix_(self, Matrix _right):
|
|
260
|
+
"""
|
|
261
|
+
This code is implicitly called for multiplying ``self`` by another
|
|
262
|
+
sparse matrix.
|
|
263
|
+
|
|
264
|
+
EXAMPLES::
|
|
265
|
+
|
|
266
|
+
sage: a = matrix(GF(43), 3, 3, range(9), sparse=True)
|
|
267
|
+
sage: b = matrix(GF(43), 3, 3, range(10,19), sparse=True)
|
|
268
|
+
sage: a*b
|
|
269
|
+
[ 2 5 8]
|
|
270
|
+
[33 2 14]
|
|
271
|
+
[21 42 20]
|
|
272
|
+
sage: a*a
|
|
273
|
+
[15 18 21]
|
|
274
|
+
[42 11 23]
|
|
275
|
+
[26 4 25]
|
|
276
|
+
sage: c = matrix(GF(43), 3, 8, range(24), sparse=True)
|
|
277
|
+
sage: a*c
|
|
278
|
+
[40 0 3 6 9 12 15 18]
|
|
279
|
+
[26 38 7 19 31 0 12 24]
|
|
280
|
+
[12 33 11 32 10 31 9 30]
|
|
281
|
+
|
|
282
|
+
Even though sparse and dense matrices are represented
|
|
283
|
+
differently, they still compare as equal if they have the
|
|
284
|
+
same entries::
|
|
285
|
+
|
|
286
|
+
sage: a*b == a._matrix_times_matrix_dense(b)
|
|
287
|
+
True
|
|
288
|
+
sage: d = matrix(GF(43), 3, 8, range(24))
|
|
289
|
+
sage: a*c == a*d
|
|
290
|
+
True
|
|
291
|
+
|
|
292
|
+
TESTS:
|
|
293
|
+
|
|
294
|
+
The following shows that :issue:`23669` has been addressed::
|
|
295
|
+
|
|
296
|
+
sage: p = next_prime(2**15)
|
|
297
|
+
sage: M = Matrix(GF(p), 1,3, lambda i,j: -1, sparse=True); M
|
|
298
|
+
[32770 32770 32770]
|
|
299
|
+
sage: M*M.transpose() # previously returned [32738]
|
|
300
|
+
[3]
|
|
301
|
+
"""
|
|
302
|
+
cdef Matrix_modn_sparse right, ans
|
|
303
|
+
right = _right
|
|
304
|
+
|
|
305
|
+
cdef c_vector_modint* v
|
|
306
|
+
|
|
307
|
+
# Build a table that gives the nonzero positions in each column of right
|
|
308
|
+
cdef list nonzero_positions_in_columns = [set() for _ in range(right._ncols)]
|
|
309
|
+
cdef Py_ssize_t i, j, k
|
|
310
|
+
for i in range(right._nrows):
|
|
311
|
+
v = &(right.rows[i])
|
|
312
|
+
for j in range(v.num_nonzero):
|
|
313
|
+
(<set> nonzero_positions_in_columns[v.positions[j]]).add(i)
|
|
314
|
+
|
|
315
|
+
ans = self.new_matrix(self._nrows, right._ncols)
|
|
316
|
+
|
|
317
|
+
# Now do the multiplication, getting each row completely before filling it in.
|
|
318
|
+
cdef int x, y, s
|
|
319
|
+
cdef set c
|
|
320
|
+
|
|
321
|
+
for i in range(self._nrows):
|
|
322
|
+
v = &(self.rows[i])
|
|
323
|
+
for j in range(right._ncols):
|
|
324
|
+
s = 0
|
|
325
|
+
c = <set> nonzero_positions_in_columns[j]
|
|
326
|
+
for k in range(v.num_nonzero):
|
|
327
|
+
if v.positions[k] in c:
|
|
328
|
+
y = get_entry(&right.rows[v.positions[k]], j)
|
|
329
|
+
x = v.entries[k] * y
|
|
330
|
+
s = (s + x) % self.p
|
|
331
|
+
set_entry(&ans.rows[i], j, s)
|
|
332
|
+
return ans
|
|
333
|
+
|
|
334
|
+
def _matrix_times_matrix_dense(self, Matrix _right):
|
|
335
|
+
"""
|
|
336
|
+
Multiply ``self`` by the sparse matrix ``_right``, and return the
|
|
337
|
+
result as a dense matrix.
|
|
338
|
+
|
|
339
|
+
EXAMPLES::
|
|
340
|
+
|
|
341
|
+
sage: # needs sage.rings.finite_rings
|
|
342
|
+
sage: a = matrix(GF(10007), 2, [1,2,3,4], sparse=True)
|
|
343
|
+
sage: b = matrix(GF(10007), 2, 3, [1..6], sparse=True)
|
|
344
|
+
sage: a * b
|
|
345
|
+
[ 9 12 15]
|
|
346
|
+
[19 26 33]
|
|
347
|
+
sage: c = a._matrix_times_matrix_dense(b); c
|
|
348
|
+
[ 9 12 15]
|
|
349
|
+
[19 26 33]
|
|
350
|
+
sage: type(c)
|
|
351
|
+
<class 'sage.matrix.matrix_modn_dense_double.Matrix_modn_dense_double'>
|
|
352
|
+
|
|
353
|
+
sage: a = matrix(GF(2), 20, 20, sparse=True)
|
|
354
|
+
sage: a*a == a._matrix_times_matrix_dense(a)
|
|
355
|
+
True
|
|
356
|
+
sage: type(a._matrix_times_matrix_dense(a))
|
|
357
|
+
<class 'sage.matrix.matrix_mod2_dense.Matrix_mod2_dense'>
|
|
358
|
+
"""
|
|
359
|
+
cdef Matrix_modn_sparse right
|
|
360
|
+
cdef Matrix_dense ans
|
|
361
|
+
right = _right
|
|
362
|
+
|
|
363
|
+
cdef c_vector_modint* v
|
|
364
|
+
|
|
365
|
+
# Build a table that gives the nonzero positions in each column of right
|
|
366
|
+
nonzero_positions_in_columns = [set() for _ in range(right._ncols)]
|
|
367
|
+
cdef Py_ssize_t i, j, k
|
|
368
|
+
for i from 0 <= i < right._nrows:
|
|
369
|
+
v = &(right.rows[i])
|
|
370
|
+
for j from 0 <= j < right.rows[i].num_nonzero:
|
|
371
|
+
nonzero_positions_in_columns[v.positions[j]].add(i)
|
|
372
|
+
|
|
373
|
+
ans = self.new_matrix(self._nrows, right._ncols, sparse=False)
|
|
374
|
+
|
|
375
|
+
# Now do the multiplication, getting each row completely before filling it in.
|
|
376
|
+
cdef int x, y, s
|
|
377
|
+
|
|
378
|
+
for i from 0 <= i < self._nrows:
|
|
379
|
+
v = &self.rows[i]
|
|
380
|
+
for j from 0 <= j < right._ncols:
|
|
381
|
+
s = 0
|
|
382
|
+
c = nonzero_positions_in_columns[j]
|
|
383
|
+
for k from 0 <= k < v.num_nonzero:
|
|
384
|
+
if v.positions[k] in c:
|
|
385
|
+
y = get_entry(&right.rows[v.positions[k]], j)
|
|
386
|
+
x = v.entries[k] * y
|
|
387
|
+
s = (s + x) % self.p
|
|
388
|
+
ans.set_unsafe_int(i, j, s)
|
|
389
|
+
#ans._matrix[i][j] = s
|
|
390
|
+
return ans
|
|
391
|
+
|
|
392
|
+
def swap_rows(self, r1, r2):
|
|
393
|
+
self.check_bounds_and_mutability(r1,0)
|
|
394
|
+
self.check_bounds_and_mutability(r2,0)
|
|
395
|
+
self.swap_rows_c(r1, r2)
|
|
396
|
+
|
|
397
|
+
cdef swap_rows_c(self, Py_ssize_t n1, Py_ssize_t n2):
|
|
398
|
+
"""
|
|
399
|
+
Swap the rows in positions n1 and n2. No bounds checking.
|
|
400
|
+
"""
|
|
401
|
+
cdef c_vector_modint tmp
|
|
402
|
+
tmp = self.rows[n1]
|
|
403
|
+
self.rows[n1] = self.rows[n2]
|
|
404
|
+
self.rows[n2] = tmp
|
|
405
|
+
|
|
406
|
+
cpdef _echelon_in_place(self, str algorithm):
|
|
407
|
+
"""
|
|
408
|
+
Replace ``self`` by its reduction to reduced row echelon form.
|
|
409
|
+
|
|
410
|
+
ALGORITHM: We use Gauss elimination, in a slightly intelligent way,
|
|
411
|
+
in that we clear each column using a row with the minimum number of
|
|
412
|
+
nonzero entries.
|
|
413
|
+
|
|
414
|
+
TODO: Implement switching to a dense method when the matrix gets
|
|
415
|
+
dense.
|
|
416
|
+
"""
|
|
417
|
+
from sage.misc.verbose import verbose, get_verbose
|
|
418
|
+
x = self.fetch('in_echelon_form')
|
|
419
|
+
if x is not None and x:
|
|
420
|
+
return # already known to be in echelon form
|
|
421
|
+
self.check_mutability()
|
|
422
|
+
|
|
423
|
+
cdef Py_ssize_t i, r, c, min, min_row, start_row
|
|
424
|
+
cdef int a_inverse, b, do_verb
|
|
425
|
+
cdef c_vector_modint tmp
|
|
426
|
+
start_row = 0
|
|
427
|
+
pivots = []
|
|
428
|
+
fifth = self._ncols / 10 + 1
|
|
429
|
+
tm = verbose(caller_name = 'sparse_matrix_pyx matrix_modint echelon')
|
|
430
|
+
do_verb = (get_verbose() >= 2)
|
|
431
|
+
|
|
432
|
+
for c from 0 <= c < self._ncols:
|
|
433
|
+
if do_verb and (c % fifth == 0 and c>0):
|
|
434
|
+
tm = verbose('on column %s of %s' % (c, self._ncols),
|
|
435
|
+
level = 2,
|
|
436
|
+
caller_name = 'matrix_modn_sparse echelon')
|
|
437
|
+
#end if
|
|
438
|
+
min = self._ncols + 1
|
|
439
|
+
min_row = -1
|
|
440
|
+
for r from start_row <= r < self._nrows:
|
|
441
|
+
if self.rows[r].num_nonzero > 0 and self.rows[r].num_nonzero < min:
|
|
442
|
+
# Since there is at least one nonzero entry, the first entry
|
|
443
|
+
# of the positions list is defined. It is the first position
|
|
444
|
+
# of a nonzero entry, and it equals c precisely if row r
|
|
445
|
+
# is a row we could use to clear column c.
|
|
446
|
+
if self.rows[r].positions[0] == c:
|
|
447
|
+
min_row = r
|
|
448
|
+
min = self.rows[r].num_nonzero
|
|
449
|
+
#endif
|
|
450
|
+
#endif
|
|
451
|
+
#endfor
|
|
452
|
+
if min_row != -1:
|
|
453
|
+
r = min_row
|
|
454
|
+
# print("min number of entries in a pivoting row = ", min)
|
|
455
|
+
pivots.append(c)
|
|
456
|
+
# Since we can use row r to clear column c, the
|
|
457
|
+
# entry in position c in row r must be the first nonzero entry.
|
|
458
|
+
a = self.rows[r].entries[0]
|
|
459
|
+
if a != 1:
|
|
460
|
+
a_inverse = ai.c_inverse_mod_int(a, self.p)
|
|
461
|
+
scale_c_vector_modint(&self.rows[r], a_inverse)
|
|
462
|
+
self.swap_rows_c(r, start_row)
|
|
463
|
+
sig_on()
|
|
464
|
+
for i from 0 <= i < self._nrows:
|
|
465
|
+
if i != start_row:
|
|
466
|
+
b = get_entry(&self.rows[i], c)
|
|
467
|
+
if b != 0:
|
|
468
|
+
add_c_vector_modint_init(&tmp, &self.rows[i],
|
|
469
|
+
&self.rows[start_row], self.p - b)
|
|
470
|
+
clear_c_vector_modint(&self.rows[i])
|
|
471
|
+
self.rows[i] = tmp
|
|
472
|
+
sig_off()
|
|
473
|
+
start_row = start_row + 1
|
|
474
|
+
|
|
475
|
+
self.cache('pivots', tuple(pivots))
|
|
476
|
+
self.cache('in_echelon_form', True)
|
|
477
|
+
|
|
478
|
+
def _nonzero_positions_by_row(self, copy=True):
|
|
479
|
+
"""
|
|
480
|
+
Return the list of pairs (i,j) such that self[i,j] != 0.
|
|
481
|
+
|
|
482
|
+
It is safe to change the resulting list (unless you give the option copy=False).
|
|
483
|
+
|
|
484
|
+
EXAMPLES::
|
|
485
|
+
|
|
486
|
+
sage: M = Matrix(GF(7), [[0,0,0,1,0,0,0,0],[0,1,0,0,0,0,1,0]], sparse=True); M
|
|
487
|
+
[0 0 0 1 0 0 0 0]
|
|
488
|
+
[0 1 0 0 0 0 1 0]
|
|
489
|
+
sage: M.nonzero_positions()
|
|
490
|
+
[(0, 3), (1, 1), (1, 6)]
|
|
491
|
+
"""
|
|
492
|
+
x = self.fetch('nonzero_positions')
|
|
493
|
+
if x is not None:
|
|
494
|
+
if copy:
|
|
495
|
+
return list(x)
|
|
496
|
+
return x
|
|
497
|
+
nzp = []
|
|
498
|
+
cdef Py_ssize_t i, j
|
|
499
|
+
for i from 0 <= i < self._nrows:
|
|
500
|
+
for j from 0 <= j < self.rows[i].num_nonzero:
|
|
501
|
+
nzp.append((i,self.rows[i].positions[j]))
|
|
502
|
+
self.cache('nonzero_positions', nzp)
|
|
503
|
+
if copy:
|
|
504
|
+
return list(nzp)
|
|
505
|
+
return nzp
|
|
506
|
+
|
|
507
|
+
def density(self):
|
|
508
|
+
"""
|
|
509
|
+
Return the density of ``self``, i.e., the ratio of the number of
|
|
510
|
+
nonzero entries of ``self`` to the total size of ``self``.
|
|
511
|
+
|
|
512
|
+
EXAMPLES::
|
|
513
|
+
|
|
514
|
+
sage: A = matrix(QQ,3,3,[0,1,2,3,0,0,6,7,8],sparse=True)
|
|
515
|
+
sage: A.density()
|
|
516
|
+
2/3
|
|
517
|
+
|
|
518
|
+
Notice that the density parameter does not ensure the density
|
|
519
|
+
of a matrix; it is only an upper bound.
|
|
520
|
+
|
|
521
|
+
::
|
|
522
|
+
|
|
523
|
+
sage: A = random_matrix(GF(127), 200, 200, density=0.3, sparse=True)
|
|
524
|
+
sage: density_sum = float(A.density())
|
|
525
|
+
sage: total = 1
|
|
526
|
+
sage: expected_density = 1.0 - (199/200)^60
|
|
527
|
+
sage: expected_density
|
|
528
|
+
0.2597...
|
|
529
|
+
sage: while abs(density_sum/total - expected_density) > 0.001:
|
|
530
|
+
....: A = random_matrix(GF(127), 200, 200, density=0.3, sparse=True)
|
|
531
|
+
....: density_sum += float(A.density())
|
|
532
|
+
....: total += 1
|
|
533
|
+
"""
|
|
534
|
+
cdef Py_ssize_t i, nonzero_entries
|
|
535
|
+
|
|
536
|
+
nonzero_entries = 0
|
|
537
|
+
for i from 0 <= i < self._nrows:
|
|
538
|
+
nonzero_entries += self.rows[i].num_nonzero
|
|
539
|
+
|
|
540
|
+
return ZZ(nonzero_entries) / ZZ(self._nrows*self._ncols)
|
|
541
|
+
|
|
542
|
+
def transpose(self):
|
|
543
|
+
"""
|
|
544
|
+
Return the transpose of ``self``.
|
|
545
|
+
|
|
546
|
+
EXAMPLES::
|
|
547
|
+
|
|
548
|
+
sage: A = matrix(GF(127),3,3,[0,1,0,2,0,0,3,0,0],sparse=True)
|
|
549
|
+
sage: A
|
|
550
|
+
[0 1 0]
|
|
551
|
+
[2 0 0]
|
|
552
|
+
[3 0 0]
|
|
553
|
+
sage: A.transpose()
|
|
554
|
+
[0 2 3]
|
|
555
|
+
[1 0 0]
|
|
556
|
+
[0 0 0]
|
|
557
|
+
|
|
558
|
+
``.T`` is a convenient shortcut for the transpose::
|
|
559
|
+
|
|
560
|
+
sage: A.T
|
|
561
|
+
[0 2 3]
|
|
562
|
+
[1 0 0]
|
|
563
|
+
[0 0 0]
|
|
564
|
+
"""
|
|
565
|
+
cdef Py_ssize_t i, j
|
|
566
|
+
cdef c_vector_modint row
|
|
567
|
+
cdef Matrix_modn_sparse B
|
|
568
|
+
|
|
569
|
+
B = self.new_matrix(nrows = self.ncols(), ncols = self.nrows())
|
|
570
|
+
for i from 0 <= i < self._nrows:
|
|
571
|
+
row = self.rows[i]
|
|
572
|
+
for j from 0 <= j < row.num_nonzero:
|
|
573
|
+
set_entry(&B.rows[row.positions[j]], i, row.entries[j])
|
|
574
|
+
if self._subdivisions is not None:
|
|
575
|
+
row_divs, col_divs = self.subdivisions()
|
|
576
|
+
B.subdivide(col_divs, row_divs)
|
|
577
|
+
return B
|
|
578
|
+
|
|
579
|
+
def matrix_from_rows(self, rows):
|
|
580
|
+
"""
|
|
581
|
+
Return the matrix constructed from ``self`` using rows with indices in
|
|
582
|
+
the rows list.
|
|
583
|
+
|
|
584
|
+
INPUT:
|
|
585
|
+
|
|
586
|
+
- ``rows`` -- list or tuple of row indices
|
|
587
|
+
|
|
588
|
+
EXAMPLES::
|
|
589
|
+
|
|
590
|
+
sage: M = MatrixSpace(GF(127),3,3,sparse=True)
|
|
591
|
+
sage: A = M(range(9)); A
|
|
592
|
+
[0 1 2]
|
|
593
|
+
[3 4 5]
|
|
594
|
+
[6 7 8]
|
|
595
|
+
sage: A.matrix_from_rows([2,1])
|
|
596
|
+
[6 7 8]
|
|
597
|
+
[3 4 5]
|
|
598
|
+
"""
|
|
599
|
+
cdef Py_ssize_t i,k
|
|
600
|
+
cdef Matrix_modn_sparse A
|
|
601
|
+
cdef c_vector_modint row
|
|
602
|
+
|
|
603
|
+
if not isinstance(rows, (list, tuple)):
|
|
604
|
+
rows = list(rows)
|
|
605
|
+
|
|
606
|
+
A = self.new_matrix(nrows = len(rows))
|
|
607
|
+
|
|
608
|
+
k = 0
|
|
609
|
+
for ii in rows:
|
|
610
|
+
i = ii
|
|
611
|
+
if i < 0 or i >= self.nrows():
|
|
612
|
+
raise IndexError("row %s out of range" % i)
|
|
613
|
+
|
|
614
|
+
row = self.rows[i]
|
|
615
|
+
for j from 0 <= j < row.num_nonzero:
|
|
616
|
+
set_entry(&A.rows[k], row.positions[j], row.entries[j])
|
|
617
|
+
k += 1
|
|
618
|
+
return A
|
|
619
|
+
|
|
620
|
+
def matrix_from_columns(self, cols):
|
|
621
|
+
"""
|
|
622
|
+
Return the matrix constructed from ``self`` using columns with indices
|
|
623
|
+
in the columns list.
|
|
624
|
+
|
|
625
|
+
EXAMPLES::
|
|
626
|
+
|
|
627
|
+
sage: M = MatrixSpace(GF(127),3,3,sparse=True)
|
|
628
|
+
sage: A = M(range(9)); A
|
|
629
|
+
[0 1 2]
|
|
630
|
+
[3 4 5]
|
|
631
|
+
[6 7 8]
|
|
632
|
+
sage: A.matrix_from_columns([2,1])
|
|
633
|
+
[2 1]
|
|
634
|
+
[5 4]
|
|
635
|
+
[8 7]
|
|
636
|
+
"""
|
|
637
|
+
cdef Py_ssize_t i,j
|
|
638
|
+
cdef Matrix_modn_sparse A
|
|
639
|
+
cdef c_vector_modint row
|
|
640
|
+
|
|
641
|
+
if not isinstance(cols, (list, tuple)):
|
|
642
|
+
cols = list(cols)
|
|
643
|
+
|
|
644
|
+
A = self.new_matrix(ncols = len(cols))
|
|
645
|
+
|
|
646
|
+
cols = dict(zip([int(e) for e in cols],range(len(cols))))
|
|
647
|
+
|
|
648
|
+
for i from 0 <= i < self.nrows():
|
|
649
|
+
row = self.rows[i]
|
|
650
|
+
for j from 0 <= j < row.num_nonzero:
|
|
651
|
+
if int(row.positions[j]) in cols:
|
|
652
|
+
set_entry(&A.rows[i], cols[int(row.positions[j])], row.entries[j])
|
|
653
|
+
return A
|
|
654
|
+
|
|
655
|
+
def _rank_det_linbox(self):
|
|
656
|
+
"""
|
|
657
|
+
Return the rank and determinant using linbox.
|
|
658
|
+
|
|
659
|
+
.. NOTE::
|
|
660
|
+
|
|
661
|
+
This method does not perform any caching contrarily to
|
|
662
|
+
:meth:`determinant` and :meth:`rank`.
|
|
663
|
+
|
|
664
|
+
EXAMPLES::
|
|
665
|
+
|
|
666
|
+
sage: m = matrix(Zmod(13), 1, sparse=True)
|
|
667
|
+
sage: m[0,0] = 0
|
|
668
|
+
sage: m._rank_det_linbox()
|
|
669
|
+
(0, 0)
|
|
670
|
+
sage: for i in range(1, 13):
|
|
671
|
+
....: m[0,0] = i
|
|
672
|
+
....: assert m._rank_det_linbox() == (1, i)
|
|
673
|
+
|
|
674
|
+
sage: m = matrix(GF(5), 2, sparse=True)
|
|
675
|
+
sage: m[0,0] = 1
|
|
676
|
+
sage: m[0,1] = 2
|
|
677
|
+
sage: m[1,0] = 1
|
|
678
|
+
sage: m[1,1] = 3
|
|
679
|
+
sage: m._rank_det_linbox()
|
|
680
|
+
(2, 1)
|
|
681
|
+
sage: m
|
|
682
|
+
[1 2]
|
|
683
|
+
[1 3]
|
|
684
|
+
|
|
685
|
+
TESTS::
|
|
686
|
+
|
|
687
|
+
sage: matrix(Zmod(3), 0, sparse=True)._rank_det_linbox()
|
|
688
|
+
(0, 1)
|
|
689
|
+
"""
|
|
690
|
+
if self._nrows == 0 or self._ncols == 0:
|
|
691
|
+
# TODO: bug in linbox (gives segfault)
|
|
692
|
+
return 0, self.base_ring().one()
|
|
693
|
+
|
|
694
|
+
cdef size_t A_rank = 0
|
|
695
|
+
cdef uint64_t A_det = 0
|
|
696
|
+
|
|
697
|
+
if not is_prime(self.p):
|
|
698
|
+
raise TypeError("only GF(p) supported via LinBox")
|
|
699
|
+
|
|
700
|
+
cdef givaro.Modular_uint64 * F = new givaro.Modular_uint64(<uint64_t> self.p)
|
|
701
|
+
cdef linbox.SparseMatrix_Modular_uint64 * A
|
|
702
|
+
A = new_linbox_matrix_modn_sparse(F[0], self)
|
|
703
|
+
|
|
704
|
+
cdef linbox.GaussDomain_Modular_uint64 * dom = new linbox.GaussDomain_Modular_uint64(F[0])
|
|
705
|
+
|
|
706
|
+
# NOTE: see above for the reason of casting...
|
|
707
|
+
if A.rowdim() >= <size_t> UINT_MAX or A.coldim() >= <size_t> UINT_MAX:
|
|
708
|
+
raise ValueError("row/column size unsupported in LinBox")
|
|
709
|
+
|
|
710
|
+
dom.InPlaceLinearPivoting(A_rank, A_det, A[0], A.rowdim(), A.coldim())
|
|
711
|
+
|
|
712
|
+
del A
|
|
713
|
+
del F
|
|
714
|
+
del dom
|
|
715
|
+
|
|
716
|
+
return <long> A_rank, self.base_ring()(A_det)
|
|
717
|
+
|
|
718
|
+
def rank(self, algorithm=None):
|
|
719
|
+
"""
|
|
720
|
+
Return the rank of this matrix.
|
|
721
|
+
|
|
722
|
+
INPUT:
|
|
723
|
+
|
|
724
|
+
- ``algorithm`` -- either ``'linbox'`` (only available for
|
|
725
|
+
matrices over prime fields) or ``'generic'``
|
|
726
|
+
|
|
727
|
+
EXAMPLES::
|
|
728
|
+
|
|
729
|
+
sage: A = matrix(GF(127), 2, 2, sparse=True)
|
|
730
|
+
sage: A[0,0] = 34
|
|
731
|
+
sage: A[0,1] = 102
|
|
732
|
+
sage: A[1,0] = 55
|
|
733
|
+
sage: A[1,1] = 74
|
|
734
|
+
sage: A.rank()
|
|
735
|
+
2
|
|
736
|
+
|
|
737
|
+
sage: A._clear_cache()
|
|
738
|
+
sage: A.rank(algorithm='generic')
|
|
739
|
+
2
|
|
740
|
+
sage: A._clear_cache()
|
|
741
|
+
sage: A.rank(algorithm='hey')
|
|
742
|
+
Traceback (most recent call last):
|
|
743
|
+
...
|
|
744
|
+
ValueError: no algorithm 'hey'
|
|
745
|
+
|
|
746
|
+
TESTS::
|
|
747
|
+
|
|
748
|
+
sage: matrix(GF(3), 0, sparse=True).rank(algorithm='generic')
|
|
749
|
+
0
|
|
750
|
+
sage: matrix(GF(3), 0, sparse=True).rank(algorithm='linbox')
|
|
751
|
+
0
|
|
752
|
+
|
|
753
|
+
sage: for _ in range(50):
|
|
754
|
+
....: nrows = randint(0, 100)
|
|
755
|
+
....: ncols = randint(0, 100)
|
|
756
|
+
....: p = random_prime(10000)
|
|
757
|
+
....: M = MatrixSpace(GF(p), nrows, ncols, sparse=True)
|
|
758
|
+
....: m = M.random_element()
|
|
759
|
+
....: rank_linbox = m.rank(algorithm='linbox')
|
|
760
|
+
....: rank_generic = m.rank(algorithm='generic')
|
|
761
|
+
....: if rank_linbox != rank_generic:
|
|
762
|
+
....: print(m)
|
|
763
|
+
....: raise RuntimeError
|
|
764
|
+
|
|
765
|
+
REFERENCES:
|
|
766
|
+
|
|
767
|
+
- Jean-Guillaume Dumas and Gilles Villars. 'Computing the Rank
|
|
768
|
+
of Large Sparse Matrices over Finite
|
|
769
|
+
Fields'. Proc. CASC'2002, The Fifth International Workshop
|
|
770
|
+
on Computer Algebra in Scientific Computing, Big Yalta,
|
|
771
|
+
Crimea, Ukraine, 22-27 sept. 2002, Springer-Verlag,
|
|
772
|
+
http://perso.ens-lyon.fr/gilles.villard/BIBLIOGRAPHIE/POSTSCRIPT/rankjgd.ps
|
|
773
|
+
|
|
774
|
+
.. NOTE::
|
|
775
|
+
|
|
776
|
+
For very sparse matrices Gaussian elimination is faster
|
|
777
|
+
because it barely has anything to do. If the fill in needs to
|
|
778
|
+
be considered, 'Symbolic Reordering' is usually much faster.
|
|
779
|
+
"""
|
|
780
|
+
if self._nrows == 0 or self._ncols == 0:
|
|
781
|
+
return 0
|
|
782
|
+
|
|
783
|
+
if not is_prime(self.p):
|
|
784
|
+
raise ArithmeticError("rank not well defined for matrices over general ring")
|
|
785
|
+
|
|
786
|
+
x = self.fetch('rank')
|
|
787
|
+
if x is not None:
|
|
788
|
+
return x
|
|
789
|
+
|
|
790
|
+
if algorithm is None or algorithm == "linbox":
|
|
791
|
+
rank, det = self._rank_det_linbox()
|
|
792
|
+
self.cache("rank", rank)
|
|
793
|
+
self.cache("det", det)
|
|
794
|
+
return rank
|
|
795
|
+
|
|
796
|
+
elif algorithm == "generic":
|
|
797
|
+
return Matrix2.rank(self)
|
|
798
|
+
|
|
799
|
+
else:
|
|
800
|
+
raise ValueError("no algorithm '%s'" % algorithm)
|
|
801
|
+
|
|
802
|
+
def determinant(self, algorithm=None):
|
|
803
|
+
r"""
|
|
804
|
+
Return the determinant of this matrix.
|
|
805
|
+
|
|
806
|
+
INPUT:
|
|
807
|
+
|
|
808
|
+
- ``algorithm`` -- either ``'linbox'`` (default) or ``'generic'``
|
|
809
|
+
|
|
810
|
+
EXAMPLES::
|
|
811
|
+
|
|
812
|
+
sage: A = matrix(GF(3), 4, range(16), sparse=True)
|
|
813
|
+
sage: B = identity_matrix(GF(3), 4, sparse=True)
|
|
814
|
+
sage: (A + B).det()
|
|
815
|
+
2
|
|
816
|
+
sage: (A + B).det(algorithm='linbox')
|
|
817
|
+
2
|
|
818
|
+
sage: (A + B).det(algorithm='generic')
|
|
819
|
+
2
|
|
820
|
+
sage: (A + B).det(algorithm='hey')
|
|
821
|
+
Traceback (most recent call last):
|
|
822
|
+
...
|
|
823
|
+
ValueError: no algorithm 'hey'
|
|
824
|
+
|
|
825
|
+
sage: matrix(GF(11), 1, 2, sparse=True).det()
|
|
826
|
+
Traceback (most recent call last):
|
|
827
|
+
...
|
|
828
|
+
ValueError: self must be a square matrix
|
|
829
|
+
|
|
830
|
+
TESTS::
|
|
831
|
+
|
|
832
|
+
sage: matrix(GF(3), 0, sparse=True).det(algorithm='generic')
|
|
833
|
+
1
|
|
834
|
+
sage: matrix(GF(3), 0, sparse=True).det(algorithm='linbox')
|
|
835
|
+
1
|
|
836
|
+
|
|
837
|
+
sage: for _ in range(100):
|
|
838
|
+
....: dim = randint(0, 50)
|
|
839
|
+
....: p = random_prime(10000)
|
|
840
|
+
....: M = MatrixSpace(GF(p), dim, sparse=True)
|
|
841
|
+
....: m = M.random_element()
|
|
842
|
+
....: det_linbox = m.det(algorithm='linbox')
|
|
843
|
+
....: det_generic = m.det(algorithm='generic')
|
|
844
|
+
....: assert parent(det_linbox) == m.base_ring()
|
|
845
|
+
....: assert parent(det_generic) == m.base_ring()
|
|
846
|
+
....: if det_linbox != det_generic:
|
|
847
|
+
....: print(m)
|
|
848
|
+
....: raise RuntimeError
|
|
849
|
+
"""
|
|
850
|
+
if self._nrows != self._ncols:
|
|
851
|
+
raise ValueError("self must be a square matrix")
|
|
852
|
+
|
|
853
|
+
if self._nrows == 0:
|
|
854
|
+
return self.base_ring().one()
|
|
855
|
+
|
|
856
|
+
d = self.fetch('det')
|
|
857
|
+
if d is not None:
|
|
858
|
+
return d
|
|
859
|
+
|
|
860
|
+
if algorithm is None or algorithm == "linbox":
|
|
861
|
+
r, d = self._rank_det_linbox()
|
|
862
|
+
self.cache('rank', r)
|
|
863
|
+
self.cache('det', d)
|
|
864
|
+
return d
|
|
865
|
+
|
|
866
|
+
if algorithm == 'generic':
|
|
867
|
+
d = Matrix_sparse.determinant(self)
|
|
868
|
+
self.cache('det', d)
|
|
869
|
+
return d
|
|
870
|
+
|
|
871
|
+
raise ValueError("no algorithm '%s'" % algorithm)
|