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

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 spmatrix, _ufuncs_with_fixed_point_at_zero 

12from ._sputils import isscalarlike, validateaxis, matrix 

13 

14__all__ = [] 

15 

16 

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) 

22 

23 def _get_dtype(self): 

24 return self.data.dtype 

25 

26 def _set_dtype(self, newtype): 

27 self.data.dtype = newtype 

28 dtype = property(fget=_get_dtype, fset=_set_dtype) 

29 

30 def _deduped_data(self): 

31 if hasattr(self, 'sum_duplicates'): 

32 self.sum_duplicates() 

33 return self.data 

34 

35 def __abs__(self): 

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

37 

38 def __round__(self, ndigits=0): 

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

40 

41 def _real(self): 

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

43 

44 def _imag(self): 

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

46 

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) 

52 

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

54 if isscalarlike(other): 

55 self.data *= other 

56 return self 

57 else: 

58 return NotImplemented 

59 

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 

67 

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 

78 

79 astype.__doc__ = spmatrix.astype.__doc__ 

80 

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 

88 

89 conj.__doc__ = spmatrix.conj.__doc__ 

90 

91 def copy(self): 

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

93 

94 copy.__doc__ = spmatrix.copy.__doc__ 

95 

96 def count_nonzero(self): 

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

98 

99 count_nonzero.__doc__ = spmatrix.count_nonzero.__doc__ 

100 

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

102 """ 

103 This function performs element-wise power. 

104 

105 Parameters 

106 ---------- 

107 n : n is a scalar 

108 

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") 

113 

114 data = self._deduped_data() 

115 if dtype is not None: 

116 data = data.astype(dtype) 

117 return self._with_data(data ** n) 

118 

119 ########################### 

120 # Multiplication handlers # 

121 ########################### 

122 

123 def _mul_scalar(self, other): 

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

125 

126 

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__ 

130 

131 def _create_method(op): 

132 def method(self): 

133 result = op(self._deduped_data()) 

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

135 

136 method.__doc__ = ("Element-wise %s.\n\n" 

137 "See `numpy.%s` for more information." % (name, name)) 

138 method.__name__ = name 

139 

140 return method 

141 

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

143 

144 

145def _find_missing_index(ind, n): 

146 for k, a in enumerate(ind): 

147 if k != a: 

148 return k 

149 

150 k += 1 

151 if k < n: 

152 return k 

153 else: 

154 return -1 

155 

156 

157class _minmax_mixin: 

158 """Mixin for min and max methods. 

159 

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

161 """ 

162 

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] 

168 

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

170 mat.sum_duplicates() 

171 

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) 

175 

176 mask = value != 0 

177 major_index = np.compress(mask, major_index) 

178 value = np.compress(mask, value) 

179 

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 ) 

190 

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.")) 

195 

196 validateaxis(axis) 

197 

198 if axis is None: 

199 if 0 in self.shape: 

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

201 

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 

209 

210 if axis < 0: 

211 axis += 2 

212 

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") 

217 

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.") 

222 

223 if axis < 0: 

224 axis += 2 

225 

226 zero = self.dtype.type(0) 

227 

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

229 mat.sum_duplicates() 

230 

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

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

233 

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 

249 

250 if axis == 1: 

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

252 

253 return matrix(ret) 

254 

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.") 

259 

260 validateaxis(axis) 

261 

262 if axis is None: 

263 if 0 in self.shape: 

264 raise ValueError("Can't apply the operation to " 

265 "an empty matrix.") 

266 

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] 

275 

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 

291 

292 return self._arg_min_or_max_axis(axis, op, compare) 

293 

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. 

298 

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`). 

305 

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. 

310 

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``. 

317 

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 

322 

323 """ 

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

325 

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. 

330 

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`). 

337 

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. 

342 

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``. 

349 

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 

354 

355 """ 

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

357 

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

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

360 

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

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

363 

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. 

373 

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) 

380 

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

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

383 

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

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

386 

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. 

396 

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)