NEURON
svd.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  File containing routines for computing the SVD of matrices
30 */
31 
32 #include <stdio.h>
33 #include "matrix.h"
34 #include "matrix2.h"
35 #include <math.h>
36 
37 
38 static char rcsid[] = "svd.c,v 1.1 1997/12/04 17:55:56 hines Exp";
39 
40 
41 
42 #define sgn(x) ((x) >= 0 ? 1 : -1)
43 #define MAX_STACK 100
44 
45 /* fixsvd -- fix minor details about SVD
46  -- make singular values non-negative
47  -- sort singular values in decreasing order
48  -- variables as for bisvd()
49  -- no argument checking */
50 static void fixsvd(d,U,V)
51 VEC *d;
52 MAT *U, *V;
53 {
54  int i, j, k, l, r, stack[MAX_STACK], sp;
55  Real tmp, v;
56 
57  /* make singular values non-negative */
58  for ( i = 0; i < d->dim; i++ )
59  if ( d->ve[i] < 0.0 )
60  {
61  d->ve[i] = - d->ve[i];
62  if ( U != MNULL )
63  for ( j = 0; j < U->m; j++ )
64  U->me[i][j] = - U->me[i][j];
65  }
66 
67  /* sort singular values */
68  /* nonrecursive implementation of quicksort due to R.Sedgewick,
69  "Algorithms in C", p. 122 (1990) */
70  sp = -1;
71  l = 0; r = d->dim - 1;
72  for ( ; ; )
73  {
74  while ( r > l )
75  {
76  /* i = partition(d->ve,l,r) */
77  v = d->ve[r];
78 
79  i = l - 1; j = r;
80  for ( ; ; )
81  { /* inequalities are "backwards" for **decreasing** order */
82  while ( d->ve[++i] > v )
83  ;
84  while ( i < j && d->ve[--j] < v )
85  ;
86  if ( i >= j )
87  break;
88  /* swap entries in d->ve */
89  tmp = d->ve[i]; d->ve[i] = d->ve[j]; d->ve[j] = tmp;
90  /* swap rows of U & V as well */
91  if ( U != MNULL )
92  for ( k = 0; k < U->n; k++ )
93  {
94  tmp = U->me[i][k];
95  U->me[i][k] = U->me[j][k];
96  U->me[j][k] = tmp;
97  }
98  if ( V != MNULL )
99  for ( k = 0; k < V->n; k++ )
100  {
101  tmp = V->me[i][k];
102  V->me[i][k] = V->me[j][k];
103  V->me[j][k] = tmp;
104  }
105  }
106  tmp = d->ve[i]; d->ve[i] = d->ve[r]; d->ve[r] = tmp;
107  if ( U != MNULL )
108  for ( k = 0; k < U->n; k++ )
109  {
110  tmp = U->me[i][k];
111  U->me[i][k] = U->me[r][k];
112  U->me[r][k] = tmp;
113  }
114  if ( V != MNULL )
115  for ( k = 0; k < V->n; k++ )
116  {
117  tmp = V->me[i][k];
118  V->me[i][k] = V->me[r][k];
119  V->me[r][k] = tmp;
120  }
121  /* end i = partition(...) */
122  if ( i - l > r - i )
123  { stack[++sp] = l; stack[++sp] = i-1; l = i+1; }
124  else
125  { stack[++sp] = i+1; stack[++sp] = r; r = i-1; }
126  }
127  if ( sp < 0 )
128  break;
129  r = stack[sp--]; l = stack[sp--];
130  }
131 }
132 
133 
134 /* bisvd -- svd of a bidiagonal m x n matrix represented by d (diagonal) and
135  f (super-diagonals)
136  -- returns with d set to the singular values, f zeroed
137  -- if U, V non-NULL, the orthogonal operations are accumulated
138  in U, V; if U, V == I on entry, then SVD == U^T.A.V
139  where A is initial matrix
140  -- returns d on exit */
141 VEC *bisvd(d,f,U,V)
142 VEC *d, *f;
143 MAT *U, *V;
144 {
145  int i, j, n;
146  int i_min, i_max, split;
147  Real c, s, shift, size, z;
148  Real d_tmp, diff, t11, t12, t22, *d_ve, *f_ve;
149 
150  if ( ! d || ! f )
151  error(E_NULL,"bisvd");
152  if ( d->dim != f->dim + 1 )
153  error(E_SIZES,"bisvd");
154  n = d->dim;
155  if ( ( U && U->n < n ) || ( V && V->m < n ) )
156  error(E_SIZES,"bisvd");
157  if ( ( U && U->m != U->n ) || ( V && V->m != V->n ) )
158  error(E_SQUARE,"bisvd");
159 
160 
161  if ( n == 1 )
162  return d;
163  d_ve = d->ve; f_ve = f->ve;
164 
165  size = v_norm_inf(d) + v_norm_inf(f);
166 
167  i_min = 0;
168  while ( i_min < n ) /* outer while loop */
169  {
170  /* find i_max to suit;
171  submatrix i_min..i_max should be irreducible */
172  i_max = n - 1;
173  for ( i = i_min; i < n - 1; i++ )
174  if ( d_ve[i] == 0.0 || f_ve[i] == 0.0 )
175  { i_max = i;
176  if ( f_ve[i] != 0.0 )
177  {
178  /* have to ``chase'' f[i] element out of matrix */
179  z = f_ve[i]; f_ve[i] = 0.0;
180  for ( j = i; j < n-1 && z != 0.0; j++ )
181  {
182  givens(d_ve[j+1],z, &c, &s);
183  s = -s;
184  d_ve[j+1] = c*d_ve[j+1] - s*z;
185  if ( j+1 < n-1 )
186  {
187  z = s*f_ve[j+1];
188  f_ve[j+1] = c*f_ve[j+1];
189  }
190  if ( U )
191  rot_rows(U,i,j+1,c,s,U);
192  }
193  }
194  break;
195  }
196  if ( i_max <= i_min )
197  {
198  i_min = i_max + 1;
199  continue;
200  }
201  /* printf("bisvd: i_min = %d, i_max = %d\n",i_min,i_max); */
202 
203  split = FALSE;
204  while ( ! split )
205  {
206  /* compute shift */
207  t11 = d_ve[i_max-1]*d_ve[i_max-1] +
208  (i_max > i_min+1 ? f_ve[i_max-2]*f_ve[i_max-2] : 0.0);
209  t12 = d_ve[i_max-1]*f_ve[i_max-1];
210  t22 = d_ve[i_max]*d_ve[i_max] + f_ve[i_max-1]*f_ve[i_max-1];
211  /* use e-val of [[t11,t12],[t12,t22]] matrix
212  closest to t22 */
213  diff = (t11-t22)/2;
214  shift = t22 - t12*t12/(diff +
215  sgn(diff)*sqrt(diff*diff+t12*t12));
216 
217  /* initial Givens' rotation */
218  givens(d_ve[i_min]*d_ve[i_min]-shift,
219  d_ve[i_min]*f_ve[i_min], &c, &s);
220 
221  /* do initial Givens' rotations */
222  d_tmp = c*d_ve[i_min] + s*f_ve[i_min];
223  f_ve[i_min] = c*f_ve[i_min] - s*d_ve[i_min];
224  d_ve[i_min] = d_tmp;
225  z = s*d_ve[i_min+1];
226  d_ve[i_min+1] = c*d_ve[i_min+1];
227  if ( V )
228  rot_rows(V,i_min,i_min+1,c,s,V);
229  /* 2nd Givens' rotation */
230  givens(d_ve[i_min],z, &c, &s);
231  d_ve[i_min] = c*d_ve[i_min] + s*z;
232  d_tmp = c*d_ve[i_min+1] - s*f_ve[i_min];
233  f_ve[i_min] = s*d_ve[i_min+1] + c*f_ve[i_min];
234  d_ve[i_min+1] = d_tmp;
235  if ( i_min+1 < i_max )
236  {
237  z = s*f_ve[i_min+1];
238  f_ve[i_min+1] = c*f_ve[i_min+1];
239  }
240  if ( U )
241  rot_rows(U,i_min,i_min+1,c,s,U);
242 
243  for ( i = i_min+1; i < i_max; i++ )
244  {
245  /* get Givens' rotation for zeroing z */
246  givens(f_ve[i-1],z, &c, &s);
247  f_ve[i-1] = c*f_ve[i-1] + s*z;
248  d_tmp = c*d_ve[i] + s*f_ve[i];
249  f_ve[i] = c*f_ve[i] - s*d_ve[i];
250  d_ve[i] = d_tmp;
251  z = s*d_ve[i+1];
252  d_ve[i+1] = c*d_ve[i+1];
253  if ( V )
254  rot_rows(V,i,i+1,c,s,V);
255  /* get 2nd Givens' rotation */
256  givens(d_ve[i],z, &c, &s);
257  d_ve[i] = c*d_ve[i] + s*z;
258  d_tmp = c*d_ve[i+1] - s*f_ve[i];
259  f_ve[i] = c*f_ve[i] + s*d_ve[i+1];
260  d_ve[i+1] = d_tmp;
261  if ( i+1 < i_max )
262  {
263  z = s*f_ve[i+1];
264  f_ve[i+1] = c*f_ve[i+1];
265  }
266  if ( U )
267  rot_rows(U,i,i+1,c,s,U);
268  }
269  /* should matrix be split? */
270  for ( i = i_min; i < i_max; i++ )
271  if ( fabs(f_ve[i]) <
272  MACHEPS*(fabs(d_ve[i])+fabs(d_ve[i+1])) )
273  {
274  split = TRUE;
275  f_ve[i] = 0.0;
276  }
277  else if ( fabs(d_ve[i]) < MACHEPS*size )
278  {
279  split = TRUE;
280  d_ve[i] = 0.0;
281  }
282  /* printf("bisvd: d =\n"); v_output(d); */
283  /* printf("bisvd: f = \n"); v_output(f); */
284  }
285  }
286  fixsvd(d,U,V);
287 
288  return d;
289 }
290 
291 /* bifactor -- perform preliminary factorisation for bisvd
292  -- updates U and/or V, which ever is not NULL */
294 MAT *A, *U, *V;
295 {
296  int k;
297  static VEC *tmp1=VNULL, *tmp2=VNULL;
298  Real beta;
299 
300  if ( ! A )
301  error(E_NULL,"bifactor");
302  if ( ( U && ( U->m != U->n ) ) || ( V && ( V->m != V->n ) ) )
303  error(E_SQUARE,"bifactor");
304  if ( ( U && U->m != A->m ) || ( V && V->m != A->n ) )
305  error(E_SIZES,"bifactor");
306  tmp1 = v_resize(tmp1,A->m);
307  tmp2 = v_resize(tmp2,A->n);
308  MEM_STAT_REG(tmp1,TYPE_VEC);
309  MEM_STAT_REG(tmp2,TYPE_VEC);
310 
311  if ( A->m >= A->n )
312  for ( k = 0; k < A->n; k++ )
313  {
314  get_col(A,k,tmp1);
315  hhvec(tmp1,k,&beta,tmp1,&(A->me[k][k]));
316  hhtrcols(A,k,k+1,tmp1,beta);
317  if ( U )
318  hhtrcols(U,k,0,tmp1,beta);
319  if ( k+1 >= A->n )
320  continue;
321  get_row(A,k,tmp2);
322  hhvec(tmp2,k+1,&beta,tmp2,&(A->me[k][k+1]));
323  hhtrrows(A,k+1,k+1,tmp2,beta);
324  if ( V )
325  hhtrcols(V,k+1,0,tmp2,beta);
326  }
327  else
328  for ( k = 0; k < A->m; k++ )
329  {
330  get_row(A,k,tmp2);
331  hhvec(tmp2,k,&beta,tmp2,&(A->me[k][k]));
332  hhtrrows(A,k+1,k,tmp2,beta);
333  if ( V )
334  hhtrcols(V,k,0,tmp2,beta);
335  if ( k+1 >= A->m )
336  continue;
337  get_col(A,k,tmp1);
338  hhvec(tmp1,k+1,&beta,tmp1,&(A->me[k+1][k]));
339  hhtrcols(A,k+1,k+1,tmp1,beta);
340  if ( U )
341  hhtrcols(U,k+1,0,tmp1,beta);
342  }
343 
344  return A;
345 }
346 
347 /* svd -- returns vector of singular values in d
348  -- also updates U and/or V, if one or the other is non-NULL
349  -- destroys A */
350 VEC *svd(A,U,V,d)
351 MAT *A, *U, *V;
352 VEC *d;
353 {
354  static VEC *f=VNULL;
355  int i, limit;
356  MAT *A_tmp;
357 
358  if ( ! A )
359  error(E_NULL,"svd");
360  if ( ( U && ( U->m != U->n ) ) || ( V && ( V->m != V->n ) ) )
361  error(E_SQUARE,"svd");
362  if ( ( U && U->m != A->m ) || ( V && V->m != A->n ) )
363  error(E_SIZES,"svd");
364 
365  A_tmp = m_copy(A,MNULL);
366  if ( U != MNULL )
367  m_ident(U);
368  if ( V != MNULL )
369  m_ident(V);
370  limit = min(A_tmp->m,A_tmp->n);
371  d = v_resize(d,limit);
372  if (f == VNULL && limit == 1) { /* some calloc's do not allow 0 size */
373  f = v_resize(f, limit);
374  }
375  f = v_resize(f,limit-1);
377 
378  bifactor(A_tmp,U,V);
379  if ( A_tmp->m >= A_tmp->n )
380  for ( i = 0; i < limit; i++ )
381  {
382  d->ve[i] = A_tmp->me[i][i];
383  if ( i+1 < limit )
384  f->ve[i] = A_tmp->me[i][i+1];
385  }
386  else
387  for ( i = 0; i < limit; i++ )
388  {
389  d->ve[i] = A_tmp->me[i][i];
390  if ( i+1 < limit )
391  f->ve[i] = A_tmp->me[i+1][i];
392  }
393 
394 
395  if ( A_tmp->m >= A_tmp->n )
396  bisvd(d,f,U,V);
397  else
398  bisvd(d,f,V,U);
399 
400  M_FREE(A_tmp);
401 
402  return d;
403 }
404 
MAT * rot_rows(MAT *mat, u_int i, u_int k, double c, double s, MAT *out)
Definition: givens.c:85
#define v_norm_inf(x)
Definition: matrix.h:512
#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
#define TRUE
Definition: err.c:57
#define v
Definition: md1redef.h:4
static philox4x32_key_t k
Definition: nrnran123.cpp:11
#define m_copy(in, out)
Definition: matrix.h:403
u_int n
Definition: matrix.h:74
#define stack
Definition: code.cpp:128
#define M_FREE(mat)
Definition: matrix.h:213
Real * ve
Definition: matrix.h:69
Definition: matrix.h:67
#define E_SIZES
Definition: err.h:95
int const size_t const size_t n
Definition: nrngsl.h:12
_CONST char * s
Definition: system.cpp:74
#define MNULL
Definition: matrix.h:632
void givens(double x, double y, Real *c, Real *s)
Definition: givens.c:47
MAT * hhtrcols(MAT *M, u_int i0, u_int j0, VEC *hh, double beta)
Definition: hsehldr.c:146
#define E_NULL
Definition: err.h:102
size_t j
VEC * hhvec(VEC *vec, u_int i0, Real *beta, VEC *out, Real *newval)
Definition: hsehldr.c:47
#define sgn(x)
Definition: svd.c:42
#define E_SQUARE
Definition: err.h:103
static char rcsid[]
Definition: svd.c:38
#define MACHEPS
Definition: machine.h:213
#define MAX_STACK
Definition: svd.c:43
sqrt
Definition: extdef.h:3
VEC * get_row(MAT *, u_int, VEC *)
VEC * bisvd(VEC *d, VEC *f, MAT *U, MAT *V)
Definition: svd.c:141
#define MEM_STAT_REG(var, type)
Definition: meminfo.h:133
#define FALSE
Definition: err.c:56
#define i
Definition: md1redef.h:12
#define A(i)
Definition: multisplit.cpp:61
#define c
VEC * get_col(MAT *, u_int, VEC *)
#define error(err_num, fn_name)
Definition: err.h:73
fabs
Definition: extdef.h:3
u_int m
Definition: matrix.h:74
static void fixsvd(VEC *d, MAT *U, MAT *V)
Definition: svd.c:50
#define VNULL
Definition: matrix.h:631
MAT * bifactor(MAT *A, MAT *U, MAT *V)
Definition: svd.c:293
Real ** me
Definition: matrix.h:76
Definition: matrix.h:73
MAT * hhtrrows(MAT *M, u_int i0, u_int j0, VEC *hh, double beta)
Definition: hsehldr.c:100
static Object ** m_ident(void *v)
Definition: matrix.cpp:517
VEC * svd(MAT *A, MAT *U, MAT *V, VEC *d)
Definition: svd.c:350