_bsr.py 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882
  1. """Compressed Block Sparse Row format"""
  2. __docformat__ = "restructuredtext en"
  3. __all__ = ['bsr_array', 'bsr_matrix', 'isspmatrix_bsr']
  4. from warnings import warn
  5. import numpy as np
  6. from scipy._lib._util import copy_if_needed
  7. from ._matrix import spmatrix
  8. from ._data import _data_matrix, _minmax_mixin
  9. from ._compressed import _cs_matrix
  10. from ._base import issparse, _formats, _spbase, sparray
  11. from ._sputils import (isshape, getdtype, getdata, to_native, upcast,
  12. check_shape)
  13. from . import _sparsetools
  14. from ._sparsetools import (bsr_matvec, bsr_matvecs, csr_matmat_maxnnz,
  15. bsr_matmat, bsr_transpose, bsr_sort_indices,
  16. bsr_tocsr)
  17. class _bsr_base(_cs_matrix, _minmax_mixin):
  18. _format = 'bsr'
  19. def __init__(self, arg1, shape=None, dtype=None, copy=False,
  20. blocksize=None, *, maxprint=None):
  21. _data_matrix.__init__(self, arg1, maxprint=maxprint)
  22. if issparse(arg1):
  23. if arg1.format == self.format and copy:
  24. arg1 = arg1.copy()
  25. else:
  26. arg1 = arg1.tobsr(blocksize=blocksize)
  27. self.indptr, self.indices, self.data, self._shape = (
  28. arg1.indptr, arg1.indices, arg1.data, arg1._shape
  29. )
  30. elif isinstance(arg1,tuple):
  31. if isshape(arg1):
  32. # it's a tuple of matrix dimensions (M,N)
  33. self._shape = check_shape(arg1)
  34. M,N = self.shape
  35. # process blocksize
  36. if blocksize is None:
  37. blocksize = (1,1)
  38. else:
  39. if not isshape(blocksize):
  40. raise ValueError(f'invalid blocksize={blocksize}')
  41. blocksize = tuple(blocksize)
  42. self.data = np.zeros((0,) + blocksize, getdtype(dtype, default=float))
  43. R,C = blocksize
  44. if (M % R) != 0 or (N % C) != 0:
  45. raise ValueError('shape must be multiple of blocksize')
  46. # Select index dtype large enough to pass array and
  47. # scalar parameters to sparsetools
  48. idx_dtype = self._get_index_dtype(maxval=max(M//R, N//C, R, C))
  49. self.indices = np.zeros(0, dtype=idx_dtype)
  50. self.indptr = np.zeros(M//R + 1, dtype=idx_dtype)
  51. elif len(arg1) == 2:
  52. # (data,(row,col)) format
  53. coo = self._coo_container(arg1, dtype=dtype, shape=shape)
  54. bsr = coo.tobsr(blocksize=blocksize)
  55. self.indptr, self.indices, self.data, self._shape = (
  56. bsr.indptr, bsr.indices, bsr.data, bsr._shape
  57. )
  58. elif len(arg1) == 3:
  59. # (data,indices,indptr) format
  60. (data, indices, indptr) = arg1
  61. # Select index dtype large enough to pass array and
  62. # scalar parameters to sparsetools
  63. maxval = 1
  64. if shape is not None:
  65. maxval = max(shape)
  66. if blocksize is not None:
  67. maxval = max(maxval, max(blocksize))
  68. idx_dtype = self._get_index_dtype((indices, indptr), maxval=maxval,
  69. check_contents=True)
  70. if not copy:
  71. copy = copy_if_needed
  72. self.indices = np.array(indices, copy=copy, dtype=idx_dtype)
  73. self.indptr = np.array(indptr, copy=copy, dtype=idx_dtype)
  74. self.data = getdata(data, copy=copy, dtype=dtype)
  75. if self.data.ndim != 3:
  76. raise ValueError(
  77. f'BSR data must be 3-dimensional, got shape={self.data.shape}'
  78. )
  79. if blocksize is not None:
  80. if not isshape(blocksize):
  81. raise ValueError(f'invalid blocksize={blocksize}')
  82. if tuple(blocksize) != self.data.shape[1:]:
  83. raise ValueError(
  84. f'mismatching blocksize={blocksize}'
  85. f' vs {self.data.shape[1:]}'
  86. )
  87. else:
  88. raise ValueError('unrecognized bsr_array constructor usage')
  89. else:
  90. # must be dense
  91. try:
  92. arg1 = np.asarray(arg1)
  93. except Exception as e:
  94. raise ValueError("unrecognized form for "
  95. f"{self.format}_matrix constructor") from e
  96. if isinstance(self, sparray) and arg1.ndim != 2:
  97. raise ValueError(f"BSR arrays don't support {arg1.ndim}D input. Use 2D")
  98. arg1 = self._coo_container(arg1, dtype=dtype).tobsr(blocksize=blocksize)
  99. self.indptr, self.indices, self.data, self._shape = (
  100. arg1.indptr, arg1.indices, arg1.data, arg1._shape
  101. )
  102. if shape is not None:
  103. self._shape = check_shape(shape)
  104. else:
  105. if self.shape is None:
  106. # shape not already set, try to infer dimensions
  107. try:
  108. M = len(self.indptr) - 1
  109. N = self.indices.max() + 1
  110. except Exception as e:
  111. raise ValueError('unable to infer matrix dimensions') from e
  112. else:
  113. R,C = self.blocksize
  114. self._shape = check_shape((M*R,N*C))
  115. if self.shape is None:
  116. if shape is None:
  117. # TODO infer shape here
  118. raise ValueError('need to infer shape')
  119. else:
  120. self._shape = check_shape(shape)
  121. if dtype is not None:
  122. self.data = self.data.astype(getdtype(dtype, self.data), copy=False)
  123. self.check_format(full_check=False)
  124. def check_format(self, full_check=True):
  125. """Check whether the array/matrix respects the BSR format.
  126. Parameters
  127. ----------
  128. full_check : bool, optional
  129. If `True`, run rigorous check, scanning arrays for valid values.
  130. Note that activating those check might copy arrays for casting,
  131. modifying indices and index pointers' inplace.
  132. If `False`, run basic checks on attributes. O(1) operations.
  133. Default is `True`.
  134. """
  135. M,N = self.shape
  136. R,C = self.blocksize
  137. # index arrays should have integer data types
  138. if self.indptr.dtype.kind != 'i':
  139. warn(f"indptr array has non-integer dtype ({self.indptr.dtype.name})",
  140. stacklevel=2)
  141. if self.indices.dtype.kind != 'i':
  142. warn(f"indices array has non-integer dtype ({self.indices.dtype.name})",
  143. stacklevel=2)
  144. # check array shapes
  145. if self.indices.ndim != 1 or self.indptr.ndim != 1:
  146. raise ValueError("indices, and indptr should be 1-D")
  147. if self.data.ndim != 3:
  148. raise ValueError("data should be 3-D")
  149. # check index pointer
  150. if (len(self.indptr) != M//R + 1):
  151. raise ValueError(
  152. f"index pointer size ({len(self.indptr)}) should be ({M//R + 1})"
  153. )
  154. if (self.indptr[0] != 0):
  155. raise ValueError("index pointer should start with 0")
  156. # check index and data arrays
  157. if (len(self.indices) != len(self.data)):
  158. raise ValueError("indices and data should have the same size")
  159. if (self.indptr[-1] > len(self.indices)):
  160. raise ValueError("Last value of index pointer should be less than "
  161. "the size of index and data arrays")
  162. self.prune()
  163. if full_check:
  164. # check format validity (more expensive)
  165. if self.nnz > 0:
  166. if self.indices.max() >= N//C:
  167. raise ValueError(
  168. f"column index values must be < {N//C}"
  169. f" (now max {self.indices.max()})"
  170. )
  171. if self.indices.min() < 0:
  172. raise ValueError("column index values must be >= 0")
  173. if np.diff(self.indptr).min() < 0:
  174. raise ValueError("index pointer values must form a "
  175. "non-decreasing sequence")
  176. idx_dtype = self._get_index_dtype((self.indices, self.indptr))
  177. self.indptr = np.asarray(self.indptr, dtype=idx_dtype)
  178. self.indices = np.asarray(self.indices, dtype=idx_dtype)
  179. self.data = to_native(self.data)
  180. # if not self.has_sorted_indices():
  181. # warn('Indices were not in sorted order. Sorting indices.')
  182. # self.sort_indices(check_first=False)
  183. @property
  184. def blocksize(self) -> tuple:
  185. """Block size of the matrix."""
  186. return self.data.shape[1:]
  187. def _getnnz(self, axis=None):
  188. if axis is not None:
  189. raise NotImplementedError("_getnnz over an axis is not implemented "
  190. "for BSR format")
  191. R, C = self.blocksize
  192. return int(self.indptr[-1]) * R * C
  193. _getnnz.__doc__ = _spbase._getnnz.__doc__
  194. def count_nonzero(self, axis=None):
  195. if axis is not None:
  196. raise NotImplementedError(
  197. "count_nonzero over axis is not implemented for BSR format."
  198. )
  199. return np.count_nonzero(self._deduped_data())
  200. count_nonzero.__doc__ = _spbase.count_nonzero.__doc__
  201. def __repr__(self):
  202. _, fmt = _formats[self.format]
  203. sparse_cls = 'array' if isinstance(self, sparray) else 'matrix'
  204. b = 'x'.join(str(x) for x in self.blocksize)
  205. return (
  206. f"<{fmt} sparse {sparse_cls} of dtype '{self.dtype}'\n"
  207. f"\twith {self.nnz} stored elements (blocksize={b}) and shape {self.shape}>"
  208. )
  209. def diagonal(self, k=0):
  210. rows, cols = self.shape
  211. if k <= -rows or k >= cols:
  212. return np.empty(0, dtype=self.data.dtype)
  213. R, C = self.blocksize
  214. y = np.zeros(min(rows + min(k, 0), cols - max(k, 0)),
  215. dtype=upcast(self.dtype))
  216. _sparsetools.bsr_diagonal(k, rows // R, cols // C, R, C,
  217. self.indptr, self.indices,
  218. np.ravel(self.data), y)
  219. return y
  220. diagonal.__doc__ = _spbase.diagonal.__doc__
  221. ##########################
  222. # NotImplemented methods #
  223. ##########################
  224. def __getitem__(self,key):
  225. raise NotImplementedError
  226. def __setitem__(self,key,val):
  227. raise NotImplementedError
  228. ######################
  229. # Arithmetic methods #
  230. ######################
  231. def _add_dense(self, other):
  232. return self.tocoo(copy=False)._add_dense(other)
  233. def _matmul_vector(self, other):
  234. M,N = self.shape
  235. R,C = self.blocksize
  236. result = np.zeros(self.shape[0], dtype=upcast(self.dtype, other.dtype))
  237. bsr_matvec(M//R, N//C, R, C,
  238. self.indptr, self.indices, self.data.ravel(),
  239. other, result)
  240. return result
  241. def _matmul_multivector(self,other):
  242. R,C = self.blocksize
  243. M,N = self.shape
  244. n_vecs = other.shape[1] # number of column vectors
  245. result = np.zeros((M,n_vecs), dtype=upcast(self.dtype,other.dtype))
  246. bsr_matvecs(M//R, N//C, n_vecs, R, C,
  247. self.indptr, self.indices, self.data.ravel(),
  248. other.ravel(), result.ravel())
  249. return result
  250. def _matmul_sparse(self, other):
  251. M, K1 = self.shape
  252. K2, N = other.shape
  253. R,n = self.blocksize
  254. # convert to this format
  255. if other.format == "bsr":
  256. C = other.blocksize[1]
  257. else:
  258. C = 1
  259. if other.format == "csr" and n == 1:
  260. other = other.tobsr(blocksize=(n,C), copy=False) # lightweight conversion
  261. else:
  262. other = other.tobsr(blocksize=(n,C))
  263. idx_dtype = self._get_index_dtype((self.indptr, self.indices,
  264. other.indptr, other.indices))
  265. n_brow = M // R
  266. n_bcol = N // C
  267. bnnz = csr_matmat_maxnnz(n_brow, n_bcol,
  268. self.indptr.astype(idx_dtype, copy=False),
  269. self.indices.astype(idx_dtype, copy=False),
  270. other.indptr.astype(idx_dtype, copy=False),
  271. other.indices.astype(idx_dtype, copy=False))
  272. idx_dtype = self._get_index_dtype((self.indptr, self.indices,
  273. other.indptr, other.indices),
  274. maxval=bnnz)
  275. indptr = np.empty(self.indptr.shape, dtype=idx_dtype)
  276. indices = np.empty(bnnz, dtype=idx_dtype)
  277. data = np.empty(R*C*bnnz, dtype=upcast(self.dtype,other.dtype))
  278. bsr_matmat(bnnz, n_brow, n_bcol, R, C, n,
  279. self.indptr.astype(idx_dtype, copy=False),
  280. self.indices.astype(idx_dtype, copy=False),
  281. np.ravel(self.data),
  282. other.indptr.astype(idx_dtype, copy=False),
  283. other.indices.astype(idx_dtype, copy=False),
  284. np.ravel(other.data),
  285. indptr,
  286. indices,
  287. data)
  288. data = data.reshape(-1,R,C)
  289. # TODO eliminate zeros
  290. return self._bsr_container(
  291. (data, indices, indptr), shape=(M, N), blocksize=(R, C)
  292. )
  293. ######################
  294. # Conversion methods #
  295. ######################
  296. def tobsr(self, blocksize=None, copy=False):
  297. """Convert this array/matrix into Block Sparse Row Format.
  298. With copy=False, the data/indices may be shared between this
  299. array/matrix and the resultant bsr_array/bsr_matrix.
  300. If blocksize=(R, C) is provided, it will be used for determining
  301. block size of the bsr_array/bsr_matrix.
  302. """
  303. if blocksize not in [None, self.blocksize]:
  304. return self.tocsr().tobsr(blocksize=blocksize)
  305. if copy:
  306. return self.copy()
  307. else:
  308. return self
  309. def tocsr(self, copy=False):
  310. M, N = self.shape
  311. R, C = self.blocksize
  312. nnz = self.nnz
  313. idx_dtype = self._get_index_dtype((self.indptr, self.indices),
  314. maxval=max(nnz, N))
  315. indptr = np.empty(M + 1, dtype=idx_dtype)
  316. indices = np.empty(nnz, dtype=idx_dtype)
  317. data = np.empty(nnz, dtype=upcast(self.dtype))
  318. bsr_tocsr(M // R, # n_brow
  319. N // C, # n_bcol
  320. R, C,
  321. self.indptr.astype(idx_dtype, copy=False),
  322. self.indices.astype(idx_dtype, copy=False),
  323. self.data,
  324. indptr,
  325. indices,
  326. data)
  327. return self._csr_container((data, indices, indptr), shape=self.shape)
  328. tocsr.__doc__ = _spbase.tocsr.__doc__
  329. def tocsc(self, copy=False):
  330. return self.tocsr(copy=False).tocsc(copy=copy)
  331. tocsc.__doc__ = _spbase.tocsc.__doc__
  332. def tocoo(self, copy=True):
  333. """Convert this array/matrix to COOrdinate format.
  334. When copy=False the data array will be shared between
  335. this array/matrix and the resultant coo_array/coo_matrix.
  336. """
  337. M,N = self.shape
  338. R,C = self.blocksize
  339. indptr_diff = np.diff(self.indptr)
  340. if indptr_diff.dtype.itemsize > np.dtype(np.intp).itemsize:
  341. # Check for potential overflow
  342. indptr_diff_limited = indptr_diff.astype(np.intp)
  343. if np.any(indptr_diff_limited != indptr_diff):
  344. raise ValueError("Matrix too big to convert")
  345. indptr_diff = indptr_diff_limited
  346. idx_dtype = self._get_index_dtype(maxval=max(M, N))
  347. row = (R * np.arange(M//R, dtype=idx_dtype)).repeat(indptr_diff)
  348. row = row.repeat(R*C).reshape(-1,R,C)
  349. row += np.tile(np.arange(R, dtype=idx_dtype).reshape(-1,1), (1,C))
  350. row = row.reshape(-1)
  351. col = ((C * self.indices).astype(idx_dtype, copy=False)
  352. .repeat(R*C).reshape(-1,R,C))
  353. col += np.tile(np.arange(C, dtype=idx_dtype), (R,1))
  354. col = col.reshape(-1)
  355. data = self.data.reshape(-1)
  356. if copy:
  357. data = data.copy()
  358. return self._coo_container(
  359. (data, (row, col)), shape=self.shape
  360. )
  361. def toarray(self, order=None, out=None):
  362. return self.tocoo(copy=False).toarray(order=order, out=out)
  363. toarray.__doc__ = _spbase.toarray.__doc__
  364. def transpose(self, axes=None, copy=False):
  365. if axes is not None and axes != (1, 0):
  366. raise ValueError("Sparse matrices do not support "
  367. "an 'axes' parameter because swapping "
  368. "dimensions is the only logical permutation.")
  369. R, C = self.blocksize
  370. M, N = self.shape
  371. NBLK = self.nnz//(R*C)
  372. if self.nnz == 0:
  373. return self._bsr_container((N, M), blocksize=(C, R),
  374. dtype=self.dtype, copy=copy)
  375. indptr = np.empty(N//C + 1, dtype=self.indptr.dtype)
  376. indices = np.empty(NBLK, dtype=self.indices.dtype)
  377. data = np.empty((NBLK, C, R), dtype=self.data.dtype)
  378. bsr_transpose(M//R, N//C, R, C,
  379. self.indptr, self.indices, self.data.ravel(),
  380. indptr, indices, data.ravel())
  381. return self._bsr_container((data, indices, indptr),
  382. shape=(N, M), copy=copy)
  383. transpose.__doc__ = _spbase.transpose.__doc__
  384. ##############################################################
  385. # methods that examine or modify the internal data structure #
  386. ##############################################################
  387. def eliminate_zeros(self):
  388. """Remove zero elements in-place."""
  389. if not self.nnz:
  390. return # nothing to do
  391. R,C = self.blocksize
  392. M,N = self.shape
  393. mask = (self.data != 0).reshape(-1,R*C).sum(axis=1) # nonzero blocks
  394. nonzero_blocks = mask.nonzero()[0]
  395. self.data[:len(nonzero_blocks)] = self.data[nonzero_blocks]
  396. # modifies self.indptr and self.indices *in place*
  397. _sparsetools.csr_eliminate_zeros(M//R, N//C, self.indptr,
  398. self.indices, mask)
  399. self.prune()
  400. def sum_duplicates(self):
  401. """Eliminate duplicate array/matrix entries by adding them together
  402. The is an *in place* operation
  403. """
  404. if self.has_canonical_format:
  405. return
  406. self.sort_indices()
  407. R, C = self.blocksize
  408. M, N = self.shape
  409. # port of _sparsetools.csr_sum_duplicates
  410. n_row = M // R
  411. nnz = 0
  412. row_end = 0
  413. for i in range(n_row):
  414. jj = row_end
  415. row_end = self.indptr[i+1]
  416. while jj < row_end:
  417. j = self.indices[jj]
  418. x = self.data[jj]
  419. jj += 1
  420. while jj < row_end and self.indices[jj] == j:
  421. x += self.data[jj]
  422. jj += 1
  423. self.indices[nnz] = j
  424. self.data[nnz] = x
  425. nnz += 1
  426. self.indptr[i+1] = nnz
  427. self.prune() # nnz may have changed
  428. self.has_canonical_format = True
  429. def sort_indices(self):
  430. """Sort the indices of this array/matrix *in place*
  431. """
  432. if self.has_sorted_indices:
  433. return
  434. R,C = self.blocksize
  435. M,N = self.shape
  436. bsr_sort_indices(M//R, N//C, R, C, self.indptr, self.indices, self.data.ravel())
  437. self.has_sorted_indices = True
  438. def prune(self):
  439. """Remove empty space after all non-zero elements.
  440. """
  441. R,C = self.blocksize
  442. M,N = self.shape
  443. if len(self.indptr) != M//R + 1:
  444. raise ValueError("index pointer has invalid length")
  445. bnnz = self.indptr[-1]
  446. if len(self.indices) < bnnz:
  447. raise ValueError("indices array has too few elements")
  448. if len(self.data) < bnnz:
  449. raise ValueError("data array has too few elements")
  450. self.data = self.data[:bnnz]
  451. self.indices = self.indices[:bnnz]
  452. # utility functions
  453. def _binopt(self, other, op, in_shape=None, out_shape=None):
  454. """Apply the binary operation fn to two sparse matrices."""
  455. # Ideally we'd take the GCDs of the blocksize dimensions
  456. # and explode self and other to match.
  457. other = self.__class__(other, blocksize=self.blocksize)
  458. # e.g. bsr_plus_bsr, etc.
  459. fn = getattr(_sparsetools, self.format + op + self.format)
  460. R,C = self.blocksize
  461. max_bnnz = len(self.data) + len(other.data)
  462. idx_dtype = self._get_index_dtype((self.indptr, self.indices,
  463. other.indptr, other.indices),
  464. maxval=max_bnnz)
  465. indptr = np.empty(self.indptr.shape, dtype=idx_dtype)
  466. indices = np.empty(max_bnnz, dtype=idx_dtype)
  467. bool_ops = ['_ne_', '_lt_', '_gt_', '_le_', '_ge_']
  468. if op in bool_ops:
  469. data = np.empty(R*C*max_bnnz, dtype=np.bool_)
  470. else:
  471. data = np.empty(R*C*max_bnnz, dtype=upcast(self.dtype,other.dtype))
  472. fn(self.shape[0]//R, self.shape[1]//C, R, C,
  473. self.indptr.astype(idx_dtype, copy=False),
  474. self.indices.astype(idx_dtype, copy=False),
  475. self.data,
  476. other.indptr.astype(idx_dtype, copy=False),
  477. other.indices.astype(idx_dtype, copy=False),
  478. np.ravel(other.data),
  479. indptr,
  480. indices,
  481. data)
  482. actual_bnnz = indptr[-1]
  483. indices = indices[:actual_bnnz]
  484. data = data[:R*C*actual_bnnz]
  485. if actual_bnnz < max_bnnz/2:
  486. indices = indices.copy()
  487. data = data.copy()
  488. data = data.reshape(-1,R,C)
  489. return self.__class__((data, indices, indptr), shape=self.shape)
  490. # needed by _data_matrix
  491. def _with_data(self,data,copy=True):
  492. """Returns a matrix with the same sparsity structure as self,
  493. but with different data. By default the structure arrays
  494. (i.e. .indptr and .indices) are copied.
  495. """
  496. if copy:
  497. return self.__class__((data,self.indices.copy(),self.indptr.copy()),
  498. shape=self.shape,dtype=data.dtype)
  499. else:
  500. return self.__class__((data,self.indices,self.indptr),
  501. shape=self.shape,dtype=data.dtype)
  502. # # these functions are used by the parent class
  503. # # to remove redundancy between bsc_matrix and bsr_matrix
  504. # def _swap(self,x):
  505. # """swap the members of x if this is a column-oriented matrix
  506. # """
  507. # return (x[0],x[1])
  508. def _broadcast_to(self, shape, copy=False):
  509. return _spbase._broadcast_to(self, shape, copy)
  510. def isspmatrix_bsr(x):
  511. """Is `x` of a bsr_matrix type?
  512. Parameters
  513. ----------
  514. x
  515. object to check for being a bsr matrix
  516. Returns
  517. -------
  518. bool
  519. True if `x` is a bsr matrix, False otherwise
  520. Examples
  521. --------
  522. >>> from scipy.sparse import bsr_array, bsr_matrix, csr_matrix, isspmatrix_bsr
  523. >>> isspmatrix_bsr(bsr_matrix([[5]]))
  524. True
  525. >>> isspmatrix_bsr(bsr_array([[5]]))
  526. False
  527. >>> isspmatrix_bsr(csr_matrix([[5]]))
  528. False
  529. """
  530. return isinstance(x, bsr_matrix)
  531. # This namespace class separates array from matrix with isinstance
  532. class bsr_array(_bsr_base, sparray):
  533. """
  534. Block Sparse Row format sparse array.
  535. This can be instantiated in several ways:
  536. bsr_array(D, [blocksize=(R,C)])
  537. where D is a 2-D ndarray.
  538. bsr_array(S, [blocksize=(R,C)])
  539. with another sparse array or matrix S (equivalent to S.tobsr())
  540. bsr_array((M, N), [blocksize=(R,C), dtype])
  541. to construct an empty sparse array with shape (M, N)
  542. dtype is optional, defaulting to dtype='d'.
  543. bsr_array((data, ij), [blocksize=(R,C), shape=(M, N)])
  544. where ``data`` and ``ij`` satisfy ``a[ij[0, k], ij[1, k]] = data[k]``
  545. bsr_array((data, indices, indptr), [shape=(M, N)])
  546. is the standard BSR representation where the block column
  547. indices for row i are stored in ``indices[indptr[i]:indptr[i+1]]``
  548. and their corresponding block values are stored in
  549. ``data[ indptr[i]: indptr[i+1] ]``. If the shape parameter is not
  550. supplied, the array dimensions are inferred from the index arrays.
  551. Attributes
  552. ----------
  553. dtype : dtype
  554. Data type of the array
  555. shape : 2-tuple
  556. Shape of the array
  557. ndim : int
  558. Number of dimensions (this is always 2)
  559. nnz
  560. size
  561. data
  562. BSR format data array of the array
  563. indices
  564. BSR format index array of the array
  565. indptr
  566. BSR format index pointer array of the array
  567. blocksize
  568. Block size
  569. has_sorted_indices : bool
  570. Whether indices are sorted
  571. has_canonical_format : bool
  572. T
  573. Notes
  574. -----
  575. Sparse arrays can be used in arithmetic operations: they support
  576. addition, subtraction, multiplication, division, and matrix power.
  577. **Summary of BSR format**
  578. The Block Sparse Row (BSR) format is very similar to the Compressed
  579. Sparse Row (CSR) format. BSR is appropriate for sparse matrices with dense
  580. sub matrices like the last example below. Such sparse block matrices often
  581. arise in vector-valued finite element discretizations. In such cases, BSR is
  582. considerably more efficient than CSR and CSC for many sparse arithmetic
  583. operations.
  584. **Blocksize**
  585. The blocksize (R,C) must evenly divide the shape of the sparse array (M,N).
  586. That is, R and C must satisfy the relationship ``M % R = 0`` and
  587. ``N % C = 0``.
  588. If no blocksize is specified, a simple heuristic is applied to determine
  589. an appropriate blocksize.
  590. **Canonical Format**
  591. In canonical format, there are no duplicate blocks and indices are sorted
  592. per row.
  593. **Limitations**
  594. Block Sparse Row format sparse arrays do not support slicing.
  595. Examples
  596. --------
  597. >>> import numpy as np
  598. >>> from scipy.sparse import bsr_array
  599. >>> bsr_array((3, 4), dtype=np.int8).toarray()
  600. array([[0, 0, 0, 0],
  601. [0, 0, 0, 0],
  602. [0, 0, 0, 0]], dtype=int8)
  603. >>> row = np.array([0, 0, 1, 2, 2, 2])
  604. >>> col = np.array([0, 2, 2, 0, 1, 2])
  605. >>> data = np.array([1, 2, 3 ,4, 5, 6])
  606. >>> bsr_array((data, (row, col)), shape=(3, 3)).toarray()
  607. array([[1, 0, 2],
  608. [0, 0, 3],
  609. [4, 5, 6]])
  610. >>> indptr = np.array([0, 2, 3, 6])
  611. >>> indices = np.array([0, 2, 2, 0, 1, 2])
  612. >>> data = np.array([1, 2, 3, 4, 5, 6]).repeat(4).reshape(6, 2, 2)
  613. >>> bsr_array((data,indices,indptr), shape=(6, 6)).toarray()
  614. array([[1, 1, 0, 0, 2, 2],
  615. [1, 1, 0, 0, 2, 2],
  616. [0, 0, 0, 0, 3, 3],
  617. [0, 0, 0, 0, 3, 3],
  618. [4, 4, 5, 5, 6, 6],
  619. [4, 4, 5, 5, 6, 6]])
  620. """
  621. class bsr_matrix(spmatrix, _bsr_base):
  622. """
  623. Block Sparse Row format sparse matrix.
  624. This can be instantiated in several ways:
  625. bsr_matrix(D, [blocksize=(R,C)])
  626. where D is a 2-D ndarray.
  627. bsr_matrix(S, [blocksize=(R,C)])
  628. with another sparse array or matrix S (equivalent to S.tobsr())
  629. bsr_matrix((M, N), [blocksize=(R,C), dtype])
  630. to construct an empty sparse matrix with shape (M, N)
  631. dtype is optional, defaulting to dtype='d'.
  632. bsr_matrix((data, ij), [blocksize=(R,C), shape=(M, N)])
  633. where ``data`` and ``ij`` satisfy ``a[ij[0, k], ij[1, k]] = data[k]``
  634. bsr_matrix((data, indices, indptr), [shape=(M, N)])
  635. is the standard BSR representation where the block column
  636. indices for row i are stored in ``indices[indptr[i]:indptr[i+1]]``
  637. and their corresponding block values are stored in
  638. ``data[ indptr[i]: indptr[i+1] ]``. If the shape parameter is not
  639. supplied, the matrix dimensions are inferred from the index arrays.
  640. Attributes
  641. ----------
  642. dtype : dtype
  643. Data type of the matrix
  644. shape : 2-tuple
  645. Shape of the matrix
  646. ndim : int
  647. Number of dimensions (this is always 2)
  648. nnz
  649. size
  650. data
  651. BSR format data array of the matrix
  652. indices
  653. BSR format index array of the matrix
  654. indptr
  655. BSR format index pointer array of the matrix
  656. blocksize
  657. Block size
  658. has_sorted_indices : bool
  659. Whether indices are sorted
  660. has_canonical_format : bool
  661. T
  662. Notes
  663. -----
  664. Sparse matrices can be used in arithmetic operations: they support
  665. addition, subtraction, multiplication, division, and matrix power.
  666. **Summary of BSR format**
  667. The Block Sparse Row (BSR) format is very similar to the Compressed
  668. Sparse Row (CSR) format. BSR is appropriate for sparse matrices with dense
  669. sub matrices like the last example below. Such sparse block matrices often
  670. arise in vector-valued finite element discretizations. In such cases, BSR is
  671. considerably more efficient than CSR and CSC for many sparse arithmetic
  672. operations.
  673. **Blocksize**
  674. The blocksize (R,C) must evenly divide the shape of the sparse matrix (M,N).
  675. That is, R and C must satisfy the relationship ``M % R = 0`` and
  676. ``N % C = 0``.
  677. If no blocksize is specified, a simple heuristic is applied to determine
  678. an appropriate blocksize.
  679. **Canonical Format**
  680. In canonical format, there are no duplicate blocks and indices are sorted
  681. per row.
  682. **Limitations**
  683. Block Sparse Row format sparse matrices do not support slicing.
  684. Examples
  685. --------
  686. >>> import numpy as np
  687. >>> from scipy.sparse import bsr_matrix
  688. >>> bsr_matrix((3, 4), dtype=np.int8).toarray()
  689. array([[0, 0, 0, 0],
  690. [0, 0, 0, 0],
  691. [0, 0, 0, 0]], dtype=int8)
  692. >>> row = np.array([0, 0, 1, 2, 2, 2])
  693. >>> col = np.array([0, 2, 2, 0, 1, 2])
  694. >>> data = np.array([1, 2, 3 ,4, 5, 6])
  695. >>> bsr_matrix((data, (row, col)), shape=(3, 3)).toarray()
  696. array([[1, 0, 2],
  697. [0, 0, 3],
  698. [4, 5, 6]])
  699. >>> indptr = np.array([0, 2, 3, 6])
  700. >>> indices = np.array([0, 2, 2, 0, 1, 2])
  701. >>> data = np.array([1, 2, 3, 4, 5, 6]).repeat(4).reshape(6, 2, 2)
  702. >>> bsr_matrix((data,indices,indptr), shape=(6, 6)).toarray()
  703. array([[1, 1, 0, 0, 2, 2],
  704. [1, 1, 0, 0, 2, 2],
  705. [0, 0, 0, 0, 3, 3],
  706. [0, 0, 0, 0, 3, 3],
  707. [4, 4, 5, 5, 6, 6],
  708. [4, 4, 5, 5, 6, 6]])
  709. """