• Multigrid Method


    • 算法特征:
      ①. pre-smoothing提取低频残差; ②. 向下插值计算残差补偿; ③.向上插值填充残差补偿; ④. post-smoothing降低整体残差
    • 算法推导:
      Part Ⅰ: 算法原理
      考虑一般线性系统:
      egin{equation}
      Ax = b
      label{eq_1}
      end{equation}
      给定某初始值$x^{0}$, 残差为:
      egin{equation}
      r^{0} = b - Ax^{0}
      label{eq_2}
      end{equation}
      以该残差为基础, 计算残差补偿:
      egin{equation}
      delta x^{0} = A^{-1}r^{0}
      label{eq_3}
      end{equation}
      填充残差补偿, 式( ef{eq_1})解表示如下:
      egin{equation}
      x = x^{0} + delta x^{0}
      label{eq_4}
      end{equation}
      Part Ⅱ: 误差(即残差补偿)特征
      现以Jacobi迭代为例, 分解系数矩阵$A$, 式( ef{eq_1})转换为
      egin{equation}
      (L + D + U)x = b
      label{eq_5}
      end{equation}
      其中, $L$、$D$、$U$分别为系数矩阵$A$的下三角矩阵、对角矩阵及上三角矩阵. Jacobi迭代公式表示如下:
      egin{equation}
      x^{k+1} = Jx^k + D^{-1}b
      label{eq_6}
      end{equation}
      其中, $J = -D^{-1}(L + U)$, 代表Jacobi迭代矩阵. 定义第$k$步迭代误差:
      egin{equation}
      e^k = x^k - x
      label{eq_7}
      end{equation}
      根据式( ef{eq_6})可知, 迭代误差满足如下条件:
      egin{equation}
      e^{k+1} = Je^k
      label{eq_8}
      end{equation}
      结合上式, 对矩阵$J$进行特征分解, 则有
      egin{equation}
      e^k = VLambda^kV^{-1}e^0
      label{eq_9}
      end{equation}
      其中, 矩阵$V$和$Lambda$分别由矩阵$J$的本征矢和本征值组成. 显然, Jacobi迭代的收敛性由相应迭代矩阵的本征值决定. 若矩阵$J$所有本征值绝对值均小于1, 则Jacobi迭代收敛; 否则, 不收敛.
      现以有限差分法离散化1维Poisson's equation($Delta u = f$)为例, 采用Jacobi迭代求解该线性系统, 并分析误差成分:
      egin{equation}
      egin{split}
      A &= egin{bmatrix}-2 & 1 &  &  \  1 & ddots & ddots &  \ & ddots & ddots & 1  \ &  & 1 & -2 end{bmatrix} frac{1}{Delta x^2}   \
      D &= -2Ifrac{1}{Delta x^2}  \
      L + U &= egin{bmatrix} 0 & 1 &  & \ 1 & ddots & ddots & \ & ddots & ddots & 1 \ & & 1 & 0 end{bmatrix} frac{1}{Delta x^2}   \
      J &= egin{bmatrix} 0 & 1/2 &  & \ 1/2 & ddots & ddots & \ & ddots & ddots & 1/2 \ & & 1/2 & 0 end{bmatrix}
      end{split}
      label{eq_10}
      end{equation}
      矩阵$J$之本征矢与本征值分别为:
      egin{equation}
      egin{split}
      V_{ij} &= sinfrac{ijpi}{n+1}   \
      lambda_j &= cosfrac{jpi}{n+1}
      end{split}
      label{eq_11}
      end{equation}
      其中, $i$、$j$取$[1, n]$之间的整数. 显然, 矩阵$J$之本征值$lambda in (-1, 1)$, Jacobi迭代在该线性系统上收敛.
      观察本征矢取值, 当$j$逐渐增大时, 频率逐渐升高. 由此可将误差大致分为低频组分与高频组分. 低频组分, 误差分布平滑, 利于近似表达; 高频组分, 误差分布不平滑, 不利于近似表达.
      观察本征值取值, 当$j$逐渐增大时, 其变化趋势为: $1 ightarrow 0 ightarrow -1$. 低频部分, 误差收敛速度慢; 中频部分, 误差收敛速度快; 高频部分, 误差收敛速度慢.
      对于低频部分, 误差分布平滑, 可采用向下插值技术将离散点上的残差由精细网格过渡至粗糙网格(即降低$n + 1$的取值), 以提升误差组分的频率, 达到快速收敛之目的, 再由向上插值将离散点上的误差由粗糙网格过渡至精细网格, 获取一个可以接受的残差补偿; 对于中频部分, 误差收敛速度快, 无需特殊处理; 对于高频部分, 由于误差收敛速度慢且分布不平滑, 需要引入under-relaxation技术.
      Part Ⅲ: under-relaxation技术
      结合式( ef{eq_6}), 采用如下凸组合改写Jacobi迭代:
      egin{equation}
      x^{k+1} = (1 - alpha)x^k + alpha [-D^{-1}(L + U)x^k + D^{-1}b], quad ext{where }alpha in (0, 1)
      label{eq_12}
      end{equation}
      根据式( ef{eq_7})之定义, 误差方程转换如下:
      egin{equation}
      e^{k+1} = [(1 - alpha )I - alpha D^{-1}(L + U)]e^k
      label{eq_13}
      end{equation}
      此时, 迭代矩阵及其本征值分别为:
      egin{equation}
      egin{cases}
      J_alpha = (1 - alpha)I - alpha D^{-1}(L + U)   \
      lambda_alpha = 1 - alpha + alphalambda
      end{cases}
      label{eq_14}
      end{equation}
      根据式( ef{eq_11}), 本征值具体取值为:
      egin{equation}
      lambda_j^alpha = 1 - alpha + alpha cdot cosfrac{jpi}{n+1} in (1 - 2alpha, 1)
      label{eq_15}
      end{equation}
      若$alpha = 0.5$, 则本征值取值区间为$(0, 1)$. 此时已成功消除分布不平滑且收敛速度慢的误差组分, 使Jacobi迭代在多重网格上求解Poisson's equation时快速收敛.
    • 代码实现:
      Part Ⅰ:
      现以Poisson's equation为例进行算法实施, 原始图像、Laplace变换图像及边界条件图像分别如下方左、中、右三图所示:

      Part Ⅱ:
      Multigrid实现如下:
        1 # 多重网格法求解Poisson's equation
        2 
        3 import copy
        4 import numpy
        5 from PIL import Image
        6 from matplotlib import pyplot as plt
        7 from matplotlib import animation
        8 
        9 
       10 
       11 def get_F(picName):
       12     '''
       13     根据图片数据获取Poisson's equation等式右侧值
       14     '''
       15     img = Image.open(picName)
       16     arr = numpy.array(img).astype(float)
       17     img.close()
       18     
       19     dx = 1 / (arr.shape[1] - 1)
       20     
       21     F = numpy.zeros(arr.shape)
       22     for i in range(1, F.shape[0]-1):
       23         for j in range(1, F.shape[1]-1):
       24             F[i, j] = ((arr[i-1, j] + arr[i+1, j] + arr[i, j-1] + arr[i, j+1]) - 4 * arr[i, j]) / dx ** 2
       25     return F
       26 
       27 
       28     
       29 def get_B(picName):
       30     '''
       31     根据图片数据获取Poisson's equation边界条件, 非边界值填充0
       32     '''
       33     img = Image.open(picName)
       34     B = numpy.array(img).astype(float)
       35     img.close()
       36     B[1:-1, 1:-1] = 0
       37     return B
       38 
       39 
       40 
       41 def jacobi(U, F):
       42     '''
       43     Jacobi迭代法求解Poisson's equation: ΔU = F
       44     U: 迭代解初始值, 含边界条件
       45     F: Poisson's equation右侧值, 边界值无效
       46     横轴长度固定取1
       47     '''
       48     dx = 1 / (U.shape[1] - 1)
       49     UNew = copy.deepcopy(U)
       50     for i in range(1, UNew.shape[0]-1):
       51         for j in range(1, UNew.shape[1]-1):
       52             UNew[i, j] = (U[i-1, j] + U[i+1, j] + U[i, j-1] + U[i, j+1] - dx ** 2 * F[i, j]) / 4
       53     return UNew
       54 
       55 
       56     
       57 class Multigrid(object):
       58     '''
       59     多重网格法之实现
       60     '''
       61     def __init__(self, B, F, alpha=0.5):
       62         self.__B = B                # 边界条件, 矩阵非边界值无效
       63         self.__F = F                # Poisson's equation等式右侧
       64         self.__alpha = alpha        # under-relaxation凸组合因子
       65         
       66         self.__UList = list()       # 记录每帧迭代解
       67         
       68         
       69     def get_solu(self):
       70         return self.__UList
       71         
       72         
       73     def optimize(self, maxIter=3000, epsilon=1.e-1):
       74         '''
       75         maxIter: 最大迭代次数
       76         epsilon: 收敛判据, 迭代解趋于稳定则收敛
       77         '''
       78         self.__UList.clear()
       79         
       80         U0 = copy.deepcopy(self.__B)
       81         F0 = copy.deepcopy(self.__F)
       82         alpha = self.__alpha
       83         
       84         self.__UList.append(numpy.rint(U0).astype("int"))
       85         for idx in range(maxIter):
       86             R0 = self.__calc_residual(U0, F0)
       87             R0Norm = numpy.linalg.norm(R0)
       88             print("iterCnt: {:3d},   ResidualNorm: {}".format(idx, R0Norm))
       89             if R0Norm <= epsilon:
       90                 break
       91             
       92             U0 = self.__do_multigrid(U0, F0, alpha)
       93             self.__UList.append(numpy.rint(U0).astype("int"))
       94             
       95         return U0
       96         
       97     
       98     def __do_multigrid(self, U0, F0, alpha):
       99         # pre-smoothing
      100         Uf = self.__smoothing(U0, F0, alpha, 10)
      101         Rf = self.__calc_residual(Uf, F0)                     # 残差计算
      102         # downward interpolation - find grid to coarse grid
      103         Rc = self.__downward_interpolate(Rf)                  # 向下插值 - 残差计算
      104         deltaU0 = numpy.zeros(Rc.shape)
      105         if (Rc.shape[0] >= 5 and Rc.shape[1] >= 5):
      106             deltaUc = self.__do_multigrid(deltaU0, Rc, alpha)
      107         else:
      108             deltaUc = self.__smoothing(deltaU0, Rf, alpha, 10)
      109         # upward interpolation - coarse grid to fine grid
      110         deltaUf = self.__upward_interpolate(deltaUc, Uf)      # 向上插值 - 残差补偿计算
      111         Uf += deltaUf
      112         # post-smoothing
      113         U0 = self.__smoothing(Uf, F0, alpha, 10)
      114         return U0
      115             
      116             
      117     def __upward_interpolate(self, deltaUc, Uf):
      118         deltaUf = numpy.zeros(Uf.shape)
      119         deltaUf[2:-2:2, 2:-2:2] = deltaUc[1:-1, 1:-1]
      120         deltaUf[1:-1:2, 2:-2:2] = (deltaUf[0:-2:2, 2:-2:2] + deltaUf[2::2, 2:-2:2]) / 2
      121         deltaUf[:, 1:-1:2] = (deltaUf[:, 0:-2:2] + deltaUf[:, 2::2]) / 2
      122         return deltaUf
      123             
      124             
      125     def __downward_interpolate(self, Rf):
      126         '''
      127         向下插值 - R中边界值无效
      128         '''
      129         RcOri = Rf[2:-2:2, 2:-2:2]
      130         RcTra = numpy.zeros((RcOri.shape[0]+2, RcOri.shape[1]+2))
      131         RcTra[1:-1, 1:-1] = RcOri
      132         return RcTra
      133     
      134     
      135     def __calc_residual(self, U, F):
      136         '''
      137         残差计算
      138         '''
      139         dx = 1 / (U.shape[1] - 1)
      140         R = numpy.zeros(U.shape)
      141         
      142         for i in range(1, U.shape[0]-1):
      143             for j in range(1, U.shape[1]-1):
      144                 R[i, j] = F[i, j] - (U[i-1, j] + U[i+1, j] + U[i, j-1] + U[i, j+1] - 4 * U[i, j]) / dx ** 2
      145         return R
      146             
      147             
      148     def __smoothing(self, U, F, alpha, iterNum=5):
      149         for idx in range(iterNum):
      150             U = (1 - alpha) * U + alpha * jacobi(U, F)       # under-relaxation
      151         return U
      152         
      153 
      154 
      155 class MultigridPlot(object):
      156 
      157     fig = None
      158     ax1 = None
      159     line = None
      160     txt = None
      161     UList = None
      162     
      163     @classmethod
      164     def plot_ani(cls, mgObj):
      165         mgObj.optimize()
      166         
      167         cls.UList = mgObj.get_solu()
      168         cls.fig = plt.figure(figsize=(5, 4))
      169         cls.ax1 = plt.subplot()
      170         cls.line = cls.ax1.imshow(cls.UList[0], cmap="gray")
      171         cls.txt = cls.ax1.text(x=100, y=100, s="")
      172         
      173         ani = animation.FuncAnimation(cls.fig, cls.update, frames=numpy.arange(len(cls.UList)), blit=True, interval=1000, repeat=True)
      174         
      175         cls.ax1.xaxis.set_visible(False)
      176         cls.ax1.yaxis.set_visible(False)
      177         cls.fig.tight_layout()
      178         ani.save("plot_ani.gif", writer="PillowWriter", fps=3, dpi=1000)
      179         plt.close()
      180     
      181     
      182     @classmethod
      183     def update(cls, frame):
      184         cls.line.set_data(cls.UList[frame])
      185         cls.txt.set_text("iterCnt: {}".format(frame))
      186         return cls.line, cls.txt
      187         
      188         
      189     @staticmethod
      190     def plot_fig(mgObj):
      191         U = mgObj.optimize()
      192         img = Image.fromarray(U).convert("L")
      193         img.save("plot_fig.png")
      194         img.close()
      195         
      196 
      197 
      198 if __name__ == "__main__":
      199     picName = "./telescope-gray.jpg"
      200     F = get_F(picName)
      201     B = get_B(picName)
      202     
      203     img = Image.fromarray(F).convert("L")
      204     img.save("F.png")
      205     img.close()
      206     
      207     img2 = Image.fromarray(B).convert("L")
      208     img2.save("B.png")
      209     img2.close()
      210     
      211     mgObj = Multigrid(B, F)
      212     MultigridPlot.plot_fig(mgObj)
      213     # MultigridPlot.plot_ani(mgObj)
      View Code
    • 结果展示:
      显然, 通过Multigrid方法, 根据中间Laplace变换图像及右侧边界条件图像即可实现对左侧原始图像的快速求解, 迭代速度较常规迭代方法(Jacobi迭代、Gauss-Seidel迭代等)提升明显.

    • 使用建议:
      ①. 适用于结构化网格, 计算过程在精细网格与粗糙网格间多次变换;
      ②. 适用于求解椭圆型方程, 尤其是线性椭圆型方程.

    • 参考文档: 
      Numerical Methods for PDE 2016 by Professor Qiqi Wang & Jacob White from MIT

  • 相关阅读:
    cocos2d多语言互调之一---lua调java
    vue 组件
    封装 XMLHttpRequest
    Ajax 请求
    jq 显示和隐藏 计时器
    jq 动画DOM及其操作
    jq 表单提交
    jq 事件绑定
    js 一些实用的封装和兼容
    工厂、原型、混合模式
  • 原文地址:https://www.cnblogs.com/xxhbdk/p/14063741.html
Copyright © 2020-2023  润新知