Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/scipy/sparse/_data.py: 24%
200 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-03-22 06:44 +0000
« prev ^ index » next coverage.py v7.4.4, created at 2024-03-22 06:44 +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 _spbase, _ufuncs_with_fixed_point_at_zero
12from ._sputils import isscalarlike, validateaxis
14__all__ = []
17# TODO implement all relevant operations
18# use .data.__methods__() instead of /=, *=, etc.
19class _data_matrix(_spbase):
20 def __init__(self):
21 _spbase.__init__(self)
23 @property
24 def dtype(self):
25 return self.data.dtype
27 @dtype.setter
28 def dtype(self, newtype):
29 self.data.dtype = newtype
31 def _deduped_data(self):
32 if hasattr(self, 'sum_duplicates'):
33 self.sum_duplicates()
34 return self.data
36 def __abs__(self):
37 return self._with_data(abs(self._deduped_data()))
39 def __round__(self, ndigits=0):
40 return self._with_data(np.around(self._deduped_data(), decimals=ndigits))
42 def _real(self):
43 return self._with_data(self.data.real)
45 def _imag(self):
46 return self._with_data(self.data.imag)
48 def __neg__(self):
49 if self.dtype.kind == 'b':
50 raise NotImplementedError('negating a boolean sparse array is not '
51 'supported')
52 return self._with_data(-self.data)
54 def __imul__(self, other): # self *= other
55 if isscalarlike(other):
56 self.data *= other
57 return self
58 else:
59 return NotImplemented
61 def __itruediv__(self, other): # self /= other
62 if isscalarlike(other):
63 recip = 1.0 / other
64 self.data *= recip
65 return self
66 else:
67 return NotImplemented
69 def astype(self, dtype, casting='unsafe', copy=True):
70 dtype = np.dtype(dtype)
71 if self.dtype != dtype:
72 matrix = self._with_data(
73 self.data.astype(dtype, casting=casting, copy=True),
74 copy=True
75 )
76 return matrix._with_data(matrix._deduped_data(), copy=False)
77 elif copy:
78 return self.copy()
79 else:
80 return self
82 astype.__doc__ = _spbase.astype.__doc__
84 def conjugate(self, copy=True):
85 if np.issubdtype(self.dtype, np.complexfloating):
86 return self._with_data(self.data.conjugate(), copy=copy)
87 elif copy:
88 return self.copy()
89 else:
90 return self
92 conjugate.__doc__ = _spbase.conjugate.__doc__
94 def copy(self):
95 return self._with_data(self.data.copy(), copy=True)
97 copy.__doc__ = _spbase.copy.__doc__
99 def count_nonzero(self):
100 return np.count_nonzero(self._deduped_data())
102 count_nonzero.__doc__ = _spbase.count_nonzero.__doc__
104 def power(self, n, dtype=None):
105 """
106 This function performs element-wise power.
108 Parameters
109 ----------
110 n : scalar
111 n is a non-zero scalar (nonzero avoids dense ones creation)
112 If zero power is desired, special case it to use `np.ones`
114 dtype : If dtype is not specified, the current dtype will be preserved.
116 Raises
117 ------
118 NotImplementedError : if n is a zero scalar
119 If zero power is desired, special case it to use
120 `np.ones(A.shape, dtype=A.dtype)`
121 """
122 if not isscalarlike(n):
123 raise NotImplementedError("input is not scalar")
124 if not n:
125 raise NotImplementedError(
126 "zero power is not supported as it would densify the matrix.\n"
127 "Use `np.ones(A.shape, dtype=A.dtype)` for this case."
128 )
130 data = self._deduped_data()
131 if dtype is not None:
132 data = data.astype(dtype)
133 return self._with_data(data ** n)
135 ###########################
136 # Multiplication handlers #
137 ###########################
139 def _mul_scalar(self, other):
140 return self._with_data(self.data * other)
143# Add the numpy unary ufuncs for which func(0) = 0 to _data_matrix.
144for npfunc in _ufuncs_with_fixed_point_at_zero:
145 name = npfunc.__name__
147 def _create_method(op):
148 def method(self):
149 result = op(self._deduped_data())
150 return self._with_data(result, copy=True)
152 method.__doc__ = (f"Element-wise {name}.\n\n"
153 f"See `numpy.{name}` for more information.")
154 method.__name__ = name
156 return method
158 setattr(_data_matrix, name, _create_method(npfunc))
161def _find_missing_index(ind, n):
162 for k, a in enumerate(ind):
163 if k != a:
164 return k
166 k += 1
167 if k < n:
168 return k
169 else:
170 return -1
173class _minmax_mixin:
174 """Mixin for min and max methods.
176 These are not implemented for dia_matrix, hence the separate class.
177 """
179 def _min_or_max_axis(self, axis, min_or_max):
180 N = self.shape[axis]
181 if N == 0:
182 raise ValueError("zero-size array to reduction operation")
183 M = self.shape[1 - axis]
184 idx_dtype = self._get_index_dtype(maxval=M)
186 mat = self.tocsc() if axis == 0 else self.tocsr()
187 mat.sum_duplicates()
189 major_index, value = mat._minor_reduce(min_or_max)
190 not_full = np.diff(mat.indptr)[major_index] < N
191 value[not_full] = min_or_max(value[not_full], 0)
193 mask = value != 0
194 major_index = np.compress(mask, major_index)
195 value = np.compress(mask, value)
197 if axis == 0:
198 return self._coo_container(
199 (value, (np.zeros(len(value), dtype=idx_dtype), major_index)),
200 dtype=self.dtype, shape=(1, M)
201 )
202 else:
203 return self._coo_container(
204 (value, (major_index, np.zeros(len(value), dtype=idx_dtype))),
205 dtype=self.dtype, shape=(M, 1)
206 )
208 def _min_or_max(self, axis, out, min_or_max):
209 if out is not None:
210 raise ValueError("Sparse arrays do not support an 'out' parameter.")
212 validateaxis(axis)
213 if self.ndim == 1:
214 if axis not in (None, 0, -1):
215 raise ValueError("axis out of range")
216 axis = None # avoid calling special axis case. no impact on 1d
218 if axis is None:
219 if 0 in self.shape:
220 raise ValueError("zero-size array to reduction operation")
222 zero = self.dtype.type(0)
223 if self.nnz == 0:
224 return zero
225 m = min_or_max.reduce(self._deduped_data().ravel())
226 if self.nnz != np.prod(self.shape):
227 m = min_or_max(zero, m)
228 return m
230 if axis < 0:
231 axis += 2
233 if (axis == 0) or (axis == 1):
234 return self._min_or_max_axis(axis, min_or_max)
235 else:
236 raise ValueError("axis out of range")
238 def _arg_min_or_max_axis(self, axis, argmin_or_argmax, compare):
239 if self.shape[axis] == 0:
240 raise ValueError("Cannot apply the operation along a zero-sized dimension.")
242 if axis < 0:
243 axis += 2
245 zero = self.dtype.type(0)
247 mat = self.tocsc() if axis == 0 else self.tocsr()
248 mat.sum_duplicates()
250 ret_size, line_size = mat._swap(mat.shape)
251 ret = np.zeros(ret_size, dtype=int)
253 nz_lines, = np.nonzero(np.diff(mat.indptr))
254 for i in nz_lines:
255 p, q = mat.indptr[i:i + 2]
256 data = mat.data[p:q]
257 indices = mat.indices[p:q]
258 extreme_index = argmin_or_argmax(data)
259 extreme_value = data[extreme_index]
260 if compare(extreme_value, zero) or q - p == line_size:
261 ret[i] = indices[extreme_index]
262 else:
263 zero_ind = _find_missing_index(indices, line_size)
264 if extreme_value == zero:
265 ret[i] = min(extreme_index, zero_ind)
266 else:
267 ret[i] = zero_ind
269 if axis == 1:
270 ret = ret.reshape(-1, 1)
272 return self._ascontainer(ret)
274 def _arg_min_or_max(self, axis, out, argmin_or_argmax, compare):
275 if out is not None:
276 raise ValueError("Sparse types do not support an 'out' parameter.")
278 validateaxis(axis)
280 if self.ndim == 1:
281 if axis not in (None, 0, -1):
282 raise ValueError("axis out of range")
283 axis = None # avoid calling special axis case. no impact on 1d
285 if axis is not None:
286 return self._arg_min_or_max_axis(axis, argmin_or_argmax, compare)
288 if 0 in self.shape:
289 raise ValueError("Cannot apply the operation to an empty matrix.")
291 if self.nnz == 0:
292 return 0
294 zero = self.dtype.type(0)
295 mat = self.tocoo()
296 # Convert to canonical form: no duplicates, sorted indices.
297 mat.sum_duplicates()
298 extreme_index = argmin_or_argmax(mat.data)
299 extreme_value = mat.data[extreme_index]
300 num_col = mat.shape[-1]
302 # If the min value is less than zero, or max is greater than zero,
303 # then we do not need to worry about implicit zeros.
304 if compare(extreme_value, zero):
305 # cast to Python int to avoid overflow and RuntimeError
306 return int(mat.row[extreme_index]) * num_col + int(mat.col[extreme_index])
308 # Cheap test for the rare case where we have no implicit zeros.
309 size = np.prod(self.shape)
310 if size == mat.nnz:
311 return int(mat.row[extreme_index]) * num_col + int(mat.col[extreme_index])
313 # At this stage, any implicit zero could be the min or max value.
314 # After sum_duplicates(), the `row` and `col` arrays are guaranteed to
315 # be sorted in C-order, which means the linearized indices are sorted.
316 linear_indices = mat.row * num_col + mat.col
317 first_implicit_zero_index = _find_missing_index(linear_indices, size)
318 if extreme_value == zero:
319 return min(first_implicit_zero_index, extreme_index)
320 return first_implicit_zero_index
322 def max(self, axis=None, out=None):
323 """
324 Return the maximum of the array/matrix or maximum along an axis.
325 This takes all elements into account, not just the non-zero ones.
327 Parameters
328 ----------
329 axis : {-2, -1, 0, 1, None} optional
330 Axis along which the sum is computed. The default is to
331 compute the maximum over all elements, returning
332 a scalar (i.e., `axis` = `None`).
334 out : None, optional
335 This argument is in the signature *solely* for NumPy
336 compatibility reasons. Do not pass in anything except
337 for the default value, as this argument is not used.
339 Returns
340 -------
341 amax : coo_matrix or scalar
342 Maximum of `a`. If `axis` is None, the result is a scalar value.
343 If `axis` is given, the result is a sparse.coo_matrix of dimension
344 ``a.ndim - 1``.
346 See Also
347 --------
348 min : The minimum value of a sparse array/matrix along a given axis.
349 numpy.matrix.max : NumPy's implementation of 'max' for matrices
351 """
352 return self._min_or_max(axis, out, np.maximum)
354 def min(self, axis=None, out=None):
355 """
356 Return the minimum of the array/matrix or maximum along an axis.
357 This takes all elements into account, not just the non-zero ones.
359 Parameters
360 ----------
361 axis : {-2, -1, 0, 1, None} optional
362 Axis along which the sum is computed. The default is to
363 compute the minimum over all elements, returning
364 a scalar (i.e., `axis` = `None`).
366 out : None, optional
367 This argument is in the signature *solely* for NumPy
368 compatibility reasons. Do not pass in anything except for
369 the default value, as this argument is not used.
371 Returns
372 -------
373 amin : coo_matrix or scalar
374 Minimum of `a`. If `axis` is None, the result is a scalar value.
375 If `axis` is given, the result is a sparse.coo_matrix of dimension
376 ``a.ndim - 1``.
378 See Also
379 --------
380 max : The maximum value of a sparse array/matrix along a given axis.
381 numpy.matrix.min : NumPy's implementation of 'min' for matrices
383 """
384 return self._min_or_max(axis, out, np.minimum)
386 def nanmax(self, axis=None, out=None):
387 """
388 Return the maximum of the array/matrix or maximum along an axis, ignoring any
389 NaNs. This takes all elements into account, not just the non-zero
390 ones.
392 .. versionadded:: 1.11.0
394 Parameters
395 ----------
396 axis : {-2, -1, 0, 1, None} optional
397 Axis along which the maximum is computed. The default is to
398 compute the maximum over all elements, returning
399 a scalar (i.e., `axis` = `None`).
401 out : None, optional
402 This argument is in the signature *solely* for NumPy
403 compatibility reasons. Do not pass in anything except
404 for the default value, as this argument is not used.
406 Returns
407 -------
408 amax : coo_matrix or scalar
409 Maximum of `a`. If `axis` is None, the result is a scalar value.
410 If `axis` is given, the result is a sparse.coo_matrix of dimension
411 ``a.ndim - 1``.
413 See Also
414 --------
415 nanmin : The minimum value of a sparse array/matrix along a given axis,
416 ignoring NaNs.
417 max : The maximum value of a sparse array/matrix along a given axis,
418 propagating NaNs.
419 numpy.nanmax : NumPy's implementation of 'nanmax'.
421 """
422 return self._min_or_max(axis, out, np.fmax)
424 def nanmin(self, axis=None, out=None):
425 """
426 Return the minimum of the array/matrix or minimum along an axis, ignoring any
427 NaNs. This takes all elements into account, not just the non-zero
428 ones.
430 .. versionadded:: 1.11.0
432 Parameters
433 ----------
434 axis : {-2, -1, 0, 1, None} optional
435 Axis along which the minimum is computed. The default is to
436 compute the minimum over all elements, returning
437 a scalar (i.e., `axis` = `None`).
439 out : None, optional
440 This argument is in the signature *solely* for NumPy
441 compatibility reasons. Do not pass in anything except for
442 the default value, as this argument is not used.
444 Returns
445 -------
446 amin : coo_matrix or scalar
447 Minimum of `a`. If `axis` is None, the result is a scalar value.
448 If `axis` is given, the result is a sparse.coo_matrix of dimension
449 ``a.ndim - 1``.
451 See Also
452 --------
453 nanmax : The maximum value of a sparse array/matrix along a given axis,
454 ignoring NaNs.
455 min : The minimum value of a sparse array/matrix along a given axis,
456 propagating NaNs.
457 numpy.nanmin : NumPy's implementation of 'nanmin'.
459 """
460 return self._min_or_max(axis, out, np.fmin)
462 def argmax(self, axis=None, out=None):
463 """Return indices of maximum elements along an axis.
465 Implicit zero elements are also taken into account. If there are
466 several maximum values, the index of the first occurrence is returned.
468 Parameters
469 ----------
470 axis : {-2, -1, 0, 1, None}, optional
471 Axis along which the argmax is computed. If None (default), index
472 of the maximum element in the flatten data is returned.
473 out : None, optional
474 This argument is in the signature *solely* for NumPy
475 compatibility reasons. Do not pass in anything except for
476 the default value, as this argument is not used.
478 Returns
479 -------
480 ind : numpy.matrix or int
481 Indices of maximum elements. If matrix, its size along `axis` is 1.
482 """
483 return self._arg_min_or_max(axis, out, np.argmax, np.greater)
485 def argmin(self, axis=None, out=None):
486 """Return indices of minimum elements along an axis.
488 Implicit zero elements are also taken into account. If there are
489 several minimum values, the index of the first occurrence is returned.
491 Parameters
492 ----------
493 axis : {-2, -1, 0, 1, None}, optional
494 Axis along which the argmin is computed. If None (default), index
495 of the minimum element in the flatten data is returned.
496 out : None, optional
497 This argument is in the signature *solely* for NumPy
498 compatibility reasons. Do not pass in anything except for
499 the default value, as this argument is not used.
501 Returns
502 -------
503 ind : numpy.matrix or int
504 Indices of minimum elements. If matrix, its size along `axis` is 1.
505 """
506 return self._arg_min_or_max(axis, out, np.argmin, np.less)