Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/scipy/sparse/_base.py: 29%
534 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"""Base class for sparse matrices"""
2from warnings import warn
4import numpy as np
5from scipy._lib._util import VisibleDeprecationWarning
7from ._sputils import (asmatrix, check_reshape_kwargs, check_shape,
8 get_sum_dtype, isdense, isscalarlike,
9 matrix, validateaxis,)
11from ._matrix import spmatrix
13__all__ = ['isspmatrix', 'issparse', 'sparray',
14 'SparseWarning', 'SparseEfficiencyWarning']
17class SparseWarning(Warning):
18 pass
21class SparseFormatWarning(SparseWarning):
22 pass
25class SparseEfficiencyWarning(SparseWarning):
26 pass
29# The formats that we might potentially understand.
30_formats = {'csc': [0, "Compressed Sparse Column"],
31 'csr': [1, "Compressed Sparse Row"],
32 'dok': [2, "Dictionary Of Keys"],
33 'lil': [3, "List of Lists"],
34 'dod': [4, "Dictionary of Dictionaries"],
35 'sss': [5, "Symmetric Sparse Skyline"],
36 'coo': [6, "COOrdinate"],
37 'lba': [7, "Linpack BAnded"],
38 'egd': [8, "Ellpack-itpack Generalized Diagonal"],
39 'dia': [9, "DIAgonal"],
40 'bsr': [10, "Block Sparse Row"],
41 'msr': [11, "Modified compressed Sparse Row"],
42 'bsc': [12, "Block Sparse Column"],
43 'msc': [13, "Modified compressed Sparse Column"],
44 'ssk': [14, "Symmetric SKyline"],
45 'nsk': [15, "Nonsymmetric SKyline"],
46 'jad': [16, "JAgged Diagonal"],
47 'uss': [17, "Unsymmetric Sparse Skyline"],
48 'vbr': [18, "Variable Block Row"],
49 'und': [19, "Undefined"]
50 }
53# These univariate ufuncs preserve zeros.
54_ufuncs_with_fixed_point_at_zero = frozenset([
55 np.sin, np.tan, np.arcsin, np.arctan, np.sinh, np.tanh, np.arcsinh,
56 np.arctanh, np.rint, np.sign, np.expm1, np.log1p, np.deg2rad,
57 np.rad2deg, np.floor, np.ceil, np.trunc, np.sqrt])
60MAXPRINT = 50
63class _spbase:
64 """ This class provides a base class for all sparse arrays. It
65 cannot be instantiated. Most of the work is provided by subclasses.
66 """
68 __array_priority__ = 10.1
69 _format = 'und' # undefined
70 ndim = 2
72 @property
73 def _bsr_container(self):
74 from ._bsr import bsr_array
75 return bsr_array
77 @property
78 def _coo_container(self):
79 from ._coo import coo_array
80 return coo_array
82 @property
83 def _csc_container(self):
84 from ._csc import csc_array
85 return csc_array
87 @property
88 def _csr_container(self):
89 from ._csr import csr_array
90 return csr_array
92 @property
93 def _dia_container(self):
94 from ._dia import dia_array
95 return dia_array
97 @property
98 def _dok_container(self):
99 from ._dok import dok_array
100 return dok_array
102 @property
103 def _lil_container(self):
104 from ._lil import lil_array
105 return lil_array
107 def __init__(self, maxprint=MAXPRINT):
108 self._shape = None
109 if self.__class__.__name__ == '_spbase':
110 raise ValueError("This class is not intended"
111 " to be instantiated directly.")
112 self.maxprint = maxprint
114 # Use this in 1.13.0 and later:
115 #
116 # @property
117 # def shape(self):
118 # return self._shape
120 def reshape(self, *args, **kwargs):
121 """reshape(self, shape, order='C', copy=False)
123 Gives a new shape to a sparse array/matrix without changing its data.
125 Parameters
126 ----------
127 shape : length-2 tuple of ints
128 The new shape should be compatible with the original shape.
129 order : {'C', 'F'}, optional
130 Read the elements using this index order. 'C' means to read and
131 write the elements using C-like index order; e.g., read entire first
132 row, then second row, etc. 'F' means to read and write the elements
133 using Fortran-like index order; e.g., read entire first column, then
134 second column, etc.
135 copy : bool, optional
136 Indicates whether or not attributes of self should be copied
137 whenever possible. The degree to which attributes are copied varies
138 depending on the type of sparse array being used.
140 Returns
141 -------
142 reshaped : sparse array/matrix
143 A sparse array/matrix with the given `shape`, not necessarily of the same
144 format as the current object.
146 See Also
147 --------
148 numpy.reshape : NumPy's implementation of 'reshape' for ndarrays
149 """
150 # If the shape already matches, don't bother doing an actual reshape
151 # Otherwise, the default is to convert to COO and use its reshape
152 shape = check_shape(args, self.shape)
153 order, copy = check_reshape_kwargs(kwargs)
154 if shape == self.shape:
155 if copy:
156 return self.copy()
157 else:
158 return self
160 return self.tocoo(copy=copy).reshape(shape, order=order, copy=False)
162 def resize(self, shape):
163 """Resize the array/matrix in-place to dimensions given by ``shape``
165 Any elements that lie within the new shape will remain at the same
166 indices, while non-zero elements lying outside the new shape are
167 removed.
169 Parameters
170 ----------
171 shape : (int, int)
172 number of rows and columns in the new array/matrix
174 Notes
175 -----
176 The semantics are not identical to `numpy.ndarray.resize` or
177 `numpy.resize`. Here, the same data will be maintained at each index
178 before and after reshape, if that index is within the new bounds. In
179 numpy, resizing maintains contiguity of the array, moving elements
180 around in the logical array but not within a flattened representation.
182 We give no guarantees about whether the underlying data attributes
183 (arrays, etc.) will be modified in place or replaced with new objects.
184 """
185 # As an inplace operation, this requires implementation in each format.
186 raise NotImplementedError(
187 f'{type(self).__name__}.resize is not implemented')
189 def astype(self, dtype, casting='unsafe', copy=True):
190 """Cast the array/matrix elements to a specified type.
192 Parameters
193 ----------
194 dtype : string or numpy dtype
195 Typecode or data-type to which to cast the data.
196 casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, optional
197 Controls what kind of data casting may occur.
198 Defaults to 'unsafe' for backwards compatibility.
199 'no' means the data types should not be cast at all.
200 'equiv' means only byte-order changes are allowed.
201 'safe' means only casts which can preserve values are allowed.
202 'same_kind' means only safe casts or casts within a kind,
203 like float64 to float32, are allowed.
204 'unsafe' means any data conversions may be done.
205 copy : bool, optional
206 If `copy` is `False`, the result might share some memory with this
207 array/matrix. If `copy` is `True`, it is guaranteed that the result and
208 this array/matrix do not share any memory.
209 """
211 dtype = np.dtype(dtype)
212 if self.dtype != dtype:
213 return self.tocsr().astype(
214 dtype, casting=casting, copy=copy).asformat(self.format)
215 elif copy:
216 return self.copy()
217 else:
218 return self
220 @classmethod
221 def _ascontainer(cls, X, **kwargs):
222 if issubclass(cls, sparray):
223 return np.asarray(X, **kwargs)
224 else:
225 return asmatrix(X, **kwargs)
227 @classmethod
228 def _container(cls, X, **kwargs):
229 if issubclass(cls, sparray):
230 return np.array(X, **kwargs)
231 else:
232 return matrix(X, **kwargs)
234 def _asfptype(self):
235 """Upcast array to a floating point format (if necessary)"""
237 fp_types = ['f', 'd', 'F', 'D']
239 if self.dtype.char in fp_types:
240 return self
241 else:
242 for fp_type in fp_types:
243 if self.dtype <= np.dtype(fp_type):
244 return self.astype(fp_type)
246 raise TypeError('cannot upcast [%s] to a floating '
247 'point format' % self.dtype.name)
249 def __iter__(self):
250 for r in range(self.shape[0]):
251 yield self[r, :]
253 def _getmaxprint(self):
254 """Maximum number of elements to display when printed."""
255 return self.maxprint
257 def count_nonzero(self):
258 """Number of non-zero entries, equivalent to
260 np.count_nonzero(a.toarray())
262 Unlike the nnz property, which return the number of stored
263 entries (the length of the data attribute), this method counts the
264 actual number of non-zero entries in data.
265 """
266 raise NotImplementedError("count_nonzero not implemented for %s." %
267 self.__class__.__name__)
269 def _getnnz(self, axis=None):
270 """Number of stored values, including explicit zeros.
272 Parameters
273 ----------
274 axis : None, 0, or 1
275 Select between the number of values across the whole array, in
276 each column, or in each row.
278 See also
279 --------
280 count_nonzero : Number of non-zero entries
281 """
282 raise NotImplementedError("getnnz not implemented for %s." %
283 self.__class__.__name__)
285 @property
286 def nnz(self) -> int:
287 """Number of stored values, including explicit zeros.
289 See also
290 --------
291 count_nonzero : Number of non-zero entries
292 """
293 return self._getnnz()
295 @property
296 def size(self) -> int:
297 """Number of stored values.
299 See also
300 --------
301 count_nonzero : Number of non-zero values.
302 """
303 return self._getnnz()
305 @property
306 def format(self) -> str:
307 """Format string for matrix."""
308 return self._format
310 @property
311 def A(self) -> np.ndarray:
312 """DEPRECATED: Return a dense array.
314 .. deprecated:: 1.11.0
316 `.A` is deprecated and will be removed in v1.13.0.
317 Use `.toarray()` instead.
318 """
319 if isinstance(self, sparray):
320 warn(VisibleDeprecationWarning(
321 "`.A` is deprecated and will be removed in v1.13.0. "
322 "Use `.toarray()` instead."
323 ))
324 return self.toarray()
326 @property
327 def T(self):
328 """Transpose."""
329 return self.transpose()
331 @property
332 def H(self):
333 """DEPRECATED: Returns the (complex) conjugate transpose.
335 .. deprecated:: 1.11.0
337 `.H` is deprecated and will be removed in v1.13.0.
338 Please use `.T.conjugate()` instead.
339 """
340 if isinstance(self, sparray):
341 warn(VisibleDeprecationWarning(
342 "`.H` is deprecated and will be removed in v1.13.0. "
343 "Please use `.T.conjugate()` instead."
344 ))
345 return self.T.conjugate()
347 @property
348 def real(self):
349 return self._real()
351 @property
352 def imag(self):
353 return self._imag()
355 def __repr__(self):
356 _, format_name = _formats[self.format]
357 sparse_cls = 'array' if isinstance(self, sparray) else 'matrix'
358 return f"<%dx%d sparse {sparse_cls} of type '%s'\n" \
359 "\twith %d stored elements in %s format>" % \
360 (self.shape + (self.dtype.type, self.nnz, format_name))
362 def __str__(self):
363 maxprint = self._getmaxprint()
365 A = self.tocoo()
367 # helper function, outputs "(i,j) v"
368 def tostr(row, col, data):
369 triples = zip(list(zip(row, col)), data)
370 return '\n'.join([(' %s\t%s' % t) for t in triples])
372 if self.nnz > maxprint:
373 half = maxprint // 2
374 out = tostr(A.row[:half], A.col[:half], A.data[:half])
375 out += "\n :\t:\n"
376 half = maxprint - maxprint//2
377 out += tostr(A.row[-half:], A.col[-half:], A.data[-half:])
378 else:
379 out = tostr(A.row, A.col, A.data)
381 return out
383 def __bool__(self): # Simple -- other ideas?
384 if self.shape == (1, 1):
385 return self.nnz != 0
386 else:
387 raise ValueError("The truth value of an array with more than one "
388 "element is ambiguous. Use a.any() or a.all().")
389 __nonzero__ = __bool__
391 # What should len(sparse) return? For consistency with dense matrices,
392 # perhaps it should be the number of rows? But for some uses the number of
393 # non-zeros is more important. For now, raise an exception!
394 def __len__(self):
395 raise TypeError("sparse array length is ambiguous; use getnnz()"
396 " or shape[0]")
398 def asformat(self, format, copy=False):
399 """Return this array/matrix in the passed format.
401 Parameters
402 ----------
403 format : {str, None}
404 The desired sparse format ("csr", "csc", "lil", "dok", "array", ...)
405 or None for no conversion.
406 copy : bool, optional
407 If True, the result is guaranteed to not share data with self.
409 Returns
410 -------
411 A : This array/matrix in the passed format.
412 """
413 if format is None or format == self.format:
414 if copy:
415 return self.copy()
416 else:
417 return self
418 else:
419 try:
420 convert_method = getattr(self, 'to' + format)
421 except AttributeError as e:
422 raise ValueError(f'Format {format} is unknown.') from e
424 # Forward the copy kwarg, if it's accepted.
425 try:
426 return convert_method(copy=copy)
427 except TypeError:
428 return convert_method()
430 ###################################################################
431 # NOTE: All arithmetic operations use csr_matrix by default.
432 # Therefore a new sparse array format just needs to define a
433 # .tocsr() method to provide arithmetic support. Any of these
434 # methods can be overridden for efficiency.
435 ####################################################################
437 def multiply(self, other):
438 """Point-wise multiplication by another array/matrix."""
439 return self.tocsr().multiply(other)
441 def maximum(self, other):
442 """Element-wise maximum between this and another array/matrix."""
443 return self.tocsr().maximum(other)
445 def minimum(self, other):
446 """Element-wise minimum between this and another array/matrix."""
447 return self.tocsr().minimum(other)
449 def dot(self, other):
450 """Ordinary dot product
452 Examples
453 --------
454 >>> import numpy as np
455 >>> from scipy.sparse import csr_array
456 >>> A = csr_array([[1, 2, 0], [0, 0, 3], [4, 0, 5]])
457 >>> v = np.array([1, 0, -1])
458 >>> A.dot(v)
459 array([ 1, -3, -1], dtype=int64)
461 """
462 if np.isscalar(other):
463 return self * other
464 else:
465 return self @ other
467 def power(self, n, dtype=None):
468 """Element-wise power."""
469 return self.tocsr().power(n, dtype=dtype)
471 def __eq__(self, other):
472 return self.tocsr().__eq__(other)
474 def __ne__(self, other):
475 return self.tocsr().__ne__(other)
477 def __lt__(self, other):
478 return self.tocsr().__lt__(other)
480 def __gt__(self, other):
481 return self.tocsr().__gt__(other)
483 def __le__(self, other):
484 return self.tocsr().__le__(other)
486 def __ge__(self, other):
487 return self.tocsr().__ge__(other)
489 def __abs__(self):
490 return abs(self.tocsr())
492 def __round__(self, ndigits=0):
493 return round(self.tocsr(), ndigits=ndigits)
495 def _add_sparse(self, other):
496 return self.tocsr()._add_sparse(other)
498 def _add_dense(self, other):
499 return self.tocoo()._add_dense(other)
501 def _sub_sparse(self, other):
502 return self.tocsr()._sub_sparse(other)
504 def _sub_dense(self, other):
505 return self.todense() - other
507 def _rsub_dense(self, other):
508 # note: this can't be replaced by other + (-self) for unsigned types
509 return other - self.todense()
511 def __add__(self, other): # self + other
512 if isscalarlike(other):
513 if other == 0:
514 return self.copy()
515 # Now we would add this scalar to every element.
516 raise NotImplementedError('adding a nonzero scalar to a '
517 'sparse array is not supported')
518 elif issparse(other):
519 if other.shape != self.shape:
520 raise ValueError("inconsistent shapes")
521 return self._add_sparse(other)
522 elif isdense(other):
523 other = np.broadcast_to(other, self.shape)
524 return self._add_dense(other)
525 else:
526 return NotImplemented
528 def __radd__(self,other): # other + self
529 return self.__add__(other)
531 def __sub__(self, other): # self - other
532 if isscalarlike(other):
533 if other == 0:
534 return self.copy()
535 raise NotImplementedError('subtracting a nonzero scalar from a '
536 'sparse array is not supported')
537 elif issparse(other):
538 if other.shape != self.shape:
539 raise ValueError("inconsistent shapes")
540 return self._sub_sparse(other)
541 elif isdense(other):
542 other = np.broadcast_to(other, self.shape)
543 return self._sub_dense(other)
544 else:
545 return NotImplemented
547 def __rsub__(self,other): # other - self
548 if isscalarlike(other):
549 if other == 0:
550 return -self.copy()
551 raise NotImplementedError('subtracting a sparse array from a '
552 'nonzero scalar is not supported')
553 elif isdense(other):
554 other = np.broadcast_to(other, self.shape)
555 return self._rsub_dense(other)
556 else:
557 return NotImplemented
559 def _mul_dispatch(self, other):
560 """`np.matrix`-compatible mul, i.e. `dot` or `NotImplemented`
562 interpret other and call one of the following
563 self._mul_scalar()
564 self._mul_vector()
565 self._mul_multivector()
566 self._mul_sparse_matrix()
567 """
568 # This method has to be different from `__matmul__` because it is also
569 # called by sparse matrix classes.
571 M, N = self.shape
573 if other.__class__ is np.ndarray:
574 # Fast path for the most common case
575 if other.shape == (N,):
576 return self._mul_vector(other)
577 elif other.shape == (N, 1):
578 return self._mul_vector(other.ravel()).reshape(M, 1)
579 elif other.ndim == 2 and other.shape[0] == N:
580 return self._mul_multivector(other)
582 if isscalarlike(other):
583 # scalar value
584 return self._mul_scalar(other)
586 if issparse(other):
587 if self.shape[1] != other.shape[0]:
588 raise ValueError('dimension mismatch')
589 return self._mul_sparse_matrix(other)
591 # If it's a list or whatever, treat it like an array
592 other_a = np.asanyarray(other)
594 if other_a.ndim == 0 and other_a.dtype == np.object_:
595 # Not interpretable as an array; return NotImplemented so that
596 # other's __rmul__ can kick in if that's implemented.
597 return NotImplemented
599 try:
600 other.shape
601 except AttributeError:
602 other = other_a
604 if other.ndim == 1 or other.ndim == 2 and other.shape[1] == 1:
605 # dense row or column vector
606 if other.shape != (N,) and other.shape != (N, 1):
607 raise ValueError('dimension mismatch')
609 result = self._mul_vector(np.ravel(other))
611 if isinstance(other, np.matrix):
612 result = self._ascontainer(result)
614 if other.ndim == 2 and other.shape[1] == 1:
615 # If 'other' was an (nx1) column vector, reshape the result
616 result = result.reshape(-1, 1)
618 return result
620 elif other.ndim == 2:
621 ##
622 # dense 2D array or matrix ("multivector")
624 if other.shape[0] != self.shape[1]:
625 raise ValueError('dimension mismatch')
627 result = self._mul_multivector(np.asarray(other))
629 if isinstance(other, np.matrix):
630 result = self._ascontainer(result)
632 return result
634 else:
635 raise ValueError('could not interpret dimensions')
637 def __mul__(self, *args, **kwargs):
638 return self.multiply(*args, **kwargs)
640 # by default, use CSR for __mul__ handlers
641 def _mul_scalar(self, other):
642 return self.tocsr()._mul_scalar(other)
644 def _mul_vector(self, other):
645 return self.tocsr()._mul_vector(other)
647 def _mul_multivector(self, other):
648 return self.tocsr()._mul_multivector(other)
650 def _mul_sparse_matrix(self, other):
651 return self.tocsr()._mul_sparse_matrix(other)
653 def _rmul_dispatch(self, other):
654 if isscalarlike(other):
655 return self._mul_scalar(other)
656 else:
657 # Don't use asarray unless we have to
658 try:
659 tr = other.transpose()
660 except AttributeError:
661 tr = np.asarray(other).transpose()
662 ret = self.transpose()._mul_dispatch(tr)
663 if ret is NotImplemented:
664 return NotImplemented
665 return ret.transpose()
667 def __rmul__(self, *args, **kwargs): # other * self
668 return self.multiply(*args, **kwargs)
670 #######################
671 # matmul (@) operator #
672 #######################
674 def __matmul__(self, other):
675 if isscalarlike(other):
676 raise ValueError("Scalar operands are not allowed, "
677 "use '*' instead")
678 return self._mul_dispatch(other)
680 def __rmatmul__(self, other):
681 if isscalarlike(other):
682 raise ValueError("Scalar operands are not allowed, "
683 "use '*' instead")
684 return self._rmul_dispatch(other)
686 ####################
687 # Other Arithmetic #
688 ####################
690 def _divide(self, other, true_divide=False, rdivide=False):
691 if isscalarlike(other):
692 if rdivide:
693 if true_divide:
694 return np.true_divide(other, self.todense())
695 else:
696 return np.divide(other, self.todense())
698 if true_divide and np.can_cast(self.dtype, np.float64):
699 return self.astype(np.float64)._mul_scalar(1./other)
700 else:
701 r = self._mul_scalar(1./other)
703 scalar_dtype = np.asarray(other).dtype
704 if (np.issubdtype(self.dtype, np.integer) and
705 np.issubdtype(scalar_dtype, np.integer)):
706 return r.astype(self.dtype)
707 else:
708 return r
710 elif isdense(other):
711 if not rdivide:
712 if true_divide:
713 recip = np.true_divide(1., other)
714 else:
715 recip = np.divide(1., other)
716 return self.multiply(recip)
717 else:
718 if true_divide:
719 return np.true_divide(other, self.todense())
720 else:
721 return np.divide(other, self.todense())
722 elif issparse(other):
723 if rdivide:
724 return other._divide(self, true_divide, rdivide=False)
726 self_csr = self.tocsr()
727 if true_divide and np.can_cast(self.dtype, np.float64):
728 return self_csr.astype(np.float64)._divide_sparse(other)
729 else:
730 return self_csr._divide_sparse(other)
731 else:
732 return NotImplemented
734 def __truediv__(self, other):
735 return self._divide(other, true_divide=True)
737 def __div__(self, other):
738 # Always do true division
739 return self._divide(other, true_divide=True)
741 def __rtruediv__(self, other):
742 # Implementing this as the inverse would be too magical -- bail out
743 return NotImplemented
745 def __rdiv__(self, other):
746 # Implementing this as the inverse would be too magical -- bail out
747 return NotImplemented
749 def __neg__(self):
750 return -self.tocsr()
752 def __iadd__(self, other):
753 return NotImplemented
755 def __isub__(self, other):
756 return NotImplemented
758 def __imul__(self, other):
759 return NotImplemented
761 def __idiv__(self, other):
762 return self.__itruediv__(other)
764 def __itruediv__(self, other):
765 return NotImplemented
767 def __pow__(self, *args, **kwargs):
768 return self.power(*args, **kwargs)
770 def transpose(self, axes=None, copy=False):
771 """
772 Reverses the dimensions of the sparse array/matrix.
774 Parameters
775 ----------
776 axes : None, optional
777 This argument is in the signature *solely* for NumPy
778 compatibility reasons. Do not pass in anything except
779 for the default value.
780 copy : bool, optional
781 Indicates whether or not attributes of `self` should be
782 copied whenever possible. The degree to which attributes
783 are copied varies depending on the type of sparse array/matrix
784 being used.
786 Returns
787 -------
788 p : `self` with the dimensions reversed.
790 See Also
791 --------
792 numpy.transpose : NumPy's implementation of 'transpose' for ndarrays
793 """
794 return self.tocsr(copy=copy).transpose(axes=axes, copy=False)
796 def conjugate(self, copy=True):
797 """Element-wise complex conjugation.
799 If the array/matrix is of non-complex data type and `copy` is False,
800 this method does nothing and the data is not copied.
802 Parameters
803 ----------
804 copy : bool, optional
805 If True, the result is guaranteed to not share data with self.
807 Returns
808 -------
809 A : The element-wise complex conjugate.
811 """
812 if np.issubdtype(self.dtype, np.complexfloating):
813 return self.tocsr(copy=copy).conjugate(copy=False)
814 elif copy:
815 return self.copy()
816 else:
817 return self
819 def conj(self, copy=True):
820 return self.conjugate(copy=copy)
822 conj.__doc__ = conjugate.__doc__
824 def _real(self):
825 return self.tocsr()._real()
827 def _imag(self):
828 return self.tocsr()._imag()
830 def nonzero(self):
831 """Nonzero indices of the array/matrix.
833 Returns a tuple of arrays (row,col) containing the indices
834 of the non-zero elements of the array.
836 Examples
837 --------
838 >>> from scipy.sparse import csr_array
839 >>> A = csr_array([[1,2,0],[0,0,3],[4,0,5]])
840 >>> A.nonzero()
841 (array([0, 0, 1, 2, 2]), array([0, 1, 2, 0, 2]))
843 """
845 # convert to COOrdinate format
846 A = self.tocoo()
847 nz_mask = A.data != 0
848 return (A.row[nz_mask], A.col[nz_mask])
850 def _getcol(self, j):
851 """Returns a copy of column j of the array, as an (m x 1) sparse
852 array (column vector).
853 """
854 # Subclasses should override this method for efficiency.
855 # Post-multiply by a (n x 1) column vector 'a' containing all zeros
856 # except for a_j = 1
857 n = self.shape[1]
858 if j < 0:
859 j += n
860 if j < 0 or j >= n:
861 raise IndexError("index out of bounds")
862 col_selector = self._csc_container(([1], [[j], [0]]),
863 shape=(n, 1), dtype=self.dtype)
864 return self @ col_selector
866 def _getrow(self, i):
867 """Returns a copy of row i of the array, as a (1 x n) sparse
868 array (row vector).
869 """
870 # Subclasses should override this method for efficiency.
871 # Pre-multiply by a (1 x m) row vector 'a' containing all zeros
872 # except for a_i = 1
873 m = self.shape[0]
874 if i < 0:
875 i += m
876 if i < 0 or i >= m:
877 raise IndexError("index out of bounds")
878 row_selector = self._csr_container(([1], [[0], [i]]),
879 shape=(1, m), dtype=self.dtype)
880 return row_selector @ self
882 # The following dunder methods cannot be implemented.
883 #
884 # def __array__(self):
885 # # Sparse matrices rely on NumPy wrapping them in object arrays under
886 # # the hood to make unary ufuncs work on them. So we cannot raise
887 # # TypeError here - which would be handy to not give users object
888 # # arrays they probably don't want (they're looking for `.toarray()`).
889 # #
890 # # Conversion with `toarray()` would also break things because of the
891 # # behavior discussed above, plus we want to avoid densification by
892 # # accident because that can too easily blow up memory.
893 #
894 # def __array_ufunc__(self):
895 # # We cannot implement __array_ufunc__ due to mismatching semantics.
896 # # See gh-7707 and gh-7349 for details.
897 #
898 # def __array_function__(self):
899 # # We cannot implement __array_function__ due to mismatching semantics.
900 # # See gh-10362 for details.
902 def todense(self, order=None, out=None):
903 """
904 Return a dense representation of this sparse array/matrix.
906 Parameters
907 ----------
908 order : {'C', 'F'}, optional
909 Whether to store multi-dimensional data in C (row-major)
910 or Fortran (column-major) order in memory. The default
911 is 'None', which provides no ordering guarantees.
912 Cannot be specified in conjunction with the `out`
913 argument.
915 out : ndarray, 2-D, optional
916 If specified, uses this array (or `numpy.matrix`) as the
917 output buffer instead of allocating a new array to
918 return. The provided array must have the same shape and
919 dtype as the sparse array/matrix on which you are calling the
920 method.
922 Returns
923 -------
924 arr : numpy.matrix, 2-D
925 A NumPy matrix object with the same shape and containing
926 the same data represented by the sparse array/matrix, with the
927 requested memory order. If `out` was passed and was an
928 array (rather than a `numpy.matrix`), it will be filled
929 with the appropriate values and returned wrapped in a
930 `numpy.matrix` object that shares the same memory.
931 """
932 return self._ascontainer(self.toarray(order=order, out=out))
934 def toarray(self, order=None, out=None):
935 """
936 Return a dense ndarray representation of this sparse array/matrix.
938 Parameters
939 ----------
940 order : {'C', 'F'}, optional
941 Whether to store multidimensional data in C (row-major)
942 or Fortran (column-major) order in memory. The default
943 is 'None', which provides no ordering guarantees.
944 Cannot be specified in conjunction with the `out`
945 argument.
947 out : ndarray, 2-D, optional
948 If specified, uses this array as the output buffer
949 instead of allocating a new array to return. The provided
950 array must have the same shape and dtype as the sparse
951 array/matrix on which you are calling the method. For most
952 sparse types, `out` is required to be memory contiguous
953 (either C or Fortran ordered).
955 Returns
956 -------
957 arr : ndarray, 2-D
958 An array with the same shape and containing the same
959 data represented by the sparse array/matrix, with the requested
960 memory order. If `out` was passed, the same object is
961 returned after being modified in-place to contain the
962 appropriate values.
963 """
964 return self.tocoo(copy=False).toarray(order=order, out=out)
966 # Any sparse array format deriving from _spbase must define one of
967 # tocsr or tocoo. The other conversion methods may be implemented for
968 # efficiency, but are not required.
969 def tocsr(self, copy=False):
970 """Convert this array/matrix to Compressed Sparse Row format.
972 With copy=False, the data/indices may be shared between this array/matrix and
973 the resultant csr_array/matrix.
974 """
975 return self.tocoo(copy=copy).tocsr(copy=False)
977 def todok(self, copy=False):
978 """Convert this array/matrix to Dictionary Of Keys format.
980 With copy=False, the data/indices may be shared between this array/matrix and
981 the resultant dok_array/matrix.
982 """
983 return self.tocoo(copy=copy).todok(copy=False)
985 def tocoo(self, copy=False):
986 """Convert this array/matrix to COOrdinate format.
988 With copy=False, the data/indices may be shared between this array/matrix and
989 the resultant coo_array/matrix.
990 """
991 return self.tocsr(copy=False).tocoo(copy=copy)
993 def tolil(self, copy=False):
994 """Convert this array/matrix to List of Lists format.
996 With copy=False, the data/indices may be shared between this array/matrix and
997 the resultant lil_array/matrix.
998 """
999 return self.tocsr(copy=False).tolil(copy=copy)
1001 def todia(self, copy=False):
1002 """Convert this array/matrix to sparse DIAgonal format.
1004 With copy=False, the data/indices may be shared between this array/matrix and
1005 the resultant dia_array/matrix.
1006 """
1007 return self.tocoo(copy=copy).todia(copy=False)
1009 def tobsr(self, blocksize=None, copy=False):
1010 """Convert this array/matrix to Block Sparse Row format.
1012 With copy=False, the data/indices may be shared between this array/matrix and
1013 the resultant bsr_array/matrix.
1015 When blocksize=(R, C) is provided, it will be used for construction of
1016 the bsr_array/matrix.
1017 """
1018 return self.tocsr(copy=False).tobsr(blocksize=blocksize, copy=copy)
1020 def tocsc(self, copy=False):
1021 """Convert this array/matrix to Compressed Sparse Column format.
1023 With copy=False, the data/indices may be shared between this array/matrix and
1024 the resultant csc_array/matrix.
1025 """
1026 return self.tocsr(copy=copy).tocsc(copy=False)
1028 def copy(self):
1029 """Returns a copy of this array/matrix.
1031 No data/indices will be shared between the returned value and current
1032 array/matrix.
1033 """
1034 return self.__class__(self, copy=True)
1036 def sum(self, axis=None, dtype=None, out=None):
1037 """
1038 Sum the array/matrix elements over a given axis.
1040 Parameters
1041 ----------
1042 axis : {-2, -1, 0, 1, None} optional
1043 Axis along which the sum is computed. The default is to
1044 compute the sum of all the array/matrix elements, returning a scalar
1045 (i.e., `axis` = `None`).
1046 dtype : dtype, optional
1047 The type of the returned array/matrix and of the accumulator in which
1048 the elements are summed. The dtype of `a` is used by default
1049 unless `a` has an integer dtype of less precision than the default
1050 platform integer. In that case, if `a` is signed then the platform
1051 integer is used while if `a` is unsigned then an unsigned integer
1052 of the same precision as the platform integer is used.
1054 .. versionadded:: 0.18.0
1056 out : np.matrix, optional
1057 Alternative output matrix in which to place the result. It must
1058 have the same shape as the expected output, but the type of the
1059 output values will be cast if necessary.
1061 .. versionadded:: 0.18.0
1063 Returns
1064 -------
1065 sum_along_axis : np.matrix
1066 A matrix with the same shape as `self`, with the specified
1067 axis removed.
1069 See Also
1070 --------
1071 numpy.matrix.sum : NumPy's implementation of 'sum' for matrices
1073 """
1074 validateaxis(axis)
1076 # We use multiplication by a matrix of ones to achieve this.
1077 # For some sparse array formats more efficient methods are
1078 # possible -- these should override this function.
1079 m, n = self.shape
1081 # Mimic numpy's casting.
1082 res_dtype = get_sum_dtype(self.dtype)
1084 if axis is None:
1085 # sum over rows and columns
1086 return (
1087 self @ self._ascontainer(np.ones((n, 1), dtype=res_dtype))
1088 ).sum(dtype=dtype, out=out)
1090 if axis < 0:
1091 axis += 2
1093 # axis = 0 or 1 now
1094 if axis == 0:
1095 # sum over columns
1096 ret = self._ascontainer(
1097 np.ones((1, m), dtype=res_dtype)
1098 ) @ self
1099 else:
1100 # sum over rows
1101 ret = self @ self._ascontainer(
1102 np.ones((n, 1), dtype=res_dtype)
1103 )
1105 if out is not None and out.shape != ret.shape:
1106 raise ValueError("dimensions do not match")
1108 return ret.sum(axis=axis, dtype=dtype, out=out)
1110 def mean(self, axis=None, dtype=None, out=None):
1111 """
1112 Compute the arithmetic mean along the specified axis.
1114 Returns the average of the array/matrix elements. The average is taken
1115 over all elements in the array/matrix by default, otherwise over the
1116 specified axis. `float64` intermediate and return values are used
1117 for integer inputs.
1119 Parameters
1120 ----------
1121 axis : {-2, -1, 0, 1, None} optional
1122 Axis along which the mean is computed. The default is to compute
1123 the mean of all elements in the array/matrix (i.e., `axis` = `None`).
1124 dtype : data-type, optional
1125 Type to use in computing the mean. For integer inputs, the default
1126 is `float64`; for floating point inputs, it is the same as the
1127 input dtype.
1129 .. versionadded:: 0.18.0
1131 out : np.matrix, optional
1132 Alternative output matrix in which to place the result. It must
1133 have the same shape as the expected output, but the type of the
1134 output values will be cast if necessary.
1136 .. versionadded:: 0.18.0
1138 Returns
1139 -------
1140 m : np.matrix
1142 See Also
1143 --------
1144 numpy.matrix.mean : NumPy's implementation of 'mean' for matrices
1146 """
1147 def _is_integral(dtype):
1148 return (np.issubdtype(dtype, np.integer) or
1149 np.issubdtype(dtype, np.bool_))
1151 validateaxis(axis)
1153 res_dtype = self.dtype.type
1154 integral = _is_integral(self.dtype)
1156 # output dtype
1157 if dtype is None:
1158 if integral:
1159 res_dtype = np.float64
1160 else:
1161 res_dtype = np.dtype(dtype).type
1163 # intermediate dtype for summation
1164 inter_dtype = np.float64 if integral else res_dtype
1165 inter_self = self.astype(inter_dtype)
1167 if axis is None:
1168 return (inter_self / np.array(
1169 self.shape[0] * self.shape[1]))\
1170 .sum(dtype=res_dtype, out=out)
1172 if axis < 0:
1173 axis += 2
1175 # axis = 0 or 1 now
1176 if axis == 0:
1177 return (inter_self * (1.0 / self.shape[0])).sum(
1178 axis=0, dtype=res_dtype, out=out)
1179 else:
1180 return (inter_self * (1.0 / self.shape[1])).sum(
1181 axis=1, dtype=res_dtype, out=out)
1183 def diagonal(self, k=0):
1184 """Returns the kth diagonal of the array/matrix.
1186 Parameters
1187 ----------
1188 k : int, optional
1189 Which diagonal to get, corresponding to elements a[i, i+k].
1190 Default: 0 (the main diagonal).
1192 .. versionadded:: 1.0
1194 See also
1195 --------
1196 numpy.diagonal : Equivalent numpy function.
1198 Examples
1199 --------
1200 >>> from scipy.sparse import csr_array
1201 >>> A = csr_array([[1, 2, 0], [0, 0, 3], [4, 0, 5]])
1202 >>> A.diagonal()
1203 array([1, 0, 5])
1204 >>> A.diagonal(k=1)
1205 array([2, 3])
1206 """
1207 return self.tocsr().diagonal(k=k)
1209 def trace(self, offset=0):
1210 """Returns the sum along diagonals of the sparse array/matrix.
1212 Parameters
1213 ----------
1214 offset : int, optional
1215 Which diagonal to get, corresponding to elements a[i, i+offset].
1216 Default: 0 (the main diagonal).
1218 """
1219 return self.diagonal(k=offset).sum()
1221 def setdiag(self, values, k=0):
1222 """
1223 Set diagonal or off-diagonal elements of the array/matrix.
1225 Parameters
1226 ----------
1227 values : array_like
1228 New values of the diagonal elements.
1230 Values may have any length. If the diagonal is longer than values,
1231 then the remaining diagonal entries will not be set. If values are
1232 longer than the diagonal, then the remaining values are ignored.
1234 If a scalar value is given, all of the diagonal is set to it.
1236 k : int, optional
1237 Which off-diagonal to set, corresponding to elements a[i,i+k].
1238 Default: 0 (the main diagonal).
1240 """
1241 M, N = self.shape
1242 if (k > 0 and k >= N) or (k < 0 and -k >= M):
1243 raise ValueError("k exceeds array dimensions")
1244 self._setdiag(np.asarray(values), k)
1246 def _setdiag(self, values, k):
1247 """This part of the implementation gets overridden by the
1248 different formats.
1249 """
1250 M, N = self.shape
1251 if k < 0:
1252 if values.ndim == 0:
1253 # broadcast
1254 max_index = min(M+k, N)
1255 for i in range(max_index):
1256 self[i - k, i] = values
1257 else:
1258 max_index = min(M+k, N, len(values))
1259 if max_index <= 0:
1260 return
1261 for i, v in enumerate(values[:max_index]):
1262 self[i - k, i] = v
1263 else:
1264 if values.ndim == 0:
1265 # broadcast
1266 max_index = min(M, N-k)
1267 for i in range(max_index):
1268 self[i, i + k] = values
1269 else:
1270 max_index = min(M, N-k, len(values))
1271 if max_index <= 0:
1272 return
1273 for i, v in enumerate(values[:max_index]):
1274 self[i, i + k] = v
1276 def _process_toarray_args(self, order, out):
1277 if out is not None:
1278 if order is not None:
1279 raise ValueError('order cannot be specified if out '
1280 'is not None')
1281 if out.shape != self.shape or out.dtype != self.dtype:
1282 raise ValueError('out array must be same dtype and shape as '
1283 'sparse array')
1284 out[...] = 0.
1285 return out
1286 else:
1287 return np.zeros(self.shape, dtype=self.dtype, order=order)
1289 def _get_index_dtype(self, arrays=(), maxval=None, check_contents=False):
1290 """
1291 Determine index dtype for array.
1293 This wraps _sputils.get_index_dtype, providing compatibility for both
1294 array and matrix API sparse matrices. Matrix API sparse matrices would
1295 attempt to downcast the indices - which can be computationally
1296 expensive and undesirable for users. The array API changes this
1297 behaviour.
1299 See discussion: https://github.com/scipy/scipy/issues/16774
1301 The get_index_dtype import is due to implementation details of the test
1302 suite. It allows the decorator ``with_64bit_maxval_limit`` to mock a
1303 lower int32 max value for checks on the matrix API's downcasting
1304 behaviour.
1305 """
1306 from ._sputils import get_index_dtype
1308 # Don't check contents for array API
1309 return get_index_dtype(arrays, maxval, (check_contents and not isinstance(self, sparray)))
1312 ## All methods below are deprecated and should be removed in
1313 ## scipy 1.13.0
1314 ##
1315 ## Also uncomment the definition of shape above.
1317 def get_shape(self):
1318 """Get shape of a sparse array/matrix.
1320 .. deprecated:: 1.11.0
1321 This method will be removed in SciPy 1.13.0.
1322 Use `X.shape` instead.
1323 """
1324 msg = (
1325 "`get_shape` is deprecated and will be removed in v1.13.0; "
1326 "use `X.shape` instead."
1327 )
1328 warn(msg, DeprecationWarning, stacklevel=2)
1330 return self._shape
1332 def set_shape(self, shape):
1333 """See `reshape`.
1335 .. deprecated:: 1.11.0
1336 This method will be removed in SciPy 1.13.0.
1337 Use `X.reshape` instead.
1338 """
1339 msg = (
1340 "Shape assignment is deprecated and will be removed in v1.13.0; "
1341 "use `reshape` instead."
1342 )
1343 warn(msg, DeprecationWarning, stacklevel=2)
1345 # Make sure copy is False since this is in place
1346 # Make sure format is unchanged because we are doing a __dict__ swap
1347 new_self = self.reshape(shape, copy=False).asformat(self.format)
1348 self.__dict__ = new_self.__dict__
1350 shape = property(
1351 fget=lambda self: self._shape,
1352 fset=set_shape,
1353 doc="""The shape of the array.
1355Note that, starting in SciPy 1.13.0, this property will no longer be
1356settable. To change the array shape, use `X.reshape` instead.
1357"""
1358 ) # noqa: F811
1360 def asfptype(self):
1361 """Upcast array/matrix to a floating point format (if necessary)
1363 .. deprecated:: 1.11.0
1364 This method is for internal use only, and will be removed from the
1365 public API in SciPy 1.13.0.
1366 """
1367 msg = (
1368 "`asfptype` is an internal function, and is deprecated "
1369 "as part of the public API. It will be removed in v1.13.0."
1370 )
1371 warn(msg, DeprecationWarning, stacklevel=2)
1372 return self._asfptype()
1374 def getmaxprint(self):
1375 """Maximum number of elements to display when printed.
1377 .. deprecated:: 1.11.0
1378 This method is for internal use only, and will be removed from the
1379 public API in SciPy 1.13.0.
1380 """
1381 msg = (
1382 "`getmaxprint` is an internal function, and is deprecated "
1383 "as part of the public API. It will be removed in v1.13.0."
1384 )
1385 warn(msg, DeprecationWarning, stacklevel=2)
1386 return self._getmaxprint()
1388 def getformat(self):
1389 """Sparse array/matrix storage format.
1391 .. deprecated:: 1.11.0
1392 This method will be removed in SciPy 1.13.0.
1393 Use `X.format` instead.
1394 """
1395 msg = (
1396 "`getformat` is deprecated and will be removed in v1.13.0; "
1397 "use `X.format` instead."
1398 )
1399 warn(msg, DeprecationWarning, stacklevel=2)
1400 return self.format
1402 def getnnz(self, axis=None):
1403 """Number of stored values, including explicit zeros.
1405 .. deprecated:: 1.11.0
1406 This method will be removed in SciPy 1.13.0. Use `X.nnz`
1407 instead. The `axis` argument will no longer be supported;
1408 please let us know if you still need this functionality.
1410 Parameters
1411 ----------
1412 axis : None, 0, or 1
1413 Select between the number of values across the whole array/matrix, in
1414 each column, or in each row.
1416 See also
1417 --------
1418 count_nonzero : Number of non-zero entries
1419 """
1420 msg = (
1421 "`getnnz` is deprecated and will be removed in v1.13.0; "
1422 "use `X.nnz` instead."
1423 )
1424 warn(msg, DeprecationWarning, stacklevel=2)
1425 return self._getnnz(axis=axis)
1427 def getH(self):
1428 """Return the Hermitian transpose of this array/matrix.
1430 .. deprecated:: 1.11.0
1431 This method will be removed in SciPy 1.13.0.
1432 Use `X.conj().T` instead.
1433 """
1434 msg = (
1435 "`getH` is deprecated and will be removed in v1.13.0; "
1436 "use `X.conj().T` instead."
1437 )
1438 warn(msg, DeprecationWarning, stacklevel=2)
1439 return self.conjugate().transpose()
1441 def getcol(self, j):
1442 """Returns a copy of column j of the array/matrix, as an (m x 1) sparse
1443 array/matrix (column vector).
1445 .. deprecated:: 1.11.0
1446 This method will be removed in SciPy 1.13.0.
1447 Use array/matrix indexing instead.
1448 """
1449 msg = (
1450 "`getcol` is deprecated and will be removed in v1.13.0; "
1451 f"use `X[:, [{j}]]` instead."
1452 )
1453 warn(msg, DeprecationWarning, stacklevel=2)
1454 return self._getcol(j)
1456 def getrow(self, i):
1457 """Returns a copy of row i of the array/matrix, as a (1 x n) sparse
1458 array/matrix (row vector).
1460 .. deprecated:: 1.11.0
1461 This method will be removed in SciPy 1.13.0.
1462 Use array/matrix indexing instead.
1463 """
1464 msg = (
1465 "`getrow` is deprecated and will be removed in v1.13.0; "
1466 f"use `X[[{i}]]` instead."
1467 )
1468 warn(msg, DeprecationWarning, stacklevel=2)
1469 return self._getrow(i)
1471 ## End 1.13.0 deprecated methods
1474class sparray:
1475 """A namespace class to separate sparray from spmatrix"""
1476 pass
1478sparray.__doc__ = _spbase.__doc__
1481def issparse(x):
1482 """Is `x` of a sparse array or sparse matrix type?
1484 Parameters
1485 ----------
1486 x
1487 object to check for being a sparse array or sparse matrix
1489 Returns
1490 -------
1491 bool
1492 True if `x` is a sparse array or a sparse matrix, False otherwise
1494 Examples
1495 --------
1496 >>> import numpy as np
1497 >>> from scipy.sparse import csr_array, csr_matrix, issparse
1498 >>> issparse(csr_matrix([[5]]))
1499 True
1500 >>> issparse(csr_array([[5]]))
1501 True
1502 >>> issparse(np.array([[5]]))
1503 False
1504 >>> issparse(5)
1505 False
1506 """
1507 return isinstance(x, _spbase)
1510def isspmatrix(x):
1511 """Is `x` of a sparse matrix type?
1513 Parameters
1514 ----------
1515 x
1516 object to check for being a sparse matrix
1518 Returns
1519 -------
1520 bool
1521 True if `x` is a sparse matrix, False otherwise
1523 Examples
1524 --------
1525 >>> import numpy as np
1526 >>> from scipy.sparse import csr_array, csr_matrix, isspmatrix
1527 >>> isspmatrix(csr_matrix([[5]]))
1528 True
1529 >>> isspmatrix(csr_array([[5]]))
1530 False
1531 >>> isspmatrix(np.array([[5]]))
1532 False
1533 >>> isspmatrix(5)
1534 False
1535 """
1536 return isinstance(x, spmatrix)