Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/scipy/sparse/_bsr.py: 14%

310 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-12 06:31 +0000

1"""Compressed Block Sparse Row matrix format""" 

2 

3__docformat__ = "restructuredtext en" 

4 

5__all__ = ['bsr_matrix', 'isspmatrix_bsr'] 

6 

7from warnings import warn 

8 

9import numpy as np 

10 

11from ._data import _data_matrix, _minmax_mixin 

12from ._compressed import _cs_matrix 

13from ._base import isspmatrix, _formats, spmatrix 

14from ._sputils import (isshape, getdtype, getdata, to_native, upcast, 

15 get_index_dtype, check_shape) 

16from . import _sparsetools 

17from ._sparsetools import (bsr_matvec, bsr_matvecs, csr_matmat_maxnnz, 

18 bsr_matmat, bsr_transpose, bsr_sort_indices, 

19 bsr_tocsr) 

20 

21 

22class bsr_matrix(_cs_matrix, _minmax_mixin): 

23 """Block Sparse Row matrix 

24 

25 This can be instantiated in several ways: 

26 bsr_matrix(D, [blocksize=(R,C)]) 

27 where D is a dense matrix or 2-D ndarray. 

28 

29 bsr_matrix(S, [blocksize=(R,C)]) 

30 with another sparse matrix S (equivalent to S.tobsr()) 

31 

32 bsr_matrix((M, N), [blocksize=(R,C), dtype]) 

33 to construct an empty matrix with shape (M, N) 

34 dtype is optional, defaulting to dtype='d'. 

35 

36 bsr_matrix((data, ij), [blocksize=(R,C), shape=(M, N)]) 

37 where ``data`` and ``ij`` satisfy ``a[ij[0, k], ij[1, k]] = data[k]`` 

38 

39 bsr_matrix((data, indices, indptr), [shape=(M, N)]) 

40 is the standard BSR representation where the block column 

41 indices for row i are stored in ``indices[indptr[i]:indptr[i+1]]`` 

42 and their corresponding block values are stored in 

43 ``data[ indptr[i]: indptr[i+1] ]``. If the shape parameter is not 

44 supplied, the matrix dimensions are inferred from the index arrays. 

45 

46 Attributes 

47 ---------- 

48 dtype : dtype 

49 Data type of the matrix 

50 shape : 2-tuple 

51 Shape of the matrix 

52 ndim : int 

53 Number of dimensions (this is always 2) 

54 nnz 

55 Number of stored values, including explicit zeros 

56 data 

57 Data array of the matrix 

58 indices 

59 BSR format index array 

60 indptr 

61 BSR format index pointer array 

62 blocksize 

63 Block size of the matrix 

64 has_sorted_indices 

65 Whether indices are sorted 

66 

67 Notes 

68 ----- 

69 Sparse matrices can be used in arithmetic operations: they support 

70 addition, subtraction, multiplication, division, and matrix power. 

71 

72 **Summary of BSR format** 

73 

74 The Block Compressed Row (BSR) format is very similar to the Compressed 

75 Sparse Row (CSR) format. BSR is appropriate for sparse matrices with dense 

76 sub matrices like the last example below. Block matrices often arise in 

77 vector-valued finite element discretizations. In such cases, BSR is 

78 considerably more efficient than CSR and CSC for many sparse arithmetic 

79 operations. 

80 

81 **Blocksize** 

82 

83 The blocksize (R,C) must evenly divide the shape of the matrix (M,N). 

84 That is, R and C must satisfy the relationship ``M % R = 0`` and 

85 ``N % C = 0``. 

86 

87 If no blocksize is specified, a simple heuristic is applied to determine 

88 an appropriate blocksize. 

89 

90 Examples 

91 -------- 

92 >>> from scipy.sparse import bsr_matrix 

93 >>> import numpy as np 

94 >>> bsr_matrix((3, 4), dtype=np.int8).toarray() 

95 array([[0, 0, 0, 0], 

96 [0, 0, 0, 0], 

97 [0, 0, 0, 0]], dtype=int8) 

98 

99 >>> row = np.array([0, 0, 1, 2, 2, 2]) 

100 >>> col = np.array([0, 2, 2, 0, 1, 2]) 

101 >>> data = np.array([1, 2, 3 ,4, 5, 6]) 

102 >>> bsr_matrix((data, (row, col)), shape=(3, 3)).toarray() 

103 array([[1, 0, 2], 

104 [0, 0, 3], 

105 [4, 5, 6]]) 

106 

107 >>> indptr = np.array([0, 2, 3, 6]) 

108 >>> indices = np.array([0, 2, 2, 0, 1, 2]) 

109 >>> data = np.array([1, 2, 3, 4, 5, 6]).repeat(4).reshape(6, 2, 2) 

110 >>> bsr_matrix((data,indices,indptr), shape=(6, 6)).toarray() 

111 array([[1, 1, 0, 0, 2, 2], 

112 [1, 1, 0, 0, 2, 2], 

113 [0, 0, 0, 0, 3, 3], 

114 [0, 0, 0, 0, 3, 3], 

115 [4, 4, 5, 5, 6, 6], 

116 [4, 4, 5, 5, 6, 6]]) 

117 

118 """ 

119 format = 'bsr' 

120 

121 def __init__(self, arg1, shape=None, dtype=None, copy=False, blocksize=None): 

122 _data_matrix.__init__(self) 

123 

124 if isspmatrix(arg1): 

125 if isspmatrix_bsr(arg1) and copy: 

126 arg1 = arg1.copy() 

127 else: 

128 arg1 = arg1.tobsr(blocksize=blocksize) 

129 self._set_self(arg1) 

130 

131 elif isinstance(arg1,tuple): 

132 if isshape(arg1): 

133 # it's a tuple of matrix dimensions (M,N) 

134 self._shape = check_shape(arg1) 

135 M,N = self.shape 

136 # process blocksize 

137 if blocksize is None: 

138 blocksize = (1,1) 

139 else: 

140 if not isshape(blocksize): 

141 raise ValueError('invalid blocksize=%s' % blocksize) 

142 blocksize = tuple(blocksize) 

143 self.data = np.zeros((0,) + blocksize, getdtype(dtype, default=float)) 

144 

145 R,C = blocksize 

146 if (M % R) != 0 or (N % C) != 0: 

147 raise ValueError('shape must be multiple of blocksize') 

148 

149 # Select index dtype large enough to pass array and 

150 # scalar parameters to sparsetools 

151 idx_dtype = get_index_dtype(maxval=max(M//R, N//C, R, C)) 

152 self.indices = np.zeros(0, dtype=idx_dtype) 

153 self.indptr = np.zeros(M//R + 1, dtype=idx_dtype) 

154 

155 elif len(arg1) == 2: 

156 # (data,(row,col)) format 

157 self._set_self( 

158 self._coo_container(arg1, dtype=dtype, shape=shape).tobsr( 

159 blocksize=blocksize 

160 ) 

161 ) 

162 

163 elif len(arg1) == 3: 

164 # (data,indices,indptr) format 

165 (data, indices, indptr) = arg1 

166 

167 # Select index dtype large enough to pass array and 

168 # scalar parameters to sparsetools 

169 maxval = 1 

170 if shape is not None: 

171 maxval = max(shape) 

172 if blocksize is not None: 

173 maxval = max(maxval, max(blocksize)) 

174 idx_dtype = get_index_dtype((indices, indptr), maxval=maxval, 

175 check_contents=True) 

176 self.indices = np.array(indices, copy=copy, dtype=idx_dtype) 

177 self.indptr = np.array(indptr, copy=copy, dtype=idx_dtype) 

178 self.data = getdata(data, copy=copy, dtype=dtype) 

179 if self.data.ndim != 3: 

180 raise ValueError( 

181 'BSR data must be 3-dimensional, got shape=%s' % ( 

182 self.data.shape,)) 

183 if blocksize is not None: 

184 if not isshape(blocksize): 

185 raise ValueError('invalid blocksize=%s' % (blocksize,)) 

186 if tuple(blocksize) != self.data.shape[1:]: 

187 raise ValueError('mismatching blocksize=%s vs %s' % ( 

188 blocksize, self.data.shape[1:])) 

189 else: 

190 raise ValueError('unrecognized bsr_matrix constructor usage') 

191 else: 

192 # must be dense 

193 try: 

194 arg1 = np.asarray(arg1) 

195 except Exception as e: 

196 raise ValueError("unrecognized form for" 

197 " %s_matrix constructor" % self.format) from e 

198 arg1 = self._coo_container( 

199 arg1, dtype=dtype 

200 ).tobsr(blocksize=blocksize) 

201 self._set_self(arg1) 

202 

203 if shape is not None: 

204 self._shape = check_shape(shape) 

205 else: 

206 if self.shape is None: 

207 # shape not already set, try to infer dimensions 

208 try: 

209 M = len(self.indptr) - 1 

210 N = self.indices.max() + 1 

211 except Exception as e: 

212 raise ValueError('unable to infer matrix dimensions') from e 

213 else: 

214 R,C = self.blocksize 

215 self._shape = check_shape((M*R,N*C)) 

216 

217 if self.shape is None: 

218 if shape is None: 

219 # TODO infer shape here 

220 raise ValueError('need to infer shape') 

221 else: 

222 self._shape = check_shape(shape) 

223 

224 if dtype is not None: 

225 self.data = self.data.astype(dtype, copy=False) 

226 

227 self.check_format(full_check=False) 

228 

229 def check_format(self, full_check=True): 

230 """check whether the matrix format is valid 

231 

232 *Parameters*: 

233 full_check: 

234 True - rigorous check, O(N) operations : default 

235 False - basic check, O(1) operations 

236 

237 """ 

238 M,N = self.shape 

239 R,C = self.blocksize 

240 

241 # index arrays should have integer data types 

242 if self.indptr.dtype.kind != 'i': 

243 warn("indptr array has non-integer dtype (%s)" 

244 % self.indptr.dtype.name) 

245 if self.indices.dtype.kind != 'i': 

246 warn("indices array has non-integer dtype (%s)" 

247 % self.indices.dtype.name) 

248 

249 idx_dtype = get_index_dtype((self.indices, self.indptr)) 

250 self.indptr = np.asarray(self.indptr, dtype=idx_dtype) 

251 self.indices = np.asarray(self.indices, dtype=idx_dtype) 

252 self.data = to_native(self.data) 

253 

254 # check array shapes 

255 if self.indices.ndim != 1 or self.indptr.ndim != 1: 

256 raise ValueError("indices, and indptr should be 1-D") 

257 if self.data.ndim != 3: 

258 raise ValueError("data should be 3-D") 

259 

260 # check index pointer 

261 if (len(self.indptr) != M//R + 1): 

262 raise ValueError("index pointer size (%d) should be (%d)" % 

263 (len(self.indptr), M//R + 1)) 

264 if (self.indptr[0] != 0): 

265 raise ValueError("index pointer should start with 0") 

266 

267 # check index and data arrays 

268 if (len(self.indices) != len(self.data)): 

269 raise ValueError("indices and data should have the same size") 

270 if (self.indptr[-1] > len(self.indices)): 

271 raise ValueError("Last value of index pointer should be less than " 

272 "the size of index and data arrays") 

273 

274 self.prune() 

275 

276 if full_check: 

277 # check format validity (more expensive) 

278 if self.nnz > 0: 

279 if self.indices.max() >= N//C: 

280 raise ValueError("column index values must be < %d (now max %d)" % (N//C, self.indices.max())) 

281 if self.indices.min() < 0: 

282 raise ValueError("column index values must be >= 0") 

283 if np.diff(self.indptr).min() < 0: 

284 raise ValueError("index pointer values must form a " 

285 "non-decreasing sequence") 

286 

287 # if not self.has_sorted_indices(): 

288 # warn('Indices were not in sorted order. Sorting indices.') 

289 # self.sort_indices(check_first=False) 

290 

291 def _get_blocksize(self): 

292 return self.data.shape[1:] 

293 blocksize = property(fget=_get_blocksize) 

294 

295 def getnnz(self, axis=None): 

296 if axis is not None: 

297 raise NotImplementedError("getnnz over an axis is not implemented " 

298 "for BSR format") 

299 R,C = self.blocksize 

300 return int(self.indptr[-1] * R * C) 

301 

302 getnnz.__doc__ = spmatrix.getnnz.__doc__ 

303 

304 def __repr__(self): 

305 format = _formats[self.getformat()][1] 

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

307 "\twith %d stored elements (blocksize = %dx%d) in %s format>" % 

308 (self.shape + (self.dtype.type, self.nnz) + self.blocksize + 

309 (format,))) 

310 

311 def diagonal(self, k=0): 

312 rows, cols = self.shape 

313 if k <= -rows or k >= cols: 

314 return np.empty(0, dtype=self.data.dtype) 

315 R, C = self.blocksize 

316 y = np.zeros(min(rows + min(k, 0), cols - max(k, 0)), 

317 dtype=upcast(self.dtype)) 

318 _sparsetools.bsr_diagonal(k, rows // R, cols // C, R, C, 

319 self.indptr, self.indices, 

320 np.ravel(self.data), y) 

321 return y 

322 

323 diagonal.__doc__ = spmatrix.diagonal.__doc__ 

324 

325 ########################## 

326 # NotImplemented methods # 

327 ########################## 

328 

329 def __getitem__(self,key): 

330 raise NotImplementedError 

331 

332 def __setitem__(self,key,val): 

333 raise NotImplementedError 

334 

335 ###################### 

336 # Arithmetic methods # 

337 ###################### 

338 

339 def _add_dense(self, other): 

340 return self.tocoo(copy=False)._add_dense(other) 

341 

342 def _mul_vector(self, other): 

343 M,N = self.shape 

344 R,C = self.blocksize 

345 

346 result = np.zeros(self.shape[0], dtype=upcast(self.dtype, other.dtype)) 

347 

348 bsr_matvec(M//R, N//C, R, C, 

349 self.indptr, self.indices, self.data.ravel(), 

350 other, result) 

351 

352 return result 

353 

354 def _mul_multivector(self,other): 

355 R,C = self.blocksize 

356 M,N = self.shape 

357 n_vecs = other.shape[1] # number of column vectors 

358 

359 result = np.zeros((M,n_vecs), dtype=upcast(self.dtype,other.dtype)) 

360 

361 bsr_matvecs(M//R, N//C, n_vecs, R, C, 

362 self.indptr, self.indices, self.data.ravel(), 

363 other.ravel(), result.ravel()) 

364 

365 return result 

366 

367 def _mul_sparse_matrix(self, other): 

368 M, K1 = self.shape 

369 K2, N = other.shape 

370 

371 R,n = self.blocksize 

372 

373 # convert to this format 

374 if isspmatrix_bsr(other): 

375 C = other.blocksize[1] 

376 else: 

377 C = 1 

378 

379 from ._csr import isspmatrix_csr 

380 

381 if isspmatrix_csr(other) and n == 1: 

382 other = other.tobsr(blocksize=(n,C), copy=False) # lightweight conversion 

383 else: 

384 other = other.tobsr(blocksize=(n,C)) 

385 

386 idx_dtype = get_index_dtype((self.indptr, self.indices, 

387 other.indptr, other.indices)) 

388 

389 bnnz = csr_matmat_maxnnz(M//R, N//C, 

390 self.indptr.astype(idx_dtype), 

391 self.indices.astype(idx_dtype), 

392 other.indptr.astype(idx_dtype), 

393 other.indices.astype(idx_dtype)) 

394 

395 idx_dtype = get_index_dtype((self.indptr, self.indices, 

396 other.indptr, other.indices), 

397 maxval=bnnz) 

398 indptr = np.empty(self.indptr.shape, dtype=idx_dtype) 

399 indices = np.empty(bnnz, dtype=idx_dtype) 

400 data = np.empty(R*C*bnnz, dtype=upcast(self.dtype,other.dtype)) 

401 

402 bsr_matmat(bnnz, M//R, N//C, R, C, n, 

403 self.indptr.astype(idx_dtype), 

404 self.indices.astype(idx_dtype), 

405 np.ravel(self.data), 

406 other.indptr.astype(idx_dtype), 

407 other.indices.astype(idx_dtype), 

408 np.ravel(other.data), 

409 indptr, 

410 indices, 

411 data) 

412 

413 data = data.reshape(-1,R,C) 

414 

415 # TODO eliminate zeros 

416 

417 return self._bsr_container( 

418 (data, indices, indptr), shape=(M, N), blocksize=(R, C) 

419 ) 

420 

421 ###################### 

422 # Conversion methods # 

423 ###################### 

424 

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

426 """Convert this matrix into Block Sparse Row Format. 

427 

428 With copy=False, the data/indices may be shared between this 

429 matrix and the resultant bsr_matrix. 

430 

431 If blocksize=(R, C) is provided, it will be used for determining 

432 block size of the bsr_matrix. 

433 """ 

434 if blocksize not in [None, self.blocksize]: 

435 return self.tocsr().tobsr(blocksize=blocksize) 

436 if copy: 

437 return self.copy() 

438 else: 

439 return self 

440 

441 def tocsr(self, copy=False): 

442 M, N = self.shape 

443 R, C = self.blocksize 

444 nnz = self.nnz 

445 idx_dtype = get_index_dtype((self.indptr, self.indices), 

446 maxval=max(nnz, N)) 

447 indptr = np.empty(M + 1, dtype=idx_dtype) 

448 indices = np.empty(nnz, dtype=idx_dtype) 

449 data = np.empty(nnz, dtype=upcast(self.dtype)) 

450 

451 bsr_tocsr(M // R, # n_brow 

452 N // C, # n_bcol 

453 R, C, 

454 self.indptr.astype(idx_dtype, copy=False), 

455 self.indices.astype(idx_dtype, copy=False), 

456 self.data, 

457 indptr, 

458 indices, 

459 data) 

460 return self._csr_container((data, indices, indptr), shape=self.shape) 

461 

462 tocsr.__doc__ = spmatrix.tocsr.__doc__ 

463 

464 def tocsc(self, copy=False): 

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

466 

467 tocsc.__doc__ = spmatrix.tocsc.__doc__ 

468 

469 def tocoo(self, copy=True): 

470 """Convert this matrix to COOrdinate format. 

471 

472 When copy=False the data array will be shared between 

473 this matrix and the resultant coo_matrix. 

474 """ 

475 

476 M,N = self.shape 

477 R,C = self.blocksize 

478 

479 indptr_diff = np.diff(self.indptr) 

480 if indptr_diff.dtype.itemsize > np.dtype(np.intp).itemsize: 

481 # Check for potential overflow 

482 indptr_diff_limited = indptr_diff.astype(np.intp) 

483 if np.any(indptr_diff_limited != indptr_diff): 

484 raise ValueError("Matrix too big to convert") 

485 indptr_diff = indptr_diff_limited 

486 

487 row = (R * np.arange(M//R)).repeat(indptr_diff) 

488 row = row.repeat(R*C).reshape(-1,R,C) 

489 row += np.tile(np.arange(R).reshape(-1,1), (1,C)) 

490 row = row.reshape(-1) 

491 

492 col = (C * self.indices).repeat(R*C).reshape(-1,R,C) 

493 col += np.tile(np.arange(C), (R,1)) 

494 col = col.reshape(-1) 

495 

496 data = self.data.reshape(-1) 

497 

498 if copy: 

499 data = data.copy() 

500 

501 return self._coo_container( 

502 (data, (row, col)), shape=self.shape 

503 ) 

504 

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

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

507 

508 toarray.__doc__ = spmatrix.toarray.__doc__ 

509 

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

511 if axes is not None: 

512 raise ValueError(("Sparse matrices do not support " 

513 "an 'axes' parameter because swapping " 

514 "dimensions is the only logical permutation.")) 

515 

516 R, C = self.blocksize 

517 M, N = self.shape 

518 NBLK = self.nnz//(R*C) 

519 

520 if self.nnz == 0: 

521 return self._bsr_container((N, M), blocksize=(C, R), 

522 dtype=self.dtype, copy=copy) 

523 

524 indptr = np.empty(N//C + 1, dtype=self.indptr.dtype) 

525 indices = np.empty(NBLK, dtype=self.indices.dtype) 

526 data = np.empty((NBLK, C, R), dtype=self.data.dtype) 

527 

528 bsr_transpose(M//R, N//C, R, C, 

529 self.indptr, self.indices, self.data.ravel(), 

530 indptr, indices, data.ravel()) 

531 

532 return self._bsr_container((data, indices, indptr), 

533 shape=(N, M), copy=copy) 

534 

535 transpose.__doc__ = spmatrix.transpose.__doc__ 

536 

537 ############################################################## 

538 # methods that examine or modify the internal data structure # 

539 ############################################################## 

540 

541 def eliminate_zeros(self): 

542 """Remove zero elements in-place.""" 

543 

544 if not self.nnz: 

545 return # nothing to do 

546 

547 R,C = self.blocksize 

548 M,N = self.shape 

549 

550 mask = (self.data != 0).reshape(-1,R*C).sum(axis=1) # nonzero blocks 

551 

552 nonzero_blocks = mask.nonzero()[0] 

553 

554 self.data[:len(nonzero_blocks)] = self.data[nonzero_blocks] 

555 

556 # modifies self.indptr and self.indices *in place* 

557 _sparsetools.csr_eliminate_zeros(M//R, N//C, self.indptr, 

558 self.indices, mask) 

559 self.prune() 

560 

561 def sum_duplicates(self): 

562 """Eliminate duplicate matrix entries by adding them together 

563 

564 The is an *in place* operation 

565 """ 

566 if self.has_canonical_format: 

567 return 

568 self.sort_indices() 

569 R, C = self.blocksize 

570 M, N = self.shape 

571 

572 # port of _sparsetools.csr_sum_duplicates 

573 n_row = M // R 

574 nnz = 0 

575 row_end = 0 

576 for i in range(n_row): 

577 jj = row_end 

578 row_end = self.indptr[i+1] 

579 while jj < row_end: 

580 j = self.indices[jj] 

581 x = self.data[jj] 

582 jj += 1 

583 while jj < row_end and self.indices[jj] == j: 

584 x += self.data[jj] 

585 jj += 1 

586 self.indices[nnz] = j 

587 self.data[nnz] = x 

588 nnz += 1 

589 self.indptr[i+1] = nnz 

590 

591 self.prune() # nnz may have changed 

592 self.has_canonical_format = True 

593 

594 def sort_indices(self): 

595 """Sort the indices of this matrix *in place* 

596 """ 

597 if self.has_sorted_indices: 

598 return 

599 

600 R,C = self.blocksize 

601 M,N = self.shape 

602 

603 bsr_sort_indices(M//R, N//C, R, C, self.indptr, self.indices, self.data.ravel()) 

604 

605 self.has_sorted_indices = True 

606 

607 def prune(self): 

608 """ Remove empty space after all non-zero elements. 

609 """ 

610 

611 R,C = self.blocksize 

612 M,N = self.shape 

613 

614 if len(self.indptr) != M//R + 1: 

615 raise ValueError("index pointer has invalid length") 

616 

617 bnnz = self.indptr[-1] 

618 

619 if len(self.indices) < bnnz: 

620 raise ValueError("indices array has too few elements") 

621 if len(self.data) < bnnz: 

622 raise ValueError("data array has too few elements") 

623 

624 self.data = self.data[:bnnz] 

625 self.indices = self.indices[:bnnz] 

626 

627 # utility functions 

628 def _binopt(self, other, op, in_shape=None, out_shape=None): 

629 """Apply the binary operation fn to two sparse matrices.""" 

630 

631 # Ideally we'd take the GCDs of the blocksize dimensions 

632 # and explode self and other to match. 

633 other = self.__class__(other, blocksize=self.blocksize) 

634 

635 # e.g. bsr_plus_bsr, etc. 

636 fn = getattr(_sparsetools, self.format + op + self.format) 

637 

638 R,C = self.blocksize 

639 

640 max_bnnz = len(self.data) + len(other.data) 

641 idx_dtype = get_index_dtype((self.indptr, self.indices, 

642 other.indptr, other.indices), 

643 maxval=max_bnnz) 

644 indptr = np.empty(self.indptr.shape, dtype=idx_dtype) 

645 indices = np.empty(max_bnnz, dtype=idx_dtype) 

646 

647 bool_ops = ['_ne_', '_lt_', '_gt_', '_le_', '_ge_'] 

648 if op in bool_ops: 

649 data = np.empty(R*C*max_bnnz, dtype=np.bool_) 

650 else: 

651 data = np.empty(R*C*max_bnnz, dtype=upcast(self.dtype,other.dtype)) 

652 

653 fn(self.shape[0]//R, self.shape[1]//C, R, C, 

654 self.indptr.astype(idx_dtype), 

655 self.indices.astype(idx_dtype), 

656 self.data, 

657 other.indptr.astype(idx_dtype), 

658 other.indices.astype(idx_dtype), 

659 np.ravel(other.data), 

660 indptr, 

661 indices, 

662 data) 

663 

664 actual_bnnz = indptr[-1] 

665 indices = indices[:actual_bnnz] 

666 data = data[:R*C*actual_bnnz] 

667 

668 if actual_bnnz < max_bnnz/2: 

669 indices = indices.copy() 

670 data = data.copy() 

671 

672 data = data.reshape(-1,R,C) 

673 

674 return self.__class__((data, indices, indptr), shape=self.shape) 

675 

676 # needed by _data_matrix 

677 def _with_data(self,data,copy=True): 

678 """Returns a matrix with the same sparsity structure as self, 

679 but with different data. By default the structure arrays 

680 (i.e. .indptr and .indices) are copied. 

681 """ 

682 if copy: 

683 return self.__class__((data,self.indices.copy(),self.indptr.copy()), 

684 shape=self.shape,dtype=data.dtype) 

685 else: 

686 return self.__class__((data,self.indices,self.indptr), 

687 shape=self.shape,dtype=data.dtype) 

688 

689# # these functions are used by the parent class 

690# # to remove redudancy between bsc_matrix and bsr_matrix 

691# def _swap(self,x): 

692# """swap the members of x if this is a column-oriented matrix 

693# """ 

694# return (x[0],x[1]) 

695 

696 

697def isspmatrix_bsr(x): 

698 """Is x of a bsr_matrix type? 

699 

700 Parameters 

701 ---------- 

702 x 

703 object to check for being a bsr matrix 

704 

705 Returns 

706 ------- 

707 bool 

708 True if x is a bsr matrix, False otherwise 

709 

710 Examples 

711 -------- 

712 >>> from scipy.sparse import bsr_matrix, isspmatrix_bsr 

713 >>> isspmatrix_bsr(bsr_matrix([[5]])) 

714 True 

715 

716 >>> from scipy.sparse import bsr_matrix, csr_matrix, isspmatrix_bsr 

717 >>> isspmatrix_bsr(csr_matrix([[5]])) 

718 False 

719 """ 

720 from ._arrays import bsr_array 

721 return isinstance(x, bsr_matrix) or isinstance(x, bsr_array)