Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/scipy/sparse/_construct.py: 10%
312 statements
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-23 06:43 +0000
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-23 06:43 +0000
1"""Functions to construct sparse matrices and arrays
2"""
4__docformat__ = "restructuredtext en"
6__all__ = ['spdiags', 'eye', 'identity', 'kron', 'kronsum',
7 'hstack', 'vstack', 'bmat', 'rand', 'random', 'diags', 'block_diag',
8 'diags_array', 'block']
10import numbers
11from functools import partial
12import numpy as np
14from scipy._lib._util import check_random_state, rng_integers
15from ._sputils import upcast, get_index_dtype, isscalarlike
17from ._sparsetools import csr_hstack
18from ._bsr import bsr_matrix
19from ._coo import coo_matrix, coo_array
20from ._csc import csc_matrix, csc_array
21from ._csr import csr_matrix, csr_array
22from ._dia import dia_matrix, dia_array
24from ._base import issparse, sparray
27def spdiags(data, diags, m=None, n=None, format=None):
28 """
29 Return a sparse matrix from diagonals.
31 Parameters
32 ----------
33 data : array_like
34 Matrix diagonals stored row-wise
35 diags : sequence of int or an int
36 Diagonals to set:
38 * k = 0 the main diagonal
39 * k > 0 the kth upper diagonal
40 * k < 0 the kth lower diagonal
41 m, n : int, tuple, optional
42 Shape of the result. If `n` is None and `m` is a given tuple,
43 the shape is this tuple. If omitted, the matrix is square and
44 its shape is len(data[0]).
45 format : str, optional
46 Format of the result. By default (format=None) an appropriate sparse
47 matrix format is returned. This choice is subject to change.
49 See Also
50 --------
51 diags : more convenient form of this function
52 dia_matrix : the sparse DIAgonal format.
54 Examples
55 --------
56 >>> import numpy as np
57 >>> from scipy.sparse import spdiags
58 >>> data = np.array([[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]])
59 >>> diags = np.array([0, -1, 2])
60 >>> spdiags(data, diags, 4, 4).toarray()
61 array([[1, 0, 3, 0],
62 [1, 2, 0, 4],
63 [0, 2, 3, 0],
64 [0, 0, 3, 4]])
66 """
67 if m is None and n is None:
68 m = n = len(data[0])
69 elif n is None:
70 m, n = m
71 return dia_matrix((data, diags), shape=(m, n)).asformat(format)
74def diags_array(diagonals, /, *, offsets=0, shape=None, format=None, dtype=None):
75 """
76 Construct a sparse array from diagonals.
78 Parameters
79 ----------
80 diagonals : sequence of array_like
81 Sequence of arrays containing the array diagonals,
82 corresponding to `offsets`.
83 offsets : sequence of int or an int, optional
84 Diagonals to set:
85 - k = 0 the main diagonal (default)
86 - k > 0 the kth upper diagonal
87 - k < 0 the kth lower diagonal
88 shape : tuple of int, optional
89 Shape of the result. If omitted, a square array large enough
90 to contain the diagonals is returned.
91 format : {"dia", "csr", "csc", "lil", ...}, optional
92 Matrix format of the result. By default (format=None) an
93 appropriate sparse array format is returned. This choice is
94 subject to change.
95 dtype : dtype, optional
96 Data type of the array.
98 Notes
99 -----
100 The result from `diags_array` is the sparse equivalent of::
102 np.diag(diagonals[0], offsets[0])
103 + ...
104 + np.diag(diagonals[k], offsets[k])
106 Repeated diagonal offsets are disallowed.
108 .. versionadded:: 1.11
110 Examples
111 --------
112 >>> from scipy.sparse import diags_array
113 >>> diagonals = [[1, 2, 3, 4], [1, 2, 3], [1, 2]]
114 >>> diags_array(diagonals, offsets=[0, -1, 2]).toarray()
115 array([[1, 0, 1, 0],
116 [1, 2, 0, 2],
117 [0, 2, 3, 0],
118 [0, 0, 3, 4]])
120 Broadcasting of scalars is supported (but shape needs to be
121 specified):
123 >>> diags_array([1, -2, 1], offsets=[-1, 0, 1], shape=(4, 4)).toarray()
124 array([[-2., 1., 0., 0.],
125 [ 1., -2., 1., 0.],
126 [ 0., 1., -2., 1.],
127 [ 0., 0., 1., -2.]])
130 If only one diagonal is wanted (as in `numpy.diag`), the following
131 works as well:
133 >>> diags_array([1, 2, 3], offsets=1).toarray()
134 array([[ 0., 1., 0., 0.],
135 [ 0., 0., 2., 0.],
136 [ 0., 0., 0., 3.],
137 [ 0., 0., 0., 0.]])
138 """
139 # if offsets is not a sequence, assume that there's only one diagonal
140 if isscalarlike(offsets):
141 # now check that there's actually only one diagonal
142 if len(diagonals) == 0 or isscalarlike(diagonals[0]):
143 diagonals = [np.atleast_1d(diagonals)]
144 else:
145 raise ValueError("Different number of diagonals and offsets.")
146 else:
147 diagonals = list(map(np.atleast_1d, diagonals))
149 offsets = np.atleast_1d(offsets)
151 # Basic check
152 if len(diagonals) != len(offsets):
153 raise ValueError("Different number of diagonals and offsets.")
155 # Determine shape, if omitted
156 if shape is None:
157 m = len(diagonals[0]) + abs(int(offsets[0]))
158 shape = (m, m)
160 # Determine data type, if omitted
161 if dtype is None:
162 dtype = np.common_type(*diagonals)
164 # Construct data array
165 m, n = shape
167 M = max([min(m + offset, n - offset) + max(0, offset)
168 for offset in offsets])
169 M = max(0, M)
170 data_arr = np.zeros((len(offsets), M), dtype=dtype)
172 K = min(m, n)
174 for j, diagonal in enumerate(diagonals):
175 offset = offsets[j]
176 k = max(0, offset)
177 length = min(m + offset, n - offset, K)
178 if length < 0:
179 raise ValueError("Offset %d (index %d) out of bounds" % (offset, j))
180 try:
181 data_arr[j, k:k+length] = diagonal[...,:length]
182 except ValueError as e:
183 if len(diagonal) != length and len(diagonal) != 1:
184 raise ValueError(
185 "Diagonal length (index %d: %d at offset %d) does not "
186 "agree with array size (%d, %d)." % (
187 j, len(diagonal), offset, m, n)) from e
188 raise
190 return dia_array((data_arr, offsets), shape=(m, n)).asformat(format)
193def diags(diagonals, offsets=0, shape=None, format=None, dtype=None):
194 """
195 Construct a sparse matrix from diagonals.
197 Parameters
198 ----------
199 diagonals : sequence of array_like
200 Sequence of arrays containing the matrix diagonals,
201 corresponding to `offsets`.
202 offsets : sequence of int or an int, optional
203 Diagonals to set:
204 - k = 0 the main diagonal (default)
205 - k > 0 the kth upper diagonal
206 - k < 0 the kth lower diagonal
207 shape : tuple of int, optional
208 Shape of the result. If omitted, a square matrix large enough
209 to contain the diagonals is returned.
210 format : {"dia", "csr", "csc", "lil", ...}, optional
211 Matrix format of the result. By default (format=None) an
212 appropriate sparse matrix format is returned. This choice is
213 subject to change.
214 dtype : dtype, optional
215 Data type of the matrix.
217 See Also
218 --------
219 spdiags : construct matrix from diagonals
221 Notes
222 -----
223 This function differs from `spdiags` in the way it handles
224 off-diagonals.
226 The result from `diags` is the sparse equivalent of::
228 np.diag(diagonals[0], offsets[0])
229 + ...
230 + np.diag(diagonals[k], offsets[k])
232 Repeated diagonal offsets are disallowed.
234 .. versionadded:: 0.11
236 Examples
237 --------
238 >>> from scipy.sparse import diags
239 >>> diagonals = [[1, 2, 3, 4], [1, 2, 3], [1, 2]]
240 >>> diags(diagonals, [0, -1, 2]).toarray()
241 array([[1, 0, 1, 0],
242 [1, 2, 0, 2],
243 [0, 2, 3, 0],
244 [0, 0, 3, 4]])
246 Broadcasting of scalars is supported (but shape needs to be
247 specified):
249 >>> diags([1, -2, 1], [-1, 0, 1], shape=(4, 4)).toarray()
250 array([[-2., 1., 0., 0.],
251 [ 1., -2., 1., 0.],
252 [ 0., 1., -2., 1.],
253 [ 0., 0., 1., -2.]])
256 If only one diagonal is wanted (as in `numpy.diag`), the following
257 works as well:
259 >>> diags([1, 2, 3], 1).toarray()
260 array([[ 0., 1., 0., 0.],
261 [ 0., 0., 2., 0.],
262 [ 0., 0., 0., 3.],
263 [ 0., 0., 0., 0.]])
264 """
265 A = diags_array(diagonals, offsets=offsets, shape=shape, dtype=dtype)
266 return dia_matrix(A).asformat(format)
269def identity(n, dtype='d', format=None):
270 """Identity matrix in sparse format
272 Returns an identity matrix with shape (n,n) using a given
273 sparse format and dtype.
275 Parameters
276 ----------
277 n : int
278 Shape of the identity matrix.
279 dtype : dtype, optional
280 Data type of the matrix
281 format : str, optional
282 Sparse format of the result, e.g., format="csr", etc.
284 Examples
285 --------
286 >>> from scipy.sparse import identity
287 >>> identity(3).toarray()
288 array([[ 1., 0., 0.],
289 [ 0., 1., 0.],
290 [ 0., 0., 1.]])
291 >>> identity(3, dtype='int8', format='dia')
292 <3x3 sparse matrix of type '<class 'numpy.int8'>'
293 with 3 stored elements (1 diagonals) in DIAgonal format>
295 """
296 return eye(n, n, dtype=dtype, format=format)
299def eye(m, n=None, k=0, dtype=float, format=None):
300 """Sparse matrix with ones on diagonal
302 Returns a sparse (m x n) matrix where the kth diagonal
303 is all ones and everything else is zeros.
305 Parameters
306 ----------
307 m : int
308 Number of rows in the matrix.
309 n : int, optional
310 Number of columns. Default: `m`.
311 k : int, optional
312 Diagonal to place ones on. Default: 0 (main diagonal).
313 dtype : dtype, optional
314 Data type of the matrix.
315 format : str, optional
316 Sparse format of the result, e.g., format="csr", etc.
318 Examples
319 --------
320 >>> import numpy as np
321 >>> from scipy import sparse
322 >>> sparse.eye(3).toarray()
323 array([[ 1., 0., 0.],
324 [ 0., 1., 0.],
325 [ 0., 0., 1.]])
326 >>> sparse.eye(3, dtype=np.int8)
327 <3x3 sparse matrix of type '<class 'numpy.int8'>'
328 with 3 stored elements (1 diagonals) in DIAgonal format>
330 """
331 if n is None:
332 n = m
333 m,n = int(m),int(n)
335 if m == n and k == 0:
336 # fast branch for special formats
337 if format in ['csr', 'csc']:
338 idx_dtype = get_index_dtype(maxval=n)
339 indptr = np.arange(n+1, dtype=idx_dtype)
340 indices = np.arange(n, dtype=idx_dtype)
341 data = np.ones(n, dtype=dtype)
342 cls = {'csr': csr_matrix, 'csc': csc_matrix}[format]
343 return cls((data,indices,indptr),(n,n))
344 elif format == 'coo':
345 idx_dtype = get_index_dtype(maxval=n)
346 row = np.arange(n, dtype=idx_dtype)
347 col = np.arange(n, dtype=idx_dtype)
348 data = np.ones(n, dtype=dtype)
349 return coo_matrix((data, (row, col)), (n, n))
351 data = np.ones((1, max(0, min(m + k, n))), dtype=dtype)
352 return diags(data, offsets=[k], shape=(m, n), dtype=dtype).asformat(format)
355def kron(A, B, format=None):
356 """kronecker product of sparse matrices A and B
358 Parameters
359 ----------
360 A : sparse or dense matrix
361 first matrix of the product
362 B : sparse or dense matrix
363 second matrix of the product
364 format : str, optional
365 format of the result (e.g. "csr")
367 Returns
368 -------
369 kronecker product in a sparse matrix format
372 Examples
373 --------
374 >>> import numpy as np
375 >>> from scipy import sparse
376 >>> A = sparse.csr_matrix(np.array([[0, 2], [5, 0]]))
377 >>> B = sparse.csr_matrix(np.array([[1, 2], [3, 4]]))
378 >>> sparse.kron(A, B).toarray()
379 array([[ 0, 0, 2, 4],
380 [ 0, 0, 6, 8],
381 [ 5, 10, 0, 0],
382 [15, 20, 0, 0]])
384 >>> sparse.kron(A, [[1, 2], [3, 4]]).toarray()
385 array([[ 0, 0, 2, 4],
386 [ 0, 0, 6, 8],
387 [ 5, 10, 0, 0],
388 [15, 20, 0, 0]])
390 """
391 B = coo_matrix(B)
393 if (format is None or format == "bsr") and 2*B.nnz >= B.shape[0] * B.shape[1]:
394 # B is fairly dense, use BSR
395 A = csr_matrix(A,copy=True)
396 output_shape = (A.shape[0]*B.shape[0], A.shape[1]*B.shape[1])
398 if A.nnz == 0 or B.nnz == 0:
399 # kronecker product is the zero matrix
400 return coo_matrix(output_shape).asformat(format)
402 B = B.toarray()
403 data = A.data.repeat(B.size).reshape(-1,B.shape[0],B.shape[1])
404 data = data * B
406 return bsr_matrix((data,A.indices,A.indptr), shape=output_shape)
407 else:
408 # use COO
409 A = coo_matrix(A)
410 output_shape = (A.shape[0]*B.shape[0], A.shape[1]*B.shape[1])
412 if A.nnz == 0 or B.nnz == 0:
413 # kronecker product is the zero matrix
414 return coo_matrix(output_shape).asformat(format)
416 # expand entries of a into blocks
417 row = A.row.repeat(B.nnz)
418 col = A.col.repeat(B.nnz)
419 data = A.data.repeat(B.nnz)
421 if max(A.shape[0]*B.shape[0], A.shape[1]*B.shape[1]) > np.iinfo('int32').max:
422 row = row.astype(np.int64)
423 col = col.astype(np.int64)
425 row *= B.shape[0]
426 col *= B.shape[1]
428 # increment block indices
429 row,col = row.reshape(-1,B.nnz),col.reshape(-1,B.nnz)
430 row += B.row
431 col += B.col
432 row,col = row.reshape(-1),col.reshape(-1)
434 # compute block entries
435 data = data.reshape(-1,B.nnz) * B.data
436 data = data.reshape(-1)
438 return coo_matrix((data,(row,col)), shape=output_shape).asformat(format)
441def kronsum(A, B, format=None):
442 """kronecker sum of sparse matrices A and B
444 Kronecker sum of two sparse matrices is a sum of two Kronecker
445 products kron(I_n,A) + kron(B,I_m) where A has shape (m,m)
446 and B has shape (n,n) and I_m and I_n are identity matrices
447 of shape (m,m) and (n,n), respectively.
449 Parameters
450 ----------
451 A
452 square matrix
453 B
454 square matrix
455 format : str
456 format of the result (e.g. "csr")
458 Returns
459 -------
460 kronecker sum in a sparse matrix format
462 Examples
463 --------
466 """
467 A = coo_matrix(A)
468 B = coo_matrix(B)
470 if A.shape[0] != A.shape[1]:
471 raise ValueError('A is not square')
473 if B.shape[0] != B.shape[1]:
474 raise ValueError('B is not square')
476 dtype = upcast(A.dtype, B.dtype)
478 L = kron(eye(B.shape[0],dtype=dtype), A, format=format)
479 R = kron(B, eye(A.shape[0],dtype=dtype), format=format)
481 return (L+R).asformat(format) # since L + R is not always same format
484def _compressed_sparse_stack(blocks, axis, return_spmatrix):
485 """
486 Stacking fast path for CSR/CSC matrices or arrays
487 (i) vstack for CSR, (ii) hstack for CSC.
488 """
489 other_axis = 1 if axis == 0 else 0
490 data = np.concatenate([b.data for b in blocks])
491 constant_dim = blocks[0].shape[other_axis]
492 idx_dtype = get_index_dtype(arrays=[b.indptr for b in blocks],
493 maxval=max(data.size, constant_dim))
494 indices = np.empty(data.size, dtype=idx_dtype)
495 indptr = np.empty(sum(b.shape[axis] for b in blocks) + 1, dtype=idx_dtype)
496 last_indptr = idx_dtype(0)
497 sum_dim = 0
498 sum_indices = 0
499 for b in blocks:
500 if b.shape[other_axis] != constant_dim:
501 raise ValueError(f'incompatible dimensions for axis {other_axis}')
502 indices[sum_indices:sum_indices+b.indices.size] = b.indices
503 sum_indices += b.indices.size
504 idxs = slice(sum_dim, sum_dim + b.shape[axis])
505 indptr[idxs] = b.indptr[:-1]
506 indptr[idxs] += last_indptr
507 sum_dim += b.shape[axis]
508 last_indptr += b.indptr[-1]
509 indptr[-1] = last_indptr
510 # TODO remove this if-structure when sparse matrices removed
511 if return_spmatrix:
512 if axis == 0:
513 return csr_matrix((data, indices, indptr),
514 shape=(sum_dim, constant_dim))
515 else:
516 return csc_matrix((data, indices, indptr),
517 shape=(constant_dim, sum_dim))
519 if axis == 0:
520 return csr_array((data, indices, indptr),
521 shape=(sum_dim, constant_dim))
522 else:
523 return csc_array((data, indices, indptr),
524 shape=(constant_dim, sum_dim))
527def _stack_along_minor_axis(blocks, axis):
528 """
529 Stacking fast path for CSR/CSC matrices along the minor axis
530 (i) hstack for CSR, (ii) vstack for CSC.
531 """
532 n_blocks = len(blocks)
533 if n_blocks == 0:
534 raise ValueError('Missing block matrices')
536 if n_blocks == 1:
537 return blocks[0]
539 # check for incompatible dimensions
540 other_axis = 1 if axis == 0 else 0
541 other_axis_dims = {b.shape[other_axis] for b in blocks}
542 if len(other_axis_dims) > 1:
543 raise ValueError(f'Mismatching dimensions along axis {other_axis}: '
544 f'{other_axis_dims}')
545 constant_dim, = other_axis_dims
547 # Do the stacking
548 indptr_list = [b.indptr for b in blocks]
549 data_cat = np.concatenate([b.data for b in blocks])
551 # Need to check if any indices/indptr, would be too large post-
552 # concatenation for np.int32:
553 # - The max value of indices is the output array's stacking-axis length - 1
554 # - The max value in indptr is the number of non-zero entries. This is
555 # exceedingly unlikely to require int64, but is checked out of an
556 # abundance of caution.
557 sum_dim = sum(b.shape[axis] for b in blocks)
558 nnz = sum(len(b.indices) for b in blocks)
559 idx_dtype = get_index_dtype(maxval=max(sum_dim - 1, nnz))
560 stack_dim_cat = np.array([b.shape[axis] for b in blocks], dtype=idx_dtype)
561 if data_cat.size > 0:
562 indptr_cat = np.concatenate(indptr_list).astype(idx_dtype)
563 indices_cat = (np.concatenate([b.indices for b in blocks])
564 .astype(idx_dtype))
565 indptr = np.empty(constant_dim + 1, dtype=idx_dtype)
566 indices = np.empty_like(indices_cat)
567 data = np.empty_like(data_cat)
568 csr_hstack(n_blocks, constant_dim, stack_dim_cat,
569 indptr_cat, indices_cat, data_cat,
570 indptr, indices, data)
571 else:
572 indptr = np.zeros(constant_dim + 1, dtype=idx_dtype)
573 indices = np.empty(0, dtype=idx_dtype)
574 data = np.empty(0, dtype=data_cat.dtype)
576 if axis == 0:
577 return blocks[0]._csc_container((data, indices, indptr),
578 shape=(sum_dim, constant_dim))
579 else:
580 return blocks[0]._csr_container((data, indices, indptr),
581 shape=(constant_dim, sum_dim))
584def hstack(blocks, format=None, dtype=None):
585 """
586 Stack sparse matrices horizontally (column wise)
588 Parameters
589 ----------
590 blocks
591 sequence of sparse matrices with compatible shapes
592 format : str
593 sparse format of the result (e.g., "csr")
594 by default an appropriate sparse matrix format is returned.
595 This choice is subject to change.
596 dtype : dtype, optional
597 The data-type of the output matrix. If not given, the dtype is
598 determined from that of `blocks`.
600 Returns
601 -------
602 new_array : sparse matrix or array
603 If any block in blocks is a sparse array, return a sparse array.
604 Otherwise return a sparse matrix.
606 If you want a sparse array built from blocks that are not sparse
607 arrays, use `block(hstack(blocks))` or convert one block
608 e.g. `blocks[0] = csr_array(blocks[0])`.
610 See Also
611 --------
612 vstack : stack sparse matrices vertically (row wise)
614 Examples
615 --------
616 >>> from scipy.sparse import coo_matrix, hstack
617 >>> A = coo_matrix([[1, 2], [3, 4]])
618 >>> B = coo_matrix([[5], [6]])
619 >>> hstack([A,B]).toarray()
620 array([[1, 2, 5],
621 [3, 4, 6]])
623 """
624 blocks = np.asarray(blocks, dtype='object')
625 if any(isinstance(b, sparray) for b in blocks.flat):
626 return _block([blocks], format, dtype)
627 else:
628 return _block([blocks], format, dtype, return_spmatrix=True)
631def vstack(blocks, format=None, dtype=None):
632 """
633 Stack sparse arrays vertically (row wise)
635 Parameters
636 ----------
637 blocks
638 sequence of sparse arrays with compatible shapes
639 format : str, optional
640 sparse format of the result (e.g., "csr")
641 by default an appropriate sparse array format is returned.
642 This choice is subject to change.
643 dtype : dtype, optional
644 The data-type of the output array. If not given, the dtype is
645 determined from that of `blocks`.
647 Returns
648 -------
649 new_array : sparse matrix or array
650 If any block in blocks is a sparse array, return a sparse array.
651 Otherwise return a sparse matrix.
653 If you want a sparse array built from blocks that are not sparse
654 arrays, use `block(vstack(blocks))` or convert one block
655 e.g. `blocks[0] = csr_array(blocks[0])`.
657 See Also
658 --------
659 hstack : stack sparse matrices horizontally (column wise)
661 Examples
662 --------
663 >>> from scipy.sparse import coo_array, vstack
664 >>> A = coo_array([[1, 2], [3, 4]])
665 >>> B = coo_array([[5, 6]])
666 >>> vstack([A, B]).toarray()
667 array([[1, 2],
668 [3, 4],
669 [5, 6]])
671 """
672 blocks = np.asarray(blocks, dtype='object')
673 if any(isinstance(b, sparray) for b in blocks.flat):
674 return _block([[b] for b in blocks], format, dtype)
675 else:
676 return _block([[b] for b in blocks], format, dtype, return_spmatrix=True)
679def bmat(blocks, format=None, dtype=None):
680 """
681 Build a sparse array or matrix from sparse sub-blocks
683 Note: `block` is preferred over `bmat`. They are the same function
684 except that `bmat` can return a deprecated sparse matrix.
685 `bmat` returns a coo_matrix if none of the inputs are a sparse array.
687 Parameters
688 ----------
689 blocks : array_like
690 Grid of sparse matrices with compatible shapes.
691 An entry of None implies an all-zero matrix.
692 format : {'bsr', 'coo', 'csc', 'csr', 'dia', 'dok', 'lil'}, optional
693 The sparse format of the result (e.g. "csr"). By default an
694 appropriate sparse matrix format is returned.
695 This choice is subject to change.
696 dtype : dtype, optional
697 The data-type of the output matrix. If not given, the dtype is
698 determined from that of `blocks`.
700 Returns
701 -------
702 bmat : sparse matrix or array
703 If any block in blocks is a sparse array, return a sparse array.
704 Otherwise return a sparse matrix.
706 If you want a sparse array built from blocks that are not sparse
707 arrays, use `block()`.
709 See Also
710 --------
711 block
713 Examples
714 --------
715 >>> from scipy.sparse import coo_array, bmat
716 >>> A = coo_array([[1, 2], [3, 4]])
717 >>> B = coo_array([[5], [6]])
718 >>> C = coo_array([[7]])
719 >>> bmat([[A, B], [None, C]]).toarray()
720 array([[1, 2, 5],
721 [3, 4, 6],
722 [0, 0, 7]])
724 >>> bmat([[A, None], [None, C]]).toarray()
725 array([[1, 2, 0],
726 [3, 4, 0],
727 [0, 0, 7]])
729 """
730 blocks = np.asarray(blocks, dtype='object')
731 if any(isinstance(b, sparray) for b in blocks.flat):
732 return _block(blocks, format, dtype)
733 else:
734 return _block(blocks, format, dtype, return_spmatrix=True)
737def block(blocks, *, format=None, dtype=None):
738 """
739 Build a sparse array from sparse sub-blocks
741 Parameters
742 ----------
743 blocks : array_like
744 Grid of sparse arrays with compatible shapes.
745 An entry of None implies an all-zero array.
746 format : {'bsr', 'coo', 'csc', 'csr', 'dia', 'dok', 'lil'}, optional
747 The sparse format of the result (e.g. "csr"). By default an
748 appropriate sparse array format is returned.
749 This choice is subject to change.
750 dtype : dtype, optional
751 The data-type of the output array. If not given, the dtype is
752 determined from that of `blocks`.
754 Returns
755 -------
756 block : sparse array
758 See Also
759 --------
760 block_diag : specify blocks along the main diagonals
761 diags : specify (possibly offset) diagonals
763 Examples
764 --------
765 >>> from scipy.sparse import coo_array, block
766 >>> A = coo_array([[1, 2], [3, 4]])
767 >>> B = coo_array([[5], [6]])
768 >>> C = coo_array([[7]])
769 >>> block([[A, B], [None, C]]).toarray()
770 array([[1, 2, 5],
771 [3, 4, 6],
772 [0, 0, 7]])
774 >>> block([[A, None], [None, C]]).toarray()
775 array([[1, 2, 0],
776 [3, 4, 0],
777 [0, 0, 7]])
779 """
780 return _block(blocks, format, dtype)
783def _block(blocks, format, dtype, return_spmatrix=False):
784 blocks = np.asarray(blocks, dtype='object')
786 if blocks.ndim != 2:
787 raise ValueError('blocks must be 2-D')
789 M,N = blocks.shape
791 # check for fast path cases
792 if (format in (None, 'csr') and
793 all(issparse(b) and b.format == 'csr' for b in blocks.flat)
794 ):
795 if N > 1:
796 # stack along columns (axis 1): must have shape (M, 1)
797 blocks = [[_stack_along_minor_axis(blocks[b, :], 1)] for b in range(M)]
798 blocks = np.asarray(blocks, dtype='object')
800 # stack along rows (axis 0):
801 A = _compressed_sparse_stack(blocks[:, 0], 0, return_spmatrix)
802 if dtype is not None:
803 A = A.astype(dtype)
804 return A
805 elif (format in (None, 'csc') and
806 all(issparse(b) and b.format == 'csc' for b in blocks.flat)
807 ):
808 if M > 1:
809 # stack along rows (axis 0): must have shape (1, N)
810 blocks = [[_stack_along_minor_axis(blocks[:, b], 0) for b in range(N)]]
811 blocks = np.asarray(blocks, dtype='object')
813 # stack along columns (axis 1):
814 A = _compressed_sparse_stack(blocks[0, :], 1, return_spmatrix)
815 if dtype is not None:
816 A = A.astype(dtype)
817 return A
819 block_mask = np.zeros(blocks.shape, dtype=bool)
820 brow_lengths = np.zeros(M, dtype=np.int64)
821 bcol_lengths = np.zeros(N, dtype=np.int64)
823 # convert everything to COO format
824 for i in range(M):
825 for j in range(N):
826 if blocks[i,j] is not None:
827 A = coo_array(blocks[i,j])
828 blocks[i,j] = A
829 block_mask[i,j] = True
831 if brow_lengths[i] == 0:
832 brow_lengths[i] = A.shape[0]
833 elif brow_lengths[i] != A.shape[0]:
834 msg = (f'blocks[{i},:] has incompatible row dimensions. '
835 f'Got blocks[{i},{j}].shape[0] == {A.shape[0]}, '
836 f'expected {brow_lengths[i]}.')
837 raise ValueError(msg)
839 if bcol_lengths[j] == 0:
840 bcol_lengths[j] = A.shape[1]
841 elif bcol_lengths[j] != A.shape[1]:
842 msg = (f'blocks[:,{j}] has incompatible column '
843 f'dimensions. '
844 f'Got blocks[{i},{j}].shape[1] == {A.shape[1]}, '
845 f'expected {bcol_lengths[j]}.')
846 raise ValueError(msg)
848 nnz = sum(block.nnz for block in blocks[block_mask])
849 if dtype is None:
850 all_dtypes = [blk.dtype for blk in blocks[block_mask]]
851 dtype = upcast(*all_dtypes) if all_dtypes else None
853 row_offsets = np.append(0, np.cumsum(brow_lengths))
854 col_offsets = np.append(0, np.cumsum(bcol_lengths))
856 shape = (row_offsets[-1], col_offsets[-1])
858 data = np.empty(nnz, dtype=dtype)
859 idx_dtype = get_index_dtype(maxval=max(shape))
860 row = np.empty(nnz, dtype=idx_dtype)
861 col = np.empty(nnz, dtype=idx_dtype)
863 nnz = 0
864 ii, jj = np.nonzero(block_mask)
865 for i, j in zip(ii, jj):
866 B = blocks[i, j]
867 idx = slice(nnz, nnz + B.nnz)
868 data[idx] = B.data
869 np.add(B.row, row_offsets[i], out=row[idx], dtype=idx_dtype)
870 np.add(B.col, col_offsets[j], out=col[idx], dtype=idx_dtype)
871 nnz += B.nnz
873 if return_spmatrix:
874 return coo_matrix((data, (row, col)), shape=shape).asformat(format)
875 return coo_array((data, (row, col)), shape=shape).asformat(format)
878def block_diag(mats, format=None, dtype=None):
879 """
880 Build a block diagonal sparse matrix or array from provided matrices.
882 Parameters
883 ----------
884 mats : sequence of matrices or arrays
885 Input matrices or arrays.
886 format : str, optional
887 The sparse format of the result (e.g., "csr"). If not given, the result
888 is returned in "coo" format.
889 dtype : dtype specifier, optional
890 The data-type of the output. If not given, the dtype is
891 determined from that of `blocks`.
893 Returns
894 -------
895 res : sparse matrix or array
896 If at least one input is a sparse array, the output is a sparse array.
897 Otherwise the output is a sparse matrix.
899 Notes
900 -----
902 .. versionadded:: 0.11.0
904 See Also
905 --------
906 block, diags
908 Examples
909 --------
910 >>> from scipy.sparse import coo_array, block_diag
911 >>> A = coo_array([[1, 2], [3, 4]])
912 >>> B = coo_array([[5], [6]])
913 >>> C = coo_array([[7]])
914 >>> block_diag((A, B, C)).toarray()
915 array([[1, 2, 0, 0],
916 [3, 4, 0, 0],
917 [0, 0, 5, 0],
918 [0, 0, 6, 0],
919 [0, 0, 0, 7]])
921 """
922 if any(isinstance(a, sparray) for a in mats):
923 container = coo_array
924 else:
925 container = coo_matrix
927 row = []
928 col = []
929 data = []
930 r_idx = 0
931 c_idx = 0
932 for a in mats:
933 if isinstance(a, (list, numbers.Number)):
934 a = coo_array(a)
935 nrows, ncols = a.shape
936 if issparse(a):
937 a = a.tocoo()
938 row.append(a.row + r_idx)
939 col.append(a.col + c_idx)
940 data.append(a.data)
941 else:
942 a_row, a_col = np.divmod(np.arange(nrows*ncols), ncols)
943 row.append(a_row + r_idx)
944 col.append(a_col + c_idx)
945 data.append(a.ravel())
946 r_idx += nrows
947 c_idx += ncols
948 row = np.concatenate(row)
949 col = np.concatenate(col)
950 data = np.concatenate(data)
951 return container((data, (row, col)),
952 shape=(r_idx, c_idx),
953 dtype=dtype).asformat(format)
956def random(m, n, density=0.01, format='coo', dtype=None,
957 random_state=None, data_rvs=None):
958 """Generate a sparse matrix of the given shape and density with randomly
959 distributed values.
961 .. warning::
963 Since numpy 1.17, passing a ``np.random.Generator`` (e.g.
964 ``np.random.default_rng``) for ``random_state`` will lead to much
965 faster execution times.
967 A much slower implementation is used by default for backwards
968 compatibility.
970 Parameters
971 ----------
972 m, n : int
973 shape of the matrix
974 density : real, optional
975 density of the generated matrix: density equal to one means a full
976 matrix, density of 0 means a matrix with no non-zero items.
977 format : str, optional
978 sparse matrix format.
979 dtype : dtype, optional
980 type of the returned matrix values.
981 random_state : {None, int, `numpy.random.Generator`,
982 `numpy.random.RandomState`}, optional
984 - If `seed` is None (or `np.random`), the `numpy.random.RandomState`
985 singleton is used.
986 - If `seed` is an int, a new ``RandomState`` instance is used,
987 seeded with `seed`.
988 - If `seed` is already a ``Generator`` or ``RandomState`` instance then
989 that instance is used.
991 This random state will be used for sampling the sparsity structure, but
992 not necessarily for sampling the values of the structurally nonzero
993 entries of the matrix.
994 data_rvs : callable, optional
995 Samples a requested number of random values.
996 This function should take a single argument specifying the length
997 of the ndarray that it will return. The structurally nonzero entries
998 of the sparse random matrix will be taken from the array sampled
999 by this function. By default, uniform [0, 1) random values will be
1000 sampled using the same random state as is used for sampling
1001 the sparsity structure.
1003 Returns
1004 -------
1005 res : sparse matrix
1007 Examples
1008 --------
1010 Passing a ``np.random.Generator`` instance for better performance:
1012 >>> from scipy.sparse import random
1013 >>> from scipy import stats
1014 >>> from numpy.random import default_rng
1015 >>> rng = default_rng()
1016 >>> S = random(3, 4, density=0.25, random_state=rng)
1018 Proving a sampler for the values:
1020 >>> rvs = stats.poisson(25, loc=10).rvs
1021 >>> S = random(3, 4, density=0.25, random_state=rng, data_rvs=rvs)
1022 >>> S.A
1023 array([[ 36., 0., 33., 0.], # random
1024 [ 0., 0., 0., 0.],
1025 [ 0., 0., 36., 0.]])
1027 Using a custom distribution:
1029 >>> class CustomDistribution(stats.rv_continuous):
1030 ... def _rvs(self, size=None, random_state=None):
1031 ... return random_state.standard_normal(size)
1032 >>> X = CustomDistribution(seed=rng)
1033 >>> Y = X() # get a frozen version of the distribution
1034 >>> S = random(3, 4, density=0.25, random_state=rng, data_rvs=Y.rvs)
1035 >>> S.A
1036 array([[ 0. , 0. , 0. , 0. ], # random
1037 [ 0.13569738, 1.9467163 , -0.81205367, 0. ],
1038 [ 0. , 0. , 0. , 0. ]])
1039 """
1040 if density < 0 or density > 1:
1041 raise ValueError("density expected to be 0 <= density <= 1")
1042 dtype = np.dtype(dtype)
1044 mn = m * n
1046 tp = np.intc
1047 if mn > np.iinfo(tp).max:
1048 tp = np.int64
1050 if mn > np.iinfo(tp).max:
1051 msg = """\
1052Trying to generate a random sparse matrix such as the product of dimensions is
1053greater than %d - this is not supported on this machine
1054"""
1055 raise ValueError(msg % np.iinfo(tp).max)
1057 # Number of non zero values
1058 k = int(round(density * m * n))
1060 random_state = check_random_state(random_state)
1062 if data_rvs is None:
1063 if np.issubdtype(dtype, np.integer):
1064 def data_rvs(n):
1065 return rng_integers(random_state,
1066 np.iinfo(dtype).min,
1067 np.iinfo(dtype).max,
1068 n,
1069 dtype=dtype)
1070 elif np.issubdtype(dtype, np.complexfloating):
1071 def data_rvs(n):
1072 return (random_state.uniform(size=n) +
1073 random_state.uniform(size=n) * 1j)
1074 else:
1075 data_rvs = partial(random_state.uniform, 0., 1.)
1077 ind = random_state.choice(mn, size=k, replace=False)
1079 j = np.floor(ind * 1. / m).astype(tp, copy=False)
1080 i = (ind - j * m).astype(tp, copy=False)
1081 vals = data_rvs(k).astype(dtype, copy=False)
1082 return coo_matrix((vals, (i, j)), shape=(m, n)).asformat(format,
1083 copy=False)
1086def rand(m, n, density=0.01, format="coo", dtype=None, random_state=None):
1087 """Generate a sparse matrix of the given shape and density with uniformly
1088 distributed values.
1090 Parameters
1091 ----------
1092 m, n : int
1093 shape of the matrix
1094 density : real, optional
1095 density of the generated matrix: density equal to one means a full
1096 matrix, density of 0 means a matrix with no non-zero items.
1097 format : str, optional
1098 sparse matrix format.
1099 dtype : dtype, optional
1100 type of the returned matrix values.
1101 random_state : {None, int, `numpy.random.Generator`,
1102 `numpy.random.RandomState`}, optional
1104 If `seed` is None (or `np.random`), the `numpy.random.RandomState`
1105 singleton is used.
1106 If `seed` is an int, a new ``RandomState`` instance is used,
1107 seeded with `seed`.
1108 If `seed` is already a ``Generator`` or ``RandomState`` instance then
1109 that instance is used.
1111 Returns
1112 -------
1113 res : sparse matrix
1115 Notes
1116 -----
1117 Only float types are supported for now.
1119 See Also
1120 --------
1121 scipy.sparse.random : Similar function that allows a user-specified random
1122 data source.
1124 Examples
1125 --------
1126 >>> from scipy.sparse import rand
1127 >>> matrix = rand(3, 4, density=0.25, format="csr", random_state=42)
1128 >>> matrix
1129 <3x4 sparse matrix of type '<class 'numpy.float64'>'
1130 with 3 stored elements in Compressed Sparse Row format>
1131 >>> matrix.toarray()
1132 array([[0.05641158, 0. , 0. , 0.65088847],
1133 [0. , 0. , 0. , 0.14286682],
1134 [0. , 0. , 0. , 0. ]])
1136 """
1137 return random(m, n, density, format, dtype, random_state)