core/vnl/algo/vnl_svd_fixed.txx
Go to the documentation of this file.
00001 // This is core/vnl/algo/vnl_svd_fixed.txx
00002 #ifndef vnl_svd_fixed_txx_
00003 #define vnl_svd_fixed_txx_
00004 //:
00005 // \file
00006 
00007 #include "vnl_svd_fixed.h"
00008 
00009 #include <vcl_cassert.h>
00010 #include <vcl_cstdlib.h>
00011 #include <vcl_complex.h>
00012 #include <vcl_iostream.h>
00013 #include <vcl_algorithm.h> // min()
00014 
00015 #include <vnl/vnl_math.h>
00016 #include <vnl/vnl_fortran_copy_fixed.h>
00017 #include <vnl/algo/vnl_netlib.h> // dsvdc_()
00018 
00019 // use C++ overloading to call the right linpack routine from the template code :
00020 #define macro(p, T) \
00021 inline void vnl_linpack_svdc_fixed(vnl_netlib_svd_proto(T)) \
00022 { v3p_netlib_##p##svdc_(vnl_netlib_svd_params); }
00023 macro(s, float);
00024 macro(d, double);
00025 macro(c, vcl_complex<float>);
00026 macro(z, vcl_complex<double>);
00027 #undef macro
00028 
00029 //--------------------------------------------------------------------------------
00030 
00031 static bool vnl_svd_fixed_test_heavily = false;
00032 #include <vnl/vnl_matlab_print.h>
00033 
00034 template <class T, unsigned int R, unsigned int C>
00035 vnl_svd_fixed<T,R,C>::vnl_svd_fixed(vnl_matrix_fixed<T,R,C> const& M, double zero_out_tol)
00036 {
00037   {
00038     const long n=R, p=C;
00039     const unsigned mm = vcl_min(R+1u,C);
00040 
00041     // Copy source matrix into fortran storage
00042     // SVD is slow, don't worry about the cost of this transpose.
00043     vnl_fortran_copy_fixed<T,R,C> X(M);
00044 
00045     // Make workspace vectors.
00046     vnl_vector_fixed<T, C> work(T(0));
00047     vnl_vector_fixed<T, R*C> uspace(T(0));
00048     vnl_vector_fixed<T, C*C> vspace(T(0));
00049     vnl_vector_fixed<T, (R+1<C?R+1:C)> wspace(T(0)); // complex fortran routine actually _wants_ complex W!
00050     vnl_vector_fixed<T, C> espace(T(0));
00051 
00052     // Call Linpack SVD
00053     long info = 0;
00054     const long job = 21; // min(n,p) svs in U, n svs in V (i.e. economy size)
00055     vnl_linpack_svdc_fixed((T*)X, &n, &n, &p,
00056                            wspace.data_block(),
00057                            espace.data_block(),
00058                            uspace.data_block(), &n,
00059                            vspace.data_block(), &p,
00060                            work.data_block(),
00061                            &job, &info);
00062 
00063     // Error return?
00064     if (info != 0)
00065     {
00066       // If info is non-zero, it contains the number of singular values
00067       // for this the SVD algorithm failed to converge. The condition is
00068       // not bogus. Even if the returned singular values are sensible,
00069       // the singular vectors can be utterly wrong.
00070 
00071       // It is possible the failure was due to NaNs or infinities in the
00072       // matrix. Check for that now.
00073       M.assert_finite();
00074 
00075       // If we get here it might be because
00076       // 1. The scalar type has such
00077       // extreme precision that too few iterations were performed to
00078       // converge to within machine precision (that is the svdc criterion).
00079       // One solution to that is to increase the maximum number of
00080       // iterations in the netlib code.
00081       //
00082       // 2. The LINPACK dsvdc_ code expects correct IEEE rounding behaviour,
00083       // which some platforms (notably x86 processors)
00084       // have trouble doing. For example, gcc can output
00085       // code in -O2 and static-linked code that causes this problem.
00086       // One solution to this is to persuade gcc to output slightly different code
00087       // by adding and -fPIC option to the command line for v3p/netlib/dsvdc.c. If
00088       // that doesn't work try adding -ffloat-store, which should fix the problem
00089       // at the expense of being significantly slower for big problems. Note that
00090       // if this is the cause, core/vnl/tests/test_svd should have failed.
00091       //
00092       // You may be able to diagnose the problem here by printing a warning message.
00093       vcl_cerr << __FILE__ ": suspicious return value (" << info << ") from SVDC\n"
00094                << __FILE__ ": M is " << M.rows() << 'x' << M.cols() << vcl_endl;
00095 
00096       vnl_matlab_print(vcl_cerr, M, "M", vnl_matlab_print_format_long);
00097       valid_ = false;
00098     }
00099     else
00100       valid_ = true;
00101 
00102     // Copy fortran outputs into our storage
00103     {
00104       const T *d = uspace.data_block();
00105       for (long j = 0; j < p; ++j)
00106         for (long i = 0; i < n; ++i)
00107           U_(i,j) = *d++;
00108     }
00109 
00110     for (unsigned j = 0; j < mm; ++j)
00111       W_(j, j) = vcl_abs(wspace(j)); // we get rid of complexness here.
00112 
00113     for (unsigned j = mm; j < C; ++j)
00114       W_(j, j) = 0;
00115 
00116     {
00117       const T *d = vspace.data_block();
00118       for (unsigned j = 0; j < C; ++j)
00119         for (unsigned i = 0; i < C; ++i)
00120           V_(i,j) = *d++;
00121     }
00122   }
00123 
00124   if (vnl_svd_fixed_test_heavily)
00125   {
00126     // Test that recomposed matrix == M
00127     typedef typename vnl_numeric_traits<T>::abs_t abs_t;
00128     abs_t recomposition_residual = vcl_abs((recompose() - M).fro_norm());
00129     abs_t n = vcl_abs(M.fro_norm());
00130     abs_t thresh = abs_t(R) * abs_t(vnl_math::eps) * n;
00131     if (recomposition_residual > thresh)
00132     {
00133       vcl_cerr << "vnl_svd_fixed<T>::vnl_svd_fixed<T>() -- Warning, recomposition_residual = "
00134                << recomposition_residual << vcl_endl
00135                << "fro_norm(M) = " << n << vcl_endl
00136                << "eps*fro_norm(M) = " << thresh << vcl_endl
00137                << "Press return to continue\n";
00138       char x;
00139       vcl_cin.get(&x, 1, '\n');
00140     }
00141   }
00142 
00143   if (zero_out_tol >= 0)
00144     // Zero out small sv's and update rank count.
00145     zero_out_absolute(double(+zero_out_tol));
00146   else
00147     // negative tolerance implies relative to max elt.
00148     zero_out_relative(double(-zero_out_tol));
00149 }
00150 
00151 template <class T, unsigned int R, unsigned int C>
00152 vcl_ostream& operator<<(vcl_ostream& s, const vnl_svd_fixed<T,R,C>& svd)
00153 {
00154   s << "vnl_svd_fixed<T,R,C>:\n"
00155 #if 0
00156     << "M = [\n" << M << "]\n"
00157 #endif // 0
00158     << "U = [\n" << svd.U() << "]\n"
00159     << "W = " << svd.W() << '\n'
00160     << "V = [\n" << svd.V() << "]\n"
00161     << "rank = " << svd.rank() << vcl_endl;
00162   return s;
00163 }
00164 
00165 //-----------------------------------------------------------------------------
00166 // Chunky bits.
00167 
00168 //: find weights below threshold tol, zero them out, and update W_ and Winverse_
00169 template <class T, unsigned int R, unsigned int C>
00170 void vnl_svd_fixed<T,R,C>::zero_out_absolute(double tol)
00171 {
00172   last_tol_ = tol;
00173   rank_ = C;
00174   for (unsigned k = 0; k < C; k++)
00175   {
00176     singval_t& weight = W_(k, k);
00177     if (vcl_abs(weight) <= tol)
00178     {
00179       Winverse_(k,k) = 0;
00180       weight = 0;
00181       --rank_;
00182     }
00183     else
00184     {
00185       Winverse_(k,k) = singval_t(1.0)/weight;
00186     }
00187   }
00188 }
00189 
00190 //: find weights below tol*max(w) and zero them out
00191 template <class T, unsigned int R, unsigned int C>
00192 void vnl_svd_fixed<T,R,C>::zero_out_relative(double tol) // sqrt(machine epsilon)
00193 {
00194   zero_out_absolute(tol * vcl_abs(sigma_max()));
00195 }
00196 
00197 static bool wf=false;
00198 inline bool warned_f() { if (wf) return true; else { wf=true; return false; } }
00199 
00200 //: Calculate determinant as product of diagonals in W.
00201 template <class T, unsigned int R, unsigned int C>
00202 typename vnl_svd_fixed<T,R,C>::singval_t vnl_svd_fixed<T,R,C>::determinant_magnitude() const
00203 {
00204   if (!warned_f() && R != C)
00205     vcl_cerr << __FILE__ ": called determinant_magnitude() on SVD of non-square matrix\n"
00206              << "(This warning is displayed only once)\n";
00207   singval_t product = W_(0, 0);
00208   for (unsigned long k = 1; k < C; k++)
00209     product *= W_(k, k);
00210 
00211   return product;
00212 }
00213 
00214 template <class T, unsigned int R, unsigned int C>
00215 typename vnl_svd_fixed<T,R,C>::singval_t vnl_svd_fixed<T,R,C>::norm() const
00216 {
00217   return vcl_abs(sigma_max());
00218 }
00219 
00220 //: Recompose SVD to U*W*V'
00221 template <class T, unsigned int R, unsigned int C>
00222 vnl_matrix_fixed<T,R,C> vnl_svd_fixed<T,R,C>::recompose(unsigned int rnk) const
00223 {
00224   if (rnk > rank_) rnk=rank_;
00225   vnl_diag_matrix_fixed<T,C> Wmatr(W_);
00226   for (unsigned int i=rnk;i<C;++i)
00227     Wmatr(i,i)=0;
00228 
00229   return U_*Wmatr*V_.conjugate_transpose();
00230 }
00231 
00232 
00233 //: Calculate pseudo-inverse.
00234 template <class T, unsigned int R, unsigned int C>
00235 vnl_matrix_fixed<T,C,R> vnl_svd_fixed<T,R,C>::pinverse(unsigned int rnk) const
00236 {
00237   if (rnk > rank_) rnk=rank_;
00238   vnl_diag_matrix_fixed<T,C> W_inverse(Winverse_);
00239   for (unsigned int i=rnk;i<C;++i)
00240     W_inverse(i,i)=0;
00241 
00242   return V_ * W_inverse * U_.conjugate_transpose();
00243 }
00244 
00245 
00246 //: Calculate (pseudo-)inverse of transpose.
00247 template <class T, unsigned int R, unsigned int C>
00248 vnl_matrix_fixed<T,R,C> vnl_svd_fixed<T,R,C>::tinverse(unsigned int rnk) const
00249 {
00250   if (rnk > rank_) rnk=rank_;
00251   vnl_diag_matrix_fixed<T,C> W_inverse(Winverse_);
00252   for (unsigned int i=rnk;i<C;++i)
00253     W_inverse(i,i)=0;
00254 
00255   return U_ * W_inverse * V_.conjugate_transpose();
00256 }
00257 
00258 
00259 //: Solve the matrix equation M X = B, returning X
00260 template <class T, unsigned int R, unsigned int C>
00261 vnl_matrix<T> vnl_svd_fixed<T,R,C>::solve(vnl_matrix<T> const& B)  const
00262 {
00263   vnl_matrix<T> x;                                      // solution matrix
00264   if (U_.rows() < U_.columns()) {                       // augment y with extra rows of
00265     vnl_matrix<T> yy(U_.rows(), B.columns(), T(0));     // zeros, so that it matches
00266     yy.update(B);                                       // cols of u.transpose. ???
00267     x = U_.conjugate_transpose() * yy;
00268   }
00269   else
00270     x = U_.conjugate_transpose() * B;
00271   for (unsigned long i = 0; i < x.rows(); ++i) {        // multiply with diagonal 1/W
00272     T weight = W_(i, i);
00273     if (weight != T(0)) // vnl_numeric_traits<T>::zero
00274       weight = T(1) / weight;
00275     for (unsigned long j = 0; j < x.columns(); ++j)
00276       x(i, j) *= weight;
00277   }
00278   x = V_ * x;                                           // premultiply with v.
00279   return x;
00280 }
00281 
00282 //: Solve the matrix-vector system M x = y, returning x.
00283 template <class T, unsigned int R, unsigned int C>
00284 vnl_vector_fixed<T, C> vnl_svd_fixed<T,R,C>::solve(vnl_vector_fixed<T, R> const& y)  const
00285 {
00286   vnl_vector_fixed<T, C> x;                   // Solution matrix.
00287   x = U_.conjugate_transpose() * y;
00288 
00289   for (unsigned i = 0; i < C; i++) {        // multiply with diagonal 1/W
00290     T weight = W_(i, i), zero_(0);
00291     if (weight != zero_)
00292       x[i] /= weight;
00293     else
00294       x[i] = zero_;
00295   }
00296   return V_ * x;                                // premultiply with v.
00297 }
00298 
00299 template <class T, unsigned int R, unsigned int C> // FIXME. this should implement the above, not the other way round.
00300 void vnl_svd_fixed<T,R,C>::solve(T const *y, T *x) const
00301 {
00302   solve(vnl_vector_fixed<T, R>(y)).copy_out(x);
00303 }
00304 
00305 //: Solve the matrix-vector system M x = y.
00306 // Assume that the singular values W have been preinverted by the caller.
00307 template <class T, unsigned int R, unsigned int C>
00308 void vnl_svd_fixed<T,R,C>::solve_preinverted(vnl_vector_fixed<T, R> const& y, vnl_vector_fixed<T, C>* x_out)  const
00309 {
00310   vnl_vector_fixed<T, C> x;              // solution matrix
00311   x = U_.conjugate_transpose() * y;
00312   for (unsigned i = 0; i < C; i++)  // multiply with diagonal W, assumed inverted
00313     x[i] *= W_(i, i);
00314 
00315   *x_out = V_ * x;                                      // premultiply with v.
00316 }
00317 
00318 //-----------------------------------------------------------------------------
00319 //: Return N s.t. M * N = 0
00320 template <class T, unsigned int R, unsigned int C>
00321 vnl_matrix<T> vnl_svd_fixed<T,R,C>::nullspace()  const
00322 {
00323   int k = rank();
00324   if (k == C)
00325     vcl_cerr << "vnl_svd_fixed<T>::nullspace() -- Matrix is full rank." << last_tol_ << vcl_endl;
00326   return nullspace(C-k);
00327 }
00328 
00329 //-----------------------------------------------------------------------------
00330 //: Return N s.t. M * N = 0
00331 template <class T, unsigned int R, unsigned int C>
00332 vnl_matrix<T> vnl_svd_fixed<T,R,C>::nullspace(int required_nullspace_dimension)  const
00333 {
00334   return V_.extract(V_.rows(), required_nullspace_dimension, 0, C - required_nullspace_dimension);
00335 }
00336 
00337 //-----------------------------------------------------------------------------
00338 //: Return N s.t. M' * N = 0
00339 template <class T, unsigned int R, unsigned int C>
00340 vnl_matrix<T> vnl_svd_fixed<T,R,C>::left_nullspace()  const
00341 {
00342   int k = rank();
00343   if (k == C)
00344     vcl_cerr << "vnl_svd_fixed<T>::left_nullspace() -- Matrix is full rank." << last_tol_ << vcl_endl;
00345   return U_.extract(U_.rows(), C-k, 0, k);
00346 }
00347 
00348 //:
00349 // \todo Implementation to be done yet; currently returns left_nullspace(). - PVr.
00350 template <class T, unsigned int R, unsigned int C>
00351 vnl_matrix<T> vnl_svd_fixed<T,R,C>::left_nullspace(int /*required_nullspace_dimension*/) const
00352 {
00353   return left_nullspace();
00354 }
00355 
00356 
00357 //-----------------------------------------------------------------------------
00358 //: Return the rightmost column of V.
00359 //  Does not check to see whether or not the matrix actually was rank-deficient -
00360 //  the caller is assumed to have examined W and decided that to his or her satisfaction.
00361 template <class T, unsigned int R, unsigned int C>
00362 vnl_vector_fixed <T,C> vnl_svd_fixed<T,R,C>::nullvector()  const
00363 {
00364   vnl_vector_fixed<T, C> ret;
00365   for (unsigned i = 0; i < C; ++i)
00366     ret(i) = V_(i, C-1);
00367   return ret;
00368 }
00369 
00370 //-----------------------------------------------------------------------------
00371 //: Return the rightmost column of U.
00372 //  Does not check to see whether or not the matrix actually was rank-deficient.
00373 template <class T, unsigned int R, unsigned int C>
00374 vnl_vector_fixed <T,R> vnl_svd_fixed<T,R,C>::left_nullvector()  const
00375 {
00376   vnl_vector_fixed<T,R> ret;
00377   const unsigned col = vcl_min(R, C) - 1;
00378   for (unsigned i = 0; i < R; ++i)
00379     ret(i) = U_(i, col);
00380   return ret;
00381 }
00382 
00383 //--------------------------------------------------------------------------------
00384 
00385 #undef VNL_SVD_FIXED_INSTANTIATE
00386 #define VNL_SVD_FIXED_INSTANTIATE(T , R , C ) \
00387 template class vnl_svd_fixed<T, R, C >; \
00388 template vcl_ostream& operator<<(vcl_ostream &, vnl_svd_fixed<T, R, C > const &)
00389 
00390 #endif // vnl_svd_fixed_txx_