Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/scipy/sparse/_base.py: 27%
511 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-12 06:31 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-12 06:31 +0000
1"""Base class for sparse matrices"""
2from warnings import warn
4import numpy as np
6from ._sputils import (asmatrix, check_reshape_kwargs, check_shape,
7 get_sum_dtype, isdense, isintlike, isscalarlike,
8 matrix, validateaxis)
10__all__ = ['spmatrix', 'isspmatrix', 'issparse',
11 'SparseWarning', 'SparseEfficiencyWarning']
14class SparseWarning(Warning):
15 pass
18class SparseFormatWarning(SparseWarning):
19 pass
22class SparseEfficiencyWarning(SparseWarning):
23 pass
26# The formats that we might potentially understand.
27_formats = {'csc': [0, "Compressed Sparse Column"],
28 'csr': [1, "Compressed Sparse Row"],
29 'dok': [2, "Dictionary Of Keys"],
30 'lil': [3, "List of Lists"],
31 'dod': [4, "Dictionary of Dictionaries"],
32 'sss': [5, "Symmetric Sparse Skyline"],
33 'coo': [6, "COOrdinate"],
34 'lba': [7, "Linpack BAnded"],
35 'egd': [8, "Ellpack-itpack Generalized Diagonal"],
36 'dia': [9, "DIAgonal"],
37 'bsr': [10, "Block Sparse Row"],
38 'msr': [11, "Modified compressed Sparse Row"],
39 'bsc': [12, "Block Sparse Column"],
40 'msc': [13, "Modified compressed Sparse Column"],
41 'ssk': [14, "Symmetric SKyline"],
42 'nsk': [15, "Nonsymmetric SKyline"],
43 'jad': [16, "JAgged Diagonal"],
44 'uss': [17, "Unsymmetric Sparse Skyline"],
45 'vbr': [18, "Variable Block Row"],
46 'und': [19, "Undefined"]
47 }
50# These univariate ufuncs preserve zeros.
51_ufuncs_with_fixed_point_at_zero = frozenset([
52 np.sin, np.tan, np.arcsin, np.arctan, np.sinh, np.tanh, np.arcsinh,
53 np.arctanh, np.rint, np.sign, np.expm1, np.log1p, np.deg2rad,
54 np.rad2deg, np.floor, np.ceil, np.trunc, np.sqrt])
57MAXPRINT = 50
60class spmatrix:
61 """ This class provides a base class for all sparse matrices. It
62 cannot be instantiated. Most of the work is provided by subclasses.
63 """
65 __array_priority__ = 10.1
66 ndim = 2
68 @property
69 def _bsr_container(self):
70 from ._bsr import bsr_matrix
71 return bsr_matrix
73 @property
74 def _coo_container(self):
75 from ._coo import coo_matrix
76 return coo_matrix
78 @property
79 def _csc_container(self):
80 from ._csc import csc_matrix
81 return csc_matrix
83 @property
84 def _csr_container(self):
85 from ._csr import csr_matrix
86 return csr_matrix
88 @property
89 def _dia_container(self):
90 from ._dia import dia_matrix
91 return dia_matrix
93 @property
94 def _dok_container(self):
95 from ._dok import dok_matrix
96 return dok_matrix
98 @property
99 def _lil_container(self):
100 from ._lil import lil_matrix
101 return lil_matrix
103 _is_array = False
105 def __init__(self, maxprint=MAXPRINT):
106 self._shape = None
107 if self.__class__.__name__ == 'spmatrix':
108 raise ValueError("This class is not intended"
109 " to be instantiated directly.")
110 self.maxprint = maxprint
112 def set_shape(self, shape):
113 """See `reshape`."""
114 # Make sure copy is False since this is in place
115 # Make sure format is unchanged because we are doing a __dict__ swap
116 new_matrix = self.reshape(shape, copy=False).asformat(self.format)
117 self.__dict__ = new_matrix.__dict__
119 def get_shape(self):
120 """Get shape of a matrix."""
121 return self._shape
123 shape = property(fget=get_shape, fset=set_shape)
125 def reshape(self, *args, **kwargs):
126 """reshape(self, shape, order='C', copy=False)
128 Gives a new shape to a sparse matrix without changing its data.
130 Parameters
131 ----------
132 shape : length-2 tuple of ints
133 The new shape should be compatible with the original shape.
134 order : {'C', 'F'}, optional
135 Read the elements using this index order. 'C' means to read and
136 write the elements using C-like index order; e.g., read entire first
137 row, then second row, etc. 'F' means to read and write the elements
138 using Fortran-like index order; e.g., read entire first column, then
139 second column, etc.
140 copy : bool, optional
141 Indicates whether or not attributes of self should be copied
142 whenever possible. The degree to which attributes are copied varies
143 depending on the type of sparse matrix being used.
145 Returns
146 -------
147 reshaped_matrix : sparse matrix
148 A sparse matrix with the given `shape`, not necessarily of the same
149 format as the current object.
151 See Also
152 --------
153 numpy.matrix.reshape : NumPy's implementation of 'reshape' for
154 matrices
155 """
156 # If the shape already matches, don't bother doing an actual reshape
157 # Otherwise, the default is to convert to COO and use its reshape
158 shape = check_shape(args, self.shape)
159 order, copy = check_reshape_kwargs(kwargs)
160 if shape == self.shape:
161 if copy:
162 return self.copy()
163 else:
164 return self
166 return self.tocoo(copy=copy).reshape(shape, order=order, copy=False)
168 def resize(self, shape):
169 """Resize the matrix in-place to dimensions given by ``shape``
171 Any elements that lie within the new shape will remain at the same
172 indices, while non-zero elements lying outside the new shape are
173 removed.
175 Parameters
176 ----------
177 shape : (int, int)
178 number of rows and columns in the new matrix
180 Notes
181 -----
182 The semantics are not identical to `numpy.ndarray.resize` or
183 `numpy.resize`. Here, the same data will be maintained at each index
184 before and after reshape, if that index is within the new bounds. In
185 numpy, resizing maintains contiguity of the array, moving elements
186 around in the logical matrix but not within a flattened representation.
188 We give no guarantees about whether the underlying data attributes
189 (arrays, etc.) will be modified in place or replaced with new objects.
190 """
191 # As an inplace operation, this requires implementation in each format.
192 raise NotImplementedError(
193 '{}.resize is not implemented'.format(type(self).__name__))
195 def astype(self, dtype, casting='unsafe', copy=True):
196 """Cast the matrix elements to a specified type.
198 Parameters
199 ----------
200 dtype : string or numpy dtype
201 Typecode or data-type to which to cast the data.
202 casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, optional
203 Controls what kind of data casting may occur.
204 Defaults to 'unsafe' for backwards compatibility.
205 'no' means the data types should not be cast at all.
206 'equiv' means only byte-order changes are allowed.
207 'safe' means only casts which can preserve values are allowed.
208 'same_kind' means only safe casts or casts within a kind,
209 like float64 to float32, are allowed.
210 'unsafe' means any data conversions may be done.
211 copy : bool, optional
212 If `copy` is `False`, the result might share some memory with this
213 matrix. If `copy` is `True`, it is guaranteed that the result and
214 this matrix do not share any memory.
215 """
217 dtype = np.dtype(dtype)
218 if self.dtype != dtype:
219 return self.tocsr().astype(
220 dtype, casting=casting, copy=copy).asformat(self.format)
221 elif copy:
222 return self.copy()
223 else:
224 return self
226 @classmethod
227 def _ascontainer(cls, X, **kwargs):
228 if cls._is_array:
229 return np.asarray(X, **kwargs)
230 else:
231 return asmatrix(X, **kwargs)
233 @classmethod
234 def _container(cls, X, **kwargs):
235 if cls._is_array:
236 return np.array(X, **kwargs)
237 else:
238 return matrix(X, **kwargs)
240 def asfptype(self):
241 """Upcast matrix to a floating point format (if necessary)"""
243 fp_types = ['f', 'd', 'F', 'D']
245 if self.dtype.char in fp_types:
246 return self
247 else:
248 for fp_type in fp_types:
249 if self.dtype <= np.dtype(fp_type):
250 return self.astype(fp_type)
252 raise TypeError('cannot upcast [%s] to a floating '
253 'point format' % self.dtype.name)
255 def __iter__(self):
256 for r in range(self.shape[0]):
257 yield self[r, :]
259 def getmaxprint(self):
260 """Maximum number of elements to display when printed."""
261 return self.maxprint
263 def count_nonzero(self):
264 """Number of non-zero entries, equivalent to
266 np.count_nonzero(a.toarray())
268 Unlike getnnz() and the nnz property, which return the number of stored
269 entries (the length of the data attribute), this method counts the
270 actual number of non-zero entries in data.
271 """
272 raise NotImplementedError("count_nonzero not implemented for %s." %
273 self.__class__.__name__)
275 def getnnz(self, axis=None):
276 """Number of stored values, including explicit zeros.
278 Parameters
279 ----------
280 axis : None, 0, or 1
281 Select between the number of values across the whole matrix, in
282 each column, or in each row.
284 See also
285 --------
286 count_nonzero : Number of non-zero entries
287 """
288 raise NotImplementedError("getnnz not implemented for %s." %
289 self.__class__.__name__)
291 @property
292 def nnz(self):
293 """Number of stored values, including explicit zeros.
295 See also
296 --------
297 count_nonzero : Number of non-zero entries
298 """
299 return self.getnnz()
301 def getformat(self):
302 """Format of a matrix representation as a string."""
303 return getattr(self, 'format', 'und')
305 def __repr__(self):
306 _, format_name = _formats[self.getformat()]
307 sparse_cls = 'array' if self._is_array else 'matrix'
308 return f"<%dx%d sparse {sparse_cls} of type '%s'\n" \
309 "\twith %d stored elements in %s format>" % \
310 (self.shape + (self.dtype.type, self.nnz, format_name))
312 def __str__(self):
313 maxprint = self.getmaxprint()
315 A = self.tocoo()
317 # helper function, outputs "(i,j) v"
318 def tostr(row, col, data):
319 triples = zip(list(zip(row, col)), data)
320 return '\n'.join([(' %s\t%s' % t) for t in triples])
322 if self.nnz > maxprint:
323 half = maxprint // 2
324 out = tostr(A.row[:half], A.col[:half], A.data[:half])
325 out += "\n :\t:\n"
326 half = maxprint - maxprint//2
327 out += tostr(A.row[-half:], A.col[-half:], A.data[-half:])
328 else:
329 out = tostr(A.row, A.col, A.data)
331 return out
333 def __bool__(self): # Simple -- other ideas?
334 if self.shape == (1, 1):
335 return self.nnz != 0
336 else:
337 raise ValueError("The truth value of an array with more than one "
338 "element is ambiguous. Use a.any() or a.all().")
339 __nonzero__ = __bool__
341 # What should len(sparse) return? For consistency with dense matrices,
342 # perhaps it should be the number of rows? But for some uses the number of
343 # non-zeros is more important. For now, raise an exception!
344 def __len__(self):
345 raise TypeError("sparse matrix length is ambiguous; use getnnz()"
346 " or shape[0]")
348 def asformat(self, format, copy=False):
349 """Return this matrix in the passed format.
351 Parameters
352 ----------
353 format : {str, None}
354 The desired matrix format ("csr", "csc", "lil", "dok", "array", ...)
355 or None for no conversion.
356 copy : bool, optional
357 If True, the result is guaranteed to not share data with self.
359 Returns
360 -------
361 A : This matrix in the passed format.
362 """
363 if format is None or format == self.format:
364 if copy:
365 return self.copy()
366 else:
367 return self
368 else:
369 try:
370 convert_method = getattr(self, 'to' + format)
371 except AttributeError as e:
372 raise ValueError('Format {} is unknown.'.format(format)) from e
374 # Forward the copy kwarg, if it's accepted.
375 try:
376 return convert_method(copy=copy)
377 except TypeError:
378 return convert_method()
380 ###################################################################
381 # NOTE: All arithmetic operations use csr_matrix by default.
382 # Therefore a new sparse matrix format just needs to define a
383 # .tocsr() method to provide arithmetic support. Any of these
384 # methods can be overridden for efficiency.
385 ####################################################################
387 def multiply(self, other):
388 """Point-wise multiplication by another matrix
389 """
390 return self.tocsr().multiply(other)
392 def maximum(self, other):
393 """Element-wise maximum between this and another matrix."""
394 return self.tocsr().maximum(other)
396 def minimum(self, other):
397 """Element-wise minimum between this and another matrix."""
398 return self.tocsr().minimum(other)
400 def dot(self, other):
401 """Ordinary dot product
403 Examples
404 --------
405 >>> import numpy as np
406 >>> from scipy.sparse import csr_matrix
407 >>> A = csr_matrix([[1, 2, 0], [0, 0, 3], [4, 0, 5]])
408 >>> v = np.array([1, 0, -1])
409 >>> A.dot(v)
410 array([ 1, -3, -1], dtype=int64)
412 """
413 if np.isscalar(other):
414 return self * other
415 else:
416 return self @ other
418 def power(self, n, dtype=None):
419 """Element-wise power."""
420 return self.tocsr().power(n, dtype=dtype)
422 def __eq__(self, other):
423 return self.tocsr().__eq__(other)
425 def __ne__(self, other):
426 return self.tocsr().__ne__(other)
428 def __lt__(self, other):
429 return self.tocsr().__lt__(other)
431 def __gt__(self, other):
432 return self.tocsr().__gt__(other)
434 def __le__(self, other):
435 return self.tocsr().__le__(other)
437 def __ge__(self, other):
438 return self.tocsr().__ge__(other)
440 def __abs__(self):
441 return abs(self.tocsr())
443 def __round__(self, ndigits=0):
444 return round(self.tocsr(), ndigits=ndigits)
446 def _add_sparse(self, other):
447 return self.tocsr()._add_sparse(other)
449 def _add_dense(self, other):
450 return self.tocoo()._add_dense(other)
452 def _sub_sparse(self, other):
453 return self.tocsr()._sub_sparse(other)
455 def _sub_dense(self, other):
456 return self.todense() - other
458 def _rsub_dense(self, other):
459 # note: this can't be replaced by other + (-self) for unsigned types
460 return other - self.todense()
462 def __add__(self, other): # self + other
463 if isscalarlike(other):
464 if other == 0:
465 return self.copy()
466 # Now we would add this scalar to every element.
467 raise NotImplementedError('adding a nonzero scalar to a '
468 'sparse matrix is not supported')
469 elif isspmatrix(other):
470 if other.shape != self.shape:
471 raise ValueError("inconsistent shapes")
472 return self._add_sparse(other)
473 elif isdense(other):
474 other = np.broadcast_to(other, self.shape)
475 return self._add_dense(other)
476 else:
477 return NotImplemented
479 def __radd__(self,other): # other + self
480 return self.__add__(other)
482 def __sub__(self, other): # self - other
483 if isscalarlike(other):
484 if other == 0:
485 return self.copy()
486 raise NotImplementedError('subtracting a nonzero scalar from a '
487 'sparse matrix is not supported')
488 elif isspmatrix(other):
489 if other.shape != self.shape:
490 raise ValueError("inconsistent shapes")
491 return self._sub_sparse(other)
492 elif isdense(other):
493 other = np.broadcast_to(other, self.shape)
494 return self._sub_dense(other)
495 else:
496 return NotImplemented
498 def __rsub__(self,other): # other - self
499 if isscalarlike(other):
500 if other == 0:
501 return -self.copy()
502 raise NotImplementedError('subtracting a sparse matrix from a '
503 'nonzero scalar is not supported')
504 elif isdense(other):
505 other = np.broadcast_to(other, self.shape)
506 return self._rsub_dense(other)
507 else:
508 return NotImplemented
510 def _mul_dispatch(self, other):
511 """`np.matrix`-compatible mul, i.e. `dot` or `NotImplemented`
513 interpret other and call one of the following
514 self._mul_scalar()
515 self._mul_vector()
516 self._mul_multivector()
517 self._mul_sparse_matrix()
518 """
519 # This method has to be different from `__mul__` because it is also
520 # called by sparse array classes via matmul, while their mul is
521 # elementwise.
523 M, N = self.shape
525 if other.__class__ is np.ndarray:
526 # Fast path for the most common case
527 if other.shape == (N,):
528 return self._mul_vector(other)
529 elif other.shape == (N, 1):
530 return self._mul_vector(other.ravel()).reshape(M, 1)
531 elif other.ndim == 2 and other.shape[0] == N:
532 return self._mul_multivector(other)
534 if isscalarlike(other):
535 # scalar value
536 return self._mul_scalar(other)
538 if issparse(other):
539 if self.shape[1] != other.shape[0]:
540 raise ValueError('dimension mismatch')
541 return self._mul_sparse_matrix(other)
543 # If it's a list or whatever, treat it like a matrix
544 other_a = np.asanyarray(other)
546 if other_a.ndim == 0 and other_a.dtype == np.object_:
547 # Not interpretable as an array; return NotImplemented so that
548 # other's __rmul__ can kick in if that's implemented.
549 return NotImplemented
551 try:
552 other.shape
553 except AttributeError:
554 other = other_a
556 if other.ndim == 1 or other.ndim == 2 and other.shape[1] == 1:
557 # dense row or column vector
558 if other.shape != (N,) and other.shape != (N, 1):
559 raise ValueError('dimension mismatch')
561 result = self._mul_vector(np.ravel(other))
563 if isinstance(other, np.matrix):
564 result = self._ascontainer(result)
566 if other.ndim == 2 and other.shape[1] == 1:
567 # If 'other' was an (nx1) column vector, reshape the result
568 result = result.reshape(-1, 1)
570 return result
572 elif other.ndim == 2:
573 ##
574 # dense 2D array or matrix ("multivector")
576 if other.shape[0] != self.shape[1]:
577 raise ValueError('dimension mismatch')
579 result = self._mul_multivector(np.asarray(other))
581 if isinstance(other, np.matrix):
582 result = self._ascontainer(result)
584 return result
586 else:
587 raise ValueError('could not interpret dimensions')
589 def __mul__(self, other):
590 return self._mul_dispatch(other)
592 # by default, use CSR for __mul__ handlers
593 def _mul_scalar(self, other):
594 return self.tocsr()._mul_scalar(other)
596 def _mul_vector(self, other):
597 return self.tocsr()._mul_vector(other)
599 def _mul_multivector(self, other):
600 return self.tocsr()._mul_multivector(other)
602 def _mul_sparse_matrix(self, other):
603 return self.tocsr()._mul_sparse_matrix(other)
605 def _rmul_dispatch(self, other):
606 if isscalarlike(other):
607 return self._mul_scalar(other)
608 else:
609 # Don't use asarray unless we have to
610 try:
611 tr = other.transpose()
612 except AttributeError:
613 tr = np.asarray(other).transpose()
614 ret = self.transpose()._mul_dispatch(tr)
615 if ret is NotImplemented:
616 return NotImplemented
617 return ret.transpose()
619 def __rmul__(self, other): # other * self
620 return self._rmul_dispatch(other)
622 #######################
623 # matmul (@) operator #
624 #######################
626 def __matmul__(self, other):
627 if isscalarlike(other):
628 raise ValueError("Scalar operands are not allowed, "
629 "use '*' instead")
630 return self._mul_dispatch(other)
632 def __rmatmul__(self, other):
633 if isscalarlike(other):
634 raise ValueError("Scalar operands are not allowed, "
635 "use '*' instead")
636 return self._rmul_dispatch(other)
638 ####################
639 # Other Arithmetic #
640 ####################
642 def _divide(self, other, true_divide=False, rdivide=False):
643 if isscalarlike(other):
644 if rdivide:
645 if true_divide:
646 return np.true_divide(other, self.todense())
647 else:
648 return np.divide(other, self.todense())
650 if true_divide and np.can_cast(self.dtype, np.float_):
651 return self.astype(np.float_)._mul_scalar(1./other)
652 else:
653 r = self._mul_scalar(1./other)
655 scalar_dtype = np.asarray(other).dtype
656 if (np.issubdtype(self.dtype, np.integer) and
657 np.issubdtype(scalar_dtype, np.integer)):
658 return r.astype(self.dtype)
659 else:
660 return r
662 elif isdense(other):
663 if not rdivide:
664 if true_divide:
665 return np.true_divide(self.todense(), other)
666 else:
667 return np.divide(self.todense(), other)
668 else:
669 if true_divide:
670 return np.true_divide(other, self.todense())
671 else:
672 return np.divide(other, self.todense())
673 elif isspmatrix(other):
674 if rdivide:
675 return other._divide(self, true_divide, rdivide=False)
677 self_csr = self.tocsr()
678 if true_divide and np.can_cast(self.dtype, np.float_):
679 return self_csr.astype(np.float_)._divide_sparse(other)
680 else:
681 return self_csr._divide_sparse(other)
682 else:
683 return NotImplemented
685 def __truediv__(self, other):
686 return self._divide(other, true_divide=True)
688 def __div__(self, other):
689 # Always do true division
690 return self._divide(other, true_divide=True)
692 def __rtruediv__(self, other):
693 # Implementing this as the inverse would be too magical -- bail out
694 return NotImplemented
696 def __rdiv__(self, other):
697 # Implementing this as the inverse would be too magical -- bail out
698 return NotImplemented
700 def __neg__(self):
701 return -self.tocsr()
703 def __iadd__(self, other):
704 return NotImplemented
706 def __isub__(self, other):
707 return NotImplemented
709 def __imul__(self, other):
710 return NotImplemented
712 def __idiv__(self, other):
713 return self.__itruediv__(other)
715 def __itruediv__(self, other):
716 return NotImplemented
718 def __pow__(self, other):
719 M, N = self.shape[0], self.shape[1]
720 if M != N:
721 raise TypeError('matrix is not square')
723 if isintlike(other):
724 other = int(other)
725 if other < 0:
726 raise ValueError('exponent must be >= 0')
728 if other == 0:
729 from ._construct import eye
730 E = eye(M, dtype=self.dtype)
731 if self._is_array:
732 from ._arrays import dia_array
733 E = dia_array(E)
734 return E
736 elif other == 1:
737 return self.copy()
738 else:
739 tmp = self.__pow__(other//2)
740 if (other % 2):
741 return self @ tmp @ tmp
742 else:
743 return tmp @ tmp
744 elif isscalarlike(other):
745 raise ValueError('exponent must be an integer')
746 else:
747 return NotImplemented
749 def __getattr__(self, attr):
750 if attr == 'A':
751 if self._is_array:
752 warn(np.VisibleDeprecationWarning(
753 "Please use `.todense()` instead"
754 ))
755 return self.toarray()
756 elif attr == 'T':
757 return self.transpose()
758 elif attr == 'H':
759 if self._is_array:
760 warn(np.VisibleDeprecationWarning(
761 "Please use `.conj().T` instead"
762 ))
763 return self.getH()
764 elif attr == 'real':
765 return self._real()
766 elif attr == 'imag':
767 return self._imag()
768 elif attr == 'size':
769 return self.getnnz()
770 else:
771 raise AttributeError(attr + " not found")
773 def transpose(self, axes=None, copy=False):
774 """
775 Reverses the dimensions of the sparse matrix.
777 Parameters
778 ----------
779 axes : None, optional
780 This argument is in the signature *solely* for NumPy
781 compatibility reasons. Do not pass in anything except
782 for the default value.
783 copy : bool, optional
784 Indicates whether or not attributes of `self` should be
785 copied whenever possible. The degree to which attributes
786 are copied varies depending on the type of sparse matrix
787 being used.
789 Returns
790 -------
791 p : `self` with the dimensions reversed.
793 See Also
794 --------
795 numpy.matrix.transpose : NumPy's implementation of 'transpose'
796 for matrices
797 """
798 return self.tocsr(copy=copy).transpose(axes=axes, copy=False)
800 def conj(self, copy=True):
801 """Element-wise complex conjugation.
803 If the matrix is of non-complex data type and `copy` is False,
804 this method does nothing and the data is not copied.
806 Parameters
807 ----------
808 copy : bool, optional
809 If True, the result is guaranteed to not share data with self.
811 Returns
812 -------
813 A : The element-wise complex conjugate.
815 """
816 if np.issubdtype(self.dtype, np.complexfloating):
817 return self.tocsr(copy=copy).conj(copy=False)
818 elif copy:
819 return self.copy()
820 else:
821 return self
823 def conjugate(self, copy=True):
824 return self.conj(copy=copy)
826 conjugate.__doc__ = conj.__doc__
828 # Renamed conjtranspose() -> getH() for compatibility with dense matrices
829 def getH(self):
830 """Return the Hermitian transpose of this matrix.
832 See Also
833 --------
834 numpy.matrix.getH : NumPy's implementation of `getH` for matrices
835 """
836 return self.transpose().conj()
838 def _real(self):
839 return self.tocsr()._real()
841 def _imag(self):
842 return self.tocsr()._imag()
844 def nonzero(self):
845 """nonzero indices
847 Returns a tuple of arrays (row,col) containing the indices
848 of the non-zero elements of the matrix.
850 Examples
851 --------
852 >>> from scipy.sparse import csr_matrix
853 >>> A = csr_matrix([[1,2,0],[0,0,3],[4,0,5]])
854 >>> A.nonzero()
855 (array([0, 0, 1, 2, 2]), array([0, 1, 2, 0, 2]))
857 """
859 # convert to COOrdinate format
860 A = self.tocoo()
861 nz_mask = A.data != 0
862 return (A.row[nz_mask], A.col[nz_mask])
864 def getcol(self, j):
865 """Returns a copy of column j of the matrix, as an (m x 1) sparse
866 matrix (column vector).
867 """
868 # Spmatrix subclasses should override this method for efficiency.
869 # Post-multiply by a (n x 1) column vector 'a' containing all zeros
870 # except for a_j = 1
871 n = self.shape[1]
872 if j < 0:
873 j += n
874 if j < 0 or j >= n:
875 raise IndexError("index out of bounds")
876 col_selector = self._csc_container(([1], [[j], [0]]),
877 shape=(n, 1), dtype=self.dtype)
878 return self @ col_selector
880 def getrow(self, i):
881 """Returns a copy of row i of the matrix, as a (1 x n) sparse
882 matrix (row vector).
883 """
884 # Spmatrix subclasses should override this method for efficiency.
885 # Pre-multiply by a (1 x m) row vector 'a' containing all zeros
886 # except for a_i = 1
887 m = self.shape[0]
888 if i < 0:
889 i += m
890 if i < 0 or i >= m:
891 raise IndexError("index out of bounds")
892 row_selector = self._csr_container(([1], [[0], [i]]),
893 shape=(1, m), dtype=self.dtype)
894 return row_selector @ self
896 # The following dunder methods cannot be implemented.
897 #
898 # def __array__(self):
899 # # Sparse matrices rely on NumPy wrapping them in object arrays under
900 # # the hood to make unary ufuncs work on them. So we cannot raise
901 # # TypeError here - which would be handy to not give users object
902 # # arrays they probably don't want (they're looking for `.toarray()`).
903 # #
904 # # Conversion with `toarray()` would also break things because of the
905 # # behavior discussed above, plus we want to avoid densification by
906 # # accident because that can too easily blow up memory.
907 #
908 # def __array_ufunc__(self):
909 # # We cannot implement __array_ufunc__ due to mismatching semantics.
910 # # See gh-7707 and gh-7349 for details.
911 #
912 # def __array_function__(self):
913 # # We cannot implement __array_function__ due to mismatching semantics.
914 # # See gh-10362 for details.
916 def todense(self, order=None, out=None):
917 """
918 Return a dense matrix representation of this matrix.
920 Parameters
921 ----------
922 order : {'C', 'F'}, optional
923 Whether to store multi-dimensional data in C (row-major)
924 or Fortran (column-major) order in memory. The default
925 is 'None', which provides no ordering guarantees.
926 Cannot be specified in conjunction with the `out`
927 argument.
929 out : ndarray, 2-D, optional
930 If specified, uses this array (or `numpy.matrix`) as the
931 output buffer instead of allocating a new array to
932 return. The provided array must have the same shape and
933 dtype as the sparse matrix on which you are calling the
934 method.
936 Returns
937 -------
938 arr : numpy.matrix, 2-D
939 A NumPy matrix object with the same shape and containing
940 the same data represented by the sparse matrix, with the
941 requested memory order. If `out` was passed and was an
942 array (rather than a `numpy.matrix`), it will be filled
943 with the appropriate values and returned wrapped in a
944 `numpy.matrix` object that shares the same memory.
945 """
946 return self._ascontainer(self.toarray(order=order, out=out))
948 def toarray(self, order=None, out=None):
949 """
950 Return a dense ndarray representation of this matrix.
952 Parameters
953 ----------
954 order : {'C', 'F'}, optional
955 Whether to store multidimensional data in C (row-major)
956 or Fortran (column-major) order in memory. The default
957 is 'None', which provides no ordering guarantees.
958 Cannot be specified in conjunction with the `out`
959 argument.
961 out : ndarray, 2-D, optional
962 If specified, uses this array as the output buffer
963 instead of allocating a new array to return. The provided
964 array must have the same shape and dtype as the sparse
965 matrix on which you are calling the method. For most
966 sparse types, `out` is required to be memory contiguous
967 (either C or Fortran ordered).
969 Returns
970 -------
971 arr : ndarray, 2-D
972 An array with the same shape and containing the same
973 data represented by the sparse matrix, with the requested
974 memory order. If `out` was passed, the same object is
975 returned after being modified in-place to contain the
976 appropriate values.
977 """
978 return self.tocoo(copy=False).toarray(order=order, out=out)
980 # Any sparse matrix format deriving from spmatrix must define one of
981 # tocsr or tocoo. The other conversion methods may be implemented for
982 # efficiency, but are not required.
983 def tocsr(self, copy=False):
984 """Convert this matrix to Compressed Sparse Row format.
986 With copy=False, the data/indices may be shared between this matrix and
987 the resultant csr_matrix.
988 """
989 return self.tocoo(copy=copy).tocsr(copy=False)
991 def todok(self, copy=False):
992 """Convert this matrix to Dictionary Of Keys format.
994 With copy=False, the data/indices may be shared between this matrix and
995 the resultant dok_matrix.
996 """
997 return self.tocoo(copy=copy).todok(copy=False)
999 def tocoo(self, copy=False):
1000 """Convert this matrix to COOrdinate format.
1002 With copy=False, the data/indices may be shared between this matrix and
1003 the resultant coo_matrix.
1004 """
1005 return self.tocsr(copy=False).tocoo(copy=copy)
1007 def tolil(self, copy=False):
1008 """Convert this matrix to List of Lists format.
1010 With copy=False, the data/indices may be shared between this matrix and
1011 the resultant lil_matrix.
1012 """
1013 return self.tocsr(copy=False).tolil(copy=copy)
1015 def todia(self, copy=False):
1016 """Convert this matrix to sparse DIAgonal format.
1018 With copy=False, the data/indices may be shared between this matrix and
1019 the resultant dia_matrix.
1020 """
1021 return self.tocoo(copy=copy).todia(copy=False)
1023 def tobsr(self, blocksize=None, copy=False):
1024 """Convert this matrix to Block Sparse Row format.
1026 With copy=False, the data/indices may be shared between this matrix and
1027 the resultant bsr_matrix.
1029 When blocksize=(R, C) is provided, it will be used for construction of
1030 the bsr_matrix.
1031 """
1032 return self.tocsr(copy=False).tobsr(blocksize=blocksize, copy=copy)
1034 def tocsc(self, copy=False):
1035 """Convert this matrix to Compressed Sparse Column format.
1037 With copy=False, the data/indices may be shared between this matrix and
1038 the resultant csc_matrix.
1039 """
1040 return self.tocsr(copy=copy).tocsc(copy=False)
1042 def copy(self):
1043 """Returns a copy of this matrix.
1045 No data/indices will be shared between the returned value and current
1046 matrix.
1047 """
1048 return self.__class__(self, copy=True)
1050 def sum(self, axis=None, dtype=None, out=None):
1051 """
1052 Sum the matrix elements over a given axis.
1054 Parameters
1055 ----------
1056 axis : {-2, -1, 0, 1, None} optional
1057 Axis along which the sum is computed. The default is to
1058 compute the sum of all the matrix elements, returning a scalar
1059 (i.e., `axis` = `None`).
1060 dtype : dtype, optional
1061 The type of the returned matrix and of the accumulator in which
1062 the elements are summed. The dtype of `a` is used by default
1063 unless `a` has an integer dtype of less precision than the default
1064 platform integer. In that case, if `a` is signed then the platform
1065 integer is used while if `a` is unsigned then an unsigned integer
1066 of the same precision as the platform integer is used.
1068 .. versionadded:: 0.18.0
1070 out : np.matrix, optional
1071 Alternative output matrix in which to place the result. It must
1072 have the same shape as the expected output, but the type of the
1073 output values will be cast if necessary.
1075 .. versionadded:: 0.18.0
1077 Returns
1078 -------
1079 sum_along_axis : np.matrix
1080 A matrix with the same shape as `self`, with the specified
1081 axis removed.
1083 See Also
1084 --------
1085 numpy.matrix.sum : NumPy's implementation of 'sum' for matrices
1087 """
1088 validateaxis(axis)
1090 # We use multiplication by a matrix of ones to achieve this.
1091 # For some sparse matrix formats more efficient methods are
1092 # possible -- these should override this function.
1093 m, n = self.shape
1095 # Mimic numpy's casting.
1096 res_dtype = get_sum_dtype(self.dtype)
1098 if axis is None:
1099 # sum over rows and columns
1100 return (
1101 self @ self._ascontainer(np.ones((n, 1), dtype=res_dtype))
1102 ).sum(dtype=dtype, out=out)
1104 if axis < 0:
1105 axis += 2
1107 # axis = 0 or 1 now
1108 if axis == 0:
1109 # sum over columns
1110 ret = self._ascontainer(
1111 np.ones((1, m), dtype=res_dtype)
1112 ) @ self
1113 else:
1114 # sum over rows
1115 ret = self @ self._ascontainer(
1116 np.ones((n, 1), dtype=res_dtype)
1117 )
1119 if out is not None and out.shape != ret.shape:
1120 raise ValueError("dimensions do not match")
1122 return ret.sum(axis=axis, dtype=dtype, out=out)
1124 def mean(self, axis=None, dtype=None, out=None):
1125 """
1126 Compute the arithmetic mean along the specified axis.
1128 Returns the average of the matrix elements. The average is taken
1129 over all elements in the matrix by default, otherwise over the
1130 specified axis. `float64` intermediate and return values are used
1131 for integer inputs.
1133 Parameters
1134 ----------
1135 axis : {-2, -1, 0, 1, None} optional
1136 Axis along which the mean is computed. The default is to compute
1137 the mean of all elements in the matrix (i.e., `axis` = `None`).
1138 dtype : data-type, optional
1139 Type to use in computing the mean. For integer inputs, the default
1140 is `float64`; for floating point inputs, it is the same as the
1141 input dtype.
1143 .. versionadded:: 0.18.0
1145 out : np.matrix, optional
1146 Alternative output matrix in which to place the result. It must
1147 have the same shape as the expected output, but the type of the
1148 output values will be cast if necessary.
1150 .. versionadded:: 0.18.0
1152 Returns
1153 -------
1154 m : np.matrix
1156 See Also
1157 --------
1158 numpy.matrix.mean : NumPy's implementation of 'mean' for matrices
1160 """
1161 def _is_integral(dtype):
1162 return (np.issubdtype(dtype, np.integer) or
1163 np.issubdtype(dtype, np.bool_))
1165 validateaxis(axis)
1167 res_dtype = self.dtype.type
1168 integral = _is_integral(self.dtype)
1170 # output dtype
1171 if dtype is None:
1172 if integral:
1173 res_dtype = np.float64
1174 else:
1175 res_dtype = np.dtype(dtype).type
1177 # intermediate dtype for summation
1178 inter_dtype = np.float64 if integral else res_dtype
1179 inter_self = self.astype(inter_dtype)
1181 if axis is None:
1182 return (inter_self / np.array(
1183 self.shape[0] * self.shape[1]))\
1184 .sum(dtype=res_dtype, out=out)
1186 if axis < 0:
1187 axis += 2
1189 # axis = 0 or 1 now
1190 if axis == 0:
1191 return (inter_self * (1.0 / self.shape[0])).sum(
1192 axis=0, dtype=res_dtype, out=out)
1193 else:
1194 return (inter_self * (1.0 / self.shape[1])).sum(
1195 axis=1, dtype=res_dtype, out=out)
1197 def diagonal(self, k=0):
1198 """Returns the kth diagonal of the matrix.
1200 Parameters
1201 ----------
1202 k : int, optional
1203 Which diagonal to get, corresponding to elements a[i, i+k].
1204 Default: 0 (the main diagonal).
1206 .. versionadded:: 1.0
1208 See also
1209 --------
1210 numpy.diagonal : Equivalent numpy function.
1212 Examples
1213 --------
1214 >>> from scipy.sparse import csr_matrix
1215 >>> A = csr_matrix([[1, 2, 0], [0, 0, 3], [4, 0, 5]])
1216 >>> A.diagonal()
1217 array([1, 0, 5])
1218 >>> A.diagonal(k=1)
1219 array([2, 3])
1220 """
1221 return self.tocsr().diagonal(k=k)
1223 def trace(self, offset=0):
1224 """Returns the sum along diagonals of the sparse matrix.
1226 Parameters
1227 ----------
1228 offset : int, optional
1229 Which diagonal to get, corresponding to elements a[i, i+offset].
1230 Default: 0 (the main diagonal).
1232 """
1233 return self.diagonal(k=offset).sum()
1235 def setdiag(self, values, k=0):
1236 """
1237 Set diagonal or off-diagonal elements of the array.
1239 Parameters
1240 ----------
1241 values : array_like
1242 New values of the diagonal elements.
1244 Values may have any length. If the diagonal is longer than values,
1245 then the remaining diagonal entries will not be set. If values are
1246 longer than the diagonal, then the remaining values are ignored.
1248 If a scalar value is given, all of the diagonal is set to it.
1250 k : int, optional
1251 Which off-diagonal to set, corresponding to elements a[i,i+k].
1252 Default: 0 (the main diagonal).
1254 """
1255 M, N = self.shape
1256 if (k > 0 and k >= N) or (k < 0 and -k >= M):
1257 raise ValueError("k exceeds matrix dimensions")
1258 self._setdiag(np.asarray(values), k)
1260 def _setdiag(self, values, k):
1261 M, N = self.shape
1262 if k < 0:
1263 if values.ndim == 0:
1264 # broadcast
1265 max_index = min(M+k, N)
1266 for i in range(max_index):
1267 self[i - k, i] = values
1268 else:
1269 max_index = min(M+k, N, len(values))
1270 if max_index <= 0:
1271 return
1272 for i, v in enumerate(values[:max_index]):
1273 self[i - k, i] = v
1274 else:
1275 if values.ndim == 0:
1276 # broadcast
1277 max_index = min(M, N-k)
1278 for i in range(max_index):
1279 self[i, i + k] = values
1280 else:
1281 max_index = min(M, N-k, len(values))
1282 if max_index <= 0:
1283 return
1284 for i, v in enumerate(values[:max_index]):
1285 self[i, i + k] = v
1287 def _process_toarray_args(self, order, out):
1288 if out is not None:
1289 if order is not None:
1290 raise ValueError('order cannot be specified if out '
1291 'is not None')
1292 if out.shape != self.shape or out.dtype != self.dtype:
1293 raise ValueError('out array must be same dtype and shape as '
1294 'sparse matrix')
1295 out[...] = 0.
1296 return out
1297 else:
1298 return np.zeros(self.shape, dtype=self.dtype, order=order)
1301def isspmatrix(x):
1302 """Is x of a sparse matrix type?
1304 Parameters
1305 ----------
1306 x
1307 object to check for being a sparse matrix
1309 Returns
1310 -------
1311 bool
1312 True if x is a sparse matrix, False otherwise
1314 Notes
1315 -----
1316 issparse and isspmatrix are aliases for the same function.
1318 Examples
1319 --------
1320 >>> from scipy.sparse import csr_matrix, isspmatrix
1321 >>> isspmatrix(csr_matrix([[5]]))
1322 True
1324 >>> from scipy.sparse import isspmatrix
1325 >>> isspmatrix(5)
1326 False
1327 """
1328 return isinstance(x, spmatrix)
1331issparse = isspmatrix