## module jacobi
''' lam,x = jacobi(a,tol = 1.0e-8).
Solution of std. eigenvalue problem [a]{x} = lam{x}
by Jacobi's method. Returns eigenvalues in vector {lam}
and the eigenvectors as columns of matrix [x].
'''
import numpy as np
import math
def jacobi(a,tol = 1.0e-8): # Jacobi method
def threshold(a):
sum = 0.0
for i in range(n-1):
for j in range (i+1,n):
sum = sum + abs(a[i,j])
return 0.5*sum/n/(n-1)
def rotate(a,p,k,l): # Rotate to make a[k,l] = 0
aDiff = a[l,l] - a[k,k]
if abs(a[k,l]) < abs(aDiff)*1.0e-36: t = a[k,l]/aDiff
else:
phi = aDiff/(2.0*a[k,l])
t = 1.0/(abs(phi) + math.sqrt(phi**2 + 1.0))
if phi < 0.0: t = -t
c = 1.0/math.sqrt(t**2 + 1.0); s = t*c
tau = s/(1.0 + c)
temp = a[k,l]
a[k,l] = 0.0
a[k,k] = a[k,k] - t*temp
a[l,l] = a[l,l] + t*temp
for i in range(k): # Case of i < k
temp = a[i,k]
a[i,k] = temp - s*(a[i,l] + tau*temp)
a[i,l] = a[i,l] + s*(temp - tau*a[i,l])
for i in range(k+1,l): # Case of k < i < l
temp = a[k,i]
a[k,i] = temp - s*(a[i,l] + tau*a[k,i])
a[i,l] = a[i,l] + s*(temp - tau*a[i,l])
for i in range(l+1,n): # Case of i > l
temp = a[k,i]
a[k,i] = temp - s*(a[l,i] + tau*temp)
a[l,i] = a[l,i] + s*(temp - tau*a[l,i])
for i in range(n): # Update transformation matrix
temp = p[i,k]
p[i,k] = temp - s*(p[i,l] + tau*p[i,k])
p[i,l] = p[i,l] + s*(temp - tau*p[i,l])
n = len(a)
p = np.identity(n,float)
for k in range(20):
mu = threshold(a) # Compute new threshold
for i in range(n-1): # Sweep through matrix
for j in range(i+1,n):
if abs(a[i,j]) >= mu:
rotate(a,p,i,j)
if mu <= tol: return np.diagonal(a),p
print('Jacobi method did not converge')
## module sortJacobi
''' sortJacobi(lam,x).
Sorts the eigenvalues {lam} and eigenvectors [x]
in order of ascending eigenvalues.
'''
import swap
def sortJacobi(lam,x):
n = len(lam)
for i in range(n-1):
index = i
val = lam[i]
for j in range(i+1,n):
if lam[j] < val:
index = j
val = lam[j]
if index != i:
swap.swapRows(lam,i,index)
swap.swapCols(x,i,index)
## module stdForm
''' h,t = stdForm(a,b).
Transforms the eigenvalue problem [a]{x} = lam[b]{x}
to the standard form [h]{z} = lam{z}. The eigenvectors
are related by {x} = [t]{z}.
'''
import numpy as np
from choleski import *
def stdForm(a,b):
def invert(L): # Inverts lower triangular matrix L
n = len(L)
for j in range(n-1):
L[j,j] = 1.0/L[j,j]
for i in range(j+1,n):
L[i,j] = -np.dot(L[i,j:i],L[j:i,j])/L[i,i]
L[n-1,n-1] = 1.0/L[n-1,n-1]
n = len(a)
L = choleski(b)
invert(L)
h = np.dot(b,np.inner(a,L))
return h,np.transpose(L)
(1) Show that the analysis of the electric circuit shown leads to a matrix eigenvalue problem. (2) Determine the circular frequencies and the relative amplitudes of the currents.
Solution:
The circular frequencies are given by \( w_{i}=\sqrt{\lambda _{i}/LC} \)
## example9_3
# Determine the circular frequencies and the relative amplitudes of the
# currents.
import numpy
from jacobi import *
import math
from sortJacobi import *
from stdForm import *
A = np.array([[ 1/3, -1/3, 0.0], \
[-1/3, 4/3, -1.0], \
[ 0.0, -1.0, 2.0]])
B = np.array([[1.0, 0.0, 0.0], \
[0.0, 1.0, 0.0], \
[0.0, 0.0, 2.0]])
H,T = stdForm(A,B) # Transform into std. form
lam,Z = jacobi(H) # Z = eigenvecs. of H
X = np.dot(T,Z) # Eigenvecs. of original problem
sortJacobi(lam,X) # Arrange in ascending order of eigenvecs.
for i in range(3): # Normalize eigenvecs.
X[:,i] = X[:,i]/math.sqrt(np.dot(X[:,i],X[:,i]))
print('Eigenvalues:\n',lam)
print('Eigenvectors:\n',X)
input ("Press return to exit")
## module inversePower
''' lam,x = inversePower(a,s,tol=1.0e-6).
Inverse power method for solving the eigenvalue problem
[a]{x} = lam{x}. Returns 'lam' closest to 's' and the
corresponding eigenvector {x}.
'''
import numpy as np
from LUdecomp import *
import math
from random import random
def inversePower(a,s,tol=1.0e-6):
n = len(a)
aStar = a - np.identity(n)*s # Form [a*] = [a] - s[I]
aStar = LUdecomp(aStar) # Decompose [a*]
x = np.zeros(n)
for i in range(n): # Seed [x] with random numbers
x[i] = random()
xMag = math.sqrt(np.dot(x,x)) # Normalize [x]
x =x/xMag
for i in range(50): # Begin iterations
xOld = x.copy() # Save current [x]
x = LUsolve(aStar,x) # Solve [a*][x] = [xOld]
xMag = math.sqrt(np.dot(x,x)) # Normalize [x]
x = x/xMag
if np.dot(xOld,x) < 0.0: # Detect change in sign of [x]
sign = -1.0
x = -x
else: sign = 1.0
if math.sqrt(np.dot(xOld - x,xOld - x)) < tol:
return s + sign/xMag,x
print('Inverse power method did not converge')
## example9_4
# The stress matrix describing the state of stress at a point is
# S =[−30 10 20
# 10 40 −50
# 20 −50 −10]MPa
# Determine the largest principal stress (the eigenvalue of S farthest from zero) by the
# power method
import numpy as np
import math
s = np.array([[-30.0, 10.0, 20.0], \
[ 10.0, 40.0, -50.0], \
[ 20.0, -50.0, -10.0]])
v = np.array([1.0, 0.0, 0.0])
for i in range(100):
vOld = v.copy()
z = np.dot(s,v)
zMag = math.sqrt(np.dot(z,z))
v = z/zMag
if np.dot(vOld,v) < 0.0:
sign = -1.0
v = -v
else: sign = 1.0
if math.sqrt(np.dot(vOld - v,vOld - v)) < 1.0e-6: break
lam = sign*zMag
print("Number of iterations =",i)
print("Eigenvalue =",lam)
input("Press return to exit")
## example9_5
# Determine the smallest eigenvalue λ1 and the corresponding eigenvector of
# A =[11 2 3 1 4
# 2 9 3 5 2
# 3 3 15 4 3
# 1 5 4 12 4
# 4 2 3 4 17]
# Use the inverse power method with eigenvalue shifting knowing that λ1 ≈ 5
import numpy as np
from inversePower import *
s = 5.0
a = np.array([[ 11.0, 2.0, 3.0, 1.0, 4.0], \
[ 2.0, 9.0, 3.0, 5.0, 2.0], \
[ 3.0, 3.0, 15.0, 4.0, 3.0], \
[ 1.0, 5.0, 4.0, 12.0, 4.0], \
[ 4.0, 2.0, 3.0, 4.0, 17.0]])
lam,x = inversePower(a,s)
print("Eigenvalue =",lam)
print("\nEigenvector:\n",x)
input("\nPrint press return to exit")
## module inversePower5
''' lam,x = inversePower5(Bv,d,e,f,tol=1.0e-6).
Inverse power method for solving the eigenvalue problem
[A]{x} = lam[B]{x}, where [A] is pentadiagonal and [B]
is sparse. User must supply the function Bv(v) that
returns the vector [B]{v}.
'''
import numpy as np
from LUdecomp5 import *
import math
from numpy.random import rand
def inversePower5(Bv,d,e,f,tol=1.0e-6):
n = len(d)
d,e,f = LUdecomp5(d,e,f)
x = rand(n) # Seed x with random numbers
xMag = math.sqrt(np.dot(x,x)) # Normalize {x}
x = x/xMag
for i in range(30): # Begin iterations
xOld = x.copy() # Save current {x}
x = Bv(xOld) # Compute [B]{x}
x = LUsolve5(d,e,f,x) # Solve [A]{z} = [B]{x}
xMag = math.sqrt(np.dot(x,x)) # Normalize {z}
x = x/xMag
if np.dot(xOld,x) < 0.0: # Detect change in sign of {x}
sign = -1.0
x = -x
else: sign = 1.0
if math.sqrt(np.dot(xOld - x,xOld - x)) < tol:
return sign/xMag,x
print('Inverse power method did not converge')
The propped cantilever beam carries a compressive axial load P. The lateral displacement
u(x) of the beam can be shown to satisfy the differential equation
$$ u^{(4)}+\frac{P}{EI}{u}''=0 $$
where EI is the bending rigidity. The boundary conditions are
u(0) = u''(0) = 0 u(L) = u'(L) = 0
(1) Show that buckling analysis of the beam results in a matrix eigenvalue problem if
the derivatives are approximated by finite differences. (2)Write a program that computes
the smallest buckling load of the beam, making full use of banded matrices.
Run the program with 100 interior nodes (n = 100)

Solution:

## example9_6
import numpy as np
from inversePower5 import *
def Bv(v): # Compute {z} = [B]{v}
n = len(v)
z = np.zeros(n)
z[0] = 2.0*v[0] - v[1]
for i in range(1,n-1):
z[i] = -v[i-1] + 2.0*v[i] - v[i+1]
z[n-1] = -v[n-2] + 2.0*v[n-1]
return z
n = 100 # Number of interior nodes
d = np.ones(n)*6.0 # Specify diagonals of [A] = [f\e\d\e\f]
d[0] = 5.0
d[n-1] = 7.0
e = np.ones(n-1)*(-4.0)
f = np.ones(n-2)*1.0
lam,x = inversePower5(Bv,d,e,f)
print("PLˆ2/EI =",lam*(n+1)**2)
input("\nPress return to exit")
## module householder
''' d,c = householder(a).
Householder similarity transformation of matrix [a] to
tridiagonal form].
p = computeP(a).
Computes the acccumulated transformation matrix [p]
after calling householder(a).
'''
import numpy as np
import math
def householder(a):
n = len(a)
for k in range(n-2):
u = a[k+1:n,k]
uMag = math.sqrt(np.dot(u,u))
if u[0] < 0.0: uMag = -uMag
u[0] = u[0] + uMag
h = np.dot(u,u)/2.0
v = np.dot(a[k+1:n,k+1:n],u)/h
g = np.dot(u,v)/(2.0*h)
v = v - g*u
a[k+1:n,k+1:n] = a[k+1:n,k+1:n] - np.outer(v,u) \
- np.outer(u,v)
a[k,k+1] = -uMag
return np.diagonal(a),np.diagonal(a,1)
def computeP(a):
n = len(a)
p = np.identity(n)*1.0
for k in range(n-2):
u = a[k+1:n,k]
h = np.dot(u,u)/2.0
v = np.dot(p[1:n,k+1:n],u)/h
p[1:n,k+1:n] = p[1:n,k+1:n] - np.outer(v,u)
return p
## example9_8
# Use the function householder to tridiagonalize the matrix
# A =[7 2 3 −1
# 2 8 5 1
# 3 5 12 9
# −1 1 9 7]
# also determine the transformation matrix P.
import numpy as np
from householder import *
a = np.array([[ 7.0, 2.0, 3.0, -1.0], \
[ 2.0, 8.0, 5.0, 1.0], \
[ 3.0, 5.0, 12.0, 9.0], \
[-1.0, 1.0, 9.0, 7.0]])
d,c = householder(a)
print("Principal diagonal {d}:\n", d)
print("\nSubdiagonal {c}:\n",c)
print("\nTransformation matrix [P]:")
print(computeP(a))
input("\nPress return to exit")
## module sturmSeq
''' p = sturmSeq(c,d,lam).
Returns the Sturm sequence {p[0],p[1],...,p[n]}
associated with the characteristic polynomial
|[A] - lam[I]| = 0, where [A] is a n x n
tridiagonal matrix.
numLam = numLambdas(p).
Returns the number of eigenvalues of a tridiagonal
matrix that are smaller than 'lam'.
Uses the Sturm sequence {p} obtained from 'sturmSeq'.
'''
import numpy as np
def sturmSeq(d,c,lam):
n = len(d) + 1
p = np.ones(n)
p[1] = d[0] - lam
for i in range(2,n):
p[i] = (d[i-1] - lam)*p[i-1] - (c[i-2]**2)*p[i-2]
return p
def numLambdas(p):
n = len(p)
signOld = 1
numLam = 0
for i in range(1,n):
if p[i] > 0.0: sign = 1
elif p[i] < 0.0: sign = -1
else: sign = -signOld
if sign*signOld < 0: numLam = numLam + 1
signOld = sign
return numLam
## module gerschgorin
''' lamMin,lamMax = gerschgorin(d,c).
Applies Gerschgorin's theorem to find the global bounds on
the eigenvalues of a symmetric tridiagonal matrix.
'''
def gerschgorin(d,c):
n = len(d)
lamMin = d[0] - abs(c[0])
lamMax = d[0] + abs(c[0])
for i in range(1,n-1):
lam = d[i] - abs(c[i]) - abs(c[i-1])
if lam < lamMin: lamMin = lam
lam = d[i] + abs(c[i]) + abs(c[i-1])
if lam > lamMax: lamMax = lam
lam = d[n-1] - abs(c[n-2])
if lam < lamMin: lamMin = lam
lam = d[n-1] + abs(c[n-2])
if lam > lamMax: lamMax = lam
return lamMin,lamMax
## module lamRange
''' r = lamRange(d,c,N).
Returns the sequence {r[0],r[1],...,r[N]} that
separates the N lowest eigenvalues of the tridiagonal
matrix; that is, r[i] < lam[i] < r[i+1].
'''
import numpy as np
from sturmSeq import *
from gerschgorin import *
def lamRange(d,c,N):
lamMin,lamMax = gerschgorin(d,c)
r = np.ones(N+1)
r[0] = lamMin
# Search for eigenvalues in descending order
for k in range(N,0,-1):
# First bisection of interval(lamMin,lamMax)
lam = (lamMax + lamMin)/2.0
h = (lamMax - lamMin)/2.0
for i in range(1000):
# Find number of eigenvalues less than lam
p = sturmSeq(d,c,lam)
numLam = numLambdas(p)
# Bisect again & find the half containing lam
h = h/2.0
if numLam < k: lam = lam + h
elif numLam > k: lam = lam - h
else: break
# If eigenvalue located, change the upper limit
# of search and record it in [r]
lamMax = lam
r[k] = lam
return r
## module eigenvals3
''' lam = eigenvals3(d,c,N).
Returns the N smallest eigenvalues of a symmetric
tridiagonal matrix defined by its diagonals d and c.
'''
from lamRange import *
from ridder import *
from sturmSeq import sturmSeq
from numpy import zeros
def eigenvals3(d,c,N):
def f(x): # f(x) = |[A] - x[I]|
p = sturmSeq(d,c,x)
return p[len(p)-1]
lam = zeros(N)
r = lamRange(d,c,N) # Bracket eigenvalues
for i in range(N): # Solve by Brent's method
lam[i] = ridder(f,r[i],r[i+1])
return lam
## example9_12
# Use eigenvals3 to determine the three smallest eigenvalues of the 100 × 100 matrix:
# A =[2 −1 0 · · · 0
# −1 2 −1 · · · 0
# 0 −1 2 · · · 0
# . . . .
# . . . .
# . . . .
# 0 0 · · · −1 2]
import numpy as np
from eigenvals3 import *
N = 3
n = 100
d = np.ones(n)*2.0
c = np.ones(n-1)*(-1.0)
lambdas = eigenvals3(d,c,N)
print (lambdas)
input("\nPress return to exit")
## module inversePower3
''' lam,x = inversePower3(d,c,s,tol=1.0e-6)
Inverse power method applied to a symmetric tridiagonal
matrix. Returns the eigenvalue closest to s
and the corresponding eigenvector.
'''
from LUdecomp3 import *
import math
import numpy as np
from numpy.random import rand
def inversePower3(d,c,s,tol=1.0e-6):
n = len(d)
e = c.copy()
cc = c.copy()
dStar = d - s # Form [A*] = [A] - s[I]
LUdecomp3(cc,dStar,e) # Decompose [A*]
x = rand(n) # Seed x with random numbers
xMag = math.sqrt(np.dot(x,x)) # Normalize [x]
x = x/xMag
for i in range(30): # Begin iterations
xOld = x.copy() # Save current [x]
LUsolve3(cc,dStar,e,x) # Solve [A*][x] = [xOld]
xMag = math.sqrt(np.dot(x,x)) # Normalize [x]
x = x/xMag
if np.dot(xOld,x) < 0.0: # Detect change in sign of [x]
sign = -1.0
x = -x
else: sign = 1.0
if math.sqrt(np.dot(xOld - x,xOld - x)) < tol:
return s + sign/xMag,x
print('Inverse power method did not converge')
return
## example9_13
# Compute the 10th smallest eigenvalue of the matrix:
# A =[2 −1 0 · · · 0
# −1 2 −1 · · · 0
# 0 −1 2 · · · 0
# . . . .
# . . . .
# . . . .
# 0 0 · · · −1 2]
import numpy as np
from lamRange import *
from inversePower3 import *
N = 10
n = 100
d = np.ones(n)*2.0
c = np.ones(n-1)*(-1.0)
r = lamRange(d,c,N) # Bracket N smallest eigenvalues
s = (r[N-1] + r[N])/2.0 # Shift to midpoint of Nth bracket
lam,x = inversePower3(d,c,s) # Inverse power method
print("Eigenvalue No.",N," =",lam)
input("\nPress return to exit")
## example9_14
# Compute the three smallest eigenvalues and the corresponding eigenvectors of the
# matrix
# A = [11 2 3 1 4
# 2 9 3 5 2
# 3 3 15 4 3
# 1 5 4 12 4
# 4 2 3 4 17]
from householder import *
from eigenvals3 import *
from inversePower3 import *
import numpy as np
N = 3 # Number of eigenvalues requested
a = np.array([[ 11.0, 2.0, 3.0, 1.0, 4.0], \
[ 2.0, 9.0, 3.0, 5.0, 2.0], \
[ 3.0, 3.0, 15.0, 4.0, 3.0], \
[ 1.0, 5.0, 4.0, 12.0, 4.0], \
[ 4.0, 2.0, 3.0, 4.0, 17.0]])
xx = np.zeros((len(a),N))
d,c = householder(a) # Tridiagonalize [A]
p = computeP(a) # Compute transformation matrix
lambdas = eigenvals3(d,c,N) # Compute eigenvalues
for i in range(N):
s = lambdas[i]*1.0000001 # Shift very close to eigenvalue
lam,x = inversePower3(d,c,s) # Compute eigenvector [x]
xx[:,i] = x # Place [x] in array [xx]
xx = np.dot(p,xx) # Recover eigenvectors of [A]
print("Eigenvalues:\n",lambdas)
print("\nEigenvectors:\n",xx)
input("Press return to exit")