Basic Image AlgorithmS Library  2.8.0
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
SVD.hh
1 /*
2 This file is part of the BIAS library (Basic ImageAlgorithmS).
3 
4 Copyright (C) 2003-2009 (see file CONTACT for details)
5  Multimediale Systeme der Informationsverarbeitung
6  Institut fuer Informatik
7  Christian-Albrechts-Universitaet Kiel
8 
9 
10 BIAS is free software; you can redistribute it and/or modify
11 it under the terms of the GNU Lesser General Public License as published by
12 the Free Software Foundation; either version 2.1 of the License, or
13 (at your option) any later version.
14 
15 BIAS is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU Lesser General Public License for more details.
19 
20 You should have received a copy of the GNU Lesser General Public License
21 along with BIAS; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24 
25 
26 #ifndef _SVD_hh_
27 #define _SVD_hh_
28 
29 #include "bias_config.h"
30 
31 #include <Base/Debug/Error.hh>
32 #include <Base/Debug/Debug.hh>
33 
34 #include <Base/Math/Matrix.hh>
35 #include <Base/Math/Vector.hh>
36 
37 #include <float.h>
38 
39 
40 
41 // for netlib lapck svd routine tests:
42 //#include "netlib/dggsvd.c"
43 
44 // double values below this threshold are treated as zero
45 // (for nullspace computation)
46 //#define DEFAULT_DOUBLE_ZERO_THRESHOLD DBL_EPSILON
47 #define DEFAULT_DOUBLE_ZERO_THRESHOLD 1E-8
48 
49 #define SVDSOLVE 0x00000001
50 #define D_SVD_RANK 0x00000002
51 #define D_SVD_SOLVE 0x00000004
52 
53 namespace BIAS {
54  /**
55  @class SVD
56  @ingroup g_mathalgo
57  @brief computes and holds the singular value decomposition
58  of a rectangular (not necessarily quadratic) Matrix A
59 
60  M = U S V't.
61 
62  The singular vectors vi and (right) singular values sigma_i satisfy the
63  equation: A vi = sigma_i vi
64 
65  The Vector S_ stores the singular values of A in decreasing
66  order (smallest is last)
67 
68  The columns of U are the left singular vectors of A and form
69  an orthonormal basis of A
70  which corresponds to the nonzero singular values.
71  while the columns of U corresponding to (nearly) zero values
72  are the nullspace.
73 
74  This function is a base class and not a member in Matrix because:
75  - holding the U,S,V matrices allows repeated queries of the
76  same SVD results.
77  -avoid namespace clutter in the Matrix class.
78  There are many other
79  decompositions that might be of interest,
80  and adding them all would make for a very large Matrix class with
81  extra members which are irrelevant for the Matrix itself.
82 
83  You can create the svd object either in relative or in absolute mode:
84  if AbsoluteZeroThreshold is true, a singular value is compared
85  to the threshold "absolutely", otherwise the ratio of the largest and
86  the current singular value is compared against the threshold.
87  Usually you want AbsoluteZeroThreshold = false, but for the sake of
88  interface stability/downwards compatibility, the default is true
89 
90  @author Jan Woetzel
91  **/
92  class BIASMathAlgo_EXPORT SVD : public Debug {
93  public:
94  /** @brief solve the general eigenproblem of the rectangular matrix M
95  or with other words, make the U*S*V^T decomposition
96  @param M matrix to decompose
97  @param ZeroThreshold for nullspace determination, if a singular value
98  is lower than ZeroThreshold, define the corresponding vector
99  in V^T to be a null-vector (also sing.vals are treated this way)
100  @param AbsoluteZeroThreshold if true, a singular value is compared
101  to the threshold "absoultely", otherwise the ratio of the largest and
102  the current singular value is compared against the threshold.
103  Usually you want AbsoluteZeroThreshold = false, but for the sake of
104  interface stability/downwards compatibility, the default is true
105  @author Jan Woetzel **/
106  SVD(const Matrix <double> & M,
107  double ZeroThreshold = DEFAULT_DOUBLE_ZERO_THRESHOLD,
108  bool AbsoluteZeroThreshold = true);
109 
110  /** @brief creates an empty svd without automatically decomposing any
111  matrix
112  @param AbsoluteZeroThreshold if true, a singular value is compared
113  to the threshold "absolutely", otherwise the ratio of the largest and
114  the current singular value is compared against the threshold.
115  Usually you want AbsoluteZeroThreshold = false, but for the sake of
116  interface stability/downwards compatibility, the default is true */
117  inline SVD(bool AbsoluteZeroThreshold = true) {
118  Decomposed_ = false;
119  ZeroThreshold_ = DEFAULT_DOUBLE_ZERO_THRESHOLD;
120  AbsoluteZeroThreshold_ = AbsoluteZeroThreshold;
121  };
122 
123  inline virtual ~SVD(){};
124 
125  /** @brief set a new matrix and compute its decomposition.
126  solves the general eigenproblem of the rectangular matrix M
127  @author Jan Woetzel 05/142002)*/
128  int Compute(const Matrix<double>& M,
129  double ZeroThreshold=DEFAULT_DOUBLE_ZERO_THRESHOLD);
130 
131  /** use our naming convention */
132  inline int compute(const Matrix<double>& M,
133  double ZeroThreshold=DEFAULT_DOUBLE_ZERO_THRESHOLD)
134  { return Compute(M, ZeroThreshold); };
135 
136 #ifdef BIAS_HAVE_OPENCV
137  /** \brief Use OpenCV for decomposition as Lapack frequently leads
138  to crashes under windows
139  \attention This does not deliver the same results as Compute().
140  The columns of U and VT differ in sign.
141  \author Ingo Schiller
142  */
143  int ComputeOpenCV(const Matrix<double>& M,
144  double ZeroThreshold=DEFAULT_DOUBLE_ZERO_THRESHOLD);
145 #endif
146  /** solve the general (non-special) eigenvalue/eigenvector problem of
147  a general (non-symmetric) matrix M
148  calls the extern liblapack routine dgesvd .
149  computes the genral singular value decomposition with
150  M = U * Sigma(S) * VT
151  with
152  Sigma(S) is the qudratic, symmetric matrix which contains
153  the singular vales in the diagonal and zero and zero else.
154  U, VT as described in the data mambers.
155  @author Jan Woetzel (04/04/2002) */
156  int General_Eigenproblem_GeneralMatrix_Lapack(const Matrix<double> &M);
157 
158  /** @brief return zerothresh currently used
159  @author koeser */
160  inline double GetZeroThreshold() const
161  { return ZeroThreshold_; }
162 
163  /** return S
164  which is a vector of the singular values of A in descending order.
165  The diagonalelements of Sigma are the singular values of A.
166  @author Jan Woetzel 04/04/2002 */
167  inline const Vector<double>& GetS() const
168  { return S_; }
169 
170  /** return one singular value (which may be zero).
171  the index runs from 0.. (size-1)
172  @author Jan Woetzel 04/04/2002 **/
173  inline const double GetSingularValue(int index ) const;
174 
175  /** return VT (=transposed(V))
176  @author Jan Woetzel (04/04/2002) **/
177  inline const Matrix<double>& GetVT() const
178  { return VT_; };
179 
180  /** return V
181  @author Jan Woetzel (04/04/2002) **/
182  inline Matrix<double> GetV() const;
183 
184  /** return U
185  U is a m x m orthogonal matrix
186  @author Jan Woetzel (04/04/2002) **/
187  inline const Matrix<double>& GetU() const
188  { return U_; };
189 
190  /** return the length of the singular value vector
191  inline const int size() const */
192  inline int size() const {
193  if (U_.num_cols()>VT_.num_cols())
194  return U_.num_cols();
195  else
196  return VT_.num_cols();
197  };
198 
199  /** @brief return the dim of the nullspace.
200 
201  For rectangular matrices this is the number of singular values which
202  are (about) zero. The nullspace is spaned by the singular vectors
203  corresponding to the zero singular values.
204  woelk 4 2003: changed API, diffentiate between Left and Right Nullspace
205  koeser 01/2005: rel/abs handled by flag:
206  AbsoluteZeroThreshold_ determines whther we compare absolute or
207  relative singular values against the threshold
208  @author Jan Woetzel 04/04/2002 **/
209  inline const int NullspaceDim() const {
210  if (AbsoluteZeroThreshold_) return AbsNullspaceDim();
211  else return RelNullspaceDim();
212  }
213 
214  /** @brief returns the dim of the matrix's right nullspace
215  @author woelk 04 2003 */
216  inline const int RightNullspaceDim() const {
217  if (AbsoluteZeroThreshold_) return AbsRightNullspaceDim();
218  else return RelRightNullspaceDim();
219  }
220 
221  /** @brief returns the dim of the matrix's left nullspace
222  @author woelk 04 2003 */
223  inline const int LeftNullspaceDim() const {
224  if (AbsoluteZeroThreshold_) return AbsLeftNullspaceDim();
225  else return RelLeftNullspaceDim();
226  }
227 
228  /** @brief returns dim of nullspace using the absolute threshold criterion
229 
230  For rectangular matrices this is the number of singular values which
231  are (about) zero. The nullspace is spaned by the singular vectors
232  corresponding to the zero singular values.
233  woelk 4 2003: changed API, diffentiate between Left and Right Nullspace
234  @author Jan Woetzel 04/04/2002 **/
235  const int AbsNullspaceDim() const;
236 
237  /** @brief returns the dim of the matrix's right nullspace, using absolute
238  threshold criterion
239  @author woelk 04 2003 */
240  const int AbsRightNullspaceDim() const;
241 
242  /** @brief returns the dim of the matrix's left nullspace, using absolute
243  threshold criterion
244  @author woelk 04 2003 */
245  const int AbsLeftNullspaceDim() const;
246 
247  /** @brief compare singular values against greatest, not absolute
248  @author Christian Buck
249  changed criterion for insignificant singular values according to
250  hartley/zisserman from s_i<ZeroThreshold to s_i/s_0<ZeroThreshold.
251  fallback to old criterion iff s_0=0. */
252  const int RelNullspaceDim() const;
253  const int RelRightNullspaceDim() const;
254  const int RelLeftNullspaceDim() const;
255 
256  /** return one of the nullvectors.
257  if last_offset=0 then the last nullvector
258  (corresponding to the smallest singular value) is returned,
259  if last_offset=1 then the last but one nullvector is returned,
260  and so on.
261  The last_offset must be in [0..NullspaceDim-1].
262  @author Jan Woetzel(08/08/2002), JMF */
263  inline Vector<double> GetNullvector(const int last_offset = 0);
264 
265  /** Returns one of the nullvectors in argument and true if
266  Nullvector exists.
267  if last_offset=0 then the last nullvector (corresponding to the
268  smallest singular value) is returned,
269  if last_offset=1 then the last but one nullvector is returned,
270  and so on.
271  The last_offset must be in [0..NullspaceDim-1].
272  @author Jan Woetzel (08/08/2002), JMF */
273  inline bool GetNullvector(Vector<double>& NullVec,
274  const int last_offset = 0 );
275 
276  /** Return one of the left nullvectors.
277  If last_offset=0 then the last nullvector
278  (corresponding to the smallest
279  singular value) is returned,
280  if last_offset=1 then the last but one nullvector is returned,
281  and so on.
282  The last_offset must be in [0..NullspaceDim-1] otherwise 0 is returned
283  @author JMF/koeser **/
284  inline bool GetLeftNullvector(Vector<double>&nv, const int last_offset=0);
285 
286  /** @brief same as above but returning vector */
287  inline Vector<double> GetLeftNullvector( const int last_offset = 0 );
288 
289  // -- Solve the matrix-vector system M x = y, returning x.
290  Vector<double> Solve(const Vector<double>& y) const;
291  /** use our naming convention */
292  inline Vector<double> solve(const Vector<double>& y)const
293  { return Solve(y); };
294 
295  /** returns pseudoinverse of A = U * S * V^T
296  A^+ = V * S^+ * U^T
297  @author F Woelk */
298  Matrix<double> Invert();
299 
300  /** returns pseudoinverse of A = U * S * V^T
301  A^+ = V * S^+ * U^T
302  the first "rank" elements of S are inverted the others are set to zero
303  @author Christian Beder */
304  Matrix<double> Invert(int rank);
305 
306  /** as above, but compute new svd for a */
307  Matrix<double> Invert(Matrix<double> A);
308 #ifdef BIAS_HAVE_OPENCV
309  /** \brief Calculates new inverse with OpenCV cvInvert
310  \attention This does not deliver the same results as Invert().
311  */
312  Matrix<double> InvertOpenCV(const Matrix<double>& A);
313 #endif
314  /** returns the square root of a symmetric positive definite matrix M
315  A = sqrt(M) = U * sqrt(S) * V_T,
316  such that A*A = M.
317  The result is only valid when the M *is* symmetric positive definite.
318  @author woelk 01/2006 */
319  Matrix<double> Sqrt();
320 
321  /** as above, but compute new svd for @param A */
322  Matrix<double> Sqrt(const Matrix<double>& A);
323 
324  /** returns the square root of a symmetric positive definite matrix M
325  A = sqrt(M) = U * sqrt(S),
326  such that A*A^T = M.
327  The result is only valid when the M *is* symmetric positive definite.
328  @author woelk 01/2006 */
329  Matrix<double> SqrtT();
330 
331  /** as above, but compute new svd for @param A */
332  Matrix<double> SqrtT(const Matrix<double>& A);
333 
334  /// returns the rank of A_
335  unsigned int Rank();
336 
337 
338  /** solves the overdetermined linear system $AX=B$ with the unknown $X$,
339  where $A$ is an $m x n$ matrix ($m>n$)and B is a vector of
340  size $m$.
341  @author woelk 07/2004 */
342  int Solve(Matrix<double>& A, Vector<double>& B, Vector<double>& X);
343 
344  /** Call this after Compute(),
345  returns the eigenvalues of the matrix in ascending
346  order (smallest first), which are NOT the singular values!
347  Works only, if the original Matrix was symmetric.
348  @author grest, July 2006 */
349  BIAS::Vector<double> GetEigenValues();
350 
351 
352  protected:
353  /// data members:
354  /// original matrix (to be decomposed)
356 
357  /// contains the singular values of A_ corresponding to the
358  /// i'th column in descending order.
360 
361  /// contains columnwise the left singular vectors
362  /// of A_ corresponding to the i'th singular value
364 
365  /// contains the right singular vectors of A- in rows
366  /// [because VT is transpose(V) ]
368 
369  /// determines whether we compare singular-values directly (absolute)
370  /// to the zero threshold or whether we compare the ratio of the sing-value
371  /// under inspection and the largest one to the threshold (relative)
373 
374  /// values below this threshold are treated as zero
375  double ZeroThreshold_;
376 
377  /// flag for holding decomposed matrix
379 
380  }; // class SVD
381 
382  /////////////////////////////////////////////////////////////////
383  /// inline implementations
384  /////////////////////////////////////////////////////////////////
385 
386  inline const double SVD::GetSingularValue(int index) const
387  {
388  BIASASSERT( index >=0 );
389  BIASASSERT( index < size() );
390  if (index < S_.size())
391  return S_[index];
392  else
393  return 0.0;
394  }
395 
396  inline Matrix<double> SVD::GetV() const
397  {
398  Matrix<double> matV;
399  //matV = transpose( VT_ ); //uncomment if error under UNIX
400  matV = VT_.Transpose();
401  return matV;
402  }
403 
404  inline Vector<double> SVD::GetNullvector(const int last_offset)
405  {
406  Vector<double> vec(VT_.num_cols(), 0.0);
407  if (!GetNullvector(vec, last_offset)) {
408  //BIASERR("No Nullvector for "<<last_offset);
409  }
410  return vec;
411  }
412 
413  inline bool SVD::GetNullvector(Vector<double>& NullVec,
414  const int last_offset )
415  {
416  // create a new vector
417  NullVec.newsize(VT_.num_cols());
418  // fill the (null-)vector
419 
420  for (int col=0; col < VT_.num_cols(); col++) {
421  NullVec[col] = VT_[ VT_.num_cols() -1 - last_offset ][col];
422  }
423 
424 // check if there is a nullvector with this last_offset_index
425  if (last_offset > (NullspaceDim() -1)) {
426  // index not OK
427  return false;
428  } else {
429  // index OK
430  return true;
431  }
432  }
433 
434  inline Vector<double> SVD::GetLeftNullvector(const int last_offset)
435  {
436  Vector<double> vec(VT_.num_rows(), 0.0);
437  if (!GetLeftNullvector(vec, last_offset)) {
438  BIASERR("No left nullvector for "<<last_offset);
439  }
440  return vec;
441  }
442 
444  const int last_offset) {
445  // check if there is a nullvector with this last_offset_index
446  if (last_offset > (NullspaceDim() -1)) {
447  // index not OK
448  BIASERR("for last_offset=" <<last_offset
449  <<" with actual NullspaceDim= "<<NullspaceDim()
450  <<" can no left nullvector be retrieved. aborting. ");
451  BIASERR(S_ << U_ << VT_);
452  return false;
453  }
454  // index OK
455  vec.newsize( U_.num_rows());
456  // fill the (null-)vector
457  for (int row=0; row < VT_.num_rows(); row++) {
458  vec[row] = U_[row][ U_.num_rows() -1 - last_offset ];
459  }
460  return true;
461  }
462 
463 
464 } // namespace BIAS
465 #endif // _SVD_hh_
466 
Vector< double > GetNullvector(const int last_offset=0)
return one of the nullvectors.
Definition: SVD.hh:404
virtual ~SVD()
Definition: SVD.hh:123
computes and holds the singular value decomposition of a rectangular (not necessarily quadratic) Matr...
Definition: SVD.hh:92
Subscript num_cols() const
Definition: cmat.h:320
Matrix< T > Transpose() const
transpose function, storing data destination matrix
Definition: Matrix.hh:823
const int NullspaceDim() const
return the dim of the nullspace.
Definition: SVD.hh:209
bool GetLeftNullvector(Vector< double > &nv, const int last_offset=0)
Return one of the left nullvectors.
Definition: SVD.hh:443
Vector< double > S_
contains the singular values of A_ corresponding to the i&#39;th column in descending order...
Definition: SVD.hh:359
Vector< double > solve(const Vector< double > &y) const
use our naming convention
Definition: SVD.hh:292
SVD(bool AbsoluteZeroThreshold=true)
creates an empty svd without automatically decomposing any matrix
Definition: SVD.hh:117
int size() const
return the length of the singular value vector inline const int size() const
Definition: SVD.hh:192
double GetZeroThreshold() const
return zerothresh currently used
Definition: SVD.hh:160
Vector< T > & newsize(Subscript N)
Definition: vec.h:220
const double GetSingularValue(int index) const
return one singular value (which may be zero).
Definition: SVD.hh:386
const int RightNullspaceDim() const
returns the dim of the matrix&#39;s right nullspace
Definition: SVD.hh:216
Matrix< double > GetV() const
return V
Definition: SVD.hh:396
const Matrix< double > & GetVT() const
return VT (=transposed(V))
Definition: SVD.hh:177
bool Decomposed_
flag for holding decomposed matrix
Definition: SVD.hh:378
const Vector< double > & GetS() const
return S which is a vector of the singular values of A in descending order.
Definition: SVD.hh:167
const int LeftNullspaceDim() const
returns the dim of the matrix&#39;s left nullspace
Definition: SVD.hh:223
Matrix< double > A_
data members: original matrix (to be decomposed)
Definition: SVD.hh:355
Matrix< double > VT_
contains the right singular vectors of A- in rows [because VT is transpose(V) ]
Definition: SVD.hh:367
double ZeroThreshold_
values below this threshold are treated as zero
Definition: SVD.hh:375
const Matrix< double > & GetU() const
return U U is a m x m orthogonal matrix
Definition: SVD.hh:187
Subscript num_rows() const
Definition: cmat.h:319
bool AbsoluteZeroThreshold_
determines whether we compare singular-values directly (absolute) to the zero threshold or whether we...
Definition: SVD.hh:372
Matrix< double > U_
contains columnwise the left singular vectors of A_ corresponding to the i&#39;th singular value ...
Definition: SVD.hh:363
Subscript size() const
Definition: vec.h:262
int compute(const Matrix< double > &M, double ZeroThreshold=DEFAULT_DOUBLE_ZERO_THRESHOLD)
use our naming convention
Definition: SVD.hh:132