123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877 |
- #!/usr/bin/python
- # -*- coding: utf-8 -*-
- ##################################################################################################
- # module for the eigenvalue problem
- # Copyright 2013 Timo Hartmann (thartmann15 at gmail.com)
- #
- # todo:
- # - implement balancing
- # - agressive early deflation
- #
- ##################################################################################################
- """
- 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.
- """
- # internally we work with householder reflections from the right.
- # let u be a row vector (i.e. u[i]=A[i,:i]). then
- # Q is build up by reflectors of the type (1-v'v) where v is a suitable
- # modification of u. these reflectors are applyed to A from the right.
- # because we work with reflectors from the right we have to start with
- # the bottom row of A and work then upwards (this corresponds to
- # some kind of RQ decomposition).
- # the first part of the vectors v (i.e. A[i,:(i-1)]) are stored as row vectors
- # in the lower left part of A (excluding the diagonal and subdiagonal).
- # the last entry of v is stored in T.
- # the upper right part of A (including diagonal and subdiagonal) becomes H.
- n = A.rows
- if n <= 2: return
- for i in xrange(n-1, 1, -1):
- # scale the vector
- 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):
- # sadly there are floating point numbers not equal to zero whose reciprocal is infinity
- T[i] = 0
- A[i,i-1] = 0
- continue
- # calculate parameters for housholder transformation
- 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):
- # apply housholder transformation (from right)
- 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):
- # apply housholder transformation (from left)
- 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
- """
- # implicitly shifted and bulge chasing is explained at p.398/399 in "Stoer, Bulirsch - Introduction to Numerical Analysis"
- # for bulge chasing see also "Watkins - The Matrix Eigenvalue Problem" sec.4.5,p.173
- # the Givens rotation we used is determined as follows: let c,s be two complex
- # numbers. then we have following relation:
- #
- # v = sqrt(|c|^2 + |s|^2)
- #
- # 1/v [ c~ s~] [c] = [v]
- # [-s c ] [s] [0]
- #
- # the matrix on the left is our Givens rotation.
- n = A.rows
- # first step
- # calculate givens rotation
- 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):
- # apply givens rotation from the left
- 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)):
- # apply givens rotation from the right
- 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):
- # eigenvectors
- x = Q[k,n0 ]
- y = Q[k,n0+1]
- Q[k,n0 ] = c * x + s * y
- Q[k,n0+1] = cc * y - cs * x
- # chase the bulge
- for j in xrange(n0, n1 - 2):
- # calculate givens rotation
- 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):
- # apply givens rotation from the left
- 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)):
- # apply givens rotation from the right
- 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):
- # eigenvectors
- 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:
- # kressner p.32 algo 3
- # the active submatrix is A[n0:n1,n0:n1]
- 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:
- # deflation found at position (k+1, k)
- A[k+1,k] = 0
- n0 = k + 1
- its = 0
- if n0 + 1 >= n1:
- # block of size at most two has converged
- n0 = 0
- n1 = k + 1
- if n1 < 2:
- # QR algorithm has converged
- return
- else:
- if (its % 30) == 10:
- # exceptional shift
- shift = A[n1-1,n1-2]
- elif (its % 30) == 20:
- # exceptional shift
- shift = abs(A[n1-1,n1-2])
- elif (its % 30) == 29:
- # exceptional shift
- shift = norm
- else:
- # A = [ a b ] det(x-A)=x*x-x*tr(A)+det(A)
- # [ c d ]
- #
- # eigenvalues bad: (tr(A)+sqrt((tr(A))**2-4*det(A)))/2
- # bad because of cancellation if |c| is small and |a-d| is small, too.
- #
- # eigenvalues good: (a+d+sqrt((a-d)**2+4*b*c))/2
- 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
- """
- # this subroutine is inspired by the lapack routines ctrevc.f,clatrs.f
- n = A.rows
- ER = ctx.eye(n)
- eps = ctx.eps
- unfl = ctx.ldexp(ctx.one, -ctx.prec * 30)
- # since mpmath effectively has no limits on the exponent, we simply scale doubles up
- # original double has prec*20
- 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)
- # since mpmath effectively has no limits on the exponent, we simply scale doubles up
- # original double has prec*20
- 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)
- # Sort eigenvalues (bubble-sort)
- for i in xrange(n):
- imax = i
- s = f(E[i]) # s is the current maximal element
- for j in xrange(i + 1, n):
- c = f(E[j])
- if c < s:
- s = c
- imax = j
- if imax != i:
- # swap eigenvalues
- 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)
|