Mercurial > hg > octave-lyh
annotate scripts/sparse/pcr.m @ 11100:cdf940db26a0
provide mxIsFunctionHandle MEX interface function
author | John W. Eaton <jwe@octave.org> |
---|---|
date | Sat, 16 Oct 2010 13:27:18 -0400 |
parents | be55736a0783 |
children | 994e2a93a8e2 |
rev | line source |
---|---|
8920 | 1 ## Copyright (C) 2004, 2006, 2007, 2009 Piotr Krzyzanowski |
5837 | 2 ## |
3 ## This file is part of Octave. | |
4 ## | |
5 ## Octave is free software; you can redistribute it and/or modify it | |
6 ## under the terms of the GNU General Public License as published by | |
7016 | 7 ## the Free Software Foundation; either version 3 of the License, or (at |
8 ## your option) any later version. | |
5837 | 9 ## |
10 ## Octave is distributed in the hope that it will be useful, but | |
11 ## WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 ## General Public License for more details. | |
14 ## | |
15 ## You should have received a copy of the GNU General Public License | |
7016 | 16 ## along with Octave; see the file COPYING. If not, see |
17 ## <http://www.gnu.org/licenses/>. | |
5837 | 18 |
19 ## -*- texinfo -*- | |
10793
be55736a0783
Grammarcheck the documentation from m-files.
Rik <octave@nomad.inbox5.com>
parents:
10791
diff
changeset
|
20 ## @deftypefn {Function File} {@var{x} =} pcr (@var{a}, @var{b}, @var{tol}, @var{maxit}, @var{m}, @var{x0}, @dots{}) |
5837 | 21 ## @deftypefnx {Function File} {[@var{x}, @var{flag}, @var{relres}, @var{iter}, @var{resvec}] =} pcr (@dots{}) |
22 ## | |
5838 | 23 ## Solves the linear system of equations @code{@var{a} * @var{x} = |
9051
1bf0ce0930be
Grammar check TexInfo in all .m files
Rik <rdrider0-list@yahoo.com>
parents:
8920
diff
changeset
|
24 ## @var{b}} by means of the Preconditioned Conjugate Residuals iterative |
1bf0ce0930be
Grammar check TexInfo in all .m files
Rik <rdrider0-list@yahoo.com>
parents:
8920
diff
changeset
|
25 ## method. The input arguments are |
5837 | 26 ## |
27 ## @itemize | |
28 ## @item | |
5838 | 29 ## @var{a} can be either a square (preferably sparse) matrix or a |
5837 | 30 ## function handle, inline function or string containing the name |
9051
1bf0ce0930be
Grammar check TexInfo in all .m files
Rik <rdrider0-list@yahoo.com>
parents:
8920
diff
changeset
|
31 ## of a function which computes @code{@var{a} * @var{x}}. In principle |
5838 | 32 ## @var{a} should be symmetric and non-singular; if @code{pcr} |
33 ## finds @var{a} to be numerically singular, you will get a warning | |
5837 | 34 ## message and the @var{flag} output parameter will be set. |
35 ## | |
36 ## @item | |
37 ## @var{b} is the right hand side vector. | |
38 ## | |
39 ## @item | |
40 ## @var{tol} is the required relative tolerance for the residual error, | |
9051
1bf0ce0930be
Grammar check TexInfo in all .m files
Rik <rdrider0-list@yahoo.com>
parents:
8920
diff
changeset
|
41 ## @code{@var{b} - @var{a} * @var{x}}. The iteration stops if @code{norm |
5838 | 42 ## (@var{b} - @var{a} * @var{x}) <= @var{tol} * norm (@var{b} - @var{a} * |
9051
1bf0ce0930be
Grammar check TexInfo in all .m files
Rik <rdrider0-list@yahoo.com>
parents:
8920
diff
changeset
|
43 ## @var{x0})}. If @var{tol} is empty or is omitted, the function sets |
5837 | 44 ## @code{@var{tol} = 1e-6} by default. |
45 ## | |
46 ## @item | |
47 ## @var{maxit} is the maximum allowable number of iterations; if | |
48 ## @code{[]} is supplied for @code{maxit}, or @code{pcr} has less | |
49 ## arguments, a default value equal to 20 is used. | |
50 ## | |
51 ## @item | |
5838 | 52 ## @var{m} is the (left) preconditioning matrix, so that the iteration is |
5837 | 53 ## (theoretically) equivalent to solving by @code{pcr} @code{@var{P} * |
5838 | 54 ## @var{x} = @var{m} \ @var{b}}, with @code{@var{P} = @var{m} \ @var{a}}. |
5837 | 55 ## Note that a proper choice of the preconditioner may dramatically |
9051
1bf0ce0930be
Grammar check TexInfo in all .m files
Rik <rdrider0-list@yahoo.com>
parents:
8920
diff
changeset
|
56 ## improve the overall performance of the method. Instead of matrix |
5838 | 57 ## @var{m}, the user may pass a function which returns the results of |
58 ## applying the inverse of @var{m} to a vector (usually this is the | |
9051
1bf0ce0930be
Grammar check TexInfo in all .m files
Rik <rdrider0-list@yahoo.com>
parents:
8920
diff
changeset
|
59 ## preferred way of using the preconditioner). If @code{[]} is supplied |
5838 | 60 ## for @var{m}, or @var{m} is omitted, no preconditioning is applied. |
5837 | 61 ## |
62 ## @item | |
9051
1bf0ce0930be
Grammar check TexInfo in all .m files
Rik <rdrider0-list@yahoo.com>
parents:
8920
diff
changeset
|
63 ## @var{x0} is the initial guess. If @var{x0} is empty or omitted, the |
5837 | 64 ## function sets @var{x0} to a zero vector by default. |
65 ## @end itemize | |
66 ## | |
67 ## The arguments which follow @var{x0} are treated as parameters, and | |
5838 | 68 ## passed in a proper way to any of the functions (@var{a} or @var{m}) |
9051
1bf0ce0930be
Grammar check TexInfo in all .m files
Rik <rdrider0-list@yahoo.com>
parents:
8920
diff
changeset
|
69 ## which are passed to @code{pcr}. See the examples below for further |
1bf0ce0930be
Grammar check TexInfo in all .m files
Rik <rdrider0-list@yahoo.com>
parents:
8920
diff
changeset
|
70 ## details. The output arguments are |
5837 | 71 ## |
72 ## @itemize | |
73 ## @item | |
74 ## @var{x} is the computed approximation to the solution of | |
5838 | 75 ## @code{@var{a} * @var{x} = @var{b}}. |
5837 | 76 ## |
77 ## @item | |
9051
1bf0ce0930be
Grammar check TexInfo in all .m files
Rik <rdrider0-list@yahoo.com>
parents:
8920
diff
changeset
|
78 ## @var{flag} reports on the convergence. @code{@var{flag} = 0} means |
5837 | 79 ## the solution converged and the tolerance criterion given by @var{tol} |
9051
1bf0ce0930be
Grammar check TexInfo in all .m files
Rik <rdrider0-list@yahoo.com>
parents:
8920
diff
changeset
|
80 ## is satisfied. @code{@var{flag} = 1} means that the @var{maxit} limit |
1bf0ce0930be
Grammar check TexInfo in all .m files
Rik <rdrider0-list@yahoo.com>
parents:
8920
diff
changeset
|
81 ## for the iteration count was reached. @code{@var{flag} = 3} reports t |
5837 | 82 ## @code{pcr} breakdown, see [1] for details. |
83 ## | |
84 ## @item | |
85 ## @var{relres} is the ratio of the final residual to its initial value, | |
86 ## measured in the Euclidean norm. | |
87 ## | |
88 ## @item | |
89 ## @var{iter} is the actual number of iterations performed. | |
90 ## | |
91 ## @item | |
92 ## @var{resvec} describes the convergence history of the method, | |
93 ## so that @code{@var{resvec} (i)} contains the Euclidean norms of the | |
7001 | 94 ## residual after the (@var{i}-1)-th iteration, @code{@var{i} = |
5838 | 95 ## 1,2, @dots{}, @var{iter}+1}. |
5837 | 96 ## @end itemize |
97 ## | |
98 ## Let us consider a trivial problem with a diagonal matrix (we exploit the | |
99 ## sparsity of A) | |
100 ## | |
101 ## @example | |
102 ## @group | |
10549 | 103 ## n = 10; |
104 ## a = sparse (diag (1:n)); | |
105 ## b = rand (N, 1); | |
5837 | 106 ## @end group |
107 ## @end example | |
108 ## | |
109 ## @sc{Example 1:} Simplest use of @code{pcr} | |
110 ## | |
111 ## @example | |
112 ## x = pcr(A, b) | |
113 ## @end example | |
114 ## | |
115 ## @sc{Example 2:} @code{pcr} with a function which computes | |
5838 | 116 ## @code{@var{a} * @var{x}}. |
5837 | 117 ## |
118 ## @example | |
119 ## @group | |
8507 | 120 ## function y = apply_a (x) |
5837 | 121 ## y = [1:10]'.*x; |
122 ## endfunction | |
123 ## | |
8507 | 124 ## x = pcr ("apply_a", b) |
5837 | 125 ## @end group |
126 ## @end example | |
127 ## | |
9051
1bf0ce0930be
Grammar check TexInfo in all .m files
Rik <rdrider0-list@yahoo.com>
parents:
8920
diff
changeset
|
128 ## @sc{Example 3:} Preconditioned iteration, with full diagnostics. The |
5837 | 129 ## preconditioner (quite strange, because even the original matrix |
5838 | 130 ## @var{a} is trivial) is defined as a function |
5837 | 131 ## |
132 ## @example | |
133 ## @group | |
10549 | 134 ## function y = apply_m (x) |
8507 | 135 ## k = floor (length(x)-2); |
5837 | 136 ## y = x; |
10549 | 137 ## y(1:k) = x(1:k)./[1:k]'; |
5837 | 138 ## endfunction |
139 ## | |
7031 | 140 ## [x, flag, relres, iter, resvec] = ... |
8507 | 141 ## pcr (a, b, [], [], "apply_m") |
5837 | 142 ## semilogy([1:iter+1], resvec); |
143 ## @end group | |
144 ## @end example | |
145 ## | |
146 ## @sc{Example 4:} Finally, a preconditioner which depends on a | |
147 ## parameter @var{k}. | |
148 ## | |
149 ## @example | |
150 ## @group | |
8507 | 151 ## function y = apply_m (x, varargin) |
152 ## k = varargin@{1@}; | |
10549 | 153 ## y = x; y(1:k) = x(1:k)./[1:k]'; |
5837 | 154 ## endfunction |
155 ## | |
7031 | 156 ## [x, flag, relres, iter, resvec] = ... |
8507 | 157 ## pcr (a, b, [], [], "apply_m"', [], 3) |
5837 | 158 ## @end group |
159 ## @end example | |
160 ## | |
10791
3140cb7a05a1
Add spellchecker scripts for Octave and run spellcheck of documentation
Rik <octave@nomad.inbox5.com>
parents:
10549
diff
changeset
|
161 ## References: |
5837 | 162 ## |
10791
3140cb7a05a1
Add spellchecker scripts for Octave and run spellcheck of documentation
Rik <octave@nomad.inbox5.com>
parents:
10549
diff
changeset
|
163 ## [1] W. Hackbusch, @cite{Iterative Solution of Large Sparse Systems of |
3140cb7a05a1
Add spellchecker scripts for Octave and run spellcheck of documentation
Rik <octave@nomad.inbox5.com>
parents:
10549
diff
changeset
|
164 ## Equations}, section 9.5.4; Springer, 1994 |
5837 | 165 ## |
166 ## @seealso{sparse, pcg} | |
167 ## @end deftypefn | |
168 | |
5838 | 169 ## Author: Piotr Krzyzanowski <piotr.krzyzanowski@mimuw.edu.pl> |
5837 | 170 |
8507 | 171 function [x, flag, relres, iter, resvec] = pcr (a, b, tol, maxit, m, x0, varargin) |
5837 | 172 |
173 breakdown = false; | |
174 | |
5838 | 175 if (nargin < 6 || isempty (x0)) |
176 x = zeros (size (b)); | |
5837 | 177 else |
178 x = x0; | |
179 endif | |
180 | |
181 if (nargin < 5) | |
8507 | 182 m = []; |
5837 | 183 endif |
184 | |
5838 | 185 if (nargin < 4 || isempty (maxit)) |
5837 | 186 maxit = 20; |
187 endif | |
188 | |
5838 | 189 maxit += 2; |
5837 | 190 |
5838 | 191 if (nargin < 3 || isempty (tol)) |
5837 | 192 tol = 1e-6; |
193 endif | |
194 | |
195 if (nargin < 2) | |
5838 | 196 print_usage (); |
5837 | 197 endif |
198 | |
199 ## init | |
10549 | 200 if (isnumeric (a)) # is A a matrix? |
8507 | 201 r = b - a*x; |
10549 | 202 else # then A should be a function! |
8507 | 203 r = b - feval (a, x, varargin{:}); |
5837 | 204 endif |
205 | |
10549 | 206 if (isnumeric (m)) # is M a matrix? |
207 if (isempty (m)) # if M is empty, use no precond | |
5837 | 208 p = r; |
10549 | 209 else # otherwise, apply the precond |
8507 | 210 p = m \ r; |
5837 | 211 endif |
10549 | 212 else # then M should be a function! |
8507 | 213 p = feval (m, r, varargin{:}); |
5837 | 214 endif |
215 | |
216 iter = 2; | |
217 | |
218 b_bot_old = 1; | |
5838 | 219 q_old = p_old = s_old = zeros (size (x)); |
5837 | 220 |
10549 | 221 if (isnumeric (a)) # is A a matrix? |
8507 | 222 q = a * p; |
10549 | 223 else # then A should be a function! |
8507 | 224 q = feval (a, p, varargin{:}); |
5837 | 225 endif |
10549 | 226 |
5838 | 227 resvec(1) = abs (norm (r)); |
5837 | 228 |
229 ## iteration | |
5838 | 230 while (resvec(iter-1) > tol*resvec(1) && iter < maxit) |
231 | |
10549 | 232 if (isnumeric (m)) # is M a matrix? |
233 if (isempty (m)) # if M is empty, use no precond | |
234 s = q; | |
235 else # otherwise, apply the precond | |
236 s = m \ q; | |
5837 | 237 endif |
10549 | 238 else # then M should be a function! |
8507 | 239 s = feval (m, q, varargin{:}); |
5837 | 240 endif |
5838 | 241 b_top = r' * s; |
242 b_bot = q' * s; | |
10549 | 243 |
5837 | 244 if (b_bot == 0.0) |
245 breakdown = true; | |
246 break; | |
247 endif | |
5838 | 248 lambda = b_top / b_bot; |
10549 | 249 |
5838 | 250 x += lambda*p; |
251 r -= lambda*q; | |
10549 | 252 |
253 if (isnumeric(a)) # is A a matrix? | |
8507 | 254 t = a*s; |
10549 | 255 else # then A should be a function! |
8507 | 256 t = feval (a, s, varargin{:}); |
5837 | 257 endif |
10549 | 258 |
5838 | 259 alpha0 = (t'*s) / b_bot; |
260 alpha1 = (t'*s_old) / b_bot_old; | |
10549 | 261 |
5838 | 262 p_temp = p; |
263 q_temp = q; | |
264 | |
5837 | 265 p = s - alpha0*p - alpha1*p_old; |
266 q = t - alpha0*q - alpha1*q_old; | |
10549 | 267 |
5837 | 268 s_old = s; |
269 p_old = p_temp; | |
270 q_old = q_temp; | |
271 b_bot_old = b_bot; | |
10549 | 272 |
5838 | 273 resvec(iter) = abs (norm (r)); |
274 iter++; | |
5837 | 275 endwhile |
276 | |
277 flag = 0; | |
5838 | 278 relres = resvec(iter-1) ./ resvec(1); |
279 iter -= 2; | |
280 if (iter >= maxit-2) | |
5837 | 281 flag = 1; |
282 if (nargout < 2) | |
6498 | 283 warning ("pcr: maximum number of iterations (%d) reached\n", iter); |
284 warning ("the initial residual norm was reduced %g times.\n", 1.0/relres); | |
5837 | 285 endif |
5838 | 286 elseif (nargout < 2 && ! breakdown) |
6498 | 287 fprintf (stderr, "pcr: converged in %d iterations. \n", iter); |
288 fprintf (stderr, "the initial residual norm was reduced %g times.\n", | |
10549 | 289 1.0 / relres); |
5837 | 290 endif |
5838 | 291 |
5837 | 292 if (breakdown) |
293 flag = 3; | |
294 if (nargout < 2) | |
7001 | 295 warning ("pcr: breakdown occurred:\n"); |
6498 | 296 warning ("system matrix singular or preconditioner indefinite?\n"); |
5837 | 297 endif |
298 endif | |
299 | |
300 endfunction | |
301 | |
302 %!demo | |
303 %! | |
10549 | 304 %! # Simplest usage of PCR (see also 'help pcr') |
5837 | 305 %! |
10549 | 306 %! N = 20; |
307 %! A = diag(linspace(-3.1,3,N)); b = rand(N,1); y = A\b; #y is the true solution | |
308 %! x = pcr(A,b); | |
309 %! printf('The solution relative error is %g\n', norm(x-y)/norm(y)); | |
5837 | 310 %! |
10549 | 311 %! # You shouldn't be afraid if PCR issues some warning messages in this |
312 %! # example: watch out in the second example, why it takes N iterations | |
313 %! # of PCR to converge to (a very accurate, by the way) solution | |
5837 | 314 %!demo |
315 %! | |
10549 | 316 %! # Full output from PCR |
317 %! # We use this output to plot the convergence history | |
5837 | 318 %! |
10549 | 319 %! N = 20; |
320 %! A = diag(linspace(-3.1,30,N)); b = rand(N,1); X = A\b; #X is the true solution | |
321 %! [x, flag, relres, iter, resvec] = pcr(A,b); | |
322 %! printf('The solution relative error is %g\n', norm(x-X)/norm(X)); | |
323 %! title('Convergence history'); xlabel('Iteration'); ylabel('log(||b-Ax||/||b||)'); | |
324 %! semilogy([0:iter],resvec/resvec(1),'o-g;relative residual;'); | |
5837 | 325 %!demo |
326 %! | |
10549 | 327 %! # Full output from PCR |
328 %! # We use indefinite matrix based on the Hilbert matrix, with one | |
329 %! # strongly negative eigenvalue | |
330 %! # Hilbert matrix is extremely ill conditioned, so is ours, | |
331 %! # and that's why PCR WILL have problems | |
5837 | 332 %! |
10549 | 333 %! N = 10; |
334 %! A = hilb(N); A(1,1)=-A(1,1); b = rand(N,1); X = A\b; #X is the true solution | |
335 %! printf('Condition number of A is %g\n', cond(A)); | |
336 %! [x, flag, relres, iter, resvec] = pcr(A,b,[],200); | |
337 %! if (flag == 3) | |
338 %! printf('PCR breakdown. System matrix is [close to] singular\n'); | |
339 %! end | |
340 %! title('Convergence history'); xlabel('Iteration'); ylabel('log(||b-Ax||)'); | |
341 %! semilogy([0:iter],resvec,'o-g;absolute residual;'); | |
5837 | 342 %!demo |
343 %! | |
10549 | 344 %! # Full output from PCR |
345 %! # We use an indefinite matrix based on the 1-D Laplacian matrix for A, | |
346 %! # and here we have cond(A) = O(N^2) | |
347 %! # That's the reason we need some preconditioner; here we take | |
348 %! # a very simple and not powerful Jacobi preconditioner, | |
349 %! # which is the diagonal of A | |
5837 | 350 %! |
10549 | 351 %! # Note that we use here indefinite preconditioners! |
5837 | 352 %! |
10549 | 353 %! N = 100; |
354 %! A = zeros(N,N); | |
355 %! for i=1:N-1 # form 1-D Laplacian matrix | |
356 %! A(i:i+1,i:i+1) = [2 -1; -1 2]; | |
357 %! endfor | |
358 %! A = [A, zeros(size(A)); zeros(size(A)), -A]; | |
359 %! b = rand(2*N,1); X = A\b; #X is the true solution | |
360 %! maxit = 80; | |
361 %! printf('System condition number is %g\n',cond(A)); | |
362 %! # No preconditioner: the convergence is very slow! | |
5837 | 363 %! |
10549 | 364 %! [x, flag, relres, iter, resvec] = pcr(A,b,[],maxit); |
365 %! title('Convergence history'); xlabel('Iteration'); ylabel('log(||b-Ax||)'); | |
366 %! semilogy([0:iter],resvec,'o-g;NO preconditioning: absolute residual;'); | |
5837 | 367 %! |
10549 | 368 %! pause(1); |
369 %! # Test Jacobi preconditioner: it will not help much!!! | |
5837 | 370 %! |
10549 | 371 %! M = diag(diag(A)); # Jacobi preconditioner |
372 %! [x, flag, relres, iter, resvec] = pcr(A,b,[],maxit,M); | |
373 %! hold on; | |
374 %! semilogy([0:iter],resvec,'o-r;JACOBI preconditioner: absolute residual;'); | |
5837 | 375 %! |
10549 | 376 %! pause(1); |
377 %! # Test nonoverlapping block Jacobi preconditioner: this one should give | |
378 %! # some convergence speedup! | |
5837 | 379 %! |
10549 | 380 %! M = zeros(N,N);k=4; |
381 %! for i=1:k:N # get k x k diagonal blocks of A | |
382 %! M(i:i+k-1,i:i+k-1) = A(i:i+k-1,i:i+k-1); | |
383 %! endfor | |
384 %! M = [M, zeros(size(M)); zeros(size(M)), -M]; | |
385 %! [x, flag, relres, iter, resvec] = pcr(A,b,[],maxit,M); | |
386 %! semilogy([0:iter],resvec,'o-b;BLOCK JACOBI preconditioner: absolute residual;'); | |
387 %! hold off; | |
5837 | 388 %!test |
389 %! | |
10549 | 390 %! #solve small indefinite diagonal system |
5837 | 391 %! |
10549 | 392 %! N = 10; |
393 %! A = diag(linspace(-10.1,10,N)); b = ones(N,1); X = A\b; #X is the true solution | |
394 %! [x, flag] = pcr(A,b,[],N+1); | |
395 %! assert(norm(x-X)/norm(X)<1e-10); | |
396 %! assert(flag,0); | |
5837 | 397 %! |
398 %!test | |
399 %! | |
10549 | 400 %! #solve tridiagonal system, do not converge in default 20 iterations |
401 %! #should perform max allowable default number of iterations | |
5837 | 402 %! |
10549 | 403 %! N = 100; |
404 %! A = zeros(N,N); | |
405 %! for i=1:N-1 # form 1-D Laplacian matrix | |
406 %! A(i:i+1,i:i+1) = [2 -1; -1 2]; | |
407 %! endfor | |
408 %! b = ones(N,1); X = A\b; #X is the true solution | |
409 %! [x, flag, relres, iter, resvec] = pcr(A,b,1e-12); | |
410 %! assert(flag,1); | |
411 %! assert(relres>0.6); | |
412 %! assert(iter,20); | |
5837 | 413 %! |
414 %!test | |
415 %! | |
10549 | 416 %! #solve tridiagonal system with 'prefect' preconditioner |
417 %! #converges in one iteration | |
5837 | 418 %! |
10549 | 419 %! N = 100; |
420 %! A = zeros(N,N); | |
421 %! for i=1:N-1 # form 1-D Laplacian matrix | |
422 %! A(i:i+1,i:i+1) = [2 -1; -1 2]; | |
423 %! endfor | |
424 %! b = ones(N,1); X = A\b; #X is the true solution | |
425 %! [x, flag, relres, iter] = pcr(A,b,[],[],A,b); | |
426 %! assert(norm(x-X)/norm(X)<1e-6); | |
427 %! assert(relres<1e-6); | |
428 %! assert(flag,0); | |
429 %! assert(iter,1); #should converge in one iteration | |
5837 | 430 %! |