NEURON
conjgrad.c
Go to the documentation of this file.
1 #include <../../nrnconf.h>
2 
3 /**************************************************************************
4 **
5 ** Copyright (C) 1993 David E. Steward & Zbigniew Leyk, all rights reserved.
6 **
7 ** Meschach Library
8 **
9 ** This Meschach Library is provided "as is" without any express
10 ** or implied warranty of any kind with respect to this software.
11 ** In particular the authors shall not be liable for any direct,
12 ** indirect, special, incidental or consequential damages arising
13 ** in any way from use of the software.
14 **
15 ** Everyone is granted permission to copy, modify and redistribute this
16 ** Meschach Library, provided:
17 ** 1. All copies contain this copyright notice.
18 ** 2. All modified copies shall carry a notice stating who
19 ** made the last modification and the date of such modification.
20 ** 3. No charge is made for this software or works derived from it.
21 ** This clause shall not be construed as constraining other software
22 ** distributed on the same medium as this software, nor is a
23 ** distribution fee considered a charge.
24 **
25 ***************************************************************************/
26 
27 
28 /*
29  Conjugate gradient routines file
30  Uses sparse matrix input & sparse Cholesky factorisation in pccg().
31 
32  All the following routines use routines to define a matrix
33  rather than use any explicit representation
34  (with the exeception of the pccg() pre-conditioner)
35  The matrix A is defined by
36 
37  VEC *(*A)(void *params, VEC *x, VEC *y)
38 
39  where y = A.x on exit, and y is returned. The params argument is
40  intended to make it easier to re-use & modify such routines.
41 
42  If we have a sparse matrix data structure
43  SPMAT *A_mat;
44  then these can be used by passing sp_mv_mlt as the function, and
45  A_mat as the param.
46 */
47 
48 #include <stdio.h>
49 #include <math.h>
50 #include "matrix.h"
51 #include "sparse.h"
52 static char rcsid[] = "conjgrad.c,v 1.1 1997/12/04 17:55:16 hines Exp";
53 
54 
55 /* #define MAX_ITER 10000 */
56 static int max_iter = 10000;
58 
59 /* matrix-as-routine type definition */
60 /* #ifdef ANSI_C */
61 /* typedef VEC *(*MTX_FN)(void *params, VEC *x, VEC *out); */
62 /* #else */
63 typedef VEC *(*MTX_FN)();
64 /* #endif */
65 #ifdef ANSI_C
66 VEC *spCHsolve(SPMAT *,VEC *,VEC *);
67 #else
68 VEC *spCHsolve();
69 #endif
70 
71 /* cg_set_maxiter -- sets maximum number of iterations if numiter > 1
72  -- just returns current max_iter otherwise
73  -- returns old maximum */
74 int cg_set_maxiter(numiter)
75 int numiter;
76 {
77  int temp;
78 
79  if ( numiter < 2 )
80  return max_iter;
81  temp = max_iter;
82  max_iter = numiter;
83  return temp;
84 }
85 
86 
87 /* pccg -- solves A.x = b using pre-conditioner M
88  (assumed factored a la spCHfctr())
89  -- results are stored in x (if x != NULL), which is returned */
90 VEC *pccg(A,A_params,M_inv,M_params,b,eps,x)
91 MTX_FN A, M_inv;
92 VEC *b, *x;
93 double eps;
94 void *A_params, *M_params;
95 {
96  VEC *r = VNULL, *p = VNULL, *q = VNULL, *z = VNULL;
97  int k;
98  Real alpha, beta, ip, old_ip, norm_b;
99 
100  if ( ! A || ! b )
101  error(E_NULL,"pccg");
102  if ( x == b )
103  error(E_INSITU,"pccg");
104  x = v_resize(x,b->dim);
105  if ( eps <= 0.0 )
106  eps = MACHEPS;
107 
108  r = v_get(b->dim);
109  p = v_get(b->dim);
110  q = v_get(b->dim);
111  z = v_get(b->dim);
112 
113  norm_b = v_norm2(b);
114 
115  v_zero(x);
116  r = v_copy(b,r);
117  old_ip = 0.0;
118  for ( k = 0; ; k++ )
119  {
120  if ( v_norm2(r) < eps*norm_b )
121  break;
122  if ( k > max_iter )
123  error(E_ITER,"pccg");
124  if ( M_inv )
125  (*M_inv)(M_params,r,z);
126  else
127  v_copy(r,z); /* M == identity */
128  ip = in_prod(z,r);
129  if ( k ) /* if ( k > 0 ) ... */
130  {
131  beta = ip/old_ip;
132  p = v_mltadd(z,p,beta,p);
133  }
134  else /* if ( k == 0 ) ... */
135  {
136  beta = 0.0;
137  p = v_copy(z,p);
138  old_ip = 0.0;
139  }
140  q = (*A)(A_params,p,q);
141  alpha = ip/in_prod(p,q);
142  x = v_mltadd(x,p,alpha,x);
143  r = v_mltadd(r,q,-alpha,r);
144  old_ip = ip;
145  }
146  cg_num_iters = k;
147 
148  V_FREE(p);
149  V_FREE(q);
150  V_FREE(r);
151  V_FREE(z);
152 
153  return x;
154 }
155 
156 /* sp_pccg -- a simple interface to pccg() which uses sparse matrix
157  data structures
158  -- assumes that LLT contains the Cholesky factorisation of the
159  actual pre-conditioner */
160 VEC *sp_pccg(A,LLT,b,eps,x)
161 SPMAT *A, *LLT;
162 VEC *b, *x;
163 double eps;
164 { return pccg(sp_mv_mlt,A,spCHsolve,LLT,b,eps,x); }
165 
166 
167 /*
168  Routines for performing the CGS (Conjugate Gradient Squared)
169  algorithm of P. Sonneveld:
170  "CGS, a fast Lanczos-type solver for nonsymmetric linear
171  systems", SIAM J. Sci. & Stat. Comp. v. 10, pp. 36--52
172 */
173 
174 /* cgs -- uses CGS to compute a solution x to A.x=b
175  -- the matrix A is not passed explicitly, rather a routine
176  A is passed where A(x,Ax,params) computes
177  Ax = A.x
178  -- the computed solution is passed */
179 VEC *cgs(A,A_params,b,r0,tol,x)
180 MTX_FN A;
181 VEC *x, *b;
182 VEC *r0; /* tilde r0 parameter -- should be random??? */
183 double tol; /* error tolerance used */
184 void *A_params;
185 {
186  VEC *p, *q, *r, *u, *v, *tmp1, *tmp2;
187  Real alpha, beta, norm_b, rho, old_rho, sigma;
188  int iter;
189 
190  if ( ! A || ! x || ! b || ! r0 )
191  error(E_NULL,"cgs");
192  if ( x->dim != b->dim || r0->dim != x->dim )
193  error(E_SIZES,"cgs");
194  if ( tol <= 0.0 )
195  tol = MACHEPS;
196 
197  p = v_get(x->dim);
198  q = v_get(x->dim);
199  r = v_get(x->dim);
200  u = v_get(x->dim);
201  v = v_get(x->dim);
202  tmp1 = v_get(x->dim);
203  tmp2 = v_get(x->dim);
204 
205  norm_b = v_norm2(b);
206  (*A)(A_params,x,tmp1);
207  v_sub(b,tmp1,r);
208  v_zero(p); v_zero(q);
209  old_rho = 1.0;
210 
211  iter = 0;
212  while ( v_norm2(r) > tol*norm_b )
213  {
214  if ( ++iter > max_iter ) break;
215  /* error(E_ITER,"cgs"); */
216  rho = in_prod(r0,r);
217  if ( old_rho == 0.0 )
218  error(E_SING,"cgs");
219  beta = rho/old_rho;
220  v_mltadd(r,q,beta,u);
221  v_mltadd(q,p,beta,tmp1);
222  v_mltadd(u,tmp1,beta,p);
223 
224  (*A)(A_params,p,v);
225 
226  sigma = in_prod(r0,v);
227  if ( sigma == 0.0 )
228  error(E_SING,"cgs");
229  alpha = rho/sigma;
230  v_mltadd(u,v,-alpha,q);
231  v_add(u,q,tmp1);
232 
233  (*A)(A_params,tmp1,tmp2);
234 
235  v_mltadd(r,tmp2,-alpha,r);
236  v_mltadd(x,tmp1,alpha,x);
237 
238  old_rho = rho;
239  }
240  cg_num_iters = iter;
241 
242  V_FREE(p); V_FREE(q); V_FREE(r);
243  V_FREE(u); V_FREE(v);
244  V_FREE(tmp1); V_FREE(tmp2);
245 
246  return x;
247 }
248 
249 /* sp_cgs -- simple interface for SPMAT data structures */
250 VEC *sp_cgs(A,b,r0,tol,x)
251 SPMAT *A;
252 VEC *b, *r0, *x;
253 double tol;
254 { return cgs(sp_mv_mlt,A,b,r0,tol,x); }
255 
256 /*
257  Routine for performing LSQR -- the least squares QR algorithm
258  of Paige and Saunders:
259  "LSQR: an algorithm for sparse linear equations and
260  sparse least squares", ACM Trans. Math. Soft., v. 8
261  pp. 43--71 (1982)
262 */
263 /* lsqr -- sparse CG-like least squares routine:
264  -- finds min_x ||A.x-b||_2 using A defined through A & AT
265  -- returns x (if x != NULL) */
266 VEC *lsqr(A,AT,A_params,b,tol,x)
267 MTX_FN A, AT; /* AT is A transposed */
268 VEC *x, *b;
269 double tol; /* error tolerance used */
270 void *A_params;
271 {
272  VEC *u, *v, *w, *tmp;
273  Real alpha, beta, norm_b, phi, phi_bar,
274  rho, rho_bar, rho_max, theta;
275  Real s, c; /* for Givens' rotations */
276  int iter, m, n;
277 
278  if ( ! b || ! x )
279  error(E_NULL,"lsqr");
280  if ( tol <= 0.0 )
281  tol = MACHEPS;
282 
283  m = b->dim; n = x->dim;
284  u = v_get((u_int)m);
285  v = v_get((u_int)n);
286  w = v_get((u_int)n);
287  tmp = v_get((u_int)n);
288  norm_b = v_norm2(b);
289 
290  v_zero(x);
291  beta = v_norm2(b);
292  if ( beta == 0.0 )
293  return x;
294  sv_mlt(1.0/beta,b,u);
295  tracecatch((*AT)(A_params,u,v),"lsqr");
296  alpha = v_norm2(v);
297  if ( alpha == 0.0 )
298  return x;
299  sv_mlt(1.0/alpha,v,v);
300  v_copy(v,w);
301  phi_bar = beta; rho_bar = alpha;
302 
303  rho_max = 1.0;
304  iter = 0;
305  do {
306  if ( ++iter > max_iter )
307  error(E_ITER,"lsqr");
308 
309  tmp = v_resize(tmp,m);
310  tracecatch((*A) (A_params,v,tmp),"lsqr");
311 
312  v_mltadd(tmp,u,-alpha,u);
313  beta = v_norm2(u); sv_mlt(1.0/beta,u,u);
314 
315  tmp = v_resize(tmp,n);
316  tracecatch((*AT)(A_params,u,tmp),"lsqr");
317  v_mltadd(tmp,v,-beta,v);
318  alpha = v_norm2(v); sv_mlt(1.0/alpha,v,v);
319 
320  rho = sqrt(rho_bar*rho_bar+beta*beta);
321  if ( rho > rho_max )
322  rho_max = rho;
323  c = rho_bar/rho;
324  s = beta/rho;
325  theta = s*alpha;
326  rho_bar = -c*alpha;
327  phi = c*phi_bar;
328  phi_bar = s*phi_bar;
329 
330  /* update x & w */
331  if ( rho == 0.0 )
332  error(E_SING,"lsqr");
333  v_mltadd(x,w,phi/rho,x);
334  v_mltadd(v,w,-theta/rho,w);
335  } while ( fabs(phi_bar*alpha*c) > tol*norm_b/rho_max );
336 
337  cg_num_iters = iter;
338 
339  V_FREE(tmp); V_FREE(u); V_FREE(v); V_FREE(w);
340 
341  return x;
342 }
343 
344 /* sp_lsqr -- simple interface for SPMAT data structures */
345 VEC *sp_lsqr(A,b,tol,x)
346 SPMAT *A;
347 VEC *b, *x;
348 double tol;
349 { return lsqr(sp_mv_mlt,sp_vm_mlt,A,b,tol,x); }
350 
static char rcsid[]
Definition: conjgrad.c:52
static int max_iter
Definition: conjgrad.c:56
VEC *(* MTX_FN)()
Definition: conjgrad.c:63
#define E_SING
Definition: err.h:98
#define in_prod(a, b)
Definition: matrix.h:485
#define Real
Definition: machine.h:189
static Object ** v_resize(void *v)
Definition: ivocvect.cpp:1297
VEC * spCHsolve(SPMAT *, VEC *, VEC *)
Definition: spchfctr.c:311
VEC * sp_lsqr(SPMAT *A, VEC *b, double tol, VEC *x)
Definition: conjgrad.c:345
size_t p
int cg_num_iters
Definition: conjgrad.c:57
#define E_ITER
Definition: err.h:107
#define v_norm2(x)
Definition: matrix.h:511
#define v
Definition: md1redef.h:4
VEC * pccg(MTX_FN A, void *A_params, MTX_FN M_inv, void *M_params, VEC *b, double eps, VEC *x)
Definition: conjgrad.c:90
static philox4x32_key_t k
Definition: nrnran123.cpp:11
int cg_set_maxiter(int numiter)
Definition: conjgrad.c:74
Definition: matrix.h:67
static Object ** v_sub(void *v1)
Definition: ivocvect.cpp:2263
#define v_copy(in, out)
Definition: matrix.h:404
#define E_SIZES
Definition: err.h:95
uint32_t u_int
Definition: machine.h:38
Definition: sparse.h:54
int const size_t const size_t n
Definition: nrngsl.h:12
_CONST char * s
Definition: system.cpp:74
u_int dim
Definition: matrix.h:68
#define tracecatch(ok_part, function)
Definition: err.h:166
VEC * v_mltadd(VEC *, VEC *, double, VEC *)
#define E_NULL
Definition: err.h:102
#define alpha
Definition: bkpfacto.c:43
VEC * sv_mlt(double, VEC *, VEC *)
VEC * sp_mv_mlt(SPMAT *A, VEC *x, VEC *out)
Definition: sparse.c:123
static double v_get(void *v)
Definition: ivocvect.cpp:1309
#define MACHEPS
Definition: machine.h:213
VEC * cgs(MTX_FN A, void *A_params, VEC *b, VEC *r0, double tol, VEC *x)
Definition: conjgrad.c:179
VEC * v_zero(VEC *x)
Definition: init.c:40
VEC * sp_pccg(SPMAT *A, SPMAT *LLT, VEC *b, double eps, VEC *x)
Definition: conjgrad.c:160
VEC * lsqr(MTX_FN A, MTX_FN AT, void *A_params, VEC *b, double tol, VEC *x)
Definition: conjgrad.c:266
sqrt
Definition: extdef.h:3
static Object ** v_add(void *v1)
Definition: ivocvect.cpp:2245
VEC * sp_cgs(SPMAT *A, VEC *b, VEC *r0, double tol, VEC *x)
Definition: conjgrad.c:250
#define A(i)
Definition: multisplit.cpp:61
#define c
#define error(err_num, fn_name)
Definition: err.h:73
fabs
Definition: extdef.h:3
#define VNULL
Definition: matrix.h:631
size_t q
#define V_FREE(vec)
Definition: matrix.h:214
#define E_INSITU
Definition: err.h:106
VEC * sp_vm_mlt(SPMAT *A, VEC *x, VEC *out)
Definition: sparse.c:159