Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/scipy/sparse/_data.py: 25%
182 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 matrice with a .data attribute
3 subclasses must provide a _with_data() method that
4 creates a new matrix with the same sparsity pattern
5 as self but with a different data array
7"""
9import numpy as np
11from ._base import spmatrix, _ufuncs_with_fixed_point_at_zero
12from ._sputils import isscalarlike, validateaxis, matrix
14__all__ = []
17# TODO implement all relevant operations
18# use .data.__methods__() instead of /=, *=, etc.
19class _data_matrix(spmatrix):
20 def __init__(self):
21 spmatrix.__init__(self)
23 def _get_dtype(self):
24 return self.data.dtype
26 def _set_dtype(self, newtype):
27 self.data.dtype = newtype
28 dtype = property(fget=_get_dtype, fset=_set_dtype)
30 def _deduped_data(self):
31 if hasattr(self, 'sum_duplicates'):
32 self.sum_duplicates()
33 return self.data
35 def __abs__(self):
36 return self._with_data(abs(self._deduped_data()))
38 def __round__(self, ndigits=0):
39 return self._with_data(np.around(self._deduped_data(), decimals=ndigits))
41 def _real(self):
42 return self._with_data(self.data.real)
44 def _imag(self):
45 return self._with_data(self.data.imag)
47 def __neg__(self):
48 if self.dtype.kind == 'b':
49 raise NotImplementedError('negating a sparse boolean '
50 'matrix is not supported')
51 return self._with_data(-self.data)
53 def __imul__(self, other): # self *= other
54 if isscalarlike(other):
55 self.data *= other
56 return self
57 else:
58 return NotImplemented
60 def __itruediv__(self, other): # self /= other
61 if isscalarlike(other):
62 recip = 1.0 / other
63 self.data *= recip
64 return self
65 else:
66 return NotImplemented
68 def astype(self, dtype, casting='unsafe', copy=True):
69 dtype = np.dtype(dtype)
70 if self.dtype != dtype:
71 return self._with_data(
72 self._deduped_data().astype(dtype, casting=casting, copy=copy),
73 copy=copy)
74 elif copy:
75 return self.copy()
76 else:
77 return self
79 astype.__doc__ = spmatrix.astype.__doc__
81 def conj(self, copy=True):
82 if np.issubdtype(self.dtype, np.complexfloating):
83 return self._with_data(self.data.conj(), copy=copy)
84 elif copy:
85 return self.copy()
86 else:
87 return self
89 conj.__doc__ = spmatrix.conj.__doc__
91 def copy(self):
92 return self._with_data(self.data.copy(), copy=True)
94 copy.__doc__ = spmatrix.copy.__doc__
96 def count_nonzero(self):
97 return np.count_nonzero(self._deduped_data())
99 count_nonzero.__doc__ = spmatrix.count_nonzero.__doc__
101 def power(self, n, dtype=None):
102 """
103 This function performs element-wise power.
105 Parameters
106 ----------
107 n : n is a scalar
109 dtype : If dtype is not specified, the current dtype will be preserved.
110 """
111 if not isscalarlike(n):
112 raise NotImplementedError("input is not scalar")
114 data = self._deduped_data()
115 if dtype is not None:
116 data = data.astype(dtype)
117 return self._with_data(data ** n)
119 ###########################
120 # Multiplication handlers #
121 ###########################
123 def _mul_scalar(self, other):
124 return self._with_data(self.data * other)
127# Add the numpy unary ufuncs for which func(0) = 0 to _data_matrix.
128for npfunc in _ufuncs_with_fixed_point_at_zero:
129 name = npfunc.__name__
131 def _create_method(op):
132 def method(self):
133 result = op(self._deduped_data())
134 return self._with_data(result, copy=True)
136 method.__doc__ = ("Element-wise %s.\n\n"
137 "See `numpy.%s` for more information." % (name, name))
138 method.__name__ = name
140 return method
142 setattr(_data_matrix, name, _create_method(npfunc))
145def _find_missing_index(ind, n):
146 for k, a in enumerate(ind):
147 if k != a:
148 return k
150 k += 1
151 if k < n:
152 return k
153 else:
154 return -1
157class _minmax_mixin:
158 """Mixin for min and max methods.
160 These are not implemented for dia_matrix, hence the separate class.
161 """
163 def _min_or_max_axis(self, axis, min_or_max):
164 N = self.shape[axis]
165 if N == 0:
166 raise ValueError("zero-size array to reduction operation")
167 M = self.shape[1 - axis]
169 mat = self.tocsc() if axis == 0 else self.tocsr()
170 mat.sum_duplicates()
172 major_index, value = mat._minor_reduce(min_or_max)
173 not_full = np.diff(mat.indptr)[major_index] < N
174 value[not_full] = min_or_max(value[not_full], 0)
176 mask = value != 0
177 major_index = np.compress(mask, major_index)
178 value = np.compress(mask, value)
180 if axis == 0:
181 return self._coo_container(
182 (value, (np.zeros(len(value)), major_index)),
183 dtype=self.dtype, shape=(1, M)
184 )
185 else:
186 return self._coo_container(
187 (value, (major_index, np.zeros(len(value)))),
188 dtype=self.dtype, shape=(M, 1)
189 )
191 def _min_or_max(self, axis, out, min_or_max):
192 if out is not None:
193 raise ValueError(("Sparse matrices do not support "
194 "an 'out' parameter."))
196 validateaxis(axis)
198 if axis is None:
199 if 0 in self.shape:
200 raise ValueError("zero-size array to reduction operation")
202 zero = self.dtype.type(0)
203 if self.nnz == 0:
204 return zero
205 m = min_or_max.reduce(self._deduped_data().ravel())
206 if self.nnz != np.prod(self.shape):
207 m = min_or_max(zero, m)
208 return m
210 if axis < 0:
211 axis += 2
213 if (axis == 0) or (axis == 1):
214 return self._min_or_max_axis(axis, min_or_max)
215 else:
216 raise ValueError("axis out of range")
218 def _arg_min_or_max_axis(self, axis, op, compare):
219 if self.shape[axis] == 0:
220 raise ValueError("Can't apply the operation along a zero-sized "
221 "dimension.")
223 if axis < 0:
224 axis += 2
226 zero = self.dtype.type(0)
228 mat = self.tocsc() if axis == 0 else self.tocsr()
229 mat.sum_duplicates()
231 ret_size, line_size = mat._swap(mat.shape)
232 ret = np.zeros(ret_size, dtype=int)
234 nz_lines, = np.nonzero(np.diff(mat.indptr))
235 for i in nz_lines:
236 p, q = mat.indptr[i:i + 2]
237 data = mat.data[p:q]
238 indices = mat.indices[p:q]
239 am = op(data)
240 m = data[am]
241 if compare(m, zero) or q - p == line_size:
242 ret[i] = indices[am]
243 else:
244 zero_ind = _find_missing_index(indices, line_size)
245 if m == zero:
246 ret[i] = min(am, zero_ind)
247 else:
248 ret[i] = zero_ind
250 if axis == 1:
251 ret = ret.reshape(-1, 1)
253 return matrix(ret)
255 def _arg_min_or_max(self, axis, out, op, compare):
256 if out is not None:
257 raise ValueError("Sparse matrices do not support "
258 "an 'out' parameter.")
260 validateaxis(axis)
262 if axis is None:
263 if 0 in self.shape:
264 raise ValueError("Can't apply the operation to "
265 "an empty matrix.")
267 if self.nnz == 0:
268 return 0
269 else:
270 zero = self.dtype.type(0)
271 mat = self.tocoo()
272 mat.sum_duplicates()
273 am = op(mat.data)
274 m = mat.data[am]
276 if compare(m, zero):
277 # cast to Python int to avoid overflow
278 # and RuntimeError
279 return int(mat.row[am])*mat.shape[1] + int(mat.col[am])
280 else:
281 size = np.prod(mat.shape)
282 if size == mat.nnz:
283 return am
284 else:
285 ind = mat.row * mat.shape[1] + mat.col
286 zero_ind = _find_missing_index(ind, size)
287 if m == zero:
288 return min(zero_ind, am)
289 else:
290 return zero_ind
292 return self._arg_min_or_max_axis(axis, op, compare)
294 def max(self, axis=None, out=None):
295 """
296 Return the maximum of the matrix or maximum along an axis.
297 This takes all elements into account, not just the non-zero ones.
299 Parameters
300 ----------
301 axis : {-2, -1, 0, 1, None} optional
302 Axis along which the sum is computed. The default is to
303 compute the maximum over all the matrix elements, returning
304 a scalar (i.e., `axis` = `None`).
306 out : None, optional
307 This argument is in the signature *solely* for NumPy
308 compatibility reasons. Do not pass in anything except
309 for the default value, as this argument is not used.
311 Returns
312 -------
313 amax : coo_matrix or scalar
314 Maximum of `a`. If `axis` is None, the result is a scalar value.
315 If `axis` is given, the result is a sparse.coo_matrix of dimension
316 ``a.ndim - 1``.
318 See Also
319 --------
320 min : The minimum value of a sparse matrix along a given axis.
321 numpy.matrix.max : NumPy's implementation of 'max' for matrices
323 """
324 return self._min_or_max(axis, out, np.maximum)
326 def min(self, axis=None, out=None):
327 """
328 Return the minimum of the matrix or maximum along an axis.
329 This takes all elements into account, not just the non-zero ones.
331 Parameters
332 ----------
333 axis : {-2, -1, 0, 1, None} optional
334 Axis along which the sum is computed. The default is to
335 compute the minimum over all the matrix elements, returning
336 a scalar (i.e., `axis` = `None`).
338 out : None, optional
339 This argument is in the signature *solely* for NumPy
340 compatibility reasons. Do not pass in anything except for
341 the default value, as this argument is not used.
343 Returns
344 -------
345 amin : coo_matrix or scalar
346 Minimum of `a`. If `axis` is None, the result is a scalar value.
347 If `axis` is given, the result is a sparse.coo_matrix of dimension
348 ``a.ndim - 1``.
350 See Also
351 --------
352 max : The maximum value of a sparse matrix along a given axis.
353 numpy.matrix.min : NumPy's implementation of 'min' for matrices
355 """
356 return self._min_or_max(axis, out, np.minimum)
358 def argmax(self, axis=None, out=None):
359 """Return indices of maximum elements along an axis.
361 Implicit zero elements are also taken into account. If there are
362 several maximum values, the index of the first occurrence is returned.
364 Parameters
365 ----------
366 axis : {-2, -1, 0, 1, None}, optional
367 Axis along which the argmax is computed. If None (default), index
368 of the maximum element in the flatten data is returned.
369 out : None, optional
370 This argument is in the signature *solely* for NumPy
371 compatibility reasons. Do not pass in anything except for
372 the default value, as this argument is not used.
374 Returns
375 -------
376 ind : numpy.matrix or int
377 Indices of maximum elements. If matrix, its size along `axis` is 1.
378 """
379 return self._arg_min_or_max(axis, out, np.argmax, np.greater)
381 def argmin(self, axis=None, out=None):
382 """Return indices of minimum elements along an axis.
384 Implicit zero elements are also taken into account. If there are
385 several minimum values, the index of the first occurrence is returned.
387 Parameters
388 ----------
389 axis : {-2, -1, 0, 1, None}, optional
390 Axis along which the argmin is computed. If None (default), index
391 of the minimum element in the flatten data is returned.
392 out : None, optional
393 This argument is in the signature *solely* for NumPy
394 compatibility reasons. Do not pass in anything except for
395 the default value, as this argument is not used.
397 Returns
398 -------
399 ind : numpy.matrix or int
400 Indices of minimum elements. If matrix, its size along `axis` is 1.
401 """
402 return self._arg_min_or_max(axis, out, np.argmin, np.less)