给出优化问题:
min
y
,
u
J
(
y
,
u
)
=
1
2
∫
Ω
(
y
−
y
Ω
)
2
+
u
2
d
x
d
y
+
∫
∂
Ω
e
Γ
y
d
s
.
s.t.
−
Δ
y
+
y
=
u
+
e
Ω
,
x
∈
Ω
,
∂
y
∂
n
=
0
,
x
∈
∂
Ω
.
0
≤
u
≤
1.
\begin{array}{cl} \min _{y,u} & J(y,u) = \frac{1}{2}\int_{\Omega} (y - y_{\Omega})^2 + u^2 dx dy + \int_{\partial \Omega} e_{\Gamma}y ds. \\ \text { s.t. } & - \Delta y + y = u + e_{\Omega},\mathbf{x} \in \Omega, \\{ }& \frac{\partial y}{\partial n} = 0, \mathbf{x} \in \partial \Omega.\\{ }& 0 \leq u \leq 1. \end{array}
miny,u s.t. J(y,u)=21∫Ω(y−yΩ)2+u2dxdy+∫∂ΩeΓyds.−Δy+y=u+eΩ,x∈Ω,∂n∂y=0,x∈∂Ω.0≤u≤1.
给出精确解:
r
2
=
(
x
1
−
0.5
)
2
+
(
x
2
−
0.5
)
2
,
Ω
=
[
0
,
1
]
2
r^2 = (x_1 - 0.5)^2 + (x_2 - 0.5)^2,\Omega = [0,1]^2
r2=(x1−0.5)2+(x2−0.5)2,Ω=[0,1]2
e
Ω
(
x
1
,
x
2
)
=
1
−
min
{
1
,
max
{
0
,
12
r
2
−
1
/
3
}
}
,
e
Γ
(
x
1
,
x
2
)
=
−
12
e_{\Omega}(x_1,x_2) = 1 - \min\{1,\max\{0,12r^2 - 1/3 \}\},e_{\Gamma}(x_1,x_2) = -12
eΩ(x1,x2)=1−min{1,max{0,12r2−1/3}},eΓ(x1,x2)=−12.
y
Ω
=
12
r
2
−
142
3
y_{\Omega} = 12r^2 - \frac{142}{3}
yΩ=12r2−3142.
y
∗
=
1
,
u
∗
=
min
{
1
,
max
{
0
,
12
r
2
−
1
/
3
}
}
y^{*} = 1,u^{*} = \min\{1,\max\{0,12r^2 - 1/3 \}\}
y∗=1,u∗=min{1,max{0,12r2−1/3}}.
将区域进行均匀剖分,得到
M
×
N
M\times N
M×N个网格点,下面将利用传统方法来求解
y
,
u
y,u
y,u在这些网格点上的取值.
min
y
,
u
J
(
y
,
u
)
=
(
y
−
y
Ω
)
T
B
(
y
−
y
Ω
)
+
u
T
B
u
+
c
T
y
.
s.t.
A
y
−
e
Ω
=
u
,
0
≤
u
≤
1.
\begin{array}{cl} \min _{y,u} & J(y,u) = (y-y_{\Omega})^TB(y - y_{\Omega}) + u^TBu+ c^{T}y. \\ \text { s.t. } & Ay - e_{\Omega} = u, \\{ }& 0 \leq u \leq 1. \end{array}
miny,u s.t. J(y,u)=(y−yΩ)TB(y−yΩ)+uTBu+cTy.Ay−eΩ=u,0≤u≤1.
其中A是由五点差分法得到的,至于这个B特别需要注意,这个B并不是简单的
0.5
∗
I
0.5*I
0.5∗I
这里的网格点选取如上,一开始本人选取的是:
J
(
u
,
y
)
=
1
2
∑
i
=
0
M
−
1
∑
j
=
0
N
−
1
(
(
y
i
,
j
−
y
Ω
i
,
j
)
2
+
u
i
,
j
2
)
+
C
T
y
J(u,y)=\frac{1}{2}\sum_{i=0}^{M-1}\sum_{j=0}^{N-1}((y_{i,j}-y_{\Omega_{i,j}})^2 + u_{i,j}^2) + C^Ty
J(u,y)=21∑i=0M−1∑j=0N−1((yi,j−yΩi,j)2+ui,j2)+CTy
其中
C
C
C满足在边界点位置为
12
/
d
x
12/dx
12/dx或者
12
/
d
y
12/dy
12/dy,然而这样做,前面这个部分并不是在整个区域的积分,就像上面这个图像一样,这个计算的其实多了一部分积分,因此这里我们需要减去一部分多余的,具体的处理刻意参考本人的代码。
另外为了更好计算,本人把约束做了一个改动,两边同时乘了一个
d
x
∗
d
y
=
s
dx*dy=s
dx∗dy=s,最终得到:
min
y
,
u
J
(
y
,
u
)
=
(
y
−
y
Ω
)
T
B
(
y
−
y
Ω
)
+
u
T
B
u
+
c
T
y
.
s.t.
A
y
−
e
Ω
s
=
u
s
,
0
≤
u
≤
1.
\begin{array}{cl} \min _{y,u} & J(y,u) = (y-y_{\Omega})^TB(y - y_{\Omega}) + u^TBu+ c^{T}y. \\ \text { s.t. } & Ay - e_{\Omega}s = us, \\{ }& 0 \leq u \leq 1. \end{array}
miny,u s.t. J(y,u)=(y−yΩ)TB(y−yΩ)+uTBu+cTy.Ay−eΩs=us,0≤u≤1.
这个问题使用ADMM来求解就非常简单了,
下面展示一下ADMM求解的结果:
import matplotlib.pyplot as plt import numpy as np import time from matplotlib import cm def Cholesky(matrix): w = matrix.shape[0] G = np.zeros((w,w))#实际上只用一半的空间就可以完成矩阵分解 for i in range(w): G[i,i] = (matrix[i,i] - np.dot(G[i,:i],G[i,:i].T))**0.5 for j in range(i+1,w): G[j,i] = (matrix[j,i] - np.dot(G[j,:i],G[i,:i].T))/G[i,i] return G def L_Gauss(A,b): x = b.copy() x[0,0] = b[0,0]/A[0,0] for i in range(1,x.shape[0]): x[i,0] = (b[i,0] - (A[i,:i]@x[:i,0]).sum())/A[i,i] return x def U_Gauss(A,b): x = b.copy() n = x.shape[0] x[n - 1,0] = b[n - 1,0]/A[n - 1,n - 1] for i in range(n - 2,-1,-1): x[i,0] = (b[i,0] - (A[i,i + 1:]*x[i + 1:,0]).sum())/A[i,i] return x def error(u_pred, u_acc): u_pred = u_pred u_acc = u_acc return (((u_pred-u_acc)**2).mean()) ** (0.5) class FENET(): def __init__(self,bounds,nx): self.dim = 2 self.bounds = bounds self.hx = [(bounds[0,1] - bounds[0,0])/nx[0], (bounds[1,1] - bounds[1,0])/nx[1]] self.nx = [nx[0] + 1,nx[1] + 1] #----------- self.size = self.nx[0]*self.nx[1] self.X = np.zeros([self.size,2]) for i in range(self.nx[0]): for j in range(self.nx[1]): self.X[i*self.nx[1] + j,0] = bounds[0,0] + i*self.hx[0] self.X[i*self.nx[1] + j,1] = bounds[1,0] + j*self.hx[1] self.y_Omega = np.zeros([self.size,1]) self.yy = np.ones([self.size,1]) self.pp = np.zeros([self.size,1]) self.uu = np.zeros([self.size,1]) for i in range(self.size): self.pp[i,0] = -12*((self.X[i,0] - 0.5)**2 + (self.X[i,1] - 0.5)**2) + 1/3 self.y_Omega[i,0] = 12*((self.X[i,0] - 0.5)**2 + (self.X[i,1] - 0.5)**2) - 142/3 self.uu[i,0] = min(1,max(0,-self.pp[i,0])) def matrix(self): A = np.zeros([self.nx[0]*self.nx[1],self.nx[0]*self.nx[1]]) dx = self.hx[0];dy = self.hx[1] for i in range(self.nx[0]): for j in range(self.nx[1]): A[i*self.nx[1]+j,i*self.nx[1]+j] = 2*(dx/dy + dy/dx) + dx*dy if i == 0: A[i*self.nx[1] + j,(i + 1)*self.nx[1] + j] = -2*dy/dx if j == 0: A[i*self.nx[1] + j,i*self.nx[1] + j + 1] = -2*dx/dy elif j == self.nx[1] - 1: A[i*self.nx[1] + j,i*self.nx[1] + j - 1] = -2*dx/dy else: A[i*self.nx[1] + j,i*self.nx[1] + j + 1] = -dx/dy A[i*self.nx[1] + j,i*self.nx[1] + j - 1] = -dx/dy elif i == self.nx[0] - 1: A[i*self.nx[1] + j,(i - 1)*self.nx[1] + j] = -2*dy/dx if j == 0: A[i*self.nx[1] + j,i*self.nx[1] + j + 1] = -2*dx/dy elif j == self.nx[1] - 1: A[i*self.nx[1] + j,i*self.nx[1] + j - 1] = -2*dx/dy else: A[i*self.nx[1] + j,i*self.nx[1] + j + 1] = -dx/dy A[i*self.nx[1] + j,i*self.nx[1] + j - 1] = -dx/dy else: A[i*self.nx[1] + j,(i - 1)*self.nx[1] + j] = -dy/dx A[i*self.nx[1] + j,(i + 1)*self.nx[1] + j] = -dy/dx if j == 0: A[i*self.nx[1] + j,i*self.nx[1] + j + 1] = -2*dx/dy elif j == self.nx[1] - 1: A[i*self.nx[1] + j,i*self.nx[1] + j - 1] = -2*dx/dy else: A[i*self.nx[1] + j,i*self.nx[1] + j + 1] = -dx/dy A[i*self.nx[1] + j,i*self.nx[1] + j - 1] = -dx/dy return A def mat(self): B = 0.5*np.eye(self.nx[0]*self.nx[1],self.nx[0]*self.nx[1]) for i in range(1,self.nx[0] - 1): B[i*self.nx[1],i*self.nx[1]] = 0.5 - 0.25 B[i*self.nx[1] + self.nx[1] - 1,i*self.nx[1] + self.nx[1] - 1] = 0.5 - 0.25 for j in range(1,self.nx[1] - 1): B[j,j] = 0.5 - 0.25 B[(self.nx[0] - 1)*self.nx[1] + j,(self.nx[0] - 1)*self.nx[1] + j] = 0.5 - 0.25 B[0,0] = 0.5 - 3/8 B[self.nx[1] - 1,self.nx[1] - 1] = 0.5 - 3/8 B[(self.nx[0] - 1)*self.nx[1],(self.nx[0] - 1)*self.nx[1]] = 0.5 - 3/8 B[-1,-1] = 0.5 - 3/8 return B def E_Omega(self): r = (self.X[:,0] - 0.5)**2 + (self.X[:,1] - 0.5)**2 tmp = np.maximum(12*r - 1/3,0) out = 1 - np.minimum(tmp,1) return out.reshape(-1,1) def right(self): c1 = np.zeros([self.size,1]) c2 = np.zeros([self.size,1]) for i in range(self.nx[0]): c1[i*self.nx[1],0] = -12*(self.nx[1] - 1) c1[i*self.nx[1] + self.nx[1] - 1,0] = -12*(self.nx[1] - 1) for j in range(1,self.nx[1] - 1): c2[j,0] = -12*(self.nx[0] - 1) c2[(self.nx[0] - 1)*self.nx[1] + j,0] = -12*(self.nx[0] - 1) return c1 + c2 class OPTS(): def __init__(self,fenet): self.epoch = 300#最大迭代次数 self.tau = 1e-4#惩罚参数的倒数的两倍 self.eps = 1e-15#停机条件 self.yy = fenet.yy self.uu = fenet.uu self.s = fenet.hx[0]*fenet.hx[1] self.A = fenet.matrix() self.m = self.A.shape[0] self.n = self.A.shape[1] self.B = fenet.mat() #self.B = 0.5*np.eye(self.m,self.n) self.e_Omega = fenet.E_Omega() self.c = fenet.right() self.y_Omega = fenet.y_Omega self.Ly = Cholesky(self.B + 0.5*self.A.T@self.A/self.tau)#储存cholesky分解矩阵 self.Lu = Cholesky(self.B + np.identity(self.m)*0.5*self.s**2/self.tau) self.y0 = np.random.randn(self.n,1)#储存初始迭代向量 #self.y0 = np.ones([self.n,1]) self.time = 0#储存运行时间 self.value = 0#储存最优函数值 self.y_error = 0#储存最终的误差 self.u_error = 0#储存最终的误差 self.epoch_end = 0#储存最终迭代次数 def value(y,y_Omega,c,u,B): area = 1/y.shape[0] tmp = (y - y_Omega).T@B@(y - y_Omega) + u.T@B@u return (tmp[0,0] + (c*y).sum())*area def y_iter(tau,A,B,Ly,y_Omega,u,c,alpha,e_Omega,s): b = B@y_Omega - 0.5*c + 0.5*A.T@(e_Omega*s + u*s - alpha*tau)/tau x1 = L_Gauss(Ly,b) return U_Gauss(Ly.T,x1) def u_iter(tau,alpha,A,B,Lu,y,e_Omega,s): b = s*alpha + (A@y - e_Omega)*s**2/tau x1 = L_Gauss(Lu,0.5*b) z = U_Gauss(Lu.T,x1) z[z > 1] = 1 z[z < 0] = 0 return z def ADMM(opts): tic = time.time() y_old = opts.y0.copy() u_old = opts.y0.copy() alpha_old = opts.y0.copy() for i in range(opts.epoch): y_new = y_iter(opts.tau,opts.A,opts.B,opts.Ly,opts.y_Omega,u_old,opts.c,alpha_old,opts.e_Omega,opts.s) u_new = u_iter(opts.tau,alpha_old,opts.A,opts.B,opts.Lu,y_new,opts.e_Omega,opts.s) alpha_new = alpha_old + (opts.A@y_new - u_new*opts.s - opts.e_Omega*opts.s)/opts.tau rho = np.linalg.norm(y_new - y_old) + np.linalg.norm(u_new - u_old) if (i + 1)%50 == 0: #err = opts.A@y_new - u_new*opts.s - opts.e_Omega*opts.s #print(np.linalg.norm(err)) val = value(y_new,opts.y_Omega,opts.c,u_new,opts.B) print('epcoh:%d,norm:%.3e,y_error:%.3e,u_error:%.3e,val:%.4e' %(i + 1,rho,error(y_new,opts.yy),error(u_new,opts.uu),val)) if rho < opts.eps: break else: y_old = y_new u_old = u_new alpha_old = alpha_new val = value(y_new,opts.y_Omega,opts.c,u_new,opts.B) ela = time.time() - tic opts.value = val opts.time = ela opts.y_error = error(y_new,opts.yy) opts.u_error = error(u_new,opts.uu) opts.epoch_end = i + 1 return y_new,u_new,opts bounds = np.array([[0,1],[0,1.0]]) nx_te = [20,20]#训练集剖分 fenet = FENET(bounds,nx_te) opts = OPTS(fenet) print(value(opts.yy,opts.y_Omega,opts.c,opts.uu,opts.B)) err = fenet.matrix()@opts.yy - opts.uu*opts.s - opts.e_Omega*opts.s print(np.linalg.norm(err)) y_new,u_new,out = ADMM(opts) print(np.linalg.norm(fenet.matrix()@y_new - u_new*opts.s - opts.e_Omega*opts.s)) #print(y_new) print('the epoch:%d,y_error:%.3e,u_error:%.3e,the value:%.4e,the time:%.2f'\ %(out.epoch_end,out.y_error,out.u_error,out.value,out.time)) fig, ax = plt.subplots(2,3,figsize=(12,7)) ax = ax.reshape((2,3)) for i in range(2): for j in range(3): ax[i,j].axis('equal') ax[i,j].set_xlim([0,1]) ax[i,j].set_ylim([0,1]) ax[i,j].axis('off') norm1 = cm.colors.Normalize(vmin=0, vmax=1) norm2 = cm.colors.Normalize(vmin=0.9, vmax=1.1) num_line = 10 x = np.linspace(bounds[0,0],bounds[0,1],nx_te[0] + 1) y = np.linspace(bounds[1,0],bounds[1,1],nx_te[1] + 1) X,Y = np.meshgrid(x,y) u = u_new.reshape(nx_te[0] + 1,nx_te[1] + 1) y = y_new.reshape(nx_te[0] + 1,nx_te[1] + 1) u_e = opts.uu.reshape(nx_te[0] + 1,nx_te[1] + 1) y_e = opts.yy.reshape(nx_te[0] + 1,nx_te[1] + 1) ax00 = ax[0,0].contourf(X, Y, u, num_line, alpha=1, cmap='rainbow') ax[0,0].contour(ax00, linewidths=0.6, colors='black') ax01 = ax[0,1].contourf(X, Y, u_e, num_line, alpha=1, cmap='rainbow') ax[0,1].contour(ax01, linewidths=0.6, colors='black') ax02 = ax[0,2].contourf(X, Y, u-u_e, num_line, alpha=1, cmap='rainbow') ax[0,2].contour(ax02, linewidths=0.6, colors='black') fig.colorbar(ax00,ax=ax[0,0]) fig.colorbar(ax01,ax=ax[0,1]) fig.colorbar(ax02,ax=ax[0,2]) ax[0,0].set_title('numerical: u') ax[0,1].set_title('exact: u') ax[0,2].set_title('difference: u') ax10 = ax[1,0].contourf(X, Y, y, num_line, alpha=1, cmap='rainbow') ax[1,0].contour(ax10, linewidths=0.6, colors='black') ax11 = ax[1,1].contourf(X, Y, y_e, num_line, alpha=1, cmap='rainbow') ax[1,1].contour(ax11, linewidths=0.6, colors='black') ax12 = ax[1,2].contourf(X, Y, y-y_e, num_line, alpha=1, cmap='rainbow') ax[1,2].contour(ax12, linewidths=0.6, colors='black') fig.colorbar(ax10,ax=ax[1,0]) fig.colorbar(ax11,ax=ax[1,1]) fig.colorbar(ax12,ax=ax[1,2]) ax[1,0].set_title('numerical: y') ax[1,1].set_title('exact: y') ax[1,2].set_title('difference: y') fig.tight_layout() # 防止子图重叠 plt.show() fig.savefig('diff200-6.6.png',dpi=300)
使用神经网络来处理,这个本人就介绍一下Galerkin的损失函数,代码不打算放出。
L
o
b
j
=
1
N
∑
i
=
1
N
J
(
u
^
i
,
y
^
i
)
L
p
d
e
=
1
N
∑
k
=
1
K
∑
i
=
1
N
(
(
∇
y
^
i
∗
∇
v
i
k
)
−
(
u
^
i
+
e
Ω
,
i
−
y
^
i
)
v
i
k
)
2
+
1
M
∑
j
=
1
M
(
∂
ν
y
^
j
)
2
L
i
u
=
1
N
∑
i
=
1
N
(
Relu
(
−
u
^
i
)
2
+
Relu
(
u
^
i
−
1
)
2
)
L
=
L
o
b
j
+
α
L
p
d
e
+
β
L
i
u
\begin{aligned} L_{o b j} &=\frac{1}{N} \sum_{i=1}^{N} J\left(\hat{u}_{i}, \hat{y}_{i}\right) \\ L_{p d e} &=\frac{1}{N} \sum_{k = 1}^{K}\sum_{i=1}^{N}\left((\nabla \hat{y}_{i}*\nabla v_{i}^k)-(\hat{u}_{i}+e_{\Omega, i} - \hat{y}_{i})v_{i}^k \right)^{2}+\\ & \frac{1}{M} \sum_{j=1}^{M}\left(\partial_{\nu} \hat{y}_{j})^2\right.\\ L_{i u} &=\frac{1}{N} \sum_{i=1}^{N}\left(\operatorname{Relu}\left(-\hat{u}_{i}\right)^{2}+\operatorname{Relu}\left(\hat{u}_{i}-1\right)^{2}\right) \\ L &=L_{o b j}+\alpha L_{p d e}+\beta L_{i u} \end{aligned}
LobjLpdeLiuL=N1i=1∑NJ(u^i,y^i)=N1k=1∑Ki=1∑N((∇y^i∗∇vik)−(u^i+eΩ,i−y^i)vik)2+M1j=1∑M(∂νy^j)2=N1i=1∑N(Relu(−u^i)2+Relu(u^i−1)2)=Lobj+αLpde+βLiu
下面是Galerkin的效果,大家自己写代码吧。