Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/scipy/linalg/interpolative.py: 12%

181 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-12 06:31 +0000

1#****************************************************************************** 

2# Copyright (C) 2013 Kenneth L. Ho 

3# 

4# Redistribution and use in source and binary forms, with or without 

5# modification, are permitted provided that the following conditions are met: 

6# 

7# Redistributions of source code must retain the above copyright notice, this 

8# list of conditions and the following disclaimer. Redistributions in binary 

9# form must reproduce the above copyright notice, this list of conditions and 

10# the following disclaimer in the documentation and/or other materials 

11# provided with the distribution. 

12# 

13# None of the names of the copyright holders may be used to endorse or 

14# promote products derived from this software without specific prior written 

15# permission. 

16# 

17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 

18# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 

19# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 

20# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 

21# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 

22# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 

23# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 

24# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 

25# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 

26# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 

27# POSSIBILITY OF SUCH DAMAGE. 

28#****************************************************************************** 

29 

30# Python module for interfacing with `id_dist`. 

31 

32r""" 

33====================================================================== 

34Interpolative matrix decomposition (:mod:`scipy.linalg.interpolative`) 

35====================================================================== 

36 

37.. moduleauthor:: Kenneth L. Ho <klho@stanford.edu> 

38 

39.. versionadded:: 0.13 

40 

41.. currentmodule:: scipy.linalg.interpolative 

42 

43An interpolative decomposition (ID) of a matrix :math:`A \in 

44\mathbb{C}^{m \times n}` of rank :math:`k \leq \min \{ m, n \}` is a 

45factorization 

46 

47.. math:: 

48 A \Pi = 

49 \begin{bmatrix} 

50 A \Pi_{1} & A \Pi_{2} 

51 \end{bmatrix} = 

52 A \Pi_{1} 

53 \begin{bmatrix} 

54 I & T 

55 \end{bmatrix}, 

56 

57where :math:`\Pi = [\Pi_{1}, \Pi_{2}]` is a permutation matrix with 

58:math:`\Pi_{1} \in \{ 0, 1 \}^{n \times k}`, i.e., :math:`A \Pi_{2} = 

59A \Pi_{1} T`. This can equivalently be written as :math:`A = BP`, 

60where :math:`B = A \Pi_{1}` and :math:`P = [I, T] \Pi^{\mathsf{T}}` 

61are the *skeleton* and *interpolation matrices*, respectively. 

62 

63If :math:`A` does not have exact rank :math:`k`, then there exists an 

64approximation in the form of an ID such that :math:`A = BP + E`, where 

65:math:`\| E \| \sim \sigma_{k + 1}` is on the order of the :math:`(k + 

661)`-th largest singular value of :math:`A`. Note that :math:`\sigma_{k 

67+ 1}` is the best possible error for a rank-:math:`k` approximation 

68and, in fact, is achieved by the singular value decomposition (SVD) 

69:math:`A \approx U S V^{*}`, where :math:`U \in \mathbb{C}^{m \times 

70k}` and :math:`V \in \mathbb{C}^{n \times k}` have orthonormal columns 

71and :math:`S = \mathop{\mathrm{diag}} (\sigma_{i}) \in \mathbb{C}^{k 

72\times k}` is diagonal with nonnegative entries. The principal 

73advantages of using an ID over an SVD are that: 

74 

75- it is cheaper to construct; 

76- it preserves the structure of :math:`A`; and 

77- it is more efficient to compute with in light of the identity submatrix of :math:`P`. 

78 

79Routines 

80======== 

81 

82Main functionality: 

83 

84.. autosummary:: 

85 :toctree: generated/ 

86 

87 interp_decomp 

88 reconstruct_matrix_from_id 

89 reconstruct_interp_matrix 

90 reconstruct_skel_matrix 

91 id_to_svd 

92 svd 

93 estimate_spectral_norm 

94 estimate_spectral_norm_diff 

95 estimate_rank 

96 

97Support functions: 

98 

99.. autosummary:: 

100 :toctree: generated/ 

101 

102 seed 

103 rand 

104 

105 

106References 

107========== 

108 

109This module uses the ID software package [1]_ by Martinsson, Rokhlin, 

110Shkolnisky, and Tygert, which is a Fortran library for computing IDs 

111using various algorithms, including the rank-revealing QR approach of 

112[2]_ and the more recent randomized methods described in [3]_, [4]_, 

113and [5]_. This module exposes its functionality in a way convenient 

114for Python users. Note that this module does not add any functionality 

115beyond that of organizing a simpler and more consistent interface. 

116 

117We advise the user to consult also the `documentation for the ID package 

118<http://tygert.com/id_doc.4.pdf>`_. 

119 

120.. [1] P.G. Martinsson, V. Rokhlin, Y. Shkolnisky, M. Tygert. "ID: a 

121 software package for low-rank approximation of matrices via interpolative 

122 decompositions, version 0.2." http://tygert.com/id_doc.4.pdf. 

123 

124.. [2] H. Cheng, Z. Gimbutas, P.G. Martinsson, V. Rokhlin. "On the 

125 compression of low rank matrices." *SIAM J. Sci. Comput.* 26 (4): 1389--1404, 

126 2005. :doi:`10.1137/030602678`. 

127 

128.. [3] E. Liberty, F. Woolfe, P.G. Martinsson, V. Rokhlin, M. 

129 Tygert. "Randomized algorithms for the low-rank approximation of matrices." 

130 *Proc. Natl. Acad. Sci. U.S.A.* 104 (51): 20167--20172, 2007. 

131 :doi:`10.1073/pnas.0709640104`. 

132 

133.. [4] P.G. Martinsson, V. Rokhlin, M. Tygert. "A randomized 

134 algorithm for the decomposition of matrices." *Appl. Comput. Harmon. Anal.* 30 

135 (1): 47--68, 2011. :doi:`10.1016/j.acha.2010.02.003`. 

136 

137.. [5] F. Woolfe, E. Liberty, V. Rokhlin, M. Tygert. "A fast 

138 randomized algorithm for the approximation of matrices." *Appl. Comput. 

139 Harmon. Anal.* 25 (3): 335--366, 2008. :doi:`10.1016/j.acha.2007.12.002`. 

140 

141 

142Tutorial 

143======== 

144 

145Initializing 

146------------ 

147 

148The first step is to import :mod:`scipy.linalg.interpolative` by issuing the 

149command: 

150 

151>>> import scipy.linalg.interpolative as sli 

152 

153Now let's build a matrix. For this, we consider a Hilbert matrix, which is well 

154know to have low rank: 

155 

156>>> from scipy.linalg import hilbert 

157>>> n = 1000 

158>>> A = hilbert(n) 

159 

160We can also do this explicitly via: 

161 

162>>> import numpy as np 

163>>> n = 1000 

164>>> A = np.empty((n, n), order='F') 

165>>> for j in range(n): 

166>>> for i in range(n): 

167>>> A[i,j] = 1. / (i + j + 1) 

168 

169Note the use of the flag ``order='F'`` in :func:`numpy.empty`. This 

170instantiates the matrix in Fortran-contiguous order and is important for 

171avoiding data copying when passing to the backend. 

172 

173We then define multiplication routines for the matrix by regarding it as a 

174:class:`scipy.sparse.linalg.LinearOperator`: 

175 

176>>> from scipy.sparse.linalg import aslinearoperator 

177>>> L = aslinearoperator(A) 

178 

179This automatically sets up methods describing the action of the matrix and its 

180adjoint on a vector. 

181 

182Computing an ID 

183--------------- 

184 

185We have several choices of algorithm to compute an ID. These fall largely 

186according to two dichotomies: 

187 

1881. how the matrix is represented, i.e., via its entries or via its action on a 

189 vector; and 

1902. whether to approximate it to a fixed relative precision or to a fixed rank. 

191 

192We step through each choice in turn below. 

193 

194In all cases, the ID is represented by three parameters: 

195 

1961. a rank ``k``; 

1972. an index array ``idx``; and 

1983. interpolation coefficients ``proj``. 

199 

200The ID is specified by the relation 

201``np.dot(A[:,idx[:k]], proj) == A[:,idx[k:]]``. 

202 

203From matrix entries 

204................... 

205 

206We first consider a matrix given in terms of its entries. 

207 

208To compute an ID to a fixed precision, type: 

209 

210>>> k, idx, proj = sli.interp_decomp(A, eps) 

211 

212where ``eps < 1`` is the desired precision. 

213 

214To compute an ID to a fixed rank, use: 

215 

216>>> idx, proj = sli.interp_decomp(A, k) 

217 

218where ``k >= 1`` is the desired rank. 

219 

220Both algorithms use random sampling and are usually faster than the 

221corresponding older, deterministic algorithms, which can be accessed via the 

222commands: 

223 

224>>> k, idx, proj = sli.interp_decomp(A, eps, rand=False) 

225 

226and: 

227 

228>>> idx, proj = sli.interp_decomp(A, k, rand=False) 

229 

230respectively. 

231 

232From matrix action 

233.................. 

234 

235Now consider a matrix given in terms of its action on a vector as a 

236:class:`scipy.sparse.linalg.LinearOperator`. 

237 

238To compute an ID to a fixed precision, type: 

239 

240>>> k, idx, proj = sli.interp_decomp(L, eps) 

241 

242To compute an ID to a fixed rank, use: 

243 

244>>> idx, proj = sli.interp_decomp(L, k) 

245 

246These algorithms are randomized. 

247 

248Reconstructing an ID 

249-------------------- 

250 

251The ID routines above do not output the skeleton and interpolation matrices 

252explicitly but instead return the relevant information in a more compact (and 

253sometimes more useful) form. To build these matrices, write: 

254 

255>>> B = sli.reconstruct_skel_matrix(A, k, idx) 

256 

257for the skeleton matrix and: 

258 

259>>> P = sli.reconstruct_interp_matrix(idx, proj) 

260 

261for the interpolation matrix. The ID approximation can then be computed as: 

262 

263>>> C = np.dot(B, P) 

264 

265This can also be constructed directly using: 

266 

267>>> C = sli.reconstruct_matrix_from_id(B, idx, proj) 

268 

269without having to first compute ``P``. 

270 

271Alternatively, this can be done explicitly as well using: 

272 

273>>> B = A[:,idx[:k]] 

274>>> P = np.hstack([np.eye(k), proj])[:,np.argsort(idx)] 

275>>> C = np.dot(B, P) 

276 

277Computing an SVD 

278---------------- 

279 

280An ID can be converted to an SVD via the command: 

281 

282>>> U, S, V = sli.id_to_svd(B, idx, proj) 

283 

284The SVD approximation is then: 

285 

286>>> C = np.dot(U, np.dot(np.diag(S), np.dot(V.conj().T))) 

287 

288The SVD can also be computed "fresh" by combining both the ID and conversion 

289steps into one command. Following the various ID algorithms above, there are 

290correspondingly various SVD algorithms that one can employ. 

291 

292From matrix entries 

293................... 

294 

295We consider first SVD algorithms for a matrix given in terms of its entries. 

296 

297To compute an SVD to a fixed precision, type: 

298 

299>>> U, S, V = sli.svd(A, eps) 

300 

301To compute an SVD to a fixed rank, use: 

302 

303>>> U, S, V = sli.svd(A, k) 

304 

305Both algorithms use random sampling; for the determinstic versions, issue the 

306keyword ``rand=False`` as above. 

307 

308From matrix action 

309.................. 

310 

311Now consider a matrix given in terms of its action on a vector. 

312 

313To compute an SVD to a fixed precision, type: 

314 

315>>> U, S, V = sli.svd(L, eps) 

316 

317To compute an SVD to a fixed rank, use: 

318 

319>>> U, S, V = sli.svd(L, k) 

320 

321Utility routines 

322---------------- 

323 

324Several utility routines are also available. 

325 

326To estimate the spectral norm of a matrix, use: 

327 

328>>> snorm = sli.estimate_spectral_norm(A) 

329 

330This algorithm is based on the randomized power method and thus requires only 

331matrix-vector products. The number of iterations to take can be set using the 

332keyword ``its`` (default: ``its=20``). The matrix is interpreted as a 

333:class:`scipy.sparse.linalg.LinearOperator`, but it is also valid to supply it 

334as a :class:`numpy.ndarray`, in which case it is trivially converted using 

335:func:`scipy.sparse.linalg.aslinearoperator`. 

336 

337The same algorithm can also estimate the spectral norm of the difference of two 

338matrices ``A1`` and ``A2`` as follows: 

339 

340>>> diff = sli.estimate_spectral_norm_diff(A1, A2) 

341 

342This is often useful for checking the accuracy of a matrix approximation. 

343 

344Some routines in :mod:`scipy.linalg.interpolative` require estimating the rank 

345of a matrix as well. This can be done with either: 

346 

347>>> k = sli.estimate_rank(A, eps) 

348 

349or: 

350 

351>>> k = sli.estimate_rank(L, eps) 

352 

353depending on the representation. The parameter ``eps`` controls the definition 

354of the numerical rank. 

355 

356Finally, the random number generation required for all randomized routines can 

357be controlled via :func:`scipy.linalg.interpolative.seed`. To reset the seed 

358values to their original values, use: 

359 

360>>> sli.seed('default') 

361 

362To specify the seed values, use: 

363 

364>>> sli.seed(s) 

365 

366where ``s`` must be an integer or array of 55 floats. If an integer, the array 

367of floats is obtained by using ``numpy.random.rand`` with the given integer 

368seed. 

369 

370To simply generate some random numbers, type: 

371 

372>>> sli.rand(n) 

373 

374where ``n`` is the number of random numbers to generate. 

375 

376Remarks 

377------- 

378 

379The above functions all automatically detect the appropriate interface and work 

380with both real and complex data types, passing input arguments to the proper 

381backend routine. 

382 

383""" 

384 

385import scipy.linalg._interpolative_backend as _backend 

386import numpy as np 

387import sys 

388 

389__all__ = [ 

390 'estimate_rank', 

391 'estimate_spectral_norm', 

392 'estimate_spectral_norm_diff', 

393 'id_to_svd', 

394 'interp_decomp', 

395 'rand', 

396 'reconstruct_interp_matrix', 

397 'reconstruct_matrix_from_id', 

398 'reconstruct_skel_matrix', 

399 'seed', 

400 'svd', 

401] 

402 

403_DTYPE_ERROR = ValueError("invalid input dtype (input must be float64 or complex128)") 

404_TYPE_ERROR = TypeError("invalid input type (must be array or LinearOperator)") 

405_32BIT_ERROR = ValueError("interpolative decomposition on 32-bit systems " 

406 "with complex128 is buggy") 

407_IS_32BIT = (sys.maxsize < 2**32) 

408 

409 

410def _is_real(A): 

411 try: 

412 if A.dtype == np.complex128: 

413 return False 

414 elif A.dtype == np.float64: 

415 return True 

416 else: 

417 raise _DTYPE_ERROR 

418 except AttributeError as e: 

419 raise _TYPE_ERROR from e 

420 

421 

422def seed(seed=None): 

423 """ 

424 Seed the internal random number generator used in this ID package. 

425 

426 The generator is a lagged Fibonacci method with 55-element internal state. 

427 

428 Parameters 

429 ---------- 

430 seed : int, sequence, 'default', optional 

431 If 'default', the random seed is reset to a default value. 

432 

433 If `seed` is a sequence containing 55 floating-point numbers 

434 in range [0,1], these are used to set the internal state of 

435 the generator. 

436 

437 If the value is an integer, the internal state is obtained 

438 from `numpy.random.RandomState` (MT19937) with the integer 

439 used as the initial seed. 

440 

441 If `seed` is omitted (None), ``numpy.random.rand`` is used to 

442 initialize the generator. 

443 

444 """ 

445 # For details, see :func:`_backend.id_srand`, :func:`_backend.id_srandi`, 

446 # and :func:`_backend.id_srando`. 

447 

448 if isinstance(seed, str) and seed == 'default': 

449 _backend.id_srando() 

450 elif hasattr(seed, '__len__'): 

451 state = np.asfortranarray(seed, dtype=float) 

452 if state.shape != (55,): 

453 raise ValueError("invalid input size") 

454 elif state.min() < 0 or state.max() > 1: 

455 raise ValueError("values not in range [0,1]") 

456 _backend.id_srandi(state) 

457 elif seed is None: 

458 _backend.id_srandi(np.random.rand(55)) 

459 else: 

460 rnd = np.random.RandomState(seed) 

461 _backend.id_srandi(rnd.rand(55)) 

462 

463 

464def rand(*shape): 

465 """ 

466 Generate standard uniform pseudorandom numbers via a very efficient lagged 

467 Fibonacci method. 

468 

469 This routine is used for all random number generation in this package and 

470 can affect ID and SVD results. 

471 

472 Parameters 

473 ---------- 

474 *shape 

475 Shape of output array 

476 

477 """ 

478 # For details, see :func:`_backend.id_srand`, and :func:`_backend.id_srando`. 

479 return _backend.id_srand(np.prod(shape)).reshape(shape) 

480 

481 

482def interp_decomp(A, eps_or_k, rand=True): 

483 """ 

484 Compute ID of a matrix. 

485 

486 An ID of a matrix `A` is a factorization defined by a rank `k`, a column 

487 index array `idx`, and interpolation coefficients `proj` such that:: 

488 

489 numpy.dot(A[:,idx[:k]], proj) = A[:,idx[k:]] 

490 

491 The original matrix can then be reconstructed as:: 

492 

493 numpy.hstack([A[:,idx[:k]], 

494 numpy.dot(A[:,idx[:k]], proj)] 

495 )[:,numpy.argsort(idx)] 

496 

497 or via the routine :func:`reconstruct_matrix_from_id`. This can 

498 equivalently be written as:: 

499 

500 numpy.dot(A[:,idx[:k]], 

501 numpy.hstack([numpy.eye(k), proj]) 

502 )[:,np.argsort(idx)] 

503 

504 in terms of the skeleton and interpolation matrices:: 

505 

506 B = A[:,idx[:k]] 

507 

508 and:: 

509 

510 P = numpy.hstack([numpy.eye(k), proj])[:,np.argsort(idx)] 

511 

512 respectively. See also :func:`reconstruct_interp_matrix` and 

513 :func:`reconstruct_skel_matrix`. 

514 

515 The ID can be computed to any relative precision or rank (depending on the 

516 value of `eps_or_k`). If a precision is specified (`eps_or_k < 1`), then 

517 this function has the output signature:: 

518 

519 k, idx, proj = interp_decomp(A, eps_or_k) 

520 

521 Otherwise, if a rank is specified (`eps_or_k >= 1`), then the output 

522 signature is:: 

523 

524 idx, proj = interp_decomp(A, eps_or_k) 

525 

526 .. This function automatically detects the form of the input parameters 

527 and passes them to the appropriate backend. For details, see 

528 :func:`_backend.iddp_id`, :func:`_backend.iddp_aid`, 

529 :func:`_backend.iddp_rid`, :func:`_backend.iddr_id`, 

530 :func:`_backend.iddr_aid`, :func:`_backend.iddr_rid`, 

531 :func:`_backend.idzp_id`, :func:`_backend.idzp_aid`, 

532 :func:`_backend.idzp_rid`, :func:`_backend.idzr_id`, 

533 :func:`_backend.idzr_aid`, and :func:`_backend.idzr_rid`. 

534 

535 Parameters 

536 ---------- 

537 A : :class:`numpy.ndarray` or :class:`scipy.sparse.linalg.LinearOperator` with `rmatvec` 

538 Matrix to be factored 

539 eps_or_k : float or int 

540 Relative error (if `eps_or_k < 1`) or rank (if `eps_or_k >= 1`) of 

541 approximation. 

542 rand : bool, optional 

543 Whether to use random sampling if `A` is of type :class:`numpy.ndarray` 

544 (randomized algorithms are always used if `A` is of type 

545 :class:`scipy.sparse.linalg.LinearOperator`). 

546 

547 Returns 

548 ------- 

549 k : int 

550 Rank required to achieve specified relative precision if 

551 `eps_or_k < 1`. 

552 idx : :class:`numpy.ndarray` 

553 Column index array. 

554 proj : :class:`numpy.ndarray` 

555 Interpolation coefficients. 

556 """ 

557 from scipy.sparse.linalg import LinearOperator 

558 

559 real = _is_real(A) 

560 

561 if isinstance(A, np.ndarray): 

562 if eps_or_k < 1: 

563 eps = eps_or_k 

564 if rand: 

565 if real: 

566 k, idx, proj = _backend.iddp_aid(eps, A) 

567 else: 

568 if _IS_32BIT: 

569 raise _32BIT_ERROR 

570 k, idx, proj = _backend.idzp_aid(eps, A) 

571 else: 

572 if real: 

573 k, idx, proj = _backend.iddp_id(eps, A) 

574 else: 

575 k, idx, proj = _backend.idzp_id(eps, A) 

576 return k, idx - 1, proj 

577 else: 

578 k = int(eps_or_k) 

579 if rand: 

580 if real: 

581 idx, proj = _backend.iddr_aid(A, k) 

582 else: 

583 if _IS_32BIT: 

584 raise _32BIT_ERROR 

585 idx, proj = _backend.idzr_aid(A, k) 

586 else: 

587 if real: 

588 idx, proj = _backend.iddr_id(A, k) 

589 else: 

590 idx, proj = _backend.idzr_id(A, k) 

591 return idx - 1, proj 

592 elif isinstance(A, LinearOperator): 

593 m, n = A.shape 

594 matveca = A.rmatvec 

595 if eps_or_k < 1: 

596 eps = eps_or_k 

597 if real: 

598 k, idx, proj = _backend.iddp_rid(eps, m, n, matveca) 

599 else: 

600 if _IS_32BIT: 

601 raise _32BIT_ERROR 

602 k, idx, proj = _backend.idzp_rid(eps, m, n, matveca) 

603 return k, idx - 1, proj 

604 else: 

605 k = int(eps_or_k) 

606 if real: 

607 idx, proj = _backend.iddr_rid(m, n, matveca, k) 

608 else: 

609 if _IS_32BIT: 

610 raise _32BIT_ERROR 

611 idx, proj = _backend.idzr_rid(m, n, matveca, k) 

612 return idx - 1, proj 

613 else: 

614 raise _TYPE_ERROR 

615 

616 

617def reconstruct_matrix_from_id(B, idx, proj): 

618 """ 

619 Reconstruct matrix from its ID. 

620 

621 A matrix `A` with skeleton matrix `B` and ID indices and coefficients `idx` 

622 and `proj`, respectively, can be reconstructed as:: 

623 

624 numpy.hstack([B, numpy.dot(B, proj)])[:,numpy.argsort(idx)] 

625 

626 See also :func:`reconstruct_interp_matrix` and 

627 :func:`reconstruct_skel_matrix`. 

628 

629 .. This function automatically detects the matrix data type and calls the 

630 appropriate backend. For details, see :func:`_backend.idd_reconid` and 

631 :func:`_backend.idz_reconid`. 

632 

633 Parameters 

634 ---------- 

635 B : :class:`numpy.ndarray` 

636 Skeleton matrix. 

637 idx : :class:`numpy.ndarray` 

638 Column index array. 

639 proj : :class:`numpy.ndarray` 

640 Interpolation coefficients. 

641 

642 Returns 

643 ------- 

644 :class:`numpy.ndarray` 

645 Reconstructed matrix. 

646 """ 

647 if _is_real(B): 

648 return _backend.idd_reconid(B, idx + 1, proj) 

649 else: 

650 return _backend.idz_reconid(B, idx + 1, proj) 

651 

652 

653def reconstruct_interp_matrix(idx, proj): 

654 """ 

655 Reconstruct interpolation matrix from ID. 

656 

657 The interpolation matrix can be reconstructed from the ID indices and 

658 coefficients `idx` and `proj`, respectively, as:: 

659 

660 P = numpy.hstack([numpy.eye(proj.shape[0]), proj])[:,numpy.argsort(idx)] 

661 

662 The original matrix can then be reconstructed from its skeleton matrix `B` 

663 via:: 

664 

665 numpy.dot(B, P) 

666 

667 See also :func:`reconstruct_matrix_from_id` and 

668 :func:`reconstruct_skel_matrix`. 

669 

670 .. This function automatically detects the matrix data type and calls the 

671 appropriate backend. For details, see :func:`_backend.idd_reconint` and 

672 :func:`_backend.idz_reconint`. 

673 

674 Parameters 

675 ---------- 

676 idx : :class:`numpy.ndarray` 

677 Column index array. 

678 proj : :class:`numpy.ndarray` 

679 Interpolation coefficients. 

680 

681 Returns 

682 ------- 

683 :class:`numpy.ndarray` 

684 Interpolation matrix. 

685 """ 

686 if _is_real(proj): 

687 return _backend.idd_reconint(idx + 1, proj) 

688 else: 

689 return _backend.idz_reconint(idx + 1, proj) 

690 

691 

692def reconstruct_skel_matrix(A, k, idx): 

693 """ 

694 Reconstruct skeleton matrix from ID. 

695 

696 The skeleton matrix can be reconstructed from the original matrix `A` and its 

697 ID rank and indices `k` and `idx`, respectively, as:: 

698 

699 B = A[:,idx[:k]] 

700 

701 The original matrix can then be reconstructed via:: 

702 

703 numpy.hstack([B, numpy.dot(B, proj)])[:,numpy.argsort(idx)] 

704 

705 See also :func:`reconstruct_matrix_from_id` and 

706 :func:`reconstruct_interp_matrix`. 

707 

708 .. This function automatically detects the matrix data type and calls the 

709 appropriate backend. For details, see :func:`_backend.idd_copycols` and 

710 :func:`_backend.idz_copycols`. 

711 

712 Parameters 

713 ---------- 

714 A : :class:`numpy.ndarray` 

715 Original matrix. 

716 k : int 

717 Rank of ID. 

718 idx : :class:`numpy.ndarray` 

719 Column index array. 

720 

721 Returns 

722 ------- 

723 :class:`numpy.ndarray` 

724 Skeleton matrix. 

725 """ 

726 if _is_real(A): 

727 return _backend.idd_copycols(A, k, idx + 1) 

728 else: 

729 return _backend.idz_copycols(A, k, idx + 1) 

730 

731 

732def id_to_svd(B, idx, proj): 

733 """ 

734 Convert ID to SVD. 

735 

736 The SVD reconstruction of a matrix with skeleton matrix `B` and ID indices and 

737 coefficients `idx` and `proj`, respectively, is:: 

738 

739 U, S, V = id_to_svd(B, idx, proj) 

740 A = numpy.dot(U, numpy.dot(numpy.diag(S), V.conj().T)) 

741 

742 See also :func:`svd`. 

743 

744 .. This function automatically detects the matrix data type and calls the 

745 appropriate backend. For details, see :func:`_backend.idd_id2svd` and 

746 :func:`_backend.idz_id2svd`. 

747 

748 Parameters 

749 ---------- 

750 B : :class:`numpy.ndarray` 

751 Skeleton matrix. 

752 idx : :class:`numpy.ndarray` 

753 Column index array. 

754 proj : :class:`numpy.ndarray` 

755 Interpolation coefficients. 

756 

757 Returns 

758 ------- 

759 U : :class:`numpy.ndarray` 

760 Left singular vectors. 

761 S : :class:`numpy.ndarray` 

762 Singular values. 

763 V : :class:`numpy.ndarray` 

764 Right singular vectors. 

765 """ 

766 if _is_real(B): 

767 U, V, S = _backend.idd_id2svd(B, idx + 1, proj) 

768 else: 

769 U, V, S = _backend.idz_id2svd(B, idx + 1, proj) 

770 return U, S, V 

771 

772 

773def estimate_spectral_norm(A, its=20): 

774 """ 

775 Estimate spectral norm of a matrix by the randomized power method. 

776 

777 .. This function automatically detects the matrix data type and calls the 

778 appropriate backend. For details, see :func:`_backend.idd_snorm` and 

779 :func:`_backend.idz_snorm`. 

780 

781 Parameters 

782 ---------- 

783 A : :class:`scipy.sparse.linalg.LinearOperator` 

784 Matrix given as a :class:`scipy.sparse.linalg.LinearOperator` with the 

785 `matvec` and `rmatvec` methods (to apply the matrix and its adjoint). 

786 its : int, optional 

787 Number of power method iterations. 

788 

789 Returns 

790 ------- 

791 float 

792 Spectral norm estimate. 

793 """ 

794 from scipy.sparse.linalg import aslinearoperator 

795 A = aslinearoperator(A) 

796 m, n = A.shape 

797 matvec = lambda x: A. matvec(x) 

798 matveca = lambda x: A.rmatvec(x) 

799 if _is_real(A): 

800 return _backend.idd_snorm(m, n, matveca, matvec, its=its) 

801 else: 

802 return _backend.idz_snorm(m, n, matveca, matvec, its=its) 

803 

804 

805def estimate_spectral_norm_diff(A, B, its=20): 

806 """ 

807 Estimate spectral norm of the difference of two matrices by the randomized 

808 power method. 

809 

810 .. This function automatically detects the matrix data type and calls the 

811 appropriate backend. For details, see :func:`_backend.idd_diffsnorm` and 

812 :func:`_backend.idz_diffsnorm`. 

813 

814 Parameters 

815 ---------- 

816 A : :class:`scipy.sparse.linalg.LinearOperator` 

817 First matrix given as a :class:`scipy.sparse.linalg.LinearOperator` with the 

818 `matvec` and `rmatvec` methods (to apply the matrix and its adjoint). 

819 B : :class:`scipy.sparse.linalg.LinearOperator` 

820 Second matrix given as a :class:`scipy.sparse.linalg.LinearOperator` with 

821 the `matvec` and `rmatvec` methods (to apply the matrix and its adjoint). 

822 its : int, optional 

823 Number of power method iterations. 

824 

825 Returns 

826 ------- 

827 float 

828 Spectral norm estimate of matrix difference. 

829 """ 

830 from scipy.sparse.linalg import aslinearoperator 

831 A = aslinearoperator(A) 

832 B = aslinearoperator(B) 

833 m, n = A.shape 

834 matvec1 = lambda x: A. matvec(x) 

835 matveca1 = lambda x: A.rmatvec(x) 

836 matvec2 = lambda x: B. matvec(x) 

837 matveca2 = lambda x: B.rmatvec(x) 

838 if _is_real(A): 

839 return _backend.idd_diffsnorm( 

840 m, n, matveca1, matveca2, matvec1, matvec2, its=its) 

841 else: 

842 return _backend.idz_diffsnorm( 

843 m, n, matveca1, matveca2, matvec1, matvec2, its=its) 

844 

845 

846def svd(A, eps_or_k, rand=True): 

847 """ 

848 Compute SVD of a matrix via an ID. 

849 

850 An SVD of a matrix `A` is a factorization:: 

851 

852 A = numpy.dot(U, numpy.dot(numpy.diag(S), V.conj().T)) 

853 

854 where `U` and `V` have orthonormal columns and `S` is nonnegative. 

855 

856 The SVD can be computed to any relative precision or rank (depending on the 

857 value of `eps_or_k`). 

858 

859 See also :func:`interp_decomp` and :func:`id_to_svd`. 

860 

861 .. This function automatically detects the form of the input parameters and 

862 passes them to the appropriate backend. For details, see 

863 :func:`_backend.iddp_svd`, :func:`_backend.iddp_asvd`, 

864 :func:`_backend.iddp_rsvd`, :func:`_backend.iddr_svd`, 

865 :func:`_backend.iddr_asvd`, :func:`_backend.iddr_rsvd`, 

866 :func:`_backend.idzp_svd`, :func:`_backend.idzp_asvd`, 

867 :func:`_backend.idzp_rsvd`, :func:`_backend.idzr_svd`, 

868 :func:`_backend.idzr_asvd`, and :func:`_backend.idzr_rsvd`. 

869 

870 Parameters 

871 ---------- 

872 A : :class:`numpy.ndarray` or :class:`scipy.sparse.linalg.LinearOperator` 

873 Matrix to be factored, given as either a :class:`numpy.ndarray` or a 

874 :class:`scipy.sparse.linalg.LinearOperator` with the `matvec` and 

875 `rmatvec` methods (to apply the matrix and its adjoint). 

876 eps_or_k : float or int 

877 Relative error (if `eps_or_k < 1`) or rank (if `eps_or_k >= 1`) of 

878 approximation. 

879 rand : bool, optional 

880 Whether to use random sampling if `A` is of type :class:`numpy.ndarray` 

881 (randomized algorithms are always used if `A` is of type 

882 :class:`scipy.sparse.linalg.LinearOperator`). 

883 

884 Returns 

885 ------- 

886 U : :class:`numpy.ndarray` 

887 Left singular vectors. 

888 S : :class:`numpy.ndarray` 

889 Singular values. 

890 V : :class:`numpy.ndarray` 

891 Right singular vectors. 

892 """ 

893 from scipy.sparse.linalg import LinearOperator 

894 

895 real = _is_real(A) 

896 

897 if isinstance(A, np.ndarray): 

898 if eps_or_k < 1: 

899 eps = eps_or_k 

900 if rand: 

901 if real: 

902 U, V, S = _backend.iddp_asvd(eps, A) 

903 else: 

904 if _IS_32BIT: 

905 raise _32BIT_ERROR 

906 U, V, S = _backend.idzp_asvd(eps, A) 

907 else: 

908 if real: 

909 U, V, S = _backend.iddp_svd(eps, A) 

910 else: 

911 U, V, S = _backend.idzp_svd(eps, A) 

912 else: 

913 k = int(eps_or_k) 

914 if k > min(A.shape): 

915 raise ValueError("Approximation rank %s exceeds min(A.shape) = " 

916 " %s " % (k, min(A.shape))) 

917 if rand: 

918 if real: 

919 U, V, S = _backend.iddr_asvd(A, k) 

920 else: 

921 if _IS_32BIT: 

922 raise _32BIT_ERROR 

923 U, V, S = _backend.idzr_asvd(A, k) 

924 else: 

925 if real: 

926 U, V, S = _backend.iddr_svd(A, k) 

927 else: 

928 U, V, S = _backend.idzr_svd(A, k) 

929 elif isinstance(A, LinearOperator): 

930 m, n = A.shape 

931 matvec = lambda x: A.matvec(x) 

932 matveca = lambda x: A.rmatvec(x) 

933 if eps_or_k < 1: 

934 eps = eps_or_k 

935 if real: 

936 U, V, S = _backend.iddp_rsvd(eps, m, n, matveca, matvec) 

937 else: 

938 if _IS_32BIT: 

939 raise _32BIT_ERROR 

940 U, V, S = _backend.idzp_rsvd(eps, m, n, matveca, matvec) 

941 else: 

942 k = int(eps_or_k) 

943 if real: 

944 U, V, S = _backend.iddr_rsvd(m, n, matveca, matvec, k) 

945 else: 

946 if _IS_32BIT: 

947 raise _32BIT_ERROR 

948 U, V, S = _backend.idzr_rsvd(m, n, matveca, matvec, k) 

949 else: 

950 raise _TYPE_ERROR 

951 return U, S, V 

952 

953 

954def estimate_rank(A, eps): 

955 """ 

956 Estimate matrix rank to a specified relative precision using randomized 

957 methods. 

958 

959 The matrix `A` can be given as either a :class:`numpy.ndarray` or a 

960 :class:`scipy.sparse.linalg.LinearOperator`, with different algorithms used 

961 for each case. If `A` is of type :class:`numpy.ndarray`, then the output 

962 rank is typically about 8 higher than the actual numerical rank. 

963 

964 .. This function automatically detects the form of the input parameters and 

965 passes them to the appropriate backend. For details, 

966 see :func:`_backend.idd_estrank`, :func:`_backend.idd_findrank`, 

967 :func:`_backend.idz_estrank`, and :func:`_backend.idz_findrank`. 

968 

969 Parameters 

970 ---------- 

971 A : :class:`numpy.ndarray` or :class:`scipy.sparse.linalg.LinearOperator` 

972 Matrix whose rank is to be estimated, given as either a 

973 :class:`numpy.ndarray` or a :class:`scipy.sparse.linalg.LinearOperator` 

974 with the `rmatvec` method (to apply the matrix adjoint). 

975 eps : float 

976 Relative error for numerical rank definition. 

977 

978 Returns 

979 ------- 

980 int 

981 Estimated matrix rank. 

982 """ 

983 from scipy.sparse.linalg import LinearOperator 

984 

985 real = _is_real(A) 

986 

987 if isinstance(A, np.ndarray): 

988 if real: 

989 rank = _backend.idd_estrank(eps, A) 

990 else: 

991 rank = _backend.idz_estrank(eps, A) 

992 if rank == 0: 

993 # special return value for nearly full rank 

994 rank = min(A.shape) 

995 return rank 

996 elif isinstance(A, LinearOperator): 

997 m, n = A.shape 

998 matveca = A.rmatvec 

999 if real: 

1000 return _backend.idd_findrank(eps, m, n, matveca) 

1001 else: 

1002 return _backend.idz_findrank(eps, m, n, matveca) 

1003 else: 

1004 raise _TYPE_ERROR