Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/scipy/sparse/_dia.py: 16%
241 statements
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-23 06:43 +0000
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-23 06:43 +0000
1"""Sparse DIAgonal format"""
3__docformat__ = "restructuredtext en"
5__all__ = ['dia_array', 'dia_matrix', 'isspmatrix_dia']
7import numpy as np
9from ._matrix import spmatrix
10from ._base import issparse, _formats, _spbase, sparray
11from ._data import _data_matrix
12from ._sputils import (isshape, upcast_char, getdtype, get_sum_dtype, validateaxis, check_shape)
13from ._sparsetools import dia_matvec
16class _dia_base(_data_matrix):
17 _format = 'dia'
19 def __init__(self, arg1, shape=None, dtype=None, copy=False):
20 _data_matrix.__init__(self)
22 if issparse(arg1):
23 if arg1.format == "dia":
24 if copy:
25 arg1 = arg1.copy()
26 self.data = arg1.data
27 self.offsets = arg1.offsets
28 self._shape = check_shape(arg1.shape)
29 else:
30 if arg1.format == self.format and copy:
31 A = arg1.copy()
32 else:
33 A = arg1.todia()
34 self.data = A.data
35 self.offsets = A.offsets
36 self._shape = check_shape(A.shape)
37 elif isinstance(arg1, tuple):
38 if isshape(arg1):
39 # It's a tuple of matrix dimensions (M, N)
40 # create empty matrix
41 self._shape = check_shape(arg1)
42 self.data = np.zeros((0,0), getdtype(dtype, default=float))
43 idx_dtype = self._get_index_dtype(maxval=max(self.shape))
44 self.offsets = np.zeros((0), dtype=idx_dtype)
45 else:
46 try:
47 # Try interpreting it as (data, offsets)
48 data, offsets = arg1
49 except Exception as e:
50 raise ValueError('unrecognized form for dia_array constructor') from e
51 else:
52 if shape is None:
53 raise ValueError('expected a shape argument')
54 self.data = np.atleast_2d(np.array(arg1[0], dtype=dtype, copy=copy))
55 self.offsets = np.atleast_1d(np.array(arg1[1],
56 dtype=self._get_index_dtype(maxval=max(shape)),
57 copy=copy))
58 self._shape = check_shape(shape)
59 else:
60 #must be dense, convert to COO first, then to DIA
61 try:
62 arg1 = np.asarray(arg1)
63 except Exception as e:
64 raise ValueError("unrecognized form for"
65 " %s_matrix constructor" % self.format) from e
66 A = self._coo_container(arg1, dtype=dtype, shape=shape).todia()
67 self.data = A.data
68 self.offsets = A.offsets
69 self._shape = check_shape(A.shape)
71 if dtype is not None:
72 self.data = self.data.astype(dtype)
74 #check format
75 if self.offsets.ndim != 1:
76 raise ValueError('offsets array must have rank 1')
78 if self.data.ndim != 2:
79 raise ValueError('data array must have rank 2')
81 if self.data.shape[0] != len(self.offsets):
82 raise ValueError('number of diagonals (%d) '
83 'does not match the number of offsets (%d)'
84 % (self.data.shape[0], len(self.offsets)))
86 if len(np.unique(self.offsets)) != len(self.offsets):
87 raise ValueError('offset array contains duplicate values')
89 def __repr__(self):
90 format = _formats[self.getformat()][1]
91 return "<%dx%d sparse matrix of type '%s'\n" \
92 "\twith %d stored elements (%d diagonals) in %s format>" % \
93 (self.shape + (self.dtype.type, self.nnz, self.data.shape[0],
94 format))
96 def _data_mask(self):
97 """Returns a mask of the same shape as self.data, where
98 mask[i,j] is True when data[i,j] corresponds to a stored element."""
99 num_rows, num_cols = self.shape
100 offset_inds = np.arange(self.data.shape[1])
101 row = offset_inds - self.offsets[:,None]
102 mask = (row >= 0)
103 mask &= (row < num_rows)
104 mask &= (offset_inds < num_cols)
105 return mask
107 def count_nonzero(self):
108 mask = self._data_mask()
109 return np.count_nonzero(self.data[mask])
111 def _getnnz(self, axis=None):
112 if axis is not None:
113 raise NotImplementedError("_getnnz over an axis is not implemented "
114 "for DIA format")
115 M,N = self.shape
116 nnz = 0
117 for k in self.offsets:
118 if k > 0:
119 nnz += min(M,N-k)
120 else:
121 nnz += min(M+k,N)
122 return int(nnz)
124 _getnnz.__doc__ = _spbase._getnnz.__doc__
125 count_nonzero.__doc__ = _spbase.count_nonzero.__doc__
127 def sum(self, axis=None, dtype=None, out=None):
128 validateaxis(axis)
130 if axis is not None and axis < 0:
131 axis += 2
133 res_dtype = get_sum_dtype(self.dtype)
134 num_rows, num_cols = self.shape
135 ret = None
137 if axis == 0:
138 mask = self._data_mask()
139 x = (self.data * mask).sum(axis=0)
140 if x.shape[0] == num_cols:
141 res = x
142 else:
143 res = np.zeros(num_cols, dtype=x.dtype)
144 res[:x.shape[0]] = x
145 ret = self._ascontainer(res, dtype=res_dtype)
147 else:
148 row_sums = np.zeros((num_rows, 1), dtype=res_dtype)
149 one = np.ones(num_cols, dtype=res_dtype)
150 dia_matvec(num_rows, num_cols, len(self.offsets),
151 self.data.shape[1], self.offsets, self.data, one, row_sums)
153 row_sums = self._ascontainer(row_sums)
155 if axis is None:
156 return row_sums.sum(dtype=dtype, out=out)
158 ret = self._ascontainer(row_sums.sum(axis=axis))
160 if out is not None and out.shape != ret.shape:
161 raise ValueError("dimensions do not match")
163 return ret.sum(axis=(), dtype=dtype, out=out)
165 sum.__doc__ = _spbase.sum.__doc__
167 def _add_sparse(self, other):
169 # Check if other is also of type dia_array
170 if not isinstance(other, type(self)):
171 # If other is not of type dia_array, default to
172 # converting to csr_matrix, as is done in the _add_sparse
173 # method of parent class _spbase
174 return self.tocsr()._add_sparse(other)
176 # The task is to compute m = self + other
177 # Start by making a copy of self, of the datatype
178 # that should result from adding self and other
179 dtype = np.promote_types(self.dtype, other.dtype)
180 m = self.astype(dtype, copy=True)
182 # Then, add all the stored diagonals of other.
183 for d in other.offsets:
184 # Check if the diagonal has already been added.
185 if d in m.offsets:
186 # If the diagonal is already there, we need to take
187 # the sum of the existing and the new
188 m.setdiag(m.diagonal(d) + other.diagonal(d), d)
189 else:
190 m.setdiag(other.diagonal(d), d)
191 return m
193 def _mul_vector(self, other):
194 x = other
196 y = np.zeros(self.shape[0], dtype=upcast_char(self.dtype.char,
197 x.dtype.char))
199 L = self.data.shape[1]
201 M,N = self.shape
203 dia_matvec(M,N, len(self.offsets), L, self.offsets, self.data, x.ravel(), y.ravel())
205 return y
207 def _mul_multimatrix(self, other):
208 return np.hstack([self._mul_vector(col).reshape(-1,1) for col in other.T])
210 def _setdiag(self, values, k=0):
211 M, N = self.shape
213 if values.ndim == 0:
214 # broadcast
215 values_n = np.inf
216 else:
217 values_n = len(values)
219 if k < 0:
220 n = min(M + k, N, values_n)
221 min_index = 0
222 max_index = n
223 else:
224 n = min(M, N - k, values_n)
225 min_index = k
226 max_index = k + n
228 if values.ndim != 0:
229 # allow also longer sequences
230 values = values[:n]
232 data_rows, data_cols = self.data.shape
233 if k in self.offsets:
234 if max_index > data_cols:
235 data = np.zeros((data_rows, max_index), dtype=self.data.dtype)
236 data[:, :data_cols] = self.data
237 self.data = data
238 self.data[self.offsets == k, min_index:max_index] = values
239 else:
240 self.offsets = np.append(self.offsets, self.offsets.dtype.type(k))
241 m = max(max_index, data_cols)
242 data = np.zeros((data_rows + 1, m), dtype=self.data.dtype)
243 data[:-1, :data_cols] = self.data
244 data[-1, min_index:max_index] = values
245 self.data = data
247 def todia(self, copy=False):
248 if copy:
249 return self.copy()
250 else:
251 return self
253 todia.__doc__ = _spbase.todia.__doc__
255 def transpose(self, axes=None, copy=False):
256 if axes is not None and axes != (1, 0):
257 raise ValueError("Sparse arrays/matrices do not support "
258 "an 'axes' parameter because swapping "
259 "dimensions is the only logical permutation.")
261 num_rows, num_cols = self.shape
262 max_dim = max(self.shape)
264 # flip diagonal offsets
265 offsets = -self.offsets
267 # re-align the data matrix
268 r = np.arange(len(offsets), dtype=np.intc)[:, None]
269 c = np.arange(num_rows, dtype=np.intc) - (offsets % max_dim)[:, None]
270 pad_amount = max(0, max_dim-self.data.shape[1])
271 data = np.hstack((self.data, np.zeros((self.data.shape[0], pad_amount),
272 dtype=self.data.dtype)))
273 data = data[r, c]
274 return self._dia_container((data, offsets), shape=(
275 num_cols, num_rows), copy=copy)
277 transpose.__doc__ = _spbase.transpose.__doc__
279 def diagonal(self, k=0):
280 rows, cols = self.shape
281 if k <= -rows or k >= cols:
282 return np.empty(0, dtype=self.data.dtype)
283 idx, = np.nonzero(self.offsets == k)
284 first_col = max(0, k)
285 last_col = min(rows + k, cols)
286 result_size = last_col - first_col
287 if idx.size == 0:
288 return np.zeros(result_size, dtype=self.data.dtype)
289 result = self.data[idx[0], first_col:last_col]
290 padding = result_size - len(result)
291 if padding > 0:
292 result = np.pad(result, (0, padding), mode='constant')
293 return result
295 diagonal.__doc__ = _spbase.diagonal.__doc__
297 def tocsc(self, copy=False):
298 if self.nnz == 0:
299 return self._csc_container(self.shape, dtype=self.dtype)
301 num_rows, num_cols = self.shape
302 num_offsets, offset_len = self.data.shape
303 offset_inds = np.arange(offset_len)
305 row = offset_inds - self.offsets[:,None]
306 mask = (row >= 0)
307 mask &= (row < num_rows)
308 mask &= (offset_inds < num_cols)
309 mask &= (self.data != 0)
311 idx_dtype = self._get_index_dtype(maxval=max(self.shape))
312 indptr = np.zeros(num_cols + 1, dtype=idx_dtype)
313 indptr[1:offset_len+1] = np.cumsum(mask.sum(axis=0)[:num_cols])
314 if offset_len < num_cols:
315 indptr[offset_len+1:] = indptr[offset_len]
316 indices = row.T[mask.T].astype(idx_dtype, copy=False)
317 data = self.data.T[mask.T]
318 return self._csc_container((data, indices, indptr), shape=self.shape,
319 dtype=self.dtype)
321 tocsc.__doc__ = _spbase.tocsc.__doc__
323 def tocoo(self, copy=False):
324 num_rows, num_cols = self.shape
325 num_offsets, offset_len = self.data.shape
326 offset_inds = np.arange(offset_len)
328 row = offset_inds - self.offsets[:,None]
329 mask = (row >= 0)
330 mask &= (row < num_rows)
331 mask &= (offset_inds < num_cols)
332 mask &= (self.data != 0)
333 row = row[mask]
334 col = np.tile(offset_inds, num_offsets)[mask.ravel()]
335 data = self.data[mask]
336 # Note: this cannot set has_canonical_format=True, because despite the
337 # lack of duplicates, we do not generate sorted indices.
338 return self._coo_container(
339 (data, (row, col)), shape=self.shape, dtype=self.dtype, copy=False
340 )
342 tocoo.__doc__ = _spbase.tocoo.__doc__
344 # needed by _data_matrix
345 def _with_data(self, data, copy=True):
346 """Returns a matrix with the same sparsity structure as self,
347 but with different data. By default the structure arrays are copied.
348 """
349 if copy:
350 return self._dia_container(
351 (data, self.offsets.copy()), shape=self.shape
352 )
353 else:
354 return self._dia_container(
355 (data, self.offsets), shape=self.shape
356 )
358 def resize(self, *shape):
359 shape = check_shape(shape)
360 M, N = shape
361 # we do not need to handle the case of expanding N
362 self.data = self.data[:, :N]
364 if (M > self.shape[0] and
365 np.any(self.offsets + self.shape[0] < self.data.shape[1])):
366 # explicitly clear values that were previously hidden
367 mask = (self.offsets[:, None] + self.shape[0] <=
368 np.arange(self.data.shape[1]))
369 self.data[mask] = 0
371 self._shape = shape
373 resize.__doc__ = _spbase.resize.__doc__
376def isspmatrix_dia(x):
377 """Is `x` of dia_matrix type?
379 Parameters
380 ----------
381 x
382 object to check for being a dia matrix
384 Returns
385 -------
386 bool
387 True if `x` is a dia matrix, False otherwise
389 Examples
390 --------
391 >>> from scipy.sparse import dia_array, dia_matrix, coo_matrix, isspmatrix_dia
392 >>> isspmatrix_dia(dia_matrix([[5]]))
393 True
394 >>> isspmatrix_dia(dia_array([[5]]))
395 False
396 >>> isspmatrix_dia(coo_matrix([[5]]))
397 False
398 """
399 return isinstance(x, dia_matrix)
402# This namespace class separates array from matrix with isinstance
403class dia_array(_dia_base, sparray):
404 """
405 Sparse array with DIAgonal storage.
407 This can be instantiated in several ways:
408 dia_array(D)
409 where D is a 2-D ndarray
411 dia_array(S)
412 with another sparse array or matrix S (equivalent to S.todia())
414 dia_array((M, N), [dtype])
415 to construct an empty array with shape (M, N),
416 dtype is optional, defaulting to dtype='d'.
418 dia_array((data, offsets), shape=(M, N))
419 where the ``data[k,:]`` stores the diagonal entries for
420 diagonal ``offsets[k]`` (See example below)
422 Attributes
423 ----------
424 dtype : dtype
425 Data type of the array
426 shape : 2-tuple
427 Shape of the array
428 ndim : int
429 Number of dimensions (this is always 2)
430 nnz
431 size
432 data
433 DIA format data array of the array
434 offsets
435 DIA format offset array of the array
436 T
438 Notes
439 -----
441 Sparse arrays can be used in arithmetic operations: they support
442 addition, subtraction, multiplication, division, and matrix power.
444 Examples
445 --------
447 >>> import numpy as np
448 >>> from scipy.sparse import dia_array
449 >>> dia_array((3, 4), dtype=np.int8).toarray()
450 array([[0, 0, 0, 0],
451 [0, 0, 0, 0],
452 [0, 0, 0, 0]], dtype=int8)
454 >>> data = np.array([[1, 2, 3, 4]]).repeat(3, axis=0)
455 >>> offsets = np.array([0, -1, 2])
456 >>> dia_array((data, offsets), shape=(4, 4)).toarray()
457 array([[1, 0, 3, 0],
458 [1, 2, 0, 4],
459 [0, 2, 3, 0],
460 [0, 0, 3, 4]])
462 >>> from scipy.sparse import dia_array
463 >>> n = 10
464 >>> ex = np.ones(n)
465 >>> data = np.array([ex, 2 * ex, ex])
466 >>> offsets = np.array([-1, 0, 1])
467 >>> dia_array((data, offsets), shape=(n, n)).toarray()
468 array([[2., 1., 0., ..., 0., 0., 0.],
469 [1., 2., 1., ..., 0., 0., 0.],
470 [0., 1., 2., ..., 0., 0., 0.],
471 ...,
472 [0., 0., 0., ..., 2., 1., 0.],
473 [0., 0., 0., ..., 1., 2., 1.],
474 [0., 0., 0., ..., 0., 1., 2.]])
475 """
478class dia_matrix(spmatrix, _dia_base):
479 """
480 Sparse matrix with DIAgonal storage.
482 This can be instantiated in several ways:
483 dia_matrix(D)
484 where D is a 2-D ndarray
486 dia_matrix(S)
487 with another sparse array or matrix S (equivalent to S.todia())
489 dia_matrix((M, N), [dtype])
490 to construct an empty matrix with shape (M, N),
491 dtype is optional, defaulting to dtype='d'.
493 dia_matrix((data, offsets), shape=(M, N))
494 where the ``data[k,:]`` stores the diagonal entries for
495 diagonal ``offsets[k]`` (See example below)
497 Attributes
498 ----------
499 dtype : dtype
500 Data type of the matrix
501 shape : 2-tuple
502 Shape of the matrix
503 ndim : int
504 Number of dimensions (this is always 2)
505 nnz
506 size
507 data
508 DIA format data array of the matrix
509 offsets
510 DIA format offset array of the matrix
511 T
513 Notes
514 -----
516 Sparse matrices can be used in arithmetic operations: they support
517 addition, subtraction, multiplication, division, and matrix power.
519 Examples
520 --------
522 >>> import numpy as np
523 >>> from scipy.sparse import dia_matrix
524 >>> dia_matrix((3, 4), dtype=np.int8).toarray()
525 array([[0, 0, 0, 0],
526 [0, 0, 0, 0],
527 [0, 0, 0, 0]], dtype=int8)
529 >>> data = np.array([[1, 2, 3, 4]]).repeat(3, axis=0)
530 >>> offsets = np.array([0, -1, 2])
531 >>> dia_matrix((data, offsets), shape=(4, 4)).toarray()
532 array([[1, 0, 3, 0],
533 [1, 2, 0, 4],
534 [0, 2, 3, 0],
535 [0, 0, 3, 4]])
537 >>> from scipy.sparse import dia_matrix
538 >>> n = 10
539 >>> ex = np.ones(n)
540 >>> data = np.array([ex, 2 * ex, ex])
541 >>> offsets = np.array([-1, 0, 1])
542 >>> dia_matrix((data, offsets), shape=(n, n)).toarray()
543 array([[2., 1., 0., ..., 0., 0., 0.],
544 [1., 2., 1., ..., 0., 0., 0.],
545 [0., 1., 2., ..., 0., 0., 0.],
546 ...,
547 [0., 0., 0., ..., 2., 1., 0.],
548 [0., 0., 0., ..., 1., 2., 1.],
549 [0., 0., 0., ..., 0., 1., 2.]])
550 """