1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807 |
- #!/usr/bin/python
- # -*- coding: utf-8 -*-
- ##################################################################################################
- # module for the symmetric eigenvalue problem
- # Copyright 2013 Timo Hartmann (thartmann15 at gmail.com)
- #
- # todo:
- # - implement balancing
- #
- ##################################################################################################
- """
- 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.
- """
- # note : the vector v of the i-th houshoulder reflector is stored in a[(i+1):,i]
- # whereas v/<v,v> is stored in a[i,(i+1):]
- n = A.rows
- for i in xrange(n - 1, 0, -1):
- # scale the vector
- scale = 0
- for k in xrange(0, i):
- scale += abs(A[k,i])
- scale_inv = 0
- if scale != 0:
- scale_inv = 1/scale
- # sadly there are floating point numbers not equal to zero whose reciprocal is infinity
- if i == 1 or scale == 0 or ctx.isinf(scale_inv):
- E[i] = A[i-1,i] # nothing to do
- D[i] = 0
- continue
- # calculate parameters for housholder transformation
- 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
- # apply housholder transformation
- for j in xrange(0, i):
- if calc_ev:
- A[i,j] = A[j,i] / H
- G = 0 # calculate A*U
- 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 # calculate P
- F += E[j] * A[j,i]
- HH = F / (2 * H)
- for j in xrange(0, i): # calculate reduced A
- F = A[j,i]
- G = E[j] - HH * F # calculate Q
- 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): # better for compatibility
- 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): # accumulate transformation matrices
- 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 the vector
- 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
- # sadly there are floating point numbers not equal to zero whose reciprocal is infinity
- 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
- # calculate parameters for housholder transformation
- 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 # T[i-1]=-T[i]*F, but we need T[i-1] as temporary storage
- G *= F
- else:
- TZ = -T[i] # T[i-1]=-T[i]
- A[i-1,i] += G
- F = 0
- # apply housholder transformation
- for j in xrange(0, i):
- A[i,j] = A[j,i] / H
- G = 0 # calculate A*U
- 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 # calculate P
- F += ctx.conj(T[j]) * A[j,i]
- HH = F / (2 * H)
- for j in xrange(0, i): # calculate reduced A
- F = A[j,i]
- G = T[j] - HH * F # calculate Q
- T[j] = G
- for k in xrange(0, j + 1):
- A[k,j] -= ctx.conj(F) * T[k] + ctx.conj(G) * A[k,i]
- # as we use the lower left part for storage
- # we have to use the transpose of the normal formula
- T[i-1] = TZ
- D[i] = H
- for i in xrange(1, n): # better for compatibility
- 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:
- # look for a small subdiagonal element
- 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
- # form shift
- 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): # this here is a slight improvement also used in gaussq.f or acm algorithm 726.
- 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):
- # calculate eigenvectors
- 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):
- # sort eigenvalues and eigenvectors (bubble-sort)
- 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":
- # legendre on the range -1 +1 , abramowitz, table 25.4, p.916
- 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":
- # legendre shifted to 0 1 , abramowitz, table 25.8, p.921
- 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":
- # hermite on the range -inf +inf , abramowitz, table 25.10,p.924
- w = ctx.sqrt(ctx.pi)
- for i in xrange(n):
- j = i + 1
- e[i] = ctx.sqrt(j / ctx.mpf(2))
- elif qtype == "laguerre":
- # laguerre on the range 0 +inf , abramowitz, table 25.9, p. 923
- w = 1
- for i in xrange(n):
- j = i + 1
- d[i] = 2 * j - 1
- e[i] = j
- elif qtype=="chebyshev1":
- # chebyshev polynimials of the first kind
- 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":
- # chebyshev polynimials of the second kind
- w = ctx.pi / 2
- for i in xrange(n):
- e[i] = 1 / ctx.mpf(2)
- elif qtype == "glaguerre":
- # generalized laguerre on the range 0 +inf
- 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":
- # jacobi polynomials
- 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 is a temporary array of size n
- work = ctx.zeros(n, 1)
- g = scale = anorm = 0
- maxits = 3 * ctx.dps
- for i in xrange(n): # householder reduction to bidiagonal form
- 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): # accumulation of right hand transformations
- 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): # accumulation of left hand transformations
- 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):
- # diagonalization of the bidiagonal form:
- # loop over singular values, and over allowed itations
- 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: # convergence
- if z < 0: # singular value is made nonnegative
- 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] # shift from bottom 2 by 2 minor
- 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 # next qt transformation
- 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: # rotation can be arbitray 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
- ##########################
- # Sort singular values into decreasing order (bubble-sort)
- for i in xrange(n):
- imax = i
- s = ctx.fabs(S[i]) # s is the current maximal element
- for j in xrange(i + 1, n):
- c = ctx.fabs(S[j])
- if c > s:
- s = c
- imax = j
- if imax != i:
- # swap singular values
- 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 is a temporary array of size n
- 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): # householder reduction to bidiagonal form
- dwork[i] = scale * g # dwork are the side-diagonal elements
- 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 # S are the diagonal elements
- 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): # accumulation of right hand transformations
- 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): # accumulation of left hand transformations
- 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):
- # diagonalization of the bidiagonal form:
- # loop over singular values, and over allowed itations
- 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: # convergence
- if z < 0: # singular value is made nonnegative
- 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] # shift from bottom 2 by 2 minor
- 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 # next qt transformation
- 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: # rotation can be arbitray 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
- ##########################
- # Sort singular values into decreasing order (bubble-sort)
- for i in xrange(n):
- imax = i
- s = ctx.fabs(S[i]) # s is the current maximal element
- for j in xrange(i + 1, n):
- c = ctx.fabs(S[j])
- if c > s:
- s = c
- imax = j
- if imax != i:
- # swap singular values
- 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)
|