Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

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

2import numpy as np 

3 

4from .sputils import (isdense, isscalarlike, isintlike, 

5 get_sum_dtype, validateaxis, check_reshape_kwargs, 

6 check_shape, asmatrix) 

7 

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

9 'SparseWarning', 'SparseEfficiencyWarning'] 

10 

11 

12class SparseWarning(Warning): 

13 pass 

14 

15 

16class SparseFormatWarning(SparseWarning): 

17 pass 

18 

19 

20class SparseEfficiencyWarning(SparseWarning): 

21 pass 

22 

23 

24# The formats that we might potentially understand. 

25_formats = {'csc': [0, "Compressed Sparse Column"], 

26 'csr': [1, "Compressed Sparse Row"], 

27 'dok': [2, "Dictionary Of Keys"], 

28 'lil': [3, "List of Lists"], 

29 'dod': [4, "Dictionary of Dictionaries"], 

30 'sss': [5, "Symmetric Sparse Skyline"], 

31 'coo': [6, "COOrdinate"], 

32 'lba': [7, "Linpack BAnded"], 

33 'egd': [8, "Ellpack-itpack Generalized Diagonal"], 

34 'dia': [9, "DIAgonal"], 

35 'bsr': [10, "Block Sparse Row"], 

36 'msr': [11, "Modified compressed Sparse Row"], 

37 'bsc': [12, "Block Sparse Column"], 

38 'msc': [13, "Modified compressed Sparse Column"], 

39 'ssk': [14, "Symmetric SKyline"], 

40 'nsk': [15, "Nonsymmetric SKyline"], 

41 'jad': [16, "JAgged Diagonal"], 

42 'uss': [17, "Unsymmetric Sparse Skyline"], 

43 'vbr': [18, "Variable Block Row"], 

44 'und': [19, "Undefined"] 

45 } 

46 

47 

48# These univariate ufuncs preserve zeros. 

49_ufuncs_with_fixed_point_at_zero = frozenset([ 

50 np.sin, np.tan, np.arcsin, np.arctan, np.sinh, np.tanh, np.arcsinh, 

51 np.arctanh, np.rint, np.sign, np.expm1, np.log1p, np.deg2rad, 

52 np.rad2deg, np.floor, np.ceil, np.trunc, np.sqrt]) 

53 

54 

55MAXPRINT = 50 

56 

57 

58class spmatrix(object): 

59 """ This class provides a base class for all sparse matrices. It 

60 cannot be instantiated. Most of the work is provided by subclasses. 

61 """ 

62 

63 __array_priority__ = 10.1 

64 ndim = 2 

65 

66 def __init__(self, maxprint=MAXPRINT): 

67 self._shape = None 

68 if self.__class__.__name__ == 'spmatrix': 

69 raise ValueError("This class is not intended" 

70 " to be instantiated directly.") 

71 self.maxprint = maxprint 

72 

73 def set_shape(self, shape): 

74 """See `reshape`.""" 

75 # Make sure copy is False since this is in place 

76 # Make sure format is unchanged because we are doing a __dict__ swap 

77 new_matrix = self.reshape(shape, copy=False).asformat(self.format) 

78 self.__dict__ = new_matrix.__dict__ 

79 

80 def get_shape(self): 

81 """Get shape of a matrix.""" 

82 return self._shape 

83 

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

85 

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

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

88 

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

90 

91 Parameters 

92 ---------- 

93 shape : length-2 tuple of ints 

94 The new shape should be compatible with the original shape. 

95 order : {'C', 'F'}, optional 

96 Read the elements using this index order. 'C' means to read and 

97 write the elements using C-like index order; e.g., read entire first 

98 row, then second row, etc. 'F' means to read and write the elements 

99 using Fortran-like index order; e.g., read entire first column, then 

100 second column, etc. 

101 copy : bool, optional 

102 Indicates whether or not attributes of self should be copied 

103 whenever possible. The degree to which attributes are copied varies 

104 depending on the type of sparse matrix being used. 

105 

106 Returns 

107 ------- 

108 reshaped_matrix : sparse matrix 

109 A sparse matrix with the given `shape`, not necessarily of the same 

110 format as the current object. 

111 

112 See Also 

113 -------- 

114 numpy.matrix.reshape : NumPy's implementation of 'reshape' for 

115 matrices 

116 """ 

117 # If the shape already matches, don't bother doing an actual reshape 

118 # Otherwise, the default is to convert to COO and use its reshape 

119 shape = check_shape(args, self.shape) 

120 order, copy = check_reshape_kwargs(kwargs) 

121 if shape == self.shape: 

122 if copy: 

123 return self.copy() 

124 else: 

125 return self 

126 

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

128 

129 def resize(self, shape): 

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

131 

132 Any elements that lie within the new shape will remain at the same 

133 indices, while non-zero elements lying outside the new shape are 

134 removed. 

135 

136 Parameters 

137 ---------- 

138 shape : (int, int) 

139 number of rows and columns in the new matrix 

140 

141 Notes 

142 ----- 

143 The semantics are not identical to `numpy.ndarray.resize` or 

144 `numpy.resize`. Here, the same data will be maintained at each index 

145 before and after reshape, if that index is within the new bounds. In 

146 numpy, resizing maintains contiguity of the array, moving elements 

147 around in the logical matrix but not within a flattened representation. 

148 

149 We give no guarantees about whether the underlying data attributes 

150 (arrays, etc.) will be modified in place or replaced with new objects. 

151 """ 

152 # As an inplace operation, this requires implementation in each format. 

153 raise NotImplementedError( 

154 '{}.resize is not implemented'.format(type(self).__name__)) 

155 

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

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

158 

159 Parameters 

160 ---------- 

161 dtype : string or numpy dtype 

162 Typecode or data-type to which to cast the data. 

163 casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, optional 

164 Controls what kind of data casting may occur. 

165 Defaults to 'unsafe' for backwards compatibility. 

166 'no' means the data types should not be cast at all. 

167 'equiv' means only byte-order changes are allowed. 

168 'safe' means only casts which can preserve values are allowed. 

169 'same_kind' means only safe casts or casts within a kind, 

170 like float64 to float32, are allowed. 

171 'unsafe' means any data conversions may be done. 

172 copy : bool, optional 

173 If `copy` is `False`, the result might share some memory with this 

174 matrix. If `copy` is `True`, it is guaranteed that the result and 

175 this matrix do not share any memory. 

176 """ 

177 

178 dtype = np.dtype(dtype) 

179 if self.dtype != dtype: 

180 return self.tocsr().astype( 

181 dtype, casting=casting, copy=copy).asformat(self.format) 

182 elif copy: 

183 return self.copy() 

184 else: 

185 return self 

186 

187 def asfptype(self): 

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

189 

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

191 

192 if self.dtype.char in fp_types: 

193 return self 

194 else: 

195 for fp_type in fp_types: 

196 if self.dtype <= np.dtype(fp_type): 

197 return self.astype(fp_type) 

198 

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

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

201 

202 def __iter__(self): 

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

204 yield self[r, :] 

205 

206 def getmaxprint(self): 

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

208 return self.maxprint 

209 

210 def count_nonzero(self): 

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

212 

213 np.count_nonzero(a.toarray()) 

214 

215 Unlike getnnz() and the nnz property, which return the number of stored 

216 entries (the length of the data attribute), this method counts the 

217 actual number of non-zero entries in data. 

218 """ 

219 raise NotImplementedError("count_nonzero not implemented for %s." % 

220 self.__class__.__name__) 

221 

222 def getnnz(self, axis=None): 

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

224 

225 Parameters 

226 ---------- 

227 axis : None, 0, or 1 

228 Select between the number of values across the whole matrix, in 

229 each column, or in each row. 

230 

231 See also 

232 -------- 

233 count_nonzero : Number of non-zero entries 

234 """ 

235 raise NotImplementedError("getnnz not implemented for %s." % 

236 self.__class__.__name__) 

237 

238 @property 

239 def nnz(self): 

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

241 

242 See also 

243 -------- 

244 count_nonzero : Number of non-zero entries 

245 """ 

246 return self.getnnz() 

247 

248 def getformat(self): 

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

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

251 

252 def __repr__(self): 

253 _, format_name = _formats[self.getformat()] 

254 return "<%dx%d sparse matrix of type '%s'\n" \ 

255 "\twith %d stored elements in %s format>" % \ 

256 (self.shape + (self.dtype.type, self.nnz, format_name)) 

257 

258 def __str__(self): 

259 maxprint = self.getmaxprint() 

260 

261 A = self.tocoo() 

262 

263 # helper function, outputs "(i,j) v" 

264 def tostr(row, col, data): 

265 triples = zip(list(zip(row, col)), data) 

266 return '\n'.join([(' %s\t%s' % t) for t in triples]) 

267 

268 if self.nnz > maxprint: 

269 half = maxprint // 2 

270 out = tostr(A.row[:half], A.col[:half], A.data[:half]) 

271 out += "\n :\t:\n" 

272 half = maxprint - maxprint//2 

273 out += tostr(A.row[-half:], A.col[-half:], A.data[-half:]) 

274 else: 

275 out = tostr(A.row, A.col, A.data) 

276 

277 return out 

278 

279 def __bool__(self): # Simple -- other ideas? 

280 if self.shape == (1, 1): 

281 return self.nnz != 0 

282 else: 

283 raise ValueError("The truth value of an array with more than one " 

284 "element is ambiguous. Use a.any() or a.all().") 

285 __nonzero__ = __bool__ 

286 

287 # What should len(sparse) return? For consistency with dense matrices, 

288 # perhaps it should be the number of rows? But for some uses the number of 

289 # non-zeros is more important. For now, raise an exception! 

290 def __len__(self): 

291 raise TypeError("sparse matrix length is ambiguous; use getnnz()" 

292 " or shape[0]") 

293 

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

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

296 

297 Parameters 

298 ---------- 

299 format : {str, None} 

300 The desired matrix format ("csr", "csc", "lil", "dok", "array", ...) 

301 or None for no conversion. 

302 copy : bool, optional 

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

304 

305 Returns 

306 ------- 

307 A : This matrix in the passed format. 

308 """ 

309 if format is None or format == self.format: 

310 if copy: 

311 return self.copy() 

312 else: 

313 return self 

314 else: 

315 try: 

316 convert_method = getattr(self, 'to' + format) 

317 except AttributeError: 

318 raise ValueError('Format {} is unknown.'.format(format)) 

319 

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

321 try: 

322 return convert_method(copy=copy) 

323 except TypeError: 

324 return convert_method() 

325 

326 ################################################################### 

327 # NOTE: All arithmetic operations use csr_matrix by default. 

328 # Therefore a new sparse matrix format just needs to define a 

329 # .tocsr() method to provide arithmetic support. Any of these 

330 # methods can be overridden for efficiency. 

331 #################################################################### 

332 

333 def multiply(self, other): 

334 """Point-wise multiplication by another matrix 

335 """ 

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

337 

338 def maximum(self, other): 

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

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

341 

342 def minimum(self, other): 

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

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

345 

346 def dot(self, other): 

347 """Ordinary dot product 

348 

349 Examples 

350 -------- 

351 >>> import numpy as np 

352 >>> from scipy.sparse import csr_matrix 

353 >>> A = csr_matrix([[1, 2, 0], [0, 0, 3], [4, 0, 5]]) 

354 >>> v = np.array([1, 0, -1]) 

355 >>> A.dot(v) 

356 array([ 1, -3, -1], dtype=int64) 

357 

358 """ 

359 return self * other 

360 

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

362 """Element-wise power.""" 

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

364 

365 def __eq__(self, other): 

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

367 

368 def __ne__(self, other): 

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

370 

371 def __lt__(self, other): 

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

373 

374 def __gt__(self, other): 

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

376 

377 def __le__(self, other): 

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

379 

380 def __ge__(self, other): 

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

382 

383 def __abs__(self): 

384 return abs(self.tocsr()) 

385 

386 def __round__(self, ndigits=0): 

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

388 

389 def _add_sparse(self, other): 

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

391 

392 def _add_dense(self, other): 

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

394 

395 def _sub_sparse(self, other): 

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

397 

398 def _sub_dense(self, other): 

399 return self.todense() - other 

400 

401 def _rsub_dense(self, other): 

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

403 return other - self.todense() 

404 

405 def __add__(self, other): # self + other 

406 if isscalarlike(other): 

407 if other == 0: 

408 return self.copy() 

409 # Now we would add this scalar to every element. 

410 raise NotImplementedError('adding a nonzero scalar to a ' 

411 'sparse matrix is not supported') 

412 elif isspmatrix(other): 

413 if other.shape != self.shape: 

414 raise ValueError("inconsistent shapes") 

415 return self._add_sparse(other) 

416 elif isdense(other): 

417 other = np.broadcast_to(other, self.shape) 

418 return self._add_dense(other) 

419 else: 

420 return NotImplemented 

421 

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

423 return self.__add__(other) 

424 

425 def __sub__(self, other): # self - other 

426 if isscalarlike(other): 

427 if other == 0: 

428 return self.copy() 

429 raise NotImplementedError('subtracting a nonzero scalar from a ' 

430 'sparse matrix is not supported') 

431 elif isspmatrix(other): 

432 if other.shape != self.shape: 

433 raise ValueError("inconsistent shapes") 

434 return self._sub_sparse(other) 

435 elif isdense(other): 

436 other = np.broadcast_to(other, self.shape) 

437 return self._sub_dense(other) 

438 else: 

439 return NotImplemented 

440 

441 def __rsub__(self,other): # other - self 

442 if isscalarlike(other): 

443 if other == 0: 

444 return -self.copy() 

445 raise NotImplementedError('subtracting a sparse matrix from a ' 

446 'nonzero scalar is not supported') 

447 elif isdense(other): 

448 other = np.broadcast_to(other, self.shape) 

449 return self._rsub_dense(other) 

450 else: 

451 return NotImplemented 

452 

453 def __mul__(self, other): 

454 """interpret other and call one of the following 

455 

456 self._mul_scalar() 

457 self._mul_vector() 

458 self._mul_multivector() 

459 self._mul_sparse_matrix() 

460 """ 

461 

462 M, N = self.shape 

463 

464 if other.__class__ is np.ndarray: 

465 # Fast path for the most common case 

466 if other.shape == (N,): 

467 return self._mul_vector(other) 

468 elif other.shape == (N, 1): 

469 return self._mul_vector(other.ravel()).reshape(M, 1) 

470 elif other.ndim == 2 and other.shape[0] == N: 

471 return self._mul_multivector(other) 

472 

473 if isscalarlike(other): 

474 # scalar value 

475 return self._mul_scalar(other) 

476 

477 if issparse(other): 

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

479 raise ValueError('dimension mismatch') 

480 return self._mul_sparse_matrix(other) 

481 

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

483 other_a = np.asanyarray(other) 

484 

485 if other_a.ndim == 0 and other_a.dtype == np.object_: 

486 # Not interpretable as an array; return NotImplemented so that 

487 # other's __rmul__ can kick in if that's implemented. 

488 return NotImplemented 

489 

490 try: 

491 other.shape 

492 except AttributeError: 

493 other = other_a 

494 

495 if other.ndim == 1 or other.ndim == 2 and other.shape[1] == 1: 

496 # dense row or column vector 

497 if other.shape != (N,) and other.shape != (N, 1): 

498 raise ValueError('dimension mismatch') 

499 

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

501 

502 if isinstance(other, np.matrix): 

503 result = asmatrix(result) 

504 

505 if other.ndim == 2 and other.shape[1] == 1: 

506 # If 'other' was an (nx1) column vector, reshape the result 

507 result = result.reshape(-1, 1) 

508 

509 return result 

510 

511 elif other.ndim == 2: 

512 ## 

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

514 

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

516 raise ValueError('dimension mismatch') 

517 

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

519 

520 if isinstance(other, np.matrix): 

521 result = asmatrix(result) 

522 

523 return result 

524 

525 else: 

526 raise ValueError('could not interpret dimensions') 

527 

528 # by default, use CSR for __mul__ handlers 

529 def _mul_scalar(self, other): 

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

531 

532 def _mul_vector(self, other): 

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

534 

535 def _mul_multivector(self, other): 

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

537 

538 def _mul_sparse_matrix(self, other): 

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

540 

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

542 if isscalarlike(other): 

543 return self.__mul__(other) 

544 else: 

545 # Don't use asarray unless we have to 

546 try: 

547 tr = other.transpose() 

548 except AttributeError: 

549 tr = np.asarray(other).transpose() 

550 return (self.transpose() * tr).transpose() 

551 

552 ####################### 

553 # matmul (@) operator # 

554 ####################### 

555 

556 def __matmul__(self, other): 

557 if isscalarlike(other): 

558 raise ValueError("Scalar operands are not allowed, " 

559 "use '*' instead") 

560 return self.__mul__(other) 

561 

562 def __rmatmul__(self, other): 

563 if isscalarlike(other): 

564 raise ValueError("Scalar operands are not allowed, " 

565 "use '*' instead") 

566 return self.__rmul__(other) 

567 

568 #################### 

569 # Other Arithmetic # 

570 #################### 

571 

572 def _divide(self, other, true_divide=False, rdivide=False): 

573 if isscalarlike(other): 

574 if rdivide: 

575 if true_divide: 

576 return np.true_divide(other, self.todense()) 

577 else: 

578 return np.divide(other, self.todense()) 

579 

580 if true_divide and np.can_cast(self.dtype, np.float_): 

581 return self.astype(np.float_)._mul_scalar(1./other) 

582 else: 

583 r = self._mul_scalar(1./other) 

584 

585 scalar_dtype = np.asarray(other).dtype 

586 if (np.issubdtype(self.dtype, np.integer) and 

587 np.issubdtype(scalar_dtype, np.integer)): 

588 return r.astype(self.dtype) 

589 else: 

590 return r 

591 

592 elif isdense(other): 

593 if not rdivide: 

594 if true_divide: 

595 return np.true_divide(self.todense(), other) 

596 else: 

597 return np.divide(self.todense(), other) 

598 else: 

599 if true_divide: 

600 return np.true_divide(other, self.todense()) 

601 else: 

602 return np.divide(other, self.todense()) 

603 elif isspmatrix(other): 

604 if rdivide: 

605 return other._divide(self, true_divide, rdivide=False) 

606 

607 self_csr = self.tocsr() 

608 if true_divide and np.can_cast(self.dtype, np.float_): 

609 return self_csr.astype(np.float_)._divide_sparse(other) 

610 else: 

611 return self_csr._divide_sparse(other) 

612 else: 

613 return NotImplemented 

614 

615 def __truediv__(self, other): 

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

617 

618 def __div__(self, other): 

619 # Always do true division 

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

621 

622 def __rtruediv__(self, other): 

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

624 return NotImplemented 

625 

626 def __rdiv__(self, other): 

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

628 return NotImplemented 

629 

630 def __neg__(self): 

631 return -self.tocsr() 

632 

633 def __iadd__(self, other): 

634 return NotImplemented 

635 

636 def __isub__(self, other): 

637 return NotImplemented 

638 

639 def __imul__(self, other): 

640 return NotImplemented 

641 

642 def __idiv__(self, other): 

643 return self.__itruediv__(other) 

644 

645 def __itruediv__(self, other): 

646 return NotImplemented 

647 

648 def __pow__(self, other): 

649 if self.shape[0] != self.shape[1]: 

650 raise TypeError('matrix is not square') 

651 

652 if isintlike(other): 

653 other = int(other) 

654 if other < 0: 

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

656 

657 if other == 0: 

658 from .construct import eye 

659 return eye(self.shape[0], dtype=self.dtype) 

660 elif other == 1: 

661 return self.copy() 

662 else: 

663 tmp = self.__pow__(other//2) 

664 if (other % 2): 

665 return self * tmp * tmp 

666 else: 

667 return tmp * tmp 

668 elif isscalarlike(other): 

669 raise ValueError('exponent must be an integer') 

670 else: 

671 return NotImplemented 

672 

673 def __getattr__(self, attr): 

674 if attr == 'A': 

675 return self.toarray() 

676 elif attr == 'T': 

677 return self.transpose() 

678 elif attr == 'H': 

679 return self.getH() 

680 elif attr == 'real': 

681 return self._real() 

682 elif attr == 'imag': 

683 return self._imag() 

684 elif attr == 'size': 

685 return self.getnnz() 

686 else: 

687 raise AttributeError(attr + " not found") 

688 

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

690 """ 

691 Reverses the dimensions of the sparse matrix. 

692 

693 Parameters 

694 ---------- 

695 axes : None, optional 

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

697 compatibility reasons. Do not pass in anything except 

698 for the default value. 

699 copy : bool, optional 

700 Indicates whether or not attributes of `self` should be 

701 copied whenever possible. The degree to which attributes 

702 are copied varies depending on the type of sparse matrix 

703 being used. 

704 

705 Returns 

706 ------- 

707 p : `self` with the dimensions reversed. 

708 

709 See Also 

710 -------- 

711 numpy.matrix.transpose : NumPy's implementation of 'transpose' 

712 for matrices 

713 """ 

714 return self.tocsr(copy=copy).transpose(axes=axes, copy=False) 

715 

716 def conj(self, copy=True): 

717 """Element-wise complex conjugation. 

718 

719 If the matrix is of non-complex data type and `copy` is False, 

720 this method does nothing and the data is not copied. 

721 

722 Parameters 

723 ---------- 

724 copy : bool, optional 

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

726 

727 Returns 

728 ------- 

729 A : The element-wise complex conjugate. 

730 

731 """ 

732 if np.issubdtype(self.dtype, np.complexfloating): 

733 return self.tocsr(copy=copy).conj(copy=False) 

734 elif copy: 

735 return self.copy() 

736 else: 

737 return self 

738 

739 def conjugate(self, copy=True): 

740 return self.conj(copy=copy) 

741 

742 conjugate.__doc__ = conj.__doc__ 

743 

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

745 def getH(self): 

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

747 

748 See Also 

749 -------- 

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

751 """ 

752 return self.transpose().conj() 

753 

754 def _real(self): 

755 return self.tocsr()._real() 

756 

757 def _imag(self): 

758 return self.tocsr()._imag() 

759 

760 def nonzero(self): 

761 """nonzero indices 

762 

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

764 of the non-zero elements of the matrix. 

765 

766 Examples 

767 -------- 

768 >>> from scipy.sparse import csr_matrix 

769 >>> A = csr_matrix([[1,2,0],[0,0,3],[4,0,5]]) 

770 >>> A.nonzero() 

771 (array([0, 0, 1, 2, 2]), array([0, 1, 2, 0, 2])) 

772 

773 """ 

774 

775 # convert to COOrdinate format 

776 A = self.tocoo() 

777 nz_mask = A.data != 0 

778 return (A.row[nz_mask], A.col[nz_mask]) 

779 

780 def getcol(self, j): 

781 """Returns a copy of column j of the matrix, as an (m x 1) sparse 

782 matrix (column vector). 

783 """ 

784 # Spmatrix subclasses should override this method for efficiency. 

785 # Post-multiply by a (n x 1) column vector 'a' containing all zeros 

786 # except for a_j = 1 

787 from .csc import csc_matrix 

788 n = self.shape[1] 

789 if j < 0: 

790 j += n 

791 if j < 0 or j >= n: 

792 raise IndexError("index out of bounds") 

793 col_selector = csc_matrix(([1], [[j], [0]]), 

794 shape=(n, 1), dtype=self.dtype) 

795 return self * col_selector 

796 

797 def getrow(self, i): 

798 """Returns a copy of row i of the matrix, as a (1 x n) sparse 

799 matrix (row vector). 

800 """ 

801 # Spmatrix subclasses should override this method for efficiency. 

802 # Pre-multiply by a (1 x m) row vector 'a' containing all zeros 

803 # except for a_i = 1 

804 from .csr import csr_matrix 

805 m = self.shape[0] 

806 if i < 0: 

807 i += m 

808 if i < 0 or i >= m: 

809 raise IndexError("index out of bounds") 

810 row_selector = csr_matrix(([1], [[0], [i]]), 

811 shape=(1, m), dtype=self.dtype) 

812 return row_selector * self 

813 

814 # def __array__(self): 

815 # return self.toarray() 

816 

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

818 """ 

819 Return a dense matrix representation of this matrix. 

820 

821 Parameters 

822 ---------- 

823 order : {'C', 'F'}, optional 

824 Whether to store multi-dimensional data in C (row-major) 

825 or Fortran (column-major) order in memory. The default 

826 is 'None', indicating the NumPy default of C-ordered. 

827 Cannot be specified in conjunction with the `out` 

828 argument. 

829 

830 out : ndarray, 2-D, optional 

831 If specified, uses this array (or `numpy.matrix`) as the 

832 output buffer instead of allocating a new array to 

833 return. The provided array must have the same shape and 

834 dtype as the sparse matrix on which you are calling the 

835 method. 

836 

837 Returns 

838 ------- 

839 arr : numpy.matrix, 2-D 

840 A NumPy matrix object with the same shape and containing 

841 the same data represented by the sparse matrix, with the 

842 requested memory order. If `out` was passed and was an 

843 array (rather than a `numpy.matrix`), it will be filled 

844 with the appropriate values and returned wrapped in a 

845 `numpy.matrix` object that shares the same memory. 

846 """ 

847 return asmatrix(self.toarray(order=order, out=out)) 

848 

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

850 """ 

851 Return a dense ndarray representation of this matrix. 

852 

853 Parameters 

854 ---------- 

855 order : {'C', 'F'}, optional 

856 Whether to store multidimensional data in C (row-major) 

857 or Fortran (column-major) order in memory. The default 

858 is 'None', indicating the NumPy default of C-ordered. 

859 Cannot be specified in conjunction with the `out` 

860 argument. 

861 

862 out : ndarray, 2-D, optional 

863 If specified, uses this array as the output buffer 

864 instead of allocating a new array to return. The provided 

865 array must have the same shape and dtype as the sparse 

866 matrix on which you are calling the method. For most 

867 sparse types, `out` is required to be memory contiguous 

868 (either C or Fortran ordered). 

869 

870 Returns 

871 ------- 

872 arr : ndarray, 2-D 

873 An array with the same shape and containing the same 

874 data represented by the sparse matrix, with the requested 

875 memory order. If `out` was passed, the same object is 

876 returned after being modified in-place to contain the 

877 appropriate values. 

878 """ 

879 return self.tocoo(copy=False).toarray(order=order, out=out) 

880 

881 # Any sparse matrix format deriving from spmatrix must define one of 

882 # tocsr or tocoo. The other conversion methods may be implemented for 

883 # efficiency, but are not required. 

884 def tocsr(self, copy=False): 

885 """Convert this matrix to Compressed Sparse Row format. 

886 

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

888 the resultant csr_matrix. 

889 """ 

890 return self.tocoo(copy=copy).tocsr(copy=False) 

891 

892 def todok(self, copy=False): 

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

894 

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

896 the resultant dok_matrix. 

897 """ 

898 return self.tocoo(copy=copy).todok(copy=False) 

899 

900 def tocoo(self, copy=False): 

901 """Convert this matrix to COOrdinate format. 

902 

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

904 the resultant coo_matrix. 

905 """ 

906 return self.tocsr(copy=False).tocoo(copy=copy) 

907 

908 def tolil(self, copy=False): 

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

910 

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

912 the resultant lil_matrix. 

913 """ 

914 return self.tocsr(copy=False).tolil(copy=copy) 

915 

916 def todia(self, copy=False): 

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

918 

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

920 the resultant dia_matrix. 

921 """ 

922 return self.tocoo(copy=copy).todia(copy=False) 

923 

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

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

926 

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

928 the resultant bsr_matrix. 

929 

930 When blocksize=(R, C) is provided, it will be used for construction of 

931 the bsr_matrix. 

932 """ 

933 return self.tocsr(copy=False).tobsr(blocksize=blocksize, copy=copy) 

934 

935 def tocsc(self, copy=False): 

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

937 

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

939 the resultant csc_matrix. 

940 """ 

941 return self.tocsr(copy=copy).tocsc(copy=False) 

942 

943 def copy(self): 

944 """Returns a copy of this matrix. 

945 

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

947 matrix. 

948 """ 

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

950 

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

952 """ 

953 Sum the matrix elements over a given axis. 

954 

955 Parameters 

956 ---------- 

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

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

959 compute the sum of all the matrix elements, returning a scalar 

960 (i.e., `axis` = `None`). 

961 dtype : dtype, optional 

962 The type of the returned matrix and of the accumulator in which 

963 the elements are summed. The dtype of `a` is used by default 

964 unless `a` has an integer dtype of less precision than the default 

965 platform integer. In that case, if `a` is signed then the platform 

966 integer is used while if `a` is unsigned then an unsigned integer 

967 of the same precision as the platform integer is used. 

968 

969 .. versionadded:: 0.18.0 

970 

971 out : np.matrix, optional 

972 Alternative output matrix in which to place the result. It must 

973 have the same shape as the expected output, but the type of the 

974 output values will be cast if necessary. 

975 

976 .. versionadded:: 0.18.0 

977 

978 Returns 

979 ------- 

980 sum_along_axis : np.matrix 

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

982 axis removed. 

983 

984 See Also 

985 -------- 

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

987 

988 """ 

989 validateaxis(axis) 

990 

991 # We use multiplication by a matrix of ones to achieve this. 

992 # For some sparse matrix formats more efficient methods are 

993 # possible -- these should override this function. 

994 m, n = self.shape 

995 

996 # Mimic numpy's casting. 

997 res_dtype = get_sum_dtype(self.dtype) 

998 

999 if axis is None: 

1000 # sum over rows and columns 

1001 return (self * asmatrix(np.ones( 

1002 (n, 1), dtype=res_dtype))).sum( 

1003 dtype=dtype, out=out) 

1004 

1005 if axis < 0: 

1006 axis += 2 

1007 

1008 # axis = 0 or 1 now 

1009 if axis == 0: 

1010 # sum over columns 

1011 ret = asmatrix(np.ones( 

1012 (1, m), dtype=res_dtype)) * self 

1013 else: 

1014 # sum over rows 

1015 ret = self * asmatrix( 

1016 np.ones((n, 1), dtype=res_dtype)) 

1017 

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

1019 raise ValueError("dimensions do not match") 

1020 

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

1022 

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

1024 """ 

1025 Compute the arithmetic mean along the specified axis. 

1026 

1027 Returns the average of the matrix elements. The average is taken 

1028 over all elements in the matrix by default, otherwise over the 

1029 specified axis. `float64` intermediate and return values are used 

1030 for integer inputs. 

1031 

1032 Parameters 

1033 ---------- 

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

1035 Axis along which the mean is computed. The default is to compute 

1036 the mean of all elements in the matrix (i.e., `axis` = `None`). 

1037 dtype : data-type, optional 

1038 Type to use in computing the mean. For integer inputs, the default 

1039 is `float64`; for floating point inputs, it is the same as the 

1040 input dtype. 

1041 

1042 .. versionadded:: 0.18.0 

1043 

1044 out : np.matrix, optional 

1045 Alternative output matrix in which to place the result. It must 

1046 have the same shape as the expected output, but the type of the 

1047 output values will be cast if necessary. 

1048 

1049 .. versionadded:: 0.18.0 

1050 

1051 Returns 

1052 ------- 

1053 m : np.matrix 

1054 

1055 See Also 

1056 -------- 

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

1058 

1059 """ 

1060 def _is_integral(dtype): 

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

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

1063 

1064 validateaxis(axis) 

1065 

1066 res_dtype = self.dtype.type 

1067 integral = _is_integral(self.dtype) 

1068 

1069 # output dtype 

1070 if dtype is None: 

1071 if integral: 

1072 res_dtype = np.float64 

1073 else: 

1074 res_dtype = np.dtype(dtype).type 

1075 

1076 # intermediate dtype for summation 

1077 inter_dtype = np.float64 if integral else res_dtype 

1078 inter_self = self.astype(inter_dtype) 

1079 

1080 if axis is None: 

1081 return (inter_self / np.array( 

1082 self.shape[0] * self.shape[1]))\ 

1083 .sum(dtype=res_dtype, out=out) 

1084 

1085 if axis < 0: 

1086 axis += 2 

1087 

1088 # axis = 0 or 1 now 

1089 if axis == 0: 

1090 return (inter_self * (1.0 / self.shape[0])).sum( 

1091 axis=0, dtype=res_dtype, out=out) 

1092 else: 

1093 return (inter_self * (1.0 / self.shape[1])).sum( 

1094 axis=1, dtype=res_dtype, out=out) 

1095 

1096 def diagonal(self, k=0): 

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

1098 

1099 Parameters 

1100 ---------- 

1101 k : int, optional 

1102 Which diagonal to get, corresponding to elements a[i, i+k]. 

1103 Default: 0 (the main diagonal). 

1104 

1105 .. versionadded:: 1.0 

1106 

1107 See also 

1108 -------- 

1109 numpy.diagonal : Equivalent numpy function. 

1110 

1111 Examples 

1112 -------- 

1113 >>> from scipy.sparse import csr_matrix 

1114 >>> A = csr_matrix([[1, 2, 0], [0, 0, 3], [4, 0, 5]]) 

1115 >>> A.diagonal() 

1116 array([1, 0, 5]) 

1117 >>> A.diagonal(k=1) 

1118 array([2, 3]) 

1119 """ 

1120 return self.tocsr().diagonal(k=k) 

1121 

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

1123 """ 

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

1125 

1126 Parameters 

1127 ---------- 

1128 values : array_like 

1129 New values of the diagonal elements. 

1130 

1131 Values may have any length. If the diagonal is longer than values, 

1132 then the remaining diagonal entries will not be set. If values if 

1133 longer than the diagonal, then the remaining values are ignored. 

1134 

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

1136 

1137 k : int, optional 

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

1139 Default: 0 (the main diagonal). 

1140 

1141 """ 

1142 M, N = self.shape 

1143 if (k > 0 and k >= N) or (k < 0 and -k >= M): 

1144 raise ValueError("k exceeds matrix dimensions") 

1145 self._setdiag(np.asarray(values), k) 

1146 

1147 def _setdiag(self, values, k): 

1148 M, N = self.shape 

1149 if k < 0: 

1150 if values.ndim == 0: 

1151 # broadcast 

1152 max_index = min(M+k, N) 

1153 for i in range(max_index): 

1154 self[i - k, i] = values 

1155 else: 

1156 max_index = min(M+k, N, len(values)) 

1157 if max_index <= 0: 

1158 return 

1159 for i, v in enumerate(values[:max_index]): 

1160 self[i - k, i] = v 

1161 else: 

1162 if values.ndim == 0: 

1163 # broadcast 

1164 max_index = min(M, N-k) 

1165 for i in range(max_index): 

1166 self[i, i + k] = values 

1167 else: 

1168 max_index = min(M, N-k, len(values)) 

1169 if max_index <= 0: 

1170 return 

1171 for i, v in enumerate(values[:max_index]): 

1172 self[i, i + k] = v 

1173 

1174 def _process_toarray_args(self, order, out): 

1175 if out is not None: 

1176 if order is not None: 

1177 raise ValueError('order cannot be specified if out ' 

1178 'is not None') 

1179 if out.shape != self.shape or out.dtype != self.dtype: 

1180 raise ValueError('out array must be same dtype and shape as ' 

1181 'sparse matrix') 

1182 out[...] = 0. 

1183 return out 

1184 else: 

1185 return np.zeros(self.shape, dtype=self.dtype, order=order) 

1186 

1187 

1188def isspmatrix(x): 

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

1190 

1191 Parameters 

1192 ---------- 

1193 x 

1194 object to check for being a sparse matrix 

1195 

1196 Returns 

1197 ------- 

1198 bool 

1199 True if x is a sparse matrix, False otherwise 

1200 

1201 Notes 

1202 ----- 

1203 issparse and isspmatrix are aliases for the same function. 

1204 

1205 Examples 

1206 -------- 

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

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

1209 True 

1210 

1211 >>> from scipy.sparse import isspmatrix 

1212 >>> isspmatrix(5) 

1213 False 

1214 """ 

1215 return isinstance(x, spmatrix) 

1216 

1217 

1218issparse = isspmatrix