File: pcg.c

package info (click to toggle)
python-scipy 0.5.2-0.1
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k
  • size: 33,888 kB
  • ctags: 44,231
  • sloc: ansic: 156,256; cpp: 90,347; python: 89,604; fortran: 73,083; sh: 1,318; objc: 424; makefile: 342
file content (302 lines) | stat: -rw-r--r-- 8,151 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
/*-------------------------------------------------------------------------

  PCG    Preconditioned Conjugate Gradients Method

  X = PCG(A,B) attempts to solve the system of linear equations A*X=B
  for X.  The coefficient matrix A must be symmetric and positive
  definite and the right hand side (column) vector B must have length
  N, where A is N-by-N.  PCG will start iterating from an initial
  guess. Iterates are produced until the method either converges,
  fails, or has computed the maximum number of iterations.
  Convergence is achieved when an iterate X has relative residual
  NORM(B-A*X)/NORM(B) less than or equal to the tolerance of the
  method. If PCG converged, then a message to that effect is
  displayed.  If PCG failed to converge after the maximum number of
  iterations or halted for any reason, then a message is printed
  displaying the relative residual and the iteration number at which
  the method stopped or failed.

  Parameters:

  N        problem size, A is an N-by-N matrix, B has length N
  X        on entry: the initial guess
           on exit:  the computed approximate solution
  B        the right hand side of the system
  TOL      error tolerance
  MAXIT    maximum number of iterations
  CLVL     verbosity of output (0: no output, 1: some output, 2: more output)
  ITER     number of iterations of iterations
  RELRES   norm of residual upon exit
  FLAG     exit code (see below)
  WORK     work array. must be of size 4*N
  MATVEC   function that performs multiplication with matrix A
  PRECON   function that applies preconditioner (can be NULL)

  FLAG describes the convergence of PCG.  If FLAG is

   0 then PCG converged to the desired tolerance TOL within MAXIT
     iterations without failing for any reason.

   1 then PCG iterated MAXIT times but did not converge.

   2 then a system of equations of the form M*Y = R was ill-conditioned.

   3 then PCG stagnated (two consecutive iterates were the same).

   4 then one of the scalar quantities calculated during PCG became
     too small or too large to continue computing.

  $Id: pcg.c,v 1.1.1.1 2003/11/09 12:35:47 geus Exp $
  Roman Geus, ETHZ

--------------------------------------------------------------------------*/

#include <assert.h>
#include "blas.h"
#include "fortran.h"
#include <float.h>
#include <limits.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pcg.h"

/* function prototypes */
static void itermsg(double tol,
		    int maxit,
		    int flag,
		    int iter,
		    double relres);

/* PCG - Conjugate Gradients Algorithm
 */
void pcg(int n, 
	 double *x, 
	 double *b,
	 double tol, 
	 int maxit,
	 int clvl,
	 int *iter, 
	 double *relres, 
	 int *flag,
	 double *work,
	 void (*matvec)(double *, double *),
	 void (*precon)(double *, double *))
{
  double ALPHA;			/* used for passing parameters */
  int ONE = 1;			/* to BLAS routines */

  double n2b;			/* norm of rhs vector */
  double tolb;			/* requested tolerance for residual */
  double normr;			/* residual norm */
  double alpha, beta;
  double rho, rho1;
  double pq;
  double dmax, ddum;		/* used to detect stagnation */
  int stag;			/* flag to indicate stagnation */
  int it;			/* current iteration number */
  int i;			/* index variable */
  double *r, *z, *p, *q;	/* pointers to vectors in PCG algorithm */
  
  /* setup pointers into work */
  r = work;
  z = work + n;
  p = work + 2*n;
  q = work + 3*n;

  /* Check for all zero right hand side vector => all zero solution */
  n2b = F77(dnrm2)(&n, b, &ONE);/* Norm of rhs vector, b */
  if (n2b == 0.0) {		/* if rhs vector is all zeros */
    for (i = 0; i < n; i ++)	/* then  solution is all zeros */
      x[i] = 0.0;
    *flag = 0;			/* a valid solution has been obtained */
    *relres = 0.0;		/* the relative residual is actually 0/0 */
    *iter = 0;			/* no iterations need be performed */
    if (clvl)
      itermsg(tol,maxit,*flag,*iter,*relres);
    return;
  }
  
  /* Set up for the method */
  *flag = 1;
  tolb = tol * n2b;		/* Relative tolerance */
  matvec(x, r);			/* Zero-th residual: r = b - A * x*/
  for (i = 0; i < n; i ++)	/* then  solution is all zeros */
    r[i] = b[i] - r[i];
  normr = F77(dnrm2)(&n, r, &ONE); /* Norm of residual */
  
  if (normr <= tolb) {		/* Initial guess is a good enough solution */
    *flag = 0;
    *relres = normr / n2b;
    *iter = 0;
    if (clvl)
      itermsg(tol,maxit,*flag,*iter,*relres);
    return;
  }

  rho = 1.0;
  stag = 0;			/* stagnation of the method */

  /* loop over maxit iterations (unless convergence or failure) */
  
  for (it = 1; it <= maxit; it ++) {
    
    if (precon) {
      precon(r, z);
      /*
	if isinf(norm(y,inf))
	flag = 2;
	break
	end
      */
    } else {
      F77(dcopy)(&n, r, &ONE, z, &ONE);
    }
   
    rho1 = rho;
    rho = F77(ddot)(&n, r, &ONE, z, &ONE);
    if (rho == 0.0) {		/* or isinf(rho) */
      *flag = 4;
      break;
    }
    if (it == 1) {
      F77(dcopy)(&n, z, &ONE, p, &ONE);
    } else {
      beta = rho / rho1;
      if (beta == 0.0) {	/* | isinf(beta) */
	*flag = 4;
	break;
      }
      for (i = 0; i < n; i ++)	/* p = z + beta * p; */
	p[i] = z[i] + beta * p[i];
    }
    matvec(p, q);		/* q = A * p */
    pq = F77(ddot)(&n, p, &ONE, q, &ONE); /* pq = p' * q */
    if (pq == 0.0) {		/* | isinf(pq) */
      *flag = 4;
      break;
    } else {
      alpha = rho / pq;
    }
    /* 
       if isinf(alpha)
       flag = 4;
       break
       end
    */
    if (alpha == 0.0)		/* stagnation of the method */
      stag = 1;
   
    /* Check for stagnation of the method */
    if (stag == 0) {
      dmax = 0.0;
      for (i = 0; i < n; i ++)
	if (x[i] != 0.0) {
	  ddum = fabs(alpha * p[i]/x[i]);
	  if (ddum > dmax)
	    dmax = ddum;
	} else
	  if (p[i] != 0.0)
	    dmax = 1.0;
      stag = (1.0 + dmax == 1.0);
    }
    
    F77(daxpy)(&n, &alpha, p, &ONE, x, &ONE); /* form new iterate */
    ALPHA = -alpha;
    F77(daxpy)(&n, &ALPHA, q, &ONE, r, &ONE); /* r = r - alpha * q */
    
    /* check for convergence */
#ifdef EXPENSIVE_CRIT
    matvec(x, z);		/* normr = norm(b - A * x) */
    for (i = 0; i < n; i ++)
      z[i] = b[i] - z[i];
    normr = F77(dnrm2)(&n, z, &ONE);
#else
    normr = F77(dnrm2)(&n, r, &ONE); /* normr = norm(r) */
#endif
    if (normr <= tolb) {
      *flag = 0;
      break;
    }
    
    if (stag == 1) {
      *flag = 3;
      break;
    }
  } /* for it = 1 : maxit */
  
  *iter = it;
  *relres = normr / n2b;

  if (clvl)
    itermsg(tol,maxit,*flag,*iter,*relres);
}

/* PCG_F77 - Fortran interface to PCG
 */
void F77(pcg_f77)(int *n, 
		  double *x, 
		  double *b,
		  double *tol, 
		  int *maxit,
		  int *clvl,
		  int *iter, 
		  double *relres, 
		  int *flag,
		  double *work,
		  void (*matvec)(double *, double *),
		  void (*precon)(double *, double *)) {
  pcg(*n,
      x,
      b,
      *tol,
      *maxit,
      *clvl,
      iter,
      relres,
      flag,
      work,
      matvec,
      precon);
}

  
/* ITERMSG - Displays the final message for PCG method
 */
static void itermsg(double tol,
		    int maxit,
		    int flag,
		    int iter,
		    double relres) {
  if (flag != 0) {
    printf("PCG stopped at iteration %d without converging to the desired tolerance %0.2g", iter, tol);
  }
  
  switch(flag) {
  case 0:
    if (iter == 0)
      printf("The initial guess has relative residual %0.2g which is within\nthe desired tolerance %0.2g so PCG returned it without iterating.",
	     relres, tol);
    else
      printf("PCG converged at iteration %d to a solution with relative residual %0.2g", iter, relres);
    break;
  case 1:
    printf("\nbecause the maximum number of iterations was reached.");
    break;
  case 2:
    printf("\nbecause the system involving the preconditioner was ill conditioned.");
    break;
  case 3:
    printf("\nbecause the method stagnated.");
    break;
  case 4:
    printf("\nbecause a scalar quantity became too small or too large to continue computing.");
    break;
  }
  
  if (flag != 0)
    printf("\nThe iterate returned (number %d) has relative residual %0.2g",iter,relres);

  printf("\n");
}