NEURON
zschur.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  File containing routines for computing the Schur decomposition
29  of a complex non-symmetric matrix
30  See also: hessen.c
31  Complex version
32 */
33 
34 
35 #include <stdio.h>
36 #include "zmatrix.h"
37 #include "zmatrix2.h"
38 #include <math.h>
39 
40 static char rcsid[] = "zschur.c,v 1.1 1997/12/04 17:56:16 hines Exp";
41 
42 #define is_zero(z) ((z).re == 0.0 && (z).im == 0.0)
43 #define b2s(t_or_f) ((t_or_f) ? "TRUE" : "FALSE")
44 
45 
46 /* zschur -- computes the Schur decomposition of the matrix A in situ
47  -- optionally, gives Q matrix such that Q^*.A.Q is upper triangular
48  -- returns upper triangular Schur matrix */
50 ZMAT *A, *Q;
51 {
52  int i, j, iter, k, k_min, k_max, k_tmp, n, split;
53  Real c;
54  complex det, discrim, lambda, lambda0, lambda1, s, sum, ztmp;
55  complex x, y; /* for chasing algorithm */
56  complex **A_me;
57  static ZVEC *diag=ZVNULL;
58 
59  if ( ! A )
60  error(E_NULL,"zschur");
61  if ( A->m != A->n || ( Q && Q->m != Q->n ) )
62  error(E_SQUARE,"zschur");
63  if ( Q != ZMNULL && Q->m != A->m )
64  error(E_SIZES,"zschur");
65  n = A->n;
66  diag = zv_resize(diag,A->n);
67  MEM_STAT_REG(diag,TYPE_ZVEC);
68  /* compute Hessenberg form */
69  zHfactor(A,diag);
70 
71  /* save Q if necessary, and make A explicitly Hessenberg */
72  zHQunpack(A,diag,Q,A);
73 
74  k_min = 0; A_me = A->me;
75 
76  while ( k_min < n )
77  {
78  /* find k_max to suit:
79  submatrix k_min..k_max should be irreducible */
80  k_max = n-1;
81  for ( k = k_min; k < k_max; k++ )
82  if ( is_zero(A_me[k+1][k]) )
83  { k_max = k; break; }
84 
85  if ( k_max <= k_min )
86  {
87  k_min = k_max + 1;
88  continue; /* outer loop */
89  }
90 
91  /* now have r x r block with r >= 2:
92  apply Francis QR step until block splits */
93  split = FALSE; iter = 0;
94  while ( ! split )
95  {
96  complex a00, a01, a10, a11;
97  iter++;
98 
99  /* set up Wilkinson/Francis complex shift */
100  /* use the smallest eigenvalue of the bottom 2 x 2 submatrix */
101  k_tmp = k_max - 1;
102 
103  a00 = A_me[k_tmp][k_tmp];
104  a01 = A_me[k_tmp][k_max];
105  a10 = A_me[k_max][k_tmp];
106  a11 = A_me[k_max][k_max];
107  ztmp.re = 0.5*(a00.re - a11.re);
108  ztmp.im = 0.5*(a00.im - a11.im);
109  discrim = zsqrt(zadd(zmlt(ztmp,ztmp),zmlt(a01,a10)));
110  sum.re = 0.5*(a00.re + a11.re);
111  sum.im = 0.5*(a00.im + a11.im);
112  lambda0 = zadd(sum,discrim);
113  lambda1 = zsub(sum,discrim);
114  det = zsub(zmlt(a00,a11),zmlt(a01,a10));
115 
116  if ( is_zero(lambda0) && is_zero(lambda1) )
117  {
118  lambda.re = lambda.im = 0.0;
119  }
120  else if ( zabs(lambda0) > zabs(lambda1) )
121  lambda = zdiv(det,lambda0);
122  else
123  lambda = zdiv(det,lambda1);
124 
125  /* perturb shift if convergence is slow */
126  if ( (iter % 10) == 0 )
127  {
128  lambda.re += iter*0.02;
129  lambda.im += iter*0.02;
130  }
131 
132  /* set up Householder transformations */
133  k_tmp = k_min + 1;
134 
135  x = zsub(A->me[k_min][k_min],lambda);
136  y = A->me[k_min+1][k_min];
137 
138  /* use Givens' rotations to "chase" off-Hessenberg entry */
139  for ( k = k_min; k <= k_max-1; k++ )
140  {
141  zgivens(x,y,&c,&s);
142  zrot_cols(A,k,k+1,c,s,A);
143  zrot_rows(A,k,k+1,c,s,A);
144  if ( Q != ZMNULL )
145  zrot_cols(Q,k,k+1,c,s,Q);
146 
147  /* zero things that should be zero */
148  if ( k > k_min )
149  A->me[k+1][k-1].re = A->me[k+1][k-1].im = 0.0;
150 
151  /* get next entry to chase along sub-diagonal */
152  x = A->me[k+1][k];
153  if ( k <= k_max - 2 )
154  y = A->me[k+2][k];
155  else
156  y.re = y.im = 0.0;
157  }
158 
159  for ( k = k_min; k <= k_max-2; k++ )
160  {
161  /* zero appropriate sub-diagonals */
162  A->me[k+2][k].re = A->me[k+2][k].im = 0.0;
163  }
164 
165  /* test to see if matrix should split */
166  for ( k = k_min; k < k_max; k++ )
167  if ( zabs(A_me[k+1][k]) < MACHEPS*
168  (zabs(A_me[k][k])+zabs(A_me[k+1][k+1])) )
169  {
170  A_me[k+1][k].re = A_me[k+1][k].im = 0.0;
171  split = TRUE;
172  }
173 
174  }
175  }
176 
177  /* polish up A by zeroing strictly lower triangular elements
178  and small sub-diagonal elements */
179  for ( i = 0; i < A->m; i++ )
180  for ( j = 0; j < i-1; j++ )
181  A_me[i][j].re = A_me[i][j].im = 0.0;
182  for ( i = 0; i < A->m - 1; i++ )
183  if ( zabs(A_me[i+1][i]) < MACHEPS*
184  (zabs(A_me[i][i])+zabs(A_me[i+1][i+1])) )
185  A_me[i+1][i].re = A_me[i+1][i].im = 0.0;
186 
187  return A;
188 }
189 
190 
191 #if 0
192 /* schur_vecs -- returns eigenvectors computed from the real Schur
193  decomposition of a matrix
194  -- T is the block upper triangular Schur matrix
195  -- Q is the orthognal matrix where A = Q.T.Q^T
196  -- if Q is null, the eigenvectors of T are returned
197  -- X_re is the real part of the matrix of eigenvectors,
198  and X_im is the imaginary part of the matrix.
199  -- X_re is returned */
200 MAT *schur_vecs(T,Q,X_re,X_im)
201 MAT *T, *Q, *X_re, *X_im;
202 {
203  int i, j, limit;
204  Real t11_re, t11_im, t12, t21, t22_re, t22_im;
205  Real l_re, l_im, det_re, det_im, invdet_re, invdet_im,
206  val1_re, val1_im, val2_re, val2_im,
207  tmp_val1_re, tmp_val1_im, tmp_val2_re, tmp_val2_im, **T_me;
208  Real sum, diff, discrim, magdet, norm, scale;
209  static VEC *tmp1_re=VNULL, *tmp1_im=VNULL,
210  *tmp2_re=VNULL, *tmp2_im=VNULL;
211 
212  if ( ! T || ! X_re )
213  error(E_NULL,"schur_vecs");
214  if ( T->m != T->n || X_re->m != X_re->n ||
215  ( Q != MNULL && Q->m != Q->n ) ||
216  ( X_im != MNULL && X_im->m != X_im->n ) )
217  error(E_SQUARE,"schur_vecs");
218  if ( T->m != X_re->m ||
219  ( Q != MNULL && T->m != Q->m ) ||
220  ( X_im != MNULL && T->m != X_im->m ) )
221  error(E_SIZES,"schur_vecs");
222 
223  tmp1_re = v_resize(tmp1_re,T->m);
224  tmp1_im = v_resize(tmp1_im,T->m);
225  tmp2_re = v_resize(tmp2_re,T->m);
226  tmp2_im = v_resize(tmp2_im,T->m);
227  MEM_STAT_REG(tmp1_re,TYPE_VEC);
228  MEM_STAT_REG(tmp1_im,TYPE_VEC);
229  MEM_STAT_REG(tmp2_re,TYPE_VEC);
230  MEM_STAT_REG(tmp2_im,TYPE_VEC);
231 
232  T_me = T->me;
233  i = 0;
234  while ( i < T->m )
235  {
236  if ( i+1 < T->m && T->me[i+1][i] != 0.0 )
237  { /* complex eigenvalue */
238  sum = 0.5*(T_me[i][i]+T_me[i+1][i+1]);
239  diff = 0.5*(T_me[i][i]-T_me[i+1][i+1]);
240  discrim = diff*diff + T_me[i][i+1]*T_me[i+1][i];
241  l_re = l_im = 0.0;
242  if ( discrim < 0.0 )
243  { /* yes -- complex e-vals */
244  l_re = sum;
245  l_im = sqrt(-discrim);
246  }
247  else /* not correct Real Schur form */
248  error(E_RANGE,"schur_vecs");
249  }
250  else
251  {
252  l_re = T_me[i][i];
253  l_im = 0.0;
254  }
255 
256  v_zero(tmp1_im);
257  v_rand(tmp1_re);
258  sv_mlt(MACHEPS,tmp1_re,tmp1_re);
259 
260  /* solve (T-l.I)x = tmp1 */
261  limit = ( l_im != 0.0 ) ? i+1 : i;
262  /* printf("limit = %d\n",limit); */
263  for ( j = limit+1; j < T->m; j++ )
264  tmp1_re->ve[j] = 0.0;
265  j = limit;
266  while ( j >= 0 )
267  {
268  if ( j > 0 && T->me[j][j-1] != 0.0 )
269  { /* 2 x 2 diagonal block */
270  /* printf("checkpoint A\n"); */
271  val1_re = tmp1_re->ve[j-1] -
272  __ip__(&(tmp1_re->ve[j+1]),&(T->me[j-1][j+1]),limit-j);
273  /* printf("checkpoint B\n"); */
274  val1_im = tmp1_im->ve[j-1] -
275  __ip__(&(tmp1_im->ve[j+1]),&(T->me[j-1][j+1]),limit-j);
276  /* printf("checkpoint C\n"); */
277  val2_re = tmp1_re->ve[j] -
278  __ip__(&(tmp1_re->ve[j+1]),&(T->me[j][j+1]),limit-j);
279  /* printf("checkpoint D\n"); */
280  val2_im = tmp1_im->ve[j] -
281  __ip__(&(tmp1_im->ve[j+1]),&(T->me[j][j+1]),limit-j);
282  /* printf("checkpoint E\n"); */
283 
284  t11_re = T_me[j-1][j-1] - l_re;
285  t11_im = - l_im;
286  t22_re = T_me[j][j] - l_re;
287  t22_im = - l_im;
288  t12 = T_me[j-1][j];
289  t21 = T_me[j][j-1];
290 
291  scale = fabs(T_me[j-1][j-1]) + fabs(T_me[j][j]) +
292  fabs(t12) + fabs(t21) + fabs(l_re) + fabs(l_im);
293 
294  det_re = t11_re*t22_re - t11_im*t22_im - t12*t21;
295  det_im = t11_re*t22_im + t11_im*t22_re;
296  magdet = det_re*det_re+det_im*det_im;
297  if ( sqrt(magdet) < MACHEPS*scale )
298  {
299  det_re = MACHEPS*scale;
300  magdet = det_re*det_re+det_im*det_im;
301  }
302  invdet_re = det_re/magdet;
303  invdet_im = - det_im/magdet;
304  tmp_val1_re = t22_re*val1_re-t22_im*val1_im-t12*val2_re;
305  tmp_val1_im = t22_im*val1_re+t22_re*val1_im-t12*val2_im;
306  tmp_val2_re = t11_re*val2_re-t11_im*val2_im-t21*val1_re;
307  tmp_val2_im = t11_im*val2_re+t11_re*val2_im-t21*val1_im;
308  tmp1_re->ve[j-1] = invdet_re*tmp_val1_re -
309  invdet_im*tmp_val1_im;
310  tmp1_im->ve[j-1] = invdet_im*tmp_val1_re +
311  invdet_re*tmp_val1_im;
312  tmp1_re->ve[j] = invdet_re*tmp_val2_re -
313  invdet_im*tmp_val2_im;
314  tmp1_im->ve[j] = invdet_im*tmp_val2_re +
315  invdet_re*tmp_val2_im;
316  j -= 2;
317  }
318  else
319  {
320  t11_re = T_me[j][j] - l_re;
321  t11_im = - l_im;
322  magdet = t11_re*t11_re + t11_im*t11_im;
323  scale = fabs(T_me[j][j]) + fabs(l_re);
324  if ( sqrt(magdet) < MACHEPS*scale )
325  {
326  t11_re = MACHEPS*scale;
327  magdet = t11_re*t11_re + t11_im*t11_im;
328  }
329  invdet_re = t11_re/magdet;
330  invdet_im = - t11_im/magdet;
331  /* printf("checkpoint F\n"); */
332  val1_re = tmp1_re->ve[j] -
333  __ip__(&(tmp1_re->ve[j+1]),&(T->me[j][j+1]),limit-j);
334  /* printf("checkpoint G\n"); */
335  val1_im = tmp1_im->ve[j] -
336  __ip__(&(tmp1_im->ve[j+1]),&(T->me[j][j+1]),limit-j);
337  /* printf("checkpoint H\n"); */
338  tmp1_re->ve[j] = invdet_re*val1_re - invdet_im*val1_im;
339  tmp1_im->ve[j] = invdet_im*val1_re + invdet_re*val1_im;
340  j -= 1;
341  }
342  }
343 
344  norm = v_norm_inf(tmp1_re) + v_norm_inf(tmp1_im);
345  sv_mlt(1/norm,tmp1_re,tmp1_re);
346  if ( l_im != 0.0 )
347  sv_mlt(1/norm,tmp1_im,tmp1_im);
348  mv_mlt(Q,tmp1_re,tmp2_re);
349  if ( l_im != 0.0 )
350  mv_mlt(Q,tmp1_im,tmp2_im);
351  if ( l_im != 0.0 )
352  norm = sqrt(in_prod(tmp2_re,tmp2_re)+in_prod(tmp2_im,tmp2_im));
353  else
354  norm = v_norm2(tmp2_re);
355  sv_mlt(1/norm,tmp2_re,tmp2_re);
356  if ( l_im != 0.0 )
357  sv_mlt(1/norm,tmp2_im,tmp2_im);
358 
359  if ( l_im != 0.0 )
360  {
361  if ( ! X_im )
362  error(E_NULL,"schur_vecs");
363  set_col(X_re,i,tmp2_re);
364  set_col(X_im,i,tmp2_im);
365  sv_mlt(-1.0,tmp2_im,tmp2_im);
366  set_col(X_re,i+1,tmp2_re);
367  set_col(X_im,i+1,tmp2_im);
368  i += 2;
369  }
370  else
371  {
372  set_col(X_re,i,tmp2_re);
373  if ( X_im != MNULL )
374  set_col(X_im,i,tmp1_im); /* zero vector */
375  i += 1;
376  }
377  }
378 
379  return X_re;
380 }
381 
382 #endif
383 
#define c
#define TRUE
Definition: err.c:57
#define FALSE
Definition: err.c:56
#define E_SQUARE
Definition: err.h:103
#define error(err_num, fn_name)
Definition: err.h:73
#define E_RANGE
Definition: err.h:104
#define E_NULL
Definition: err.h:102
#define E_SIZES
Definition: err.h:95
#define diag(s)
Definition: fmenu.cpp:192
VEC * v_zero(VEC *x)
Definition: init.c:40
VEC * v_rand(VEC *x)
Definition: init.c:220
static Object ** v_resize(void *v)
Definition: ivocvect.cpp:1383
double __ip__(Real *dp1, Real *dp2, int len)
Definition: machine.c:40
#define Real
Definition: machine.h:189
#define MACHEPS
Definition: machine.h:213
VEC * mv_mlt(MAT *A, VEC *b, VEC *out)
Definition: matop.c:205
MAT * schur_vecs(MAT *T, MAT *Q, MAT *X_re, MAT *X_im)
Definition: schur.c:488
#define VNULL
Definition: matrix.h:631
#define set_col(mat, col, vec)
Definition: matrix.h:564
#define in_prod(a, b)
Definition: matrix.h:485
#define v_norm_inf(x)
Definition: matrix.h:512
#define MNULL
Definition: matrix.h:632
VEC * sv_mlt(double, VEC *, VEC *)
#define v_norm2(x)
Definition: matrix.h:511
#define i
Definition: md1redef.h:12
#define TYPE_VEC
Definition: meminfo.h:52
#define MEM_STAT_REG(var, type)
Definition: meminfo.h:133
sqrt
Definition: extdef.h:3
fabs
Definition: extdef.h:3
#define A(i)
Definition: multisplit.cpp:63
int const size_t const size_t n
Definition: nrngsl.h:11
size_t j
static philox4x32_key_t k
Definition: nrnran123.cpp:11
double T
Definition: rbtqueue.cpp:25
Definition: matrix.h:73
Definition: matrix.h:67
Real * ve
Definition: matrix.h:69
Definition: zmatrix.h:50
Definition: zmatrix.h:44
Real re
Definition: zmatrix.h:40
Real im
Definition: zmatrix.h:40
complex zdiv(complex z1, complex z2)
Definition: zfunc.c:166
complex zmlt(complex z1, complex z2)
Definition: zfunc.c:121
complex zsub(complex z1, complex z2)
Definition: zfunc.c:107
complex zadd(complex z1, complex z2)
Definition: zfunc.c:93
complex zsqrt(complex z)
Definition: zfunc.c:175
double zabs(complex z)
Definition: zfunc.c:65
void zgivens(complex x, complex y, Real *c, complex *s)
Definition: zgivens.c:52
ZMAT * zrot_rows(ZMAT *mat, int i, int k, double c, complex s, ZMAT *out)
Definition: zgivens.c:114
ZMAT * zrot_cols(ZMAT *mat, int i, int k, double c, complex s, ZMAT *out)
Definition: zgivens.c:154
ZMAT * zHfactor(ZMAT *A, ZVEC *diag)
Definition: zhessen.c:45
ZMAT * zHQunpack(ZMAT *HQ, ZVEC *diag, ZMAT *Q, ZMAT *H)
Definition: zhessen.c:86
ZVEC * zv_resize(ZVEC *x, int new_dim)
Definition: zmemory.c:362
#define ZVNULL
Definition: zmatrix.h:57
#define ZMNULL
Definition: zmatrix.h:58
ZMAT * zschur(ZMAT *A, ZMAT *Q)
Definition: zschur.c:49
#define is_zero(z)
Definition: zschur.c:42
static char rcsid[]
Definition: zschur.c:40