如何在matlab中实现灵活的gmres?

计算科学 matlab 克雷洛夫法 线性系统 格瑞斯
2021-12-04 01:15:59

关于灵活的 GMRES (fgmres),我们知道它是右预条件 gmres 的变体。matlab中的健壮命令gmres如下:

>> help gmres
 gmres   Generalized Minimum Residual Method.
    X = gmres(A,B) attempts to solve the system of linear equations A*X = B
    for X.  The N-by-N coefficient matrix A must be square and the right
    hand side column vector B must have length N. This uses the unrestarted
    method with MIN(N,10) total iterations.

我们可以看到matlab命令gmres可以支持左右预处理gmres。如何实现fgmres使用matlab的gmres.m

这是我的简单示例,左右预处理器成功但 fgmres 失败:

clc;clear;
n = 21; 
A = rand(n);
b = sum(A,2);
tol = 1e-7;
maxit = n;
M = diag(diag(A));
x_true = A\b;%  exact solution
restart = n;
%   left precondition
x1 = gmres(A,b,restart,tol,maxit,M);
norm(x_true-x1)
%   right precondition
x2 = gmres(@(x)A*(M\x),b,restart,tol,maxit);
norm(x_true-M\x2)
%   fgmres
Mfun=@(x) minres(M,x);
x3 = gmres(@(x)A*Mfun(x),b,restart,tol,maxit);
norm(x_true-Mfun(x3))

编辑:

我写了一个fgmres.m但是当它发生故障时,它没有得到正确的解决方案,即当故障发生时,迭代步数为外循环为3,内循环为1(重启= 30),即总迭代步数为61 , 但近似解是x61=NaN. 能给我一些帮助吗,非常感谢。无需修改任何代码即可在matlab中运行。我的matlab是2018b,8GB内存。

clc;clear;close all;
restart = 30;
maxit = 100;
tol = 1e-6;
%%
fprintf('-----------------------    fgmres with inexact inner solves  -----------\n');
mu = 1;q =64;
fprintf('------------------Grid = %4d,     mu = %6.4f----------\n',q,mu);
fprintf('flag\t\t|\t\titer\t\t|\t\tcputime\t\t|\t\trelres\t\t|\t\t|x-x_m|_2\n');
alpha = mu;
%%  generate the saddle point matrix :      bigA*x = rhs
h = 1/(1+q);
n = 2*q^2;m = q^2;
N = m+n;
I = speye(q);
T = spdiags(ones(q,1).*[-1 2 -1],[-1 0 1],q,q)*mu/h^2;
F = spdiags(ones(q,1).*[-1 1 0],[-1 0 1],q,q)/h;
B = [kron(I,F);kron(F,I)]';
A = kron(I,T)+kron(T,I);
A = blkdiag(A,A);
bigA = [A, B';-B,sparse(m,m)];
x_true = ones(N,1);
rhs = bigA*x_true;x0 = zeros(N,1);
fprintf('------------------------   my fgmres  --------------------\n');
%%  Hss
tic;
M = @(x)hss_precd_inexact(alpha,A,B,x);%   a function handle returns M_j\x
[x,flag,relres,iter,resvec]=myfgmres_right(bigA,rhs,restart,tol,maxit,M);
t=toc;
iter = (iter(1)-1)*restart+iter(2);
err = norm(x_true-x);
fprintf('%4d%19d%25.4f%20.4e%22.4e\n',flag,iter,t,relres,err);
%%  the defined preconditioner which uses iterative method to solve the sub system
function z = hss_precd_inexact(alpha,A,B,r)
%   HSS peconditioner for saddle point using iterative method for solving
%   inner sub-linear systems 
%   20191228
%   P_hss = [alpha*In+A   O        ]                [alpha*In       B']
%           [  O           alpha*Im]    *           [-B             alpha*Im]
[m,n]=size(B);
In = speye(n);
% Im = speye(m);
r1 = r(1:n,1);
r2 = r(n+1:end,1);
% L_A = ichol(alpha*In+A);
% L_B = chol(alpha*Im+1/alpha*(B*B'),'low');
[w1,~] = pcg(@afun1,r1);
w2 = 1/alpha*r2;
temp = 1/alpha*B*w1+w2;
t1 = 1/alpha*w1;
[t2,~] = pcg(@afun2,temp);
z1 = t1-1/alpha*B'*t2;
z2 = t2;
z = [z1;z2];
%%  handle returns A*x
    function y = afun1(x)
        y =alpha*x+A*x;
    end
    function y = afun2(x)
        y =alpha*x+1/alpha*(B*(B'*x));
    end
end
%%  my fgmres.m
function [x,flag,relres,iter,resvec] = myfgmres_right(A,b,restart,tol,maxit,M,x0)
%   myfgmres.m   generalized minimal residual to solve : A*x= b using right
%               preconditioner i.e.,            A*inv(M)  *u = b,      u=M*x
%   input
%           A           any real nonsingular matrix or function handle
%                           returns A*x
%           b           real right hand side
%           restart     the maximum of iteration (means dimension of Krylov)
%           tol         tolerance
%           maxit       outer iteration steps
%           x0          initialized guess vector (default is zero vector)
%           M           right preconditioner: matrix or function handle
%                           returns M\x
%   output
%           x           approxiamte solution: x_k
%           flag        0 = converge, 1=unconverge
%           relres      relative residual
%           iter        the iteration steps
%           resvec      ||r_k||_2,       r_k=b-A*x_k, res(1)=norm(b-A*x0)
%-------------------
% Initialization
%-------------------
% size of the problem
n = size(b,1);
if nargin==7
    %   do nothing
elseif nargin==6
    x0 = zeros(n,1);
elseif nargin ==5
    M=[];x0 = zeros(n,1);
elseif nargin ==4
    maxit=n;M=[];x0 = zeros(n,1);
elseif nargin ==3
    tol = 1e-6;maxit=n;M=[];x0 = zeros(n,1);
elseif nargin == 2
    restart = 10;tol = 1e-6;maxit=n;M=[];x0 = zeros(n,1);
else
    error('Input variables are not enough!!!!!!!!!!!!!!!!');
end
%% restart number
if isempty(restart)%    full gmres
    restart = maxit;
    maxit=1;
elseif restart ~= 0
    restart = min(restart, n);
    restart = min(restart,maxit);
elseif restart == 0
    error('restart ==0   is wrong!!!!!!');
else
    error('restart number is wrong!!!!!!');
end
%%  initialization space
m = restart;
% n = length(A);
H = zeros(m +1,m );%  the upper hessenberg matrix H (m+1,m)*****
c = zeros(m,1);%  the   givens transformation parameters: G1,...Gm
s = zeros(m,1);
resvec = zeros(maxit*m+1,1);%    preallocate the maximum space of residual norm
flag = 1;%  unconverge
Z = zeros(n,m);
V = zeros(n,m+1);
%%  prepare to iteration
x = x0;
%   initial residual
r = b-afun(x);%   r0=b-A*x0
% r = mfun(M,r);%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%   left precondition: M\r
resvec(1) = norm(r);%   initial residual
total_iter = 0;%    total iteration steps
for out = 1:maxit
    r = b-afun(x);%   r0=b-A*x0
    %     r = mfun(M,r);%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%   left precondition: M\r
    beta = norm(r);
    e1 = zeros(m+1,1);e1(1) = 1;% e1
    g = beta * e1;% beta*e1
    % V = zeros(n,maxit);%    orthonormal basis V = [v1,v2,...v_m]
    V(:,1) =  r/beta;%  %  v1
    %%  begin iteration
    for j = 1: m
        total_iter = total_iter+1;            
        Z(:,j) = mfun(V(:,j));          %        right precondition
        w = afun(Z(:,j));%        right precondition
        %   modified Gram-Schmidt
        for i = 1:j
            H(i,j) = w.'*V(:,i);%    h_ij
            w = w - H(i,j) * V(:,i);%    w_j = w_j - ...
        end
        H(j+1,j) = norm(w);%    ||w||_2
        %%  lucky breakdown
        if H(j+1,j) < eps
            fprintf('lucky breakdown!!!!!!!!!!!\n');
            flag = 0;
            %   apply the first j-1 givens to the last column of H_{j+1}_{j}
             for k = 1:j-1
                temp = c(k)*H(k,j)+s(k)*H(k+1,j);
                H(k+1,j) = -s(k)*H(k,j)+c(k)*H(k+1,j);
                H(k,j) = temp;
            end
            %   apply the givens to the last 2 elements of H(:,j)
            [s(j), c(j),r] = mygivens(H(j,j), H(j+1,j));
            H(j,j) = r;
            H(j+1,j) = 0;
            %   apply givens to the last 2 elements of g= beta*e1
            %         g(j:j+1,1) = [c(j) s(j);-s(j) c(j)] * [g(j);0];  %20191210
            %----------------- 20191227
            g(j+1) = -s(j)*g(j);
            g(j) = c(j)*g(j);
            %----------------- 20191227
            resvec(total_iter+1) = abs(g(j+1));     % obtain norm(r_k)
            relres = resvec(total_iter+1)/resvec(1);%   ||r_k||/||r0||
            break;
        end
        %%  generate a new orthonomal basis
        V(:,j+1) = w/H(j+1,j);%    v_{j+1}

        %   apply the first j-1 givens to the last column of H_{j+1}_{j}
        for k = 1:j-1
            temp = c(k)*H(k,j)+s(k)*H(k+1,j);
            H(k+1,j) = -s(k)*H(k,j)+c(k)*H(k+1,j);
            H(k,j) = temp;
        end
        %   apply the givens to the last 2 elements of H(:,j)
        [s(j), c(j),r] = mygivens(H(j,j), H(j+1,j));
        H(j,j) = r;
        H(j+1,j) = 0;
        %   apply givens to the last 2 elements of g= beta*e1
        %         g(j:j+1,1) = [c(j) s(j);-s(j) c(j)] * [g(j);0];  %20191210
        %----------------- 20191227
        g(j+1) = -s(j)*g(j);
        g(j) = c(j)*g(j);
        %----------------- 20191227
        resvec(total_iter+1) = abs(g(j+1));     % obtain norm(r_k)
        relres = resvec(total_iter+1)/resvec(1);%   ||r_k||/||r0||
        %   check convergence
        if relres < tol
            flag = 0;
            break;
        end
    end%    end of inner iteration
    %%  update the new iterate    
    y = H(1:j,1:j)\g(1:j);
    %     x = x + V(:,1:j)*y;
    x = x+Z(:,1:j)*y;%--------------- right precondition
    if flag==0
        break;
    end
end%    end of outer iteration
iter = [out, j];
resvec  = resvec(1:total_iter+1);
%   end of gmres
%%  children function
%%  givens transformation
    function [s,c,r] = mygivens(a,b)
        %   function Givens transformation: make sure r >= 0
        %   [c  s]     *[a]     =[r]
        %   -s  c]      [b]     =[0]
        %  written by Sun,Zhen-Wei on 2019.6.20
        if ( a==0 && b==0 )
            c=1;s=0;r=0;
            return;
        end
        if ( a==0 && b~=0 )
            c = 0;
            s = sign(b);
            r = abs(b);
            return;
        end
        if ( a~=0 && b==0 )
            c = sign(a);
            s = 0;
            r = abs(a);
            return;
        end
        %%  case for   a~=0 and b~=0
        if abs(b) > abs(a)
            tau = a/b;
            s = sign(b)/sqrt(1+tau^2);
            c = s*tau;
        else
            tau = b/a;
            c = sign(a)/sqrt(1+tau^2);
            s = c*tau;
        end
        r = sqrt(a^2+b^2);
    end
%%  function handle returns A*x
    function y = afun(x)
        if isa(A,'double')
            y = A*x;
        elseif isa(A,'function_handle')
            y = A(x);
        else
            error('------- A is neither a matrix or a function hanlde');
        end
    end
%%  preconditioner: returns M\x
    function z = mfun(x)
        if isempty(M)
            z = x;
        elseif isa(M,'double')
            z = M\x;
        elseif isa(M,'function_handle')
            z = M(x);
        else
            error('-----------  Precnoditioner is neither a matrix or function handle');
        end
    end
end
```
1个回答

首先,MATLABgmres假设您使用的预处理器是线性的。这个很重要!实际上,这是 FGMRES 和 GMRES 之间的主要区别。如果使用线性预条件子,右预条件 GMRES 和 FGMRES 完全相同,但是,FGMRES 允许使用非线性预条件子。

非线性预处理器是什么意思?对于 MINRES,即Mfun=@(x) minres(M,x),不能保证Mfun(x+av) = Mfun(x)+aMfun(v),因此,MINRES 是非线性算子。所以 MATLABgmres会失败。因此,我认为您的问题属于 scicomp StackExchange。相信我,行业专家落入了这个陷阱。

一种快速解决方法是将 MINRES 容差设置为小于 GMRES 容差,例如 tol_minres = 1e-10, tol_gmres = 1e-7。在这种情况下,MINRES 将成为 GMRES 容差的线性算子,Mfun(x+av) = Mfun(x)+aMfun(v)+E|E|<1e-7所以从 GMRES 的角度来看,Mfun它是一个线性算子,E将被忽略。请注意,这是手动操作,据我所知,没有人对此进行过分析。

您的 FGMRES 实现很难调试,所以我只是建议一个好的实现。如果还想自己做,可以比较对比。 https://github.com/oseledets/TT-Toolbox/blob/master/solve/fgmres.m