lapack.C

Go to the documentation of this file.
00001 /*!@file Image/lapack.C */
00002 
00003 // //////////////////////////////////////////////////////////////////// //
00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005   //
00005 // by the University of Southern California (USC) and the iLab at USC.  //
00006 // See http://iLab.usc.edu for information about this project.          //
00007 // //////////////////////////////////////////////////////////////////// //
00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected //
00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency //
00010 // in Visual Environments, and Applications'' by Christof Koch and      //
00011 // Laurent Itti, California Institute of Technology, 2001 (patent       //
00012 // pending; application number 09/912,225 filed July 23, 2001; see      //
00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status).     //
00014 // //////////////////////////////////////////////////////////////////// //
00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit.       //
00016 //                                                                      //
00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can   //
00018 // redistribute it and/or modify it under the terms of the GNU General  //
00019 // Public License as published by the Free Software Foundation; either  //
00020 // version 2 of the License, or (at your option) any later version.     //
00021 //                                                                      //
00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope  //
00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the   //
00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      //
00025 // PURPOSE.  See the GNU General Public License for more details.       //
00026 //                                                                      //
00027 // You should have received a copy of the GNU General Public License    //
00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write   //
00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,   //
00030 // Boston, MA 02111-1307 USA.                                           //
00031 // //////////////////////////////////////////////////////////////////// //
00032 //
00033 // Primary maintainer for this file: Rob Peters <rjpeters at usc dot edu>
00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Image/lapack.C $
00035 // $Id: lapack.C 14627 2011-03-23 16:29:28Z lior $
00036 //
00037 
00038 #ifndef IMAGE_LAPACK_C_DEFINED
00039 #define IMAGE_LAPACK_C_DEFINED
00040 
00041 #ifdef HAVE_LAPACK
00042 
00043 #include "Image/lapack.H"
00044 
00045 #include "Image/Image.H"
00046 #include "Image/LinearAlgebraFlags.H"
00047 #include "Image/MatrixOps.H" // for transpose()
00048 #include "Image/f77lapack.H" // for dgesdd_()
00049 #include "Util/log.H"
00050 #include "rutz/trace.h"
00051 #include <stdio.h>
00052 
00053 namespace
00054 {
00055   // ############################################################
00056   /** Compute the Singular Value Decomposition.
00057 
00058      Compute the singular value decomposition (SVD) of a complex M-by-N
00059      matrix A, also computing the left and right singular vectors, by
00060      using a divide-and-conquer method.
00061 
00062      In lapack this is dgesdd. dgesdd computes the singular value
00063      decomposition (SVD) of a real M-by-N matrix A, optionally computing
00064      the left and/or right singular vectors, by using divide-and-conquer
00065      method. The SVD is written
00066 
00067      \f[A = U \cdot Sigma \cdot V^T\f]
00068 
00069      where Sigma is an M-by-N matrix which is zero except for its \c
00070      min(m,n) diagonal elements, U is an M-by-M orthogonal matrix, and V
00071      is an N-by-N orthogonal matrix.  The diagonal elements of SIGMA are
00072      the singular values of A; they are real and non- negative, and are
00073      returned in descending order.  The first \c min(m,n) columns of U
00074      and V are the left and right singular vectors of A.
00075 
00076      Note that the routine returns VT = V**T (transposed), not V.
00077 
00078      Now watch out: This routine has several modes of operation,
00079      depending on the size of the input matrices \c U and \c VT. This is:
00080 
00081      - If \c U is M-by-M and \c VT is N-by-N (the normal mode), then \e
00082      all left and right singular vectors are calculated and are returned
00083      in \c U and \c VT.
00084 
00085      - If \c U is M-by-min(M,N) and \c VT is min(M,N)-by-N, then the
00086      first min(M,N) left and right singular vectors are calculated,
00087      respectively, and are returned in \c U and \c VT. FIXME: needs verification.
00088 
00089      - If M >= N, \c U is of size 0, and \c VT is N-by-N, then the first
00090      N left singular vectors are calculated and returned in the first
00091      columns of \c A, and all right singular vectors are calculated and
00092      returned in \c VT. In this mode, \c U is unused. FIXME: needs verification.
00093 
00094      - If M < N, \c U is M-by-M, and \c VT is of size 0, then all left
00095      singular vectors are calculated and returned in \c U, and the first
00096      M right singular vectors are calculated and returned in the first M
00097      rows of \c A. In this mode, \c VT is unused. FIXME: needs verification.
00098 
00099      In any other combination of matrix sizes, an exception is thrown.
00100 
00101      @param A The M-by-N input matrix to be decomposed. It will be
00102      destroyed during the computation.
00103 
00104      @param Sigma A real-valued vector of length \c min(M,N) that will
00105      return the singular values. WATCH OUT: The length has to be \e
00106      exactly \c min(M,N) or else an exception will be thrown.
00107 
00108      @param U In the normal mode of calculation, the M-by-M matrix of
00109      the left singular vectors. In other modes this might be unused.
00110 
00111      @param VT In the normal mode of calculation, the N-by-N matrix of
00112      the right singular vectors. In other modes this might be unused.
00113 
00114      NOTE: This wrapper code is based heavily on code from the C++
00115      library "lapackpp" (http://www.sourceforge.net/projects/lapackpp)
00116      by Christian Stimming <stimming at tuhh dot de>, also licensed
00117      under the GPL. Specifically, this function is based on code from
00118      the source file "lapackpp/src/lasvd.cc", with the main changes
00119      being that the code is adapted to use our Image class rather than
00120      the LaGenMatDouble of lapackpp, and the preceding doc comment is
00121      taken verbatim from "lapackpp/include/lasvd.h".
00122    */
00123   void svd_lapack(Image<double>& A, Image<double>& Sigma,
00124                   Image<double>& U, Image<double>& VT)
00125   {
00126   GVX_TRACE(__PRETTY_FUNCTION__);
00127 
00128     char jobz = '?';
00129     f77_integer info = 0;
00130     int M = A.getWidth();
00131     int N = A.getHeight();
00132     int MNmin = std::min(M,N);
00133     f77_integer Ml = M;
00134     f77_integer Nl = N;
00135     f77_integer lda = A.getWidth();
00136 
00137     if (Sigma.getSize() != MNmin)
00138       LFATAL("Sigma is not of correct size");
00139 
00140     if ((U.getWidth() == M && U.getHeight() == M)
00141         && (VT.getWidth() == N && VT.getHeight() == N))
00142       jobz = 'A';
00143     else if ((U.getWidth() == M && U.getHeight() == MNmin)
00144              && (VT.getWidth() == MNmin && VT.getHeight() == N))
00145       jobz = 'S';
00146     else if (M >= N
00147              && U.getWidth() == 0
00148              && (VT.getWidth() == N && VT.getHeight() == N))
00149       jobz = 'O';
00150     else if (M < N
00151              && (U.getWidth() == M && U.getHeight() == M)
00152              && VT.getWidth() == 0)
00153       jobz = 'O';
00154     else
00155       LFATAL("U or VT is not of correct size");
00156 
00157     f77_integer ldu = U.getWidth();
00158     f77_integer ldvt = VT.getWidth();
00159 
00160     int liwork = 8*MNmin;
00161     Image<f77_integer> iwork(liwork, 1, NO_INIT);
00162 
00163     f77_integer lwork = -1;
00164     Image<double> work(1, 1, ZEROS);
00165     // Calculate the optimum temporary workspace
00166     dgesdd_(&jobz, &Ml, &Nl, A.getArrayPtr(), &lda,
00167             Sigma.getArrayPtr(), U.getArrayPtr(), &ldu,
00168             VT.getArrayPtr(), &ldvt,
00169             work.getArrayPtr(), &lwork, iwork.getArrayPtr(),
00170             &info);
00171     lwork = int(work[0]);
00172     work.resize(lwork, 1);
00173 
00174     // Now the real calculation
00175     dgesdd_(&jobz, &Ml, &Nl, A.getArrayPtr(), &lda,
00176             Sigma.getArrayPtr(), U.getArrayPtr(), &ldu,
00177             VT.getArrayPtr(), &ldvt,
00178             work.getArrayPtr(), &lwork, iwork.getArrayPtr(),
00179             &info);
00180 
00181     if (info != 0)
00182       LFATAL("Internal error in LAPACK: dgesdd() (info=%ld)", info);
00183   }
00184 
00185   //! Single-precision counterpart of svd_lapack()
00186   void svdf_lapack(Image<float>& A, Image<float>& Sigma,
00187                    Image<float>& U, Image<float>& VT)
00188   {
00189   GVX_TRACE(__PRETTY_FUNCTION__);
00190 
00191     char jobz = '?';
00192     f77_integer info = 0;
00193     int M = A.getWidth();
00194     int N = A.getHeight();
00195     int MNmin = std::min(M,N);
00196     f77_integer Ml = M;
00197     f77_integer Nl = N;
00198     f77_integer lda = A.getWidth();
00199 
00200     if (Sigma.getSize() != MNmin)
00201       LFATAL("Sigma is not of correct size");
00202 
00203     if ((U.getWidth() == M && U.getHeight() == M)
00204         && (VT.getWidth() == N && VT.getHeight() == N))
00205       jobz = 'A';
00206     else if ((U.getWidth() == M && U.getHeight() == MNmin)
00207              && (VT.getWidth() == MNmin && VT.getHeight() == N))
00208       jobz = 'S';
00209     else if (M >= N
00210              && U.getWidth() == 0
00211              && (VT.getWidth() == N && VT.getHeight() == N))
00212       jobz = 'O';
00213     else if (M < N
00214              && (U.getWidth() == M && U.getHeight() == M)
00215              && VT.getWidth() == 0)
00216       jobz = 'O';
00217     else
00218       LFATAL("U or VT is not of correct size");
00219 
00220     f77_integer ldu = U.getWidth();
00221     f77_integer ldvt = VT.getWidth();
00222 
00223     int liwork = 8*MNmin;
00224     Image<f77_integer> iwork(liwork, 1, NO_INIT);
00225 
00226     f77_integer lwork = -1;
00227     Image<float> work(1, 1, ZEROS);
00228     // Calculate the optimum temporary workspace
00229     sgesdd_(&jobz, &Ml, &Nl, A.getArrayPtr(), &lda,
00230             Sigma.getArrayPtr(), U.getArrayPtr(), &ldu,
00231             VT.getArrayPtr(), &ldvt,
00232             work.getArrayPtr(), &lwork, iwork.getArrayPtr(),
00233             &info);
00234     lwork = int(work[0]);
00235     work.resize(lwork, 1);
00236 
00237     // Now the real calculation
00238     sgesdd_(&jobz, &Ml, &Nl, A.getArrayPtr(), &lda,
00239             Sigma.getArrayPtr(), U.getArrayPtr(), &ldu,
00240             VT.getArrayPtr(), &ldvt,
00241             work.getArrayPtr(), &lwork, iwork.getArrayPtr(),
00242             &info);
00243 
00244     if (info != 0)
00245       LFATAL("Internal error in LAPACK: sgesdd() (info=%ld)", info);
00246   }
00247 
00248 } // end unnamed namespace
00249 
00250 // ######################################################################
00251 void lapack::svd(const Image<double>& A,
00252                  Image<double>& U, Image<double>& S, Image<double>& V,
00253                  const SvdFlag flags)
00254 {
00255   const int N = A.getWidth();
00256   const int M = A.getHeight();
00257 
00258   // we require M >= N
00259   if (M < N)
00260     LFATAL("expected M >= N, got M=%d and N=%d", M, N);
00261 
00262   Image<double> MMtxp = transpose(A);
00263   Image<double> SS(N, 1, ZEROS);
00264   Image<double> UUtxp(M,
00265                       (flags & SVD_FULL) ? M : N,
00266                       ZEROS);
00267   Image<double> VV(N, N, ZEROS);
00268 
00269   // lapack does everything in column-major format, but our
00270   // Image class is in row-major format, so we have to pass
00271   // tranposed matrices to lapack and the un-transpose the
00272   // result matrices
00273   svd_lapack(MMtxp, SS, UUtxp, VV);
00274 
00275   U = transpose(UUtxp);
00276   V = VV;
00277   S = Image<double>(N,
00278                     (flags & SVD_FULL) ? M : N,
00279                     ZEROS);
00280   for (int c = 0; c < N; ++c)
00281     S[Point2D<int>(c, c)] = SS[c];
00282 }
00283 
00284 // ######################################################################
00285 void lapack::svdf(const Image<float>& A,
00286                   Image<float>& U, Image<float>& S, Image<float>& V,
00287                   const SvdFlag flags)
00288 {
00289   const int N = A.getWidth();
00290   const int M = A.getHeight();
00291 
00292   // we require M >= N
00293   if (M < N)
00294     LFATAL("expected M >= N, got M=%d and N=%d", M, N);
00295 
00296   Image<float> MMtxp = transpose(A);
00297   Image<float> SS(N, 1, ZEROS);
00298   Image<float> UUtxp(M,
00299                      (flags & SVD_FULL) ? M : N,
00300                      ZEROS);
00301   Image<float> VV(N, N, ZEROS);
00302 
00303   // lapack does everything in column-major format, but our
00304   // Image class is in row-major format, so we have to pass
00305   // tranposed matrices to lapack and the un-transpose the
00306   // result matrices
00307   svdf_lapack(MMtxp, SS, UUtxp, VV);
00308 
00309   U = transpose(UUtxp);
00310   V = VV;
00311   S = Image<float>(N,
00312                    (flags & SVD_FULL) ? M : N,
00313                    ZEROS);
00314   for (int c = 0; c < N; ++c)
00315     S[Point2D<int>(c, c)] = SS[c];
00316 }
00317 
00318 // #################################################################
00319 Image<double> lapack::dgemv(const Image<double>* v,
00320                             const Image<double>* Mat)
00321 {
00322   GVX_TRACE(__PRETTY_FUNCTION__);
00323 
00324   const int wv = v->getWidth(), hv = v->getHeight();
00325   const int wm = Mat->getWidth(), hm = Mat->getHeight();
00326 
00327   ASSERT(wv == hm);
00328   ASSERT(hv == 1);
00329 
00330   Image<double> y(wm, hv /* == 1*/, NO_INIT);
00331 
00332   /* As with the matrix-matrix math (see lapack::dgemm() below),
00333      we're dealing with row-major vs. column-major issues here. The
00334      lapack function dgemv() actually wants to do M*v, with a
00335      column-major matrix right-multiplied by a column vector,
00336      returning the result in another column vector. But we actually
00337      want to do v*M, with a row-major matrix left-multiplied by a
00338      row vector, returning the result in a row vector. Those two are
00339      actually equivalent operations.
00340   */
00341 
00342   char trans = 'N';
00343   f77_integer M = Mat->getWidth(),
00344     N = Mat->getHeight(), lda = Mat->getWidth(),
00345     incv = 1, incy = 1;
00346 
00347   double alpha = 1.0, beta = 0.0;
00348 
00349   assert(Mat->getWidth() == y.getSize());
00350   assert(Mat->getHeight() == v->getSize());
00351 
00352   {GVX_TRACE("dgemv");
00353   dgemv_(&trans, &M, &N, &alpha,
00354          Mat->getArrayPtr(), &lda,
00355          v->getArrayPtr(), &incv,
00356          &beta, y.getArrayPtr(), &incy);
00357   }
00358 
00359   return y;
00360 }
00361 
00362 // #################################################################
00363 Image<float> lapack::sgemv(const Image<float>* v,
00364                            const Image<float>* Mat)
00365 {
00366   GVX_TRACE(__PRETTY_FUNCTION__);
00367 
00368   const int wv = v->getWidth(), hv = v->getHeight();
00369   const int wm = Mat->getWidth(), hm = Mat->getHeight();
00370 
00371   ASSERT(wv == hm);
00372   ASSERT(hv == 1);
00373 
00374   Image<float> y(wm, hv /* == 1*/, NO_INIT);
00375 
00376   // see comments in lapack::dgemv() above
00377 
00378   char trans = 'N';
00379   f77_integer M = Mat->getWidth(),
00380     N = Mat->getHeight(), lda = Mat->getWidth(),
00381     incv = 1, incy = 1;
00382 
00383   float alpha = 1.0, beta = 0.0;
00384 
00385   assert(Mat->getWidth() == y.getSize());
00386   assert(Mat->getHeight() == v->getSize());
00387 
00388   {GVX_TRACE("sgemv");
00389   sgemv_(&trans, &M, &N, &alpha,
00390          Mat->getArrayPtr(), &lda,
00391          v->getArrayPtr(), &incv,
00392          &beta, y.getArrayPtr(), &incy);
00393   }
00394 
00395   return y;
00396 }
00397 
00398 // #################################################################
00399 Image<double> lapack::dgemm(const Image<double>* A,
00400                             const Image<double>* B)
00401 {
00402   GVX_TRACE(__PRETTY_FUNCTION__);
00403 
00404   /* This is slightly tricky to get the conversions between row-major
00405      and column-major just right. dgemm() expects to multiply two
00406      column-major matrices, giving C=A*B. But what we actually have
00407      are two row-major matrices. Or, if we swap getWidth() with
00408      getHeight(), then what we have are two transposed column-major
00409      matrices. So as far as lapack is concerned, the layout of our
00410      input matrices means we have A^T and B^T. Then, if we swap the
00411      order in which the matrices are passed to lapack we can have it
00412      compute (B^T * A^T), which is just (A*B)^T. So what lapack is
00413      computing for us is the column-major transpose C^T, but that is
00414      exactly the UN-transposed row-major matrix C that we want.
00415 
00416      (The tricky part for me was realizing that I needed no explicit
00417      transpose() calls, nor did I need to pass any 'T' arguments to
00418      tell lapack to do any transposition).
00419   */
00420 
00421   Image<double> C(B->getWidth(), A->getHeight(), NO_INIT);
00422 
00423   char t = 'N'; // 'N' means "don't transpose", 'T' means "do transpose"
00424 
00425   f77_integer m = B->getWidth(), k = B->getHeight(), n = A->getHeight();
00426   f77_integer lda = B->getWidth(), ldb = A->getWidth(), ldc = C.getWidth();
00427   assert(B->getWidth() == C.getWidth());
00428   assert(A->getHeight() == C.getHeight());
00429   assert(B->getHeight() == A->getWidth());
00430 
00431   double alpha = 1.0, beta = 0.0;
00432 
00433   {GVX_TRACE("dgemm");
00434   dgemm_(&t, &t, &m, &n, &k, &alpha,
00435          B->getArrayPtr(), &lda,
00436          A->getArrayPtr(), &ldb,
00437          &beta, C.getArrayPtr(), &ldc);
00438   }
00439 
00440   return C;
00441 }
00442 
00443 // #################################################################
00444 Image<float> lapack::sgemm(const Image<float>* A,
00445                            const Image<float>* B)
00446 {
00447   GVX_TRACE(__PRETTY_FUNCTION__);
00448 
00449   // see comments in lapack::dgemm() above
00450 
00451   Image<float> C(B->getWidth(), A->getHeight(), NO_INIT);
00452 
00453   char t = 'N'; // 'N' means "don't transpose", 'T' means "do transpose"
00454 
00455   f77_integer m = B->getWidth(), k = B->getHeight(), n = A->getHeight();
00456   f77_integer lda = B->getWidth(), ldb = A->getWidth(), ldc = C.getWidth();
00457   assert(B->getWidth() == C.getWidth());
00458   assert(A->getHeight() == C.getHeight());
00459   assert(B->getHeight() == A->getWidth());
00460 
00461   float alpha = 1.0, beta = 0.0;
00462 
00463   {GVX_TRACE("sgemm");
00464   sgemm_(&t, &t, &m, &n, &k, &alpha,
00465          B->getArrayPtr(), &lda,
00466          A->getArrayPtr(), &ldb,
00467          &beta, C.getArrayPtr(), &ldc);
00468   }
00469 
00470   return C;
00471 }
00472 
00473 // #################################################################
00474 Image<double> lapack::dpotrf(const Image<double>* Mat)
00475 {
00476 
00477   GVX_TRACE(__PRETTY_FUNCTION__);
00478 
00479 
00480   Image<double> A = *Mat;
00481 
00482   f77_integer order =  Mat->getWidth(), lda = Mat->getWidth(),
00483               flags = 0;
00484 
00485   {GVX_TRACE("dpotrf");
00486 
00487   char uplo = 'L';
00488 
00489   dpotrf_(&uplo, &order,
00490          A.getArrayPtr(), &lda, &flags);
00491   }
00492 
00493   return A;
00494 }
00495 
00496 double lapack::det(const Image<double>* Mat)
00497 {
00498 
00499   GVX_TRACE(__PRETTY_FUNCTION__);
00500 
00501   //Derived from http://vismod.media.mit.edu/pub/tpminka/MRSAR/lapack.c
00502 
00503   Image<double> A = *Mat;
00504 
00505   f77_integer m =  Mat->getHeight(), n =  Mat->getWidth(), lda = Mat->getWidth(), info = 0;
00506   f77_integer ipvt[std::min(m,n)];
00507 
00508   {
00509     GVX_TRACE("dgetrf");
00510 
00511     dgetrf_(&m,&n, A.getArrayPtr(), &lda, ipvt, &info);
00512     if (info > 0)
00513     {
00514       LINFO("ERROR: singuler matrix");
00515       return -1.0;
00516     }
00517   }
00518 
00519   //Take the product of the diagonal elements
00520   double det = 1.0;
00521   bool neg = false;
00522   for(int i=0; i<m; i++)
00523   {
00524     det *= A.getVal(i,i);
00525     if (ipvt[i] != (i+1))
00526       neg = !neg;
00527   }
00528 
00529   /* Since A is an LU decomposition of a rowwise permutation of A,
00530      multiply by appropriate sign */
00531   return neg ? -det : det;
00532 }
00533 
00534 
00535 #endif // HAVE_LAPACK
00536 
00537 // ######################################################################
00538 /* So things look consistent in everyone's emacs... */
00539 /* Local Variables: */
00540 /* indent-tabs-mode: nil */
00541 /* End: */
00542 
00543 #endif // IMAGE_LAPACK_C_DEFINED
Generated on Sun May 8 08:40:55 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3