chore: 添加虚拟环境到仓库
- 添加 backend_service/venv 虚拟环境 - 包含所有Python依赖包 - 注意:虚拟环境约393MB,包含12655个文件
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
from . import eigen # to set methods
|
||||
from . import eigen_symmetric # to set methods
|
||||
@@ -0,0 +1,531 @@
|
||||
from ..libmp.backend import xrange
|
||||
|
||||
# TODO: should use diagonalization-based algorithms
|
||||
|
||||
class MatrixCalculusMethods(object):
|
||||
|
||||
def _exp_pade(ctx, a):
|
||||
"""
|
||||
Exponential of a matrix using Pade approximants.
|
||||
|
||||
See G. H. Golub, C. F. van Loan 'Matrix Computations',
|
||||
third Ed., page 572
|
||||
|
||||
TODO:
|
||||
- find a good estimate for q
|
||||
- reduce the number of matrix multiplications to improve
|
||||
performance
|
||||
"""
|
||||
def eps_pade(p):
|
||||
return ctx.mpf(2)**(3-2*p) * \
|
||||
ctx.factorial(p)**2/(ctx.factorial(2*p)**2 * (2*p + 1))
|
||||
q = 4
|
||||
extraq = 8
|
||||
while 1:
|
||||
if eps_pade(q) < ctx.eps:
|
||||
break
|
||||
q += 1
|
||||
q += extraq
|
||||
j = int(max(1, ctx.mag(ctx.mnorm(a,'inf'))))
|
||||
extra = q
|
||||
prec = ctx.prec
|
||||
ctx.dps += extra + 3
|
||||
try:
|
||||
a = a/2**j
|
||||
na = a.rows
|
||||
den = ctx.eye(na)
|
||||
num = ctx.eye(na)
|
||||
x = ctx.eye(na)
|
||||
c = ctx.mpf(1)
|
||||
for k in range(1, q+1):
|
||||
c *= ctx.mpf(q - k + 1)/((2*q - k + 1) * k)
|
||||
x = a*x
|
||||
cx = c*x
|
||||
num += cx
|
||||
den += (-1)**k * cx
|
||||
f = ctx.lu_solve_mat(den, num)
|
||||
for k in range(j):
|
||||
f = f*f
|
||||
finally:
|
||||
ctx.prec = prec
|
||||
return f*1
|
||||
|
||||
def expm(ctx, A, method='taylor'):
|
||||
r"""
|
||||
Computes the matrix exponential of a square matrix `A`, which is defined
|
||||
by the power series
|
||||
|
||||
.. math ::
|
||||
|
||||
\exp(A) = I + A + \frac{A^2}{2!} + \frac{A^3}{3!} + \ldots
|
||||
|
||||
With method='taylor', the matrix exponential is computed
|
||||
using the Taylor series. With method='pade', Pade approximants
|
||||
are used instead.
|
||||
|
||||
**Examples**
|
||||
|
||||
Basic examples::
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 15; mp.pretty = True
|
||||
>>> expm(zeros(3))
|
||||
[1.0 0.0 0.0]
|
||||
[0.0 1.0 0.0]
|
||||
[0.0 0.0 1.0]
|
||||
>>> expm(eye(3))
|
||||
[2.71828182845905 0.0 0.0]
|
||||
[ 0.0 2.71828182845905 0.0]
|
||||
[ 0.0 0.0 2.71828182845905]
|
||||
>>> expm([[1,1,0],[1,0,1],[0,1,0]])
|
||||
[ 3.86814500615414 2.26812870852145 0.841130841230196]
|
||||
[ 2.26812870852145 2.44114713886289 1.42699786729125]
|
||||
[0.841130841230196 1.42699786729125 1.6000162976327]
|
||||
>>> expm([[1,1,0],[1,0,1],[0,1,0]], method='pade')
|
||||
[ 3.86814500615414 2.26812870852145 0.841130841230196]
|
||||
[ 2.26812870852145 2.44114713886289 1.42699786729125]
|
||||
[0.841130841230196 1.42699786729125 1.6000162976327]
|
||||
>>> expm([[1+j, 0], [1+j,1]])
|
||||
[(1.46869393991589 + 2.28735528717884j) 0.0]
|
||||
[ (1.03776739863568 + 3.536943175722j) (2.71828182845905 + 0.0j)]
|
||||
|
||||
Matrices with large entries are allowed::
|
||||
|
||||
>>> expm(matrix([[1,2],[2,3]])**25)
|
||||
[5.65024064048415e+2050488462815550 9.14228140091932e+2050488462815550]
|
||||
[9.14228140091932e+2050488462815550 1.47925220414035e+2050488462815551]
|
||||
|
||||
The identity `\exp(A+B) = \exp(A) \exp(B)` does not hold for
|
||||
noncommuting matrices::
|
||||
|
||||
>>> A = hilbert(3)
|
||||
>>> B = A + eye(3)
|
||||
>>> chop(mnorm(A*B - B*A))
|
||||
0.0
|
||||
>>> chop(mnorm(expm(A+B) - expm(A)*expm(B)))
|
||||
0.0
|
||||
>>> B = A + ones(3)
|
||||
>>> mnorm(A*B - B*A)
|
||||
1.8
|
||||
>>> mnorm(expm(A+B) - expm(A)*expm(B))
|
||||
42.0927851137247
|
||||
|
||||
"""
|
||||
if method == 'pade':
|
||||
prec = ctx.prec
|
||||
try:
|
||||
A = ctx.matrix(A)
|
||||
ctx.prec += 2*A.rows
|
||||
res = ctx._exp_pade(A)
|
||||
finally:
|
||||
ctx.prec = prec
|
||||
return res
|
||||
A = ctx.matrix(A)
|
||||
prec = ctx.prec
|
||||
j = int(max(1, ctx.mag(ctx.mnorm(A,'inf'))))
|
||||
j += int(0.5*prec**0.5)
|
||||
try:
|
||||
ctx.prec += 10 + 2*j
|
||||
tol = +ctx.eps
|
||||
A = A/2**j
|
||||
T = A
|
||||
Y = A**0 + A
|
||||
k = 2
|
||||
while 1:
|
||||
T *= A * (1/ctx.mpf(k))
|
||||
if ctx.mnorm(T, 'inf') < tol:
|
||||
break
|
||||
Y += T
|
||||
k += 1
|
||||
for k in xrange(j):
|
||||
Y = Y*Y
|
||||
finally:
|
||||
ctx.prec = prec
|
||||
Y *= 1
|
||||
return Y
|
||||
|
||||
def cosm(ctx, A):
|
||||
r"""
|
||||
Gives the cosine of a square matrix `A`, defined in analogy
|
||||
with the matrix exponential.
|
||||
|
||||
Examples::
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 15; mp.pretty = True
|
||||
>>> X = eye(3)
|
||||
>>> cosm(X)
|
||||
[0.54030230586814 0.0 0.0]
|
||||
[ 0.0 0.54030230586814 0.0]
|
||||
[ 0.0 0.0 0.54030230586814]
|
||||
>>> X = hilbert(3)
|
||||
>>> cosm(X)
|
||||
[ 0.424403834569555 -0.316643413047167 -0.221474945949293]
|
||||
[-0.316643413047167 0.820646708837824 -0.127183694770039]
|
||||
[-0.221474945949293 -0.127183694770039 0.909236687217541]
|
||||
>>> X = matrix([[1+j,-2],[0,-j]])
|
||||
>>> cosm(X)
|
||||
[(0.833730025131149 - 0.988897705762865j) (1.07485840848393 - 0.17192140544213j)]
|
||||
[ 0.0 (1.54308063481524 + 0.0j)]
|
||||
"""
|
||||
B = 0.5 * (ctx.expm(A*ctx.j) + ctx.expm(A*(-ctx.j)))
|
||||
if not sum(A.apply(ctx.im).apply(abs)):
|
||||
B = B.apply(ctx.re)
|
||||
return B
|
||||
|
||||
def sinm(ctx, A):
|
||||
r"""
|
||||
Gives the sine of a square matrix `A`, defined in analogy
|
||||
with the matrix exponential.
|
||||
|
||||
Examples::
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 15; mp.pretty = True
|
||||
>>> X = eye(3)
|
||||
>>> sinm(X)
|
||||
[0.841470984807897 0.0 0.0]
|
||||
[ 0.0 0.841470984807897 0.0]
|
||||
[ 0.0 0.0 0.841470984807897]
|
||||
>>> X = hilbert(3)
|
||||
>>> sinm(X)
|
||||
[0.711608512150994 0.339783913247439 0.220742837314741]
|
||||
[0.339783913247439 0.244113865695532 0.187231271174372]
|
||||
[0.220742837314741 0.187231271174372 0.155816730769635]
|
||||
>>> X = matrix([[1+j,-2],[0,-j]])
|
||||
>>> sinm(X)
|
||||
[(1.29845758141598 + 0.634963914784736j) (-1.96751511930922 + 0.314700021761367j)]
|
||||
[ 0.0 (0.0 - 1.1752011936438j)]
|
||||
"""
|
||||
B = (-0.5j) * (ctx.expm(A*ctx.j) - ctx.expm(A*(-ctx.j)))
|
||||
if not sum(A.apply(ctx.im).apply(abs)):
|
||||
B = B.apply(ctx.re)
|
||||
return B
|
||||
|
||||
def _sqrtm_rot(ctx, A, _may_rotate):
|
||||
# If the iteration fails to converge, cheat by performing
|
||||
# a rotation by a complex number
|
||||
u = ctx.j**0.3
|
||||
return ctx.sqrtm(u*A, _may_rotate) / ctx.sqrt(u)
|
||||
|
||||
def sqrtm(ctx, A, _may_rotate=2):
|
||||
r"""
|
||||
Computes a square root of the square matrix `A`, i.e. returns
|
||||
a matrix `B = A^{1/2}` such that `B^2 = A`. The square root
|
||||
of a matrix, if it exists, is not unique.
|
||||
|
||||
**Examples**
|
||||
|
||||
Square roots of some simple matrices::
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 15; mp.pretty = True
|
||||
>>> sqrtm([[1,0], [0,1]])
|
||||
[1.0 0.0]
|
||||
[0.0 1.0]
|
||||
>>> sqrtm([[0,0], [0,0]])
|
||||
[0.0 0.0]
|
||||
[0.0 0.0]
|
||||
>>> sqrtm([[2,0],[0,1]])
|
||||
[1.4142135623731 0.0]
|
||||
[ 0.0 1.0]
|
||||
>>> sqrtm([[1,1],[1,0]])
|
||||
[ (0.920442065259926 - 0.21728689675164j) (0.568864481005783 + 0.351577584254143j)]
|
||||
[(0.568864481005783 + 0.351577584254143j) (0.351577584254143 - 0.568864481005783j)]
|
||||
>>> sqrtm([[1,0],[0,1]])
|
||||
[1.0 0.0]
|
||||
[0.0 1.0]
|
||||
>>> sqrtm([[-1,0],[0,1]])
|
||||
[(0.0 - 1.0j) 0.0]
|
||||
[ 0.0 (1.0 + 0.0j)]
|
||||
>>> sqrtm([[j,0],[0,j]])
|
||||
[(0.707106781186547 + 0.707106781186547j) 0.0]
|
||||
[ 0.0 (0.707106781186547 + 0.707106781186547j)]
|
||||
|
||||
A square root of a rotation matrix, giving the corresponding
|
||||
half-angle rotation matrix::
|
||||
|
||||
>>> t1 = 0.75
|
||||
>>> t2 = t1 * 0.5
|
||||
>>> A1 = matrix([[cos(t1), -sin(t1)], [sin(t1), cos(t1)]])
|
||||
>>> A2 = matrix([[cos(t2), -sin(t2)], [sin(t2), cos(t2)]])
|
||||
>>> sqrtm(A1)
|
||||
[0.930507621912314 -0.366272529086048]
|
||||
[0.366272529086048 0.930507621912314]
|
||||
>>> A2
|
||||
[0.930507621912314 -0.366272529086048]
|
||||
[0.366272529086048 0.930507621912314]
|
||||
|
||||
The identity `(A^2)^{1/2} = A` does not necessarily hold::
|
||||
|
||||
>>> A = matrix([[4,1,4],[7,8,9],[10,2,11]])
|
||||
>>> sqrtm(A**2)
|
||||
[ 4.0 1.0 4.0]
|
||||
[ 7.0 8.0 9.0]
|
||||
[10.0 2.0 11.0]
|
||||
>>> sqrtm(A)**2
|
||||
[ 4.0 1.0 4.0]
|
||||
[ 7.0 8.0 9.0]
|
||||
[10.0 2.0 11.0]
|
||||
>>> A = matrix([[-4,1,4],[7,-8,9],[10,2,11]])
|
||||
>>> sqrtm(A**2)
|
||||
[ 7.43715112194995 -0.324127569985474 1.8481718827526]
|
||||
[-0.251549715716942 9.32699765900402 2.48221180985147]
|
||||
[ 4.11609388833616 0.775751877098258 13.017955697342]
|
||||
>>> chop(sqrtm(A)**2)
|
||||
[-4.0 1.0 4.0]
|
||||
[ 7.0 -8.0 9.0]
|
||||
[10.0 2.0 11.0]
|
||||
|
||||
For some matrices, a square root does not exist::
|
||||
|
||||
>>> sqrtm([[0,1], [0,0]])
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ZeroDivisionError: matrix is numerically singular
|
||||
|
||||
Two examples from the documentation for Matlab's ``sqrtm``::
|
||||
|
||||
>>> mp.dps = 15; mp.pretty = True
|
||||
>>> sqrtm([[7,10],[15,22]])
|
||||
[1.56669890360128 1.74077655955698]
|
||||
[2.61116483933547 4.17786374293675]
|
||||
>>>
|
||||
>>> X = matrix(\
|
||||
... [[5,-4,1,0,0],
|
||||
... [-4,6,-4,1,0],
|
||||
... [1,-4,6,-4,1],
|
||||
... [0,1,-4,6,-4],
|
||||
... [0,0,1,-4,5]])
|
||||
>>> Y = matrix(\
|
||||
... [[2,-1,-0,-0,-0],
|
||||
... [-1,2,-1,0,-0],
|
||||
... [0,-1,2,-1,0],
|
||||
... [-0,0,-1,2,-1],
|
||||
... [-0,-0,-0,-1,2]])
|
||||
>>> mnorm(sqrtm(X) - Y)
|
||||
4.53155328326114e-19
|
||||
|
||||
"""
|
||||
A = ctx.matrix(A)
|
||||
# Trivial
|
||||
if A*0 == A:
|
||||
return A
|
||||
prec = ctx.prec
|
||||
if _may_rotate:
|
||||
d = ctx.det(A)
|
||||
if abs(ctx.im(d)) < 16*ctx.eps and ctx.re(d) < 0:
|
||||
return ctx._sqrtm_rot(A, _may_rotate-1)
|
||||
try:
|
||||
ctx.prec += 10
|
||||
tol = ctx.eps * 128
|
||||
Y = A
|
||||
Z = I = A**0
|
||||
k = 0
|
||||
# Denman-Beavers iteration
|
||||
while 1:
|
||||
Yprev = Y
|
||||
try:
|
||||
Y, Z = 0.5*(Y+ctx.inverse(Z)), 0.5*(Z+ctx.inverse(Y))
|
||||
except ZeroDivisionError:
|
||||
if _may_rotate:
|
||||
Y = ctx._sqrtm_rot(A, _may_rotate-1)
|
||||
break
|
||||
else:
|
||||
raise
|
||||
mag1 = ctx.mnorm(Y-Yprev, 'inf')
|
||||
mag2 = ctx.mnorm(Y, 'inf')
|
||||
if mag1 <= mag2*tol:
|
||||
break
|
||||
if _may_rotate and k > 6 and not mag1 < mag2 * 0.001:
|
||||
return ctx._sqrtm_rot(A, _may_rotate-1)
|
||||
k += 1
|
||||
if k > ctx.prec:
|
||||
raise ctx.NoConvergence
|
||||
finally:
|
||||
ctx.prec = prec
|
||||
Y *= 1
|
||||
return Y
|
||||
|
||||
def logm(ctx, A):
|
||||
r"""
|
||||
Computes a logarithm of the square matrix `A`, i.e. returns
|
||||
a matrix `B = \log(A)` such that `\exp(B) = A`. The logarithm
|
||||
of a matrix, if it exists, is not unique.
|
||||
|
||||
**Examples**
|
||||
|
||||
Logarithms of some simple matrices::
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 15; mp.pretty = True
|
||||
>>> X = eye(3)
|
||||
>>> logm(X)
|
||||
[0.0 0.0 0.0]
|
||||
[0.0 0.0 0.0]
|
||||
[0.0 0.0 0.0]
|
||||
>>> logm(2*X)
|
||||
[0.693147180559945 0.0 0.0]
|
||||
[ 0.0 0.693147180559945 0.0]
|
||||
[ 0.0 0.0 0.693147180559945]
|
||||
>>> logm(expm(X))
|
||||
[1.0 0.0 0.0]
|
||||
[0.0 1.0 0.0]
|
||||
[0.0 0.0 1.0]
|
||||
|
||||
A logarithm of a complex matrix::
|
||||
|
||||
>>> X = matrix([[2+j, 1, 3], [1-j, 1-2*j, 1], [-4, -5, j]])
|
||||
>>> B = logm(X)
|
||||
>>> nprint(B)
|
||||
[ (0.808757 + 0.107759j) (2.20752 + 0.202762j) (1.07376 - 0.773874j)]
|
||||
[ (0.905709 - 0.107795j) (0.0287395 - 0.824993j) (0.111619 + 0.514272j)]
|
||||
[(-0.930151 + 0.399512j) (-2.06266 - 0.674397j) (0.791552 + 0.519839j)]
|
||||
>>> chop(expm(B))
|
||||
[(2.0 + 1.0j) 1.0 3.0]
|
||||
[(1.0 - 1.0j) (1.0 - 2.0j) 1.0]
|
||||
[ -4.0 -5.0 (0.0 + 1.0j)]
|
||||
|
||||
A matrix `X` close to the identity matrix, for which
|
||||
`\log(\exp(X)) = \exp(\log(X)) = X` holds::
|
||||
|
||||
>>> X = eye(3) + hilbert(3)/4
|
||||
>>> X
|
||||
[ 1.25 0.125 0.0833333333333333]
|
||||
[ 0.125 1.08333333333333 0.0625]
|
||||
[0.0833333333333333 0.0625 1.05]
|
||||
>>> logm(expm(X))
|
||||
[ 1.25 0.125 0.0833333333333333]
|
||||
[ 0.125 1.08333333333333 0.0625]
|
||||
[0.0833333333333333 0.0625 1.05]
|
||||
>>> expm(logm(X))
|
||||
[ 1.25 0.125 0.0833333333333333]
|
||||
[ 0.125 1.08333333333333 0.0625]
|
||||
[0.0833333333333333 0.0625 1.05]
|
||||
|
||||
A logarithm of a rotation matrix, giving back the angle of
|
||||
the rotation::
|
||||
|
||||
>>> t = 3.7
|
||||
>>> A = matrix([[cos(t),sin(t)],[-sin(t),cos(t)]])
|
||||
>>> chop(logm(A))
|
||||
[ 0.0 -2.58318530717959]
|
||||
[2.58318530717959 0.0]
|
||||
>>> (2*pi-t)
|
||||
2.58318530717959
|
||||
|
||||
For some matrices, a logarithm does not exist::
|
||||
|
||||
>>> logm([[1,0], [0,0]])
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ZeroDivisionError: matrix is numerically singular
|
||||
|
||||
Logarithm of a matrix with large entries::
|
||||
|
||||
>>> logm(hilbert(3) * 10**20).apply(re)
|
||||
[ 45.5597513593433 1.27721006042799 0.317662687717978]
|
||||
[ 1.27721006042799 42.5222778973542 2.24003708791604]
|
||||
[0.317662687717978 2.24003708791604 42.395212822267]
|
||||
|
||||
"""
|
||||
A = ctx.matrix(A)
|
||||
prec = ctx.prec
|
||||
try:
|
||||
ctx.prec += 10
|
||||
tol = ctx.eps * 128
|
||||
I = A**0
|
||||
B = A
|
||||
n = 0
|
||||
while 1:
|
||||
B = ctx.sqrtm(B)
|
||||
n += 1
|
||||
if ctx.mnorm(B-I, 'inf') < 0.125:
|
||||
break
|
||||
T = X = B-I
|
||||
L = X*0
|
||||
k = 1
|
||||
while 1:
|
||||
if k & 1:
|
||||
L += T / k
|
||||
else:
|
||||
L -= T / k
|
||||
T *= X
|
||||
if ctx.mnorm(T, 'inf') < tol:
|
||||
break
|
||||
k += 1
|
||||
if k > ctx.prec:
|
||||
raise ctx.NoConvergence
|
||||
finally:
|
||||
ctx.prec = prec
|
||||
L *= 2**n
|
||||
return L
|
||||
|
||||
def powm(ctx, A, r):
|
||||
r"""
|
||||
Computes `A^r = \exp(A \log r)` for a matrix `A` and complex
|
||||
number `r`.
|
||||
|
||||
**Examples**
|
||||
|
||||
Powers and inverse powers of a matrix::
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 15; mp.pretty = True
|
||||
>>> A = matrix([[4,1,4],[7,8,9],[10,2,11]])
|
||||
>>> powm(A, 2)
|
||||
[ 63.0 20.0 69.0]
|
||||
[174.0 89.0 199.0]
|
||||
[164.0 48.0 179.0]
|
||||
>>> chop(powm(powm(A, 4), 1/4.))
|
||||
[ 4.0 1.0 4.0]
|
||||
[ 7.0 8.0 9.0]
|
||||
[10.0 2.0 11.0]
|
||||
>>> powm(extraprec(20)(powm)(A, -4), -1/4.)
|
||||
[ 4.0 1.0 4.0]
|
||||
[ 7.0 8.0 9.0]
|
||||
[10.0 2.0 11.0]
|
||||
>>> chop(powm(powm(A, 1+0.5j), 1/(1+0.5j)))
|
||||
[ 4.0 1.0 4.0]
|
||||
[ 7.0 8.0 9.0]
|
||||
[10.0 2.0 11.0]
|
||||
>>> powm(extraprec(5)(powm)(A, -1.5), -1/(1.5))
|
||||
[ 4.0 1.0 4.0]
|
||||
[ 7.0 8.0 9.0]
|
||||
[10.0 2.0 11.0]
|
||||
|
||||
A Fibonacci-generating matrix::
|
||||
|
||||
>>> powm([[1,1],[1,0]], 10)
|
||||
[89.0 55.0]
|
||||
[55.0 34.0]
|
||||
>>> fib(10)
|
||||
55.0
|
||||
>>> powm([[1,1],[1,0]], 6.5)
|
||||
[(16.5166626964253 - 0.0121089837381789j) (10.2078589271083 + 0.0195927472575932j)]
|
||||
[(10.2078589271083 + 0.0195927472575932j) (6.30880376931698 - 0.0317017309957721j)]
|
||||
>>> (phi**6.5 - (1-phi)**6.5)/sqrt(5)
|
||||
(10.2078589271083 - 0.0195927472575932j)
|
||||
>>> powm([[1,1],[1,0]], 6.2)
|
||||
[ (14.3076953002666 - 0.008222855781077j) (8.81733464837593 + 0.0133048601383712j)]
|
||||
[(8.81733464837593 + 0.0133048601383712j) (5.49036065189071 - 0.0215277159194482j)]
|
||||
>>> (phi**6.2 - (1-phi)**6.2)/sqrt(5)
|
||||
(8.81733464837593 - 0.0133048601383712j)
|
||||
|
||||
"""
|
||||
A = ctx.matrix(A)
|
||||
r = ctx.convert(r)
|
||||
prec = ctx.prec
|
||||
try:
|
||||
ctx.prec += 10
|
||||
if ctx.isint(r):
|
||||
v = A ** int(r)
|
||||
elif ctx.isint(r*2):
|
||||
y = int(r*2)
|
||||
v = ctx.sqrtm(A) ** y
|
||||
else:
|
||||
v = ctx.expm(r*ctx.logm(A))
|
||||
finally:
|
||||
ctx.prec = prec
|
||||
v *= 1
|
||||
return v
|
||||
@@ -0,0 +1,877 @@
|
||||
#!/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)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,790 @@
|
||||
"""
|
||||
Linear algebra
|
||||
--------------
|
||||
|
||||
Linear equations
|
||||
................
|
||||
|
||||
Basic linear algebra is implemented; you can for example solve the linear
|
||||
equation system::
|
||||
|
||||
x + 2*y = -10
|
||||
3*x + 4*y = 10
|
||||
|
||||
using ``lu_solve``::
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.pretty = False
|
||||
>>> A = matrix([[1, 2], [3, 4]])
|
||||
>>> b = matrix([-10, 10])
|
||||
>>> x = lu_solve(A, b)
|
||||
>>> x
|
||||
matrix(
|
||||
[['30.0'],
|
||||
['-20.0']])
|
||||
|
||||
If you don't trust the result, use ``residual`` to calculate the residual ||A*x-b||::
|
||||
|
||||
>>> residual(A, x, b)
|
||||
matrix(
|
||||
[['3.46944695195361e-18'],
|
||||
['3.46944695195361e-18']])
|
||||
>>> str(eps)
|
||||
'2.22044604925031e-16'
|
||||
|
||||
As you can see, the solution is quite accurate. The error is caused by the
|
||||
inaccuracy of the internal floating point arithmetic. Though, it's even smaller
|
||||
than the current machine epsilon, which basically means you can trust the
|
||||
result.
|
||||
|
||||
If you need more speed, use NumPy, or ``fp.lu_solve`` for a floating-point computation.
|
||||
|
||||
>>> fp.lu_solve(A, b) # doctest: +ELLIPSIS
|
||||
matrix(...)
|
||||
|
||||
``lu_solve`` accepts overdetermined systems. It is usually not possible to solve
|
||||
such systems, so the residual is minimized instead. Internally this is done
|
||||
using Cholesky decomposition to compute a least squares approximation. This means
|
||||
that that ``lu_solve`` will square the errors. If you can't afford this, use
|
||||
``qr_solve`` instead. It is twice as slow but more accurate, and it calculates
|
||||
the residual automatically.
|
||||
|
||||
|
||||
Matrix factorization
|
||||
....................
|
||||
|
||||
The function ``lu`` computes an explicit LU factorization of a matrix::
|
||||
|
||||
>>> P, L, U = lu(matrix([[0,2,3],[4,5,6],[7,8,9]]))
|
||||
>>> print(P)
|
||||
[0.0 0.0 1.0]
|
||||
[1.0 0.0 0.0]
|
||||
[0.0 1.0 0.0]
|
||||
>>> print(L)
|
||||
[ 1.0 0.0 0.0]
|
||||
[ 0.0 1.0 0.0]
|
||||
[0.571428571428571 0.214285714285714 1.0]
|
||||
>>> print(U)
|
||||
[7.0 8.0 9.0]
|
||||
[0.0 2.0 3.0]
|
||||
[0.0 0.0 0.214285714285714]
|
||||
>>> print(P.T*L*U)
|
||||
[0.0 2.0 3.0]
|
||||
[4.0 5.0 6.0]
|
||||
[7.0 8.0 9.0]
|
||||
|
||||
Interval matrices
|
||||
-----------------
|
||||
|
||||
Matrices may contain interval elements. This allows one to perform
|
||||
basic linear algebra operations such as matrix multiplication
|
||||
and equation solving with rigorous error bounds::
|
||||
|
||||
>>> a = iv.matrix([['0.1','0.3','1.0'],
|
||||
... ['7.1','5.5','4.8'],
|
||||
... ['3.2','4.4','5.6']])
|
||||
>>>
|
||||
>>> b = iv.matrix(['4','0.6','0.5'])
|
||||
>>> c = iv.lu_solve(a, b)
|
||||
>>> print(c)
|
||||
[ [5.2582327113062568605927528666, 5.25823271130625686059275702219]]
|
||||
[[-13.1550493962678375411635581388, -13.1550493962678375411635540152]]
|
||||
[ [7.42069154774972557628979076189, 7.42069154774972557628979190734]]
|
||||
>>> print(a*c)
|
||||
[ [3.99999999999999999999999844904, 4.00000000000000000000000155096]]
|
||||
[[0.599999999999999999999968898009, 0.600000000000000000000031763736]]
|
||||
[[0.499999999999999999999979320485, 0.500000000000000000000020679515]]
|
||||
"""
|
||||
|
||||
# TODO:
|
||||
# *implement high-level qr()
|
||||
# *test unitvector
|
||||
# *iterative solving
|
||||
|
||||
from copy import copy
|
||||
|
||||
from ..libmp.backend import xrange
|
||||
|
||||
class LinearAlgebraMethods(object):
|
||||
|
||||
def LU_decomp(ctx, A, overwrite=False, use_cache=True):
|
||||
"""
|
||||
LU-factorization of a n*n matrix using the Gauss algorithm.
|
||||
Returns L and U in one matrix and the pivot indices.
|
||||
|
||||
Use overwrite to specify whether A will be overwritten with L and U.
|
||||
"""
|
||||
if not A.rows == A.cols:
|
||||
raise ValueError('need n*n matrix')
|
||||
# get from cache if possible
|
||||
if use_cache and isinstance(A, ctx.matrix) and A._LU:
|
||||
return A._LU
|
||||
if not overwrite:
|
||||
orig = A
|
||||
A = A.copy()
|
||||
tol = ctx.absmin(ctx.mnorm(A,1) * ctx.eps) # each pivot element has to be bigger
|
||||
n = A.rows
|
||||
p = [None]*(n - 1)
|
||||
for j in xrange(n - 1):
|
||||
# pivoting, choose max(abs(reciprocal row sum)*abs(pivot element))
|
||||
biggest = 0
|
||||
for k in xrange(j, n):
|
||||
s = ctx.fsum([ctx.absmin(A[k,l]) for l in xrange(j, n)])
|
||||
if ctx.absmin(s) <= tol:
|
||||
raise ZeroDivisionError('matrix is numerically singular')
|
||||
current = 1/s * ctx.absmin(A[k,j])
|
||||
if current > biggest: # TODO: what if equal?
|
||||
biggest = current
|
||||
p[j] = k
|
||||
# swap rows according to p
|
||||
ctx.swap_row(A, j, p[j])
|
||||
if ctx.absmin(A[j,j]) <= tol:
|
||||
raise ZeroDivisionError('matrix is numerically singular')
|
||||
# calculate elimination factors and add rows
|
||||
for i in xrange(j + 1, n):
|
||||
A[i,j] /= A[j,j]
|
||||
for k in xrange(j + 1, n):
|
||||
A[i,k] -= A[i,j]*A[j,k]
|
||||
if ctx.absmin(A[n - 1,n - 1]) <= tol:
|
||||
raise ZeroDivisionError('matrix is numerically singular')
|
||||
# cache decomposition
|
||||
if not overwrite and isinstance(orig, ctx.matrix):
|
||||
orig._LU = (A, p)
|
||||
return A, p
|
||||
|
||||
def L_solve(ctx, L, b, p=None):
|
||||
"""
|
||||
Solve the lower part of a LU factorized matrix for y.
|
||||
"""
|
||||
if L.rows != L.cols:
|
||||
raise RuntimeError("need n*n matrix")
|
||||
n = L.rows
|
||||
if len(b) != n:
|
||||
raise ValueError("Value should be equal to n")
|
||||
b = copy(b)
|
||||
if p: # swap b according to p
|
||||
for k in xrange(0, len(p)):
|
||||
ctx.swap_row(b, k, p[k])
|
||||
# solve
|
||||
for i in xrange(1, n):
|
||||
for j in xrange(i):
|
||||
b[i] -= L[i,j] * b[j]
|
||||
return b
|
||||
|
||||
def U_solve(ctx, U, y):
|
||||
"""
|
||||
Solve the upper part of a LU factorized matrix for x.
|
||||
"""
|
||||
if U.rows != U.cols:
|
||||
raise RuntimeError("need n*n matrix")
|
||||
n = U.rows
|
||||
if len(y) != n:
|
||||
raise ValueError("Value should be equal to n")
|
||||
x = copy(y)
|
||||
for i in xrange(n - 1, -1, -1):
|
||||
for j in xrange(i + 1, n):
|
||||
x[i] -= U[i,j] * x[j]
|
||||
x[i] /= U[i,i]
|
||||
return x
|
||||
|
||||
def lu_solve(ctx, A, b, **kwargs):
|
||||
"""
|
||||
Ax = b => x
|
||||
|
||||
Solve a determined or overdetermined linear equations system.
|
||||
Fast LU decomposition is used, which is less accurate than QR decomposition
|
||||
(especially for overdetermined systems), but it's twice as efficient.
|
||||
Use qr_solve if you want more precision or have to solve a very ill-
|
||||
conditioned system.
|
||||
|
||||
If you specify real=True, it does not check for overdeterminded complex
|
||||
systems.
|
||||
"""
|
||||
prec = ctx.prec
|
||||
try:
|
||||
ctx.prec += 10
|
||||
# do not overwrite A nor b
|
||||
A, b = ctx.matrix(A, **kwargs).copy(), ctx.matrix(b, **kwargs).copy()
|
||||
if A.rows < A.cols:
|
||||
raise ValueError('cannot solve underdetermined system')
|
||||
if A.rows > A.cols:
|
||||
# use least-squares method if overdetermined
|
||||
# (this increases errors)
|
||||
AH = A.H
|
||||
A = AH * A
|
||||
b = AH * b
|
||||
if (kwargs.get('real', False) or
|
||||
not sum(type(i) is ctx.mpc for i in A)):
|
||||
# TODO: necessary to check also b?
|
||||
x = ctx.cholesky_solve(A, b)
|
||||
else:
|
||||
x = ctx.lu_solve(A, b)
|
||||
else:
|
||||
# LU factorization
|
||||
A, p = ctx.LU_decomp(A)
|
||||
b = ctx.L_solve(A, b, p)
|
||||
x = ctx.U_solve(A, b)
|
||||
finally:
|
||||
ctx.prec = prec
|
||||
return x
|
||||
|
||||
def improve_solution(ctx, A, x, b, maxsteps=1):
|
||||
"""
|
||||
Improve a solution to a linear equation system iteratively.
|
||||
|
||||
This re-uses the LU decomposition and is thus cheap.
|
||||
Usually 3 up to 4 iterations are giving the maximal improvement.
|
||||
"""
|
||||
if A.rows != A.cols:
|
||||
raise RuntimeError("need n*n matrix") # TODO: really?
|
||||
for _ in xrange(maxsteps):
|
||||
r = ctx.residual(A, x, b)
|
||||
if ctx.norm(r, 2) < 10*ctx.eps:
|
||||
break
|
||||
# this uses cached LU decomposition and is thus cheap
|
||||
dx = ctx.lu_solve(A, -r)
|
||||
x += dx
|
||||
return x
|
||||
|
||||
def lu(ctx, A):
|
||||
"""
|
||||
A -> P, L, U
|
||||
|
||||
LU factorisation of a square matrix A. L is the lower, U the upper part.
|
||||
P is the permutation matrix indicating the row swaps.
|
||||
|
||||
P*A = L*U
|
||||
|
||||
If you need efficiency, use the low-level method LU_decomp instead, it's
|
||||
much more memory efficient.
|
||||
"""
|
||||
# get factorization
|
||||
A, p = ctx.LU_decomp(A)
|
||||
n = A.rows
|
||||
L = ctx.matrix(n)
|
||||
U = ctx.matrix(n)
|
||||
for i in xrange(n):
|
||||
for j in xrange(n):
|
||||
if i > j:
|
||||
L[i,j] = A[i,j]
|
||||
elif i == j:
|
||||
L[i,j] = 1
|
||||
U[i,j] = A[i,j]
|
||||
else:
|
||||
U[i,j] = A[i,j]
|
||||
# calculate permutation matrix
|
||||
P = ctx.eye(n)
|
||||
for k in xrange(len(p)):
|
||||
ctx.swap_row(P, k, p[k])
|
||||
return P, L, U
|
||||
|
||||
def unitvector(ctx, n, i):
|
||||
"""
|
||||
Return the i-th n-dimensional unit vector.
|
||||
"""
|
||||
assert 0 < i <= n, 'this unit vector does not exist'
|
||||
return [ctx.zero]*(i-1) + [ctx.one] + [ctx.zero]*(n-i)
|
||||
|
||||
def inverse(ctx, A, **kwargs):
|
||||
"""
|
||||
Calculate the inverse of a matrix.
|
||||
|
||||
If you want to solve an equation system Ax = b, it's recommended to use
|
||||
solve(A, b) instead, it's about 3 times more efficient.
|
||||
"""
|
||||
prec = ctx.prec
|
||||
try:
|
||||
ctx.prec += 10
|
||||
# do not overwrite A
|
||||
A = ctx.matrix(A, **kwargs).copy()
|
||||
n = A.rows
|
||||
# get LU factorisation
|
||||
A, p = ctx.LU_decomp(A)
|
||||
cols = []
|
||||
# calculate unit vectors and solve corresponding system to get columns
|
||||
for i in xrange(1, n + 1):
|
||||
e = ctx.unitvector(n, i)
|
||||
y = ctx.L_solve(A, e, p)
|
||||
cols.append(ctx.U_solve(A, y))
|
||||
# convert columns to matrix
|
||||
inv = []
|
||||
for i in xrange(n):
|
||||
row = []
|
||||
for j in xrange(n):
|
||||
row.append(cols[j][i])
|
||||
inv.append(row)
|
||||
result = ctx.matrix(inv, **kwargs)
|
||||
finally:
|
||||
ctx.prec = prec
|
||||
return result
|
||||
|
||||
def householder(ctx, A):
|
||||
"""
|
||||
(A|b) -> H, p, x, res
|
||||
|
||||
(A|b) is the coefficient matrix with left hand side of an optionally
|
||||
overdetermined linear equation system.
|
||||
H and p contain all information about the transformation matrices.
|
||||
x is the solution, res the residual.
|
||||
"""
|
||||
if not isinstance(A, ctx.matrix):
|
||||
raise TypeError("A should be a type of ctx.matrix")
|
||||
m = A.rows
|
||||
n = A.cols
|
||||
if m < n - 1:
|
||||
raise RuntimeError("Columns should not be less than rows")
|
||||
# calculate Householder matrix
|
||||
p = []
|
||||
for j in xrange(0, n - 1):
|
||||
s = ctx.fsum(abs(A[i,j])**2 for i in xrange(j, m))
|
||||
if not abs(s) > ctx.eps:
|
||||
raise ValueError('matrix is numerically singular')
|
||||
p.append(-ctx.sign(ctx.re(A[j,j])) * ctx.sqrt(s))
|
||||
kappa = ctx.one / (s - p[j] * A[j,j])
|
||||
A[j,j] -= p[j]
|
||||
for k in xrange(j+1, n):
|
||||
y = ctx.fsum(ctx.conj(A[i,j]) * A[i,k] for i in xrange(j, m)) * kappa
|
||||
for i in xrange(j, m):
|
||||
A[i,k] -= A[i,j] * y
|
||||
# solve Rx = c1
|
||||
x = [A[i,n - 1] for i in xrange(n - 1)]
|
||||
for i in xrange(n - 2, -1, -1):
|
||||
x[i] -= ctx.fsum(A[i,j] * x[j] for j in xrange(i + 1, n - 1))
|
||||
x[i] /= p[i]
|
||||
# calculate residual
|
||||
if not m == n - 1:
|
||||
r = [A[m-1-i, n-1] for i in xrange(m - n + 1)]
|
||||
else:
|
||||
# determined system, residual should be 0
|
||||
r = [0]*m # maybe a bad idea, changing r[i] will change all elements
|
||||
return A, p, x, r
|
||||
|
||||
#def qr(ctx, A):
|
||||
# """
|
||||
# A -> Q, R
|
||||
#
|
||||
# QR factorisation of a square matrix A using Householder decomposition.
|
||||
# Q is orthogonal, this leads to very few numerical errors.
|
||||
#
|
||||
# A = Q*R
|
||||
# """
|
||||
# H, p, x, res = householder(A)
|
||||
# TODO: implement this
|
||||
|
||||
def residual(ctx, A, x, b, **kwargs):
|
||||
"""
|
||||
Calculate the residual of a solution to a linear equation system.
|
||||
|
||||
r = A*x - b for A*x = b
|
||||
"""
|
||||
oldprec = ctx.prec
|
||||
try:
|
||||
ctx.prec *= 2
|
||||
A, x, b = ctx.matrix(A, **kwargs), ctx.matrix(x, **kwargs), ctx.matrix(b, **kwargs)
|
||||
return A*x - b
|
||||
finally:
|
||||
ctx.prec = oldprec
|
||||
|
||||
def qr_solve(ctx, A, b, norm=None, **kwargs):
|
||||
"""
|
||||
Ax = b => x, ||Ax - b||
|
||||
|
||||
Solve a determined or overdetermined linear equations system and
|
||||
calculate the norm of the residual (error).
|
||||
QR decomposition using Householder factorization is applied, which gives very
|
||||
accurate results even for ill-conditioned matrices. qr_solve is twice as
|
||||
efficient.
|
||||
"""
|
||||
if norm is None:
|
||||
norm = ctx.norm
|
||||
prec = ctx.prec
|
||||
try:
|
||||
ctx.prec += 10
|
||||
# do not overwrite A nor b
|
||||
A, b = ctx.matrix(A, **kwargs).copy(), ctx.matrix(b, **kwargs).copy()
|
||||
if A.rows < A.cols:
|
||||
raise ValueError('cannot solve underdetermined system')
|
||||
H, p, x, r = ctx.householder(ctx.extend(A, b))
|
||||
res = ctx.norm(r)
|
||||
# calculate residual "manually" for determined systems
|
||||
if res == 0:
|
||||
res = ctx.norm(ctx.residual(A, x, b))
|
||||
return ctx.matrix(x, **kwargs), res
|
||||
finally:
|
||||
ctx.prec = prec
|
||||
|
||||
def cholesky(ctx, A, tol=None):
|
||||
r"""
|
||||
Cholesky decomposition of a symmetric positive-definite matrix `A`.
|
||||
Returns a lower triangular matrix `L` such that `A = L \times L^T`.
|
||||
More generally, for a complex Hermitian positive-definite matrix,
|
||||
a Cholesky decomposition satisfying `A = L \times L^H` is returned.
|
||||
|
||||
The Cholesky decomposition can be used to solve linear equation
|
||||
systems twice as efficiently as LU decomposition, or to
|
||||
test whether `A` is positive-definite.
|
||||
|
||||
The optional parameter ``tol`` determines the tolerance for
|
||||
verifying positive-definiteness.
|
||||
|
||||
**Examples**
|
||||
|
||||
Cholesky decomposition of a positive-definite symmetric matrix::
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 25; mp.pretty = True
|
||||
>>> A = eye(3) + hilbert(3)
|
||||
>>> nprint(A)
|
||||
[ 2.0 0.5 0.333333]
|
||||
[ 0.5 1.33333 0.25]
|
||||
[0.333333 0.25 1.2]
|
||||
>>> L = cholesky(A)
|
||||
>>> nprint(L)
|
||||
[ 1.41421 0.0 0.0]
|
||||
[0.353553 1.09924 0.0]
|
||||
[0.235702 0.15162 1.05899]
|
||||
>>> chop(A - L*L.T)
|
||||
[0.0 0.0 0.0]
|
||||
[0.0 0.0 0.0]
|
||||
[0.0 0.0 0.0]
|
||||
|
||||
Cholesky decomposition of a Hermitian matrix::
|
||||
|
||||
>>> A = eye(3) + matrix([[0,0.25j,-0.5j],[-0.25j,0,0],[0.5j,0,0]])
|
||||
>>> L = cholesky(A)
|
||||
>>> nprint(L)
|
||||
[ 1.0 0.0 0.0]
|
||||
[(0.0 - 0.25j) (0.968246 + 0.0j) 0.0]
|
||||
[ (0.0 + 0.5j) (0.129099 + 0.0j) (0.856349 + 0.0j)]
|
||||
>>> chop(A - L*L.H)
|
||||
[0.0 0.0 0.0]
|
||||
[0.0 0.0 0.0]
|
||||
[0.0 0.0 0.0]
|
||||
|
||||
Attempted Cholesky decomposition of a matrix that is not positive
|
||||
definite::
|
||||
|
||||
>>> A = -eye(3) + hilbert(3)
|
||||
>>> L = cholesky(A)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: matrix is not positive-definite
|
||||
|
||||
**References**
|
||||
|
||||
1. [Wikipedia]_ http://en.wikipedia.org/wiki/Cholesky_decomposition
|
||||
|
||||
"""
|
||||
if not isinstance(A, ctx.matrix):
|
||||
raise RuntimeError("A should be a type of ctx.matrix")
|
||||
if not A.rows == A.cols:
|
||||
raise ValueError('need n*n matrix')
|
||||
if tol is None:
|
||||
tol = +ctx.eps
|
||||
n = A.rows
|
||||
L = ctx.matrix(n)
|
||||
for j in xrange(n):
|
||||
c = ctx.re(A[j,j])
|
||||
if abs(c-A[j,j]) > tol:
|
||||
raise ValueError('matrix is not Hermitian')
|
||||
s = c - ctx.fsum((L[j,k] for k in xrange(j)),
|
||||
absolute=True, squared=True)
|
||||
if s < tol:
|
||||
raise ValueError('matrix is not positive-definite')
|
||||
L[j,j] = ctx.sqrt(s)
|
||||
for i in xrange(j, n):
|
||||
it1 = (L[i,k] for k in xrange(j))
|
||||
it2 = (L[j,k] for k in xrange(j))
|
||||
t = ctx.fdot(it1, it2, conjugate=True)
|
||||
L[i,j] = (A[i,j] - t) / L[j,j]
|
||||
return L
|
||||
|
||||
def cholesky_solve(ctx, A, b, **kwargs):
|
||||
"""
|
||||
Ax = b => x
|
||||
|
||||
Solve a symmetric positive-definite linear equation system.
|
||||
This is twice as efficient as lu_solve.
|
||||
|
||||
Typical use cases:
|
||||
* A.T*A
|
||||
* Hessian matrix
|
||||
* differential equations
|
||||
"""
|
||||
prec = ctx.prec
|
||||
try:
|
||||
ctx.prec += 10
|
||||
# do not overwrite A nor b
|
||||
A, b = ctx.matrix(A, **kwargs).copy(), ctx.matrix(b, **kwargs).copy()
|
||||
if A.rows != A.cols:
|
||||
raise ValueError('can only solve determined system')
|
||||
# Cholesky factorization
|
||||
L = ctx.cholesky(A)
|
||||
# solve
|
||||
n = L.rows
|
||||
if len(b) != n:
|
||||
raise ValueError("Value should be equal to n")
|
||||
for i in xrange(n):
|
||||
b[i] -= ctx.fsum(L[i,j] * b[j] for j in xrange(i))
|
||||
b[i] /= L[i,i]
|
||||
x = ctx.U_solve(L.T, b)
|
||||
return x
|
||||
finally:
|
||||
ctx.prec = prec
|
||||
|
||||
def det(ctx, A):
|
||||
"""
|
||||
Calculate the determinant of a matrix.
|
||||
"""
|
||||
prec = ctx.prec
|
||||
try:
|
||||
# do not overwrite A
|
||||
A = ctx.matrix(A).copy()
|
||||
# use LU factorization to calculate determinant
|
||||
try:
|
||||
R, p = ctx.LU_decomp(A)
|
||||
except ZeroDivisionError:
|
||||
return 0
|
||||
z = 1
|
||||
for i, e in enumerate(p):
|
||||
if i != e:
|
||||
z *= -1
|
||||
for i in xrange(A.rows):
|
||||
z *= R[i,i]
|
||||
return z
|
||||
finally:
|
||||
ctx.prec = prec
|
||||
|
||||
def cond(ctx, A, norm=None):
|
||||
"""
|
||||
Calculate the condition number of a matrix using a specified matrix norm.
|
||||
|
||||
The condition number estimates the sensitivity of a matrix to errors.
|
||||
Example: small input errors for ill-conditioned coefficient matrices
|
||||
alter the solution of the system dramatically.
|
||||
|
||||
For ill-conditioned matrices it's recommended to use qr_solve() instead
|
||||
of lu_solve(). This does not help with input errors however, it just avoids
|
||||
to add additional errors.
|
||||
|
||||
Definition: cond(A) = ||A|| * ||A**-1||
|
||||
"""
|
||||
if norm is None:
|
||||
norm = lambda x: ctx.mnorm(x,1)
|
||||
return norm(A) * norm(ctx.inverse(A))
|
||||
|
||||
def lu_solve_mat(ctx, a, b):
|
||||
"""Solve a * x = b where a and b are matrices."""
|
||||
r = ctx.matrix(a.rows, b.cols)
|
||||
for i in range(b.cols):
|
||||
c = ctx.lu_solve(a, b.column(i))
|
||||
for j in range(len(c)):
|
||||
r[j, i] = c[j]
|
||||
return r
|
||||
|
||||
def qr(ctx, A, mode = 'full', edps = 10):
|
||||
"""
|
||||
Compute a QR factorization $A = QR$ where
|
||||
A is an m x n matrix of real or complex numbers where m >= n
|
||||
|
||||
mode has following meanings:
|
||||
(1) mode = 'raw' returns two matrixes (A, tau) in the
|
||||
internal format used by LAPACK
|
||||
(2) mode = 'skinny' returns the leading n columns of Q
|
||||
and n rows of R
|
||||
(3) Any other value returns the leading m columns of Q
|
||||
and m rows of R
|
||||
|
||||
edps is the increase in mp precision used for calculations
|
||||
|
||||
**Examples**
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 15
|
||||
>>> mp.pretty = True
|
||||
>>> A = matrix([[1, 2], [3, 4], [1, 1]])
|
||||
>>> Q, R = qr(A)
|
||||
>>> Q
|
||||
[-0.301511344577764 0.861640436855329 0.408248290463863]
|
||||
[-0.904534033733291 -0.123091490979333 -0.408248290463863]
|
||||
[-0.301511344577764 -0.492365963917331 0.816496580927726]
|
||||
>>> R
|
||||
[-3.3166247903554 -4.52267016866645]
|
||||
[ 0.0 0.738548945875996]
|
||||
[ 0.0 0.0]
|
||||
>>> Q * R
|
||||
[1.0 2.0]
|
||||
[3.0 4.0]
|
||||
[1.0 1.0]
|
||||
>>> chop(Q.T * Q)
|
||||
[1.0 0.0 0.0]
|
||||
[0.0 1.0 0.0]
|
||||
[0.0 0.0 1.0]
|
||||
>>> B = matrix([[1+0j, 2-3j], [3+j, 4+5j]])
|
||||
>>> Q, R = qr(B)
|
||||
>>> nprint(Q)
|
||||
[ (-0.301511 + 0.0j) (0.0695795 - 0.95092j)]
|
||||
[(-0.904534 - 0.301511j) (-0.115966 + 0.278318j)]
|
||||
>>> nprint(R)
|
||||
[(-3.31662 + 0.0j) (-5.72872 - 2.41209j)]
|
||||
[ 0.0 (3.91965 + 0.0j)]
|
||||
>>> Q * R
|
||||
[(1.0 + 0.0j) (2.0 - 3.0j)]
|
||||
[(3.0 + 1.0j) (4.0 + 5.0j)]
|
||||
>>> chop(Q.T * Q.conjugate())
|
||||
[1.0 0.0]
|
||||
[0.0 1.0]
|
||||
|
||||
"""
|
||||
|
||||
# check values before continuing
|
||||
assert isinstance(A, ctx.matrix)
|
||||
m = A.rows
|
||||
n = A.cols
|
||||
assert n >= 0
|
||||
assert m >= n
|
||||
assert edps >= 0
|
||||
|
||||
# check for complex data type
|
||||
cmplx = any(type(x) is ctx.mpc for x in A)
|
||||
|
||||
# temporarily increase the precision and initialize
|
||||
with ctx.extradps(edps):
|
||||
tau = ctx.matrix(n,1)
|
||||
A = A.copy()
|
||||
|
||||
# ---------------
|
||||
# FACTOR MATRIX A
|
||||
# ---------------
|
||||
if cmplx:
|
||||
one = ctx.mpc('1.0', '0.0')
|
||||
zero = ctx.mpc('0.0', '0.0')
|
||||
rzero = ctx.mpf('0.0')
|
||||
|
||||
# main loop to factor A (complex)
|
||||
for j in xrange(0, n):
|
||||
alpha = A[j,j]
|
||||
alphr = ctx.re(alpha)
|
||||
alphi = ctx.im(alpha)
|
||||
|
||||
if (m-j) >= 2:
|
||||
xnorm = ctx.fsum( A[i,j]*ctx.conj(A[i,j]) for i in xrange(j+1, m) )
|
||||
xnorm = ctx.re( ctx.sqrt(xnorm) )
|
||||
else:
|
||||
xnorm = rzero
|
||||
|
||||
if (xnorm == rzero) and (alphi == rzero):
|
||||
tau[j] = zero
|
||||
continue
|
||||
|
||||
if alphr < rzero:
|
||||
beta = ctx.sqrt(alphr**2 + alphi**2 + xnorm**2)
|
||||
else:
|
||||
beta = -ctx.sqrt(alphr**2 + alphi**2 + xnorm**2)
|
||||
|
||||
tau[j] = ctx.mpc( (beta - alphr) / beta, -alphi / beta )
|
||||
t = -ctx.conj(tau[j])
|
||||
za = one / (alpha - beta)
|
||||
|
||||
for i in xrange(j+1, m):
|
||||
A[i,j] *= za
|
||||
|
||||
A[j,j] = one
|
||||
for k in xrange(j+1, n):
|
||||
y = ctx.fsum(A[i,j] * ctx.conj(A[i,k]) for i in xrange(j, m))
|
||||
temp = t * ctx.conj(y)
|
||||
for i in xrange(j, m):
|
||||
A[i,k] += A[i,j] * temp
|
||||
|
||||
A[j,j] = ctx.mpc(beta, '0.0')
|
||||
else:
|
||||
one = ctx.mpf('1.0')
|
||||
zero = ctx.mpf('0.0')
|
||||
|
||||
# main loop to factor A (real)
|
||||
for j in xrange(0, n):
|
||||
alpha = A[j,j]
|
||||
|
||||
if (m-j) > 2:
|
||||
xnorm = ctx.fsum( (A[i,j])**2 for i in xrange(j+1, m) )
|
||||
xnorm = ctx.sqrt(xnorm)
|
||||
elif (m-j) == 2:
|
||||
xnorm = abs( A[m-1,j] )
|
||||
else:
|
||||
xnorm = zero
|
||||
|
||||
if xnorm == zero:
|
||||
tau[j] = zero
|
||||
continue
|
||||
|
||||
if alpha < zero:
|
||||
beta = ctx.sqrt(alpha**2 + xnorm**2)
|
||||
else:
|
||||
beta = -ctx.sqrt(alpha**2 + xnorm**2)
|
||||
|
||||
tau[j] = (beta - alpha) / beta
|
||||
t = -tau[j]
|
||||
da = one / (alpha - beta)
|
||||
|
||||
for i in xrange(j+1, m):
|
||||
A[i,j] *= da
|
||||
|
||||
A[j,j] = one
|
||||
for k in xrange(j+1, n):
|
||||
y = ctx.fsum( A[i,j] * A[i,k] for i in xrange(j, m) )
|
||||
temp = t * y
|
||||
for i in xrange(j,m):
|
||||
A[i,k] += A[i,j] * temp
|
||||
|
||||
A[j,j] = beta
|
||||
|
||||
# return factorization in same internal format as LAPACK
|
||||
if (mode == 'raw') or (mode == 'RAW'):
|
||||
return A, tau
|
||||
|
||||
# ----------------------------------
|
||||
# FORM Q USING BACKWARD ACCUMULATION
|
||||
# ----------------------------------
|
||||
|
||||
# form R before the values are overwritten
|
||||
R = A.copy()
|
||||
for j in xrange(0, n):
|
||||
for i in xrange(j+1, m):
|
||||
R[i,j] = zero
|
||||
|
||||
# set the value of p (number of columns of Q to return)
|
||||
p = m
|
||||
if (mode == 'skinny') or (mode == 'SKINNY'):
|
||||
p = n
|
||||
|
||||
# add columns to A if needed and initialize
|
||||
A.cols += (p-n)
|
||||
for j in xrange(0, p):
|
||||
A[j,j] = one
|
||||
for i in xrange(0, j):
|
||||
A[i,j] = zero
|
||||
|
||||
# main loop to form Q
|
||||
for j in xrange(n-1, -1, -1):
|
||||
t = -tau[j]
|
||||
A[j,j] += t
|
||||
|
||||
for k in xrange(j+1, p):
|
||||
if cmplx:
|
||||
y = ctx.fsum(A[i,j] * ctx.conj(A[i,k]) for i in xrange(j+1, m))
|
||||
temp = t * ctx.conj(y)
|
||||
else:
|
||||
y = ctx.fsum(A[i,j] * A[i,k] for i in xrange(j+1, m))
|
||||
temp = t * y
|
||||
A[j,k] = temp
|
||||
for i in xrange(j+1, m):
|
||||
A[i,k] += A[i,j] * temp
|
||||
|
||||
for i in xrange(j+1, m):
|
||||
A[i, j] *= t
|
||||
|
||||
return A, R[0:p,0:n]
|
||||
|
||||
# ------------------
|
||||
# END OF FUNCTION QR
|
||||
# ------------------
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user