| | |
| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | """ |
| | The symmetric eigenvalue problem. |
| | --------------------------------- |
| | |
| | This file contains routines for the symmetric eigenvalue problem. |
| | |
| | high level routines: |
| | |
| | eigsy : real symmetric (ordinary) eigenvalue problem |
| | eighe : complex hermitian (ordinary) eigenvalue problem |
| | eigh : unified interface for eigsy and eighe |
| | svd_r : singular value decomposition for real matrices |
| | svd_c : singular value decomposition for complex matrices |
| | svd : unified interface for svd_r and svd_c |
| | |
| | |
| | low level routines: |
| | |
| | r_sy_tridiag : reduction of real symmetric matrix to real symmetric tridiagonal matrix |
| | c_he_tridiag_0 : reduction of complex hermitian matrix to real symmetric tridiagonal matrix |
| | c_he_tridiag_1 : auxiliary routine to c_he_tridiag_0 |
| | c_he_tridiag_2 : auxiliary routine to c_he_tridiag_0 |
| | tridiag_eigen : solves the real symmetric tridiagonal matrix eigenvalue problem |
| | svd_r_raw : raw singular value decomposition for real matrices |
| | svd_c_raw : raw singular value decomposition for complex matrices |
| | """ |
| |
|
| | from ..libmp.backend import xrange |
| | from .eigen import defun |
| |
|
| |
|
| | def r_sy_tridiag(ctx, A, D, E, calc_ev = True): |
| | """ |
| | This routine transforms a real symmetric matrix A to a real symmetric |
| | tridiagonal matrix T using an orthogonal similarity transformation: |
| | Q' * A * Q = T (here ' denotes the matrix transpose). |
| | The orthogonal matrix Q is build up from Householder reflectors. |
| | |
| | parameters: |
| | A (input/output) On input, A contains the real symmetric matrix of |
| | dimension (n,n). On output, if calc_ev is true, A contains the |
| | orthogonal matrix Q, otherwise A is destroyed. |
| | |
| | D (output) real array of length n, contains the diagonal elements |
| | of the tridiagonal matrix |
| | |
| | E (output) real array of length n, contains the offdiagonal elements |
| | of the tridiagonal matrix in E[0:(n-1)] where is the dimension of |
| | the matrix A. E[n-1] is undefined. |
| | |
| | calc_ev (input) If calc_ev is true, this routine explicitly calculates the |
| | orthogonal matrix Q which is then returned in A. If calc_ev is |
| | false, Q is not explicitly calculated resulting in a shorter run time. |
| | |
| | This routine is a python translation of the fortran routine tred2.f in the |
| | software library EISPACK (see netlib.org) which itself is based on the algol |
| | procedure tred2 described in: |
| | - Num. Math. 11, p.181-195 (1968) by Martin, Reinsch and Wilkonson |
| | - Handbook for auto. comp., Vol II, Linear Algebra, p.212-226 (1971) |
| | |
| | For a good introduction to Householder reflections, see also |
| | Stoer, Bulirsch - Introduction to Numerical Analysis. |
| | """ |
| |
|
| | |
| | |
| |
|
| | n = A.rows |
| | for i in xrange(n - 1, 0, -1): |
| | |
| |
|
| | scale = 0 |
| | for k in xrange(0, i): |
| | scale += abs(A[k,i]) |
| |
|
| | scale_inv = 0 |
| | if scale != 0: |
| | scale_inv = 1/scale |
| |
|
| | |
| |
|
| | if i == 1 or scale == 0 or ctx.isinf(scale_inv): |
| | E[i] = A[i-1,i] |
| | D[i] = 0 |
| | continue |
| |
|
| | |
| |
|
| | H = 0 |
| | for k in xrange(0, i): |
| | A[k,i] *= scale_inv |
| | H += A[k,i] * A[k,i] |
| |
|
| | F = A[i-1,i] |
| | G = ctx.sqrt(H) |
| | if F > 0: |
| | G = -G |
| | E[i] = scale * G |
| | H -= F * G |
| | A[i-1,i] = F - G |
| | F = 0 |
| |
|
| | |
| |
|
| | for j in xrange(0, i): |
| | if calc_ev: |
| | A[i,j] = A[j,i] / H |
| |
|
| | G = 0 |
| | for k in xrange(0, j + 1): |
| | G += A[k,j] * A[k,i] |
| | for k in xrange(j + 1, i): |
| | G += A[j,k] * A[k,i] |
| |
|
| | E[j] = G / H |
| | F += E[j] * A[j,i] |
| |
|
| | HH = F / (2 * H) |
| |
|
| | for j in xrange(0, i): |
| | F = A[j,i] |
| | G = E[j] - HH * F |
| | E[j] = G |
| |
|
| | for k in xrange(0, j + 1): |
| | A[k,j] -= F * E[k] + G * A[k,i] |
| |
|
| | D[i] = H |
| |
|
| | for i in xrange(1, n): |
| | E[i-1] = E[i] |
| | E[n-1] = 0 |
| |
|
| | if calc_ev: |
| | D[0] = 0 |
| | for i in xrange(0, n): |
| | if D[i] != 0: |
| | for j in xrange(0, i): |
| | G = 0 |
| | for k in xrange(0, i): |
| | G += A[i,k] * A[k,j] |
| | for k in xrange(0, i): |
| | A[k,j] -= G * A[k,i] |
| |
|
| | D[i] = A[i,i] |
| | A[i,i] = 1 |
| |
|
| | for j in xrange(0, i): |
| | A[j,i] = A[i,j] = 0 |
| | else: |
| | for i in xrange(0, n): |
| | D[i] = A[i,i] |
| |
|
| |
|
| |
|
| |
|
| |
|
| | def c_he_tridiag_0(ctx, A, D, E, T): |
| | """ |
| | This routine transforms a complex hermitian matrix A to a real symmetric |
| | tridiagonal matrix T using an unitary similarity transformation: |
| | Q' * A * Q = T (here ' denotes the hermitian matrix transpose, |
| | i.e. transposition und conjugation). |
| | The unitary matrix Q is build up from Householder reflectors and |
| | an unitary diagonal matrix. |
| | |
| | parameters: |
| | A (input/output) On input, A contains the complex hermitian matrix |
| | of dimension (n,n). On output, A contains the unitary matrix Q |
| | in compressed form. |
| | |
| | D (output) real array of length n, contains the diagonal elements |
| | of the tridiagonal matrix. |
| | |
| | E (output) real array of length n, contains the offdiagonal elements |
| | of the tridiagonal matrix in E[0:(n-1)] where is the dimension of |
| | the matrix A. E[n-1] is undefined. |
| | |
| | T (output) complex array of length n, contains a unitary diagonal |
| | matrix. |
| | |
| | This routine is a python translation (in slightly modified form) of the fortran |
| | routine htridi.f in the software library EISPACK (see netlib.org) which itself |
| | is a complex version of the algol procedure tred1 described in: |
| | - Num. Math. 11, p.181-195 (1968) by Martin, Reinsch and Wilkonson |
| | - Handbook for auto. comp., Vol II, Linear Algebra, p.212-226 (1971) |
| | |
| | For a good introduction to Householder reflections, see also |
| | Stoer, Bulirsch - Introduction to Numerical Analysis. |
| | """ |
| |
|
| | n = A.rows |
| | T[n-1] = 1 |
| | for i in xrange(n - 1, 0, -1): |
| |
|
| | |
| |
|
| | scale = 0 |
| | for k in xrange(0, i): |
| | scale += abs(ctx.re(A[k,i])) + abs(ctx.im(A[k,i])) |
| |
|
| | scale_inv = 0 |
| | if scale != 0: |
| | scale_inv = 1 / scale |
| |
|
| | |
| |
|
| | if scale == 0 or ctx.isinf(scale_inv): |
| | E[i] = 0 |
| | D[i] = 0 |
| | T[i-1] = 1 |
| | continue |
| |
|
| | if i == 1: |
| | F = A[i-1,i] |
| | f = abs(F) |
| | E[i] = f |
| | D[i] = 0 |
| | if f != 0: |
| | T[i-1] = T[i] * F / f |
| | else: |
| | T[i-1] = T[i] |
| | continue |
| |
|
| | |
| |
|
| | H = 0 |
| | for k in xrange(0, i): |
| | A[k,i] *= scale_inv |
| | rr = ctx.re(A[k,i]) |
| | ii = ctx.im(A[k,i]) |
| | H += rr * rr + ii * ii |
| |
|
| | F = A[i-1,i] |
| | f = abs(F) |
| | G = ctx.sqrt(H) |
| | H += G * f |
| | E[i] = scale * G |
| | if f != 0: |
| | F = F / f |
| | TZ = - T[i] * F |
| | G *= F |
| | else: |
| | TZ = -T[i] |
| | A[i-1,i] += G |
| | F = 0 |
| |
|
| | |
| |
|
| | for j in xrange(0, i): |
| | A[i,j] = A[j,i] / H |
| |
|
| | G = 0 |
| | for k in xrange(0, j + 1): |
| | G += ctx.conj(A[k,j]) * A[k,i] |
| | for k in xrange(j + 1, i): |
| | G += A[j,k] * A[k,i] |
| |
|
| | T[j] = G / H |
| | F += ctx.conj(T[j]) * A[j,i] |
| |
|
| | HH = F / (2 * H) |
| |
|
| | for j in xrange(0, i): |
| | F = A[j,i] |
| | G = T[j] - HH * F |
| | T[j] = G |
| |
|
| | for k in xrange(0, j + 1): |
| | A[k,j] -= ctx.conj(F) * T[k] + ctx.conj(G) * A[k,i] |
| | |
| | |
| |
|
| | T[i-1] = TZ |
| | D[i] = H |
| |
|
| | for i in xrange(1, n): |
| | E[i-1] = E[i] |
| | E[n-1] = 0 |
| |
|
| | D[0] = 0 |
| | for i in xrange(0, n): |
| | zw = D[i] |
| | D[i] = ctx.re(A[i,i]) |
| | A[i,i] = zw |
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | def c_he_tridiag_1(ctx, A, T): |
| | """ |
| | This routine forms the unitary matrix Q described in c_he_tridiag_0. |
| | |
| | parameters: |
| | A (input/output) On input, A is the same matrix as delivered by |
| | c_he_tridiag_0. On output, A is set to Q. |
| | |
| | T (input) On input, T is the same array as delivered by c_he_tridiag_0. |
| | |
| | """ |
| |
|
| | n = A.rows |
| |
|
| | for i in xrange(0, n): |
| | if A[i,i] != 0: |
| | for j in xrange(0, i): |
| | G = 0 |
| | for k in xrange(0, i): |
| | G += ctx.conj(A[i,k]) * A[k,j] |
| | for k in xrange(0, i): |
| | A[k,j] -= G * A[k,i] |
| |
|
| | A[i,i] = 1 |
| |
|
| | for j in xrange(0, i): |
| | A[j,i] = A[i,j] = 0 |
| |
|
| | for i in xrange(0, n): |
| | for k in xrange(0, n): |
| | A[i,k] *= T[k] |
| |
|
| |
|
| |
|
| |
|
| | def c_he_tridiag_2(ctx, A, T, B): |
| | """ |
| | This routine applied the unitary matrix Q described in c_he_tridiag_0 |
| | onto the the matrix B, i.e. it forms Q*B. |
| | |
| | parameters: |
| | A (input) On input, A is the same matrix as delivered by c_he_tridiag_0. |
| | |
| | T (input) On input, T is the same array as delivered by c_he_tridiag_0. |
| | |
| | B (input/output) On input, B is a complex matrix. On output B is replaced |
| | by Q*B. |
| | |
| | This routine is a python translation of the fortran routine htribk.f in the |
| | software library EISPACK (see netlib.org). See c_he_tridiag_0 for more |
| | references. |
| | """ |
| |
|
| | n = A.rows |
| |
|
| | for i in xrange(0, n): |
| | for k in xrange(0, n): |
| | B[k,i] *= T[k] |
| |
|
| | for i in xrange(0, n): |
| | if A[i,i] != 0: |
| | for j in xrange(0, n): |
| | G = 0 |
| | for k in xrange(0, i): |
| | G += ctx.conj(A[i,k]) * B[k,j] |
| | for k in xrange(0, i): |
| | B[k,j] -= G * A[k,i] |
| |
|
| |
|
| |
|
| |
|
| |
|
| | def tridiag_eigen(ctx, d, e, z = False): |
| | """ |
| | This subroutine find the eigenvalues and the first components of the |
| | eigenvectors of a real symmetric tridiagonal matrix using the implicit |
| | QL method. |
| | |
| | parameters: |
| | |
| | d (input/output) real array of length n. on input, d contains the diagonal |
| | elements of the input matrix. on output, d contains the eigenvalues in |
| | ascending order. |
| | |
| | e (input) real array of length n. on input, e contains the offdiagonal |
| | elements of the input matrix in e[0:(n-1)]. On output, e has been |
| | destroyed. |
| | |
| | z (input/output) If z is equal to False, no eigenvectors will be computed. |
| | Otherwise on input z should have the format z[0:m,0:n] (i.e. a real or |
| | complex matrix of dimension (m,n) ). On output this matrix will be |
| | multiplied by the matrix of the eigenvectors (i.e. the columns of this |
| | matrix are the eigenvectors): z --> z*EV |
| | That means if z[i,j]={1 if j==j; 0 otherwise} on input, then on output |
| | z will contain the first m components of the eigenvectors. That means |
| | if m is equal to n, the i-th eigenvector will be z[:,i]. |
| | |
| | This routine is a python translation (in slightly modified form) of the |
| | fortran routine imtql2.f in the software library EISPACK (see netlib.org) |
| | which itself is based on the algol procudure imtql2 desribed in: |
| | - num. math. 12, p. 377-383(1968) by matrin and wilkinson |
| | - modified in num. math. 15, p. 450(1970) by dubrulle |
| | - handbook for auto. comp., vol. II-linear algebra, p. 241-248 (1971) |
| | See also the routine gaussq.f in netlog.org or acm algorithm 726. |
| | """ |
| |
|
| | n = len(d) |
| | e[n-1] = 0 |
| | iterlim = 2 * ctx.dps |
| |
|
| | for l in xrange(n): |
| | j = 0 |
| | while 1: |
| | m = l |
| | while 1: |
| | |
| | if m + 1 == n: |
| | break |
| | if abs(e[m]) <= ctx.eps * (abs(d[m]) + abs(d[m + 1])): |
| | break |
| | m = m + 1 |
| | if m == l: |
| | break |
| |
|
| | if j >= iterlim: |
| | raise RuntimeError("tridiag_eigen: no convergence to an eigenvalue after %d iterations" % iterlim) |
| |
|
| | j += 1 |
| |
|
| | |
| |
|
| | p = d[l] |
| | g = (d[l + 1] - p) / (2 * e[l]) |
| | r = ctx.hypot(g, 1) |
| |
|
| | if g < 0: |
| | s = g - r |
| | else: |
| | s = g + r |
| |
|
| | g = d[m] - p + e[l] / s |
| |
|
| | s, c, p = 1, 1, 0 |
| |
|
| | for i in xrange(m - 1, l - 1, -1): |
| | f = s * e[i] |
| | b = c * e[i] |
| | if abs(f) > abs(g): |
| | c = g / f |
| | r = ctx.hypot(c, 1) |
| | e[i + 1] = f * r |
| | s = 1 / r |
| | c = c * s |
| | else: |
| | s = f / g |
| | r = ctx.hypot(s, 1) |
| | e[i + 1] = g * r |
| | c = 1 / r |
| | s = s * c |
| | g = d[i + 1] - p |
| | r = (d[i] - g) * s + 2 * c * b |
| | p = s * r |
| | d[i + 1] = g + p |
| | g = c * r - b |
| |
|
| | if not isinstance(z, bool): |
| | |
| | for w in xrange(z.rows): |
| | f = z[w,i+1] |
| | z[w,i+1] = s * z[w,i] + c * f |
| | z[w,i ] = c * z[w,i] - s * f |
| |
|
| | d[l] = d[l] - p |
| | e[l] = g |
| | e[m] = 0 |
| |
|
| | for ii in xrange(1, n): |
| | |
| | i = ii - 1 |
| | k = i |
| | p = d[i] |
| | for j in xrange(ii, n): |
| | if d[j] >= p: |
| | continue |
| | k = j |
| | p = d[k] |
| | if k == i: |
| | continue |
| | d[k] = d[i] |
| | d[i] = p |
| |
|
| | if not isinstance(z, bool): |
| | for w in xrange(z.rows): |
| | p = z[w,i] |
| | z[w,i] = z[w,k] |
| | z[w,k] = p |
| |
|
| | |
| |
|
| | @defun |
| | def eigsy(ctx, A, eigvals_only = False, overwrite_a = False): |
| | """ |
| | This routine solves the (ordinary) eigenvalue problem for a real symmetric |
| | square matrix A. Given A, an orthogonal matrix Q is calculated which |
| | diagonalizes A: |
| | |
| | Q' A Q = diag(E) and Q Q' = Q' Q = 1 |
| | |
| | Here diag(E) is a diagonal matrix whose diagonal is E. |
| | ' denotes the transpose. |
| | |
| | The columns of Q are the eigenvectors of A and E contains the eigenvalues: |
| | |
| | A Q[:,i] = E[i] Q[:,i] |
| | |
| | |
| | input: |
| | |
| | A: real matrix of format (n,n) which is symmetric |
| | (i.e. A=A' or A[i,j]=A[j,i]) |
| | |
| | eigvals_only: if true, calculates only the eigenvalues E. |
| | if false, calculates both eigenvectors and eigenvalues. |
| | |
| | overwrite_a: if true, allows modification of A which may improve |
| | performance. if false, A is not modified. |
| | |
| | output: |
| | |
| | E: vector of format (n). contains the eigenvalues of A in ascending order. |
| | |
| | Q: orthogonal matrix of format (n,n). contains the eigenvectors |
| | of A as columns. |
| | |
| | return value: |
| | |
| | E if eigvals_only is true |
| | (E, Q) if eigvals_only is false |
| | |
| | example: |
| | >>> from mpmath import mp |
| | >>> A = mp.matrix([[3, 2], [2, 0]]) |
| | >>> E = mp.eigsy(A, eigvals_only = True) |
| | >>> print(E) |
| | [-1.0] |
| | [ 4.0] |
| | |
| | >>> A = mp.matrix([[1, 2], [2, 3]]) |
| | >>> E, Q = mp.eigsy(A) |
| | >>> print(mp.chop(A * Q[:,0] - E[0] * Q[:,0])) |
| | [0.0] |
| | [0.0] |
| | |
| | see also: eighe, eigh, eig |
| | """ |
| |
|
| | if not overwrite_a: |
| | A = A.copy() |
| |
|
| | d = ctx.zeros(A.rows, 1) |
| | e = ctx.zeros(A.rows, 1) |
| |
|
| | if eigvals_only: |
| | r_sy_tridiag(ctx, A, d, e, calc_ev = False) |
| | tridiag_eigen(ctx, d, e, False) |
| | return d |
| | else: |
| | r_sy_tridiag(ctx, A, d, e, calc_ev = True) |
| | tridiag_eigen(ctx, d, e, A) |
| | return (d, A) |
| |
|
| |
|
| | @defun |
| | def eighe(ctx, A, eigvals_only = False, overwrite_a = False): |
| | """ |
| | This routine solves the (ordinary) eigenvalue problem for a complex |
| | hermitian square matrix A. Given A, an unitary matrix Q is calculated which |
| | diagonalizes A: |
| | |
| | Q' A Q = diag(E) and Q Q' = Q' Q = 1 |
| | |
| | Here diag(E) a is diagonal matrix whose diagonal is E. |
| | ' denotes the hermitian transpose (i.e. ordinary transposition and |
| | complex conjugation). |
| | |
| | The columns of Q are the eigenvectors of A and E contains the eigenvalues: |
| | |
| | A Q[:,i] = E[i] Q[:,i] |
| | |
| | |
| | input: |
| | |
| | A: complex matrix of format (n,n) which is hermitian |
| | (i.e. A=A' or A[i,j]=conj(A[j,i])) |
| | |
| | eigvals_only: if true, calculates only the eigenvalues E. |
| | if false, calculates both eigenvectors and eigenvalues. |
| | |
| | overwrite_a: if true, allows modification of A which may improve |
| | performance. if false, A is not modified. |
| | |
| | output: |
| | |
| | E: vector of format (n). contains the eigenvalues of A in ascending order. |
| | |
| | Q: unitary matrix of format (n,n). contains the eigenvectors |
| | of A as columns. |
| | |
| | return value: |
| | |
| | E if eigvals_only is true |
| | (E, Q) if eigvals_only is false |
| | |
| | example: |
| | >>> from mpmath import mp |
| | >>> A = mp.matrix([[1, -3 - 1j], [-3 + 1j, -2]]) |
| | >>> E = mp.eighe(A, eigvals_only = True) |
| | >>> print(E) |
| | [-4.0] |
| | [ 3.0] |
| | |
| | >>> A = mp.matrix([[1, 2 + 5j], [2 - 5j, 3]]) |
| | >>> E, Q = mp.eighe(A) |
| | >>> print(mp.chop(A * Q[:,0] - E[0] * Q[:,0])) |
| | [0.0] |
| | [0.0] |
| | |
| | see also: eigsy, eigh, eig |
| | """ |
| |
|
| | if not overwrite_a: |
| | A = A.copy() |
| |
|
| | d = ctx.zeros(A.rows, 1) |
| | e = ctx.zeros(A.rows, 1) |
| | t = ctx.zeros(A.rows, 1) |
| |
|
| | if eigvals_only: |
| | c_he_tridiag_0(ctx, A, d, e, t) |
| | tridiag_eigen(ctx, d, e, False) |
| | return d |
| | else: |
| | c_he_tridiag_0(ctx, A, d, e, t) |
| | B = ctx.eye(A.rows) |
| | tridiag_eigen(ctx, d, e, B) |
| | c_he_tridiag_2(ctx, A, t, B) |
| | return (d, B) |
| |
|
| | @defun |
| | def eigh(ctx, A, eigvals_only = False, overwrite_a = False): |
| | """ |
| | "eigh" is a unified interface for "eigsy" and "eighe". Depending on |
| | whether A is real or complex the appropriate function is called. |
| | |
| | This routine solves the (ordinary) eigenvalue problem for a real symmetric |
| | or complex hermitian square matrix A. Given A, an orthogonal (A real) or |
| | unitary (A complex) matrix Q is calculated which diagonalizes A: |
| | |
| | Q' A Q = diag(E) and Q Q' = Q' Q = 1 |
| | |
| | Here diag(E) a is diagonal matrix whose diagonal is E. |
| | ' denotes the hermitian transpose (i.e. ordinary transposition and |
| | complex conjugation). |
| | |
| | The columns of Q are the eigenvectors of A and E contains the eigenvalues: |
| | |
| | A Q[:,i] = E[i] Q[:,i] |
| | |
| | input: |
| | |
| | A: a real or complex square matrix of format (n,n) which is symmetric |
| | (i.e. A[i,j]=A[j,i]) or hermitian (i.e. A[i,j]=conj(A[j,i])). |
| | |
| | eigvals_only: if true, calculates only the eigenvalues E. |
| | if false, calculates both eigenvectors and eigenvalues. |
| | |
| | overwrite_a: if true, allows modification of A which may improve |
| | performance. if false, A is not modified. |
| | |
| | output: |
| | |
| | E: vector of format (n). contains the eigenvalues of A in ascending order. |
| | |
| | Q: an orthogonal or unitary matrix of format (n,n). contains the |
| | eigenvectors of A as columns. |
| | |
| | return value: |
| | |
| | E if eigvals_only is true |
| | (E, Q) if eigvals_only is false |
| | |
| | example: |
| | >>> from mpmath import mp |
| | >>> A = mp.matrix([[3, 2], [2, 0]]) |
| | >>> E = mp.eigh(A, eigvals_only = True) |
| | >>> print(E) |
| | [-1.0] |
| | [ 4.0] |
| | |
| | >>> A = mp.matrix([[1, 2], [2, 3]]) |
| | >>> E, Q = mp.eigh(A) |
| | >>> print(mp.chop(A * Q[:,0] - E[0] * Q[:,0])) |
| | [0.0] |
| | [0.0] |
| | |
| | >>> A = mp.matrix([[1, 2 + 5j], [2 - 5j, 3]]) |
| | >>> E, Q = mp.eigh(A) |
| | >>> print(mp.chop(A * Q[:,0] - E[0] * Q[:,0])) |
| | [0.0] |
| | [0.0] |
| | |
| | see also: eigsy, eighe, eig |
| | """ |
| |
|
| | iscomplex = any(type(x) is ctx.mpc for x in A) |
| |
|
| | if iscomplex: |
| | return ctx.eighe(A, eigvals_only = eigvals_only, overwrite_a = overwrite_a) |
| | else: |
| | return ctx.eigsy(A, eigvals_only = eigvals_only, overwrite_a = overwrite_a) |
| |
|
| |
|
| | @defun |
| | def gauss_quadrature(ctx, n, qtype = "legendre", alpha = 0, beta = 0): |
| | """ |
| | This routine calulates gaussian quadrature rules for different |
| | families of orthogonal polynomials. Let (a, b) be an interval, |
| | W(x) a positive weight function and n a positive integer. |
| | Then the purpose of this routine is to calculate pairs (x_k, w_k) |
| | for k=0, 1, 2, ... (n-1) which give |
| | |
| | int(W(x) * F(x), x = a..b) = sum(w_k * F(x_k),k = 0..(n-1)) |
| | |
| | exact for all polynomials F(x) of degree (strictly) less than 2*n. For all |
| | integrable functions F(x) the sum is a (more or less) good approximation to |
| | the integral. The x_k are called nodes (which are the zeros of the |
| | related orthogonal polynomials) and the w_k are called the weights. |
| | |
| | parameters |
| | n (input) The degree of the quadrature rule, i.e. its number of |
| | nodes. |
| | |
| | qtype (input) The family of orthogonal polynmomials for which to |
| | compute the quadrature rule. See the list below. |
| | |
| | alpha (input) real number, used as parameter for some orthogonal |
| | polynomials |
| | |
| | beta (input) real number, used as parameter for some orthogonal |
| | polynomials. |
| | |
| | return value |
| | |
| | (X, W) a pair of two real arrays where x_k = X[k] and w_k = W[k]. |
| | |
| | |
| | orthogonal polynomials: |
| | |
| | qtype polynomial |
| | ----- ---------- |
| | |
| | "legendre" Legendre polynomials, W(x)=1 on the interval (-1, +1) |
| | "legendre01" shifted Legendre polynomials, W(x)=1 on the interval (0, +1) |
| | "hermite" Hermite polynomials, W(x)=exp(-x*x) on (-infinity,+infinity) |
| | "laguerre" Laguerre polynomials, W(x)=exp(-x) on (0,+infinity) |
| | "glaguerre" generalized Laguerre polynomials, W(x)=exp(-x)*x**alpha |
| | on (0, +infinity) |
| | "chebyshev1" Chebyshev polynomials of the first kind, W(x)=1/sqrt(1-x*x) |
| | on (-1, +1) |
| | "chebyshev2" Chebyshev polynomials of the second kind, W(x)=sqrt(1-x*x) |
| | on (-1, +1) |
| | "jacobi" Jacobi polynomials, W(x)=(1-x)**alpha * (1+x)**beta on (-1, +1) |
| | with alpha>-1 and beta>-1 |
| | |
| | examples: |
| | >>> from mpmath import mp |
| | >>> f = lambda x: x**8 + 2 * x**6 - 3 * x**4 + 5 * x**2 - 7 |
| | >>> X, W = mp.gauss_quadrature(5, "hermite") |
| | >>> A = mp.fdot([(f(x), w) for x, w in zip(X, W)]) |
| | >>> B = mp.sqrt(mp.pi) * 57 / 16 |
| | >>> C = mp.quad(lambda x: mp.exp(- x * x) * f(x), [-mp.inf, +mp.inf]) |
| | >>> mp.nprint((mp.chop(A-B, tol = 1e-10), mp.chop(A-C, tol = 1e-10))) |
| | (0.0, 0.0) |
| | |
| | >>> f = lambda x: x**5 - 2 * x**4 + 3 * x**3 - 5 * x**2 + 7 * x - 11 |
| | >>> X, W = mp.gauss_quadrature(3, "laguerre") |
| | >>> A = mp.fdot([(f(x), w) for x, w in zip(X, W)]) |
| | >>> B = 76 |
| | >>> C = mp.quad(lambda x: mp.exp(-x) * f(x), [0, +mp.inf]) |
| | >>> mp.nprint(mp.chop(A-B, tol = 1e-10), mp.chop(A-C, tol = 1e-10)) |
| | .0 |
| | |
| | # orthogonality of the chebyshev polynomials: |
| | >>> f = lambda x: mp.chebyt(3, x) * mp.chebyt(2, x) |
| | >>> X, W = mp.gauss_quadrature(3, "chebyshev1") |
| | >>> A = mp.fdot([(f(x), w) for x, w in zip(X, W)]) |
| | >>> print(mp.chop(A, tol = 1e-10)) |
| | 0.0 |
| | |
| | references: |
| | - golub and welsch, "calculations of gaussian quadrature rules", mathematics of |
| | computation 23, p. 221-230 (1969) |
| | - golub, "some modified matrix eigenvalue problems", siam review 15, p. 318-334 (1973) |
| | - stroud and secrest, "gaussian quadrature formulas", prentice-hall (1966) |
| | |
| | See also the routine gaussq.f in netlog.org or ACM Transactions on |
| | Mathematical Software algorithm 726. |
| | """ |
| |
|
| | d = ctx.zeros(n, 1) |
| | e = ctx.zeros(n, 1) |
| | z = ctx.zeros(1, n) |
| |
|
| | z[0,0] = 1 |
| |
|
| | if qtype == "legendre": |
| | |
| | w = 2 |
| | for i in xrange(n): |
| | j = i + 1 |
| | e[i] = ctx.sqrt(j * j / (4 * j * j - ctx.mpf(1))) |
| | elif qtype == "legendre01": |
| | |
| | w = 1 |
| | for i in xrange(n): |
| | d[i] = 1 / ctx.mpf(2) |
| | j = i + 1 |
| | e[i] = ctx.sqrt(j * j / (16 * j * j - ctx.mpf(4))) |
| | elif qtype == "hermite": |
| | |
| | w = ctx.sqrt(ctx.pi) |
| | for i in xrange(n): |
| | j = i + 1 |
| | e[i] = ctx.sqrt(j / ctx.mpf(2)) |
| | elif qtype == "laguerre": |
| | |
| | w = 1 |
| | for i in xrange(n): |
| | j = i + 1 |
| | d[i] = 2 * j - 1 |
| | e[i] = j |
| | elif qtype=="chebyshev1": |
| | |
| | w = ctx.pi |
| | for i in xrange(n): |
| | e[i] = 1 / ctx.mpf(2) |
| | e[0] = ctx.sqrt(1 / ctx.mpf(2)) |
| | elif qtype == "chebyshev2": |
| | |
| | w = ctx.pi / 2 |
| | for i in xrange(n): |
| | e[i] = 1 / ctx.mpf(2) |
| | elif qtype == "glaguerre": |
| | |
| | w = ctx.gamma(1 + alpha) |
| | for i in xrange(n): |
| | j = i + 1 |
| | d[i] = 2 * j - 1 + alpha |
| | e[i] = ctx.sqrt(j * (j + alpha)) |
| | elif qtype == "jacobi": |
| | |
| | alpha = ctx.mpf(alpha) |
| | beta = ctx.mpf(beta) |
| | ab = alpha + beta |
| | abi = ab + 2 |
| | w = (2**(ab+1)) * ctx.gamma(alpha + 1) * ctx.gamma(beta + 1) / ctx.gamma(abi) |
| | d[0] = (beta - alpha) / abi |
| | e[0] = ctx.sqrt(4 * (1 + alpha) * (1 + beta) / ((abi + 1) * (abi * abi))) |
| | a2b2 = beta * beta - alpha * alpha |
| | for i in xrange(1, n): |
| | j = i + 1 |
| | abi = 2 * j + ab |
| | d[i] = a2b2 / ((abi - 2) * abi) |
| | e[i] = ctx.sqrt(4 * j * (j + alpha) * (j + beta) * (j + ab) / ((abi * abi - 1) * abi * abi)) |
| | elif isinstance(qtype, str): |
| | raise ValueError("unknown quadrature rule \"%s\"" % qtype) |
| | elif not isinstance(qtype, str): |
| | w = qtype(d, e) |
| | else: |
| | assert 0 |
| |
|
| | tridiag_eigen(ctx, d, e, z) |
| |
|
| | for i in xrange(len(z)): |
| | z[i] *= z[i] |
| |
|
| | z = z.transpose() |
| | return (d, w * z) |
| |
|
| | |
| | |
| | |
| |
|
| | def svd_r_raw(ctx, A, V = False, calc_u = False): |
| | """ |
| | This routine computes the singular value decomposition of a matrix A. |
| | Given A, two orthogonal matrices U and V are calculated such that |
| | |
| | A = U S V |
| | |
| | where S is a suitable shaped matrix whose off-diagonal elements are zero. |
| | The diagonal elements of S are the singular values of A, i.e. the |
| | squareroots of the eigenvalues of A' A or A A'. Here ' denotes the transpose. |
| | Householder bidiagonalization and a variant of the QR algorithm is used. |
| | |
| | overview of the matrices : |
| | |
| | A : m*n A gets replaced by U |
| | U : m*n U replaces A. If n>m then only the first m*m block of U is |
| | non-zero. column-orthogonal: U' U = B |
| | here B is a n*n matrix whose first min(m,n) diagonal |
| | elements are 1 and all other elements are zero. |
| | S : n*n diagonal matrix, only the diagonal elements are stored in |
| | the array S. only the first min(m,n) diagonal elements are non-zero. |
| | V : n*n orthogonal: V V' = V' V = 1 |
| | |
| | parameters: |
| | A (input/output) On input, A contains a real matrix of shape m*n. |
| | On output, if calc_u is true A contains the column-orthogonal |
| | matrix U; otherwise A is simply used as workspace and thus destroyed. |
| | |
| | V (input/output) if false, the matrix V is not calculated. otherwise |
| | V must be a matrix of shape n*n. |
| | |
| | calc_u (input) If true, the matrix U is calculated and replaces A. |
| | if false, U is not calculated and A is simply destroyed |
| | |
| | return value: |
| | S an array of length n containing the singular values of A sorted by |
| | decreasing magnitude. only the first min(m,n) elements are non-zero. |
| | |
| | This routine is a python translation of the fortran routine svd.f in the |
| | software library EISPACK (see netlib.org) which itself is based on the |
| | algol procedure svd described in: |
| | - num. math. 14, 403-420(1970) by golub and reinsch. |
| | - wilkinson/reinsch: handbook for auto. comp., vol ii-linear algebra, 134-151(1971). |
| | |
| | """ |
| |
|
| | m, n = A.rows, A.cols |
| |
|
| | S = ctx.zeros(n, 1) |
| |
|
| | |
| | work = ctx.zeros(n, 1) |
| |
|
| | g = scale = anorm = 0 |
| | maxits = 3 * ctx.dps |
| |
|
| | for i in xrange(n): |
| | work[i] = scale*g |
| | g = s = scale = 0 |
| | if i < m: |
| | for k in xrange(i, m): |
| | scale += ctx.fabs(A[k,i]) |
| | if scale != 0: |
| | for k in xrange(i, m): |
| | A[k,i] /= scale |
| | s += A[k,i] * A[k,i] |
| | f = A[i,i] |
| | g = -ctx.sqrt(s) |
| | if f < 0: |
| | g = -g |
| | h = f * g - s |
| | A[i,i] = f - g |
| | for j in xrange(i+1, n): |
| | s = 0 |
| | for k in xrange(i, m): |
| | s += A[k,i] * A[k,j] |
| | f = s / h |
| | for k in xrange(i, m): |
| | A[k,j] += f * A[k,i] |
| | for k in xrange(i,m): |
| | A[k,i] *= scale |
| |
|
| | S[i] = scale * g |
| | g = s = scale = 0 |
| |
|
| | if i < m and i != n - 1: |
| | for k in xrange(i+1, n): |
| | scale += ctx.fabs(A[i,k]) |
| | if scale: |
| | for k in xrange(i+1, n): |
| | A[i,k] /= scale |
| | s += A[i,k] * A[i,k] |
| | f = A[i,i+1] |
| | g = -ctx.sqrt(s) |
| | if f < 0: |
| | g = -g |
| | h = f * g - s |
| | A[i,i+1] = f - g |
| |
|
| | for k in xrange(i+1, n): |
| | work[k] = A[i,k] / h |
| |
|
| | for j in xrange(i+1, m): |
| | s = 0 |
| | for k in xrange(i+1, n): |
| | s += A[j,k] * A[i,k] |
| | for k in xrange(i+1, n): |
| | A[j,k] += s * work[k] |
| |
|
| | for k in xrange(i+1, n): |
| | A[i,k] *= scale |
| |
|
| | anorm = max(anorm, ctx.fabs(S[i]) + ctx.fabs(work[i])) |
| |
|
| | if not isinstance(V, bool): |
| | for i in xrange(n-2, -1, -1): |
| | V[i+1,i+1] = 1 |
| |
|
| | if work[i+1] != 0: |
| | for j in xrange(i+1, n): |
| | V[i,j] = (A[i,j] / A[i,i+1]) / work[i+1] |
| | for j in xrange(i+1, n): |
| | s = 0 |
| | for k in xrange(i+1, n): |
| | s += A[i,k] * V[j,k] |
| | for k in xrange(i+1, n): |
| | V[j,k] += s * V[i,k] |
| |
|
| | for j in xrange(i+1, n): |
| | V[j,i] = V[i,j] = 0 |
| |
|
| | V[0,0] = 1 |
| |
|
| | if m<n : minnm = m |
| | else : minnm = n |
| |
|
| | if calc_u: |
| | for i in xrange(minnm-1, -1, -1): |
| | g = S[i] |
| | for j in xrange(i+1, n): |
| | A[i,j] = 0 |
| | if g != 0: |
| | g = 1 / g |
| | for j in xrange(i+1, n): |
| | s = 0 |
| | for k in xrange(i+1, m): |
| | s += A[k,i] * A[k,j] |
| | f = (s / A[i,i]) * g |
| | for k in xrange(i, m): |
| | A[k,j] += f * A[k,i] |
| | for j in xrange(i, m): |
| | A[j,i] *= g |
| | else: |
| | for j in xrange(i, m): |
| | A[j,i] = 0 |
| | A[i,i] += 1 |
| |
|
| | for k in xrange(n - 1, -1, -1): |
| | |
| | |
| |
|
| | its = 0 |
| | while 1: |
| | its += 1 |
| | flag = True |
| |
|
| | for l in xrange(k, -1, -1): |
| | nm = l-1 |
| |
|
| | if ctx.fabs(work[l]) + anorm == anorm: |
| | flag = False |
| | break |
| |
|
| | if ctx.fabs(S[nm]) + anorm == anorm: |
| | break |
| |
|
| | if flag: |
| | c = 0 |
| | s = 1 |
| | for i in xrange(l, k + 1): |
| | f = s * work[i] |
| | work[i] *= c |
| | if ctx.fabs(f) + anorm == anorm: |
| | break |
| | g = S[i] |
| | h = ctx.hypot(f, g) |
| | S[i] = h |
| | h = 1 / h |
| | c = g * h |
| | s = - f * h |
| |
|
| | if calc_u: |
| | for j in xrange(m): |
| | y = A[j,nm] |
| | z = A[j,i] |
| | A[j,nm] = y * c + z * s |
| | A[j,i] = z * c - y * s |
| |
|
| | z = S[k] |
| |
|
| | if l == k: |
| | if z < 0: |
| | S[k] = -z |
| | if not isinstance(V, bool): |
| | for j in xrange(n): |
| | V[k,j] = -V[k,j] |
| | break |
| |
|
| | if its >= maxits: |
| | raise RuntimeError("svd: no convergence to an eigenvalue after %d iterations" % its) |
| |
|
| | x = S[l] |
| | nm = k-1 |
| | y = S[nm] |
| | g = work[nm] |
| | h = work[k] |
| | f = ((y - z) * (y + z) + (g - h) * (g + h))/(2 * h * y) |
| | g = ctx.hypot(f, 1) |
| | if f >= 0: f = ((x - z) * (x + z) + h * ((y / (f + g)) - h)) / x |
| | else: f = ((x - z) * (x + z) + h * ((y / (f - g)) - h)) / x |
| |
|
| | c = s = 1 |
| |
|
| | for j in xrange(l, nm + 1): |
| | g = work[j+1] |
| | y = S[j+1] |
| | h = s * g |
| | g = c * g |
| | z = ctx.hypot(f, h) |
| | work[j] = z |
| | c = f / z |
| | s = h / z |
| | f = x * c + g * s |
| | g = g * c - x * s |
| | h = y * s |
| | y *= c |
| | if not isinstance(V, bool): |
| | for jj in xrange(n): |
| | x = V[j ,jj] |
| | z = V[j+1,jj] |
| | V[j ,jj]= x * c + z * s |
| | V[j+1 ,jj]= z * c - x * s |
| | z = ctx.hypot(f, h) |
| | S[j] = z |
| | if z != 0: |
| | z = 1 / z |
| | c = f * z |
| | s = h * z |
| | f = c * g + s * y |
| | x = c * y - s * g |
| |
|
| | if calc_u: |
| | for jj in xrange(m): |
| | y = A[jj,j ] |
| | z = A[jj,j+1] |
| | A[jj,j ] = y * c + z * s |
| | A[jj,j+1 ] = z * c - y * s |
| |
|
| | work[l] = 0 |
| | work[k] = f |
| | S[k] = x |
| |
|
| | |
| |
|
| | |
| |
|
| | for i in xrange(n): |
| | imax = i |
| | s = ctx.fabs(S[i]) |
| |
|
| | for j in xrange(i + 1, n): |
| | c = ctx.fabs(S[j]) |
| | if c > s: |
| | s = c |
| | imax = j |
| |
|
| | if imax != i: |
| | |
| |
|
| | z = S[i] |
| | S[i] = S[imax] |
| | S[imax] = z |
| |
|
| | if calc_u: |
| | for j in xrange(m): |
| | z = A[j,i] |
| | A[j,i] = A[j,imax] |
| | A[j,imax] = z |
| |
|
| | if not isinstance(V, bool): |
| | for j in xrange(n): |
| | z = V[i,j] |
| | V[i,j] = V[imax,j] |
| | V[imax,j] = z |
| |
|
| | return S |
| |
|
| | |
| |
|
| | def svd_c_raw(ctx, A, V = False, calc_u = False): |
| | """ |
| | This routine computes the singular value decomposition of a matrix A. |
| | Given A, two unitary matrices U and V are calculated such that |
| | |
| | A = U S V |
| | |
| | where S is a suitable shaped matrix whose off-diagonal elements are zero. |
| | The diagonal elements of S are the singular values of A, i.e. the |
| | squareroots of the eigenvalues of A' A or A A'. Here ' denotes the hermitian |
| | transpose (i.e. transposition and conjugation). Householder bidiagonalization |
| | and a variant of the QR algorithm is used. |
| | |
| | overview of the matrices : |
| | |
| | A : m*n A gets replaced by U |
| | U : m*n U replaces A. If n>m then only the first m*m block of U is |
| | non-zero. column-unitary: U' U = B |
| | here B is a n*n matrix whose first min(m,n) diagonal |
| | elements are 1 and all other elements are zero. |
| | S : n*n diagonal matrix, only the diagonal elements are stored in |
| | the array S. only the first min(m,n) diagonal elements are non-zero. |
| | V : n*n unitary: V V' = V' V = 1 |
| | |
| | parameters: |
| | A (input/output) On input, A contains a complex matrix of shape m*n. |
| | On output, if calc_u is true A contains the column-unitary |
| | matrix U; otherwise A is simply used as workspace and thus destroyed. |
| | |
| | V (input/output) if false, the matrix V is not calculated. otherwise |
| | V must be a matrix of shape n*n. |
| | |
| | calc_u (input) If true, the matrix U is calculated and replaces A. |
| | if false, U is not calculated and A is simply destroyed |
| | |
| | return value: |
| | S an array of length n containing the singular values of A sorted by |
| | decreasing magnitude. only the first min(m,n) elements are non-zero. |
| | |
| | This routine is a python translation of the fortran routine svd.f in the |
| | software library EISPACK (see netlib.org) which itself is based on the |
| | algol procedure svd described in: |
| | - num. math. 14, 403-420(1970) by golub and reinsch. |
| | - wilkinson/reinsch: handbook for auto. comp., vol ii-linear algebra, 134-151(1971). |
| | |
| | """ |
| |
|
| | m, n = A.rows, A.cols |
| |
|
| | S = ctx.zeros(n, 1) |
| |
|
| | |
| | work = ctx.zeros(n, 1) |
| | lbeta = ctx.zeros(n, 1) |
| | rbeta = ctx.zeros(n, 1) |
| | dwork = ctx.zeros(n, 1) |
| |
|
| | g = scale = anorm = 0 |
| | maxits = 3 * ctx.dps |
| |
|
| | for i in xrange(n): |
| | dwork[i] = scale * g |
| | g = s = scale = 0 |
| | if i < m: |
| | for k in xrange(i, m): |
| | scale += ctx.fabs(ctx.re(A[k,i])) + ctx.fabs(ctx.im(A[k,i])) |
| | if scale != 0: |
| | for k in xrange(i, m): |
| | A[k,i] /= scale |
| | ar = ctx.re(A[k,i]) |
| | ai = ctx.im(A[k,i]) |
| | s += ar * ar + ai * ai |
| | f = A[i,i] |
| | g = -ctx.sqrt(s) |
| | if ctx.re(f) < 0: |
| | beta = -g - ctx.conj(f) |
| | g = -g |
| | else: |
| | beta = -g + ctx.conj(f) |
| | beta /= ctx.conj(beta) |
| | beta += 1 |
| | h = 2 * (ctx.re(f) * g - s) |
| | A[i,i] = f - g |
| | beta /= h |
| | lbeta[i] = (beta / scale) / scale |
| | for j in xrange(i+1, n): |
| | s = 0 |
| | for k in xrange(i, m): |
| | s += ctx.conj(A[k,i]) * A[k,j] |
| | f = beta * s |
| | for k in xrange(i, m): |
| | A[k,j] += f * A[k,i] |
| | for k in xrange(i, m): |
| | A[k,i] *= scale |
| |
|
| | S[i] = scale * g |
| | g = s = scale = 0 |
| |
|
| | if i < m and i != n - 1: |
| | for k in xrange(i+1, n): |
| | scale += ctx.fabs(ctx.re(A[i,k])) + ctx.fabs(ctx.im(A[i,k])) |
| | if scale: |
| | for k in xrange(i+1, n): |
| | A[i,k] /= scale |
| | ar = ctx.re(A[i,k]) |
| | ai = ctx.im(A[i,k]) |
| | s += ar * ar + ai * ai |
| | f = A[i,i+1] |
| | g = -ctx.sqrt(s) |
| | if ctx.re(f) < 0: |
| | beta = -g - ctx.conj(f) |
| | g = -g |
| | else: |
| | beta = -g + ctx.conj(f) |
| |
|
| | beta /= ctx.conj(beta) |
| | beta += 1 |
| |
|
| | h = 2 * (ctx.re(f) * g - s) |
| | A[i,i+1] = f - g |
| |
|
| | beta /= h |
| | rbeta[i] = (beta / scale) / scale |
| |
|
| | for k in xrange(i+1, n): |
| | work[k] = A[i, k] |
| |
|
| | for j in xrange(i+1, m): |
| | s = 0 |
| | for k in xrange(i+1, n): |
| | s += ctx.conj(A[i,k]) * A[j,k] |
| | f = s * beta |
| | for k in xrange(i+1,n): |
| | A[j,k] += f * work[k] |
| |
|
| | for k in xrange(i+1, n): |
| | A[i,k] *= scale |
| |
|
| | anorm = max(anorm,ctx.fabs(S[i]) + ctx.fabs(dwork[i])) |
| |
|
| | if not isinstance(V, bool): |
| | for i in xrange(n-2, -1, -1): |
| | V[i+1,i+1] = 1 |
| |
|
| | if dwork[i+1] != 0: |
| | f = ctx.conj(rbeta[i]) |
| | for j in xrange(i+1, n): |
| | V[i,j] = A[i,j] * f |
| | for j in xrange(i+1, n): |
| | s = 0 |
| | for k in xrange(i+1, n): |
| | s += ctx.conj(A[i,k]) * V[j,k] |
| | for k in xrange(i+1, n): |
| | V[j,k] += s * V[i,k] |
| |
|
| | for j in xrange(i+1,n): |
| | V[j,i] = V[i,j] = 0 |
| |
|
| | V[0,0] = 1 |
| |
|
| | if m < n : minnm = m |
| | else : minnm = n |
| |
|
| | if calc_u: |
| | for i in xrange(minnm-1, -1, -1): |
| | g = S[i] |
| | for j in xrange(i+1, n): |
| | A[i,j] = 0 |
| | if g != 0: |
| | g = 1 / g |
| | for j in xrange(i+1, n): |
| | s = 0 |
| | for k in xrange(i+1, m): |
| | s += ctx.conj(A[k,i]) * A[k,j] |
| | f = s * ctx.conj(lbeta[i]) |
| | for k in xrange(i, m): |
| | A[k,j] += f * A[k,i] |
| | for j in xrange(i, m): |
| | A[j,i] *= g |
| | else: |
| | for j in xrange(i, m): |
| | A[j,i] = 0 |
| | A[i,i] += 1 |
| |
|
| | for k in xrange(n-1, -1, -1): |
| | |
| | |
| |
|
| | its = 0 |
| | while 1: |
| | its += 1 |
| | flag = True |
| |
|
| | for l in xrange(k, -1, -1): |
| | nm = l - 1 |
| |
|
| | if ctx.fabs(dwork[l]) + anorm == anorm: |
| | flag = False |
| | break |
| |
|
| | if ctx.fabs(S[nm]) + anorm == anorm: |
| | break |
| |
|
| | if flag: |
| | c = 0 |
| | s = 1 |
| | for i in xrange(l, k+1): |
| | f = s * dwork[i] |
| | dwork[i] *= c |
| | if ctx.fabs(f) + anorm == anorm: |
| | break |
| | g = S[i] |
| | h = ctx.hypot(f, g) |
| | S[i] = h |
| | h = 1 / h |
| | c = g * h |
| | s = -f * h |
| |
|
| | if calc_u: |
| | for j in xrange(m): |
| | y = A[j,nm] |
| | z = A[j,i] |
| | A[j,nm]= y * c + z * s |
| | A[j,i] = z * c - y * s |
| |
|
| | z = S[k] |
| |
|
| | if l == k: |
| | if z < 0: |
| | S[k] = -z |
| | if not isinstance(V, bool): |
| | for j in xrange(n): |
| | V[k,j] = -V[k,j] |
| | break |
| |
|
| | if its >= maxits: |
| | raise RuntimeError("svd: no convergence to an eigenvalue after %d iterations" % its) |
| |
|
| | x = S[l] |
| | nm = k-1 |
| | y = S[nm] |
| | g = dwork[nm] |
| | h = dwork[k] |
| | f = ((y - z) * (y + z) + (g - h) * (g + h)) / (2 * h * y) |
| | g = ctx.hypot(f, 1) |
| | if f >=0: f = (( x - z) *( x + z) + h *((y / (f + g)) - h)) / x |
| | else: f = (( x - z) *( x + z) + h *((y / (f - g)) - h)) / x |
| |
|
| | c = s = 1 |
| |
|
| | for j in xrange(l, nm + 1): |
| | g = dwork[j+1] |
| | y = S[j+1] |
| | h = s * g |
| | g = c * g |
| | z = ctx.hypot(f, h) |
| | dwork[j] = z |
| | c = f / z |
| | s = h / z |
| | f = x * c + g * s |
| | g = g * c - x * s |
| | h = y * s |
| | y *= c |
| | if not isinstance(V, bool): |
| | for jj in xrange(n): |
| | x = V[j ,jj] |
| | z = V[j+1,jj] |
| | V[j ,jj]= x * c + z * s |
| | V[j+1,jj ]= z * c - x * s |
| | z = ctx.hypot(f, h) |
| | S[j] = z |
| | if z != 0: |
| | z = 1 / z |
| | c = f * z |
| | s = h * z |
| | f = c * g + s * y |
| | x = c * y - s * g |
| | if calc_u: |
| | for jj in xrange(m): |
| | y = A[jj,j ] |
| | z = A[jj,j+1] |
| | A[jj,j ]= y * c + z * s |
| | A[jj,j+1 ]= z * c - y * s |
| |
|
| | dwork[l] = 0 |
| | dwork[k] = f |
| | S[k] = x |
| |
|
| | |
| |
|
| | |
| |
|
| | for i in xrange(n): |
| | imax = i |
| | s = ctx.fabs(S[i]) |
| |
|
| | for j in xrange(i + 1, n): |
| | c = ctx.fabs(S[j]) |
| | if c > s: |
| | s = c |
| | imax = j |
| |
|
| | if imax != i: |
| | |
| |
|
| | z = S[i] |
| | S[i] = S[imax] |
| | S[imax] = z |
| |
|
| | if calc_u: |
| | for j in xrange(m): |
| | z = A[j,i] |
| | A[j,i] = A[j,imax] |
| | A[j,imax] = z |
| |
|
| | if not isinstance(V, bool): |
| | for j in xrange(n): |
| | z = V[i,j] |
| | V[i,j] = V[imax,j] |
| | V[imax,j] = z |
| |
|
| | return S |
| |
|
| | |
| |
|
| | @defun |
| | def svd_r(ctx, A, full_matrices = False, compute_uv = True, overwrite_a = False): |
| | """ |
| | This routine computes the singular value decomposition of a matrix A. |
| | Given A, two orthogonal matrices U and V are calculated such that |
| | |
| | A = U S V and U' U = 1 and V V' = 1 |
| | |
| | where S is a suitable shaped matrix whose off-diagonal elements are zero. |
| | Here ' denotes the transpose. The diagonal elements of S are the singular |
| | values of A, i.e. the squareroots of the eigenvalues of A' A or A A'. |
| | |
| | input: |
| | A : a real matrix of shape (m, n) |
| | full_matrices : if true, U and V are of shape (m, m) and (n, n). |
| | if false, U and V are of shape (m, min(m, n)) and (min(m, n), n). |
| | compute_uv : if true, U and V are calculated. if false, only S is calculated. |
| | overwrite_a : if true, allows modification of A which may improve |
| | performance. if false, A is not modified. |
| | |
| | output: |
| | U : an orthogonal matrix: U' U = 1. if full_matrices is true, U is of |
| | shape (m, m). ortherwise it is of shape (m, min(m, n)). |
| | |
| | S : an array of length min(m, n) containing the singular values of A sorted by |
| | decreasing magnitude. |
| | |
| | V : an orthogonal matrix: V V' = 1. if full_matrices is true, V is of |
| | shape (n, n). ortherwise it is of shape (min(m, n), n). |
| | |
| | return value: |
| | |
| | S if compute_uv is false |
| | (U, S, V) if compute_uv is true |
| | |
| | overview of the matrices: |
| | |
| | full_matrices true: |
| | A : m*n |
| | U : m*m U' U = 1 |
| | S as matrix : m*n |
| | V : n*n V V' = 1 |
| | |
| | full_matrices false: |
| | A : m*n |
| | U : m*min(n,m) U' U = 1 |
| | S as matrix : min(m,n)*min(m,n) |
| | V : min(m,n)*n V V' = 1 |
| | |
| | examples: |
| | |
| | >>> from mpmath import mp |
| | >>> A = mp.matrix([[2, -2, -1], [3, 4, -2], [-2, -2, 0]]) |
| | >>> S = mp.svd_r(A, compute_uv = False) |
| | >>> print(S) |
| | [6.0] |
| | [3.0] |
| | [1.0] |
| | |
| | >>> U, S, V = mp.svd_r(A) |
| | >>> print(mp.chop(A - U * mp.diag(S) * V)) |
| | [0.0 0.0 0.0] |
| | [0.0 0.0 0.0] |
| | [0.0 0.0 0.0] |
| | |
| | |
| | see also: svd, svd_c |
| | """ |
| |
|
| | m, n = A.rows, A.cols |
| |
|
| | if not compute_uv: |
| | if not overwrite_a: |
| | A = A.copy() |
| | S = svd_r_raw(ctx, A, V = False, calc_u = False) |
| | S = S[:min(m,n)] |
| | return S |
| |
|
| | if full_matrices and n < m: |
| | V = ctx.zeros(m, m) |
| | A0 = ctx.zeros(m, m) |
| | A0[:,:n] = A |
| | S = svd_r_raw(ctx, A0, V, calc_u = True) |
| |
|
| | S = S[:n] |
| | V = V[:n,:n] |
| |
|
| | return (A0, S, V) |
| | else: |
| | if not overwrite_a: |
| | A = A.copy() |
| | V = ctx.zeros(n, n) |
| | S = svd_r_raw(ctx, A, V, calc_u = True) |
| |
|
| | if n > m: |
| | if full_matrices == False: |
| | V = V[:m,:] |
| |
|
| | S = S[:m] |
| | A = A[:,:m] |
| |
|
| | return (A, S, V) |
| |
|
| | |
| |
|
| | @defun |
| | def svd_c(ctx, A, full_matrices = False, compute_uv = True, overwrite_a = False): |
| | """ |
| | This routine computes the singular value decomposition of a matrix A. |
| | Given A, two unitary matrices U and V are calculated such that |
| | |
| | A = U S V and U' U = 1 and V V' = 1 |
| | |
| | where S is a suitable shaped matrix whose off-diagonal elements are zero. |
| | Here ' denotes the hermitian transpose (i.e. transposition and complex |
| | conjugation). The diagonal elements of S are the singular values of A, |
| | i.e. the squareroots of the eigenvalues of A' A or A A'. |
| | |
| | input: |
| | A : a complex matrix of shape (m, n) |
| | full_matrices : if true, U and V are of shape (m, m) and (n, n). |
| | if false, U and V are of shape (m, min(m, n)) and (min(m, n), n). |
| | compute_uv : if true, U and V are calculated. if false, only S is calculated. |
| | overwrite_a : if true, allows modification of A which may improve |
| | performance. if false, A is not modified. |
| | |
| | output: |
| | U : an unitary matrix: U' U = 1. if full_matrices is true, U is of |
| | shape (m, m). ortherwise it is of shape (m, min(m, n)). |
| | |
| | S : an array of length min(m, n) containing the singular values of A sorted by |
| | decreasing magnitude. |
| | |
| | V : an unitary matrix: V V' = 1. if full_matrices is true, V is of |
| | shape (n, n). ortherwise it is of shape (min(m, n), n). |
| | |
| | return value: |
| | |
| | S if compute_uv is false |
| | (U, S, V) if compute_uv is true |
| | |
| | overview of the matrices: |
| | |
| | full_matrices true: |
| | A : m*n |
| | U : m*m U' U = 1 |
| | S as matrix : m*n |
| | V : n*n V V' = 1 |
| | |
| | full_matrices false: |
| | A : m*n |
| | U : m*min(n,m) U' U = 1 |
| | S as matrix : min(m,n)*min(m,n) |
| | V : min(m,n)*n V V' = 1 |
| | |
| | example: |
| | >>> from mpmath import mp |
| | >>> A = mp.matrix([[-2j, -1-3j, -2+2j], [2-2j, -1-3j, 1], [-3+1j,-2j,0]]) |
| | >>> S = mp.svd_c(A, compute_uv = False) |
| | >>> print(mp.chop(S - mp.matrix([mp.sqrt(34), mp.sqrt(15), mp.sqrt(6)]))) |
| | [0.0] |
| | [0.0] |
| | [0.0] |
| | |
| | >>> U, S, V = mp.svd_c(A) |
| | >>> print(mp.chop(A - U * mp.diag(S) * V)) |
| | [0.0 0.0 0.0] |
| | [0.0 0.0 0.0] |
| | [0.0 0.0 0.0] |
| | |
| | see also: svd, svd_r |
| | """ |
| |
|
| | m, n = A.rows, A.cols |
| |
|
| | if not compute_uv: |
| | if not overwrite_a: |
| | A = A.copy() |
| | S = svd_c_raw(ctx, A, V = False, calc_u = False) |
| | S = S[:min(m,n)] |
| | return S |
| |
|
| | if full_matrices and n < m: |
| | V = ctx.zeros(m, m) |
| | A0 = ctx.zeros(m, m) |
| | A0[:,:n] = A |
| | S = svd_c_raw(ctx, A0, V, calc_u = True) |
| |
|
| | S = S[:n] |
| | V = V[:n,:n] |
| |
|
| | return (A0, S, V) |
| | else: |
| | if not overwrite_a: |
| | A = A.copy() |
| | V = ctx.zeros(n, n) |
| | S = svd_c_raw(ctx, A, V, calc_u = True) |
| |
|
| | if n > m: |
| | if full_matrices == False: |
| | V = V[:m,:] |
| |
|
| | S = S[:m] |
| | A = A[:,:m] |
| |
|
| | return (A, S, V) |
| |
|
| | @defun |
| | def svd(ctx, A, full_matrices = False, compute_uv = True, overwrite_a = False): |
| | """ |
| | "svd" is a unified interface for "svd_r" and "svd_c". Depending on |
| | whether A is real or complex the appropriate function is called. |
| | |
| | This routine computes the singular value decomposition of a matrix A. |
| | Given A, two orthogonal (A real) or unitary (A complex) matrices U and V |
| | are calculated such that |
| | |
| | A = U S V and U' U = 1 and V V' = 1 |
| | |
| | where S is a suitable shaped matrix whose off-diagonal elements are zero. |
| | Here ' denotes the hermitian transpose (i.e. transposition and complex |
| | conjugation). The diagonal elements of S are the singular values of A, |
| | i.e. the squareroots of the eigenvalues of A' A or A A'. |
| | |
| | input: |
| | A : a real or complex matrix of shape (m, n) |
| | full_matrices : if true, U and V are of shape (m, m) and (n, n). |
| | if false, U and V are of shape (m, min(m, n)) and (min(m, n), n). |
| | compute_uv : if true, U and V are calculated. if false, only S is calculated. |
| | overwrite_a : if true, allows modification of A which may improve |
| | performance. if false, A is not modified. |
| | |
| | output: |
| | U : an orthogonal or unitary matrix: U' U = 1. if full_matrices is true, U is of |
| | shape (m, m). ortherwise it is of shape (m, min(m, n)). |
| | |
| | S : an array of length min(m, n) containing the singular values of A sorted by |
| | decreasing magnitude. |
| | |
| | V : an orthogonal or unitary matrix: V V' = 1. if full_matrices is true, V is of |
| | shape (n, n). ortherwise it is of shape (min(m, n), n). |
| | |
| | return value: |
| | |
| | S if compute_uv is false |
| | (U, S, V) if compute_uv is true |
| | |
| | overview of the matrices: |
| | |
| | full_matrices true: |
| | A : m*n |
| | U : m*m U' U = 1 |
| | S as matrix : m*n |
| | V : n*n V V' = 1 |
| | |
| | full_matrices false: |
| | A : m*n |
| | U : m*min(n,m) U' U = 1 |
| | S as matrix : min(m,n)*min(m,n) |
| | V : min(m,n)*n V V' = 1 |
| | |
| | examples: |
| | |
| | >>> from mpmath import mp |
| | >>> A = mp.matrix([[2, -2, -1], [3, 4, -2], [-2, -2, 0]]) |
| | >>> S = mp.svd(A, compute_uv = False) |
| | >>> print(S) |
| | [6.0] |
| | [3.0] |
| | [1.0] |
| | |
| | >>> U, S, V = mp.svd(A) |
| | >>> print(mp.chop(A - U * mp.diag(S) * V)) |
| | [0.0 0.0 0.0] |
| | [0.0 0.0 0.0] |
| | [0.0 0.0 0.0] |
| | |
| | see also: svd_r, svd_c |
| | """ |
| |
|
| | iscomplex = any(type(x) is ctx.mpc for x in A) |
| |
|
| | if iscomplex: |
| | return ctx.svd_c(A, full_matrices = full_matrices, compute_uv = compute_uv, overwrite_a = overwrite_a) |
| | else: |
| | return ctx.svd_r(A, full_matrices = full_matrices, compute_uv = compute_uv, overwrite_a = overwrite_a) |
| |
|