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
« prev ^ index » next coverage.py v7.4.4, created at 2024-03-22 06:44 +0000
1"""Compressed Block Sparse Row format"""
3__docformat__ = "restructuredtext en"
5__all__ = ['bsr_array', 'bsr_matrix', 'isspmatrix_bsr']
7from warnings import warn
9import numpy as np
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)
24class _bsr_base(_cs_matrix, _minmax_mixin):
25 _format = 'bsr'
27 def __init__(self, arg1, shape=None, dtype=None, copy=False, blocksize=None):
28 _data_matrix.__init__(self)
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 )
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))
53 R,C = blocksize
54 if (M % R) != 0 or (N % C) != 0:
55 raise ValueError('shape must be multiple of blocksize')
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)
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 )
71 elif len(arg1) == 3:
72 # (data,indices,indptr) format
73 (data, indices, indptr) = arg1
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 )
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))
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)
136 if dtype is not None:
137 self.data = self.data.astype(dtype, copy=False)
139 self.check_format(full_check=False)
141 def check_format(self, full_check=True):
142 """Check whether the array/matrix respects the BSR format.
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
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)
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")
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")
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")
184 self.prune()
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")
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)
206 @property
207 def blocksize(self) -> tuple:
208 """Block size of the matrix."""
209 return self.data.shape[1:]
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)
218 _getnnz.__doc__ = _spbase._getnnz.__doc__
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 )
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
242 diagonal.__doc__ = _spbase.diagonal.__doc__
244 ##########################
245 # NotImplemented methods #
246 ##########################
248 def __getitem__(self,key):
249 raise NotImplementedError
251 def __setitem__(self,key,val):
252 raise NotImplementedError
254 ######################
255 # Arithmetic methods #
256 ######################
258 def _add_dense(self, other):
259 return self.tocoo(copy=False)._add_dense(other)
261 def _matmul_vector(self, other):
262 M,N = self.shape
263 R,C = self.blocksize
265 result = np.zeros(self.shape[0], dtype=upcast(self.dtype, other.dtype))
267 bsr_matvec(M//R, N//C, R, C,
268 self.indptr, self.indices, self.data.ravel(),
269 other, result)
271 return result
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
278 result = np.zeros((M,n_vecs), dtype=upcast(self.dtype,other.dtype))
280 bsr_matvecs(M//R, N//C, n_vecs, R, C,
281 self.indptr, self.indices, self.data.ravel(),
282 other.ravel(), result.ravel())
284 return result
286 def _matmul_sparse(self, other):
287 M, K1 = self.shape
288 K2, N = other.shape
290 R,n = self.blocksize
292 # convert to this format
293 if other.format == "bsr":
294 C = other.blocksize[1]
295 else:
296 C = 1
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))
303 idx_dtype = self._get_index_dtype((self.indptr, self.indices,
304 other.indptr, other.indices))
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))
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))
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)
330 data = data.reshape(-1,R,C)
332 # TODO eliminate zeros
334 return self._bsr_container(
335 (data, indices, indptr), shape=(M, N), blocksize=(R, C)
336 )
338 ######################
339 # Conversion methods #
340 ######################
342 def tobsr(self, blocksize=None, copy=False):
343 """Convert this array/matrix into Block Sparse Row Format.
345 With copy=False, the data/indices may be shared between this
346 array/matrix and the resultant bsr_array/bsr_matrix.
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
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))
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)
379 tocsr.__doc__ = _spbase.tocsr.__doc__
381 def tocsc(self, copy=False):
382 return self.tocsr(copy=False).tocsc(copy=copy)
384 tocsc.__doc__ = _spbase.tocsc.__doc__
386 def tocoo(self, copy=True):
387 """Convert this array/matrix to COOrdinate format.
389 When copy=False the data array will be shared between
390 this array/matrix and the resultant coo_array/coo_matrix.
391 """
393 M,N = self.shape
394 R,C = self.blocksize
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
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)
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)
415 data = self.data.reshape(-1)
417 if copy:
418 data = data.copy()
420 return self._coo_container(
421 (data, (row, col)), shape=self.shape
422 )
424 def toarray(self, order=None, out=None):
425 return self.tocoo(copy=False).toarray(order=order, out=out)
427 toarray.__doc__ = _spbase.toarray.__doc__
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.")
435 R, C = self.blocksize
436 M, N = self.shape
437 NBLK = self.nnz//(R*C)
439 if self.nnz == 0:
440 return self._bsr_container((N, M), blocksize=(C, R),
441 dtype=self.dtype, copy=copy)
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)
447 bsr_transpose(M//R, N//C, R, C,
448 self.indptr, self.indices, self.data.ravel(),
449 indptr, indices, data.ravel())
451 return self._bsr_container((data, indices, indptr),
452 shape=(N, M), copy=copy)
454 transpose.__doc__ = _spbase.transpose.__doc__
456 ##############################################################
457 # methods that examine or modify the internal data structure #
458 ##############################################################
460 def eliminate_zeros(self):
461 """Remove zero elements in-place."""
463 if not self.nnz:
464 return # nothing to do
466 R,C = self.blocksize
467 M,N = self.shape
469 mask = (self.data != 0).reshape(-1,R*C).sum(axis=1) # nonzero blocks
471 nonzero_blocks = mask.nonzero()[0]
473 self.data[:len(nonzero_blocks)] = self.data[nonzero_blocks]
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()
480 def sum_duplicates(self):
481 """Eliminate duplicate array/matrix entries by adding them together
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
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
510 self.prune() # nnz may have changed
511 self.has_canonical_format = True
513 def sort_indices(self):
514 """Sort the indices of this array/matrix *in place*
515 """
516 if self.has_sorted_indices:
517 return
519 R,C = self.blocksize
520 M,N = self.shape
522 bsr_sort_indices(M//R, N//C, R, C, self.indptr, self.indices, self.data.ravel())
524 self.has_sorted_indices = True
526 def prune(self):
527 """Remove empty space after all non-zero elements.
528 """
530 R,C = self.blocksize
531 M,N = self.shape
533 if len(self.indptr) != M//R + 1:
534 raise ValueError("index pointer has invalid length")
536 bnnz = self.indptr[-1]
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")
543 self.data = self.data[:bnnz]
544 self.indices = self.indices[:bnnz]
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."""
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)
554 # e.g. bsr_plus_bsr, etc.
555 fn = getattr(_sparsetools, self.format + op + self.format)
557 R,C = self.blocksize
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)
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))
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)
583 actual_bnnz = indptr[-1]
584 indices = indices[:actual_bnnz]
585 data = data[:R*C*actual_bnnz]
587 if actual_bnnz < max_bnnz/2:
588 indices = indices.copy()
589 data = data.copy()
591 data = data.reshape(-1,R,C)
593 return self.__class__((data, indices, indptr), shape=self.shape)
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)
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])
616def isspmatrix_bsr(x):
617 """Is `x` of a bsr_matrix type?
619 Parameters
620 ----------
621 x
622 object to check for being a bsr matrix
624 Returns
625 -------
626 bool
627 True if `x` is a bsr matrix, False otherwise
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)
642# This namespace class separates array from matrix with isinstance
643class bsr_array(_bsr_base, sparray):
644 """
645 Block Sparse Row format sparse array.
647 This can be instantiated in several ways:
648 bsr_array(D, [blocksize=(R,C)])
649 where D is a 2-D ndarray.
651 bsr_array(S, [blocksize=(R,C)])
652 with another sparse array or matrix S (equivalent to S.tobsr())
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'.
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]``
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.
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
691 Notes
692 -----
693 Sparse arrays can be used in arithmetic operations: they support
694 addition, subtraction, multiplication, division, and matrix power.
696 **Summary of BSR format**
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.
705 **Blocksize**
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``.
711 If no blocksize is specified, a simple heuristic is applied to determine
712 an appropriate blocksize.
714 **Canonical Format**
716 In canonical format, there are no duplicate blocks and indices are sorted
717 per row.
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)
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]])
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]])
747 """
750class bsr_matrix(spmatrix, _bsr_base):
751 """
752 Block Sparse Row format sparse matrix.
754 This can be instantiated in several ways:
755 bsr_matrix(D, [blocksize=(R,C)])
756 where D is a 2-D ndarray.
758 bsr_matrix(S, [blocksize=(R,C)])
759 with another sparse array or matrix S (equivalent to S.tobsr())
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'.
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]``
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.
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
798 Notes
799 -----
800 Sparse matrices can be used in arithmetic operations: they support
801 addition, subtraction, multiplication, division, and matrix power.
803 **Summary of BSR format**
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.
812 **Blocksize**
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``.
818 If no blocksize is specified, a simple heuristic is applied to determine
819 an appropriate blocksize.
821 **Canonical Format**
823 In canonical format, there are no duplicate blocks and indices are sorted
824 per row.
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)
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]])
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]])
854 """