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

320 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-03-22 06:44 +0000

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

2 

3__docformat__ = "restructuredtext en" 

4 

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

6 

7from warnings import warn 

8 

9import numpy as np 

10 

11from scipy._lib._util import copy_if_needed 

12from ._matrix import spmatrix 

13from ._data import _data_matrix, _minmax_mixin 

14from ._compressed import _cs_matrix 

15from ._base import issparse, _formats, _spbase, sparray 

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

17 check_shape) 

18from . import _sparsetools 

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

20 bsr_matmat, bsr_transpose, bsr_sort_indices, 

21 bsr_tocsr) 

22 

23 

24class _bsr_base(_cs_matrix, _minmax_mixin): 

25 _format = 'bsr' 

26 

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

28 _data_matrix.__init__(self) 

29 

30 if issparse(arg1): 

31 if arg1.format == self.format and copy: 

32 arg1 = arg1.copy() 

33 else: 

34 arg1 = arg1.tobsr(blocksize=blocksize) 

35 self.indptr, self.indices, self.data, self._shape = ( 

36 arg1.indptr, arg1.indices, arg1.data, arg1._shape 

37 ) 

38 

39 elif isinstance(arg1,tuple): 

40 if isshape(arg1): 

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

42 self._shape = check_shape(arg1) 

43 M,N = self.shape 

44 # process blocksize 

45 if blocksize is None: 

46 blocksize = (1,1) 

47 else: 

48 if not isshape(blocksize): 

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

50 blocksize = tuple(blocksize) 

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

52 

53 R,C = blocksize 

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

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

56 

57 # Select index dtype large enough to pass array and 

58 # scalar parameters to sparsetools 

59 idx_dtype = self._get_index_dtype(maxval=max(M//R, N//C, R, C)) 

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

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

62 

63 elif len(arg1) == 2: 

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

65 coo = self._coo_container(arg1, dtype=dtype, shape=shape) 

66 bsr = coo.tobsr(blocksize=blocksize) 

67 self.indptr, self.indices, self.data, self._shape = ( 

68 bsr.indptr, bsr.indices, bsr.data, bsr._shape 

69 ) 

70 

71 elif len(arg1) == 3: 

72 # (data,indices,indptr) format 

73 (data, indices, indptr) = arg1 

74 

75 # Select index dtype large enough to pass array and 

76 # scalar parameters to sparsetools 

77 maxval = 1 

78 if shape is not None: 

79 maxval = max(shape) 

80 if blocksize is not None: 

81 maxval = max(maxval, max(blocksize)) 

82 idx_dtype = self._get_index_dtype((indices, indptr), maxval=maxval, 

83 check_contents=True) 

84 if not copy: 

85 copy = copy_if_needed 

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

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

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

89 if self.data.ndim != 3: 

90 raise ValueError( 

91 f'BSR data must be 3-dimensional, got shape={self.data.shape}' 

92 ) 

93 if blocksize is not None: 

94 if not isshape(blocksize): 

95 raise ValueError(f'invalid blocksize={blocksize}') 

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

97 raise ValueError('mismatching blocksize={} vs {}'.format( 

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

99 else: 

100 raise ValueError('unrecognized bsr_array constructor usage') 

101 else: 

102 # must be dense 

103 try: 

104 arg1 = np.asarray(arg1) 

105 except Exception as e: 

106 raise ValueError("unrecognized form for" 

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

108 arg1 = self._coo_container( 

109 arg1, dtype=dtype 

110 ).tobsr(blocksize=blocksize) 

111 self.indptr, self.indices, self.data, self._shape = ( 

112 arg1.indptr, arg1.indices, arg1.data, arg1._shape 

113 ) 

114 

115 if shape is not None: 

116 self._shape = check_shape(shape) 

117 else: 

118 if self.shape is None: 

119 # shape not already set, try to infer dimensions 

120 try: 

121 M = len(self.indptr) - 1 

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

123 except Exception as e: 

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

125 else: 

126 R,C = self.blocksize 

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

128 

129 if self.shape is None: 

130 if shape is None: 

131 # TODO infer shape here 

132 raise ValueError('need to infer shape') 

133 else: 

134 self._shape = check_shape(shape) 

135 

136 if dtype is not None: 

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

138 

139 self.check_format(full_check=False) 

140 

141 def check_format(self, full_check=True): 

142 """Check whether the array/matrix respects the BSR format. 

143 

144 Parameters 

145 ---------- 

146 full_check : bool, optional 

147 If `True`, run rigorous check, scanning arrays for valid values. 

148 Note that activating those check might copy arrays for casting, 

149 modifying indices and index pointers' inplace. 

150 If `False`, run basic checks on attributes. O(1) operations. 

151 Default is `True`. 

152 """ 

153 M,N = self.shape 

154 R,C = self.blocksize 

155 

156 # index arrays should have integer data types 

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

158 warn(f"indptr array has non-integer dtype ({self.indptr.dtype.name})", 

159 stacklevel=2) 

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

161 warn(f"indices array has non-integer dtype ({self.indices.dtype.name})", 

162 stacklevel=2) 

163 

164 # check array shapes 

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

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

167 if self.data.ndim != 3: 

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

169 

170 # check index pointer 

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

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

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

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

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

176 

177 # check index and data arrays 

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

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

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

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

182 "the size of index and data arrays") 

183 

184 self.prune() 

185 

186 if full_check: 

187 # check format validity (more expensive) 

188 if self.nnz > 0: 

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

190 raise ValueError("column index values must be < %d (now max %d)" 

191 % (N//C, self.indices.max())) 

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

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

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

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

196 "non-decreasing sequence") 

197 

198 idx_dtype = self._get_index_dtype((self.indices, self.indptr)) 

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

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

201 self.data = to_native(self.data) 

202 # if not self.has_sorted_indices(): 

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

204 # self.sort_indices(check_first=False) 

205 

206 @property 

207 def blocksize(self) -> tuple: 

208 """Block size of the matrix.""" 

209 return self.data.shape[1:] 

210 

211 def _getnnz(self, axis=None): 

212 if axis is not None: 

213 raise NotImplementedError("_getnnz over an axis is not implemented " 

214 "for BSR format") 

215 R,C = self.blocksize 

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

217 

218 _getnnz.__doc__ = _spbase._getnnz.__doc__ 

219 

220 def __repr__(self): 

221 _, fmt = _formats[self.format] 

222 sparse_cls = 'array' if isinstance(self, sparray) else 'matrix' 

223 shape_str = 'x'.join(str(x) for x in self.shape) 

224 blksz = 'x'.join(str(x) for x in self.blocksize) 

225 return ( 

226 f"<{shape_str} sparse {sparse_cls} of type '{self.dtype.type}'\n" 

227 f"\twith {self.nnz} stored elements (blocksize = {blksz}) in {fmt} format>" 

228 ) 

229 

230 def diagonal(self, k=0): 

231 rows, cols = self.shape 

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

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

234 R, C = self.blocksize 

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

236 dtype=upcast(self.dtype)) 

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

238 self.indptr, self.indices, 

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

240 return y 

241 

242 diagonal.__doc__ = _spbase.diagonal.__doc__ 

243 

244 ########################## 

245 # NotImplemented methods # 

246 ########################## 

247 

248 def __getitem__(self,key): 

249 raise NotImplementedError 

250 

251 def __setitem__(self,key,val): 

252 raise NotImplementedError 

253 

254 ###################### 

255 # Arithmetic methods # 

256 ###################### 

257 

258 def _add_dense(self, other): 

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

260 

261 def _matmul_vector(self, other): 

262 M,N = self.shape 

263 R,C = self.blocksize 

264 

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

266 

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

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

269 other, result) 

270 

271 return result 

272 

273 def _matmul_multivector(self,other): 

274 R,C = self.blocksize 

275 M,N = self.shape 

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

277 

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

279 

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

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

282 other.ravel(), result.ravel()) 

283 

284 return result 

285 

286 def _matmul_sparse(self, other): 

287 M, K1 = self.shape 

288 K2, N = other.shape 

289 

290 R,n = self.blocksize 

291 

292 # convert to this format 

293 if other.format == "bsr": 

294 C = other.blocksize[1] 

295 else: 

296 C = 1 

297 

298 if other.format == "csr" and n == 1: 

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

300 else: 

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

302 

303 idx_dtype = self._get_index_dtype((self.indptr, self.indices, 

304 other.indptr, other.indices)) 

305 

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

307 self.indptr.astype(idx_dtype), 

308 self.indices.astype(idx_dtype), 

309 other.indptr.astype(idx_dtype), 

310 other.indices.astype(idx_dtype)) 

311 

312 idx_dtype = self._get_index_dtype((self.indptr, self.indices, 

313 other.indptr, other.indices), 

314 maxval=bnnz) 

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

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

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

318 

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

320 self.indptr.astype(idx_dtype), 

321 self.indices.astype(idx_dtype), 

322 np.ravel(self.data), 

323 other.indptr.astype(idx_dtype), 

324 other.indices.astype(idx_dtype), 

325 np.ravel(other.data), 

326 indptr, 

327 indices, 

328 data) 

329 

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

331 

332 # TODO eliminate zeros 

333 

334 return self._bsr_container( 

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

336 ) 

337 

338 ###################### 

339 # Conversion methods # 

340 ###################### 

341 

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

343 """Convert this array/matrix into Block Sparse Row Format. 

344 

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

346 array/matrix and the resultant bsr_array/bsr_matrix. 

347 

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

349 block size of the bsr_array/bsr_matrix. 

350 """ 

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

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

353 if copy: 

354 return self.copy() 

355 else: 

356 return self 

357 

358 def tocsr(self, copy=False): 

359 M, N = self.shape 

360 R, C = self.blocksize 

361 nnz = self.nnz 

362 idx_dtype = self._get_index_dtype((self.indptr, self.indices), 

363 maxval=max(nnz, N)) 

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

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

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

367 

368 bsr_tocsr(M // R, # n_brow 

369 N // C, # n_bcol 

370 R, C, 

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

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

373 self.data, 

374 indptr, 

375 indices, 

376 data) 

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

378 

379 tocsr.__doc__ = _spbase.tocsr.__doc__ 

380 

381 def tocsc(self, copy=False): 

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

383 

384 tocsc.__doc__ = _spbase.tocsc.__doc__ 

385 

386 def tocoo(self, copy=True): 

387 """Convert this array/matrix to COOrdinate format. 

388 

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

390 this array/matrix and the resultant coo_array/coo_matrix. 

391 """ 

392 

393 M,N = self.shape 

394 R,C = self.blocksize 

395 

396 indptr_diff = np.diff(self.indptr) 

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

398 # Check for potential overflow 

399 indptr_diff_limited = indptr_diff.astype(np.intp) 

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

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

402 indptr_diff = indptr_diff_limited 

403 

404 idx_dtype = self._get_index_dtype(maxval=max(M, N)) 

405 row = (R * np.arange(M//R, dtype=idx_dtype)).repeat(indptr_diff) 

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

407 row += np.tile(np.arange(R, dtype=idx_dtype).reshape(-1,1), (1,C)) 

408 row = row.reshape(-1) 

409 

410 col = ((C * self.indices).astype(idx_dtype, copy=False) 

411 .repeat(R*C).reshape(-1,R,C)) 

412 col += np.tile(np.arange(C, dtype=idx_dtype), (R,1)) 

413 col = col.reshape(-1) 

414 

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

416 

417 if copy: 

418 data = data.copy() 

419 

420 return self._coo_container( 

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

422 ) 

423 

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

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

426 

427 toarray.__doc__ = _spbase.toarray.__doc__ 

428 

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

430 if axes is not None and axes != (1, 0): 

431 raise ValueError("Sparse matrices do not support " 

432 "an 'axes' parameter because swapping " 

433 "dimensions is the only logical permutation.") 

434 

435 R, C = self.blocksize 

436 M, N = self.shape 

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

438 

439 if self.nnz == 0: 

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

441 dtype=self.dtype, copy=copy) 

442 

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

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

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

446 

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

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

449 indptr, indices, data.ravel()) 

450 

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

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

453 

454 transpose.__doc__ = _spbase.transpose.__doc__ 

455 

456 ############################################################## 

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

458 ############################################################## 

459 

460 def eliminate_zeros(self): 

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

462 

463 if not self.nnz: 

464 return # nothing to do 

465 

466 R,C = self.blocksize 

467 M,N = self.shape 

468 

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

470 

471 nonzero_blocks = mask.nonzero()[0] 

472 

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

474 

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

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

477 self.indices, mask) 

478 self.prune() 

479 

480 def sum_duplicates(self): 

481 """Eliminate duplicate array/matrix entries by adding them together 

482 

483 The is an *in place* operation 

484 """ 

485 if self.has_canonical_format: 

486 return 

487 self.sort_indices() 

488 R, C = self.blocksize 

489 M, N = self.shape 

490 

491 # port of _sparsetools.csr_sum_duplicates 

492 n_row = M // R 

493 nnz = 0 

494 row_end = 0 

495 for i in range(n_row): 

496 jj = row_end 

497 row_end = self.indptr[i+1] 

498 while jj < row_end: 

499 j = self.indices[jj] 

500 x = self.data[jj] 

501 jj += 1 

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

503 x += self.data[jj] 

504 jj += 1 

505 self.indices[nnz] = j 

506 self.data[nnz] = x 

507 nnz += 1 

508 self.indptr[i+1] = nnz 

509 

510 self.prune() # nnz may have changed 

511 self.has_canonical_format = True 

512 

513 def sort_indices(self): 

514 """Sort the indices of this array/matrix *in place* 

515 """ 

516 if self.has_sorted_indices: 

517 return 

518 

519 R,C = self.blocksize 

520 M,N = self.shape 

521 

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

523 

524 self.has_sorted_indices = True 

525 

526 def prune(self): 

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

528 """ 

529 

530 R,C = self.blocksize 

531 M,N = self.shape 

532 

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

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

535 

536 bnnz = self.indptr[-1] 

537 

538 if len(self.indices) < bnnz: 

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

540 if len(self.data) < bnnz: 

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

542 

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

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

545 

546 # utility functions 

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

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

549 

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

551 # and explode self and other to match. 

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

553 

554 # e.g. bsr_plus_bsr, etc. 

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

556 

557 R,C = self.blocksize 

558 

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

560 idx_dtype = self._get_index_dtype((self.indptr, self.indices, 

561 other.indptr, other.indices), 

562 maxval=max_bnnz) 

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

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

565 

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

567 if op in bool_ops: 

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

569 else: 

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

571 

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

573 self.indptr.astype(idx_dtype), 

574 self.indices.astype(idx_dtype), 

575 self.data, 

576 other.indptr.astype(idx_dtype), 

577 other.indices.astype(idx_dtype), 

578 np.ravel(other.data), 

579 indptr, 

580 indices, 

581 data) 

582 

583 actual_bnnz = indptr[-1] 

584 indices = indices[:actual_bnnz] 

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

586 

587 if actual_bnnz < max_bnnz/2: 

588 indices = indices.copy() 

589 data = data.copy() 

590 

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

592 

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

594 

595 # needed by _data_matrix 

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

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

598 but with different data. By default the structure arrays 

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

600 """ 

601 if copy: 

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

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

604 else: 

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

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

607 

608# # these functions are used by the parent class 

609# # to remove redundancy between bsc_matrix and bsr_matrix 

610# def _swap(self,x): 

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

612# """ 

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

614 

615 

616def isspmatrix_bsr(x): 

617 """Is `x` of a bsr_matrix type? 

618 

619 Parameters 

620 ---------- 

621 x 

622 object to check for being a bsr matrix 

623 

624 Returns 

625 ------- 

626 bool 

627 True if `x` is a bsr matrix, False otherwise 

628 

629 Examples 

630 -------- 

631 >>> from scipy.sparse import bsr_array, bsr_matrix, csr_matrix, isspmatrix_bsr 

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

633 True 

634 >>> isspmatrix_bsr(bsr_array([[5]])) 

635 False 

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

637 False 

638 """ 

639 return isinstance(x, bsr_matrix) 

640 

641 

642# This namespace class separates array from matrix with isinstance 

643class bsr_array(_bsr_base, sparray): 

644 """ 

645 Block Sparse Row format sparse array. 

646 

647 This can be instantiated in several ways: 

648 bsr_array(D, [blocksize=(R,C)]) 

649 where D is a 2-D ndarray. 

650 

651 bsr_array(S, [blocksize=(R,C)]) 

652 with another sparse array or matrix S (equivalent to S.tobsr()) 

653 

654 bsr_array((M, N), [blocksize=(R,C), dtype]) 

655 to construct an empty sparse array with shape (M, N) 

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

657 

658 bsr_array((data, ij), [blocksize=(R,C), shape=(M, N)]) 

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

660 

661 bsr_array((data, indices, indptr), [shape=(M, N)]) 

662 is the standard BSR representation where the block column 

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

664 and their corresponding block values are stored in 

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

666 supplied, the array dimensions are inferred from the index arrays. 

667 

668 Attributes 

669 ---------- 

670 dtype : dtype 

671 Data type of the array 

672 shape : 2-tuple 

673 Shape of the array 

674 ndim : int 

675 Number of dimensions (this is always 2) 

676 nnz 

677 size 

678 data 

679 BSR format data array of the array 

680 indices 

681 BSR format index array of the array 

682 indptr 

683 BSR format index pointer array of the array 

684 blocksize 

685 Block size 

686 has_sorted_indices : bool 

687 Whether indices are sorted 

688 has_canonical_format : bool 

689 T 

690 

691 Notes 

692 ----- 

693 Sparse arrays can be used in arithmetic operations: they support 

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

695 

696 **Summary of BSR format** 

697 

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

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

700 sub matrices like the last example below. Such sparse block matrices often 

701 arise in vector-valued finite element discretizations. In such cases, BSR is 

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

703 operations. 

704 

705 **Blocksize** 

706 

707 The blocksize (R,C) must evenly divide the shape of the sparse array (M,N). 

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

709 ``N % C = 0``. 

710 

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

712 an appropriate blocksize. 

713 

714 **Canonical Format** 

715 

716 In canonical format, there are no duplicate blocks and indices are sorted 

717 per row. 

718 

719 Examples 

720 -------- 

721 >>> import numpy as np 

722 >>> from scipy.sparse import bsr_array 

723 >>> bsr_array((3, 4), dtype=np.int8).toarray() 

724 array([[0, 0, 0, 0], 

725 [0, 0, 0, 0], 

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

727 

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

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

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

731 >>> bsr_array((data, (row, col)), shape=(3, 3)).toarray() 

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

733 [0, 0, 3], 

734 [4, 5, 6]]) 

735 

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

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

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

739 >>> bsr_array((data,indices,indptr), shape=(6, 6)).toarray() 

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

741 [1, 1, 0, 0, 2, 2], 

742 [0, 0, 0, 0, 3, 3], 

743 [0, 0, 0, 0, 3, 3], 

744 [4, 4, 5, 5, 6, 6], 

745 [4, 4, 5, 5, 6, 6]]) 

746 

747 """ 

748 

749 

750class bsr_matrix(spmatrix, _bsr_base): 

751 """ 

752 Block Sparse Row format sparse matrix. 

753 

754 This can be instantiated in several ways: 

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

756 where D is a 2-D ndarray. 

757 

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

759 with another sparse array or matrix S (equivalent to S.tobsr()) 

760 

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

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

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

764 

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

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

767 

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

769 is the standard BSR representation where the block column 

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

771 and their corresponding block values are stored in 

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

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

774 

775 Attributes 

776 ---------- 

777 dtype : dtype 

778 Data type of the matrix 

779 shape : 2-tuple 

780 Shape of the matrix 

781 ndim : int 

782 Number of dimensions (this is always 2) 

783 nnz 

784 size 

785 data 

786 BSR format data array of the matrix 

787 indices 

788 BSR format index array of the matrix 

789 indptr 

790 BSR format index pointer array of the matrix 

791 blocksize 

792 Block size 

793 has_sorted_indices : bool 

794 Whether indices are sorted 

795 has_canonical_format : bool 

796 T 

797 

798 Notes 

799 ----- 

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

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

802 

803 **Summary of BSR format** 

804 

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

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

807 sub matrices like the last example below. Such sparse block matrices often 

808 arise in vector-valued finite element discretizations. In such cases, BSR is 

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

810 operations. 

811 

812 **Blocksize** 

813 

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

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

816 ``N % C = 0``. 

817 

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

819 an appropriate blocksize. 

820 

821 **Canonical Format** 

822 

823 In canonical format, there are no duplicate blocks and indices are sorted 

824 per row. 

825 

826 Examples 

827 -------- 

828 >>> import numpy as np 

829 >>> from scipy.sparse import bsr_matrix 

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

831 array([[0, 0, 0, 0], 

832 [0, 0, 0, 0], 

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

834 

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

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

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

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

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

840 [0, 0, 3], 

841 [4, 5, 6]]) 

842 

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

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

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

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

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

848 [1, 1, 0, 0, 2, 2], 

849 [0, 0, 0, 0, 3, 3], 

850 [0, 0, 0, 0, 3, 3], 

851 [4, 4, 5, 5, 6, 6], 

852 [4, 4, 5, 5, 6, 6]]) 

853 

854 """ 

855