2.2 Eigenvalue problems¶
We solve the generalized eigenvalue problem
where \(A\) comes from \(\int \nabla u \nabla v\), and \(M\) from \(\int u v\), on the space \(H_0^1\)
In [1]:
import netgen.gui
%gui tk
from netgen.geom2d import unit_square
from ngsolve import *
from math import pi
import scipy.linalg
from scipy import random
mesh = Mesh(unit_square.GenerateMesh(maxh=0.1))
We setup a stiffness matrix \(A\) and a mass matrix \(M\), and declare a preconditioner for \(A\):
In [2]:
fes = H1(mesh, order=4, dirichlet=[1,2,3,4])
u = fes.TrialFunction()
v = fes.TestFunction()
a = BilinearForm(fes)
a += SymbolicBFI(grad(u)*grad(v))
pre = Preconditioner(a, "multigrid")
m = BilinearForm(fes)
m += SymbolicBFI(u*v)
a.Assemble()
m.Assemble()
u = GridFunction(fes)
The inverse iteration is
where the Rayleigh quotient
converges to the smallest eigenvalue, with rate of convergence \(\lambda_1 / \lambda_2\).
The preconditioned inverse iteration (PINVIT), see [Knyazef+Neymeyr], replaces \(A^{-1}\) by an approximate inverse \(C^{-1}\):
The optimal step-size \(\alpha\) is found by minimizing the Rayleigh-quotient on a two-dimensional space:
This minimization problem can be solved by a small eigenvalue problem
Then, the new iterate is
where \(y\) is the eigenvector corresponding to the smaller eigenvalue.
Implementation in NGSolve¶
Create some help vectors:
In [3]:
r = u.vec.CreateVector()
w = u.vec.CreateVector()
Mu = u.vec.CreateVector()
Au = u.vec.CreateVector()
Mw = u.vec.CreateVector()
Aw = u.vec.CreateVector()
random initial condition, zero on the boundary
In [4]:
r.FV().NumPy()[:] = random.rand(fes.ndof)
u.vec.data = Projector(fes.FreeDofs(), True) * r
# from random import random
# freedofs = fes.FreeDofs()
# for i in range(len(u.vec)):
# u.vec[i] = random() if freedofs[i] else 0
In [5]:
for i in range(20):
Au.data = a.mat * u.vec
Mu.data = m.mat * u.vec
auu = InnerProduct(Au, u.vec)
muu = InnerProduct(Mu, u.vec)
# Rayleigh quotiont
lam = auu/muu
print (lam / (pi**2))
# residual
r.data = Au - lam * Mu
w.data = pre.mat * r.data
w.data = 1/Norm(w) * w
Aw.data = a.mat * w
Mw.data = m.mat * w
# setup and solve 2x2 small eigenvalue problem
asmall = Matrix(2,2)
asmall[0,0] = auu
asmall[0,1] = asmall[1,0] = InnerProduct(Au, w)
asmall[1,1] = InnerProduct(Aw, w)
msmall = Matrix(2,2)
msmall[0,0] = muu
msmall[0,1] = msmall[1,0] = InnerProduct(Mu, w)
msmall[1,1] = InnerProduct(Mw, w)
# print ("asmall =", asmall, ", msmall = ", msmall)
eval,evec = scipy.linalg.eigh(a=asmall, b=msmall)
# print (eval, evec)
u.vec.data = float(evec[0,0]) * u.vec + float(evec[1,0]) * w
Draw (u)
26.310136660400275
2.0782284240636826
2.0016788490372455
2.0002002507016474
2.0000413285947802
2.0000105080928825
2.0000027530788227
2.0000007313313577
2.0000001959000486
2.0000000528126267
2.0000000143203
2.000000003918371
2.0000000010972063
2.000000000329983
2.0000000001208567
2.0000000000637526
2.000000000048131
2.000000000043857
2.0000000000426827
2.0000000000423617
Simultaneous iteration for several eigenvalues¶
Declare GridFunction with multiple components to store several eigenvectors.
In [6]:
num = 5
u = GridFunction(fes, multidim=num)
Create list of help vectors, random initialization of u vectors:
In [7]:
r = u.vec.CreateVector()
Av = u.vec.CreateVector()
Mv = u.vec.CreateVector()
vecs = []
for i in range(2*num):
vecs.append (u.vec.CreateVector())
for v in u.vecs:
r.FV().NumPy()[:] = random.rand(fes.ndof)
v.data = Projector(fes.FreeDofs(), True) * r
Compute num residuals, and solve small eigenvalue problem on \(2 num\)-dimensional space
In [8]:
asmall = Matrix(2*num, 2*num)
msmall = Matrix(2*num, 2*num)
lams = num * [1]
for i in range(20):
for j in range(num):
vecs[j].data = u.vecs[j]
r.data = a.mat * vecs[j] - lams[j] * m.mat * vecs[j]
vecs[num+j].data = pre.mat * r
for j in range(2*num):
Av.data = a.mat * vecs[j]
Mv.data = m.mat * vecs[j]
for k in range(2*num):
asmall[j,k] = InnerProduct(Av, vecs[k])
msmall[j,k] = InnerProduct(Mv, vecs[k])
ev,evec = scipy.linalg.eigh(a=asmall, b=msmall)
lams[:] = ev[0:num]
print (i, ":", [lam/pi**2 for lam in lams])
for j in range(num):
u.vecs[j][:] = 0.0
for k in range(2*num):
u.vecs[j].data += float(evec[k,j]) * vecs[k]
Draw (u)
0 : [11.6130455768801, 61.066110947635224, 82.49711365113266, 90.41384577806575, 96.98583176269047]
1 : [2.037296014502914, 5.994129376899316, 9.212270101228079, 16.752145334653957, 19.356324864358374]
2 : [2.0014100615066996, 5.092284538737162, 5.238911096053245, 10.123988712254931, 10.879020322598242]
3 : [2.0002554163633968, 5.012268562196962, 5.016722138616416, 9.213915664378316, 10.256909163977726]
4 : [2.000057169527135, 5.001025982274766, 5.0025582296087965, 8.719831456835285, 10.0918178567872]
5 : [2.000013593722611, 5.000120805946273, 5.000384594365776, 8.377992268553804, 10.035379830359405]
6 : [2.000003314919647, 5.000020247177596, 5.000055876284345, 8.184287429684003, 10.01410422992045]
7 : [2.000000821205276, 5.000004016939583, 5.00000849353781, 8.08609184849783, 10.005671478126507]
8 : [2.0000002058391035, 5.0000008179428255, 5.000001478437589, 8.039447631058222, 10.002288011579898]
9 : [2.0000000520461136, 5.000000168593007, 5.000000304198153, 8.017928866313158, 10.000924591937792]
10 : [2.000000013279318, 5.000000039218533, 5.000000071766628, 8.008121745129989, 10.000374075767763]
11 : [2.0000000034302508, 5.000000012511654, 5.000000020495333, 8.003675600991937, 10.000151525029434]
12 : [2.0000000009138295, 5.000000006632997, 5.0000000086298915, 8.00166315351873, 10.00006147486362]
13 : [2.000000000267371, 5.000000005221903, 5.000000005864453, 8.00075273753271, 10.000025013254238]
14 : [2.0000000001006097, 5.000000004822398, 5.000000005260717, 8.00034080330491, 10.000010241080249]
15 : [2.000000000057419, 5.00000000470997, 5.000000005129163, 8.000154364958556, 10.000004253440505]
16 : [2.0000000000461995, 5.000000004681857, 5.000000005098049, 8.00006995469134, 10.000001825435998]
17 : [2.000000000043275, 5.000000004674421, 5.000000005090545, 8.000031724949128, 10.000000840538995]
18 : [2.0000000000425104, 5.000000004672643, 5.000000005088353, 8.000014405452335, 10.000000440900093]
19 : [2.00000000004231, 5.000000004672329, 5.000000005087833, 8.000006556850161, 10.000000278698185]
The multidim-component select in the Visualization dialog box allows to switch between the components of the multidim-GridFunction.
In [ ]: