Mercurial > forge
changeset 1663:f2ee0c8dff0a octave-forge
First version of PCR function which implements preconditioned conjugate residual method
author | przykry2004 |
---|---|
date | Mon, 23 Aug 2004 11:54:07 +0000 |
parents | cf21a705b2e8 |
children | caa93f544424 |
files | main/sparse/pcr.m |
diffstat | 1 files changed, 411 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main/sparse/pcr.m Mon Aug 23 11:54:07 2004 +0000 @@ -0,0 +1,411 @@ +function [x, flag, relres, iter, resvec] = pcr(A,b,tol,maxit,M,x0,varargin) +% +%[X, FLAG, RELRES, ITER, RESVEC] = pcr( A, B, TOL, MAXIT, M, X0, ... ) +% +%Solves the linear system of equations A*x = B by means of the Preconditioned +%Conjugate Residuals iterative method. +% +%INPUT ARGUMENTS +%--------------- +% +%A can be either a square (preferably sparse) matrix or a name of a function +%which computes A*x. In principle A should be symmetric and nonsingular; +%if PCR finds A (numerically) singular, you will get a warning message +%and the FLAG output parameter will be set. +% +%B is the right hand side vector. +% +%TOL is the required relative tolerance for the residual error, B-A*X. The +%iteration stops if ||B-A*X|| <= TOL*||B-A*X0||, where ||.|| denotes the +%Euclidean norm. If TOL is empty or is omitted, the function sets TOL = 1e-6 by +%default. +% +%MAXIT is the maximum allowable number of iterations; if [] is supplied for +%MAXIT, or PCR has less arguments, a default value equal to 20 is used. +% +%M is the (left) preconditioning matrix, so that the iteration is +%(theoretically) equivalent to solving by PCR P*x = M\B, with P = M\A. +%Instead of a matrix, the user may pass here a name of a function which returns +%the result of applying the inverse of M to a vector (usually this is the +%preferred way of using the preconditioner), see EXAMPLES for details. +%Usually it is assumed that M is positive definite. Note that a proper choice of +%the preconditioner may dramatically improve the overall performance of the +%method! If [] is supplied for M, or PCR has less arguments, no preconditioning +%is applied. +% +%X0 is the initial guess. If X0 is empty or omitted, the function sets X0 to a +%zero vector by default. +% +%The arguments which follow X0 are treated as parameters, and passed in a proper +%way to any of the functions (A or M) which are passed to PCR. See the EXAMPLES +%for details. +% +%OUTPUT ARGUMENTS +%---------------- +% +%X is the computed approximation to the solution x of A*x=B. +% +%FLAG reports on the convergence. FLAG = 0 means the solution converged +%and the tolerance criterion given by TOL is satisfied. FLAG = 1 means that the +%MAXIT limit for the iteration count was reached. FLAG = 3 reports +%PCR breakdown, see [1] for details. +% +%RELRES is the ratio of the final residual to its initial value, measured in the +%Euclidean norm. +% +%ITER is the actual number of iterations performed. +% +%RESVEC describes the convergence history of the method, so that RESVEC(i) +%contains the Euclidean norms of the residual after the (i-1)-th iteration, +%i = 1,2,...ITER+1. +% +%EXAMPLES +%-------- +% +%Let us consider a trivial problem with a diagonal matrix (we exploit the +%sparsity of A) +% +% N = 10; +% A = diag([1:N]); A = sparse(A); +% b = rand(N,1); +% +%EX. 1. Simplest use of PCR +% +% x = pcr(A,b) +% +%EX. 2. PCR with a function which computes A*x +% +% function y = applyA(x) y = [1:10]'.*x; end +% +% x = pcr('applyA',b) +% +%EX. 3. Preconditioned iteration, with full diagnostics. The preconditioner (quite +%strange, because even the original matrix A is trivial) is defined as a +%function: +% +% function y = applyM(x) +% K = floor(length(x)-2); +% y = x; +% y(1:K) = x(1:K)./[1:K]'; +% end +% +% [x, flag, relres, iter, resvec] = pcr(A,b,[],[],'applyM') +% semilogy([1:iter+1], resvec); +% +%EX. 4. Finally, a preconditioner which depends on a parameter K: +% +% function y = applyM(x, varargin) +% K = varargin{1}; +% y = x; y(1:K) = x(1:K)./[1:K]'; +% end +% +% [x, flag, relres, iter, resvec] = pcr(A,b,[],[],'applyM',[],3) +% +%You can also run +% +% demo('pcr') +% +%from the command line to see simple examples of how the PCR works. +% +%SEE ALSO: sparse, pcg, gmres +% +%REFERENCES +% +% [1] W. Hackbusch, "Iterative Solution of Large Sparse Systems of +% Equations", section 9.5.4; Springer, 1994 + +%% Copyright (C) 2004 Piotr Krzyzanowski <piotr.krzyzanowski@mimuw.edu.pl> +%% +%% This program is free software; you can redistribute it and/or modify +%% it under the terms of the GNU General Public License as published by +%% the Free Software Foundation; either version 2 of the License, or +%% (at your option) any later version. +%% +%% This program is distributed in the hope that it will be useful, +%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +%% GNU General Public License for more details. +%% +%% You should have received a copy of the GNU General Public License +%% along with this program; if not, write to the Free Software +%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +%% +%% REVISION HISTORY +%% +%% 2004-08-14, Piotr Krzyzanowski <piotr.krzyzanowski@mimuw.edu.pl> +%% +%% Added 4 demos and 4 tests +%% +%% 2004-08-01, Piotr Krzyzanowski <piotr.krzyzanowski@mimuw.edu.pl> +%% +%% First version of pcr code + +breakdown = false; + +if ((nargin < 6) || isempty(x0)) + x = zeros(size(b)); +else + x = x0; +end + +if (nargin < 5) + M = []; +end + +if ((nargin < 4) || isempty(maxit)) + maxit = 20; +end + +maxit = maxit + 2; + +if ((nargin < 3) || isempty(tol)) + tol = 1e-6; +end + +if (nargin < 2) + usage('x=pcr(A,b)'); +end + +% init +if (isnumeric(A)) % is A a matrix? + r = b-A*x; +else % then A should be a function! + r = b-feval(A,x,varargin{:}); +end + +if (isnumeric(M)) % is M a matrix? + if isempty(M) % if M is empty, use no precond + p = r; + else % otherwise, apply the precond + p = M\r; + end +else % then M should be a function! + p = feval(M,r,varargin{:}); +end + +iter = 2; + +b_bot_old = 1; +q_old = p_old = s_old = zeros(size(x)); + + if (isnumeric(A)) % is A a matrix? + q = A*p; + else % then A should be a function! + q = feval(A,p,varargin{:}); + end + +resvec(1) = abs(norm(r)); + +%iteration +while ( (resvec(iter-1) > tol*resvec(1)) && (iter < maxit) ), + + if (isnumeric(M)) % is M a matrix? + if isempty(M) % if M is empty, use no precond + s = q; + else % otherwise, apply the precond + s = M\q; + end + else % then M should be a function! + s = feval(M,q,varargin{:}); + end + b_top = r'*s; + b_bot = q'*s; + + if (b_bot == 0.0) + breakdown = true; + break; + end + lambda = b_top/b_bot; + + x = x + lambda*p; + r = r - lambda*q; + + if (isnumeric(A)) % is A a matrix? + t = A*s; + else % then A should be a function! + t = feval(A,s,varargin{:}); + end + + alpha0 = (t'*s)/b_bot; + alpha1 = (t'*s_old)/b_bot_old; + + p_temp = p; q_temp = q; + p = s - alpha0*p - alpha1*p_old; + q = t - alpha0*q - alpha1*q_old; + + s_old = s; + p_old = p_temp; + q_old = q_temp; + b_bot_old = b_bot; + + + resvec(iter) = abs(norm(r)); + iter = iter + 1; +end + +flag = 0; +relres = resvec(iter-1)./resvec(1); +iter = iter - 2; +if ( iter >= (maxit-2) ) + flag = 1; + if (nargout < 2) + warning('PCR: maximum number of iterations (%d) reached\n',... + iter); + warning('The initial residual norm was reduced %g times.\n',... + 1.0/relres); + end +else + if ((nargout < 2) && (~breakdown)) + fprintf(stderr, 'PCR: converged in %d iterations. \n', iter); + fprintf(stderr, 'The initial residual norm was reduced %g times.\n',... + 1.0/relres); + end +end +if (breakdown) + flag = 3; + if (nargout < 2) + warning('PCR: breakdown occured.\n'); + warning('System matrix singular or preconditioner indefinite?\n'); + end +end + +end +%!demo +%! +%! # Simplest usage of PCR (see also 'help pcr') +%! +%! N = 20; +%! A = diag(linspace(-3.1,3,N)); b = rand(N,1); y = A\b; #y is the true solution +%! x = pcr(A,b); +%! printf('The solution relative error is %g\n', norm(x-y)/norm(y)); +%! +%! # You shouldn't be afraid if PCR issues some warning messages in this +%! # example: watch out in the second example, why it takes N iterations +%! # of PCR to converge to (a very accurate, by the way) solution +%!demo +%! +%! # Full output from PCR +%! # We use this output to plot the convergence history +%! +%! N = 20; +%! A = diag(linspace(-3.1,30,N)); b = rand(N,1); X = A\b; #X is the true solution +%! [x, flag, relres, iter, resvec] = pcr(A,b); +%! printf('The solution relative error is %g\n', norm(x-X)/norm(X)); +%! title('Convergence history'); xlabel('Iteration'); ylabel('log(||b-Ax||/||b||)'); +%! semilogy([0:iter],resvec/resvec(1),'o-g;relative residual;'); +%!demo +%! +%! # Full output from PCR +%! # We use indefinite matrix based on the Hilbert matrix, with one +%! # strongly negative eigenvalue +%! # Hilbert matrix is extremely ill conditioned, so is ours, +%! # and that's why PCR WILL have problems +%! +%! N = 10; +%! A = hilb(N); A(1,1)=-A(1,1); b = rand(N,1); X = A\b; #X is the true solution +%! printf('Condition number of A is %g\n', cond(A)); +%! [x, flag, relres, iter, resvec] = pcr(A,b,[],200); +%! if (flag == 3) +%! printf('PCR breakdown. System matrix is [close to] singular\n'); +%! end +%! title('Convergence history'); xlabel('Iteration'); ylabel('log(||b-Ax||)'); +%! semilogy([0:iter],resvec,'o-g;absolute residual;'); +%!demo +%! +%! # Full output from PCR +%! # We use an indefinite matrix based on the 1-D Laplacian matrix for A, +%! # and here we have cond(A) = O(N^2) +%! # That's the reason we need some preconditioner; here we take +%! # a very simple and not powerful Jacobi preconditioner, +%! # which is the diagonal of A +%! +%! # Note that we use here indefinite preconditioners! +%! +%! N = 100; +%! A = zeros(N,N); +%! for i=1:N-1 # form 1-D Laplacian matrix +%! A(i:i+1,i:i+1) = [2 -1; -1 2]; +%! endfor +%! A = [A, zeros(size(A)); zeros(size(A)), -A]; +%! b = rand(2*N,1); X = A\b; #X is the true solution +%! maxit = 80; +%! printf('System condition number is %g\n',cond(A)); +%! # No preconditioner: the convergence is very slow! +%! +%! [x, flag, relres, iter, resvec] = pcr(A,b,[],maxit); +%! title('Convergence history'); xlabel('Iteration'); ylabel('log(||b-Ax||)'); +%! semilogy([0:iter],resvec,'o-g;NO preconditioning: absolute residual;'); +%! +%! pause(1); +%! # Test Jacobi preconditioner: it will not help much!!! +%! +%! M = diag(diag(A)); # Jacobi preconditioner +%! [x, flag, relres, iter, resvec] = pcr(A,b,[],maxit,M); +%! hold on; +%! semilogy([0:iter],resvec,'o-r;JACOBI preconditioner: absolute residual;'); +%! +%! pause(1); +%! # Test nonoverlapping block Jacobi preconditioner: this one should give +%! # some convergence speedup! +%! +%! M = zeros(N,N);k=4; +%! for i=1:k:N # get k x k diagonal blocks of A +%! M(i:i+k-1,i:i+k-1) = A(i:i+k-1,i:i+k-1); +%! endfor +%! M = [M, zeros(size(M)); zeros(size(M)), -M]; +%! [x, flag, relres, iter, resvec] = pcr(A,b,[],maxit,M); +%! semilogy([0:iter],resvec,'o-b;BLOCK JACOBI preconditioner: absolute residual;'); +%! hold off; +%!test +%! +%! #solve small indefinite diagonal system +%! +%! N = 10; +%! A = diag(linspace(-10.1,10,N)); b = rand(N,1); X = A\b; #X is the true solution +%! [x, flag] = pcr(A,b,[],N+1); +%! assert(norm(x-X)/norm(X),0,1e-10); +%! assert(flag,0); +%! +%!test +%! +%! #solve small nonsymmetric system +%! #lack of symmetry of A is detected +%! +%! N = 10; +%! A = diag([1:N].*(-ones(1,N).^2)); A(N,1)=1; b = rand(N,1); X = A\b; #X is the true solution +%! [x, flag] = pcr(A,b,[],N+1); +%! assert(norm(x-X)/norm(X)>2e-3, 1); +%! assert(flag,3); +%! +%!test +%! +%! #solve tridiagonal system, do not converge in default 20 iterations +%! +%! N = 100; +%! A = zeros(N,N); +%! for i=1:N-1 # form 1-D Laplacian matrix +%! A(i:i+1,i:i+1) = [2 -1; -1 2]; +%! endfor +%! b = ones(N,1); X = A\b; #X is the true solution +%! [x, flag, relres, iter, resvec] = pcr(A,b,1e-12); +%! assert(flag,1); +%! assert(relres>0.6,1); +%! assert(iter,20); #should perform max allowable default number of iterations +%! +%!test +%! +%! #solve tridiagonal system with 'prefect' preconditioner +%! #converges in one iteration +%! +%! N = 100; +%! A = zeros(N,N); +%! for i=1:N-1 # form 1-D Laplacian matrix +%! A(i:i+1,i:i+1) = [2 -1; -1 2]; +%! endfor +%! b = ones(N,1); X = A\b; #X is the true solution +%! [x, flag, relres, iter] = pcr(A,b,[],[],A,b); +%! assert(norm(x-X)/norm(X),0,1e-6); +%! assert(relres,0,1e-6); +%! assert(flag,0); +%! assert(iter,1); #should converge in one iteration +%!