Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/scipy/sparse/_coo.py: 15%

382 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-03-22 06:44 +0000

1""" A sparse matrix in COOrdinate or 'triplet' format""" 

2 

3__docformat__ = "restructuredtext en" 

4 

5__all__ = ['coo_array', 'coo_matrix', 'isspmatrix_coo'] 

6 

7import math 

8from warnings import warn 

9 

10import numpy as np 

11 

12from .._lib._util import copy_if_needed 

13from ._matrix import spmatrix 

14from ._sparsetools import coo_tocsr, coo_todense, coo_matvec 

15from ._base import issparse, SparseEfficiencyWarning, _spbase, sparray 

16from ._data import _data_matrix, _minmax_mixin 

17from ._sputils import (upcast_char, to_native, isshape, getdtype, 

18 getdata, downcast_intp_index, get_index_dtype, 

19 check_shape, check_reshape_kwargs) 

20 

21import operator 

22 

23 

24class _coo_base(_data_matrix, _minmax_mixin): 

25 _format = 'coo' 

26 

27 def __init__(self, arg1, shape=None, dtype=None, copy=False): 

28 _data_matrix.__init__(self) 

29 is_array = isinstance(self, sparray) 

30 if not copy: 

31 copy = copy_if_needed 

32 

33 if isinstance(arg1, tuple): 

34 if isshape(arg1, allow_1d=is_array): 

35 self._shape = check_shape(arg1, allow_1d=is_array) 

36 idx_dtype = self._get_index_dtype(maxval=max(self._shape)) 

37 data_dtype = getdtype(dtype, default=float) 

38 self.coords = tuple(np.array([], dtype=idx_dtype) 

39 for _ in range(len(self._shape))) 

40 self.data = np.array([], dtype=data_dtype) 

41 self.has_canonical_format = True 

42 else: 

43 try: 

44 obj, coords = arg1 

45 except (TypeError, ValueError) as e: 

46 raise TypeError('invalid input format') from e 

47 

48 if shape is None: 

49 if any(len(idx) == 0 for idx in coords): 

50 raise ValueError('cannot infer dimensions from zero ' 

51 'sized index arrays') 

52 shape = tuple(operator.index(np.max(idx)) + 1 

53 for idx in coords) 

54 self._shape = check_shape(shape, allow_1d=is_array) 

55 

56 idx_dtype = self._get_index_dtype(coords, 

57 maxval=max(self.shape), 

58 check_contents=True) 

59 self.coords = tuple(np.array(idx, copy=copy, dtype=idx_dtype) 

60 for idx in coords) 

61 self.data = getdata(obj, copy=copy, dtype=dtype) 

62 self.has_canonical_format = False 

63 else: 

64 if issparse(arg1): 

65 if arg1.format == self.format and copy: 

66 self.coords = tuple(idx.copy() for idx in arg1.coords) 

67 self.data = arg1.data.copy() 

68 self._shape = check_shape(arg1.shape, allow_1d=is_array) 

69 self.has_canonical_format = arg1.has_canonical_format 

70 else: 

71 coo = arg1.tocoo() 

72 self.coords = tuple(coo.coords) 

73 self.data = coo.data 

74 self._shape = check_shape(coo.shape, allow_1d=is_array) 

75 self.has_canonical_format = False 

76 else: 

77 # dense argument 

78 M = np.asarray(arg1) 

79 if not is_array: 

80 M = np.atleast_2d(M) 

81 if M.ndim != 2: 

82 raise TypeError('expected dimension <= 2 array or matrix') 

83 

84 self._shape = check_shape(M.shape, allow_1d=is_array) 

85 if shape is not None: 

86 if check_shape(shape, allow_1d=is_array) != self._shape: 

87 message = f'inconsistent shapes: {shape} != {self._shape}' 

88 raise ValueError(message) 

89 index_dtype = self._get_index_dtype(maxval=max(self._shape)) 

90 coords = M.nonzero() 

91 self.coords = tuple(idx.astype(index_dtype, copy=False) 

92 for idx in coords) 

93 self.data = M[coords] 

94 self.has_canonical_format = True 

95 

96 if dtype is not None: 

97 self.data = self.data.astype(dtype, copy=False) 

98 

99 self._check() 

100 

101 @property 

102 def row(self): 

103 if self.ndim > 1: 

104 return self.coords[-2] 

105 result = np.zeros_like(self.col) 

106 result.setflags(write=False) 

107 return result 

108 

109 

110 @row.setter 

111 def row(self, new_row): 

112 if self.ndim < 2: 

113 raise ValueError('cannot set row attribute of a 1-dimensional sparse array') 

114 new_row = np.asarray(new_row, dtype=self.coords[-2].dtype) 

115 self.coords = self.coords[:-2] + (new_row,) + self.coords[-1:] 

116 

117 @property 

118 def col(self): 

119 return self.coords[-1] 

120 

121 @col.setter 

122 def col(self, new_col): 

123 new_col = np.asarray(new_col, dtype=self.coords[-1].dtype) 

124 self.coords = self.coords[:-1] + (new_col,) 

125 

126 def reshape(self, *args, **kwargs): 

127 is_array = isinstance(self, sparray) 

128 shape = check_shape(args, self.shape, allow_1d=is_array) 

129 order, copy = check_reshape_kwargs(kwargs) 

130 

131 # Return early if reshape is not required 

132 if shape == self.shape: 

133 if copy: 

134 return self.copy() 

135 else: 

136 return self 

137 

138 # When reducing the number of dimensions, we need to be careful about 

139 # index overflow. This is why we can't simply call 

140 # `np.ravel_multi_index()` followed by `np.unravel_index()` here. 

141 flat_coords = _ravel_coords(self.coords, self.shape, order=order) 

142 if len(shape) == 2: 

143 if order == 'C': 

144 new_coords = divmod(flat_coords, shape[1]) 

145 else: 

146 new_coords = divmod(flat_coords, shape[0])[::-1] 

147 else: 

148 new_coords = np.unravel_index(flat_coords, shape, order=order) 

149 

150 # Handle copy here rather than passing on to the constructor so that no 

151 # copy will be made of `new_coords` regardless. 

152 if copy: 

153 new_data = self.data.copy() 

154 else: 

155 new_data = self.data 

156 

157 return self.__class__((new_data, new_coords), shape=shape, copy=False) 

158 

159 reshape.__doc__ = _spbase.reshape.__doc__ 

160 

161 def _getnnz(self, axis=None): 

162 if axis is None or (axis == 0 and self.ndim == 1): 

163 nnz = len(self.data) 

164 if any(len(idx) != nnz for idx in self.coords): 

165 raise ValueError('all index and data arrays must have the ' 

166 'same length') 

167 

168 if self.data.ndim != 1 or any(idx.ndim != 1 for idx in self.coords): 

169 raise ValueError('row, column, and data arrays must be 1-D') 

170 

171 return int(nnz) 

172 

173 if axis < 0: 

174 axis += self.ndim 

175 if axis >= self.ndim: 

176 raise ValueError('axis out of bounds') 

177 if self.ndim > 2: 

178 raise NotImplementedError('per-axis nnz for COO arrays with >2 ' 

179 'dimensions is not supported') 

180 return np.bincount(downcast_intp_index(self.coords[1 - axis]), 

181 minlength=self.shape[1 - axis]) 

182 

183 _getnnz.__doc__ = _spbase._getnnz.__doc__ 

184 

185 def _check(self): 

186 """ Checks data structure for consistency """ 

187 if self.ndim != len(self.coords): 

188 raise ValueError('mismatching number of index arrays for shape; ' 

189 f'got {len(self.coords)}, expected {self.ndim}') 

190 

191 # index arrays should have integer data types 

192 for i, idx in enumerate(self.coords): 

193 if idx.dtype.kind != 'i': 

194 warn(f'index array {i} has non-integer dtype ({idx.dtype.name})', 

195 stacklevel=3) 

196 

197 idx_dtype = self._get_index_dtype(self.coords, maxval=max(self.shape)) 

198 self.coords = tuple(np.asarray(idx, dtype=idx_dtype) 

199 for idx in self.coords) 

200 self.data = to_native(self.data) 

201 

202 if self.nnz > 0: 

203 for i, idx in enumerate(self.coords): 

204 if idx.max() >= self.shape[i]: 

205 raise ValueError(f'axis {i} index {idx.max()} exceeds ' 

206 f'matrix dimension {self.shape[i]}') 

207 if idx.min() < 0: 

208 raise ValueError(f'negative axis {i} index: {idx.min()}') 

209 

210 def transpose(self, axes=None, copy=False): 

211 if axes is None: 

212 axes = range(self.ndim)[::-1] 

213 elif isinstance(self, sparray): 

214 if len(axes) != self.ndim: 

215 raise ValueError("axes don't match matrix dimensions") 

216 if len(set(axes)) != self.ndim: 

217 raise ValueError("repeated axis in transpose") 

218 elif axes != (1, 0): 

219 raise ValueError("Sparse matrices do not support an 'axes' " 

220 "parameter because swapping dimensions is the " 

221 "only logical permutation.") 

222 

223 permuted_shape = tuple(self._shape[i] for i in axes) 

224 permuted_coords = tuple(self.coords[i] for i in axes) 

225 return self.__class__((self.data, permuted_coords), 

226 shape=permuted_shape, copy=copy) 

227 

228 transpose.__doc__ = _spbase.transpose.__doc__ 

229 

230 def resize(self, *shape) -> None: 

231 is_array = isinstance(self, sparray) 

232 shape = check_shape(shape, allow_1d=is_array) 

233 

234 # Check for added dimensions. 

235 if len(shape) > self.ndim: 

236 flat_coords = _ravel_coords(self.coords, self.shape) 

237 max_size = math.prod(shape) 

238 self.coords = np.unravel_index(flat_coords[:max_size], shape) 

239 self.data = self.data[:max_size] 

240 self._shape = shape 

241 return 

242 

243 # Check for removed dimensions. 

244 if len(shape) < self.ndim: 

245 tmp_shape = ( 

246 self._shape[:len(shape) - 1] # Original shape without last axis 

247 + (-1,) # Last axis is used to flatten the array 

248 + (1,) * (self.ndim - len(shape)) # Pad with ones 

249 ) 

250 tmp = self.reshape(tmp_shape) 

251 self.coords = tmp.coords[:len(shape)] 

252 self._shape = tmp.shape[:len(shape)] 

253 

254 # Handle truncation of existing dimensions. 

255 is_truncating = any(old > new for old, new in zip(self.shape, shape)) 

256 if is_truncating: 

257 mask = np.logical_and.reduce([ 

258 idx < size for idx, size in zip(self.coords, shape) 

259 ]) 

260 if not mask.all(): 

261 self.coords = tuple(idx[mask] for idx in self.coords) 

262 self.data = self.data[mask] 

263 

264 self._shape = shape 

265 

266 resize.__doc__ = _spbase.resize.__doc__ 

267 

268 def toarray(self, order=None, out=None): 

269 B = self._process_toarray_args(order, out) 

270 fortran = int(B.flags.f_contiguous) 

271 if not fortran and not B.flags.c_contiguous: 

272 raise ValueError("Output array must be C or F contiguous") 

273 if self.ndim > 2: 

274 raise ValueError("Cannot densify higher-rank sparse array") 

275 # This handles both 0D and 1D cases correctly regardless of the 

276 # original shape. 

277 M, N = self._shape_as_2d 

278 coo_todense(M, N, self.nnz, self.row, self.col, self.data, 

279 B.ravel('A'), fortran) 

280 # Note: reshape() doesn't copy here, but does return a new array (view). 

281 return B.reshape(self.shape) 

282 

283 toarray.__doc__ = _spbase.toarray.__doc__ 

284 

285 def tocsc(self, copy=False): 

286 """Convert this array/matrix to Compressed Sparse Column format 

287 

288 Duplicate entries will be summed together. 

289 

290 Examples 

291 -------- 

292 >>> from numpy import array 

293 >>> from scipy.sparse import coo_array 

294 >>> row = array([0, 0, 1, 3, 1, 0, 0]) 

295 >>> col = array([0, 2, 1, 3, 1, 0, 0]) 

296 >>> data = array([1, 1, 1, 1, 1, 1, 1]) 

297 >>> A = coo_array((data, (row, col)), shape=(4, 4)).tocsc() 

298 >>> A.toarray() 

299 array([[3, 0, 1, 0], 

300 [0, 2, 0, 0], 

301 [0, 0, 0, 0], 

302 [0, 0, 0, 1]]) 

303 

304 """ 

305 if self.ndim != 2: 

306 raise ValueError("Cannot convert a 1d sparse array to csc format") 

307 if self.nnz == 0: 

308 return self._csc_container(self.shape, dtype=self.dtype) 

309 else: 

310 from ._csc import csc_array 

311 indptr, indices, data, shape = self._coo_to_compressed(csc_array._swap) 

312 

313 x = self._csc_container((data, indices, indptr), shape=shape) 

314 if not self.has_canonical_format: 

315 x.sum_duplicates() 

316 return x 

317 

318 def tocsr(self, copy=False): 

319 """Convert this array/matrix to Compressed Sparse Row format 

320 

321 Duplicate entries will be summed together. 

322 

323 Examples 

324 -------- 

325 >>> from numpy import array 

326 >>> from scipy.sparse import coo_array 

327 >>> row = array([0, 0, 1, 3, 1, 0, 0]) 

328 >>> col = array([0, 2, 1, 3, 1, 0, 0]) 

329 >>> data = array([1, 1, 1, 1, 1, 1, 1]) 

330 >>> A = coo_array((data, (row, col)), shape=(4, 4)).tocsr() 

331 >>> A.toarray() 

332 array([[3, 0, 1, 0], 

333 [0, 2, 0, 0], 

334 [0, 0, 0, 0], 

335 [0, 0, 0, 1]]) 

336 

337 """ 

338 if self.ndim != 2: 

339 raise ValueError("Cannot convert a 1d sparse array to csr format") 

340 if self.nnz == 0: 

341 return self._csr_container(self.shape, dtype=self.dtype) 

342 else: 

343 from ._csr import csr_array 

344 indptr, indices, data, shape = self._coo_to_compressed(csr_array._swap) 

345 

346 x = self._csr_container((data, indices, indptr), shape=self.shape) 

347 if not self.has_canonical_format: 

348 x.sum_duplicates() 

349 return x 

350 

351 def _coo_to_compressed(self, swap): 

352 """convert (shape, coords, data) to (indptr, indices, data, shape)""" 

353 M, N = swap(self.shape) 

354 major, minor = swap(self.coords) 

355 nnz = len(major) 

356 # convert idx_dtype intc to int32 for pythran. 

357 # tested in scipy/optimize/tests/test__numdiff.py::test_group_columns 

358 idx_dtype = self._get_index_dtype(self.coords, maxval=max(self.nnz, N)) 

359 major = major.astype(idx_dtype, copy=False) 

360 minor = minor.astype(idx_dtype, copy=False) 

361 

362 indptr = np.empty(M + 1, dtype=idx_dtype) 

363 indices = np.empty_like(minor, dtype=idx_dtype) 

364 data = np.empty_like(self.data, dtype=self.dtype) 

365 

366 coo_tocsr(M, N, nnz, major, minor, self.data, indptr, indices, data) 

367 return indptr, indices, data, self.shape 

368 

369 def tocoo(self, copy=False): 

370 if copy: 

371 return self.copy() 

372 else: 

373 return self 

374 

375 tocoo.__doc__ = _spbase.tocoo.__doc__ 

376 

377 def todia(self, copy=False): 

378 if self.ndim != 2: 

379 raise ValueError("Cannot convert a 1d sparse array to dia format") 

380 self.sum_duplicates() 

381 ks = self.col - self.row # the diagonal for each nonzero 

382 diags, diag_idx = np.unique(ks, return_inverse=True) 

383 

384 if len(diags) > 100: 

385 # probably undesired, should todia() have a maxdiags parameter? 

386 warn("Constructing a DIA matrix with %d diagonals " 

387 "is inefficient" % len(diags), 

388 SparseEfficiencyWarning, stacklevel=2) 

389 

390 #initialize and fill in data array 

391 if self.data.size == 0: 

392 data = np.zeros((0, 0), dtype=self.dtype) 

393 else: 

394 data = np.zeros((len(diags), self.col.max()+1), dtype=self.dtype) 

395 data[diag_idx, self.col] = self.data 

396 

397 return self._dia_container((data, diags), shape=self.shape) 

398 

399 todia.__doc__ = _spbase.todia.__doc__ 

400 

401 def todok(self, copy=False): 

402 self.sum_duplicates() 

403 dok = self._dok_container(self.shape, dtype=self.dtype) 

404 # ensure that 1d coordinates are not tuples 

405 if self.ndim == 1: 

406 coords = self.coords[0] 

407 else: 

408 coords = zip(*self.coords) 

409 

410 dok._dict = dict(zip(coords, self.data)) 

411 return dok 

412 

413 todok.__doc__ = _spbase.todok.__doc__ 

414 

415 def diagonal(self, k=0): 

416 if self.ndim != 2: 

417 raise ValueError("diagonal requires two dimensions") 

418 rows, cols = self.shape 

419 if k <= -rows or k >= cols: 

420 return np.empty(0, dtype=self.data.dtype) 

421 diag = np.zeros(min(rows + min(k, 0), cols - max(k, 0)), 

422 dtype=self.dtype) 

423 diag_mask = (self.row + k) == self.col 

424 

425 if self.has_canonical_format: 

426 row = self.row[diag_mask] 

427 data = self.data[diag_mask] 

428 else: 

429 inds = tuple(idx[diag_mask] for idx in self.coords) 

430 (row, _), data = self._sum_duplicates(inds, self.data[diag_mask]) 

431 diag[row + min(k, 0)] = data 

432 

433 return diag 

434 

435 diagonal.__doc__ = _data_matrix.diagonal.__doc__ 

436 

437 def _setdiag(self, values, k): 

438 if self.ndim != 2: 

439 raise ValueError("setting a diagonal requires two dimensions") 

440 M, N = self.shape 

441 if values.ndim and not len(values): 

442 return 

443 idx_dtype = self.row.dtype 

444 

445 # Determine which triples to keep and where to put the new ones. 

446 full_keep = self.col - self.row != k 

447 if k < 0: 

448 max_index = min(M+k, N) 

449 if values.ndim: 

450 max_index = min(max_index, len(values)) 

451 keep = np.logical_or(full_keep, self.col >= max_index) 

452 new_row = np.arange(-k, -k + max_index, dtype=idx_dtype) 

453 new_col = np.arange(max_index, dtype=idx_dtype) 

454 else: 

455 max_index = min(M, N-k) 

456 if values.ndim: 

457 max_index = min(max_index, len(values)) 

458 keep = np.logical_or(full_keep, self.row >= max_index) 

459 new_row = np.arange(max_index, dtype=idx_dtype) 

460 new_col = np.arange(k, k + max_index, dtype=idx_dtype) 

461 

462 # Define the array of data consisting of the entries to be added. 

463 if values.ndim: 

464 new_data = values[:max_index] 

465 else: 

466 new_data = np.empty(max_index, dtype=self.dtype) 

467 new_data[:] = values 

468 

469 # Update the internal structure. 

470 self.coords = (np.concatenate((self.row[keep], new_row)), 

471 np.concatenate((self.col[keep], new_col))) 

472 self.data = np.concatenate((self.data[keep], new_data)) 

473 self.has_canonical_format = False 

474 

475 # needed by _data_matrix 

476 def _with_data(self, data, copy=True): 

477 """Returns a matrix with the same sparsity structure as self, 

478 but with different data. By default the index arrays are copied. 

479 """ 

480 if copy: 

481 coords = tuple(idx.copy() for idx in self.coords) 

482 else: 

483 coords = self.coords 

484 return self.__class__((data, coords), shape=self.shape, dtype=data.dtype) 

485 

486 def sum_duplicates(self) -> None: 

487 """Eliminate duplicate entries by adding them together 

488 

489 This is an *in place* operation 

490 """ 

491 if self.has_canonical_format: 

492 return 

493 summed = self._sum_duplicates(self.coords, self.data) 

494 self.coords, self.data = summed 

495 self.has_canonical_format = True 

496 

497 def _sum_duplicates(self, coords, data): 

498 # Assumes coords not in canonical format. 

499 if len(data) == 0: 

500 return coords, data 

501 # Sort coords w.r.t. rows, then cols. This corresponds to C-order, 

502 # which we rely on for argmin/argmax to return the first index in the 

503 # same way that numpy does (in the case of ties). 

504 order = np.lexsort(coords[::-1]) 

505 coords = tuple(idx[order] for idx in coords) 

506 data = data[order] 

507 unique_mask = np.logical_or.reduce([ 

508 idx[1:] != idx[:-1] for idx in coords 

509 ]) 

510 unique_mask = np.append(True, unique_mask) 

511 coords = tuple(idx[unique_mask] for idx in coords) 

512 unique_inds, = np.nonzero(unique_mask) 

513 data = np.add.reduceat(data, unique_inds, dtype=self.dtype) 

514 return coords, data 

515 

516 def eliminate_zeros(self): 

517 """Remove zero entries from the array/matrix 

518 

519 This is an *in place* operation 

520 """ 

521 mask = self.data != 0 

522 self.data = self.data[mask] 

523 self.coords = tuple(idx[mask] for idx in self.coords) 

524 

525 ####################### 

526 # Arithmetic handlers # 

527 ####################### 

528 

529 def _add_dense(self, other): 

530 if other.shape != self.shape: 

531 raise ValueError(f'Incompatible shapes ({self.shape} and {other.shape})') 

532 dtype = upcast_char(self.dtype.char, other.dtype.char) 

533 result = np.array(other, dtype=dtype, copy=True) 

534 fortran = int(result.flags.f_contiguous) 

535 M, N = self._shape_as_2d 

536 coo_todense(M, N, self.nnz, self.row, self.col, self.data, 

537 result.ravel('A'), fortran) 

538 return self._container(result, copy=False) 

539 

540 def _matmul_vector(self, other): 

541 result_shape = self.shape[0] if self.ndim > 1 else 1 

542 result = np.zeros(result_shape, 

543 dtype=upcast_char(self.dtype.char, other.dtype.char)) 

544 

545 if self.ndim == 2: 

546 col = self.col 

547 row = self.row 

548 elif self.ndim == 1: 

549 col = self.coords[0] 

550 row = np.zeros_like(col) 

551 else: 

552 raise NotImplementedError( 

553 f"coo_matvec not implemented for ndim={self.ndim}") 

554 

555 coo_matvec(self.nnz, row, col, self.data, other, result) 

556 # Array semantics return a scalar here, not a single-element array. 

557 if isinstance(self, sparray) and result_shape == 1: 

558 return result[0] 

559 return result 

560 

561 def _matmul_multivector(self, other): 

562 result_dtype = upcast_char(self.dtype.char, other.dtype.char) 

563 if self.ndim == 2: 

564 result_shape = (other.shape[1], self.shape[0]) 

565 col = self.col 

566 row = self.row 

567 elif self.ndim == 1: 

568 result_shape = (other.shape[1],) 

569 col = self.coords[0] 

570 row = np.zeros_like(col) 

571 else: 

572 raise NotImplementedError( 

573 f"coo_matvec not implemented for ndim={self.ndim}") 

574 

575 result = np.zeros(result_shape, dtype=result_dtype) 

576 for i, other_col in enumerate(other.T): 

577 coo_matvec(self.nnz, row, col, self.data, other_col, result[i:i + 1]) 

578 return result.T.view(type=type(other)) 

579 

580 

581def _ravel_coords(coords, shape, order='C'): 

582 """Like np.ravel_multi_index, but avoids some overflow issues.""" 

583 if len(coords) == 1: 

584 return coords[0] 

585 # Handle overflow as in https://github.com/scipy/scipy/pull/9132 

586 if len(coords) == 2: 

587 nrows, ncols = shape 

588 row, col = coords 

589 if order == 'C': 

590 maxval = (ncols * max(0, nrows - 1) + max(0, ncols - 1)) 

591 idx_dtype = get_index_dtype(maxval=maxval) 

592 return np.multiply(ncols, row, dtype=idx_dtype) + col 

593 elif order == 'F': 

594 maxval = (nrows * max(0, ncols - 1) + max(0, nrows - 1)) 

595 idx_dtype = get_index_dtype(maxval=maxval) 

596 return np.multiply(nrows, col, dtype=idx_dtype) + row 

597 else: 

598 raise ValueError("'order' must be 'C' or 'F'") 

599 return np.ravel_multi_index(coords, shape, order=order) 

600 

601 

602def isspmatrix_coo(x): 

603 """Is `x` of coo_matrix type? 

604 

605 Parameters 

606 ---------- 

607 x 

608 object to check for being a coo matrix 

609 

610 Returns 

611 ------- 

612 bool 

613 True if `x` is a coo matrix, False otherwise 

614 

615 Examples 

616 -------- 

617 >>> from scipy.sparse import coo_array, coo_matrix, csr_matrix, isspmatrix_coo 

618 >>> isspmatrix_coo(coo_matrix([[5]])) 

619 True 

620 >>> isspmatrix_coo(coo_array([[5]])) 

621 False 

622 >>> isspmatrix_coo(csr_matrix([[5]])) 

623 False 

624 """ 

625 return isinstance(x, coo_matrix) 

626 

627 

628# This namespace class separates array from matrix with isinstance 

629class coo_array(_coo_base, sparray): 

630 """ 

631 A sparse array in COOrdinate format. 

632 

633 Also known as the 'ijv' or 'triplet' format. 

634 

635 This can be instantiated in several ways: 

636 coo_array(D) 

637 where D is an ndarray 

638 

639 coo_array(S) 

640 with another sparse array or matrix S (equivalent to S.tocoo()) 

641 

642 coo_array(shape, [dtype]) 

643 to construct an empty sparse array with shape `shape` 

644 dtype is optional, defaulting to dtype='d'. 

645 

646 coo_array((data, coords), [shape]) 

647 to construct from existing data and index arrays: 

648 1. data[:] the entries of the sparse array, in any order 

649 2. coords[i][:] the axis-i coordinates of the data entries 

650 

651 Where ``A[coords] = data``, and coords is a tuple of index arrays. 

652 When shape is not specified, it is inferred from the index arrays. 

653 

654 Attributes 

655 ---------- 

656 dtype : dtype 

657 Data type of the sparse array 

658 shape : tuple of integers 

659 Shape of the sparse array 

660 ndim : int 

661 Number of dimensions of the sparse array 

662 nnz 

663 size 

664 data 

665 COO format data array of the sparse array 

666 coords 

667 COO format tuple of index arrays 

668 has_canonical_format : bool 

669 Whether the matrix has sorted coordinates and no duplicates 

670 format 

671 T 

672 

673 Notes 

674 ----- 

675 

676 Sparse arrays can be used in arithmetic operations: they support 

677 addition, subtraction, multiplication, division, and matrix power. 

678 

679 Advantages of the COO format 

680 - facilitates fast conversion among sparse formats 

681 - permits duplicate entries (see example) 

682 - very fast conversion to and from CSR/CSC formats 

683 

684 Disadvantages of the COO format 

685 - does not directly support: 

686 + arithmetic operations 

687 + slicing 

688 

689 Intended Usage 

690 - COO is a fast format for constructing sparse arrays 

691 - Once a COO array has been constructed, convert to CSR or 

692 CSC format for fast arithmetic and matrix vector operations 

693 - By default when converting to CSR or CSC format, duplicate (i,j) 

694 entries will be summed together. This facilitates efficient 

695 construction of finite element matrices and the like. (see example) 

696 

697 Canonical format 

698 - Entries and coordinates sorted by row, then column. 

699 - There are no duplicate entries (i.e. duplicate (i,j) locations) 

700 - Data arrays MAY have explicit zeros. 

701 

702 Examples 

703 -------- 

704 

705 >>> # Constructing an empty sparse array 

706 >>> import numpy as np 

707 >>> from scipy.sparse import coo_array 

708 >>> coo_array((3, 4), dtype=np.int8).toarray() 

709 array([[0, 0, 0, 0], 

710 [0, 0, 0, 0], 

711 [0, 0, 0, 0]], dtype=int8) 

712 

713 >>> # Constructing a sparse array using ijv format 

714 >>> row = np.array([0, 3, 1, 0]) 

715 >>> col = np.array([0, 3, 1, 2]) 

716 >>> data = np.array([4, 5, 7, 9]) 

717 >>> coo_array((data, (row, col)), shape=(4, 4)).toarray() 

718 array([[4, 0, 9, 0], 

719 [0, 7, 0, 0], 

720 [0, 0, 0, 0], 

721 [0, 0, 0, 5]]) 

722 

723 >>> # Constructing a sparse array with duplicate coordinates 

724 >>> row = np.array([0, 0, 1, 3, 1, 0, 0]) 

725 >>> col = np.array([0, 2, 1, 3, 1, 0, 0]) 

726 >>> data = np.array([1, 1, 1, 1, 1, 1, 1]) 

727 >>> coo = coo_array((data, (row, col)), shape=(4, 4)) 

728 >>> # Duplicate coordinates are maintained until implicitly or explicitly summed 

729 >>> np.max(coo.data) 

730 1 

731 >>> coo.toarray() 

732 array([[3, 0, 1, 0], 

733 [0, 2, 0, 0], 

734 [0, 0, 0, 0], 

735 [0, 0, 0, 1]]) 

736 

737 """ 

738 

739 

740class coo_matrix(spmatrix, _coo_base): 

741 """ 

742 A sparse matrix in COOrdinate format. 

743 

744 Also known as the 'ijv' or 'triplet' format. 

745 

746 This can be instantiated in several ways: 

747 coo_matrix(D) 

748 where D is a 2-D ndarray 

749 

750 coo_matrix(S) 

751 with another sparse array or matrix S (equivalent to S.tocoo()) 

752 

753 coo_matrix((M, N), [dtype]) 

754 to construct an empty matrix with shape (M, N) 

755 dtype is optional, defaulting to dtype='d'. 

756 

757 coo_matrix((data, (i, j)), [shape=(M, N)]) 

758 to construct from three arrays: 

759 1. data[:] the entries of the matrix, in any order 

760 2. i[:] the row indices of the matrix entries 

761 3. j[:] the column indices of the matrix entries 

762 

763 Where ``A[i[k], j[k]] = data[k]``. When shape is not 

764 specified, it is inferred from the index arrays 

765 

766 Attributes 

767 ---------- 

768 dtype : dtype 

769 Data type of the matrix 

770 shape : 2-tuple 

771 Shape of the matrix 

772 ndim : int 

773 Number of dimensions (this is always 2) 

774 nnz 

775 size 

776 data 

777 COO format data array of the matrix 

778 row 

779 COO format row index array of the matrix 

780 col 

781 COO format column index array of the matrix 

782 has_canonical_format : bool 

783 Whether the matrix has sorted indices and no duplicates 

784 format 

785 T 

786 

787 Notes 

788 ----- 

789 

790 Sparse matrices can be used in arithmetic operations: they support 

791 addition, subtraction, multiplication, division, and matrix power. 

792 

793 Advantages of the COO format 

794 - facilitates fast conversion among sparse formats 

795 - permits duplicate entries (see example) 

796 - very fast conversion to and from CSR/CSC formats 

797 

798 Disadvantages of the COO format 

799 - does not directly support: 

800 + arithmetic operations 

801 + slicing 

802 

803 Intended Usage 

804 - COO is a fast format for constructing sparse matrices 

805 - Once a COO matrix has been constructed, convert to CSR or 

806 CSC format for fast arithmetic and matrix vector operations 

807 - By default when converting to CSR or CSC format, duplicate (i,j) 

808 entries will be summed together. This facilitates efficient 

809 construction of finite element matrices and the like. (see example) 

810 

811 Canonical format 

812 - Entries and coordinates sorted by row, then column. 

813 - There are no duplicate entries (i.e. duplicate (i,j) locations) 

814 - Data arrays MAY have explicit zeros. 

815 

816 Examples 

817 -------- 

818 

819 >>> # Constructing an empty matrix 

820 >>> import numpy as np 

821 >>> from scipy.sparse import coo_matrix 

822 >>> coo_matrix((3, 4), dtype=np.int8).toarray() 

823 array([[0, 0, 0, 0], 

824 [0, 0, 0, 0], 

825 [0, 0, 0, 0]], dtype=int8) 

826 

827 >>> # Constructing a matrix using ijv format 

828 >>> row = np.array([0, 3, 1, 0]) 

829 >>> col = np.array([0, 3, 1, 2]) 

830 >>> data = np.array([4, 5, 7, 9]) 

831 >>> coo_matrix((data, (row, col)), shape=(4, 4)).toarray() 

832 array([[4, 0, 9, 0], 

833 [0, 7, 0, 0], 

834 [0, 0, 0, 0], 

835 [0, 0, 0, 5]]) 

836 

837 >>> # Constructing a matrix with duplicate coordinates 

838 >>> row = np.array([0, 0, 1, 3, 1, 0, 0]) 

839 >>> col = np.array([0, 2, 1, 3, 1, 0, 0]) 

840 >>> data = np.array([1, 1, 1, 1, 1, 1, 1]) 

841 >>> coo = coo_matrix((data, (row, col)), shape=(4, 4)) 

842 >>> # Duplicate coordinates are maintained until implicitly or explicitly summed 

843 >>> np.max(coo.data) 

844 1 

845 >>> coo.toarray() 

846 array([[3, 0, 1, 0], 

847 [0, 2, 0, 0], 

848 [0, 0, 0, 0], 

849 [0, 0, 0, 1]]) 

850 

851 """ 

852 

853 def __setstate__(self, state): 

854 if 'coords' not in state: 

855 # For retro-compatibility with the previous attributes 

856 # storing nnz coordinates for 2D COO matrix. 

857 state['coords'] = (state.pop('row'), state.pop('col')) 

858 self.__dict__.update(state)