| | |
| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | """ |
| | The eigenvalue problem |
| | ---------------------- |
| | |
| | This file contains routines for the eigenvalue problem. |
| | |
| | high level routines: |
| | |
| | hessenberg : reduction of a real or complex square matrix to upper Hessenberg form |
| | schur : reduction of a real or complex square matrix to upper Schur form |
| | eig : eigenvalues and eigenvectors of a real or complex square matrix |
| | |
| | low level routines: |
| | |
| | hessenberg_reduce_0 : reduction of a real or complex square matrix to upper Hessenberg form |
| | hessenberg_reduce_1 : auxiliary routine to hessenberg_reduce_0 |
| | qr_step : a single implicitly shifted QR step for an upper Hessenberg matrix |
| | hessenberg_qr : Schur decomposition of an upper Hessenberg matrix |
| | eig_tr_r : right eigenvectors of an upper triangular matrix |
| | eig_tr_l : left eigenvectors of an upper triangular matrix |
| | """ |
| |
|
| | from ..libmp.backend import xrange |
| |
|
| | class Eigen(object): |
| | pass |
| |
|
| | def defun(f): |
| | setattr(Eigen, f.__name__, f) |
| | return f |
| |
|
| | def hessenberg_reduce_0(ctx, A, T): |
| | """ |
| | This routine computes the (upper) Hessenberg decomposition of a square matrix A. |
| | Given A, an unitary matrix Q is calculated such that |
| | |
| | Q' A Q = H and Q' Q = Q Q' = 1 |
| | |
| | where H is an upper Hessenberg matrix, meaning that it only contains zeros |
| | below the first subdiagonal. Here ' denotes the hermitian transpose (i.e. |
| | transposition and conjugation). |
| | |
| | parameters: |
| | A (input/output) On input, A contains the square matrix A of |
| | dimension (n,n). On output, A contains a compressed representation |
| | of Q and H. |
| | T (output) An array of length n containing the first elements of |
| | the Householder reflectors. |
| | """ |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| |
|
| | n = A.rows |
| | if n <= 2: return |
| |
|
| | for i in xrange(n-1, 1, -1): |
| |
|
| | |
| |
|
| | scale = 0 |
| | for k in xrange(0, i): |
| | scale += abs(ctx.re(A[i,k])) + abs(ctx.im(A[i,k])) |
| |
|
| | scale_inv = 0 |
| | if scale != 0: |
| | scale_inv = 1 / scale |
| |
|
| | if scale == 0 or ctx.isinf(scale_inv): |
| | |
| | T[i] = 0 |
| | A[i,i-1] = 0 |
| | continue |
| |
|
| | |
| |
|
| | H = 0 |
| | for k in xrange(0, i): |
| | A[i,k] *= scale_inv |
| | rr = ctx.re(A[i,k]) |
| | ii = ctx.im(A[i,k]) |
| | H += rr * rr + ii * ii |
| |
|
| | F = A[i,i-1] |
| | f = abs(F) |
| | G = ctx.sqrt(H) |
| | A[i,i-1] = - G * scale |
| |
|
| | if f == 0: |
| | T[i] = G |
| | else: |
| | ff = F / f |
| | T[i] = F + G * ff |
| | A[i,i-1] *= ff |
| |
|
| | H += G * f |
| | H = 1 / ctx.sqrt(H) |
| |
|
| | T[i] *= H |
| | for k in xrange(0, i - 1): |
| | A[i,k] *= H |
| |
|
| | for j in xrange(0, i): |
| | |
| |
|
| | G = ctx.conj(T[i]) * A[j,i-1] |
| | for k in xrange(0, i-1): |
| | G += ctx.conj(A[i,k]) * A[j,k] |
| |
|
| | A[j,i-1] -= G * T[i] |
| | for k in xrange(0, i-1): |
| | A[j,k] -= G * A[i,k] |
| |
|
| | for j in xrange(0, n): |
| | |
| |
|
| | G = T[i] * A[i-1,j] |
| | for k in xrange(0, i-1): |
| | G += A[i,k] * A[k,j] |
| |
|
| | A[i-1,j] -= G * ctx.conj(T[i]) |
| | for k in xrange(0, i-1): |
| | A[k,j] -= G * ctx.conj(A[i,k]) |
| |
|
| |
|
| |
|
| | def hessenberg_reduce_1(ctx, A, T): |
| | """ |
| | This routine forms the unitary matrix Q described in hessenberg_reduce_0. |
| | |
| | parameters: |
| | A (input/output) On input, A is the same matrix as delivered by |
| | hessenberg_reduce_0. On output, A is set to Q. |
| | |
| | T (input) On input, T is the same array as delivered by hessenberg_reduce_0. |
| | """ |
| |
|
| | n = A.rows |
| |
|
| | if n == 1: |
| | A[0,0] = 1 |
| | return |
| |
|
| | A[0,0] = A[1,1] = 1 |
| | A[0,1] = A[1,0] = 0 |
| |
|
| | for i in xrange(2, n): |
| | if T[i] != 0: |
| |
|
| | for j in xrange(0, i): |
| | G = T[i] * A[i-1,j] |
| | for k in xrange(0, i-1): |
| | G += A[i,k] * A[k,j] |
| |
|
| | A[i-1,j] -= G * ctx.conj(T[i]) |
| | for k in xrange(0, i-1): |
| | A[k,j] -= G * ctx.conj(A[i,k]) |
| |
|
| | A[i,i] = 1 |
| | for j in xrange(0, i): |
| | A[j,i] = A[i,j] = 0 |
| |
|
| |
|
| |
|
| | @defun |
| | def hessenberg(ctx, A, overwrite_a = False): |
| | """ |
| | This routine computes the Hessenberg decomposition of a square matrix A. |
| | Given A, an unitary matrix Q is determined such that |
| | |
| | Q' A Q = H and Q' Q = Q Q' = 1 |
| | |
| | where H is an upper right Hessenberg matrix. Here ' denotes the hermitian |
| | transpose (i.e. transposition and conjugation). |
| | |
| | input: |
| | A : a real or complex square matrix |
| | overwrite_a : if true, allows modification of A which may improve |
| | performance. if false, A is not modified. |
| | |
| | output: |
| | Q : an unitary matrix |
| | H : an upper right Hessenberg matrix |
| | |
| | example: |
| | >>> from mpmath import mp |
| | >>> A = mp.matrix([[3, -1, 2], [2, 5, -5], [-2, -3, 7]]) |
| | >>> Q, H = mp.hessenberg(A) |
| | >>> mp.nprint(H, 3) # doctest:+SKIP |
| | [ 3.15 2.23 4.44] |
| | [-0.769 4.85 3.05] |
| | [ 0.0 3.61 7.0] |
| | >>> print(mp.chop(A - Q * H * Q.transpose_conj())) |
| | [0.0 0.0 0.0] |
| | [0.0 0.0 0.0] |
| | [0.0 0.0 0.0] |
| | |
| | return value: (Q, H) |
| | """ |
| |
|
| | n = A.rows |
| |
|
| | if n == 1: |
| | return (ctx.matrix([[1]]), A) |
| |
|
| | if not overwrite_a: |
| | A = A.copy() |
| |
|
| | T = ctx.matrix(n, 1) |
| |
|
| | hessenberg_reduce_0(ctx, A, T) |
| | Q = A.copy() |
| | hessenberg_reduce_1(ctx, Q, T) |
| |
|
| | for x in xrange(n): |
| | for y in xrange(x+2, n): |
| | A[y,x] = 0 |
| |
|
| | return Q, A |
| |
|
| |
|
| | |
| |
|
| |
|
| | def qr_step(ctx, n0, n1, A, Q, shift): |
| | """ |
| | This subroutine executes a single implicitly shifted QR step applied to an |
| | upper Hessenberg matrix A. Given A and shift as input, first an QR |
| | decomposition is calculated: |
| | |
| | Q R = A - shift * 1 . |
| | |
| | The output is then following matrix: |
| | |
| | R Q + shift * 1 |
| | |
| | parameters: |
| | n0, n1 (input) Two integers which specify the submatrix A[n0:n1,n0:n1] |
| | on which this subroutine operators. The subdiagonal elements |
| | to the left and below this submatrix must be deflated (i.e. zero). |
| | following restriction is imposed: n1>=n0+2 |
| | A (input/output) On input, A is an upper Hessenberg matrix. |
| | On output, A is replaced by "R Q + shift * 1" |
| | Q (input/output) The parameter Q is multiplied by the unitary matrix |
| | Q arising from the QR decomposition. Q can also be false, in which |
| | case the unitary matrix Q is not computated. |
| | shift (input) a complex number specifying the shift. idealy close to an |
| | eigenvalue of the bottemmost part of the submatrix A[n0:n1,n0:n1]. |
| | |
| | references: |
| | Stoer, Bulirsch - Introduction to Numerical Analysis. |
| | Kresser : Numerical Methods for General and Structured Eigenvalue Problems |
| | """ |
| |
|
| | |
| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | n = A.rows |
| |
|
| | |
| |
|
| | |
| | c = A[n0 ,n0] - shift |
| | s = A[n0+1,n0] |
| |
|
| | v = ctx.hypot(ctx.hypot(ctx.re(c), ctx.im(c)), ctx.hypot(ctx.re(s), ctx.im(s))) |
| |
|
| | if v == 0: |
| | v = 1 |
| | c = 1 |
| | s = 0 |
| | else: |
| | c /= v |
| | s /= v |
| |
|
| | cc = ctx.conj(c) |
| | cs = ctx.conj(s) |
| |
|
| | for k in xrange(n0, n): |
| | |
| | x = A[n0 ,k] |
| | y = A[n0+1,k] |
| | A[n0 ,k] = cc * x + cs * y |
| | A[n0+1,k] = c * y - s * x |
| |
|
| | for k in xrange(min(n1, n0+3)): |
| | |
| | x = A[k,n0 ] |
| | y = A[k,n0+1] |
| | A[k,n0 ] = c * x + s * y |
| | A[k,n0+1] = cc * y - cs * x |
| |
|
| | if not isinstance(Q, bool): |
| | for k in xrange(n): |
| | |
| | x = Q[k,n0 ] |
| | y = Q[k,n0+1] |
| | Q[k,n0 ] = c * x + s * y |
| | Q[k,n0+1] = cc * y - cs * x |
| |
|
| | |
| |
|
| | for j in xrange(n0, n1 - 2): |
| | |
| |
|
| | c = A[j+1,j] |
| | s = A[j+2,j] |
| |
|
| | v = ctx.hypot(ctx.hypot(ctx.re(c), ctx.im(c)), ctx.hypot(ctx.re(s), ctx.im(s))) |
| |
|
| | if v == 0: |
| | A[j+1,j] = 0 |
| | v = 1 |
| | c = 1 |
| | s = 0 |
| | else: |
| | A[j+1,j] = v |
| | c /= v |
| | s /= v |
| |
|
| | A[j+2,j] = 0 |
| |
|
| | cc = ctx.conj(c) |
| | cs = ctx.conj(s) |
| |
|
| | for k in xrange(j+1, n): |
| | |
| | x = A[j+1,k] |
| | y = A[j+2,k] |
| | A[j+1,k] = cc * x + cs * y |
| | A[j+2,k] = c * y - s * x |
| |
|
| | for k in xrange(0, min(n1, j+4)): |
| | |
| | x = A[k,j+1] |
| | y = A[k,j+2] |
| | A[k,j+1] = c * x + s * y |
| | A[k,j+2] = cc * y - cs * x |
| |
|
| | if not isinstance(Q, bool): |
| | for k in xrange(0, n): |
| | |
| | x = Q[k,j+1] |
| | y = Q[k,j+2] |
| | Q[k,j+1] = c * x + s * y |
| | Q[k,j+2] = cc * y - cs * x |
| |
|
| |
|
| |
|
| | def hessenberg_qr(ctx, A, Q): |
| | """ |
| | This routine computes the Schur decomposition of an upper Hessenberg matrix A. |
| | Given A, an unitary matrix Q is determined such that |
| | |
| | Q' A Q = R and Q' Q = Q Q' = 1 |
| | |
| | where R is an upper right triangular matrix. Here ' denotes the hermitian |
| | transpose (i.e. transposition and conjugation). |
| | |
| | parameters: |
| | A (input/output) On input, A contains an upper Hessenberg matrix. |
| | On output, A is replace by the upper right triangluar matrix R. |
| | |
| | Q (input/output) The parameter Q is multiplied by the unitary |
| | matrix Q arising from the Schur decomposition. Q can also be |
| | false, in which case the unitary matrix Q is not computated. |
| | """ |
| |
|
| | n = A.rows |
| |
|
| | norm = 0 |
| | for x in xrange(n): |
| | for y in xrange(min(x+2, n)): |
| | norm += ctx.re(A[y,x]) ** 2 + ctx.im(A[y,x]) ** 2 |
| | norm = ctx.sqrt(norm) / n |
| |
|
| | if norm == 0: |
| | return |
| |
|
| | n0 = 0 |
| | n1 = n |
| |
|
| | eps = ctx.eps / (100 * n) |
| | maxits = ctx.dps * 4 |
| |
|
| | its = totalits = 0 |
| |
|
| | while 1: |
| | |
| | |
| |
|
| | k = n0 |
| |
|
| | while k + 1 < n1: |
| | s = abs(ctx.re(A[k,k])) + abs(ctx.im(A[k,k])) + abs(ctx.re(A[k+1,k+1])) + abs(ctx.im(A[k+1,k+1])) |
| | if s < eps * norm: |
| | s = norm |
| | if abs(A[k+1,k]) < eps * s: |
| | break |
| | k += 1 |
| |
|
| | if k + 1 < n1: |
| | |
| |
|
| | A[k+1,k] = 0 |
| | n0 = k + 1 |
| |
|
| | its = 0 |
| |
|
| | if n0 + 1 >= n1: |
| | |
| | n0 = 0 |
| | n1 = k + 1 |
| | if n1 < 2: |
| | |
| | return |
| | else: |
| | if (its % 30) == 10: |
| | |
| | shift = A[n1-1,n1-2] |
| | elif (its % 30) == 20: |
| | |
| | shift = abs(A[n1-1,n1-2]) |
| | elif (its % 30) == 29: |
| | |
| | shift = norm |
| | else: |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | t = A[n1-2,n1-2] + A[n1-1,n1-1] |
| | s = (A[n1-1,n1-1] - A[n1-2,n1-2]) ** 2 + 4 * A[n1-1,n1-2] * A[n1-2,n1-1] |
| | if ctx.re(s) > 0: |
| | s = ctx.sqrt(s) |
| | else: |
| | s = ctx.sqrt(-s) * 1j |
| | a = (t + s) / 2 |
| | b = (t - s) / 2 |
| | if abs(A[n1-1,n1-1] - a) > abs(A[n1-1,n1-1] - b): |
| | shift = b |
| | else: |
| | shift = a |
| |
|
| | its += 1 |
| | totalits += 1 |
| |
|
| | qr_step(ctx, n0, n1, A, Q, shift) |
| |
|
| | if its > maxits: |
| | raise RuntimeError("qr: failed to converge after %d steps" % its) |
| |
|
| |
|
| | @defun |
| | def schur(ctx, A, overwrite_a = False): |
| | """ |
| | This routine computes the Schur decomposition of a square matrix A. |
| | Given A, an unitary matrix Q is determined such that |
| | |
| | Q' A Q = R and Q' Q = Q Q' = 1 |
| | |
| | where R is an upper right triangular matrix. Here ' denotes the |
| | hermitian transpose (i.e. transposition and conjugation). |
| | |
| | input: |
| | A : a real or complex square matrix |
| | overwrite_a : if true, allows modification of A which may improve |
| | performance. if false, A is not modified. |
| | |
| | output: |
| | Q : an unitary matrix |
| | R : an upper right triangular matrix |
| | |
| | return value: (Q, R) |
| | |
| | example: |
| | >>> from mpmath import mp |
| | >>> A = mp.matrix([[3, -1, 2], [2, 5, -5], [-2, -3, 7]]) |
| | >>> Q, R = mp.schur(A) |
| | >>> mp.nprint(R, 3) # doctest:+SKIP |
| | [2.0 0.417 -2.53] |
| | [0.0 4.0 -4.74] |
| | [0.0 0.0 9.0] |
| | >>> print(mp.chop(A - Q * R * Q.transpose_conj())) |
| | [0.0 0.0 0.0] |
| | [0.0 0.0 0.0] |
| | [0.0 0.0 0.0] |
| | |
| | warning: The Schur decomposition is not unique. |
| | """ |
| |
|
| | n = A.rows |
| |
|
| | if n == 1: |
| | return (ctx.matrix([[1]]), A) |
| |
|
| | if not overwrite_a: |
| | A = A.copy() |
| |
|
| | T = ctx.matrix(n, 1) |
| |
|
| | hessenberg_reduce_0(ctx, A, T) |
| | Q = A.copy() |
| | hessenberg_reduce_1(ctx, Q, T) |
| |
|
| | for x in xrange(n): |
| | for y in xrange(x + 2, n): |
| | A[y,x] = 0 |
| |
|
| | hessenberg_qr(ctx, A, Q) |
| |
|
| | return Q, A |
| |
|
| |
|
| | def eig_tr_r(ctx, A): |
| | """ |
| | This routine calculates the right eigenvectors of an upper right triangular matrix. |
| | |
| | input: |
| | A an upper right triangular matrix |
| | |
| | output: |
| | ER a matrix whose columns form the right eigenvectors of A |
| | |
| | return value: ER |
| | """ |
| |
|
| | |
| |
|
| | n = A.rows |
| |
|
| | ER = ctx.eye(n) |
| |
|
| | eps = ctx.eps |
| |
|
| | unfl = ctx.ldexp(ctx.one, -ctx.prec * 30) |
| | |
| | |
| |
|
| | smlnum = unfl * (n / eps) |
| | simin = 1 / ctx.sqrt(eps) |
| |
|
| | rmax = 1 |
| |
|
| | for i in xrange(1, n): |
| | s = A[i,i] |
| |
|
| | smin = max(eps * abs(s), smlnum) |
| |
|
| | for j in xrange(i - 1, -1, -1): |
| |
|
| | r = 0 |
| | for k in xrange(j + 1, i + 1): |
| | r += A[j,k] * ER[k,i] |
| |
|
| | t = A[j,j] - s |
| | if abs(t) < smin: |
| | t = smin |
| |
|
| | r = -r / t |
| | ER[j,i] = r |
| |
|
| | rmax = max(rmax, abs(r)) |
| | if rmax > simin: |
| | for k in xrange(j, i+1): |
| | ER[k,i] /= rmax |
| | rmax = 1 |
| |
|
| | if rmax != 1: |
| | for k in xrange(0, i + 1): |
| | ER[k,i] /= rmax |
| |
|
| | return ER |
| |
|
| | def eig_tr_l(ctx, A): |
| | """ |
| | This routine calculates the left eigenvectors of an upper right triangular matrix. |
| | |
| | input: |
| | A an upper right triangular matrix |
| | |
| | output: |
| | EL a matrix whose rows form the left eigenvectors of A |
| | |
| | return value: EL |
| | """ |
| |
|
| | n = A.rows |
| |
|
| | EL = ctx.eye(n) |
| |
|
| | eps = ctx.eps |
| |
|
| | unfl = ctx.ldexp(ctx.one, -ctx.prec * 30) |
| | |
| | |
| |
|
| | smlnum = unfl * (n / eps) |
| | simin = 1 / ctx.sqrt(eps) |
| |
|
| | rmax = 1 |
| |
|
| | for i in xrange(0, n - 1): |
| | s = A[i,i] |
| |
|
| | smin = max(eps * abs(s), smlnum) |
| |
|
| | for j in xrange(i + 1, n): |
| |
|
| | r = 0 |
| | for k in xrange(i, j): |
| | r += EL[i,k] * A[k,j] |
| |
|
| | t = A[j,j] - s |
| | if abs(t) < smin: |
| | t = smin |
| |
|
| | r = -r / t |
| | EL[i,j] = r |
| |
|
| | rmax = max(rmax, abs(r)) |
| | if rmax > simin: |
| | for k in xrange(i, j + 1): |
| | EL[i,k] /= rmax |
| | rmax = 1 |
| |
|
| | if rmax != 1: |
| | for k in xrange(i, n): |
| | EL[i,k] /= rmax |
| |
|
| | return EL |
| |
|
| | @defun |
| | def eig(ctx, A, left = False, right = True, overwrite_a = False): |
| | """ |
| | This routine computes the eigenvalues and optionally the left and right |
| | eigenvectors of a square matrix A. Given A, a vector E and matrices ER |
| | and EL are calculated such that |
| | |
| | A ER[:,i] = E[i] ER[:,i] |
| | EL[i,:] A = EL[i,:] E[i] |
| | |
| | E contains the eigenvalues of A. The columns of ER contain the right eigenvectors |
| | of A whereas the rows of EL contain the left eigenvectors. |
| | |
| | |
| | input: |
| | A : a real or complex square matrix of shape (n, n) |
| | left : if true, the left eigenvectors are calculated. |
| | right : if true, the right eigenvectors are calculated. |
| | overwrite_a : if true, allows modification of A which may improve |
| | performance. if false, A is not modified. |
| | |
| | output: |
| | E : a list of length n containing the eigenvalues of A. |
| | ER : a matrix whose columns contain the right eigenvectors of A. |
| | EL : a matrix whose rows contain the left eigenvectors of A. |
| | |
| | return values: |
| | E if left and right are both false. |
| | (E, ER) if right is true and left is false. |
| | (E, EL) if left is true and right is false. |
| | (E, EL, ER) if left and right are true. |
| | |
| | |
| | examples: |
| | >>> from mpmath import mp |
| | >>> A = mp.matrix([[3, -1, 2], [2, 5, -5], [-2, -3, 7]]) |
| | >>> E, ER = mp.eig(A) |
| | >>> print(mp.chop(A * ER[:,0] - E[0] * ER[:,0])) |
| | [0.0] |
| | [0.0] |
| | [0.0] |
| | |
| | >>> E, EL, ER = mp.eig(A,left = True, right = True) |
| | >>> E, EL, ER = mp.eig_sort(E, EL, ER) |
| | >>> mp.nprint(E) |
| | [2.0, 4.0, 9.0] |
| | >>> print(mp.chop(A * ER[:,0] - E[0] * ER[:,0])) |
| | [0.0] |
| | [0.0] |
| | [0.0] |
| | >>> print(mp.chop( EL[0,:] * A - EL[0,:] * E[0])) |
| | [0.0 0.0 0.0] |
| | |
| | warning: |
| | - If there are multiple eigenvalues, the eigenvectors do not necessarily |
| | span the whole vectorspace, i.e. ER and EL may have not full rank. |
| | Furthermore in that case the eigenvectors are numerical ill-conditioned. |
| | - In the general case the eigenvalues have no natural order. |
| | |
| | see also: |
| | - eigh (or eigsy, eighe) for the symmetric eigenvalue problem. |
| | - eig_sort for sorting of eigenvalues and eigenvectors |
| | """ |
| |
|
| | n = A.rows |
| |
|
| | if n == 1: |
| | if left and (not right): |
| | return ([A[0]], ctx.matrix([[1]])) |
| |
|
| | if right and (not left): |
| | return ([A[0]], ctx.matrix([[1]])) |
| |
|
| | return ([A[0]], ctx.matrix([[1]]), ctx.matrix([[1]])) |
| |
|
| | if not overwrite_a: |
| | A = A.copy() |
| |
|
| | T = ctx.zeros(n, 1) |
| |
|
| | hessenberg_reduce_0(ctx, A, T) |
| |
|
| | if left or right: |
| | Q = A.copy() |
| | hessenberg_reduce_1(ctx, Q, T) |
| | else: |
| | Q = False |
| |
|
| | for x in xrange(n): |
| | for y in xrange(x + 2, n): |
| | A[y,x] = 0 |
| |
|
| | hessenberg_qr(ctx, A, Q) |
| |
|
| | E = [0 for i in xrange(n)] |
| | for i in xrange(n): |
| | E[i] = A[i,i] |
| |
|
| | if not (left or right): |
| | return E |
| |
|
| | if left: |
| | EL = eig_tr_l(ctx, A) |
| | EL = EL * Q.transpose_conj() |
| |
|
| | if right: |
| | ER = eig_tr_r(ctx, A) |
| | ER = Q * ER |
| |
|
| | if left and (not right): |
| | return (E, EL) |
| |
|
| | if right and (not left): |
| | return (E, ER) |
| |
|
| | return (E, EL, ER) |
| |
|
| | @defun |
| | def eig_sort(ctx, E, EL = False, ER = False, f = "real"): |
| | """ |
| | This routine sorts the eigenvalues and eigenvectors delivered by ``eig``. |
| | |
| | parameters: |
| | E : the eigenvalues as delivered by eig |
| | EL : the left eigenvectors as delivered by eig, or false |
| | ER : the right eigenvectors as delivered by eig, or false |
| | f : either a string ("real" sort by increasing real part, "imag" sort by |
| | increasing imag part, "abs" sort by absolute value) or a function |
| | mapping complexs to the reals, i.e. ``f = lambda x: -mp.re(x) `` |
| | would sort the eigenvalues by decreasing real part. |
| | |
| | return values: |
| | E if EL and ER are both false. |
| | (E, ER) if ER is not false and left is false. |
| | (E, EL) if EL is not false and right is false. |
| | (E, EL, ER) if EL and ER are not false. |
| | |
| | example: |
| | >>> from mpmath import mp |
| | >>> A = mp.matrix([[3, -1, 2], [2, 5, -5], [-2, -3, 7]]) |
| | >>> E, EL, ER = mp.eig(A,left = True, right = True) |
| | >>> E, EL, ER = mp.eig_sort(E, EL, ER) |
| | >>> mp.nprint(E) |
| | [2.0, 4.0, 9.0] |
| | >>> E, EL, ER = mp.eig_sort(E, EL, ER,f = lambda x: -mp.re(x)) |
| | >>> mp.nprint(E) |
| | [9.0, 4.0, 2.0] |
| | >>> print(mp.chop(A * ER[:,0] - E[0] * ER[:,0])) |
| | [0.0] |
| | [0.0] |
| | [0.0] |
| | >>> print(mp.chop( EL[0,:] * A - EL[0,:] * E[0])) |
| | [0.0 0.0 0.0] |
| | """ |
| |
|
| | if isinstance(f, str): |
| | if f == "real": |
| | f = ctx.re |
| | elif f == "imag": |
| | f = ctx.im |
| | elif f == "abs": |
| | f = abs |
| | else: |
| | raise RuntimeError("unknown function %s" % f) |
| |
|
| | n = len(E) |
| |
|
| | |
| |
|
| | for i in xrange(n): |
| | imax = i |
| | s = f(E[i]) |
| |
|
| | for j in xrange(i + 1, n): |
| | c = f(E[j]) |
| | if c < s: |
| | s = c |
| | imax = j |
| |
|
| | if imax != i: |
| | |
| |
|
| | z = E[i] |
| | E[i] = E[imax] |
| | E[imax] = z |
| |
|
| | if not isinstance(EL, bool): |
| | for j in xrange(n): |
| | z = EL[i,j] |
| | EL[i,j] = EL[imax,j] |
| | EL[imax,j] = z |
| |
|
| | if not isinstance(ER, bool): |
| | for j in xrange(n): |
| | z = ER[j,i] |
| | ER[j,i] = ER[j,imax] |
| | ER[j,imax] = z |
| |
|
| | if isinstance(EL, bool) and isinstance(ER, bool): |
| | return E |
| |
|
| | if isinstance(EL, bool) and not(isinstance(ER, bool)): |
| | return (E, ER) |
| |
|
| | if isinstance(ER, bool) and not(isinstance(EL, bool)): |
| | return (E, EL) |
| |
|
| | return (E, EL, ER) |
| |
|