_dia.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653
  1. """Sparse DIAgonal format"""
  2. __docformat__ = "restructuredtext en"
  3. __all__ = ['dia_array', 'dia_matrix', 'isspmatrix_dia']
  4. import numpy as np
  5. from .._lib._util import _prune_array, copy_if_needed
  6. from ._matrix import spmatrix
  7. from ._base import issparse, _formats, _spbase, sparray
  8. from ._data import _data_matrix
  9. from ._sputils import (
  10. isdense, isscalarlike, isshape, upcast_char, getdtype, get_sum_dtype,
  11. validateaxis, check_shape
  12. )
  13. from ._sparsetools import dia_matmat, dia_matvec, dia_matvecs, dia_tocsr
  14. class _dia_base(_data_matrix):
  15. _format = 'dia'
  16. def __init__(self, arg1, shape=None, dtype=None, copy=False, *, maxprint=None):
  17. _data_matrix.__init__(self, arg1, maxprint=maxprint)
  18. if issparse(arg1):
  19. if arg1.format == "dia":
  20. if copy:
  21. arg1 = arg1.copy()
  22. self.data = arg1.data
  23. self.offsets = arg1.offsets
  24. self._shape = check_shape(arg1.shape)
  25. else:
  26. if arg1.format == self.format and copy:
  27. A = arg1.copy()
  28. else:
  29. A = arg1.todia()
  30. self.data = A.data
  31. self.offsets = A.offsets
  32. self._shape = check_shape(A.shape)
  33. elif isinstance(arg1, tuple):
  34. if isshape(arg1):
  35. # It's a tuple of matrix dimensions (M, N)
  36. # create empty matrix
  37. self._shape = check_shape(arg1)
  38. self.data = np.zeros((0,0), getdtype(dtype, default=float))
  39. idx_dtype = self._get_index_dtype(maxval=max(self.shape))
  40. self.offsets = np.zeros((0), dtype=idx_dtype)
  41. else:
  42. try:
  43. # Try interpreting it as (data, offsets)
  44. data, offsets = arg1
  45. except Exception as e:
  46. message = 'unrecognized form for dia_array constructor'
  47. raise ValueError(message) from e
  48. else:
  49. if shape is None:
  50. raise ValueError('expected a shape argument')
  51. if not copy:
  52. copy = copy_if_needed
  53. self.data = np.atleast_2d(np.array(arg1[0], dtype=dtype, copy=copy))
  54. offsets = np.array(arg1[1],
  55. dtype=self._get_index_dtype(maxval=max(shape)),
  56. copy=copy)
  57. self.offsets = np.atleast_1d(offsets)
  58. self._shape = check_shape(shape)
  59. else:
  60. # must be dense, convert to COO first, then to DIA
  61. try:
  62. arg1 = np.asarray(arg1)
  63. except Exception as e:
  64. raise ValueError("unrecognized form for "
  65. f"{self.format}_matrix constructor") from e
  66. if isinstance(self, sparray) and arg1.ndim != 2:
  67. raise ValueError(f"DIA arrays don't support {arg1.ndim}D input. Use 2D")
  68. A = self._coo_container(arg1, dtype=dtype, shape=shape).todia()
  69. self.data = A.data
  70. self.offsets = A.offsets
  71. self._shape = check_shape(A.shape)
  72. if dtype is not None:
  73. newdtype = getdtype(dtype)
  74. self.data = self.data.astype(newdtype, copy=False)
  75. # check format
  76. if self.offsets.ndim != 1:
  77. raise ValueError('offsets array must have rank 1')
  78. if self.data.ndim != 2:
  79. raise ValueError('data array must have rank 2')
  80. if self.data.shape[0] != len(self.offsets):
  81. raise ValueError(
  82. f'number of diagonals ({self.data.shape[0]}) does not match the number '
  83. f'of offsets ({len(self.offsets)})'
  84. )
  85. if len(np.unique(self.offsets)) != len(self.offsets):
  86. raise ValueError('offset array contains duplicate values')
  87. def __repr__(self):
  88. _, fmt = _formats[self.format]
  89. sparse_cls = 'array' if isinstance(self, sparray) else 'matrix'
  90. d = self.data.shape[0]
  91. return (
  92. f"<{fmt} sparse {sparse_cls} of dtype '{self.dtype}'\n"
  93. f"\twith {self.nnz} stored elements ({d} diagonals) and shape {self.shape}>"
  94. )
  95. def _data_mask(self):
  96. """Returns a mask of the same shape as self.data, where
  97. mask[i,j] is True when data[i,j] corresponds to a stored element."""
  98. num_rows, num_cols = self.shape
  99. offset_inds = np.arange(self.data.shape[1])
  100. row = offset_inds - self.offsets[:,None]
  101. mask = (row >= 0)
  102. mask &= (row < num_rows)
  103. mask &= (offset_inds < num_cols)
  104. return mask
  105. def count_nonzero(self, axis=None):
  106. if axis is not None:
  107. raise NotImplementedError(
  108. "count_nonzero over an axis is not implemented for DIA format"
  109. )
  110. mask = self._data_mask()
  111. return np.count_nonzero(self.data[mask])
  112. count_nonzero.__doc__ = _spbase.count_nonzero.__doc__
  113. def _getnnz(self, axis=None):
  114. if axis is not None:
  115. raise NotImplementedError("_getnnz over an axis is not implemented "
  116. "for DIA format")
  117. M, N = self.shape
  118. L = min(self.data.shape[1], N)
  119. return int(np.maximum(np.minimum(M + self.offsets, L) -
  120. np.maximum(self.offsets, 0),
  121. 0).sum())
  122. _getnnz.__doc__ = _spbase._getnnz.__doc__
  123. def sum(self, axis=None, dtype=None, out=None):
  124. axis = validateaxis(axis)
  125. res_dtype = get_sum_dtype(self.dtype)
  126. num_rows, num_cols = self.shape
  127. ret = None
  128. if axis == (0,):
  129. mask = self._data_mask()
  130. x = (self.data * mask).sum(axis=0)
  131. if x.shape[0] == num_cols:
  132. res = x
  133. else:
  134. res = np.zeros(num_cols, dtype=x.dtype)
  135. res[:x.shape[0]] = x
  136. ret = self._ascontainer(res, dtype=res_dtype)
  137. else: # axis is None or (1,)
  138. row_sums = np.zeros((num_rows, 1), dtype=res_dtype)
  139. one = np.ones(num_cols, dtype=res_dtype)
  140. dia_matvec(num_rows, num_cols, len(self.offsets),
  141. self.data.shape[1], self.offsets, self.data, one, row_sums)
  142. row_sums = self._ascontainer(row_sums)
  143. if axis is None:
  144. return row_sums.sum(dtype=dtype, out=out)
  145. ret = self._ascontainer(row_sums.sum(axis=axis))
  146. return ret.sum(axis=(), dtype=dtype, out=out)
  147. sum.__doc__ = _spbase.sum.__doc__
  148. def _add_sparse(self, other, sub=False):
  149. # If other is not DIA format, let them handle us instead.
  150. if not isinstance(other, _dia_base):
  151. return other._add_sparse(self)
  152. # Fast path for exact equality of the sparsity structure.
  153. if np.array_equal(self.offsets, other.offsets):
  154. return self._with_data(self.data - other.data if sub else
  155. self.data + other.data)
  156. # Find the union of the offsets (which will be sorted and unique).
  157. new_offsets = np.union1d(self.offsets, other.offsets)
  158. self_idx = np.searchsorted(new_offsets, self.offsets)
  159. other_idx = np.searchsorted(new_offsets, other.offsets)
  160. self_d = self.data.shape[1]
  161. other_d = other.data.shape[1]
  162. # Fast path for a sparsity structure where the final offsets are a
  163. # permutation of the existing offsets and the diagonal lengths match.
  164. if self_d == other_d and len(new_offsets) == len(self.offsets):
  165. new_data = self.data[_invert_index(self_idx)]
  166. if sub:
  167. new_data[other_idx, :] -= other.data
  168. else:
  169. new_data[other_idx, :] += other.data
  170. elif self_d == other_d and len(new_offsets) == len(other.offsets):
  171. if sub:
  172. new_data = -other.data[_invert_index(other_idx)]
  173. else:
  174. new_data = other.data[_invert_index(other_idx)]
  175. new_data[self_idx, :] += self.data
  176. else:
  177. # Maximum diagonal length of the result.
  178. d = min(self.shape[0] + new_offsets[-1], self.shape[1])
  179. # Add all diagonals to a freshly allocated data array.
  180. new_data = np.zeros(
  181. (len(new_offsets), d),
  182. dtype=np.result_type(self.data, other.data),
  183. )
  184. new_data[self_idx, :self_d] += self.data[:, :d]
  185. if sub:
  186. new_data[other_idx, :other_d] -= other.data[:, :d]
  187. else:
  188. new_data[other_idx, :other_d] += other.data[:, :d]
  189. return self._dia_container((new_data, new_offsets), shape=self.shape)
  190. def _sub_sparse(self, other):
  191. # If other is not DIA format, use default handler.
  192. if not isinstance(other, _dia_base):
  193. return super()._sub_sparse(other)
  194. return self._add_sparse(other, sub=True)
  195. def _mul_scalar(self, other):
  196. return self._with_data(self.data * other)
  197. def multiply(self, other):
  198. if isscalarlike(other):
  199. return self._mul_scalar(other)
  200. if isdense(other):
  201. if other.ndim > 2:
  202. return self.toarray() * other
  203. # Use default handler for pathological cases.
  204. if 0 in self.shape or 1 in self.shape or 0 in other.shape:
  205. return super().multiply(other)
  206. other = np.atleast_2d(other)
  207. other_rows, other_cols = other.shape
  208. rows, cols = self.shape
  209. L = min(self.data.shape[1], cols)
  210. data = self.data[:, :L].astype(np.result_type(self.data, other)) # copy
  211. if other_rows == 1:
  212. data *= other[0, :L]
  213. elif other_rows != rows:
  214. raise ValueError('inconsistent shapes')
  215. else:
  216. j = np.arange(L)
  217. if L > rows:
  218. i = (j - self.offsets[:, None]) % rows
  219. else: # can use faster method
  220. i = j - self.offsets[:, None] % rows
  221. if other_cols == 1:
  222. j = 0
  223. elif other_cols != cols:
  224. raise ValueError('inconsistent shapes')
  225. data *= other[i, j]
  226. return self._with_data(data)
  227. # If other is not DIA format or needs broadcasting (unreasonable
  228. # use case for DIA anyway), use default handler.
  229. if not isinstance(other, _dia_base) or other.shape != self.shape:
  230. return super().multiply(other)
  231. # Find common offsets (unique diagonals don't contribute)
  232. # and indices corresponding to them in multiplicand and multiplier.
  233. offsets, self_idx, other_idx = \
  234. np.intersect1d(self.offsets, other.offsets,
  235. assume_unique=True, return_indices=True)
  236. # Only overlapping length of diagonals can have non-zero products.
  237. L = min(self.data.shape[1], other.data.shape[1])
  238. data = self.data[self_idx, :L] * other.data[other_idx, :L]
  239. return self._dia_container((data, offsets), shape=self.shape)
  240. def _matmul_vector(self, other):
  241. x = other
  242. y = np.zeros(self.shape[0], dtype=upcast_char(self.dtype.char,
  243. x.dtype.char))
  244. L = self.data.shape[1]
  245. M,N = self.shape
  246. dia_matvec(M,N, len(self.offsets), L, self.offsets, self.data,
  247. x.ravel(), y.ravel())
  248. return y
  249. def _matmul_multivector(self, other):
  250. res = np.zeros((self.shape[0], other.shape[1]),
  251. dtype=np.result_type(self.data, other))
  252. dia_matvecs(*self.shape, *self.data.shape, self.offsets, self.data,
  253. other.shape[1], other, res)
  254. return res
  255. def _matmul_sparse(self, other):
  256. # If other is not DIA format, use default handler.
  257. if not isinstance(other, _dia_base):
  258. return super()._matmul_sparse(other)
  259. # If any dimension is zero, return empty array immediately.
  260. if 0 in self.shape or 0 in other.shape:
  261. return self._dia_container((self.shape[0], other.shape[1]))
  262. offsets, data = dia_matmat(*self.shape, *self.data.shape,
  263. self.offsets, self.data,
  264. other.shape[1], *other.data.shape,
  265. other.offsets, other.data)
  266. return self._dia_container((data.reshape(len(offsets), -1), offsets),
  267. (self.shape[0], other.shape[1]))
  268. def _setdiag(self, values, k=0):
  269. M, N = self.shape
  270. if values.ndim == 0:
  271. # broadcast
  272. values_n = np.inf
  273. else:
  274. values_n = len(values)
  275. if k < 0:
  276. n = min(M + k, N, values_n)
  277. min_index = 0
  278. max_index = n
  279. else:
  280. n = min(M, N - k, values_n)
  281. min_index = k
  282. max_index = k + n
  283. if values.ndim != 0:
  284. # allow also longer sequences
  285. values = values[:n]
  286. data_rows, data_cols = self.data.shape
  287. if k in self.offsets:
  288. if max_index > data_cols:
  289. data = np.zeros((data_rows, max_index), dtype=self.data.dtype)
  290. data[:, :data_cols] = self.data
  291. self.data = data
  292. self.data[self.offsets == k, min_index:max_index] = values
  293. else:
  294. self.offsets = np.append(self.offsets, self.offsets.dtype.type(k))
  295. m = max(max_index, data_cols)
  296. data = np.zeros((data_rows + 1, m), dtype=self.data.dtype)
  297. data[:-1, :data_cols] = self.data
  298. data[-1, min_index:max_index] = values
  299. self.data = data
  300. def todia(self, copy=False):
  301. if copy:
  302. return self.copy()
  303. else:
  304. return self
  305. todia.__doc__ = _spbase.todia.__doc__
  306. def transpose(self, axes=None, copy=False):
  307. if axes is not None and axes != (1, 0):
  308. raise ValueError("Sparse arrays/matrices do not support "
  309. "an 'axes' parameter because swapping "
  310. "dimensions is the only logical permutation.")
  311. num_rows, num_cols = self.shape
  312. max_dim = max(self.shape)
  313. # flip diagonal offsets
  314. offsets = -self.offsets
  315. # re-align the data matrix
  316. r = np.arange(len(offsets), dtype=np.intc)[:, None]
  317. c = np.arange(num_rows, dtype=np.intc) - (offsets % max_dim)[:, None]
  318. pad_amount = max(0, max_dim-self.data.shape[1])
  319. data = np.hstack((self.data, np.zeros((self.data.shape[0], pad_amount),
  320. dtype=self.data.dtype)))
  321. data = data[r, c]
  322. return self._dia_container((data, offsets), shape=(
  323. num_cols, num_rows), copy=copy)
  324. transpose.__doc__ = _spbase.transpose.__doc__
  325. def diagonal(self, k=0):
  326. rows, cols = self.shape
  327. if k <= -rows or k >= cols:
  328. return np.empty(0, dtype=self.data.dtype)
  329. idx, = np.nonzero(self.offsets == k)
  330. first_col = max(0, k)
  331. last_col = min(rows + k, cols)
  332. result_size = last_col - first_col
  333. if idx.size == 0:
  334. return np.zeros(result_size, dtype=self.data.dtype)
  335. result = self.data[idx[0], first_col:last_col]
  336. padding = result_size - len(result)
  337. if padding > 0:
  338. result = np.pad(result, (0, padding), mode='constant')
  339. return result
  340. diagonal.__doc__ = _spbase.diagonal.__doc__
  341. def tocsr(self, copy=False):
  342. if 0 in self.shape or len(self.offsets) == 0:
  343. return self._csr_container(self.shape, dtype=self.dtype)
  344. n_rows, n_cols = self.shape
  345. max_nnz = self.nnz
  346. # np.argsort always returns dtype=int, which can cause automatic dtype
  347. # expansion for everything else even if not needed (see gh19245), but
  348. # CSR wants common dtype for indices, indptr and shape, so care should
  349. # be taken to use appropriate indexing dtype throughout.
  350. idx_dtype = self._get_index_dtype(maxval=max(max_nnz, n_rows, n_cols))
  351. order = np.argsort(self.offsets).astype(idx_dtype, copy=False)
  352. csr_data = np.empty(max_nnz, dtype=self.dtype)
  353. indices = np.empty(max_nnz, dtype=idx_dtype)
  354. indptr = np.empty(1 + n_rows, dtype=idx_dtype)
  355. # Conversion eliminates explicit zeros and returns actual nnz.
  356. nnz = dia_tocsr(n_rows, n_cols, *self.data.shape,
  357. self.offsets.astype(idx_dtype, copy=False), self.data,
  358. order, csr_data, indices, indptr)
  359. # Shrink indexing dtype, if needed, and prune arrays.
  360. idx_dtype = self._get_index_dtype(maxval=max(nnz, n_rows, n_cols))
  361. csr_data = _prune_array(csr_data[:nnz])
  362. indices = _prune_array(indices[:nnz].astype(idx_dtype, copy=False))
  363. indptr = indptr.astype(idx_dtype, copy=False)
  364. out = self._csr_container((csr_data, indices, indptr),
  365. shape=self.shape, dtype=self.dtype)
  366. out.has_canonical_format = True
  367. return out
  368. tocsr.__doc__ = _spbase.tocsr.__doc__
  369. # needed by _data_matrix
  370. def _with_data(self, data, copy=True):
  371. """Returns a matrix with the same sparsity structure as self,
  372. but with different data. By default the structure arrays are copied.
  373. """
  374. if copy:
  375. return self._dia_container(
  376. (data, self.offsets.copy()), shape=self.shape
  377. )
  378. else:
  379. return self._dia_container(
  380. (data, self.offsets), shape=self.shape
  381. )
  382. def resize(self, *shape):
  383. shape = check_shape(shape)
  384. M, N = shape
  385. # we do not need to handle the case of expanding N
  386. self.data = self.data[:, :N]
  387. if (M > self.shape[0] and
  388. np.any(self.offsets + self.shape[0] < self.data.shape[1])):
  389. # explicitly clear values that were previously hidden
  390. mask = (self.offsets[:, None] + self.shape[0] <=
  391. np.arange(self.data.shape[1]))
  392. self.data[mask] = 0
  393. self._shape = shape
  394. resize.__doc__ = _spbase.resize.__doc__
  395. def _invert_index(idx):
  396. """Helper function to invert an index array."""
  397. inv = np.zeros_like(idx)
  398. inv[idx] = np.arange(len(idx))
  399. return inv
  400. def isspmatrix_dia(x):
  401. """Is `x` of dia_matrix type?
  402. Parameters
  403. ----------
  404. x
  405. object to check for being a dia matrix
  406. Returns
  407. -------
  408. bool
  409. True if `x` is a dia matrix, False otherwise
  410. Examples
  411. --------
  412. >>> from scipy.sparse import dia_array, dia_matrix, coo_matrix, isspmatrix_dia
  413. >>> isspmatrix_dia(dia_matrix([[5]]))
  414. True
  415. >>> isspmatrix_dia(dia_array([[5]]))
  416. False
  417. >>> isspmatrix_dia(coo_matrix([[5]]))
  418. False
  419. """
  420. return isinstance(x, dia_matrix)
  421. # This namespace class separates array from matrix with isinstance
  422. class dia_array(_dia_base, sparray):
  423. """
  424. Sparse array with DIAgonal storage.
  425. This can be instantiated in several ways:
  426. dia_array(D)
  427. where D is a 2-D ndarray
  428. dia_array(S)
  429. with another sparse array or matrix S (equivalent to S.todia())
  430. dia_array((M, N), [dtype])
  431. to construct an empty array with shape (M, N),
  432. dtype is optional, defaulting to dtype='d'.
  433. dia_array((data, offsets), shape=(M, N))
  434. where the ``data[k,:]`` stores the diagonal entries for
  435. diagonal ``offsets[k]`` (See example below)
  436. Attributes
  437. ----------
  438. dtype : dtype
  439. Data type of the array
  440. shape : 2-tuple
  441. Shape of the array
  442. ndim : int
  443. Number of dimensions (this is always 2)
  444. nnz
  445. size
  446. data
  447. DIA format data array of the array
  448. offsets
  449. DIA format offset array of the array
  450. T
  451. Notes
  452. -----
  453. Sparse arrays can be used in arithmetic operations: they support
  454. addition, subtraction, multiplication, division, and matrix power.
  455. Sparse arrays with DIAgonal storage do not support slicing.
  456. Examples
  457. --------
  458. >>> import numpy as np
  459. >>> from scipy.sparse import dia_array
  460. >>> dia_array((3, 4), dtype=np.int8).toarray()
  461. array([[0, 0, 0, 0],
  462. [0, 0, 0, 0],
  463. [0, 0, 0, 0]], dtype=int8)
  464. >>> data = np.array([[1, 2, 3, 4]]).repeat(3, axis=0)
  465. >>> offsets = np.array([0, -1, 2])
  466. >>> dia_array((data, offsets), shape=(4, 4)).toarray()
  467. array([[1, 0, 3, 0],
  468. [1, 2, 0, 4],
  469. [0, 2, 3, 0],
  470. [0, 0, 3, 4]])
  471. >>> from scipy.sparse import dia_array
  472. >>> n = 10
  473. >>> ex = np.ones(n)
  474. >>> data = np.array([ex, 2 * ex, ex])
  475. >>> offsets = np.array([-1, 0, 1])
  476. >>> dia_array((data, offsets), shape=(n, n)).toarray()
  477. array([[2., 1., 0., ..., 0., 0., 0.],
  478. [1., 2., 1., ..., 0., 0., 0.],
  479. [0., 1., 2., ..., 0., 0., 0.],
  480. ...,
  481. [0., 0., 0., ..., 2., 1., 0.],
  482. [0., 0., 0., ..., 1., 2., 1.],
  483. [0., 0., 0., ..., 0., 1., 2.]])
  484. """
  485. class dia_matrix(spmatrix, _dia_base):
  486. """
  487. Sparse matrix with DIAgonal storage.
  488. This can be instantiated in several ways:
  489. dia_matrix(D)
  490. where D is a 2-D ndarray
  491. dia_matrix(S)
  492. with another sparse array or matrix S (equivalent to S.todia())
  493. dia_matrix((M, N), [dtype])
  494. to construct an empty matrix with shape (M, N),
  495. dtype is optional, defaulting to dtype='d'.
  496. dia_matrix((data, offsets), shape=(M, N))
  497. where the ``data[k,:]`` stores the diagonal entries for
  498. diagonal ``offsets[k]`` (See example below)
  499. Attributes
  500. ----------
  501. dtype : dtype
  502. Data type of the matrix
  503. shape : 2-tuple
  504. Shape of the matrix
  505. ndim : int
  506. Number of dimensions (this is always 2)
  507. nnz
  508. size
  509. data
  510. DIA format data array of the matrix
  511. offsets
  512. DIA format offset array of the matrix
  513. T
  514. Notes
  515. -----
  516. Sparse matrices can be used in arithmetic operations: they support
  517. addition, subtraction, multiplication, division, and matrix power.
  518. Sparse matrices with DIAgonal storage do not support slicing.
  519. Examples
  520. --------
  521. >>> import numpy as np
  522. >>> from scipy.sparse import dia_matrix
  523. >>> dia_matrix((3, 4), dtype=np.int8).toarray()
  524. array([[0, 0, 0, 0],
  525. [0, 0, 0, 0],
  526. [0, 0, 0, 0]], dtype=int8)
  527. >>> data = np.array([[1, 2, 3, 4]]).repeat(3, axis=0)
  528. >>> offsets = np.array([0, -1, 2])
  529. >>> dia_matrix((data, offsets), shape=(4, 4)).toarray()
  530. array([[1, 0, 3, 0],
  531. [1, 2, 0, 4],
  532. [0, 2, 3, 0],
  533. [0, 0, 3, 4]])
  534. >>> from scipy.sparse import dia_matrix
  535. >>> n = 10
  536. >>> ex = np.ones(n)
  537. >>> data = np.array([ex, 2 * ex, ex])
  538. >>> offsets = np.array([-1, 0, 1])
  539. >>> dia_matrix((data, offsets), shape=(n, n)).toarray()
  540. array([[2., 1., 0., ..., 0., 0., 0.],
  541. [1., 2., 1., ..., 0., 0., 0.],
  542. [0., 1., 2., ..., 0., 0., 0.],
  543. ...,
  544. [0., 0., 0., ..., 2., 1., 0.],
  545. [0., 0., 0., ..., 1., 2., 1.],
  546. [0., 0., 0., ..., 0., 1., 2.]])
  547. """