Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/matrix/matrix_integer_sparse.pyx
8817 views
1
"""
2
Sparse integer matrices.
3
4
AUTHORS:
5
-- William Stein (2007-02-21)
6
-- Soroosh Yazdani (2007-02-21)
7
8
TESTS:
9
sage: a = matrix(ZZ,2,range(4), sparse=True)
10
sage: TestSuite(a).run()
11
sage: Matrix(ZZ,0,0,sparse=True).inverse()
12
[]
13
"""
14
15
##############################################################################
16
# Copyright (C) 2007 William Stein <[email protected]>
17
# Distributed under the terms of the GNU General Public License (GPL)
18
# The full text of the GPL is available at:
19
# http://www.gnu.org/licenses/
20
##############################################################################
21
22
include 'sage/modules/binary_search.pxi'
23
include 'sage/modules/vector_integer_sparse_h.pxi'
24
include 'sage/modules/vector_integer_sparse_c.pxi'
25
include 'sage/modules/vector_modn_sparse_h.pxi'
26
include 'sage/modules/vector_modn_sparse_c.pxi'
27
from cpython.sequence cimport *
28
29
include 'sage/ext/stdsage.pxi'
30
31
from sage.rings.integer cimport Integer
32
from matrix cimport Matrix
33
34
from matrix_modn_sparse cimport Matrix_modn_sparse
35
from sage.structure.element cimport ModuleElement, RingElement, Element, Vector
36
37
import matrix_space
38
39
from sage.rings.integer_ring import ZZ
40
from sage.rings.finite_rings.integer_mod_ring import IntegerModRing
41
42
43
cdef class Matrix_integer_sparse(matrix_sparse.Matrix_sparse):
44
45
########################################################################
46
# LEVEL 1 functionality
47
# * __cinit__
48
# * __dealloc__
49
# * __init__
50
# * set_unsafe
51
# * get_unsafe
52
# * __richcmp__ -- always the same
53
# * __hash__ -- always simple
54
########################################################################
55
def __cinit__(self, parent, entries, copy, coerce):
56
self._initialized = False
57
# set the parent, nrows, ncols, etc.
58
matrix_sparse.Matrix_sparse.__init__(self, parent)
59
60
self._matrix = <mpz_vector*> sage_malloc(parent.nrows()*sizeof(mpz_vector))
61
if self._matrix == NULL:
62
raise MemoryError("error allocating sparse matrix")
63
64
# initialize the rows
65
for i from 0 <= i < parent.nrows():
66
mpz_vector_init(&self._matrix[i], self._ncols, 0)
67
# record that rows have been initialized
68
self._initialized = True
69
70
def __dealloc__(self):
71
cdef Py_ssize_t i
72
if self._initialized:
73
for i from 0 <= i < self._nrows:
74
mpz_vector_clear(&self._matrix[i])
75
sage_free(self._matrix)
76
77
def __init__(self, parent, entries, copy, coerce):
78
"""
79
Create a sparse matrix over the integers.
80
81
INPUT:
82
parent -- a matrix space
83
entries -- * a Python list of triples (i,j,x), where 0 <= i < nrows,
84
0 <= j < ncols, and x is coercible to an int. The i,j
85
entry of self is set to x. The x's can be 0.
86
* Alternatively, entries can be a list of *all* the entries
87
of the sparse matrix (so they would be mostly 0).
88
copy -- ignored
89
coerce -- ignored
90
"""
91
cdef Py_ssize_t i, j, k
92
cdef Integer z
93
cdef PyObject** X
94
95
# fill in entries in the dict case
96
if entries is None: return
97
if isinstance(entries, dict):
98
R = self.base_ring()
99
for ij, x in entries.iteritems():
100
z = R(x)
101
if z != 0:
102
i, j = ij # nothing better to do since this is user input, which may be bogus.
103
if i < 0 or j < 0 or i >= self._nrows or j >= self._ncols:
104
raise IndexError("invalid entries list")
105
mpz_vector_set_entry(&self._matrix[i], j, z.value)
106
107
elif isinstance(entries, list):
108
109
# Dense input format -- fill in entries
110
if len(entries) != self._nrows * self._ncols:
111
raise TypeError("list of entries must be a dictionary of (i,j):x or a dense list of n * m elements")
112
seq = PySequence_Fast(entries,"expected a list")
113
X = PySequence_Fast_ITEMS(seq)
114
k = 0
115
R = self.base_ring()
116
# Get fast access to the entries list.
117
for i from 0 <= i < self._nrows:
118
for j from 0 <= j < self._ncols:
119
z = R(<object>X[k])
120
if z != 0:
121
mpz_vector_set_entry(&self._matrix[i], j, z.value)
122
k = k + 1
123
124
else:
125
126
# fill in entries in the scalar case
127
z = Integer(entries)
128
if z == 0:
129
return
130
if self._nrows != self._ncols:
131
raise TypeError("matrix must be square to initialize with a scalar.")
132
for i from 0 <= i < self._nrows:
133
mpz_vector_set_entry(&self._matrix[i], i, z.value)
134
135
136
cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, x):
137
mpz_vector_set_entry(&self._matrix[i], j, (<Integer> x).value)
138
139
cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j):
140
cdef Integer x
141
x = Integer()
142
mpz_vector_get_entry(&x.value, &self._matrix[i], j)
143
return x
144
145
def __richcmp__(Matrix self, right, int op): # always need for mysterious reasons.
146
return self._richcmp(right, op)
147
148
def __hash__(self):
149
return self._hash()
150
151
152
########################################################################
153
# LEVEL 2 functionality
154
# * def _pickle
155
# * def _unpickle
156
# * cdef _add_
157
# * cdef _sub_
158
# * cdef _mul_
159
# * cdef _cmp_c_impl
160
# * __neg__
161
# * __invert__
162
# * __copy__
163
# * _multiply_classical
164
# * _matrix_times_matrix_
165
# * _list -- list of underlying elements (need not be a copy)
166
# * x _dict -- sparse dictionary of underlying elements (need not be a copy)
167
########################################################################
168
# def _pickle(self):
169
# def _unpickle(self, data, int version): # use version >= 0
170
# cpdef ModuleElement _add_(self, ModuleElement right):
171
# cdef _mul_(self, Matrix right):
172
# cdef int _cmp_c_impl(self, Matrix right) except -2:
173
# def __neg__(self):
174
# def __invert__(self):
175
# def __copy__(self):
176
# def _multiply_classical(left, matrix.Matrix _right):
177
# def _list(self):
178
179
cpdef ModuleElement _lmul_(self, RingElement right):
180
"""
181
EXAMPLES::
182
183
sage: a = matrix(ZZ,2,range(6), sparse=True)
184
sage: 3 * a
185
[ 0 3 6]
186
[ 9 12 15]
187
"""
188
cdef Py_ssize_t i
189
cdef mpz_vector* self_row, *M_row
190
cdef Matrix_integer_sparse M
191
cdef Integer _x
192
_x = Integer(right)
193
M = Matrix_integer_sparse.__new__(Matrix_integer_sparse, self._parent, None, None, None)
194
for i from 0 <= i < self._nrows:
195
self_row = &self._matrix[i]
196
M_row = &M._matrix[i]
197
mpz_vector_scalar_multiply(M_row, self_row, _x.value)
198
return M
199
200
cpdef ModuleElement _add_(self, ModuleElement right):
201
cdef Py_ssize_t i, j
202
cdef mpz_vector* self_row, *M_row
203
cdef Matrix_integer_sparse M
204
205
M = Matrix_integer_sparse.__new__(Matrix_integer_sparse, self._parent, None, None, None)
206
cdef mpz_t mul
207
mpz_init_set_si(mul,1)
208
for i from 0 <= i < self._nrows:
209
mpz_vector_clear(&M._matrix[i])
210
add_mpz_vector_init(&M._matrix[i], &self._matrix[i], &(<Matrix_integer_sparse>right)._matrix[i], mul)
211
mpz_clear(mul)
212
return M
213
214
cpdef ModuleElement _sub_(self, ModuleElement right):
215
cdef Py_ssize_t i, j
216
cdef mpz_vector* self_row, *M_row
217
cdef Matrix_integer_sparse M
218
219
M = Matrix_integer_sparse.__new__(Matrix_integer_sparse, self._parent, None, None, None)
220
cdef mpz_t mul
221
mpz_init_set_si(mul,-1)
222
for i from 0 <= i < self._nrows:
223
mpz_vector_clear(&M._matrix[i])
224
add_mpz_vector_init(&M._matrix[i], &self._matrix[i], &(<Matrix_integer_sparse>right)._matrix[i], mul)
225
mpz_clear(mul)
226
return M
227
228
def _dict(self):
229
"""
230
Unsafe version of the dict method, mainly for internal use.
231
This may return the dict of elements, but as an *unsafe*
232
reference to the underlying dict of the object. It might
233
be dangerous if you change entries of the returned dict.
234
"""
235
d = self.fetch('dict')
236
if not d is None:
237
return d
238
239
cdef Py_ssize_t i, j, k
240
d = {}
241
for i from 0 <= i < self._nrows:
242
for j from 0 <= j < self._matrix[i].num_nonzero:
243
x = Integer()
244
mpz_set((<Integer>x).value, self._matrix[i].entries[j])
245
d[(int(i),int(self._matrix[i].positions[j]))] = x
246
self.cache('dict', d)
247
return d
248
249
########################################################################
250
# LEVEL 3 functionality (Optional)
251
# * cdef _sub_
252
# * __deepcopy__
253
# * __invert__
254
# * Matrix windows -- only if you need strassen for that base
255
# * Other functions (list them here):
256
########################################################################
257
258
def _nonzero_positions_by_row(self, copy=True):
259
"""
260
Returns the list of pairs (i,j) such that self[i,j] != 0.
261
262
It is safe to change the resulting list (unless you give the option copy=False).
263
264
EXAMPLE::
265
sage: M = Matrix(ZZ, [[0,0,0,1,0,0,0,0],[0,1,0,0,0,0,1,0]], sparse=True); M
266
[0 0 0 1 0 0 0 0]
267
[0 1 0 0 0 0 1 0]
268
sage: M._nonzero_positions_by_row()
269
[(0, 3), (1, 1), (1, 6)]
270
271
"""
272
x = self.fetch('nonzero_positions')
273
if not x is None:
274
if copy:
275
return list(x)
276
return x
277
nzp = []
278
cdef Py_ssize_t i, j
279
for i from 0 <= i < self._nrows:
280
for j from 0 <= j < self._matrix[i].num_nonzero:
281
nzp.append((i,self._matrix[i].positions[j]))
282
self.cache('nonzero_positions', nzp)
283
if copy:
284
return list(nzp)
285
return nzp
286
287
def _nonzero_positions_by_column(self, copy=True):
288
"""
289
Returns the list of pairs (i,j) such that self[i,j] != 0, but
290
sorted by columns, i.e., column j=0 entries occur first, then
291
column j=1 entries, etc.
292
293
It is safe to change the resulting list (unless you give the option copy=False).
294
295
EXAMPLE::
296
sage: M = Matrix(ZZ, [[0,0,0,1,0,0,0,0],[0,1,0,0,0,0,1,0]], sparse=True); M
297
[0 0 0 1 0 0 0 0]
298
[0 1 0 0 0 0 1 0]
299
sage: M._nonzero_positions_by_column()
300
[(1, 1), (0, 3), (1, 6)]
301
302
"""
303
x = self.fetch('nonzero_positions_by_column')
304
if not x is None:
305
if copy:
306
return list(x)
307
return x
308
nzc = [[] for _ in xrange(self._ncols)]
309
cdef Py_ssize_t i, j
310
for i from 0 <= i < self._nrows:
311
for j from 0 <= j < self._matrix[i].num_nonzero:
312
p = self._matrix[i].positions[j]
313
nzc[p].append((i,p))
314
nzc = sum(nzc,[])
315
self.cache('nonzero_positions_by_column', nzc)
316
if copy:
317
return list(nzc)
318
return nzc
319
320
def _mod_int(self, modulus):
321
"""
322
Helper function in reducing matrices mod n.
323
324
INPUT:
325
326
- `modulus` - a number
327
328
OUTPUT:
329
330
This matrix, over `ZZ/nZZ`.
331
332
TESTS::
333
334
sage: M = Matrix(ZZ, sparse=True)
335
sage: B = M._mod_int(7)
336
sage: B.parent()
337
Full MatrixSpace of 0 by 0 sparse matrices over Ring of integers modulo 7
338
339
"""
340
return self._mod_int_c(modulus)
341
342
cdef _mod_int_c(self, mod_int p):
343
cdef Py_ssize_t i, j
344
cdef Matrix_modn_sparse res
345
cdef mpz_vector* self_row
346
cdef c_vector_modint* res_row
347
res = Matrix_modn_sparse.__new__(Matrix_modn_sparse, matrix_space.MatrixSpace(
348
IntegerModRing(p), self._nrows, self._ncols, sparse=True), None, None, None)
349
for i from 0 <= i < self._nrows:
350
self_row = &(self._matrix[i])
351
res_row = &(res.rows[i])
352
for j from 0 <= j < self_row.num_nonzero:
353
set_entry(res_row, self_row.positions[j], mpz_fdiv_ui(self_row.entries[j], p))
354
return res
355
356
357
def rational_reconstruction(self, N):
358
"""
359
Use rational reconstruction to lift self to a matrix over the
360
rational numbers (if possible), where we view self as a matrix
361
modulo N.
362
363
EXAMPLES:
364
sage: A = matrix(ZZ, 3, 4, [(1/3)%500, 2, 3, (-4)%500, 7, 2, 2, 3, 4, 3, 4, (5/7)%500], sparse=True)
365
sage: A.rational_reconstruction(500)
366
[1/3 2 3 -4]
367
[ 7 2 2 3]
368
[ 4 3 4 5/7]
369
370
TEST:
371
372
Check that ticket #9345 is fixed::
373
374
sage: A = random_matrix(ZZ, 3, 3, sparse = True)
375
sage: A.rational_reconstruction(0)
376
Traceback (most recent call last):
377
...
378
ZeroDivisionError: The modulus cannot be zero
379
"""
380
import misc
381
return misc.matrix_integer_sparse_rational_reconstruction(self, N)
382
383
def _linbox_sparse(self):
384
cdef Py_ssize_t i, j
385
v = ['%s %s M'%(self._nrows, self._ncols)]
386
d = self._dict()
387
for ij, x in d.iteritems():
388
v.append('%s %s %s'%(ij[0]+1,ij[1]+1,x))
389
v.append('0 0 0\n')
390
return '\n'.join(v)
391
392
def _right_kernel_matrix(self, **kwds):
393
r"""
394
Returns a pair that includes a matrix of basis vectors
395
for the right kernel of ``self``.
396
397
INPUT:
398
399
- ``algorithm`` - determines which algorithm to use, options are:
400
401
- 'pari' - use the ``matkerint()`` function from the PARI library
402
- 'padic' - use the p-adic algorithm from the IML library
403
- 'default' - use a heuristic to decide which of the two above
404
routines is fastest. This is the default value.
405
406
- ``proof`` - this is passed to the p-adic IML algorithm.
407
If not specified, the global flag for linear algebra will be used.
408
409
OUTPUT:
410
411
Returns a pair. First item is the string is either
412
'computed-pari-int' or 'computed-iml-int', which identifies
413
the nature of the basis vectors.
414
415
Second item is a matrix whose rows are a basis for the right kernel,
416
over the integers, as computed by either the IML or PARI libraries.
417
418
EXAMPLES::
419
420
sage: A = matrix(ZZ, [[4, 7, 9, 7, 5, 0],
421
... [1, 0, 5, 8, 9, 1],
422
... [0, 1, 0, 1, 9, 7],
423
... [4, 7, 6, 5, 1, 4]],
424
... sparse = True)
425
426
sage: result = A._right_kernel_matrix(algorithm='pari')
427
sage: result[0]
428
'computed-pari-int'
429
sage: X = result[1]; X
430
[-26 31 -30 21 2 -10]
431
[-47 -13 48 -14 -11 18]
432
sage: A*X.transpose() == zero_matrix(ZZ, 4, 2)
433
True
434
435
sage: result = A._right_kernel_matrix(algorithm='padic')
436
sage: result[0]
437
'computed-iml-int'
438
sage: X = result[1]; X
439
[-469 214 -30 119 -37 0]
440
[ 370 -165 18 -91 30 -2]
441
sage: A*X.transpose() == zero_matrix(ZZ, 4, 2)
442
True
443
444
sage: result = A._right_kernel_matrix(algorithm='default')
445
sage: result[0]
446
'computed-pari-int'
447
sage: result[1]
448
[-26 31 -30 21 2 -10]
449
[-47 -13 48 -14 -11 18]
450
451
sage: result = A._right_kernel_matrix()
452
sage: result[0]
453
'computed-pari-int'
454
sage: result[1]
455
[-26 31 -30 21 2 -10]
456
[-47 -13 48 -14 -11 18]
457
458
With the 'default' given as the algorithm, several heuristics are
459
used to determine if PARI or IML ('padic') is used. The code has
460
exact details, but roughly speaking, relevant factors are: the
461
absolute size of the matrix, or the relative dimensions, or the
462
magnitude of the entries. ::
463
464
sage: A = random_matrix(ZZ, 18, 11, sparse=True)
465
sage: A._right_kernel_matrix(algorithm='default')[0]
466
'computed-pari-int'
467
sage: A = random_matrix(ZZ, 18, 11, x = 10^200, sparse=True)
468
sage: A._right_kernel_matrix(algorithm='default')[0]
469
'computed-iml-int'
470
sage: A = random_matrix(ZZ, 60, 60, sparse=True)
471
sage: A._right_kernel_matrix(algorithm='default')[0]
472
'computed-iml-int'
473
sage: A = random_matrix(ZZ, 60, 55, sparse=True)
474
sage: A._right_kernel_matrix(algorithm='default')[0]
475
'computed-pari-int'
476
477
TESTS:
478
479
We test three trivial cases. PARI is used for small matrices,
480
but we let the heuristic decide that. ::
481
482
sage: A = matrix(ZZ, 0, 2, sparse=True)
483
sage: A._right_kernel_matrix()[1]
484
[1 0]
485
[0 1]
486
sage: A = matrix(ZZ, 2, 0, sparse=True)
487
sage: A._right_kernel_matrix()[1].parent()
488
Full MatrixSpace of 0 by 0 dense matrices over Integer Ring
489
sage: A = zero_matrix(ZZ, 4, 3, sparse=True)
490
sage: A._right_kernel_matrix()[1]
491
[1 0 0]
492
[0 1 0]
493
[0 0 1]
494
"""
495
return self.dense_matrix()._right_kernel_matrix(**kwds)
496
497
hermite_form = Matrix.echelon_form
498
499
def elementary_divisors(self, algorithm='pari'):
500
"""
501
Return the elementary divisors of self, in order.
502
503
The elementary divisors are the invariants of the finite
504
abelian group that is the cokernel of *left* multiplication by
505
this matrix. They are ordered in reverse by divisibility.
506
507
INPUT:
508
self -- matrix
509
algorithm -- (default: 'pari')
510
'pari': works robustly, but is slower.
511
'linbox' -- use linbox (currently off, broken)
512
513
OUTPUT:
514
list of integers
515
516
EXAMPLES:
517
sage: matrix(3, range(9),sparse=True).elementary_divisors()
518
[1, 3, 0]
519
sage: M = matrix(ZZ, 3, [1,5,7, 3,6,9, 0,1,2], sparse=True)
520
sage: M.elementary_divisors()
521
[1, 1, 6]
522
523
This returns a copy, which is safe to change:
524
sage: edivs = M.elementary_divisors()
525
sage: edivs.pop()
526
6
527
sage: M.elementary_divisors()
528
[1, 1, 6]
529
530
SEE ALSO: smith_form
531
"""
532
return self.dense_matrix().elementary_divisors(algorithm=algorithm)
533
534
def smith_form(self):
535
r"""
536
Returns matrices S, U, and V such that S = U\*self\*V, and S is in
537
Smith normal form. Thus S is diagonal with diagonal entries the
538
ordered elementary divisors of S.
539
540
This version is for sparse matrices and simply makes the matrix
541
dense and calls the version for dense integer matrices.
542
543
.. warning::
544
545
The elementary_divisors function, which returns the
546
diagonal entries of S, is VASTLY faster than this function.
547
548
The elementary divisors are the invariants of the finite abelian
549
group that is the cokernel of this matrix. They are ordered in
550
reverse by divisibility.
551
552
EXAMPLES::
553
554
sage: A = MatrixSpace(IntegerRing(), 3, sparse=True)(range(9))
555
sage: D, U, V = A.smith_form()
556
sage: D
557
[1 0 0]
558
[0 3 0]
559
[0 0 0]
560
sage: U
561
[ 0 1 0]
562
[ 0 -1 1]
563
[-1 2 -1]
564
sage: V
565
[-1 4 1]
566
[ 1 -3 -2]
567
[ 0 0 1]
568
sage: U*A*V
569
[1 0 0]
570
[0 3 0]
571
[0 0 0]
572
573
It also makes sense for nonsquare matrices::
574
575
sage: A = Matrix(ZZ,3,2,range(6), sparse=True)
576
sage: D, U, V = A.smith_form()
577
sage: D
578
[1 0]
579
[0 2]
580
[0 0]
581
sage: U
582
[ 0 1 0]
583
[ 0 -1 1]
584
[-1 2 -1]
585
sage: V
586
[-1 3]
587
[ 1 -2]
588
sage: U * A * V
589
[1 0]
590
[0 2]
591
[0 0]
592
593
The examples above show that Trac ticket #10626 has been implemented.
594
595
596
.. seealso::
597
598
:meth:`elementary_divisors`
599
"""
600
return self.dense_matrix().smith_form()
601
602