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

378 statements  

« prev     ^ index     » next       coverage.py v7.4.1, created at 2024-02-14 06:37 +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 ._matrix import spmatrix 

13from ._sparsetools import coo_tocsr, coo_todense, coo_matvec 

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

15from ._data import _data_matrix, _minmax_mixin 

16from ._sputils import (upcast, upcast_char, to_native, isshape, getdtype, 

17 getdata, downcast_intp_index, get_index_dtype, 

18 check_shape, check_reshape_kwargs) 

19 

20import operator 

21 

22 

23class _coo_base(_data_matrix, _minmax_mixin): 

24 _format = 'coo' 

25 

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

27 _data_matrix.__init__(self) 

28 is_array = isinstance(self, sparray) 

29 

30 if isinstance(arg1, tuple): 

31 if isshape(arg1, allow_1d=is_array): 

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

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

34 data_dtype = getdtype(dtype, default=float) 

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

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

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

38 self.has_canonical_format = True 

39 else: 

40 try: 

41 obj, coords = arg1 

42 except (TypeError, ValueError) as e: 

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

44 

45 if shape is None: 

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

47 raise ValueError('cannot infer dimensions from zero ' 

48 'sized index arrays') 

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

50 for idx in coords) 

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

52 

53 idx_dtype = self._get_index_dtype(coords, 

54 maxval=max(self.shape), 

55 check_contents=True) 

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

57 for idx in coords) 

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

59 self.has_canonical_format = False 

60 else: 

61 if issparse(arg1): 

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

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

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

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

66 self.has_canonical_format = arg1.has_canonical_format 

67 else: 

68 coo = arg1.tocoo() 

69 self.coords = tuple(coo.coords) 

70 self.data = coo.data 

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

72 self.has_canonical_format = False 

73 else: 

74 # dense argument 

75 M = np.asarray(arg1) 

76 if not is_array: 

77 M = np.atleast_2d(M) 

78 if M.ndim != 2: 

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

80 

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

82 if shape is not None: 

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

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

85 raise ValueError(message) 

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

87 coords = M.nonzero() 

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

89 for idx in coords) 

90 self.data = M[coords] 

91 self.has_canonical_format = True 

92 

93 if dtype is not None: 

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

95 

96 self._check() 

97 

98 @property 

99 def row(self): 

100 if self.ndim > 1: 

101 return self.coords[-2] 

102 result = np.zeros_like(self.col) 

103 result.setflags(write=False) 

104 return result 

105 

106 

107 @row.setter 

108 def row(self, new_row): 

109 if self.ndim < 2: 

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

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

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

113 

114 @property 

115 def col(self): 

116 return self.coords[-1] 

117 

118 @col.setter 

119 def col(self, new_col): 

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

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

122 

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

124 is_array = isinstance(self, sparray) 

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

126 order, copy = check_reshape_kwargs(kwargs) 

127 

128 # Return early if reshape is not required 

129 if shape == self.shape: 

130 if copy: 

131 return self.copy() 

132 else: 

133 return self 

134 

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

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

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

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

139 if len(shape) == 2: 

140 if order == 'C': 

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

142 else: 

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

144 else: 

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

146 

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

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

149 if copy: 

150 new_data = self.data.copy() 

151 else: 

152 new_data = self.data 

153 

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

155 

156 reshape.__doc__ = _spbase.reshape.__doc__ 

157 

158 def _getnnz(self, axis=None): 

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

160 nnz = len(self.data) 

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

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

163 'same length') 

164 

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

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

167 

168 return int(nnz) 

169 

170 if axis < 0: 

171 axis += self.ndim 

172 if axis >= self.ndim: 

173 raise ValueError('axis out of bounds') 

174 if self.ndim > 2: 

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

176 'dimensions is not supported') 

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

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

179 

180 _getnnz.__doc__ = _spbase._getnnz.__doc__ 

181 

182 def _check(self): 

183 """ Checks data structure for consistency """ 

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

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

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

187 

188 # index arrays should have integer data types 

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

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

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

192 stacklevel=3) 

193 

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

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

196 for idx in self.coords) 

197 self.data = to_native(self.data) 

198 

199 if self.nnz > 0: 

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

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

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

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

204 if idx.min() < 0: 

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

206 

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

208 if axes is None: 

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

210 elif isinstance(self, sparray): 

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

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

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

214 raise ValueError("repeated axis in transpose") 

215 elif axes != (1, 0): 

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

217 "parameter because swapping dimensions is the " 

218 "only logical permutation.") 

219 

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

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

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

223 shape=permuted_shape, copy=copy) 

224 

225 transpose.__doc__ = _spbase.transpose.__doc__ 

226 

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

228 is_array = isinstance(self, sparray) 

229 shape = check_shape(shape, allow_1d=is_array) 

230 

231 # Check for added dimensions. 

232 if len(shape) > self.ndim: 

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

234 max_size = math.prod(shape) 

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

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

237 self._shape = shape 

238 return 

239 

240 # Check for removed dimensions. 

241 if len(shape) < self.ndim: 

242 tmp_shape = ( 

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

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

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

246 ) 

247 tmp = self.reshape(tmp_shape) 

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

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

250 

251 # Handle truncation of existing dimensions. 

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

253 if is_truncating: 

254 mask = np.logical_and.reduce([ 

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

256 ]) 

257 if not mask.all(): 

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

259 self.data = self.data[mask] 

260 

261 self._shape = shape 

262 

263 resize.__doc__ = _spbase.resize.__doc__ 

264 

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

266 B = self._process_toarray_args(order, out) 

267 fortran = int(B.flags.f_contiguous) 

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

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

270 if self.ndim > 2: 

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

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

273 # original shape. 

274 M, N = self._shape_as_2d 

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

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

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

278 return B.reshape(self.shape) 

279 

280 toarray.__doc__ = _spbase.toarray.__doc__ 

281 

282 def tocsc(self, copy=False): 

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

284 

285 Duplicate entries will be summed together. 

286 

287 Examples 

288 -------- 

289 >>> from numpy import array 

290 >>> from scipy.sparse import coo_array 

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

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

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

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

295 >>> A.toarray() 

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

297 [0, 2, 0, 0], 

298 [0, 0, 0, 0], 

299 [0, 0, 0, 1]]) 

300 

301 """ 

302 if self.ndim != 2: 

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

304 if self.nnz == 0: 

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

306 else: 

307 M,N = self.shape 

308 idx_dtype = self._get_index_dtype(self.coords, maxval=max(self.nnz, M)) 

309 row = self.row.astype(idx_dtype, copy=False) 

310 col = self.col.astype(idx_dtype, copy=False) 

311 

312 indptr = np.empty(N + 1, dtype=idx_dtype) 

313 indices = np.empty_like(row, dtype=idx_dtype) 

314 data = np.empty_like(self.data, dtype=upcast(self.dtype)) 

315 

316 coo_tocsr(N, M, self.nnz, col, row, self.data, 

317 indptr, indices, data) 

318 

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

320 if not self.has_canonical_format: 

321 x.sum_duplicates() 

322 return x 

323 

324 def tocsr(self, copy=False): 

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

326 

327 Duplicate entries will be summed together. 

328 

329 Examples 

330 -------- 

331 >>> from numpy import array 

332 >>> from scipy.sparse import coo_array 

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

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

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

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

337 >>> A.toarray() 

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

339 [0, 2, 0, 0], 

340 [0, 0, 0, 0], 

341 [0, 0, 0, 1]]) 

342 

343 """ 

344 if self.ndim != 2: 

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

346 if self.nnz == 0: 

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

348 else: 

349 M,N = self.shape 

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

351 row = self.row.astype(idx_dtype, copy=False) 

352 col = self.col.astype(idx_dtype, copy=False) 

353 

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

355 indices = np.empty_like(col, dtype=idx_dtype) 

356 data = np.empty_like(self.data, dtype=upcast(self.dtype)) 

357 

358 coo_tocsr(M, N, self.nnz, row, col, self.data, 

359 indptr, indices, data) 

360 

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

362 if not self.has_canonical_format: 

363 x.sum_duplicates() 

364 return x 

365 

366 def tocoo(self, copy=False): 

367 if copy: 

368 return self.copy() 

369 else: 

370 return self 

371 

372 tocoo.__doc__ = _spbase.tocoo.__doc__ 

373 

374 def todia(self, copy=False): 

375 if self.ndim != 2: 

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

377 self.sum_duplicates() 

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

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

380 

381 if len(diags) > 100: 

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

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

384 "is inefficient" % len(diags), 

385 SparseEfficiencyWarning, stacklevel=2) 

386 

387 #initialize and fill in data array 

388 if self.data.size == 0: 

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

390 else: 

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

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

393 

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

395 

396 todia.__doc__ = _spbase.todia.__doc__ 

397 

398 def todok(self, copy=False): 

399 if self.ndim != 2: 

400 raise ValueError("Cannot convert a 1d sparse array to dok format") 

401 self.sum_duplicates() 

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

403 dok._update(zip(zip(self.row,self.col),self.data)) 

404 

405 return dok 

406 

407 todok.__doc__ = _spbase.todok.__doc__ 

408 

409 def diagonal(self, k=0): 

410 if self.ndim != 2: 

411 raise ValueError("diagonal requires two dimensions") 

412 rows, cols = self.shape 

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

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

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

416 dtype=self.dtype) 

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

418 

419 if self.has_canonical_format: 

420 row = self.row[diag_mask] 

421 data = self.data[diag_mask] 

422 else: 

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

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

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

426 

427 return diag 

428 

429 diagonal.__doc__ = _data_matrix.diagonal.__doc__ 

430 

431 def _setdiag(self, values, k): 

432 if self.ndim != 2: 

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

434 M, N = self.shape 

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

436 return 

437 idx_dtype = self.row.dtype 

438 

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

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

441 if k < 0: 

442 max_index = min(M+k, N) 

443 if values.ndim: 

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

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

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

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

448 else: 

449 max_index = min(M, N-k) 

450 if values.ndim: 

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

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

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

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

455 

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

457 if values.ndim: 

458 new_data = values[:max_index] 

459 else: 

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

461 new_data[:] = values 

462 

463 # Update the internal structure. 

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

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

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

467 self.has_canonical_format = False 

468 

469 # needed by _data_matrix 

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

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

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

473 """ 

474 if copy: 

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

476 else: 

477 coords = self.coords 

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

479 

480 def sum_duplicates(self) -> None: 

481 """Eliminate duplicate entries by adding them together 

482 

483 This is an *in place* operation 

484 """ 

485 if self.has_canonical_format: 

486 return 

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

488 self.coords, self.data = summed 

489 self.has_canonical_format = True 

490 

491 def _sum_duplicates(self, coords, data): 

492 # Assumes coords not in canonical format. 

493 if len(data) == 0: 

494 return coords, data 

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

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

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

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

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

500 data = data[order] 

501 unique_mask = np.logical_or.reduce([ 

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

503 ]) 

504 unique_mask = np.append(True, unique_mask) 

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

506 unique_inds, = np.nonzero(unique_mask) 

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

508 return coords, data 

509 

510 def eliminate_zeros(self): 

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

512 

513 This is an *in place* operation 

514 """ 

515 mask = self.data != 0 

516 self.data = self.data[mask] 

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

518 

519 ####################### 

520 # Arithmetic handlers # 

521 ####################### 

522 

523 def _add_dense(self, other): 

524 if other.shape != self.shape: 

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

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

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

528 fortran = int(result.flags.f_contiguous) 

529 M, N = self._shape_as_2d 

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

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

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

533 

534 def _matmul_vector(self, other): 

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

536 result = np.zeros(result_shape, 

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

538 

539 if self.ndim == 2: 

540 col = self.col 

541 row = self.row 

542 elif self.ndim == 1: 

543 col = self.coords[0] 

544 row = np.zeros_like(col) 

545 else: 

546 raise NotImplementedError( 

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

548 

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

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

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

552 return result[0] 

553 return result 

554 

555 def _matmul_multivector(self, other): 

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

557 if self.ndim == 2: 

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

559 col = self.col 

560 row = self.row 

561 elif self.ndim == 1: 

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

563 col = self.coords[0] 

564 row = np.zeros_like(col) 

565 else: 

566 raise NotImplementedError( 

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

568 

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

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

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

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

573 

574 

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

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

577 if len(coords) == 1: 

578 return coords[0] 

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

580 if len(coords) == 2: 

581 nrows, ncols = shape 

582 row, col = coords 

583 if order == 'C': 

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

585 idx_dtype = get_index_dtype(maxval=maxval) 

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

587 elif order == 'F': 

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

589 idx_dtype = get_index_dtype(maxval=maxval) 

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

591 else: 

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

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

594 

595 

596def isspmatrix_coo(x): 

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

598 

599 Parameters 

600 ---------- 

601 x 

602 object to check for being a coo matrix 

603 

604 Returns 

605 ------- 

606 bool 

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

608 

609 Examples 

610 -------- 

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

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

613 True 

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

615 False 

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

617 False 

618 """ 

619 return isinstance(x, coo_matrix) 

620 

621 

622# This namespace class separates array from matrix with isinstance 

623class coo_array(_coo_base, sparray): 

624 """ 

625 A sparse array in COOrdinate format. 

626 

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

628 

629 This can be instantiated in several ways: 

630 coo_array(D) 

631 where D is an ndarray 

632 

633 coo_array(S) 

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

635 

636 coo_array(shape, [dtype]) 

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

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

639 

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

641 to construct from existing data and index arrays: 

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

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

644 

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

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

647 

648 Attributes 

649 ---------- 

650 dtype : dtype 

651 Data type of the sparse array 

652 shape : tuple of integers 

653 Shape of the sparse array 

654 ndim : int 

655 Number of dimensions of the sparse array 

656 nnz 

657 size 

658 data 

659 COO format data array of the sparse array 

660 coords 

661 COO format tuple of index arrays 

662 has_canonical_format : bool 

663 Whether the matrix has sorted coordinates and no duplicates 

664 format 

665 T 

666 

667 Notes 

668 ----- 

669 

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

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

672 

673 Advantages of the COO format 

674 - facilitates fast conversion among sparse formats 

675 - permits duplicate entries (see example) 

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

677 

678 Disadvantages of the COO format 

679 - does not directly support: 

680 + arithmetic operations 

681 + slicing 

682 

683 Intended Usage 

684 - COO is a fast format for constructing sparse arrays 

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

686 CSC format for fast arithmetic and matrix vector operations 

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

688 entries will be summed together. This facilitates efficient 

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

690 

691 Canonical format 

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

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

694 - Data arrays MAY have explicit zeros. 

695 

696 Examples 

697 -------- 

698 

699 >>> # Constructing an empty sparse array 

700 >>> import numpy as np 

701 >>> from scipy.sparse import coo_array 

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

703 array([[0, 0, 0, 0], 

704 [0, 0, 0, 0], 

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

706 

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

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

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

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

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

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

713 [0, 7, 0, 0], 

714 [0, 0, 0, 0], 

715 [0, 0, 0, 5]]) 

716 

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

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

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

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

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

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

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

724 1 

725 >>> coo.toarray() 

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

727 [0, 2, 0, 0], 

728 [0, 0, 0, 0], 

729 [0, 0, 0, 1]]) 

730 

731 """ 

732 

733 

734class coo_matrix(spmatrix, _coo_base): 

735 """ 

736 A sparse matrix in COOrdinate format. 

737 

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

739 

740 This can be instantiated in several ways: 

741 coo_matrix(D) 

742 where D is a 2-D ndarray 

743 

744 coo_matrix(S) 

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

746 

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

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

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

750 

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

752 to construct from three arrays: 

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

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

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

756 

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

758 specified, it is inferred from the index arrays 

759 

760 Attributes 

761 ---------- 

762 dtype : dtype 

763 Data type of the matrix 

764 shape : 2-tuple 

765 Shape of the matrix 

766 ndim : int 

767 Number of dimensions (this is always 2) 

768 nnz 

769 size 

770 data 

771 COO format data array of the matrix 

772 row 

773 COO format row index array of the matrix 

774 col 

775 COO format column index array of the matrix 

776 has_canonical_format : bool 

777 Whether the matrix has sorted indices and no duplicates 

778 format 

779 T 

780 

781 Notes 

782 ----- 

783 

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

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

786 

787 Advantages of the COO format 

788 - facilitates fast conversion among sparse formats 

789 - permits duplicate entries (see example) 

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

791 

792 Disadvantages of the COO format 

793 - does not directly support: 

794 + arithmetic operations 

795 + slicing 

796 

797 Intended Usage 

798 - COO is a fast format for constructing sparse matrices 

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

800 CSC format for fast arithmetic and matrix vector operations 

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

802 entries will be summed together. This facilitates efficient 

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

804 

805 Canonical format 

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

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

808 - Data arrays MAY have explicit zeros. 

809 

810 Examples 

811 -------- 

812 

813 >>> # Constructing an empty matrix 

814 >>> import numpy as np 

815 >>> from scipy.sparse import coo_matrix 

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

817 array([[0, 0, 0, 0], 

818 [0, 0, 0, 0], 

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

820 

821 >>> # Constructing a matrix using ijv format 

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

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

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

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

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

827 [0, 7, 0, 0], 

828 [0, 0, 0, 0], 

829 [0, 0, 0, 5]]) 

830 

831 >>> # Constructing a matrix with duplicate coordinates 

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

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

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

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

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

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

838 1 

839 >>> coo.toarray() 

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

841 [0, 2, 0, 0], 

842 [0, 0, 0, 0], 

843 [0, 0, 0, 1]]) 

844 

845 """ 

846 

847 def __setstate__(self, state): 

848 if 'coords' not in state: 

849 # For retro-compatibility with the previous attributes 

850 # storing nnz coordinates for 2D COO matrix. 

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

852 self.__dict__.update(state)