## 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")