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

1"""Base class for sparse matrices""" 

2from warnings import warn 

3 

4import numpy as np 

5 

6from ._sputils import (asmatrix, check_reshape_kwargs, check_shape, 

7 get_sum_dtype, isdense, isintlike, isscalarlike, 

8 matrix, validateaxis) 

9 

10__all__ = ['spmatrix', 'isspmatrix', 'issparse', 

11 'SparseWarning', 'SparseEfficiencyWarning'] 

12 

13 

14class SparseWarning(Warning): 

15 pass 

16 

17 

18class SparseFormatWarning(SparseWarning): 

19 pass 

20 

21 

22class SparseEfficiencyWarning(SparseWarning): 

23 pass 

24 

25 

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 } 

48 

49 

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

55 

56 

57MAXPRINT = 50 

58 

59 

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

64 

65 __array_priority__ = 10.1 

66 ndim = 2 

67 

68 @property 

69 def _bsr_container(self): 

70 from ._bsr import bsr_matrix 

71 return bsr_matrix 

72 

73 @property 

74 def _coo_container(self): 

75 from ._coo import coo_matrix 

76 return coo_matrix 

77 

78 @property 

79 def _csc_container(self): 

80 from ._csc import csc_matrix 

81 return csc_matrix 

82 

83 @property 

84 def _csr_container(self): 

85 from ._csr import csr_matrix 

86 return csr_matrix 

87 

88 @property 

89 def _dia_container(self): 

90 from ._dia import dia_matrix 

91 return dia_matrix 

92 

93 @property 

94 def _dok_container(self): 

95 from ._dok import dok_matrix 

96 return dok_matrix 

97 

98 @property 

99 def _lil_container(self): 

100 from ._lil import lil_matrix 

101 return lil_matrix 

102 

103 _is_array = False 

104 

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 

111 

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__ 

118 

119 def get_shape(self): 

120 """Get shape of a matrix.""" 

121 return self._shape 

122 

123 shape = property(fget=get_shape, fset=set_shape) 

124 

125 def reshape(self, *args, **kwargs): 

126 """reshape(self, shape, order='C', copy=False) 

127 

128 Gives a new shape to a sparse matrix without changing its data. 

129 

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. 

144 

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. 

150 

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 

165 

166 return self.tocoo(copy=copy).reshape(shape, order=order, copy=False) 

167 

168 def resize(self, shape): 

169 """Resize the matrix in-place to dimensions given by ``shape`` 

170 

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. 

174 

175 Parameters 

176 ---------- 

177 shape : (int, int) 

178 number of rows and columns in the new matrix 

179 

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. 

187 

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

194 

195 def astype(self, dtype, casting='unsafe', copy=True): 

196 """Cast the matrix elements to a specified type. 

197 

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

216 

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 

225 

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) 

232 

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) 

239 

240 def asfptype(self): 

241 """Upcast matrix to a floating point format (if necessary)""" 

242 

243 fp_types = ['f', 'd', 'F', 'D'] 

244 

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) 

251 

252 raise TypeError('cannot upcast [%s] to a floating ' 

253 'point format' % self.dtype.name) 

254 

255 def __iter__(self): 

256 for r in range(self.shape[0]): 

257 yield self[r, :] 

258 

259 def getmaxprint(self): 

260 """Maximum number of elements to display when printed.""" 

261 return self.maxprint 

262 

263 def count_nonzero(self): 

264 """Number of non-zero entries, equivalent to 

265 

266 np.count_nonzero(a.toarray()) 

267 

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

274 

275 def getnnz(self, axis=None): 

276 """Number of stored values, including explicit zeros. 

277 

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. 

283 

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

290 

291 @property 

292 def nnz(self): 

293 """Number of stored values, including explicit zeros. 

294 

295 See also 

296 -------- 

297 count_nonzero : Number of non-zero entries 

298 """ 

299 return self.getnnz() 

300 

301 def getformat(self): 

302 """Format of a matrix representation as a string.""" 

303 return getattr(self, 'format', 'und') 

304 

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

311 

312 def __str__(self): 

313 maxprint = self.getmaxprint() 

314 

315 A = self.tocoo() 

316 

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

321 

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) 

330 

331 return out 

332 

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__ 

340 

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

347 

348 def asformat(self, format, copy=False): 

349 """Return this matrix in the passed format. 

350 

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. 

358 

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 

373 

374 # Forward the copy kwarg, if it's accepted. 

375 try: 

376 return convert_method(copy=copy) 

377 except TypeError: 

378 return convert_method() 

379 

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 #################################################################### 

386 

387 def multiply(self, other): 

388 """Point-wise multiplication by another matrix 

389 """ 

390 return self.tocsr().multiply(other) 

391 

392 def maximum(self, other): 

393 """Element-wise maximum between this and another matrix.""" 

394 return self.tocsr().maximum(other) 

395 

396 def minimum(self, other): 

397 """Element-wise minimum between this and another matrix.""" 

398 return self.tocsr().minimum(other) 

399 

400 def dot(self, other): 

401 """Ordinary dot product 

402 

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) 

411 

412 """ 

413 if np.isscalar(other): 

414 return self * other 

415 else: 

416 return self @ other 

417 

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

419 """Element-wise power.""" 

420 return self.tocsr().power(n, dtype=dtype) 

421 

422 def __eq__(self, other): 

423 return self.tocsr().__eq__(other) 

424 

425 def __ne__(self, other): 

426 return self.tocsr().__ne__(other) 

427 

428 def __lt__(self, other): 

429 return self.tocsr().__lt__(other) 

430 

431 def __gt__(self, other): 

432 return self.tocsr().__gt__(other) 

433 

434 def __le__(self, other): 

435 return self.tocsr().__le__(other) 

436 

437 def __ge__(self, other): 

438 return self.tocsr().__ge__(other) 

439 

440 def __abs__(self): 

441 return abs(self.tocsr()) 

442 

443 def __round__(self, ndigits=0): 

444 return round(self.tocsr(), ndigits=ndigits) 

445 

446 def _add_sparse(self, other): 

447 return self.tocsr()._add_sparse(other) 

448 

449 def _add_dense(self, other): 

450 return self.tocoo()._add_dense(other) 

451 

452 def _sub_sparse(self, other): 

453 return self.tocsr()._sub_sparse(other) 

454 

455 def _sub_dense(self, other): 

456 return self.todense() - other 

457 

458 def _rsub_dense(self, other): 

459 # note: this can't be replaced by other + (-self) for unsigned types 

460 return other - self.todense() 

461 

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 

478 

479 def __radd__(self,other): # other + self 

480 return self.__add__(other) 

481 

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 

497 

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 

509 

510 def _mul_dispatch(self, other): 

511 """`np.matrix`-compatible mul, i.e. `dot` or `NotImplemented` 

512 

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. 

522 

523 M, N = self.shape 

524 

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) 

533 

534 if isscalarlike(other): 

535 # scalar value 

536 return self._mul_scalar(other) 

537 

538 if issparse(other): 

539 if self.shape[1] != other.shape[0]: 

540 raise ValueError('dimension mismatch') 

541 return self._mul_sparse_matrix(other) 

542 

543 # If it's a list or whatever, treat it like a matrix 

544 other_a = np.asanyarray(other) 

545 

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 

550 

551 try: 

552 other.shape 

553 except AttributeError: 

554 other = other_a 

555 

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

560 

561 result = self._mul_vector(np.ravel(other)) 

562 

563 if isinstance(other, np.matrix): 

564 result = self._ascontainer(result) 

565 

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) 

569 

570 return result 

571 

572 elif other.ndim == 2: 

573 ## 

574 # dense 2D array or matrix ("multivector") 

575 

576 if other.shape[0] != self.shape[1]: 

577 raise ValueError('dimension mismatch') 

578 

579 result = self._mul_multivector(np.asarray(other)) 

580 

581 if isinstance(other, np.matrix): 

582 result = self._ascontainer(result) 

583 

584 return result 

585 

586 else: 

587 raise ValueError('could not interpret dimensions') 

588 

589 def __mul__(self, other): 

590 return self._mul_dispatch(other) 

591 

592 # by default, use CSR for __mul__ handlers 

593 def _mul_scalar(self, other): 

594 return self.tocsr()._mul_scalar(other) 

595 

596 def _mul_vector(self, other): 

597 return self.tocsr()._mul_vector(other) 

598 

599 def _mul_multivector(self, other): 

600 return self.tocsr()._mul_multivector(other) 

601 

602 def _mul_sparse_matrix(self, other): 

603 return self.tocsr()._mul_sparse_matrix(other) 

604 

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

618 

619 def __rmul__(self, other): # other * self 

620 return self._rmul_dispatch(other) 

621 

622 ####################### 

623 # matmul (@) operator # 

624 ####################### 

625 

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) 

631 

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) 

637 

638 #################### 

639 # Other Arithmetic # 

640 #################### 

641 

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

649 

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) 

654 

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 

661 

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) 

676 

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 

684 

685 def __truediv__(self, other): 

686 return self._divide(other, true_divide=True) 

687 

688 def __div__(self, other): 

689 # Always do true division 

690 return self._divide(other, true_divide=True) 

691 

692 def __rtruediv__(self, other): 

693 # Implementing this as the inverse would be too magical -- bail out 

694 return NotImplemented 

695 

696 def __rdiv__(self, other): 

697 # Implementing this as the inverse would be too magical -- bail out 

698 return NotImplemented 

699 

700 def __neg__(self): 

701 return -self.tocsr() 

702 

703 def __iadd__(self, other): 

704 return NotImplemented 

705 

706 def __isub__(self, other): 

707 return NotImplemented 

708 

709 def __imul__(self, other): 

710 return NotImplemented 

711 

712 def __idiv__(self, other): 

713 return self.__itruediv__(other) 

714 

715 def __itruediv__(self, other): 

716 return NotImplemented 

717 

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

722 

723 if isintlike(other): 

724 other = int(other) 

725 if other < 0: 

726 raise ValueError('exponent must be >= 0') 

727 

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 

735 

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 

748 

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

772 

773 def transpose(self, axes=None, copy=False): 

774 """ 

775 Reverses the dimensions of the sparse matrix. 

776 

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. 

788 

789 Returns 

790 ------- 

791 p : `self` with the dimensions reversed. 

792 

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) 

799 

800 def conj(self, copy=True): 

801 """Element-wise complex conjugation. 

802 

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. 

805 

806 Parameters 

807 ---------- 

808 copy : bool, optional 

809 If True, the result is guaranteed to not share data with self. 

810 

811 Returns 

812 ------- 

813 A : The element-wise complex conjugate. 

814 

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 

822 

823 def conjugate(self, copy=True): 

824 return self.conj(copy=copy) 

825 

826 conjugate.__doc__ = conj.__doc__ 

827 

828 # Renamed conjtranspose() -> getH() for compatibility with dense matrices 

829 def getH(self): 

830 """Return the Hermitian transpose of this matrix. 

831 

832 See Also 

833 -------- 

834 numpy.matrix.getH : NumPy's implementation of `getH` for matrices 

835 """ 

836 return self.transpose().conj() 

837 

838 def _real(self): 

839 return self.tocsr()._real() 

840 

841 def _imag(self): 

842 return self.tocsr()._imag() 

843 

844 def nonzero(self): 

845 """nonzero indices 

846 

847 Returns a tuple of arrays (row,col) containing the indices 

848 of the non-zero elements of the matrix. 

849 

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

856 

857 """ 

858 

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

863 

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 

879 

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 

895 

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. 

915 

916 def todense(self, order=None, out=None): 

917 """ 

918 Return a dense matrix representation of this matrix. 

919 

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. 

928 

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. 

935 

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

947 

948 def toarray(self, order=None, out=None): 

949 """ 

950 Return a dense ndarray representation of this matrix. 

951 

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. 

960 

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

968 

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) 

979 

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. 

985 

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) 

990 

991 def todok(self, copy=False): 

992 """Convert this matrix to Dictionary Of Keys format. 

993 

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) 

998 

999 def tocoo(self, copy=False): 

1000 """Convert this matrix to COOrdinate format. 

1001 

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) 

1006 

1007 def tolil(self, copy=False): 

1008 """Convert this matrix to List of Lists format. 

1009 

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) 

1014 

1015 def todia(self, copy=False): 

1016 """Convert this matrix to sparse DIAgonal format. 

1017 

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) 

1022 

1023 def tobsr(self, blocksize=None, copy=False): 

1024 """Convert this matrix to Block Sparse Row format. 

1025 

1026 With copy=False, the data/indices may be shared between this matrix and 

1027 the resultant bsr_matrix. 

1028 

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) 

1033 

1034 def tocsc(self, copy=False): 

1035 """Convert this matrix to Compressed Sparse Column format. 

1036 

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) 

1041 

1042 def copy(self): 

1043 """Returns a copy of this matrix. 

1044 

1045 No data/indices will be shared between the returned value and current 

1046 matrix. 

1047 """ 

1048 return self.__class__(self, copy=True) 

1049 

1050 def sum(self, axis=None, dtype=None, out=None): 

1051 """ 

1052 Sum the matrix elements over a given axis. 

1053 

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. 

1067 

1068 .. versionadded:: 0.18.0 

1069 

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. 

1074 

1075 .. versionadded:: 0.18.0 

1076 

1077 Returns 

1078 ------- 

1079 sum_along_axis : np.matrix 

1080 A matrix with the same shape as `self`, with the specified 

1081 axis removed. 

1082 

1083 See Also 

1084 -------- 

1085 numpy.matrix.sum : NumPy's implementation of 'sum' for matrices 

1086 

1087 """ 

1088 validateaxis(axis) 

1089 

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 

1094 

1095 # Mimic numpy's casting. 

1096 res_dtype = get_sum_dtype(self.dtype) 

1097 

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) 

1103 

1104 if axis < 0: 

1105 axis += 2 

1106 

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 ) 

1118 

1119 if out is not None and out.shape != ret.shape: 

1120 raise ValueError("dimensions do not match") 

1121 

1122 return ret.sum(axis=axis, dtype=dtype, out=out) 

1123 

1124 def mean(self, axis=None, dtype=None, out=None): 

1125 """ 

1126 Compute the arithmetic mean along the specified axis. 

1127 

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. 

1132 

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. 

1142 

1143 .. versionadded:: 0.18.0 

1144 

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. 

1149 

1150 .. versionadded:: 0.18.0 

1151 

1152 Returns 

1153 ------- 

1154 m : np.matrix 

1155 

1156 See Also 

1157 -------- 

1158 numpy.matrix.mean : NumPy's implementation of 'mean' for matrices 

1159 

1160 """ 

1161 def _is_integral(dtype): 

1162 return (np.issubdtype(dtype, np.integer) or 

1163 np.issubdtype(dtype, np.bool_)) 

1164 

1165 validateaxis(axis) 

1166 

1167 res_dtype = self.dtype.type 

1168 integral = _is_integral(self.dtype) 

1169 

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 

1176 

1177 # intermediate dtype for summation 

1178 inter_dtype = np.float64 if integral else res_dtype 

1179 inter_self = self.astype(inter_dtype) 

1180 

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) 

1185 

1186 if axis < 0: 

1187 axis += 2 

1188 

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) 

1196 

1197 def diagonal(self, k=0): 

1198 """Returns the kth diagonal of the matrix. 

1199 

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

1205 

1206 .. versionadded:: 1.0 

1207 

1208 See also 

1209 -------- 

1210 numpy.diagonal : Equivalent numpy function. 

1211 

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) 

1222 

1223 def trace(self, offset=0): 

1224 """Returns the sum along diagonals of the sparse matrix. 

1225 

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

1231 

1232 """ 

1233 return self.diagonal(k=offset).sum() 

1234 

1235 def setdiag(self, values, k=0): 

1236 """ 

1237 Set diagonal or off-diagonal elements of the array. 

1238 

1239 Parameters 

1240 ---------- 

1241 values : array_like 

1242 New values of the diagonal elements. 

1243 

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. 

1247 

1248 If a scalar value is given, all of the diagonal is set to it. 

1249 

1250 k : int, optional 

1251 Which off-diagonal to set, corresponding to elements a[i,i+k]. 

1252 Default: 0 (the main diagonal). 

1253 

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) 

1259 

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 

1286 

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) 

1299 

1300 

1301def isspmatrix(x): 

1302 """Is x of a sparse matrix type? 

1303 

1304 Parameters 

1305 ---------- 

1306 x 

1307 object to check for being a sparse matrix 

1308 

1309 Returns 

1310 ------- 

1311 bool 

1312 True if x is a sparse matrix, False otherwise 

1313 

1314 Notes 

1315 ----- 

1316 issparse and isspmatrix are aliases for the same function. 

1317 

1318 Examples 

1319 -------- 

1320 >>> from scipy.sparse import csr_matrix, isspmatrix 

1321 >>> isspmatrix(csr_matrix([[5]])) 

1322 True 

1323 

1324 >>> from scipy.sparse import isspmatrix 

1325 >>> isspmatrix(5) 

1326 False 

1327 """ 

1328 return isinstance(x, spmatrix) 

1329 

1330 

1331issparse = isspmatrix