Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/scipy/sparse/_base.py: 1%
564 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-03 06:39 +0000
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-03 06:39 +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
71 @property
72 def ndim(self) -> int:
73 return len(self._shape)
75 @property
76 def _shape_as_2d(self):
77 s = self._shape
78 return (1, s[-1]) if len(s) == 1 else s
80 @property
81 def _bsr_container(self):
82 from ._bsr import bsr_array
83 return bsr_array
85 @property
86 def _coo_container(self):
87 from ._coo import coo_array
88 return coo_array
90 @property
91 def _csc_container(self):
92 from ._csc import csc_array
93 return csc_array
95 @property
96 def _csr_container(self):
97 from ._csr import csr_array
98 return csr_array
100 @property
101 def _dia_container(self):
102 from ._dia import dia_array
103 return dia_array
105 @property
106 def _dok_container(self):
107 from ._dok import dok_array
108 return dok_array
110 @property
111 def _lil_container(self):
112 from ._lil import lil_array
113 return lil_array
115 def __init__(self, maxprint=MAXPRINT):
116 self._shape = None
117 if self.__class__.__name__ == '_spbase':
118 raise ValueError("This class is not intended"
119 " to be instantiated directly.")
120 self.maxprint = maxprint
122 # Use this in 1.14.0 and later:
123 #
124 # @property
125 # def shape(self):
126 # return self._shape
128 def reshape(self, *args, **kwargs):
129 """reshape(self, shape, order='C', copy=False)
131 Gives a new shape to a sparse array/matrix without changing its data.
133 Parameters
134 ----------
135 shape : length-2 tuple of ints
136 The new shape should be compatible with the original shape.
137 order : {'C', 'F'}, optional
138 Read the elements using this index order. 'C' means to read and
139 write the elements using C-like index order; e.g., read entire first
140 row, then second row, etc. 'F' means to read and write the elements
141 using Fortran-like index order; e.g., read entire first column, then
142 second column, etc.
143 copy : bool, optional
144 Indicates whether or not attributes of self should be copied
145 whenever possible. The degree to which attributes are copied varies
146 depending on the type of sparse array being used.
148 Returns
149 -------
150 reshaped : sparse array/matrix
151 A sparse array/matrix with the given `shape`, not necessarily of the same
152 format as the current object.
154 See Also
155 --------
156 numpy.reshape : NumPy's implementation of 'reshape' for ndarrays
157 """
158 # If the shape already matches, don't bother doing an actual reshape
159 # Otherwise, the default is to convert to COO and use its reshape
160 is_array = isinstance(self, sparray)
161 shape = check_shape(args, self.shape, allow_1d=is_array)
162 order, copy = check_reshape_kwargs(kwargs)
163 if shape == self.shape:
164 if copy:
165 return self.copy()
166 else:
167 return self
169 return self.tocoo(copy=copy).reshape(shape, order=order, copy=False)
171 def resize(self, shape):
172 """Resize the array/matrix in-place to dimensions given by ``shape``
174 Any elements that lie within the new shape will remain at the same
175 indices, while non-zero elements lying outside the new shape are
176 removed.
178 Parameters
179 ----------
180 shape : (int, int)
181 number of rows and columns in the new array/matrix
183 Notes
184 -----
185 The semantics are not identical to `numpy.ndarray.resize` or
186 `numpy.resize`. Here, the same data will be maintained at each index
187 before and after reshape, if that index is within the new bounds. In
188 numpy, resizing maintains contiguity of the array, moving elements
189 around in the logical array but not within a flattened representation.
191 We give no guarantees about whether the underlying data attributes
192 (arrays, etc.) will be modified in place or replaced with new objects.
193 """
194 # As an inplace operation, this requires implementation in each format.
195 raise NotImplementedError(
196 f'{type(self).__name__}.resize is not implemented')
198 def astype(self, dtype, casting='unsafe', copy=True):
199 """Cast the array/matrix elements to a specified type.
201 Parameters
202 ----------
203 dtype : string or numpy dtype
204 Typecode or data-type to which to cast the data.
205 casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, optional
206 Controls what kind of data casting may occur.
207 Defaults to 'unsafe' for backwards compatibility.
208 'no' means the data types should not be cast at all.
209 'equiv' means only byte-order changes are allowed.
210 'safe' means only casts which can preserve values are allowed.
211 'same_kind' means only safe casts or casts within a kind,
212 like float64 to float32, are allowed.
213 'unsafe' means any data conversions may be done.
214 copy : bool, optional
215 If `copy` is `False`, the result might share some memory with this
216 array/matrix. If `copy` is `True`, it is guaranteed that the result and
217 this array/matrix do not share any memory.
218 """
220 dtype = np.dtype(dtype)
221 if self.dtype != dtype:
222 return self.tocsr().astype(
223 dtype, casting=casting, copy=copy).asformat(self.format)
224 elif copy:
225 return self.copy()
226 else:
227 return self
229 @classmethod
230 def _ascontainer(cls, X, **kwargs):
231 if issubclass(cls, sparray):
232 return np.asarray(X, **kwargs)
233 else:
234 return asmatrix(X, **kwargs)
236 @classmethod
237 def _container(cls, X, **kwargs):
238 if issubclass(cls, sparray):
239 return np.array(X, **kwargs)
240 else:
241 return matrix(X, **kwargs)
243 def _asfptype(self):
244 """Upcast array to a floating point format (if necessary)"""
246 fp_types = ['f', 'd', 'F', 'D']
248 if self.dtype.char in fp_types:
249 return self
250 else:
251 for fp_type in fp_types:
252 if self.dtype <= np.dtype(fp_type):
253 return self.astype(fp_type)
255 raise TypeError('cannot upcast [%s] to a floating '
256 'point format' % self.dtype.name)
258 def __iter__(self):
259 for r in range(self.shape[0]):
260 yield self[r]
262 def _getmaxprint(self):
263 """Maximum number of elements to display when printed."""
264 return self.maxprint
266 def count_nonzero(self):
267 """Number of non-zero entries, equivalent to
269 np.count_nonzero(a.toarray())
271 Unlike the nnz property, which return the number of stored
272 entries (the length of the data attribute), this method counts the
273 actual number of non-zero entries in data.
274 """
275 raise NotImplementedError("count_nonzero not implemented for %s." %
276 self.__class__.__name__)
278 def _getnnz(self, axis=None):
279 """Number of stored values, including explicit zeros.
281 Parameters
282 ----------
283 axis : None, 0, or 1
284 Select between the number of values across the whole array, in
285 each column, or in each row.
287 See also
288 --------
289 count_nonzero : Number of non-zero entries
290 """
291 raise NotImplementedError("getnnz not implemented for %s." %
292 self.__class__.__name__)
294 @property
295 def nnz(self) -> int:
296 """Number of stored values, including explicit zeros.
298 See also
299 --------
300 count_nonzero : Number of non-zero entries
301 """
302 return self._getnnz()
304 @property
305 def size(self) -> int:
306 """Number of stored values.
308 See also
309 --------
310 count_nonzero : Number of non-zero values.
311 """
312 return self._getnnz()
314 @property
315 def format(self) -> str:
316 """Format string for matrix."""
317 return self._format
319 @property
320 def A(self) -> np.ndarray:
321 """DEPRECATED: Return a dense array.
323 .. deprecated:: 1.11.0
325 `.A` is deprecated and will be removed in v1.14.0.
326 Use `.toarray()` instead.
327 """
328 if isinstance(self, sparray):
329 message = ("`.A` is deprecated and will be removed in v1.14.0. "
330 "Use `.toarray()` instead.")
331 warn(VisibleDeprecationWarning(message), stacklevel=2)
332 return self.toarray()
334 @property
335 def T(self):
336 """Transpose."""
337 return self.transpose()
339 @property
340 def H(self):
341 """DEPRECATED: Returns the (complex) conjugate transpose.
343 .. deprecated:: 1.11.0
345 `.H` is deprecated and will be removed in v1.14.0.
346 Please use `.T.conjugate()` instead.
347 """
348 if isinstance(self, sparray):
349 message = ("`.H` is deprecated and will be removed in v1.14.0. "
350 "Please use `.T.conjugate()` instead.")
351 warn(VisibleDeprecationWarning(message), stacklevel=2)
352 return self.T.conjugate()
354 @property
355 def real(self):
356 return self._real()
358 @property
359 def imag(self):
360 return self._imag()
362 def __repr__(self):
363 _, format_name = _formats[self.format]
364 sparse_cls = 'array' if isinstance(self, sparray) else 'matrix'
365 shape_str = 'x'.join(str(x) for x in self.shape)
366 return (
367 f"<{shape_str} sparse {sparse_cls} of type '{self.dtype.type}'\n"
368 f"\twith {self.nnz} stored elements in {format_name} format>"
369 )
371 def __str__(self):
372 maxprint = self._getmaxprint()
374 A = self.tocoo()
376 # helper function, outputs "(i,j) v"
377 def tostr(row, col, data):
378 triples = zip(list(zip(row, col)), data)
379 return '\n'.join([(' {}\t{}'.format(*t)) for t in triples])
381 if self.nnz > maxprint:
382 half = maxprint // 2
383 out = tostr(A.row[:half], A.col[:half], A.data[:half])
384 out += "\n :\t:\n"
385 half = maxprint - maxprint//2
386 out += tostr(A.row[-half:], A.col[-half:], A.data[-half:])
387 else:
388 out = tostr(A.row, A.col, A.data)
390 return out
392 def __bool__(self): # Simple -- other ideas?
393 if self.shape == (1, 1):
394 return self.nnz != 0
395 else:
396 raise ValueError("The truth value of an array with more than one "
397 "element is ambiguous. Use a.any() or a.all().")
398 __nonzero__ = __bool__
400 # What should len(sparse) return? For consistency with dense matrices,
401 # perhaps it should be the number of rows? But for some uses the number of
402 # non-zeros is more important. For now, raise an exception!
403 def __len__(self):
404 raise TypeError("sparse array length is ambiguous; use getnnz()"
405 " or shape[0]")
407 def asformat(self, format, copy=False):
408 """Return this array/matrix in the passed format.
410 Parameters
411 ----------
412 format : {str, None}
413 The desired sparse format ("csr", "csc", "lil", "dok", "array", ...)
414 or None for no conversion.
415 copy : bool, optional
416 If True, the result is guaranteed to not share data with self.
418 Returns
419 -------
420 A : This array/matrix in the passed format.
421 """
422 if format is None or format == self.format:
423 if copy:
424 return self.copy()
425 else:
426 return self
427 else:
428 try:
429 convert_method = getattr(self, 'to' + format)
430 except AttributeError as e:
431 raise ValueError(f'Format {format} is unknown.') from e
433 # Forward the copy kwarg, if it's accepted.
434 try:
435 return convert_method(copy=copy)
436 except TypeError:
437 return convert_method()
439 ###################################################################
440 # NOTE: All arithmetic operations use csr_matrix by default.
441 # Therefore a new sparse array format just needs to define a
442 # .tocsr() method to provide arithmetic support. Any of these
443 # methods can be overridden for efficiency.
444 ####################################################################
446 def multiply(self, other):
447 """Point-wise multiplication by another array/matrix."""
448 return self.tocsr().multiply(other)
450 def maximum(self, other):
451 """Element-wise maximum between this and another array/matrix."""
452 return self.tocsr().maximum(other)
454 def minimum(self, other):
455 """Element-wise minimum between this and another array/matrix."""
456 return self.tocsr().minimum(other)
458 def dot(self, other):
459 """Ordinary dot product
461 Examples
462 --------
463 >>> import numpy as np
464 >>> from scipy.sparse import csr_array
465 >>> A = csr_array([[1, 2, 0], [0, 0, 3], [4, 0, 5]])
466 >>> v = np.array([1, 0, -1])
467 >>> A.dot(v)
468 array([ 1, -3, -1], dtype=int64)
470 """
471 if np.isscalar(other):
472 return self * other
473 else:
474 return self @ other
476 def power(self, n, dtype=None):
477 """Element-wise power."""
478 return self.tocsr().power(n, dtype=dtype)
480 def __eq__(self, other):
481 return self.tocsr().__eq__(other)
483 def __ne__(self, other):
484 return self.tocsr().__ne__(other)
486 def __lt__(self, other):
487 return self.tocsr().__lt__(other)
489 def __gt__(self, other):
490 return self.tocsr().__gt__(other)
492 def __le__(self, other):
493 return self.tocsr().__le__(other)
495 def __ge__(self, other):
496 return self.tocsr().__ge__(other)
498 def __abs__(self):
499 return abs(self.tocsr())
501 def __round__(self, ndigits=0):
502 return round(self.tocsr(), ndigits=ndigits)
504 def _add_sparse(self, other):
505 return self.tocsr()._add_sparse(other)
507 def _add_dense(self, other):
508 return self.tocoo()._add_dense(other)
510 def _sub_sparse(self, other):
511 return self.tocsr()._sub_sparse(other)
513 def _sub_dense(self, other):
514 return self.todense() - other
516 def _rsub_dense(self, other):
517 # note: this can't be replaced by other + (-self) for unsigned types
518 return other - self.todense()
520 def __add__(self, other): # self + other
521 if isscalarlike(other):
522 if other == 0:
523 return self.copy()
524 # Now we would add this scalar to every element.
525 raise NotImplementedError('adding a nonzero scalar to a '
526 'sparse array is not supported')
527 elif issparse(other):
528 if other.shape != self.shape:
529 raise ValueError("inconsistent shapes")
530 return self._add_sparse(other)
531 elif isdense(other):
532 other = np.broadcast_to(other, self.shape)
533 return self._add_dense(other)
534 else:
535 return NotImplemented
537 def __radd__(self,other): # other + self
538 return self.__add__(other)
540 def __sub__(self, other): # self - other
541 if isscalarlike(other):
542 if other == 0:
543 return self.copy()
544 raise NotImplementedError('subtracting a nonzero scalar from a '
545 'sparse array is not supported')
546 elif issparse(other):
547 if other.shape != self.shape:
548 raise ValueError("inconsistent shapes")
549 return self._sub_sparse(other)
550 elif isdense(other):
551 other = np.broadcast_to(other, self.shape)
552 return self._sub_dense(other)
553 else:
554 return NotImplemented
556 def __rsub__(self,other): # other - self
557 if isscalarlike(other):
558 if other == 0:
559 return -self.copy()
560 raise NotImplementedError('subtracting a sparse array from a '
561 'nonzero scalar is not supported')
562 elif isdense(other):
563 other = np.broadcast_to(other, self.shape)
564 return self._rsub_dense(other)
565 else:
566 return NotImplemented
568 def _matmul_dispatch(self, other):
569 """np.array-like matmul & `np.matrix`-like mul, i.e. `dot` or `NotImplemented`
571 interpret other and call one of the following
572 self._mul_scalar()
573 self._matmul_vector()
574 self._matmul_multivector()
575 self._matmul_sparse()
576 """
577 # This method has to be different from `__matmul__` because it is also
578 # called by sparse matrix classes.
580 # Currently matrix multiplication is only supported
581 # for 2D arrays. Hence we unpacked and use only the
582 # two last axes' lengths.
583 M, N = self._shape_as_2d
585 if other.__class__ is np.ndarray:
586 # Fast path for the most common case
587 if other.shape == (N,):
588 return self._matmul_vector(other)
589 elif other.shape == (N, 1):
590 result = self._matmul_vector(other.ravel())
591 if self.ndim == 1:
592 return result
593 return result.reshape(M, 1)
594 elif other.ndim == 2 and other.shape[0] == N:
595 return self._matmul_multivector(other)
597 if isscalarlike(other):
598 # scalar value
599 return self._mul_scalar(other)
601 if issparse(other):
602 if self.shape[-1] != other.shape[0]:
603 raise ValueError('dimension mismatch')
604 if other.ndim == 1:
605 raise ValueError('Cannot yet multiply a 1d sparse array')
606 return self._matmul_sparse(other)
608 # If it's a list or whatever, treat it like an array
609 other_a = np.asanyarray(other)
611 if other_a.ndim == 0 and other_a.dtype == np.object_:
612 # Not interpretable as an array; return NotImplemented so that
613 # other's __rmatmul__ can kick in if that's implemented.
614 return NotImplemented
616 try:
617 other.shape
618 except AttributeError:
619 other = other_a
621 if other.ndim == 1 or other.ndim == 2 and other.shape[1] == 1:
622 # dense row or column vector
623 if other.shape != (N,) and other.shape != (N, 1):
624 raise ValueError('dimension mismatch')
626 result = self._matmul_vector(np.ravel(other))
628 if isinstance(other, np.matrix):
629 result = self._ascontainer(result)
631 if other.ndim == 2 and other.shape[1] == 1:
632 # If 'other' was an (nx1) column vector, reshape the result
633 result = result.reshape(-1, 1)
635 return result
637 elif other.ndim == 2:
638 ##
639 # dense 2D array or matrix ("multivector")
641 if other.shape[0] != N:
642 raise ValueError('dimension mismatch')
644 result = self._matmul_multivector(np.asarray(other))
646 if isinstance(other, np.matrix):
647 result = self._ascontainer(result)
649 return result
651 else:
652 raise ValueError('could not interpret dimensions')
654 def __mul__(self, other):
655 return self.multiply(other)
657 def __rmul__(self, other): # other * self
658 return self.multiply(other)
660 # by default, use CSR for __mul__ handlers
661 def _mul_scalar(self, other):
662 return self.tocsr()._mul_scalar(other)
664 def _matmul_vector(self, other):
665 return self.tocsr()._matmul_vector(other)
667 def _matmul_multivector(self, other):
668 return self.tocsr()._matmul_multivector(other)
670 def _matmul_sparse(self, other):
671 return self.tocsr()._matmul_sparse(other)
673 def _rmatmul_dispatch(self, other):
674 if isscalarlike(other):
675 return self._mul_scalar(other)
676 else:
677 # Don't use asarray unless we have to
678 try:
679 tr = other.transpose()
680 except AttributeError:
681 tr = np.asarray(other).transpose()
682 ret = self.transpose()._matmul_dispatch(tr)
683 if ret is NotImplemented:
684 return NotImplemented
685 return ret.transpose()
687 #######################
688 # matmul (@) operator #
689 #######################
691 def __matmul__(self, other):
692 if isscalarlike(other):
693 raise ValueError("Scalar operands are not allowed, "
694 "use '*' instead")
695 return self._matmul_dispatch(other)
697 def __rmatmul__(self, other):
698 if isscalarlike(other):
699 raise ValueError("Scalar operands are not allowed, "
700 "use '*' instead")
701 return self._rmatmul_dispatch(other)
703 ####################
704 # Other Arithmetic #
705 ####################
707 def _divide(self, other, true_divide=False, rdivide=False):
708 if isscalarlike(other):
709 if rdivide:
710 if true_divide:
711 return np.true_divide(other, self.todense())
712 else:
713 return np.divide(other, self.todense())
715 if true_divide and np.can_cast(self.dtype, np.float64):
716 return self.astype(np.float64)._mul_scalar(1./other)
717 else:
718 r = self._mul_scalar(1./other)
720 scalar_dtype = np.asarray(other).dtype
721 if (np.issubdtype(self.dtype, np.integer) and
722 np.issubdtype(scalar_dtype, np.integer)):
723 return r.astype(self.dtype)
724 else:
725 return r
727 elif isdense(other):
728 if not rdivide:
729 if true_divide:
730 recip = np.true_divide(1., other)
731 else:
732 recip = np.divide(1., other)
733 return self.multiply(recip)
734 else:
735 if true_divide:
736 return np.true_divide(other, self.todense())
737 else:
738 return np.divide(other, self.todense())
739 elif issparse(other):
740 if rdivide:
741 return other._divide(self, true_divide, rdivide=False)
743 self_csr = self.tocsr()
744 if true_divide and np.can_cast(self.dtype, np.float64):
745 return self_csr.astype(np.float64)._divide_sparse(other)
746 else:
747 return self_csr._divide_sparse(other)
748 else:
749 return NotImplemented
751 def __truediv__(self, other):
752 return self._divide(other, true_divide=True)
754 def __div__(self, other):
755 # Always do true division
756 return self._divide(other, true_divide=True)
758 def __rtruediv__(self, other):
759 # Implementing this as the inverse would be too magical -- bail out
760 return NotImplemented
762 def __rdiv__(self, other):
763 # Implementing this as the inverse would be too magical -- bail out
764 return NotImplemented
766 def __neg__(self):
767 return -self.tocsr()
769 def __iadd__(self, other):
770 return NotImplemented
772 def __isub__(self, other):
773 return NotImplemented
775 def __imul__(self, other):
776 return NotImplemented
778 def __idiv__(self, other):
779 return self.__itruediv__(other)
781 def __itruediv__(self, other):
782 return NotImplemented
784 def __pow__(self, *args, **kwargs):
785 return self.power(*args, **kwargs)
787 def transpose(self, axes=None, copy=False):
788 """
789 Reverses the dimensions of the sparse array/matrix.
791 Parameters
792 ----------
793 axes : None, optional
794 This argument is in the signature *solely* for NumPy
795 compatibility reasons. Do not pass in anything except
796 for the default value.
797 copy : bool, optional
798 Indicates whether or not attributes of `self` should be
799 copied whenever possible. The degree to which attributes
800 are copied varies depending on the type of sparse array/matrix
801 being used.
803 Returns
804 -------
805 p : `self` with the dimensions reversed.
807 Notes
808 -----
809 If `self` is a `csr_array` or a `csc_array`, then this will return a
810 `csc_array` or a `csr_array`, respectively.
812 See Also
813 --------
814 numpy.transpose : NumPy's implementation of 'transpose' for ndarrays
815 """
816 return self.tocsr(copy=copy).transpose(axes=axes, copy=False)
818 def conjugate(self, copy=True):
819 """Element-wise complex conjugation.
821 If the array/matrix is of non-complex data type and `copy` is False,
822 this method does nothing and the data is not copied.
824 Parameters
825 ----------
826 copy : bool, optional
827 If True, the result is guaranteed to not share data with self.
829 Returns
830 -------
831 A : The element-wise complex conjugate.
833 """
834 if np.issubdtype(self.dtype, np.complexfloating):
835 return self.tocsr(copy=copy).conjugate(copy=False)
836 elif copy:
837 return self.copy()
838 else:
839 return self
841 def conj(self, copy=True):
842 return self.conjugate(copy=copy)
844 conj.__doc__ = conjugate.__doc__
846 def _real(self):
847 return self.tocsr()._real()
849 def _imag(self):
850 return self.tocsr()._imag()
852 def nonzero(self):
853 """Nonzero indices of the array/matrix.
855 Returns a tuple of arrays (row,col) containing the indices
856 of the non-zero elements of the array.
858 Examples
859 --------
860 >>> from scipy.sparse import csr_array
861 >>> A = csr_array([[1,2,0],[0,0,3],[4,0,5]])
862 >>> A.nonzero()
863 (array([0, 0, 1, 2, 2]), array([0, 1, 2, 0, 2]))
865 """
867 # convert to COOrdinate format
868 A = self.tocoo()
869 nz_mask = A.data != 0
870 return (A.row[nz_mask], A.col[nz_mask])
872 def _getcol(self, j):
873 """Returns a copy of column j of the array, as an (m x 1) sparse
874 array (column vector).
875 """
876 if self.ndim == 1:
877 raise ValueError("getcol not provided for 1d arrays. Use indexing A[j]")
878 # Subclasses should override this method for efficiency.
879 # Post-multiply by a (n x 1) column vector 'a' containing all zeros
880 # except for a_j = 1
881 N = self.shape[-1]
882 if j < 0:
883 j += N
884 if j < 0 or j >= N:
885 raise IndexError("index out of bounds")
886 col_selector = self._csc_container(([1], [[j], [0]]),
887 shape=(N, 1), dtype=self.dtype)
888 result = self @ col_selector
889 return result
891 def _getrow(self, i):
892 """Returns a copy of row i of the array, as a (1 x n) sparse
893 array (row vector).
894 """
895 if self.ndim == 1:
896 raise ValueError("getrow not meaningful for a 1d array")
897 # Subclasses should override this method for efficiency.
898 # Pre-multiply by a (1 x m) row vector 'a' containing all zeros
899 # except for a_i = 1
900 M = self.shape[0]
901 if i < 0:
902 i += M
903 if i < 0 or i >= M:
904 raise IndexError("index out of bounds")
905 row_selector = self._csr_container(([1], [[0], [i]]),
906 shape=(1, M), dtype=self.dtype)
907 return row_selector @ self
909 # The following dunder methods cannot be implemented.
910 #
911 # def __array__(self):
912 # # Sparse matrices rely on NumPy wrapping them in object arrays under
913 # # the hood to make unary ufuncs work on them. So we cannot raise
914 # # TypeError here - which would be handy to not give users object
915 # # arrays they probably don't want (they're looking for `.toarray()`).
916 # #
917 # # Conversion with `toarray()` would also break things because of the
918 # # behavior discussed above, plus we want to avoid densification by
919 # # accident because that can too easily blow up memory.
920 #
921 # def __array_ufunc__(self):
922 # # We cannot implement __array_ufunc__ due to mismatching semantics.
923 # # See gh-7707 and gh-7349 for details.
924 #
925 # def __array_function__(self):
926 # # We cannot implement __array_function__ due to mismatching semantics.
927 # # See gh-10362 for details.
929 def todense(self, order=None, out=None):
930 """
931 Return a dense representation of this sparse array/matrix.
933 Parameters
934 ----------
935 order : {'C', 'F'}, optional
936 Whether to store multi-dimensional data in C (row-major)
937 or Fortran (column-major) order in memory. The default
938 is 'None', which provides no ordering guarantees.
939 Cannot be specified in conjunction with the `out`
940 argument.
942 out : ndarray, 2-D, optional
943 If specified, uses this array (or `numpy.matrix`) as the
944 output buffer instead of allocating a new array to
945 return. The provided array must have the same shape and
946 dtype as the sparse array/matrix on which you are calling the
947 method.
949 Returns
950 -------
951 arr : numpy.matrix, 2-D
952 A NumPy matrix object with the same shape and containing
953 the same data represented by the sparse array/matrix, with the
954 requested memory order. If `out` was passed and was an
955 array (rather than a `numpy.matrix`), it will be filled
956 with the appropriate values and returned wrapped in a
957 `numpy.matrix` object that shares the same memory.
958 """
959 return self._ascontainer(self.toarray(order=order, out=out))
961 def toarray(self, order=None, out=None):
962 """
963 Return a dense ndarray representation of this sparse array/matrix.
965 Parameters
966 ----------
967 order : {'C', 'F'}, optional
968 Whether to store multidimensional data in C (row-major)
969 or Fortran (column-major) order in memory. The default
970 is 'None', which provides no ordering guarantees.
971 Cannot be specified in conjunction with the `out`
972 argument.
974 out : ndarray, 2-D, optional
975 If specified, uses this array as the output buffer
976 instead of allocating a new array to return. The provided
977 array must have the same shape and dtype as the sparse
978 array/matrix on which you are calling the method. For most
979 sparse types, `out` is required to be memory contiguous
980 (either C or Fortran ordered).
982 Returns
983 -------
984 arr : ndarray, 2-D
985 An array with the same shape and containing the same
986 data represented by the sparse array/matrix, with the requested
987 memory order. If `out` was passed, the same object is
988 returned after being modified in-place to contain the
989 appropriate values.
990 """
991 return self.tocoo(copy=False).toarray(order=order, out=out)
993 # Any sparse array format deriving from _spbase must define one of
994 # tocsr or tocoo. The other conversion methods may be implemented for
995 # efficiency, but are not required.
996 def tocsr(self, copy=False):
997 """Convert this array/matrix to Compressed Sparse Row format.
999 With copy=False, the data/indices may be shared between this array/matrix and
1000 the resultant csr_array/matrix.
1001 """
1002 return self.tocoo(copy=copy).tocsr(copy=False)
1004 def todok(self, copy=False):
1005 """Convert this array/matrix to Dictionary Of Keys format.
1007 With copy=False, the data/indices may be shared between this array/matrix and
1008 the resultant dok_array/matrix.
1009 """
1010 return self.tocoo(copy=copy).todok(copy=False)
1012 def tocoo(self, copy=False):
1013 """Convert this array/matrix to COOrdinate format.
1015 With copy=False, the data/indices may be shared between this array/matrix and
1016 the resultant coo_array/matrix.
1017 """
1018 return self.tocsr(copy=False).tocoo(copy=copy)
1020 def tolil(self, copy=False):
1021 """Convert this array/matrix to List of Lists format.
1023 With copy=False, the data/indices may be shared between this array/matrix and
1024 the resultant lil_array/matrix.
1025 """
1026 return self.tocsr(copy=False).tolil(copy=copy)
1028 def todia(self, copy=False):
1029 """Convert this array/matrix to sparse DIAgonal format.
1031 With copy=False, the data/indices may be shared between this array/matrix and
1032 the resultant dia_array/matrix.
1033 """
1034 return self.tocoo(copy=copy).todia(copy=False)
1036 def tobsr(self, blocksize=None, copy=False):
1037 """Convert this array/matrix to Block Sparse Row format.
1039 With copy=False, the data/indices may be shared between this array/matrix and
1040 the resultant bsr_array/matrix.
1042 When blocksize=(R, C) is provided, it will be used for construction of
1043 the bsr_array/matrix.
1044 """
1045 return self.tocsr(copy=False).tobsr(blocksize=blocksize, copy=copy)
1047 def tocsc(self, copy=False):
1048 """Convert this array/matrix to Compressed Sparse Column format.
1050 With copy=False, the data/indices may be shared between this array/matrix and
1051 the resultant csc_array/matrix.
1052 """
1053 return self.tocsr(copy=copy).tocsc(copy=False)
1055 def copy(self):
1056 """Returns a copy of this array/matrix.
1058 No data/indices will be shared between the returned value and current
1059 array/matrix.
1060 """
1061 return self.__class__(self, copy=True)
1063 def sum(self, axis=None, dtype=None, out=None):
1064 """
1065 Sum the array/matrix elements over a given axis.
1067 Parameters
1068 ----------
1069 axis : {-2, -1, 0, 1, None} optional
1070 Axis along which the sum is computed. The default is to
1071 compute the sum of all the array/matrix elements, returning a scalar
1072 (i.e., `axis` = `None`).
1073 dtype : dtype, optional
1074 The type of the returned array/matrix and of the accumulator in which
1075 the elements are summed. The dtype of `a` is used by default
1076 unless `a` has an integer dtype of less precision than the default
1077 platform integer. In that case, if `a` is signed then the platform
1078 integer is used while if `a` is unsigned then an unsigned integer
1079 of the same precision as the platform integer is used.
1081 .. versionadded:: 0.18.0
1083 out : np.matrix, optional
1084 Alternative output matrix in which to place the result. It must
1085 have the same shape as the expected output, but the type of the
1086 output values will be cast if necessary.
1088 .. versionadded:: 0.18.0
1090 Returns
1091 -------
1092 sum_along_axis : np.matrix
1093 A matrix with the same shape as `self`, with the specified
1094 axis removed.
1096 See Also
1097 --------
1098 numpy.matrix.sum : NumPy's implementation of 'sum' for matrices
1100 """
1101 validateaxis(axis)
1103 # Mimic numpy's casting.
1104 res_dtype = get_sum_dtype(self.dtype)
1106 if self.ndim == 1:
1107 if axis not in (None, -1, 0):
1108 raise ValueError("axis must be None, -1 or 0")
1109 ret = (self @ np.ones(self.shape, dtype=res_dtype)).astype(dtype)
1111 if out is not None:
1112 if any(dim != 1 for dim in out.shape):
1113 raise ValueError("dimensions do not match")
1114 out[...] = ret
1115 return ret
1117 # We use multiplication by a matrix of ones to achieve this.
1118 # For some sparse array formats more efficient methods are
1119 # possible -- these should override this function.
1120 M, N = self.shape
1122 if axis is None:
1123 # sum over rows and columns
1124 return (
1125 self @ self._ascontainer(np.ones((N, 1), dtype=res_dtype))
1126 ).sum(dtype=dtype, out=out)
1128 if axis < 0:
1129 axis += 2
1131 # axis = 0 or 1 now
1132 if axis == 0:
1133 # sum over columns
1134 ret = self._ascontainer(
1135 np.ones((1, M), dtype=res_dtype)
1136 ) @ self
1137 else:
1138 # sum over rows
1139 ret = self @ self._ascontainer(
1140 np.ones((N, 1), dtype=res_dtype)
1141 )
1143 if out is not None and out.shape != ret.shape:
1144 raise ValueError("dimensions do not match")
1146 return ret.sum(axis=axis, dtype=dtype, out=out)
1148 def mean(self, axis=None, dtype=None, out=None):
1149 """
1150 Compute the arithmetic mean along the specified axis.
1152 Returns the average of the array/matrix elements. The average is taken
1153 over all elements in the array/matrix by default, otherwise over the
1154 specified axis. `float64` intermediate and return values are used
1155 for integer inputs.
1157 Parameters
1158 ----------
1159 axis : {-2, -1, 0, 1, None} optional
1160 Axis along which the mean is computed. The default is to compute
1161 the mean of all elements in the array/matrix (i.e., `axis` = `None`).
1162 dtype : data-type, optional
1163 Type to use in computing the mean. For integer inputs, the default
1164 is `float64`; for floating point inputs, it is the same as the
1165 input dtype.
1167 .. versionadded:: 0.18.0
1169 out : np.matrix, optional
1170 Alternative output matrix in which to place the result. It must
1171 have the same shape as the expected output, but the type of the
1172 output values will be cast if necessary.
1174 .. versionadded:: 0.18.0
1176 Returns
1177 -------
1178 m : np.matrix
1180 See Also
1181 --------
1182 numpy.matrix.mean : NumPy's implementation of 'mean' for matrices
1184 """
1185 validateaxis(axis)
1187 res_dtype = self.dtype.type
1188 integral = (np.issubdtype(self.dtype, np.integer) or
1189 np.issubdtype(self.dtype, np.bool_))
1191 # output dtype
1192 if dtype is None:
1193 if integral:
1194 res_dtype = np.float64
1195 else:
1196 res_dtype = np.dtype(dtype).type
1198 # intermediate dtype for summation
1199 inter_dtype = np.float64 if integral else res_dtype
1200 inter_self = self.astype(inter_dtype)
1202 if self.ndim == 1:
1203 if axis not in (None, -1, 0):
1204 raise ValueError("axis must be None, -1 or 0")
1205 res = inter_self / self.shape[-1]
1206 return res.sum(dtype=res_dtype, out=out)
1208 if axis is None:
1209 return (inter_self / (self.shape[0] * self.shape[1]))\
1210 .sum(dtype=res_dtype, out=out)
1212 if axis < 0:
1213 axis += 2
1215 # axis = 0 or 1 now
1216 if axis == 0:
1217 return (inter_self * (1.0 / self.shape[0])).sum(
1218 axis=0, dtype=res_dtype, out=out)
1219 else:
1220 return (inter_self * (1.0 / self.shape[1])).sum(
1221 axis=1, dtype=res_dtype, out=out)
1223 def diagonal(self, k=0):
1224 """Returns the kth diagonal of the array/matrix.
1226 Parameters
1227 ----------
1228 k : int, optional
1229 Which diagonal to get, corresponding to elements a[i, i+k].
1230 Default: 0 (the main diagonal).
1232 .. versionadded:: 1.0
1234 See also
1235 --------
1236 numpy.diagonal : Equivalent numpy function.
1238 Examples
1239 --------
1240 >>> from scipy.sparse import csr_array
1241 >>> A = csr_array([[1, 2, 0], [0, 0, 3], [4, 0, 5]])
1242 >>> A.diagonal()
1243 array([1, 0, 5])
1244 >>> A.diagonal(k=1)
1245 array([2, 3])
1246 """
1247 return self.tocsr().diagonal(k=k)
1249 def trace(self, offset=0):
1250 """Returns the sum along diagonals of the sparse array/matrix.
1252 Parameters
1253 ----------
1254 offset : int, optional
1255 Which diagonal to get, corresponding to elements a[i, i+offset].
1256 Default: 0 (the main diagonal).
1258 """
1259 return self.diagonal(k=offset).sum()
1261 def setdiag(self, values, k=0):
1262 """
1263 Set diagonal or off-diagonal elements of the array/matrix.
1265 Parameters
1266 ----------
1267 values : array_like
1268 New values of the diagonal elements.
1270 Values may have any length. If the diagonal is longer than values,
1271 then the remaining diagonal entries will not be set. If values are
1272 longer than the diagonal, then the remaining values are ignored.
1274 If a scalar value is given, all of the diagonal is set to it.
1276 k : int, optional
1277 Which off-diagonal to set, corresponding to elements a[i,i+k].
1278 Default: 0 (the main diagonal).
1280 """
1281 M, N = self.shape
1282 if (k > 0 and k >= N) or (k < 0 and -k >= M):
1283 raise ValueError("k exceeds array dimensions")
1284 self._setdiag(np.asarray(values), k)
1286 def _setdiag(self, values, k):
1287 """This part of the implementation gets overridden by the
1288 different formats.
1289 """
1290 M, N = self.shape
1291 if k < 0:
1292 if values.ndim == 0:
1293 # broadcast
1294 max_index = min(M+k, N)
1295 for i in range(max_index):
1296 self[i - k, i] = values
1297 else:
1298 max_index = min(M+k, N, len(values))
1299 if max_index <= 0:
1300 return
1301 for i, v in enumerate(values[:max_index]):
1302 self[i - k, i] = v
1303 else:
1304 if values.ndim == 0:
1305 # broadcast
1306 max_index = min(M, N-k)
1307 for i in range(max_index):
1308 self[i, i + k] = values
1309 else:
1310 max_index = min(M, N-k, len(values))
1311 if max_index <= 0:
1312 return
1313 for i, v in enumerate(values[:max_index]):
1314 self[i, i + k] = v
1316 def _process_toarray_args(self, order, out):
1317 if out is not None:
1318 if order is not None:
1319 raise ValueError('order cannot be specified if out '
1320 'is not None')
1321 if out.shape != self.shape or out.dtype != self.dtype:
1322 raise ValueError('out array must be same dtype and shape as '
1323 'sparse array')
1324 out[...] = 0.
1325 return out
1326 else:
1327 return np.zeros(self.shape, dtype=self.dtype, order=order)
1329 def _get_index_dtype(self, arrays=(), maxval=None, check_contents=False):
1330 """
1331 Determine index dtype for array.
1333 This wraps _sputils.get_index_dtype, providing compatibility for both
1334 array and matrix API sparse matrices. Matrix API sparse matrices would
1335 attempt to downcast the indices - which can be computationally
1336 expensive and undesirable for users. The array API changes this
1337 behaviour.
1339 See discussion: https://github.com/scipy/scipy/issues/16774
1341 The get_index_dtype import is due to implementation details of the test
1342 suite. It allows the decorator ``with_64bit_maxval_limit`` to mock a
1343 lower int32 max value for checks on the matrix API's downcasting
1344 behaviour.
1345 """
1346 from ._sputils import get_index_dtype
1348 # Don't check contents for array API
1349 return get_index_dtype(arrays,
1350 maxval,
1351 (check_contents and not isinstance(self, sparray)))
1354 ## All methods below are deprecated and should be removed in
1355 ## scipy 1.14.0
1356 ##
1357 ## Also uncomment the definition of shape above.
1359 def get_shape(self):
1360 """Get shape of a sparse array/matrix.
1362 .. deprecated:: 1.11.0
1363 This method will be removed in SciPy 1.14.0.
1364 Use `X.shape` instead.
1365 """
1366 msg = (
1367 "`get_shape` is deprecated and will be removed in v1.14.0; "
1368 "use `X.shape` instead."
1369 )
1370 warn(msg, DeprecationWarning, stacklevel=2)
1372 return self._shape
1374 def set_shape(self, shape):
1375 """See `reshape`.
1377 .. deprecated:: 1.11.0
1378 This method will be removed in SciPy 1.14.0.
1379 Use `X.reshape` instead.
1380 """
1381 msg = (
1382 "Shape assignment is deprecated and will be removed in v1.14.0; "
1383 "use `reshape` instead."
1384 )
1385 warn(msg, DeprecationWarning, stacklevel=2)
1387 # Make sure copy is False since this is in place
1388 # Make sure format is unchanged because we are doing a __dict__ swap
1389 new_self = self.reshape(shape, copy=False).asformat(self.format)
1390 self.__dict__ = new_self.__dict__
1392 shape = property(
1393 fget=lambda self: self._shape,
1394 fset=set_shape,
1395 doc="""The shape of the array.
1397Note that, starting in SciPy 1.14.0, this property will no longer be
1398settable. To change the array shape, use `X.reshape` instead.
1399"""
1400 )
1402 def asfptype(self):
1403 """Upcast array/matrix to a floating point format (if necessary)
1405 .. deprecated:: 1.11.0
1406 This method is for internal use only, and will be removed from the
1407 public API in SciPy 1.14.0.
1408 """
1409 msg = (
1410 "`asfptype` is an internal function, and is deprecated "
1411 "as part of the public API. It will be removed in v1.14.0."
1412 )
1413 warn(msg, DeprecationWarning, stacklevel=2)
1414 return self._asfptype()
1416 def getmaxprint(self):
1417 """Maximum number of elements to display when printed.
1419 .. deprecated:: 1.11.0
1420 This method is for internal use only, and will be removed from the
1421 public API in SciPy 1.14.0.
1422 """
1423 msg = (
1424 "`getmaxprint` is an internal function, and is deprecated "
1425 "as part of the public API. It will be removed in v1.14.0."
1426 )
1427 warn(msg, DeprecationWarning, stacklevel=2)
1428 return self._getmaxprint()
1430 def getformat(self):
1431 """Sparse array/matrix storage format.
1433 .. deprecated:: 1.11.0
1434 This method will be removed in SciPy 1.14.0.
1435 Use `X.format` instead.
1436 """
1437 msg = (
1438 "`getformat` is deprecated and will be removed in v1.14.0; "
1439 "use `X.format` instead."
1440 )
1441 warn(msg, DeprecationWarning, stacklevel=2)
1442 return self.format
1444 def getnnz(self, axis=None):
1445 """Number of stored values, including explicit zeros.
1447 Parameters
1448 ----------
1449 axis : None, 0, or 1
1450 Select between the number of values across the whole array/matrix, in
1451 each column, or in each row.
1453 See also
1454 --------
1455 count_nonzero : Number of non-zero entries
1456 """
1457 return self._getnnz(axis=axis)
1459 def getH(self):
1460 """Return the Hermitian transpose of this array/matrix.
1462 .. deprecated:: 1.11.0
1463 This method will be removed in SciPy 1.14.0.
1464 Use `X.conj().T` instead.
1465 """
1466 msg = (
1467 "`getH` is deprecated and will be removed in v1.14.0; "
1468 "use `X.conj().T` instead."
1469 )
1470 warn(msg, DeprecationWarning, stacklevel=2)
1471 return self.conjugate().transpose()
1473 def getcol(self, j):
1474 """Returns a copy of column j of the array/matrix, as an (m x 1) sparse
1475 array/matrix (column vector).
1477 .. deprecated:: 1.11.0
1478 This method will be removed in SciPy 1.14.0.
1479 Use array/matrix indexing instead.
1480 """
1481 msg = (
1482 "`getcol` is deprecated and will be removed in v1.14.0; "
1483 f"use `X[:, [{j}]]` instead."
1484 )
1485 warn(msg, DeprecationWarning, stacklevel=2)
1486 return self._getcol(j)
1488 def getrow(self, i):
1489 """Returns a copy of row i of the array/matrix, as a (1 x n) sparse
1490 array/matrix (row vector).
1492 .. deprecated:: 1.11.0
1493 This method will be removed in SciPy 1.14.0.
1494 Use array/matrix indexing instead.
1495 """
1496 msg = (
1497 "`getrow` is deprecated and will be removed in v1.14.0; "
1498 f"use `X[[{i}]]` instead."
1499 )
1500 warn(msg, DeprecationWarning, stacklevel=2)
1501 return self._getrow(i)
1503 ## End 1.14.0 deprecated methods
1506class sparray:
1507 """A namespace class to separate sparray from spmatrix"""
1508 pass
1510sparray.__doc__ = _spbase.__doc__
1513def issparse(x):
1514 """Is `x` of a sparse array or sparse matrix type?
1516 Parameters
1517 ----------
1518 x
1519 object to check for being a sparse array or sparse matrix
1521 Returns
1522 -------
1523 bool
1524 True if `x` is a sparse array or a sparse matrix, False otherwise
1526 Examples
1527 --------
1528 >>> import numpy as np
1529 >>> from scipy.sparse import csr_array, csr_matrix, issparse
1530 >>> issparse(csr_matrix([[5]]))
1531 True
1532 >>> issparse(csr_array([[5]]))
1533 True
1534 >>> issparse(np.array([[5]]))
1535 False
1536 >>> issparse(5)
1537 False
1538 """
1539 return isinstance(x, _spbase)
1542def isspmatrix(x):
1543 """Is `x` of a sparse matrix type?
1545 Parameters
1546 ----------
1547 x
1548 object to check for being a sparse matrix
1550 Returns
1551 -------
1552 bool
1553 True if `x` is a sparse matrix, False otherwise
1555 Examples
1556 --------
1557 >>> import numpy as np
1558 >>> from scipy.sparse import csr_array, csr_matrix, isspmatrix
1559 >>> isspmatrix(csr_matrix([[5]]))
1560 True
1561 >>> isspmatrix(csr_array([[5]]))
1562 False
1563 >>> isspmatrix(np.array([[5]]))
1564 False
1565 >>> isspmatrix(5)
1566 False
1567 """
1568 return isinstance(x, spmatrix)