Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/scipy/sparse/_data.py: 25%

190 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-09-23 06:43 +0000

1"""Base class for sparse matrice with a .data attribute 

2 

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 

6 

7""" 

8 

9import numpy as np 

10 

11from ._base import _spbase, _ufuncs_with_fixed_point_at_zero 

12from ._sputils import isscalarlike, validateaxis 

13 

14__all__ = [] 

15 

16 

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) 

22 

23 @property 

24 def dtype(self): 

25 return self.data.dtype 

26 

27 @dtype.setter 

28 def dtype(self, newtype): 

29 self.data.dtype = newtype 

30 

31 def _deduped_data(self): 

32 if hasattr(self, 'sum_duplicates'): 

33 self.sum_duplicates() 

34 return self.data 

35 

36 def __abs__(self): 

37 return self._with_data(abs(self._deduped_data())) 

38 

39 def __round__(self, ndigits=0): 

40 return self._with_data(np.around(self._deduped_data(), decimals=ndigits)) 

41 

42 def _real(self): 

43 return self._with_data(self.data.real) 

44 

45 def _imag(self): 

46 return self._with_data(self.data.imag) 

47 

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) 

53 

54 def __imul__(self, other): # self *= other 

55 if isscalarlike(other): 

56 self.data *= other 

57 return self 

58 else: 

59 return NotImplemented 

60 

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 

68 

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 

81 

82 astype.__doc__ = _spbase.astype.__doc__ 

83 

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 

91 

92 conjugate.__doc__ = _spbase.conjugate.__doc__ 

93 

94 def copy(self): 

95 return self._with_data(self.data.copy(), copy=True) 

96 

97 copy.__doc__ = _spbase.copy.__doc__ 

98 

99 def count_nonzero(self): 

100 return np.count_nonzero(self._deduped_data()) 

101 

102 count_nonzero.__doc__ = _spbase.count_nonzero.__doc__ 

103 

104 def power(self, n, dtype=None): 

105 """ 

106 This function performs element-wise power. 

107 

108 Parameters 

109 ---------- 

110 n : n is a scalar 

111 

112 dtype : If dtype is not specified, the current dtype will be preserved. 

113 """ 

114 if not isscalarlike(n): 

115 raise NotImplementedError("input is not scalar") 

116 

117 data = self._deduped_data() 

118 if dtype is not None: 

119 data = data.astype(dtype) 

120 return self._with_data(data ** n) 

121 

122 ########################### 

123 # Multiplication handlers # 

124 ########################### 

125 

126 def _mul_scalar(self, other): 

127 return self._with_data(self.data * other) 

128 

129 

130# Add the numpy unary ufuncs for which func(0) = 0 to _data_matrix. 

131for npfunc in _ufuncs_with_fixed_point_at_zero: 

132 name = npfunc.__name__ 

133 

134 def _create_method(op): 

135 def method(self): 

136 result = op(self._deduped_data()) 

137 return self._with_data(result, copy=True) 

138 

139 method.__doc__ = ("Element-wise {}.\n\n" 

140 "See `numpy.{}` for more information.".format(name, name)) 

141 method.__name__ = name 

142 

143 return method 

144 

145 setattr(_data_matrix, name, _create_method(npfunc)) 

146 

147 

148def _find_missing_index(ind, n): 

149 for k, a in enumerate(ind): 

150 if k != a: 

151 return k 

152 

153 k += 1 

154 if k < n: 

155 return k 

156 else: 

157 return -1 

158 

159 

160class _minmax_mixin: 

161 """Mixin for min and max methods. 

162 

163 These are not implemented for dia_matrix, hence the separate class. 

164 """ 

165 

166 def _min_or_max_axis(self, axis, min_or_max): 

167 N = self.shape[axis] 

168 if N == 0: 

169 raise ValueError("zero-size array to reduction operation") 

170 M = self.shape[1 - axis] 

171 idx_dtype = self._get_index_dtype(maxval=M) 

172 

173 mat = self.tocsc() if axis == 0 else self.tocsr() 

174 mat.sum_duplicates() 

175 

176 major_index, value = mat._minor_reduce(min_or_max) 

177 not_full = np.diff(mat.indptr)[major_index] < N 

178 value[not_full] = min_or_max(value[not_full], 0) 

179 

180 mask = value != 0 

181 major_index = np.compress(mask, major_index) 

182 value = np.compress(mask, value) 

183 

184 if axis == 0: 

185 return self._coo_container( 

186 (value, (np.zeros(len(value), dtype=idx_dtype), major_index)), 

187 dtype=self.dtype, shape=(1, M) 

188 ) 

189 else: 

190 return self._coo_container( 

191 (value, (major_index, np.zeros(len(value), dtype=idx_dtype))), 

192 dtype=self.dtype, shape=(M, 1) 

193 ) 

194 

195 def _min_or_max(self, axis, out, min_or_max): 

196 if out is not None: 

197 raise ValueError("Sparse matrices do not support " 

198 "an 'out' parameter.") 

199 

200 validateaxis(axis) 

201 

202 if axis is None: 

203 if 0 in self.shape: 

204 raise ValueError("zero-size array to reduction operation") 

205 

206 zero = self.dtype.type(0) 

207 if self.nnz == 0: 

208 return zero 

209 m = min_or_max.reduce(self._deduped_data().ravel()) 

210 if self.nnz != np.prod(self.shape): 

211 m = min_or_max(zero, m) 

212 return m 

213 

214 if axis < 0: 

215 axis += 2 

216 

217 if (axis == 0) or (axis == 1): 

218 return self._min_or_max_axis(axis, min_or_max) 

219 else: 

220 raise ValueError("axis out of range") 

221 

222 def _arg_min_or_max_axis(self, axis, argmin_or_argmax, compare): 

223 if self.shape[axis] == 0: 

224 raise ValueError("Can't apply the operation along a zero-sized " 

225 "dimension.") 

226 

227 if axis < 0: 

228 axis += 2 

229 

230 zero = self.dtype.type(0) 

231 

232 mat = self.tocsc() if axis == 0 else self.tocsr() 

233 mat.sum_duplicates() 

234 

235 ret_size, line_size = mat._swap(mat.shape) 

236 ret = np.zeros(ret_size, dtype=int) 

237 

238 nz_lines, = np.nonzero(np.diff(mat.indptr)) 

239 for i in nz_lines: 

240 p, q = mat.indptr[i:i + 2] 

241 data = mat.data[p:q] 

242 indices = mat.indices[p:q] 

243 extreme_index = argmin_or_argmax(data) 

244 extreme_value = data[extreme_index] 

245 if compare(extreme_value, zero) or q - p == line_size: 

246 ret[i] = indices[extreme_index] 

247 else: 

248 zero_ind = _find_missing_index(indices, line_size) 

249 if extreme_value == zero: 

250 ret[i] = min(extreme_index, zero_ind) 

251 else: 

252 ret[i] = zero_ind 

253 

254 if axis == 1: 

255 ret = ret.reshape(-1, 1) 

256 

257 return self._ascontainer(ret) 

258 

259 def _arg_min_or_max(self, axis, out, argmin_or_argmax, compare): 

260 if out is not None: 

261 raise ValueError("Sparse types do not support an 'out' parameter.") 

262 

263 validateaxis(axis) 

264 

265 if axis is not None: 

266 return self._arg_min_or_max_axis(axis, argmin_or_argmax, compare) 

267 

268 if 0 in self.shape: 

269 raise ValueError("Can't apply the operation to an empty matrix.") 

270 

271 if self.nnz == 0: 

272 return 0 

273 

274 zero = self.dtype.type(0) 

275 mat = self.tocoo() 

276 # Convert to canonical form: no duplicates, sorted indices. 

277 mat.sum_duplicates() 

278 extreme_index = argmin_or_argmax(mat.data) 

279 extreme_value = mat.data[extreme_index] 

280 num_row, num_col = mat.shape 

281 

282 # If the min value is less than zero, or max is greater than zero, 

283 # then we don't need to worry about implicit zeros. 

284 if compare(extreme_value, zero): 

285 # cast to Python int to avoid overflow and RuntimeError 

286 return (int(mat.row[extreme_index]) * num_col + 

287 int(mat.col[extreme_index])) 

288 

289 # Cheap test for the rare case where we have no implicit zeros. 

290 size = num_row * num_col 

291 if size == mat.nnz: 

292 return (int(mat.row[extreme_index]) * num_col + 

293 int(mat.col[extreme_index])) 

294 

295 # At this stage, any implicit zero could be the min or max value. 

296 # After sum_duplicates(), the `row` and `col` arrays are guaranteed to 

297 # be sorted in C-order, which means the linearized indices are sorted. 

298 linear_indices = mat.row * num_col + mat.col 

299 first_implicit_zero_index = _find_missing_index(linear_indices, size) 

300 if extreme_value == zero: 

301 return min(first_implicit_zero_index, extreme_index) 

302 return first_implicit_zero_index 

303 

304 def max(self, axis=None, out=None): 

305 """ 

306 Return the maximum of the array/matrix or maximum along an axis. 

307 This takes all elements into account, not just the non-zero ones. 

308 

309 Parameters 

310 ---------- 

311 axis : {-2, -1, 0, 1, None} optional 

312 Axis along which the sum is computed. The default is to 

313 compute the maximum over all elements, returning 

314 a scalar (i.e., `axis` = `None`). 

315 

316 out : None, optional 

317 This argument is in the signature *solely* for NumPy 

318 compatibility reasons. Do not pass in anything except 

319 for the default value, as this argument is not used. 

320 

321 Returns 

322 ------- 

323 amax : coo_matrix or scalar 

324 Maximum of `a`. If `axis` is None, the result is a scalar value. 

325 If `axis` is given, the result is a sparse.coo_matrix of dimension 

326 ``a.ndim - 1``. 

327 

328 See Also 

329 -------- 

330 min : The minimum value of a sparse array/matrix along a given axis. 

331 numpy.matrix.max : NumPy's implementation of 'max' for matrices 

332 

333 """ 

334 return self._min_or_max(axis, out, np.maximum) 

335 

336 def min(self, axis=None, out=None): 

337 """ 

338 Return the minimum of the array/matrix or maximum along an axis. 

339 This takes all elements into account, not just the non-zero ones. 

340 

341 Parameters 

342 ---------- 

343 axis : {-2, -1, 0, 1, None} optional 

344 Axis along which the sum is computed. The default is to 

345 compute the minimum over all elements, returning 

346 a scalar (i.e., `axis` = `None`). 

347 

348 out : None, optional 

349 This argument is in the signature *solely* for NumPy 

350 compatibility reasons. Do not pass in anything except for 

351 the default value, as this argument is not used. 

352 

353 Returns 

354 ------- 

355 amin : coo_matrix or scalar 

356 Minimum of `a`. If `axis` is None, the result is a scalar value. 

357 If `axis` is given, the result is a sparse.coo_matrix of dimension 

358 ``a.ndim - 1``. 

359 

360 See Also 

361 -------- 

362 max : The maximum value of a sparse array/matrix along a given axis. 

363 numpy.matrix.min : NumPy's implementation of 'min' for matrices 

364 

365 """ 

366 return self._min_or_max(axis, out, np.minimum) 

367 

368 def nanmax(self, axis=None, out=None): 

369 """ 

370 Return the maximum of the array/matrix or maximum along an axis, ignoring any 

371 NaNs. This takes all elements into account, not just the non-zero 

372 ones. 

373 

374 .. versionadded:: 1.11.0 

375 

376 Parameters 

377 ---------- 

378 axis : {-2, -1, 0, 1, None} optional 

379 Axis along which the maximum is computed. The default is to 

380 compute the maximum over all elements, returning 

381 a scalar (i.e., `axis` = `None`). 

382 

383 out : None, optional 

384 This argument is in the signature *solely* for NumPy 

385 compatibility reasons. Do not pass in anything except 

386 for the default value, as this argument is not used. 

387 

388 Returns 

389 ------- 

390 amax : coo_matrix or scalar 

391 Maximum of `a`. If `axis` is None, the result is a scalar value. 

392 If `axis` is given, the result is a sparse.coo_matrix of dimension 

393 ``a.ndim - 1``. 

394 

395 See Also 

396 -------- 

397 nanmin : The minimum value of a sparse array/matrix along a given axis, 

398 ignoring NaNs. 

399 max : The maximum value of a sparse array/matrix along a given axis, 

400 propagating NaNs. 

401 numpy.nanmax : NumPy's implementation of 'nanmax'. 

402 

403 """ 

404 return self._min_or_max(axis, out, np.fmax) 

405 

406 def nanmin(self, axis=None, out=None): 

407 """ 

408 Return the minimum of the array/matrix or minimum along an axis, ignoring any 

409 NaNs. This takes all elements into account, not just the non-zero 

410 ones. 

411 

412 .. versionadded:: 1.11.0 

413 

414 Parameters 

415 ---------- 

416 axis : {-2, -1, 0, 1, None} optional 

417 Axis along which the minimum is computed. The default is to 

418 compute the minimum over all elements, returning 

419 a scalar (i.e., `axis` = `None`). 

420 

421 out : None, optional 

422 This argument is in the signature *solely* for NumPy 

423 compatibility reasons. Do not pass in anything except for 

424 the default value, as this argument is not used. 

425 

426 Returns 

427 ------- 

428 amin : coo_matrix or scalar 

429 Minimum of `a`. If `axis` is None, the result is a scalar value. 

430 If `axis` is given, the result is a sparse.coo_matrix of dimension 

431 ``a.ndim - 1``. 

432 

433 See Also 

434 -------- 

435 nanmax : The maximum value of a sparse array/matrix along a given axis, 

436 ignoring NaNs. 

437 min : The minimum value of a sparse array/matrix along a given axis, 

438 propagating NaNs. 

439 numpy.nanmin : NumPy's implementation of 'nanmin'. 

440 

441 """ 

442 return self._min_or_max(axis, out, np.fmin) 

443 

444 def argmax(self, axis=None, out=None): 

445 """Return indices of maximum elements along an axis. 

446 

447 Implicit zero elements are also taken into account. If there are 

448 several maximum values, the index of the first occurrence is returned. 

449 

450 Parameters 

451 ---------- 

452 axis : {-2, -1, 0, 1, None}, optional 

453 Axis along which the argmax is computed. If None (default), index 

454 of the maximum element in the flatten data is returned. 

455 out : None, optional 

456 This argument is in the signature *solely* for NumPy 

457 compatibility reasons. Do not pass in anything except for 

458 the default value, as this argument is not used. 

459 

460 Returns 

461 ------- 

462 ind : numpy.matrix or int 

463 Indices of maximum elements. If matrix, its size along `axis` is 1. 

464 """ 

465 return self._arg_min_or_max(axis, out, np.argmax, np.greater) 

466 

467 def argmin(self, axis=None, out=None): 

468 """Return indices of minimum elements along an axis. 

469 

470 Implicit zero elements are also taken into account. If there are 

471 several minimum values, the index of the first occurrence is returned. 

472 

473 Parameters 

474 ---------- 

475 axis : {-2, -1, 0, 1, None}, optional 

476 Axis along which the argmin is computed. If None (default), index 

477 of the minimum element in the flatten data is returned. 

478 out : None, optional 

479 This argument is in the signature *solely* for NumPy 

480 compatibility reasons. Do not pass in anything except for 

481 the default value, as this argument is not used. 

482 

483 Returns 

484 ------- 

485 ind : numpy.matrix or int 

486 Indices of minimum elements. If matrix, its size along `axis` is 1. 

487 """ 

488 return self._arg_min_or_max(axis, out, np.argmin, np.less)