NEURON
splufctr.c
Go to the documentation of this file.
1 #include <../../nrnconf.h>
2 
3 /**************************************************************************
4 **
5 ** Copyright (C) 1993 David E. Stewart & 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  Sparse LU factorisation
30  See also: sparse.[ch] etc for details about sparse matrices
31 */
32 
33 #include <stdio.h>
34 #include "sparse2.h"
35 #include <math.h>
36 
37 
38 
39 /* Macro for speedup */
40 /* #define sprow_idx2(r,c,hint) \
41  ( ( (hint) >= 0 && (r)->elt[hint].col == (c)) ? hint : sprow_idx((r),(c)) ) */
42 
43 
44 /* spLUfactor -- sparse LU factorisation with pivoting
45  -- uses partial pivoting and Markowitz criterion
46  |a[p][k]| >= alpha * max_i |a[i][k]|
47  -- creates fill-in as needed
48  -- in situ factorisation */
50 SPMAT *A;
51 PERM *px;
52 double alpha;
53 {
54  int i, best_i, k, idx, len, best_len, m, n;
55  SPROW *r, *r_piv, tmp_row;
56  static SPROW *merge = (SPROW *)NULL;
57  Real max_val, tmp;
58  static VEC *col_vals=VNULL;
59 
60  if ( ! A || ! px )
61  error(E_NULL,"spLUfctr");
62  if ( alpha <= 0.0 || alpha > 1.0 )
63  error(E_RANGE,"alpha in spLUfctr");
64  if ( px->size <= A->m )
65  px = px_resize(px,A->m);
66  px_ident(px);
67  col_vals = v_resize(col_vals,A->m);
68  MEM_STAT_REG(col_vals,TYPE_VEC);
69 
70  m = A->m; n = A->n;
71  if ( ! A->flag_col )
73  if ( ! A->flag_diag )
75  A->flag_col = A->flag_diag = FALSE;
76  if ( ! merge ) {
77  merge = sprow_get(20);
78  MEM_STAT_REG(merge,TYPE_SPROW);
79  }
80 
81  for ( k = 0; k < n; k++ )
82  {
83  /* find pivot row/element for partial pivoting */
84 
85  /* get first row with a non-zero entry in the k-th column */
86  max_val = 0.0;
87  for ( i = k; i < m; i++ )
88  {
89  r = &(A->row[i]);
90  idx = sprow_idx(r,k);
91  if ( idx < 0 )
92  tmp = 0.0;
93  else
94  tmp = r->elt[idx].val;
95  if ( fabs(tmp) > max_val )
96  max_val = fabs(tmp);
97  col_vals->ve[i] = tmp;
98  }
99 
100  if ( max_val == 0.0 )
101  continue;
102 
103  best_len = n+1; /* only if no possibilities */
104  best_i = -1;
105  for ( i = k; i < m; i++ )
106  {
107  tmp = fabs(col_vals->ve[i]);
108  if ( tmp == 0.0 )
109  continue;
110  if ( tmp >= alpha*max_val )
111  {
112  r = &(A->row[i]);
113  idx = sprow_idx(r,k);
114  len = (r->len) - idx;
115  if ( len < best_len )
116  {
117  best_len = len;
118  best_i = i;
119  }
120  }
121  }
122 
123  /* swap row #best_i with row #k */
124  MEM_COPY(&(A->row[best_i]),&tmp_row,sizeof(SPROW));
125  MEM_COPY(&(A->row[k]),&(A->row[best_i]),sizeof(SPROW));
126  MEM_COPY(&tmp_row,&(A->row[k]),sizeof(SPROW));
127  /* swap col_vals entries */
128  tmp = col_vals->ve[best_i];
129  col_vals->ve[best_i] = col_vals->ve[k];
130  col_vals->ve[k] = tmp;
131  px_transp(px,k,best_i);
132 
133  r_piv = &(A->row[k]);
134  for ( i = k+1; i < n; i++ )
135  {
136  /* compute and set multiplier */
137  tmp = col_vals->ve[i]/col_vals->ve[k];
138  if ( tmp != 0.0 )
139  sp_set_val(A,i,k,tmp);
140  else
141  continue;
142 
143  /* perform row operations */
144  merge->len = 0;
145  r = &(A->row[i]);
146  sprow_mltadd(r,r_piv,-tmp,k+1,merge,TYPE_SPROW);
147  idx = sprow_idx(r,k+1);
148  if ( idx < 0 )
149  idx = -(idx+2);
150  /* see if r needs expanding */
151  if ( r->maxlen < idx + merge->len )
152  sprow_xpd(r,idx+merge->len,TYPE_SPMAT);
153  r->len = idx+merge->len;
154  MEM_COPY((char *)(merge->elt),(char *)&(r->elt[idx]),
155  merge->len*sizeof(row_elt));
156  }
157  }
158 
159  return A;
160 }
161 
162 /* spLUsolve -- solve A.x = b using factored matrix A from spLUfactor()
163  -- returns x
164  -- may not be in-situ */
165 VEC *spLUsolve(A,pivot,b,x)
166 SPMAT *A;
167 PERM *pivot;
168 VEC *b, *x;
169 {
170  int i, idx, len, lim;
171  Real sum, *x_ve;
172  SPROW *r;
173  row_elt *elt;
174 
175  if ( ! A || ! b )
176  error(E_NULL,"spLUsolve");
177  if ( (pivot != PNULL && A->m != pivot->size) || A->m != b->dim )
178  error(E_SIZES,"spLUsolve");
179  if ( ! x || x->dim != A->n )
180  x = v_resize(x,A->n);
181 
182  if ( pivot != PNULL )
183  x = px_vec(pivot,b,x);
184  else
185  x = v_copy(b,x);
186 
187  x_ve = x->ve;
188  lim = min(A->m,A->n);
189  for ( i = 0; i < lim; i++ )
190  {
191  sum = x_ve[i];
192  r = &(A->row[i]);
193  len = r->len;
194  elt = r->elt;
195  for ( idx = 0; idx < len && elt->col < i; idx++, elt++ )
196  sum -= elt->val*x_ve[elt->col];
197  x_ve[i] = sum;
198  }
199 
200  for ( i = lim-1; i >= 0; i-- )
201  {
202  sum = x_ve[i];
203  r = &(A->row[i]);
204  len = r->len;
205  elt = &(r->elt[len-1]);
206  for ( idx = len-1; idx >= 0 && elt->col > i; idx--, elt-- )
207  sum -= elt->val*x_ve[elt->col];
208  if ( idx < 0 || elt->col != i || elt->val == 0.0 )
209  error(E_SING,"spLUsolve");
210  x_ve[i] = sum/elt->val;
211  }
212 
213  return x;
214 }
215 
216 /* spLUTsolve -- solve A.x = b using factored matrix A from spLUfactor()
217  -- returns x
218  -- may not be in-situ */
219 VEC *spLUTsolve(A,pivot,b,x)
220 SPMAT *A;
221 PERM *pivot;
222 VEC *b, *x;
223 {
224  int i, idx, lim, rownum;
225  Real sum, *tmp_ve;
226  /* SPROW *r; */
227  row_elt *elt;
228  static VEC *tmp=VNULL;
229 
230  if ( ! A || ! b )
231  error(E_NULL,"spLUTsolve");
232  if ( (pivot != PNULL && A->m != pivot->size) || A->m != b->dim )
233  error(E_SIZES,"spLUTsolve");
234  tmp = v_copy(b,tmp);
235  MEM_STAT_REG(tmp,TYPE_VEC);
236 
237  if ( ! A->flag_col )
238  sp_col_access(A);
239  if ( ! A->flag_diag )
240  sp_diag_access(A);
241 
242  lim = min(A->m,A->n);
243  tmp_ve = tmp->ve;
244  /* solve U^T.tmp = b */
245  for ( i = 0; i < lim; i++ )
246  {
247  sum = tmp_ve[i];
248  rownum = A->start_row[i];
249  idx = A->start_idx[i];
250  if ( rownum < 0 || idx < 0 )
251  error(E_SING,"spLUTsolve");
252  while ( rownum < i && rownum >= 0 && idx >= 0 )
253  {
254  elt = &(A->row[rownum].elt[idx]);
255  sum -= elt->val*tmp_ve[rownum];
256  rownum = elt->nxt_row;
257  idx = elt->nxt_idx;
258  }
259  if ( rownum != i )
260  error(E_SING,"spLUTsolve");
261  elt = &(A->row[rownum].elt[idx]);
262  if ( elt->val == 0.0 )
263  error(E_SING,"spLUTsolve");
264  tmp_ve[i] = sum/elt->val;
265  }
266 
267  /* now solve L^T.tmp = (old) tmp */
268  for ( i = lim-1; i >= 0; i-- )
269  {
270  sum = tmp_ve[i];
271  rownum = i;
272  idx = A->row[rownum].diag;
273  if ( idx < 0 )
274  error(E_NULL,"spLUTsolve");
275  elt = &(A->row[rownum].elt[idx]);
276  rownum = elt->nxt_row;
277  idx = elt->nxt_idx;
278  while ( rownum < lim && rownum >= 0 && idx >= 0 )
279  {
280  elt = &(A->row[rownum].elt[idx]);
281  sum -= elt->val*tmp_ve[rownum];
282  rownum = elt->nxt_row;
283  idx = elt->nxt_idx;
284  }
285  tmp_ve[i] = sum;
286  }
287 
288  if ( pivot != PNULL )
289  x = pxinv_vec(pivot,tmp,x);
290  else
291  x = v_copy(tmp,x);
292 
293  return x;
294 }
295 
296 /* spILUfactor -- sparse modified incomplete LU factorisation with
297  no pivoting
298  -- all pivot entries are ensured to be >= alpha in magnitude
299  -- setting alpha = 0 gives incomplete LU factorisation
300  -- no fill-in is generated
301  -- in situ factorisation */
303 SPMAT *A;
304 double alpha;
305 {
306  int i, k, idx, idx_piv, m, n, old_idx, old_idx_piv;
307  SPROW *r, *r_piv;
308  Real piv_val, tmp;
309 
310  /* printf("spILUfactor: entered\n"); */
311  if ( ! A )
312  error(E_NULL,"spILUfactor");
313  if ( alpha < 0.0 )
314  error(E_RANGE,"[alpha] in spILUfactor");
315 
316  m = A->m; n = A->n;
317  sp_diag_access(A);
318  sp_col_access(A);
319 
320  for ( k = 0; k < n; k++ )
321  {
322  /* printf("spILUfactor(l.%d): checkpoint A: k = %d\n",__LINE__,k); */
323  /* printf("spILUfactor(l.%d): A =\n", __LINE__); */
324  /* sp_output(A); */
325  r_piv = &(A->row[k]);
326  idx_piv = r_piv->diag;
327  if ( idx_piv < 0 )
328  {
329  sprow_set_val(r_piv,k,alpha);
330  idx_piv = sprow_idx(r_piv,k);
331  }
332  /* printf("spILUfactor: checkpoint B\n"); */
333  if ( idx_piv < 0 )
334  error(E_BOUNDS,"spILUfactor");
335  old_idx_piv = idx_piv;
336  piv_val = r_piv->elt[idx_piv].val;
337  /* printf("spILUfactor: checkpoint C\n"); */
338  if ( fabs(piv_val) < alpha )
339  piv_val = ( piv_val < 0.0 ) ? -alpha : alpha;
340  if ( piv_val == 0.0 ) /* alpha == 0.0 too! */
341  error(E_SING,"spILUfactor");
342 
343  /* go to next row with a non-zero in this column */
344  i = r_piv->elt[idx_piv].nxt_row;
345  old_idx = idx = r_piv->elt[idx_piv].nxt_idx;
346  while ( i >= k )
347  {
348  /* printf("spILUfactor: checkpoint D: i = %d\n",i); */
349  /* perform row operations */
350  r = &(A->row[i]);
351  /* idx = sprow_idx(r,k); */
352  /* printf("spLUfactor(l.%d) i = %d, idx = %d\n",
353  __LINE__, i, idx); */
354  if ( idx < 0 )
355  {
356  idx = r->elt[old_idx].nxt_idx;
357  i = r->elt[old_idx].nxt_row;
358  continue;
359  }
360  /* printf("spILUfactor: checkpoint E\n"); */
361  /* compute and set multiplier */
362  r->elt[idx].val = tmp = r->elt[idx].val/piv_val;
363  /* printf("spILUfactor: piv_val = %g, multiplier = %g\n",
364  piv_val, tmp); */
365  /* printf("spLUfactor(l.%d) multiplier = %g\n", __LINE__, tmp); */
366  if ( tmp == 0.0 )
367  {
368  idx = r->elt[old_idx].nxt_idx;
369  i = r->elt[old_idx].nxt_row;
370  continue;
371  }
372  /* idx = sprow_idx(r,k+1); */
373  /* if ( idx < 0 )
374  idx = -(idx+2); */
375  idx_piv++; idx++; /* now look beyond the multiplier entry */
376  /* printf("spILUfactor: checkpoint F: idx = %d, idx_piv = %d\n",
377  idx, idx_piv); */
378  while ( idx_piv < r_piv->len && idx < r->len )
379  {
380  /* printf("spILUfactor: checkpoint G: idx = %d, idx_piv = %d\n",
381  idx, idx_piv); */
382  if ( r_piv->elt[idx_piv].col < r->elt[idx].col )
383  idx_piv++;
384  else if ( r_piv->elt[idx_piv].col > r->elt[idx].col )
385  idx++;
386  else /* column numbers match */
387  {
388  /* printf("spILUfactor(l.%d) subtract %g times the ",
389  __LINE__, tmp); */
390  /* printf("(%d,%d) entry to the (%d,%d) entry\n",
391  k, r_piv->elt[idx_piv].col,
392  i, r->elt[idx].col); */
393  r->elt[idx].val -= tmp*r_piv->elt[idx_piv].val;
394  idx++; idx_piv++;
395  }
396  }
397 
398  /* bump to next row with a non-zero in column k */
399  /* printf("spILUfactor(l.%d) column = %d, row[%d] =\n",
400  __LINE__, r->elt[old_idx].col, i); */
401  /* sprow_foutput(stdout,r); */
402  i = r->elt[old_idx].nxt_row;
403  old_idx = idx = r->elt[old_idx].nxt_idx;
404  /* printf("spILUfactor(l.%d) i = %d, idx = %d\n", __LINE__, i, idx); */
405  /* and restore idx_piv to index of pivot entry */
406  idx_piv = old_idx_piv;
407  }
408  }
409  /* printf("spILUfactor: exiting\n"); */
410  return A;
411 }
#define PNULL
Definition: matrix.h:633
Real val
Definition: sparse.h:46
double sp_set_val(SPMAT *A, int i, int j, double val)
Definition: sparse.c:66
#define E_SING
Definition: err.h:98
int diag
Definition: sparse.h:50
double sprow_set_val(SPROW *, int, double)
Definition: sprow.c:666
SPMAT * sp_diag_access(SPMAT *A)
Definition: sparse.c:417
if(status)
#define TYPE_VEC
Definition: meminfo.h:52
#define min(a, b)
Definition: matrix.h:157
#define Real
Definition: machine.h:189
static Object ** v_resize(void *v)
Definition: ivocvect.cpp:1297
VEC * spLUsolve(SPMAT *A, PERM *pivot, VEC *b, VEC *x)
Definition: splufctr.c:165
row_elt * elt
Definition: sparse.h:51
int nxt_row
Definition: sparse.h:45
static philox4x32_key_t k
Definition: nrnran123.cpp:11
VEC * px_vec(PERM *, VEC *, VEC *)
PERM * px_transp(PERM *px, u_int i, u_int j)
Definition: pxop.c:208
Definition: sparse.h:44
int col
Definition: sparse.h:45
Real * ve
Definition: matrix.h:69
Definition: matrix.h:67
#define v_copy(in, out)
Definition: matrix.h:404
u_int size
Definition: matrix.h:88
#define E_SIZES
Definition: err.h:95
Definition: sparse.h:54
int const size_t const size_t n
Definition: nrngsl.h:12
#define E_RANGE
Definition: err.h:104
PERM * px_resize(PERM *, int)
Definition: memory.c:399
u_int dim
Definition: matrix.h:68
SPROW * sprow_mltadd(SPROW *, SPROW *, double, int, SPROW *, int type)
Definition: sprow.c:399
int sprow_idx(SPROW *, int)
Definition: sprow.c:75
Definition: sparse.h:49
#define E_BOUNDS
Definition: err.h:96
VEC * spLUTsolve(SPMAT *A, PERM *pivot, VEC *b, VEC *x)
Definition: splufctr.c:219
void MEM_COPY(char *from, char *to, int len)
Definition: extras.c:37
#define E_NULL
Definition: err.h:102
#define alpha
Definition: bkpfacto.c:43
SPMAT * spLUfactor(SPMAT *A, PERM *px, double alpha)
Definition: splufctr.c:49
int len
Definition: sparse.h:50
VEC * pxinv_vec(PERM *, VEC *, VEC *)
SPMAT * spILUfactor(SPMAT *A, double alpha)
Definition: splufctr.c:302
int maxlen
Definition: sparse.h:50
#define MEM_STAT_REG(var, type)
Definition: meminfo.h:133
#define FALSE
Definition: err.c:56
int nxt_idx
Definition: sparse.h:45
#define i
Definition: md1redef.h:12
#define A(i)
Definition: multisplit.cpp:61
#define error(err_num, fn_name)
Definition: err.h:73
fabs
Definition: extdef.h:3
SPROW * sprow_get(int)
SPMAT * sp_col_access(SPMAT *A)
Definition: sparse.c:376
#define VNULL
Definition: matrix.h:631
SPROW * sprow_xpd(SPROW *r, int n, int type)
Definition: matrix.h:87
PERM * px_ident(PERM *px)
Definition: init.c:108
return NULL
Definition: cabcode.cpp:461