Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/matrix/matrix_generic_sparse.pyx
4048 views
1
r"""
2
Sparse Matrices over a general ring
3
4
EXAMPLES::
5
6
sage: R.<x> = PolynomialRing(QQ)
7
sage: M = MatrixSpace(QQ['x'],2,3,sparse=True); M
8
Full MatrixSpace of 2 by 3 sparse matrices over Univariate Polynomial Ring in x over Rational Field
9
sage: a = M(range(6)); a
10
[0 1 2]
11
[3 4 5]
12
sage: b = M([x^n for n in range(6)]); b
13
[ 1 x x^2]
14
[x^3 x^4 x^5]
15
sage: a * b.transpose()
16
[ 2*x^2 + x 2*x^5 + x^4]
17
[ 5*x^2 + 4*x + 3 5*x^5 + 4*x^4 + 3*x^3]
18
sage: pari(a)*pari(b.transpose())
19
[2*x^2 + x, 2*x^5 + x^4; 5*x^2 + 4*x + 3, 5*x^5 + 4*x^4 + 3*x^3]
20
sage: c = copy(b); c
21
[ 1 x x^2]
22
[x^3 x^4 x^5]
23
sage: c[0,0] = 5; c
24
[ 5 x x^2]
25
[x^3 x^4 x^5]
26
sage: b[0,0]
27
1
28
sage: c.dict()
29
{(0, 1): x, (1, 2): x^5, (0, 0): 5, (1, 0): x^3, (0, 2): x^2, (1, 1): x^4}
30
sage: c.list()
31
[5, x, x^2, x^3, x^4, x^5]
32
sage: c.rows()
33
[(5, x, x^2), (x^3, x^4, x^5)]
34
sage: TestSuite(c).run()
35
sage: d = c.change_ring(CC['x']); d
36
[5.00000000000000 x x^2]
37
[ x^3 x^4 x^5]
38
sage: latex(c)
39
\left(\begin{array}{rrr}
40
5 & x & x^{2} \\
41
x^{3} & x^{4} & x^{5}
42
\end{array}\right)
43
sage: c.sparse_rows()
44
[(5, x, x^2), (x^3, x^4, x^5)]
45
sage: d = c.dense_matrix(); d
46
[ 5 x x^2]
47
[x^3 x^4 x^5]
48
sage: parent(d)
49
Full MatrixSpace of 2 by 3 dense matrices over Univariate Polynomial Ring in x over Rational Field
50
sage: c.sparse_matrix() is c
51
True
52
sage: c.is_sparse()
53
True
54
"""
55
56
cimport matrix
57
cimport matrix_sparse
58
cimport sage.structure.element
59
from sage.structure.element cimport ModuleElement
60
61
import sage.misc.misc as misc
62
63
cdef class Matrix_generic_sparse(matrix_sparse.Matrix_sparse):
64
r"""
65
The ``Matrix_generic_sparse`` class derives from
66
``Matrix``, and defines functionality for sparse
67
matrices over any base ring. A generic sparse matrix is represented
68
using a dictionary with keys pairs `(i,j)` and values in
69
the base ring.
70
71
The values of the dictionary must never be zero.
72
"""
73
########################################################################
74
# LEVEL 1 functionality
75
# * __cinit__
76
# * __init__
77
# * __dealloc__
78
# * set_unsafe
79
# * get_unsafe
80
# * def _pickle
81
# * def _unpickle
82
########################################################################
83
def __cinit__(self, parent, entries=0, coerce=True, copy=True):
84
self._entries = {} # crucial so that pickling works
85
86
def __init__(self, parent, entries=None, coerce=True, copy=True):
87
cdef Py_ssize_t i, j
88
matrix.Matrix.__init__(self, parent)
89
90
R = self._base_ring
91
self._zero = R(0)
92
if not isinstance(entries, (list, dict)):
93
if entries is None:
94
x = R.zero_element()
95
else:
96
x = R(entries)
97
entries = {}
98
if x != self._zero:
99
if self._nrows != self._ncols:
100
raise TypeError, "scalar matrix must be square"
101
for i from 0 <= i < self._nrows:
102
entries[(int(i),int(i))] = x
103
104
if isinstance(entries, list) and len(entries) > 0 and \
105
hasattr(entries[0], "is_vector"):
106
entries = _convert_sparse_entries_to_dict(entries)
107
108
if isinstance(entries, list):
109
if len(entries) != self.nrows() * self.ncols():
110
raise TypeError, "entries has the wrong length"
111
x = entries
112
entries = {}
113
k = 0
114
for i from 0 <= i < self._nrows:
115
for j from 0 <= j < self._ncols:
116
if x[k] != 0:
117
entries[(int(i),int(j))] = x[k]
118
k = k + 1
119
copy = False
120
121
if not isinstance(entries, dict):
122
raise TypeError, "entries must be a dict"
123
124
if coerce:
125
try:
126
v = {}
127
for k, x in entries.iteritems():
128
w = R(x)
129
if w != 0:
130
v[k] = w
131
entries = v
132
except TypeError:
133
raise TypeError, "Unable to coerce entries to %s"%R
134
else:
135
if copy:
136
# Make a copy
137
entries = dict(entries)
138
for k in entries.keys():
139
if entries[k].is_zero():
140
del entries[k]
141
142
self._entries = entries
143
144
cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, value):
145
# TODO: maybe make faster with Python/C API
146
k = (int(i),int(j))
147
if value.is_zero():
148
try:
149
del self._entries[k]
150
except KeyError:
151
pass
152
else:
153
self._entries[k] = value
154
155
cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j):
156
# TODO: maybe make faster with Python/C API
157
try:
158
return self._entries[(int(i),int(j))]
159
except KeyError:
160
return self._zero
161
162
def _pickle(self):
163
version = 0
164
return self._entries, version
165
166
def _unpickle(self, data, int version):
167
"""
168
EXAMPLES::
169
170
sage: a = matrix([[1,10],[3,4]],sparse=True); a
171
[ 1 10]
172
[ 3 4]
173
sage: loads(dumps(a)) == a
174
True
175
"""
176
cdef Py_ssize_t i, j, k
177
178
if version == 0:
179
self._entries = data
180
self._zero = self._base_ring(0)
181
else:
182
raise RuntimeError, "unknown matrix version (=%s)"%version
183
184
def __richcmp__(matrix.Matrix self, right, int op): # always need for mysterious reasons.
185
return self._richcmp(right, op)
186
def __hash__(self):
187
return self._hash()
188
189
########################################################################
190
# LEVEL 2 functionality
191
# x * cdef _add_
192
# * cdef _mul_
193
# * cdef _cmp_c_impl
194
# * __neg__
195
# * __invert__
196
# x * __copy__
197
# * _multiply_classical
198
# x * _list -- copy of the list of underlying elements
199
# x * _dict -- copy of the sparse dictionary of underlying elements
200
########################################################################
201
202
cpdef ModuleElement _add_(self, ModuleElement _other):
203
"""
204
EXAMPLES::
205
206
sage: a = matrix([[1,10],[3,4]],sparse=True); a
207
[ 1 10]
208
[ 3 4]
209
sage: a+a
210
[ 2 20]
211
[ 6 8]
212
213
::
214
215
sage: a = matrix([[1,10,-5/3],[2/8,3,4]],sparse=True); a
216
[ 1 10 -5/3]
217
[ 1/4 3 4]
218
sage: a+a
219
[ 2 20 -10/3]
220
[ 1/2 6 8]
221
"""
222
# Compute the sum of two sparse matrices.
223
# This is complicated because of how we represent sparse matrices.
224
# Algorithm:
225
# 1. Sort both entry coefficient lists.
226
# 2. March through building a new list, adding when the two i,j are the same.
227
cdef Py_ssize_t i, j, len_v, len_w
228
cdef Matrix_generic_sparse other
229
other = <Matrix_generic_sparse> _other
230
v = self._entries.items()
231
v.sort()
232
w = other._entries.items()
233
w.sort()
234
s = {}
235
i = 0 # pointer into self
236
j = 0 # pointer into other
237
len_v = len(v) # could be sped up with Python/C API??
238
len_w = len(w)
239
while i < len_v and j < len_w:
240
vi = v[i][0]
241
wj = w[j][0]
242
if vi < wj:
243
s[vi] = v[i][1]
244
i = i + 1
245
elif vi > wj:
246
s[wj] = w[j][1]
247
j = j + 1
248
else: # equal
249
sm = v[i][1] + w[j][1]
250
if not sm.is_zero():
251
s[vi] = sm
252
i = i + 1
253
j = j + 1
254
while i < len(v):
255
s[v[i][0]] = v[i][1]
256
i = i + 1
257
while j < len(w):
258
s[w[j][0]] = w[j][1]
259
j = j + 1
260
261
cdef Matrix_generic_sparse A
262
A = Matrix_generic_sparse.__new__(Matrix_generic_sparse, self._parent, 0,0,0)
263
matrix.Matrix.__init__(A, self._parent)
264
A._entries = s
265
A._zero = self._zero
266
A._base_ring = self._base_ring
267
return A
268
269
def __copy__(self):
270
A = self.__class__(self._parent, self._entries, copy = True, coerce=False)
271
if self._subdivisions is not None:
272
A.subdivide(*self.subdivisions())
273
return A
274
275
276
def _list(self):
277
"""
278
Return all entries of self as a list of numbers of rows times
279
number of columns entries.
280
"""
281
x = self.fetch('list')
282
if not x is None:
283
return x
284
v = [self._base_ring(0)]*(self._nrows * self._ncols)
285
for ij, x in self._entries.iteritems():
286
i, j = ij # todo: optimize
287
v[i*self._ncols + j] = x
288
self.cache('list', v)
289
return v
290
291
def _dict(self):
292
"""
293
Return the underlying dictionary of self.
294
"""
295
return self._entries
296
297
########################################################################
298
# LEVEL 3 functionality -- matrix windows, etc.
299
########################################################################
300
301
def _nonzero_positions_by_row(self, copy=True):
302
x = self.fetch('nonzero_positions')
303
if x is not None:
304
if copy:
305
return list(x)
306
return x
307
308
v = self._entries.keys()
309
v.sort()
310
311
self.cache('nonzero_positions', v)
312
if copy:
313
return list(v)
314
315
return v
316
317
def _nonzero_positions_by_column(self, copy=True):
318
x = self.fetch('nonzero_positions_by_column')
319
if x is not None:
320
if copy:
321
return list(x)
322
return x
323
324
v = self._entries.keys()
325
v.sort(_cmp_backward)
326
327
self.cache('nonzero_positions_by_column', v)
328
if copy:
329
return list(v)
330
return v
331
332
######################
333
# Echelon support
334
######################
335
336
337
## def dense_matrix(self):
338
## import sage.matrix.matrix
339
## M = sage.matrix.matrix.MatrixSpace(self.base_ring(),
340
## self._nrows, self._ncols,
341
## sparse = False)(0)
342
## for i, j, x in self._entries
343
## M[i,j] = x
344
## return M
345
346
## def nonpivots(self):
347
## # We convert the pivots to a set so we have fast
348
## # inclusion testing
349
## X = set(self.pivots())
350
## # [j for j in xrange(self.ncols()) if not (j in X)]
351
## np = []
352
## for j in xrange(self.ncols()):
353
## if not (j in X):
354
## np.append(j)
355
## return np
356
357
## def matrix_from_nonpivot_columns(self):
358
## """
359
## The sparse matrix got by deleted all pivot columns.
360
## """
361
## return self.matrix_from_columns(self.nonpivots())
362
363
## def matrix_from_columns(self, columns):
364
## """
365
## Returns the sparse submatrix of self composed of the given
366
## list of columns.
367
368
## INPUT:
369
## columns -- list of int's
370
## OUTPUT:
371
## a sparse matrix.
372
## """
373
## # Let A be this matrix and let B denote this matrix with
374
## # the columns deleted.
375
## # ALGORITHM:
376
## # 1. Make a table that encodes the function
377
## # f : Z --> Z,
378
## # f(j) = column of B where the j-th column of A goes
379
## # 2. Build new list of entries and return resulting matrix.
380
## C = set(columns)
381
## X = []
382
## j = 0
383
## for i in xrange(self.ncols()):
384
## if i in C:
385
## X.append(j)
386
## j = j + 1
387
## else:
388
## X.append(-1) # column to be deleted.
389
## entries2 = []
390
## for i, j, x in self.entries():
391
## if j in C:
392
## entries2.append((i,X[j],x))
393
## return SparseMatrix(self.base_ring(), self.nrows(),
394
## len(C), entries2, coerce=False, sort=False)
395
396
## def matrix_from_rows(self, rows):
397
## """
398
## Returns the sparse submatrix of self composed of the given
399
## list of rows.
400
401
## INPUT:
402
## rows -- list of int's
403
## OUTPUT:
404
## a sparse matrix.
405
## """
406
## R = set(rows)
407
## if not R.issubset(set(xrange(self.nrows()))):
408
## raise ArithmeticError, "Invalid rows."
409
## X = []
410
## i = 0
411
## for j in xrange(self.nrows()):
412
## if j in R:
413
## X.append(i)
414
## i = i + 1
415
## else:
416
## X.append(-1) # row to be deleted.
417
## entries2 = []
418
## for i, j, x in self.entries():
419
## if i in R:
420
## entries2.append((X[i],j,x))
421
## return SparseMatrix(self.base_ring(), len(R),
422
## self.ncols(), entries2, coerce=False, sort=False)
423
424
425
## def transpose(self):
426
## """
427
## Returns the transpose of self.
428
## """
429
## entries2 = [] # [(j,i,x) for i,j,x in self.entries()]
430
## for i,j,x in self.entries():
431
## entries2.append((j,i,x))
432
## return SparseMatrix(self.base_ring(), self.ncols(),
433
## self.nrows(), entries2, coerce=False, sort=True)
434
435
## def __rmul__(self, left):
436
## return self.scalar_multiple(left)
437
438
## def scalar_multiple(self, left):
439
## R = self.base_ring()
440
## left = R(left)
441
## if left == R(1):
442
## return self
443
## if left == R(0):
444
## return SparseMatrix(R, self.nrows(), self.ncols(), coerce=False, sort=False)
445
446
## X = []
447
## for i, j, x in self.list():
448
## X.append((i,j,left*x))
449
## return SparseMatrix(self.base_ring(), self.nrows(),
450
## self.ncols(), X, coerce=False, sort=False)
451
452
453
####################################################################################
454
# Various helper functions
455
####################################################################################
456
import matrix_space
457
458
def Matrix_sparse_from_rows(X):
459
"""
460
INPUT:
461
462
463
- ``X`` - nonempty list of SparseVector rows
464
465
466
OUTPUT: Sparse_matrix with those rows.
467
468
EXAMPLES::
469
470
sage: V = VectorSpace(QQ,20,sparse=True)
471
sage: v = V(0)
472
sage: v[9] = 4
473
sage: from sage.matrix.matrix_generic_sparse import Matrix_sparse_from_rows
474
sage: Matrix_sparse_from_rows([v])
475
[0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0]
476
sage: Matrix_sparse_from_rows([v, v, v, V(0)])
477
[0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0]
478
[0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0]
479
[0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0]
480
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
481
"""
482
cdef Py_ssize_t i, j
483
484
if not isinstance(X, (list, tuple)):
485
raise TypeError, "X (=%s) must be a list or tuple"%X
486
if len(X) == 0:
487
raise ArithmeticError, "X must be nonempty"
488
entries = {}
489
R = X[0].base_ring()
490
ncols = X[0].degree()
491
for i from 0 <= i < len(X):
492
for j, x in X[i].iteritems():
493
entries[(i,j)] = x
494
M = matrix_space.MatrixSpace(R, len(X), ncols, sparse=True)
495
return M(entries, coerce=False, copy=False)
496
497
498
## def _sparse_dot_product(v, w):
499
## """
500
## INPUT:
501
## v and w are dictionaries with integer keys.
502
## """
503
## x = set(v.keys()).intersection(set(w.keys()))
504
## a = 0
505
## for k in x:
506
## a = a + v[k]*w[k]
507
## return a
508
509
cdef _convert_sparse_entries_to_dict(entries):
510
e = {}
511
for i in xrange(len(entries)):
512
for j, x in (entries[i].dict()).iteritems():
513
e[(i,j)] = x
514
return e
515
516
517
518
def _cmp_backward(x,y): # todo: speed up via Python/C API
519
cdef int c
520
# compare two 2-tuples, but in reverse order, i.e., second entry than first
521
c = cmp(x[1],y[1])
522
if c: return c
523
c = cmp(x[0],y[0])
524
if c: return c
525
return 0
526
527