CEDRIC  Revision_backup-2009-02
SVD.h
Go to the documentation of this file.
1 #ifndef CEDRIC_ANALYSIS_SVD_H
2 #define CEDRIC_ANALYSIS_SVD_H
3 
4 #include <algorithm> // for min, max
5 #include <vector>
6 #include <math.h>
7 
8 #include <assert.h>
9 
10 #include <boost/numeric/ublas/matrix.hpp>
11 #include <boost/numeric/ublas/vector.hpp>
12 
19 template<class ValueType>
20 class SVD
21 {
22 public:
23  typedef boost::numeric::ublas::matrix<ValueType> Matrix;
24  typedef boost::numeric::ublas::vector<ValueType> Vector;
25 
33  SVD( Matrix& matrix );
34 
37  const Vector& getSingularValues() const { return m_S; }
38 
41  const Matrix& getV() const { return m_V; }
42 
43 protected:
44  void svdcmp( Matrix& a, Vector& w, Matrix& v );
45 
46 private:
47  int m_rows, m_cols;
48  Vector m_S;
49  Matrix m_V;
50 };
51 
52 
53 //=====================================================================================================================
54 
55 // HELPER FUNCTIONS
56 
57 //=====================================================================================================================
58 
59 namespace { // use anonymous namespace to avoid namespace pollution
60 
61 // TODO: currently there is a bug in my nrutil replacement, so they can't be used for now!
62 #ifdef NRUTIL_REPLACEMENT
63  template<class T>
66  inline T SQR( T arg ) { return arg == 0.0 ? 0.0 : arg*arg; }
67 
68  template<class T>
69  inline T SIGN( T a, T b ) { return b >= 0.0 ? fabs(a) : fabs(b); }
71 #else
72  // #include "nrutil.h" --> extraced needed macros below
73  static double maxarg1,maxarg2;
74  #define FMAX(a,b) (maxarg1=(a),maxarg2=(b),(maxarg1) > (maxarg2) ?\
75  (maxarg1) : (maxarg2))
76  static int iminarg1,iminarg2;
77  #define IMIN(a,b) (iminarg1=(a),iminarg2=(b),(iminarg1) < (iminarg2) ?\
78  (iminarg1) : (iminarg2))
79  #define SIGN(a,b) ((b) >= 0.0 ? fabs(a) : -fabs(a))
80  static double sqrarg;
81  #define SQR(a) ((sqrarg=(a)) == 0.0 ? 0.0 : sqrarg*sqrarg)
82 #endif
83 
84 
86 template<class T>
87 T pythag( T a, T b )
88 {
89  T absa,absb;
90  absa=fabs(a);
91  absb=fabs(b);
92  if (absa > absb)
93  return absa*sqrt(1.0+SQR(absb/absa));
94  else
95  return (absb == 0.0 ? 0.0 : absb*sqrt(1.0+SQR(absa/absb)));
96 }
97 
98 template<class Matrix,class Vector> void swap_columns( Matrix& M, int i, int j )
99 {
100  Vector v = boost::numeric::ublas::column(M,i);
101  boost::numeric::ublas::column(M,i) = boost::numeric::ublas::column(M,j);
102  boost::numeric::ublas::column(M,j) = v;
103 }
104 
105 } // -- end of anonymous namespace
106 
107 //=====================================================================================================================
108 
109 // TEMPLATE IMPLEMENTATION
110 
111 //=====================================================================================================================
112 
113 #include <iostream>
114 template<class T>
115 SVD<T>::SVD( Matrix& matrix )
116  : m_rows( matrix.size1() ),
117  m_cols( matrix.size2() ),
118  m_S( m_cols ),
119  m_V( m_cols, m_cols )
120 {
121  svdcmp( matrix, m_S, m_V );
122 
123  // BOGO BUG: sorting of eigenvalues is not correct (-> hint that i f***ed up the code?)
124  // use simple bubble sort because i only observed one fehlstand
125  for( unsigned int run=0; run < m_S.size(); ++run )
126  {
127  bool ordered = true;
128 
129  for( unsigned int i=0; i < m_S.size()-1; ++i )
130  {
131  if( m_S[i] < m_S[i+1] )
132  {
133  T tmp = m_S[i];
134  m_S[i] = m_S[i+1];
135  m_S[i+1] = tmp;
136 
137  swap_columns<Matrix,Vector>( matrix, i, i+1 );
138  swap_columns<Matrix,Vector>( m_V, i, i+1 );
139 
140  ordered = false;
141  }
142  }
143 
144  if( ordered ) break;
145  }
146 }
147 
151 template<class T>
152 void SVD<T>::svdcmp( Matrix& a, Vector& w, Matrix& v )
153 {
154  int m = a.size1();
155  int n = a.size2();
156 
157  int flag,i,its,j,jj,k,l,nm;
158 
159  double anorm,c,f,g,h,s,scale,x,y,z;
160 
161  std::vector<double> rv1( n );
162 
163  g=scale=anorm=0.0; // Householder reduction to bidiagonal form.
164 
165  for (i=1;i<=n;i++)
166  {
167  l=i+1;
168  rv1[i-1]=scale*g;
169  g=s=scale=0.0;
170 
171  if (i <= m)
172  {
173  for (k=i;k<=m;k++) scale += fabs(a(k-1,i-1));
174  if (scale)
175  {
176  for (k=i;k<=m;k++)
177  {
178  a(k-1,i-1) /= scale;
179  s += a(k-1,i-1)*a(k-1,i-1);
180  }
181 
182  f=a(i-1,i-1);
183  g = -SIGN(sqrt(s),f);
184  h=f*g-s;
185  a(i-1,i-1)=f-g;
186 
187  for (j=l;j<=n;j++)
188  {
189  for (s=0.0,k=i;k<=m;k++) s += a(k-1,i-1)*a(k-1,j-1);
190  f=s/h;
191  for (k=i;k<=m;k++) a(k-1,j-1) += f*a(k-1,i-1);
192  }
193 
194  for (k=i;k<=m;k++) a(k-1,i-1) *= scale;
195  }
196  }
197 
198  w[i-1]=scale *g;
199  g=s=scale=0.0;
200 
201  if (i <= m && i != n)
202  {
203  for (k=l;k<=n;k++) scale += fabs(a(i-1,k-1));
204  if (scale)
205  {
206  for (k=l;k<=n;k++)
207  {
208  a(i-1,k-1) /= scale;
209  s += a(i-1,k-1)*a(i-1,k-1);
210  }
211 
212  f=a(i-1,l-1);
213  g = -SIGN(sqrt(s),f);
214  h=f*g-s;
215  a(i-1,l-1)=f-g;
216 
217  for (k=l;k<=n;k++) rv1[k-1]=a(i-1,k-1)/h;
218 
219  for (j=l;j<=m;j++)
220  {
221  for (s=0.0,k=l;k<=n;k++) s += a(j-1,k-1)*a(i-1,k-1);
222  for (k=l;k<=n;k++) a(j-1,k-1) += s*rv1[k-1];
223  }
224 
225  for (k=l;k<=n;k++) a(i-1,k-1) *= scale;
226  }
227  }
228 
229  #ifdef NRUTIL_REPLACEMENT
230  anorm=std::max(anorm,(fabs(w[i-1])+fabs(rv1[i-1])));
231  #else
232  anorm=FMAX(anorm,(fabs(w[i-1])+fabs(rv1[i])));
233  #endif
234  }
235 
236 
237  for (i=n;i>=1;i--) // Accumulation of right-hand transformations.
238  {
239  if (i < n)
240  {
241  if (g)
242  {
243  for (j=l;j<=n;j++) // Double division to avoid possible under .o .
244  v(j-1,i-1)=(a(i-1,j-1)/a(i-1,l-1))/g;
245 
246  for (j=l;j<=n;j++)
247  {
248  for (s=0.0,k=l;k<=n;k++) s += a(i-1,k-1)*v(k-1,j-1);
249  for (k=l;k<=n;k++) v(k-1,j-1) += s*v(k-1,i-1);
250  }
251  }
252 
253  for (j=l;j<=n;j++) v(i-1,j-1)=v(j-1,i-1)=0.0;
254  }
255 
256  v(i-1,i-1)=1.0;
257  g=rv1[i-1];
258  l=i;
259  }
260 
261  #ifdef NRUTIL_REPLACEMENT
262  for (i=std::min(m,n);i>=1;i--) // Accumulation of left-hand transformations.
263  #else
264  for (i=IMIN(m,n);i>=1;i--) // Accumulation of left-hand transformations.
265  #endif
266  {
267  l=i+1;
268  g=w[i-1];
269 
270  for (j=l;j<=n;j++) a(i-1,j-1)=0.0;
271 
272  if (g)
273  {
274  g=1.0/g;
275 
276  for (j=l;j<=n;j++)
277  {
278  for (s=0.0,k=l;k<=m;k++) s += a(k-1,i-1)*a(k-1,j-1);
279  f=(s/a(i-1,i-1))*g;
280  for (k=i;k<=m;k++) a(k-1,j-1) += f*a(k-1,i-1);
281  }
282 
283  for (j=i;j<=m;j++) a(j-1,i-1) *= g;
284 
285  }
286  else for (j=i;j<=m;j++) a(j-1,i-1)=0.0;
287 
288  //++a(i-1,i-1); //?????????
289  a(i-1,i-1)+=1;
290  }
291 
292  for (k=n;k>=1;k--) // Diagonalization of the bidiagonal form:Loop over
293  {
294 
295  for (its=1;its<=30;its++) // singular values,and over allo ed iterations.
296  {
297  flag=1;
298  for (l=k;l>=1;l--) // Test for splitting.
299  {
300  nm=l-1; // Note that rv1[1] is always zero.
301  if ((double)(fabs(rv1[l-1])+anorm) == anorm)
302  {
303  flag=0;
304  break;
305  }
306 
307  if ((double)(fabs(w[nm-1])+anorm) == anorm) break;
308  }
309 
310  if (flag)
311  {
312  c=0.0; // Cancellation of rv1[l],if l > 1
313  s=1.0;
314 
315  for (i=l;i<=k;i++)
316  {
317  f=s*rv1[i-1];
318  rv1[i-1]=c*rv1[i-1];
319  if ((double)(fabs(f)+anorm) == anorm) break;
320  g=w[i-1];
321  h=pythag(f,g);
322  w[i-1]=h;
323  h=1.0/h;
324  c=g*h;
325  s = -f*h;
326 
327  for (j=1;j<=m;j++)
328  {
329  y=a(j-1,nm-1);
330  z=a(j-1,i-1);
331  a(j-1,nm-1)=y*c+z*s;
332  a(j-1,i-1)=z*c-y*s;
333  }
334  }
335  }
336 
337  z=w[k-1];
338 
339  if (l == k) // Convergence.
340  {
341  if (z < 0.0) // Singular value is made nonnegative.
342  {
343  w[k-1] = -z;
344  for (j=1;j<=n;j++) v(j-1,k-1) = -v(j-1,k-1);
345  }
346  break;
347  }
348 
349  if (its == 30) assert(false); // TODO: throw some kind of exception
350  //nrerror("no convergence in 30 svdcmp iterations");
351 
352  x=w[l-1]; // Shift from bottom 2-by-2 minor.
353  nm=k-1;
354  y=w[nm-1];
355  g=rv1[nm-1];
356  h=rv1[k-1];
357  f=((y-z)*(y+z)+(g-h)*(g+h))/(2.0*h*y);
358  g=pythag(f,1.0);
359  f=((x-z)*(x+z)+h*((y/(f+SIGN(g,f)))-h))/x;
360  c=s=1.0; // Next QR transformation:
361  for (j=l;j<=nm;j++)
362  {
363  i=j+1;
364  g=rv1[i-1];
365  y=w[i-1];
366  h=s*g;
367  g=c*g;
368  z=pythag(f,h);
369  rv1[j-1]=z;
370  c=f/z;
371  s=h/z;
372  f=x*c+g*s;
373  g = g*c-x*s;
374  h=y*s;
375  y *=c;
376 
377  for (jj=1;jj<=n;jj++)
378  {
379  x=v(jj-1,j-1);
380  z=v(jj-1,i-1);
381  v(jj-1,j-1)=x*c+z*s;
382  v(jj-1,i-1)=z*c-x*s;
383  }
384 
385  z=pythag(f,h);
386  w[j-1]=z; // Rotation can be arbitrary if z =0
387 
388  if (z)
389  {
390  z=1.0/z;
391  c=f*z;
392  s=h*z;
393  }
394 
395  f=c*g+s*y;
396  x=c*y-s*g;
397 
398  for (jj=1;jj<=m;jj++)
399  {
400  y=a(jj-1,j-1);
401  z=a(j-1,i-1);
402  a(jj-1,j-1)=y*c+z*s;
403  a(jj-1,i-1)=z*c-y*s;
404  }
405  }
406 
407  rv1[l-1]=0.0;
408  rv1[k-1]=f;
409  w[k-1]=x;
410  }
411  }
412 }
413 
414 #endif // CEDRIC_ANALYSIS_SVD_H
boost::numeric::ublas::vector< ValueType > Vector
Definition: SVD.h:24
const Vector & getSingularValues() const
Definition: SVD.h:37
void svdcmp(Matrix &a, Vector &w, Matrix &v)
Definition: SVD.h:152
SVD(Matrix &matrix)
Compute Singular Value Decomposition.
Definition: SVD.h:115
boost::numeric::ublas::matrix< ValueType > Matrix
Definition: SVD.h:23
const Matrix & getV() const
Definition: SVD.h:41
#define SQR(a)
Definition: SVD.h:81
#define FMAX(a, b)
Definition: SVD.h:74
Compute Singular Value Decomposition, Wrapper for super-old Numerical Recipes code.
Definition: SVD.h:20
#define SIGN(a, b)
Definition: SVD.h:79
#define IMIN(a, b)
Definition: SVD.h:77