NEURON
mfunc.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  This file contains routines for computing functions of matrices
30  especially polynomials and exponential functions
31  Copyright (C) Teresa Leyk and David Stewart, 1993
32  */
33 
34 #include <stdio.h>
35 #include "matrix.h"
36 #include "matrix2.h"
37 #include <math.h>
38 
39 static char rcsid[] = "mfunc.c,v 1.1 1997/12/04 17:55:41 hines Exp";
40 
41 
42 
43 /* _m_pow -- computes integer powers of a square matrix A, A^p
44  -- uses tmp as temporary workspace */
45 MAT *_m_pow(A, p, tmp, out)
46 MAT *A, *tmp, *out;
47 int p;
48 {
49  int it_cnt, k, max_bit;
50 
51  /*
52  File containing routines for evaluating matrix functions
53  esp. the exponential function
54  */
55 
56 #define Z(k) (((k) & 1) ? tmp : out)
57 
58  if ( ! A )
59  error(E_NULL,"_m_pow");
60  if ( A->m != A->n )
61  error(E_SQUARE,"_m_pow");
62  if ( p < 0 )
63  error(E_NEG,"_m_pow");
64  out = m_resize(out,A->m,A->n);
65  tmp = m_resize(tmp,A->m,A->n);
66 
67  if ( p == 0 )
68  m_ident(out);
69  else if ( p > 0 )
70  {
71  it_cnt = 1;
72  for ( max_bit = 0; ; max_bit++ )
73  if ( (p >> (max_bit+1)) == 0 )
74  break;
75  tmp = m_copy(A,tmp);
76 
77  for ( k = 0; k < max_bit; k++ )
78  {
79  m_mlt(Z(it_cnt),Z(it_cnt),Z(it_cnt+1));
80  it_cnt++;
81  if ( p & (1 << (max_bit-1)) )
82  {
83  m_mlt(A,Z(it_cnt),Z(it_cnt+1));
84  /* m_copy(Z(it_cnt),out); */
85  it_cnt++;
86  }
87  p <<= 1;
88  }
89  if (it_cnt & 1)
90  out = m_copy(Z(it_cnt),out);
91  }
92 
93  return out;
94 
95 #undef Z
96 }
97 
98 /* m_pow -- computes integer powers of a square matrix A, A^p */
99 MAT *m_pow(A, p, out)
100 MAT *A, *out;
101 int p;
102 {
103  static MAT *wkspace, *tmp;
104 
105  if ( ! A )
106  error(E_NULL,"m_pow");
107  if ( A->m != A->n )
108  error(E_SQUARE,"m_pow");
109 
110  wkspace = m_resize(wkspace,A->m,A->n);
111  MEM_STAT_REG(wkspace,TYPE_MAT);
112  if ( p < 0 )
113  {
114  tmp = m_resize(tmp,A->m,A->n);
115  MEM_STAT_REG(tmp,TYPE_MAT);
116  tracecatch(m_inverse(A,tmp),"m_pow");
117  return _m_pow(tmp, -p, wkspace, out);
118  }
119  else
120  return _m_pow(A, p, wkspace, out);
121 
122 }
123 
124 /**************************************************/
125 
126 /* _m_exp -- compute matrix exponential of A and save it in out
127  -- uses Pade approximation followed by repeated squaring
128  -- eps is the tolerance used for the Pade approximation
129  -- A is not changed
130  -- q_out - degree of the Pade approximation (q_out,q_out)
131  -- j_out - the power of 2 for scaling the matrix A
132  such that ||A/2^j_out|| <= 0.5
133 */
134 MAT *_m_exp(A,eps,out,q_out,j_out)
135 MAT *A,*out;
136 double eps;
137 int *q_out, *j_out;
138 {
139  static MAT *D = MNULL, *Apow = MNULL, *N = MNULL, *Y = MNULL;
140  static VEC *c1 = VNULL, *tmp = VNULL;
141  VEC y0, y1; /* additional structures */
142  static PERM *pivot = PNULL;
143  int j, k, l, q, r, s, j2max, t;
144  double inf_norm, eqq, power2, c, sign;
145 
146  if ( ! A )
147  error(E_SIZES,"_m_exp");
148  if ( A->m != A->n )
149  error(E_SIZES,"_m_exp");
150  if ( A == out )
151  error(E_INSITU,"_m_exp");
152  if ( eps < 0.0 )
153  error(E_RANGE,"_m_exp");
154  else if (eps == 0.0)
155  eps = MACHEPS;
156 
157  N = m_resize(N,A->m,A->n);
158  D = m_resize(D,A->m,A->n);
159  Apow = m_resize(Apow,A->m,A->n);
160  out = m_resize(out,A->m,A->n);
161 
164  MEM_STAT_REG(Apow,TYPE_MAT);
165 
166  /* normalise A to have ||A||_inf <= 1 */
167  inf_norm = m_norm_inf(A);
168  if (inf_norm <= 0.0) {
169  m_ident(out);
170  *q_out = -1;
171  *j_out = 0;
172  return out;
173  }
174  else {
175  j2max = floor(1+log(inf_norm)/log(2.0));
176  j2max = max(0, j2max);
177  }
178 
179  power2 = 1.0;
180  for ( k = 1; k <= j2max; k++ )
181  power2 *= 2;
182  power2 = 1.0/power2;
183  if ( j2max > 0 )
184  sm_mlt(power2,A,A);
185 
186  /* compute order for polynomial approximation */
187  eqq = 1.0/6.0;
188  for ( q = 1; eqq > eps; q++ )
189  eqq /= 16.0*(2.0*q+1.0)*(2.0*q+3.0);
190 
191  /* construct vector of coefficients */
192  c1 = v_resize(c1,q+1);
194  c1->ve[0] = 1.0;
195  for ( k = 1; k <= q; k++ )
196  c1->ve[k] = c1->ve[k-1]*(q-k+1)/((2*q-k+1)*(double)k);
197 
198  tmp = v_resize(tmp,A->n);
199  MEM_STAT_REG(tmp,TYPE_VEC);
200 
201  s = (int)floor(sqrt((double)q/2.0));
202  if ( s <= 0 ) s = 1;
203  _m_pow(A,s,out,Apow);
204  r = q/s;
205 
206  Y = m_resize(Y,s,A->n);
208  /* y0 and y1 are pointers to rows of Y, N and D */
209  y0.dim = y0.max_dim = A->n;
210  y1.dim = y1.max_dim = A->n;
211 
212  m_zero(Y);
213  m_zero(N);
214  m_zero(D);
215 
216  for( j = 0; j < A->n; j++ )
217  {
218  if (j > 0)
219  Y->me[0][j-1] = 0.0;
220  y0.ve = Y->me[0];
221  y0.ve[j] = 1.0;
222  for ( k = 0; k < s-1; k++ )
223  {
224  y1.ve = Y->me[k+1];
225  mv_mlt(A,&y0,&y1);
226  y0.ve = y1.ve;
227  }
228 
229  y0.ve = N->me[j];
230  y1.ve = D->me[j];
231  t = s*r;
232  for ( l = 0; l <= q-t; l++ )
233  {
234  c = c1->ve[t+l];
235  sign = ((t+l) & 1) ? -1.0 : 1.0;
236  __mltadd__(y0.ve,Y->me[l],c, Y->n);
237  __mltadd__(y1.ve,Y->me[l],c*sign,Y->n);
238  }
239 
240  for (k=1; k <= r; k++)
241  {
242  v_copy(mv_mlt(Apow,&y0,tmp),&y0);
243  v_copy(mv_mlt(Apow,&y1,tmp),&y1);
244  t = s*(r-k);
245  for (l=0; l < s; l++)
246  {
247  c = c1->ve[t+l];
248  sign = ((t+l) & 1) ? -1.0 : 1.0;
249  __mltadd__(y0.ve,Y->me[l],c, Y->n);
250  __mltadd__(y1.ve,Y->me[l],c*sign,Y->n);
251  }
252  }
253  }
254 
255  pivot = px_resize(pivot,A->m);
256  MEM_STAT_REG(pivot,TYPE_PERM);
257 
258  /* note that N and D are transposed,
259  therefore we use LUTsolve;
260  out is saved row-wise, and must be transposed
261  after this */
262 
263  LUfactor(D,pivot);
264  for (k=0; k < A->n; k++)
265  {
266  y0.ve = N->me[k];
267  y1.ve = out->me[k];
268  LUTsolve(D,pivot,&y0,&y1);
269  }
270  m_transp(out,out);
271 
272 
273  /* Use recursive squaring to turn the normalised exponential to the
274  true exponential */
275 
276 #define Z(k) ((k) & 1 ? Apow : out)
277 
278  for( k = 1; k <= j2max; k++)
279  m_mlt(Z(k-1),Z(k-1),Z(k));
280 
281  if (Z(k) == out)
282  m_copy(Apow,out);
283 
284  /* output parameters */
285  *j_out = j2max;
286  *q_out = q;
287 
288  /* restore the matrix A */
289  sm_mlt(1.0/power2,A,A);
290  return out;
291 
292 #undef Z
293 }
294 
295 
296 /* simple interface for _m_exp */
297 MAT *m_exp(A,eps,out)
298 MAT *A,*out;
299 double eps;
300 {
301  int q_out, j_out;
302 
303  return _m_exp(A,eps,out,&q_out,&j_out);
304 }
305 
306 
307 /*--------------------------------*/
308 
309 /* m_poly -- computes sum_i a[i].A^i, where i=0,1,...dim(a);
310  -- uses C. Van Loan's fast and memory efficient method */
311 MAT *m_poly(A,a,out)
312 MAT *A,*out;
313 VEC *a;
314 {
315  static MAT *Apow = MNULL, *Y = MNULL;
316  static VEC *tmp;
317  VEC y0, y1; /* additional vectors */
318  int j, k, l, q, r, s, t;
319 
320  if ( ! A || ! a )
321  error(E_NULL,"m_poly");
322  if ( A->m != A->n )
323  error(E_SIZES,"m_poly");
324  if ( A == out )
325  error(E_INSITU,"m_poly");
326 
327  out = m_resize(out,A->m,A->n);
328  Apow = m_resize(Apow,A->m,A->n);
329  MEM_STAT_REG(Apow,TYPE_MAT);
330  tmp = v_resize(tmp,A->n);
331  MEM_STAT_REG(tmp,TYPE_VEC);
332 
333  q = a->dim - 1;
334  if ( q == 0 ) {
335  m_zero(out);
336  for (j=0; j < out->n; j++)
337  out->me[j][j] = a->ve[0];
338  return out;
339  }
340  else if ( q == 1) {
341  sm_mlt(a->ve[1],A,out);
342  for (j=0; j < out->n; j++)
343  out->me[j][j] += a->ve[0];
344  return out;
345  }
346 
347  s = (int)floor(sqrt((double)q/2.0));
348  if ( s <= 0 ) s = 1;
349  _m_pow(A,s,out,Apow);
350  r = q/s;
351 
352  Y = m_resize(Y,s,A->n);
354  /* pointers to rows of Y */
355  y0.dim = y0.max_dim = A->n;
356  y1.dim = y1.max_dim = A->n;
357 
358  m_zero(Y);
359  m_zero(out);
360 
361 #define Z(k) ((k) & 1 ? tmp : &y0)
362 #define ZZ(k) ((k) & 1 ? tmp->ve : y0.ve)
363 
364  for( j = 0; j < A->n; j++)
365  {
366  if( j > 0 )
367  Y->me[0][j-1] = 0.0;
368  Y->me[0][j] = 1.0;
369 
370  y0.ve = Y->me[0];
371  for (k = 0; k < s-1; k++)
372  {
373  y1.ve = Y->me[k+1];
374  mv_mlt(A,&y0,&y1);
375  y0.ve = y1.ve;
376  }
377 
378  y0.ve = out->me[j];
379 
380  t = s*r;
381  for ( l = 0; l <= q-t; l++ )
382  __mltadd__(y0.ve,Y->me[l],a->ve[t+l],Y->n);
383 
384  for (k=1; k <= r; k++)
385  {
386  mv_mlt(Apow,Z(k-1),Z(k));
387  t = s*(r-k);
388  for (l=0; l < s; l++)
389  __mltadd__(ZZ(k),Y->me[l],a->ve[t+l],Y->n);
390  }
391  if (Z(k) == &y0) v_copy(tmp,&y0);
392  }
393 
394  m_transp(out,out);
395 
396  return out;
397 }
398 
399 
double t
Definition: cvodeobj.cpp:59
#define c
#define E_SQUARE
Definition: err.h:103
#define error(err_num, fn_name)
Definition: err.h:73
#define tracecatch(ok_part, function)
Definition: err.h:166
#define E_RANGE
Definition: err.h:104
#define E_NULL
Definition: err.h:102
#define E_NEG
Definition: err.h:114
#define E_INSITU
Definition: err.h:106
#define E_SIZES
Definition: err.h:95
static Object ** v_resize(void *v)
Definition: ivocvect.cpp:1383
MAT * LUfactor(MAT *A, PERM *pivot)
Definition: lufactor.c:46
VEC * LUTsolve(MAT *LU, PERM *pivot, VEC *b, VEC *x)
Definition: lufactor.c:159
void __mltadd__(Real *dp1, Real *dp2, double s, int len)
Definition: machine.c:76
#define MACHEPS
Definition: machine.h:213
MAT * sm_mlt(double scalar, MAT *matrix, MAT *out)
Definition: matop.c:241
VEC * mv_mlt(MAT *A, VEC *b, VEC *out)
Definition: matop.c:205
MAT * m_transp(MAT *in, MAT *out)
Definition: matop.c:300
MAT * m_mlt(MAT *A, MAT *B, MAT *OUT)
Definition: matop.c:88
static Object ** m_zero(void *v)
Definition: matrix.cpp:512
static Object ** m_ident(void *v)
Definition: matrix.cpp:518
static Object ** m_resize(void *v)
Definition: matrix.cpp:191
static Object ** m_inverse(void *v)
Definition: matrix.cpp:539
#define VNULL
Definition: matrix.h:631
#define PNULL
Definition: matrix.h:633
#define v_copy(in, out)
Definition: matrix.h:404
#define m_copy(in, out)
Definition: matrix.h:403
PERM * px_resize(PERM *, int)
Definition: memory.c:399
#define MNULL
Definition: matrix.h:632
double m_norm_inf(MAT *A)
#define max(a, b)
Definition: matrix.h:154
#define TYPE_PERM
Definition: meminfo.h:51
#define TYPE_VEC
Definition: meminfo.h:52
#define TYPE_MAT
Definition: meminfo.h:49
#define MEM_STAT_REG(var, type)
Definition: meminfo.h:133
MAT * _m_pow(MAT *A, int p, MAT *tmp, MAT *out)
Definition: mfunc.c:45
MAT * m_pow(MAT *A, int p, MAT *out)
Definition: mfunc.c:99
#define Z(k)
MAT * m_exp(MAT *A, double eps, MAT *out)
Definition: mfunc.c:297
#define ZZ(k)
MAT * m_poly(MAT *A, VEC *a, MAT *out)
Definition: mfunc.c:311
MAT * _m_exp(MAT *A, double eps, MAT *out, int *q_out, int *j_out)
Definition: mfunc.c:134
static char rcsid[]
Definition: mfunc.c:39
sqrt
Definition: extdef.h:3
floor
Definition: extdef.h:4
log
Definition: extdef.h:4
#define D(i)
Definition: multisplit.cpp:65
#define A(i)
Definition: multisplit.cpp:63
size_t q
size_t p
size_t j
static philox4x32_key_t k
Definition: nrnran123.cpp:11
#define sign(x)
Definition: qrfactor.c:52
Definition: matrix.h:73
Definition: matrix.h:87
Definition: matrix.h:67
u_int dim
Definition: matrix.h:68
u_int max_dim
Definition: matrix.h:68
Real * ve
Definition: matrix.h:69