_dok.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  1. """Dictionary Of Keys based matrix"""
  2. __docformat__ = "restructuredtext en"
  3. __all__ = ['dok_array', 'dok_matrix', 'isspmatrix_dok']
  4. import itertools
  5. import numpy as np
  6. from ._matrix import spmatrix
  7. from ._base import _spbase, sparray, issparse
  8. from ._index import IndexMixin
  9. from ._sputils import (isdense, getdtype, isshape, isintlike, isscalarlike,
  10. upcast, upcast_scalar, check_shape)
  11. class _dok_base(_spbase, IndexMixin, dict):
  12. _format = 'dok'
  13. _allow_nd = (1, 2)
  14. def __init__(self, arg1, shape=None, dtype=None, copy=False, *, maxprint=None):
  15. _spbase.__init__(self, arg1, maxprint=maxprint)
  16. if isinstance(arg1, tuple) and isshape(arg1, allow_nd=self._allow_nd):
  17. self._shape = check_shape(arg1, allow_nd=self._allow_nd)
  18. self._dict = {}
  19. self.dtype = getdtype(dtype, default=float)
  20. elif issparse(arg1): # Sparse ctor
  21. if arg1.format == self.format:
  22. arg1 = arg1.copy() if copy else arg1
  23. else:
  24. arg1 = arg1.todok()
  25. if dtype is not None:
  26. arg1 = arg1.astype(dtype, copy=False)
  27. self._dict = arg1._dict
  28. self._shape = check_shape(arg1.shape, allow_nd=self._allow_nd)
  29. self.dtype = getdtype(arg1.dtype)
  30. else: # Dense ctor
  31. try:
  32. arg1 = np.asarray(arg1)
  33. except Exception as e:
  34. raise TypeError('Invalid input format.') from e
  35. if arg1.ndim > 2:
  36. raise ValueError(f"DOK arrays don't yet support {arg1.ndim}D input.")
  37. if arg1.ndim == 1:
  38. if dtype is not None:
  39. arg1 = arg1.astype(dtype, copy=False)
  40. self._dict = {i: v for i, v in enumerate(arg1) if v != 0}
  41. self.dtype = getdtype(arg1.dtype)
  42. else:
  43. d = self._coo_container(arg1, shape=shape, dtype=dtype).todok()
  44. self._dict = d._dict
  45. self.dtype = getdtype(d.dtype)
  46. self._shape = check_shape(arg1.shape, allow_nd=self._allow_nd)
  47. def update(self, val):
  48. """Update values from a dict, sparse dok or iterable of 2-tuples like .items()
  49. Keys of the input must be sequences of nonnegative integers less than the shape
  50. for each axis.
  51. """
  52. if isinstance(val, dict):
  53. inputs = val.items()
  54. else:
  55. inputs = val
  56. for key, value in inputs:
  57. index = (key,) if isintlike(key) else tuple(key)
  58. if len(index) != self.ndim:
  59. raise IndexError(f'Index {key} length needs to match self.shape')
  60. if not all(
  61. isintlike(idx) and 0 <= idx < max_idx
  62. for idx, max_idx in zip(index, self.shape)
  63. ):
  64. # Error handling. Re-search to find which error occured
  65. for idx, max_idx in zip(index, self.shape):
  66. if not isintlike(idx):
  67. raise IndexError(f'integer keys required for update. Got {key}')
  68. if idx < 0:
  69. raise IndexError(f'negative index {key} not allowed in update')
  70. if idx >= max_idx:
  71. raise IndexError(f'index {key} is too large for self.shape')
  72. # do the update
  73. self._dict.update(inputs)
  74. def _getnnz(self, axis=None):
  75. if axis is not None:
  76. raise NotImplementedError(
  77. "_getnnz over an axis is not implemented for DOK format."
  78. )
  79. return len(self._dict)
  80. def count_nonzero(self, axis=None):
  81. if axis is not None:
  82. raise NotImplementedError(
  83. "count_nonzero over an axis is not implemented for DOK format."
  84. )
  85. return sum(x != 0 for x in self.values())
  86. _getnnz.__doc__ = _spbase._getnnz.__doc__
  87. count_nonzero.__doc__ = _spbase.count_nonzero.__doc__
  88. def __len__(self):
  89. return len(self._dict)
  90. def __contains__(self, key):
  91. return key in self._dict
  92. def setdefault(self, key, default=None, /):
  93. return self._dict.setdefault(key, default)
  94. def __delitem__(self, key, /):
  95. del self._dict[key]
  96. def clear(self):
  97. return self._dict.clear()
  98. def pop(self, /, *args):
  99. return self._dict.pop(*args)
  100. def __reversed__(self):
  101. raise TypeError("reversed is not defined for dok_array type")
  102. def __or__(self, other):
  103. type_names = f"{type(self).__name__} and {type(other).__name__}"
  104. raise TypeError(f"unsupported operand type for |: {type_names}")
  105. def __ror__(self, other):
  106. type_names = f"{type(self).__name__} and {type(other).__name__}"
  107. raise TypeError(f"unsupported operand type for |: {type_names}")
  108. def __ior__(self, other):
  109. type_names = f"{type(self).__name__} and {type(other).__name__}"
  110. raise TypeError(f"unsupported operand type for |: {type_names}")
  111. def popitem(self):
  112. return self._dict.popitem()
  113. def items(self):
  114. return self._dict.items()
  115. def keys(self):
  116. return self._dict.keys()
  117. def values(self):
  118. return self._dict.values()
  119. def get(self, key, default=0.0):
  120. """This provides dict.get method functionality with type checking"""
  121. if key in self._dict:
  122. return self._dict[key]
  123. if isintlike(key) and self.ndim == 1:
  124. key = (key,)
  125. if self.ndim != len(key):
  126. raise IndexError(f'Index {key} length needs to match self.shape')
  127. try:
  128. for i in key:
  129. assert isintlike(i)
  130. except (AssertionError, TypeError, ValueError) as e:
  131. raise IndexError('Index must be or consist of integers.') from e
  132. key = tuple(i + M if i < 0 else i for i, M in zip(key, self.shape))
  133. if any(i < 0 or i >= M for i, M in zip(key, self.shape)):
  134. raise IndexError('Index out of bounds.')
  135. if self.ndim == 1:
  136. key = key[0]
  137. return self._dict.get(key, default)
  138. # 1D get methods
  139. def _get_int(self, idx):
  140. return self._dict.get(idx, self.dtype.type(0))
  141. def _get_slice(self, idx):
  142. i_range = range(*idx.indices(self.shape[0]))
  143. return self._get_array(list(i_range))
  144. def _get_array(self, idx):
  145. idx = np.asarray(idx)
  146. if idx.ndim == 0:
  147. val = self._dict.get(int(idx), self.dtype.type(0))
  148. return np.array(val, stype=self.dtype)
  149. new_dok = self._dok_container(idx.shape, dtype=self.dtype)
  150. dok_vals = [self._dict.get(i, 0) for i in idx.ravel()]
  151. if dok_vals:
  152. if len(idx.shape) == 1:
  153. for i, v in enumerate(dok_vals):
  154. if v:
  155. new_dok._dict[i] = v
  156. else:
  157. new_idx = np.unravel_index(np.arange(len(dok_vals)), idx.shape)
  158. new_idx = new_idx[0] if len(new_idx) == 1 else zip(*new_idx)
  159. for i, v in zip(new_idx, dok_vals, strict=True):
  160. if v:
  161. new_dok._dict[i] = v
  162. return new_dok
  163. # 2D get methods
  164. def _get_intXint(self, row, col):
  165. return self._dict.get((row, col), self.dtype.type(0))
  166. def _get_intXslice(self, row, col):
  167. return self._get_sliceXslice(slice(row, row + 1), col)
  168. def _get_sliceXint(self, row, col):
  169. return self._get_sliceXslice(row, slice(col, col + 1))
  170. def _get_sliceXslice(self, row, col):
  171. row_start, row_stop, row_step = row.indices(self.shape[0])
  172. col_start, col_stop, col_step = col.indices(self.shape[1])
  173. row_range = range(row_start, row_stop, row_step)
  174. col_range = range(col_start, col_stop, col_step)
  175. shape = (len(row_range), len(col_range))
  176. # Switch paths only when advantageous
  177. # (count the iterations in the loops, adjust for complexity)
  178. if len(self) >= 2 * shape[0] * shape[1]:
  179. # O(nr*nc) path: loop over <row x col>
  180. return self._get_columnXarray(row_range, col_range)
  181. # O(nnz) path: loop over entries of self
  182. newdok = self._dok_container(shape, dtype=self.dtype)
  183. for key in self.keys():
  184. i, ri = divmod(int(key[0]) - row_start, row_step)
  185. if ri != 0 or i < 0 or i >= shape[0]:
  186. continue
  187. j, rj = divmod(int(key[1]) - col_start, col_step)
  188. if rj != 0 or j < 0 or j >= shape[1]:
  189. continue
  190. newdok._dict[i, j] = self._dict[key]
  191. return newdok
  192. def _get_intXarray(self, row, col):
  193. return self._get_columnXarray([row], col.ravel())
  194. def _get_arrayXint(self, row, col):
  195. res = self._get_columnXarray(row.ravel(), [col])
  196. if row.ndim > 1:
  197. return res.reshape(row.shape)
  198. return res
  199. def _get_sliceXarray(self, row, col):
  200. row = list(range(*row.indices(self.shape[0])))
  201. return self._get_columnXarray(row, col)
  202. def _get_arrayXslice(self, row, col):
  203. col = list(range(*col.indices(self.shape[1])))
  204. return self._get_columnXarray(row, col)
  205. def _get_columnXarray(self, row, col):
  206. # outer indexing
  207. newdok = self._dok_container((len(row), len(col)), dtype=self.dtype)
  208. for i, r in enumerate(row):
  209. for j, c in enumerate(col):
  210. v = self._dict.get((r, c), 0)
  211. if v:
  212. newdok._dict[i, j] = v
  213. return newdok
  214. def _get_arrayXarray(self, row, col):
  215. # inner indexing
  216. i, j = map(np.atleast_2d, np.broadcast_arrays(row, col))
  217. newdok = self._dok_container(i.shape, dtype=self.dtype)
  218. for key in itertools.product(range(i.shape[0]), range(i.shape[1])):
  219. v = self._dict.get((i[key], j[key]), 0)
  220. if v:
  221. newdok._dict[key] = v
  222. return newdok
  223. # 1D set methods
  224. def _set_int(self, idx, x):
  225. if x:
  226. self._dict[idx] = x
  227. elif idx in self._dict:
  228. del self._dict[idx]
  229. def _set_array(self, idx, x):
  230. idx_set = idx.ravel()
  231. x_set = x.ravel()
  232. if len(idx_set) != len(x_set):
  233. if len(x_set) == 1:
  234. x_set = np.full(len(idx_set), x_set[0], dtype=self.dtype)
  235. else:
  236. raise ValueError("Need len(index)==len(data) or len(data)==1")
  237. for i, v in zip(idx_set, x_set):
  238. if v:
  239. self._dict[i] = v
  240. elif i in self._dict:
  241. del self._dict[i]
  242. # 2D set methods
  243. def _set_intXint(self, row, col, x):
  244. key = (row, col)
  245. if x:
  246. self._dict[key] = x
  247. elif key in self._dict:
  248. del self._dict[key]
  249. def _set_arrayXarray(self, row, col, x):
  250. row = list(map(int, row.ravel()))
  251. col = list(map(int, col.ravel()))
  252. x = x.ravel()
  253. self._dict.update(zip(zip(row, col), x))
  254. for i in np.nonzero(x == 0)[0]:
  255. key = (row[i], col[i])
  256. if self._dict[key] == 0:
  257. # may have been superseded by later update
  258. del self._dict[key]
  259. def __add__(self, other):
  260. if isscalarlike(other):
  261. res_dtype = upcast_scalar(self.dtype, other)
  262. new = self._dok_container(self.shape, dtype=res_dtype)
  263. # Add this scalar to each element.
  264. for key in itertools.product(*[range(d) for d in self.shape]):
  265. aij = self._dict.get(key, 0) + other
  266. if aij:
  267. new[key] = aij
  268. elif issparse(other):
  269. if other.shape != self.shape:
  270. raise ValueError("Matrix dimensions are not equal.")
  271. res_dtype = upcast(self.dtype, other.dtype)
  272. new = self._dok_container(self.shape, dtype=res_dtype)
  273. new._dict = self._dict.copy()
  274. if other.format == "dok":
  275. o_items = other.items()
  276. else:
  277. other = other.tocoo()
  278. if self.ndim == 1:
  279. o_items = zip(other.coords[0], other.data)
  280. else:
  281. o_items = zip(zip(*other.coords), other.data)
  282. with np.errstate(over='ignore'):
  283. new._dict.update((k, new[k] + v) for k, v in o_items)
  284. elif isdense(other):
  285. new = self.todense() + other
  286. else:
  287. return NotImplemented
  288. return new
  289. def __radd__(self, other):
  290. return self + other # addition is commutative
  291. def __neg__(self):
  292. if self.dtype.kind == 'b':
  293. raise NotImplementedError(
  294. 'Negating a sparse boolean matrix is not supported.'
  295. )
  296. new = self._dok_container(self.shape, dtype=self.dtype)
  297. new._dict.update((k, -v) for k, v in self.items())
  298. return new
  299. def _mul_scalar(self, other):
  300. res_dtype = upcast_scalar(self.dtype, other)
  301. # Multiply this scalar by every element.
  302. new = self._dok_container(self.shape, dtype=res_dtype)
  303. new._dict.update(((k, v * other) for k, v in self.items()))
  304. return new
  305. def _matmul_vector(self, other):
  306. res_dtype = upcast(self.dtype, other.dtype)
  307. # vector @ vector
  308. if self.ndim == 1:
  309. if issparse(other):
  310. if other.format == "dok":
  311. keys = self.keys() & other.keys()
  312. else:
  313. keys = self.keys() & other.tocoo().coords[0]
  314. return res_dtype(sum(self._dict[k] * other._dict[k] for k in keys))
  315. elif isdense(other):
  316. return res_dtype(sum(other[k] * v for k, v in self.items()))
  317. else:
  318. return NotImplemented
  319. # matrix @ vector
  320. result = np.zeros(self.shape[0], dtype=res_dtype)
  321. for (i, j), v in self.items():
  322. result[i] += v * other[j]
  323. return result
  324. def _matmul_multivector(self, other):
  325. result_dtype = upcast(self.dtype, other.dtype)
  326. # vector @ multivector
  327. if self.ndim == 1:
  328. # works for other 1d or 2d
  329. return sum(v * other[j] for j, v in self._dict.items())
  330. # matrix @ multivector
  331. M = self.shape[0]
  332. new_shape = (M,) if other.ndim == 1 else (M, other.shape[1])
  333. result = np.zeros(new_shape, dtype=result_dtype)
  334. for (i, j), v in self.items():
  335. result[i] += v * other[j]
  336. return result
  337. def __imul__(self, other):
  338. if isscalarlike(other):
  339. self._dict.update((k, v * other) for k, v in self.items())
  340. return self
  341. return NotImplemented
  342. def __truediv__(self, other):
  343. if isscalarlike(other):
  344. res_dtype = upcast_scalar(self.dtype, other)
  345. new = self._dok_container(self.shape, dtype=res_dtype)
  346. new._dict.update(((k, v / other) for k, v in self.items()))
  347. return new
  348. return self.tocsr() / other
  349. def __itruediv__(self, other):
  350. if isscalarlike(other):
  351. self._dict.update((k, v / other) for k, v in self.items())
  352. return self
  353. return NotImplemented
  354. def __reduce__(self):
  355. # this approach is necessary because __setstate__ is called after
  356. # __setitem__ upon unpickling and since __init__ is not called there
  357. # is no shape attribute hence it is not possible to unpickle it.
  358. return dict.__reduce__(self)
  359. def diagonal(self, k=0):
  360. if self.ndim == 2:
  361. return super().diagonal(k)
  362. raise ValueError("diagonal requires two dimensions")
  363. def transpose(self, axes=None, copy=False):
  364. if self.ndim == 1:
  365. return self.copy()
  366. if axes is not None and axes != (1, 0):
  367. raise ValueError(
  368. "Sparse arrays/matrices do not support "
  369. "an 'axes' parameter because swapping "
  370. "dimensions is the only logical permutation."
  371. )
  372. M, N = self.shape
  373. new = self._dok_container((N, M), dtype=self.dtype, copy=copy)
  374. new._dict.update((((right, left), val) for (left, right), val in self.items()))
  375. return new
  376. transpose.__doc__ = _spbase.transpose.__doc__
  377. def copy(self):
  378. new = self._dok_container(self.shape, dtype=self.dtype)
  379. new._dict.update(self._dict)
  380. return new
  381. copy.__doc__ = _spbase.copy.__doc__
  382. @classmethod
  383. def fromkeys(cls, iterable, value=1, /):
  384. tmp = dict.fromkeys(iterable, value)
  385. if isinstance(next(iter(tmp)), tuple):
  386. shape = tuple(max(idx) + 1 for idx in zip(*tmp))
  387. else:
  388. shape = (max(tmp) + 1,)
  389. result = cls(shape, dtype=type(value))
  390. result._dict = tmp
  391. return result
  392. def tocoo(self, copy=False):
  393. nnz = self.nnz
  394. if nnz == 0:
  395. return self._coo_container(self.shape, dtype=self.dtype)
  396. idx_dtype = self._get_index_dtype(maxval=max(self.shape))
  397. data = np.fromiter(self.values(), dtype=self.dtype, count=nnz)
  398. # handle 1d keys specially b/c not a tuple
  399. inds = zip(*self.keys()) if self.ndim > 1 else (self.keys(),)
  400. coords = tuple(np.fromiter(ix, dtype=idx_dtype, count=nnz) for ix in inds)
  401. A = self._coo_container((data, coords), shape=self.shape, dtype=self.dtype)
  402. A.has_canonical_format = True
  403. return A
  404. tocoo.__doc__ = _spbase.tocoo.__doc__
  405. def todok(self, copy=False):
  406. if copy:
  407. return self.copy()
  408. return self
  409. todok.__doc__ = _spbase.todok.__doc__
  410. def tocsc(self, copy=False):
  411. if self.ndim == 1:
  412. raise NotImplementedError("tocsr() not valid for 1d sparse array")
  413. return self.tocoo(copy=False).tocsc(copy=copy)
  414. tocsc.__doc__ = _spbase.tocsc.__doc__
  415. def resize(self, *shape):
  416. shape = check_shape(shape, allow_nd=self._allow_nd)
  417. if len(shape) != len(self.shape):
  418. # TODO implement resize across dimensions
  419. raise NotImplementedError
  420. if self.ndim == 1:
  421. newN = shape[-1]
  422. for i in list(self._dict):
  423. if i >= newN:
  424. del self._dict[i]
  425. self._shape = shape
  426. return
  427. newM, newN = shape
  428. M, N = self.shape
  429. if newM < M or newN < N:
  430. # Remove all elements outside new dimensions
  431. for i, j in list(self.keys()):
  432. if i >= newM or j >= newN:
  433. del self._dict[i, j]
  434. self._shape = shape
  435. resize.__doc__ = _spbase.resize.__doc__
  436. # Added for 1d to avoid `tocsr` from _base.py
  437. def astype(self, dtype, casting='unsafe', copy=True):
  438. dtype = np.dtype(dtype)
  439. if self.dtype != dtype:
  440. result = self._dok_container(self.shape, dtype=dtype)
  441. data = np.array(list(self._dict.values()), dtype=dtype)
  442. result._dict = dict(zip(self._dict, data))
  443. return result
  444. elif copy:
  445. return self.copy()
  446. return self
  447. def isspmatrix_dok(x):
  448. """Is `x` of dok_array type?
  449. Parameters
  450. ----------
  451. x
  452. object to check for being a dok matrix
  453. Returns
  454. -------
  455. bool
  456. True if `x` is a dok matrix, False otherwise
  457. Examples
  458. --------
  459. >>> from scipy.sparse import dok_array, dok_matrix, coo_matrix, isspmatrix_dok
  460. >>> isspmatrix_dok(dok_matrix([[5]]))
  461. True
  462. >>> isspmatrix_dok(dok_array([[5]]))
  463. False
  464. >>> isspmatrix_dok(coo_matrix([[5]]))
  465. False
  466. """
  467. return isinstance(x, dok_matrix)
  468. # This namespace class separates array from matrix with isinstance
  469. class dok_array(_dok_base, sparray):
  470. """
  471. Dictionary Of Keys based sparse array.
  472. This is an efficient structure for constructing sparse
  473. arrays incrementally.
  474. This can be instantiated in several ways:
  475. dok_array(D)
  476. where D is a 2-D ndarray
  477. dok_array(S)
  478. with another sparse array or matrix S (equivalent to S.todok())
  479. dok_array((M,N), [dtype])
  480. create the array with initial shape (M,N)
  481. dtype is optional, defaulting to dtype='d'
  482. Attributes
  483. ----------
  484. dtype : dtype
  485. Data type of the array
  486. shape : 2-tuple
  487. Shape of the array
  488. ndim : int
  489. Number of dimensions (this is always 2)
  490. nnz
  491. Number of nonzero elements
  492. size
  493. T
  494. Notes
  495. -----
  496. Sparse arrays can be used in arithmetic operations: they support
  497. addition, subtraction, multiplication, division, and matrix power.
  498. - Allows for efficient O(1) access of individual elements.
  499. - Duplicates are not allowed.
  500. - Can be efficiently converted to a coo_array once constructed.
  501. Examples
  502. --------
  503. >>> import numpy as np
  504. >>> from scipy.sparse import dok_array
  505. >>> S = dok_array((5, 5), dtype=np.float32)
  506. >>> for i in range(5):
  507. ... for j in range(5):
  508. ... S[i, j] = i + j # Update element
  509. """
  510. class dok_matrix(spmatrix, _dok_base):
  511. """
  512. Dictionary Of Keys based sparse matrix.
  513. This is an efficient structure for constructing sparse
  514. matrices incrementally.
  515. This can be instantiated in several ways:
  516. dok_matrix(D)
  517. where D is a 2-D ndarray
  518. dok_matrix(S)
  519. with another sparse array or matrix S (equivalent to S.todok())
  520. dok_matrix((M,N), [dtype])
  521. create the matrix with initial shape (M,N)
  522. dtype is optional, defaulting to dtype='d'
  523. Attributes
  524. ----------
  525. dtype : dtype
  526. Data type of the matrix
  527. shape : 2-tuple
  528. Shape of the matrix
  529. ndim : int
  530. Number of dimensions (this is always 2)
  531. nnz
  532. Number of nonzero elements
  533. size
  534. T
  535. Notes
  536. -----
  537. Sparse matrices can be used in arithmetic operations: they support
  538. addition, subtraction, multiplication, division, and matrix power.
  539. - Allows for efficient O(1) access of individual elements.
  540. - Duplicates are not allowed.
  541. - Can be efficiently converted to a coo_matrix once constructed.
  542. Examples
  543. --------
  544. >>> import numpy as np
  545. >>> from scipy.sparse import dok_matrix
  546. >>> S = dok_matrix((5, 5), dtype=np.float32)
  547. >>> for i in range(5):
  548. ... for j in range(5):
  549. ... S[i, j] = i + j # Update element
  550. """
  551. def set_shape(self, shape):
  552. new_matrix = self.reshape(shape, copy=False).asformat(self.format)
  553. self.__dict__ = new_matrix.__dict__
  554. def get_shape(self):
  555. """Get shape of a sparse matrix."""
  556. return self._shape
  557. shape = property(fget=get_shape, fset=set_shape)
  558. def __reversed__(self):
  559. return self._dict.__reversed__()
  560. def __or__(self, other):
  561. if isinstance(other, _dok_base):
  562. return self._dict | other._dict
  563. return self._dict | other
  564. def __ror__(self, other):
  565. if isinstance(other, _dok_base):
  566. return self._dict | other._dict
  567. return self._dict | other
  568. def __ior__(self, other):
  569. if isinstance(other, _dok_base):
  570. self._dict |= other._dict
  571. else:
  572. self._dict |= other
  573. return self