arpack.cpp

Go to the documentation of this file.
00001 /*
00002  * This program is free software; you can redistribute it and/or modify
00003  * it under the terms of the GNU General Public License as published by
00004  * the Free Software Foundation; either version 3 of the License, or
00005  * (at your option) any later version.
00006  *
00007  * Written (W) 2011 Sergey Lisitsyn
00008  * Copyright (C) 2011 Berlin Institute of Technology and Max-Planck-Society
00009  */
00010 
00011 #include <shogun/mathematics/arpack.h>
00012 #ifdef HAVE_ARPACK
00013 #ifdef HAVE_LAPACK
00014 #include <shogun/io/SGIO.h>
00015 #include <string.h>
00016 #include <shogun/mathematics/lapack.h>
00017 
00018 #ifdef HAVE_SUPERLU
00019 #include <superlu/slu_ddefs.h>
00020 #endif
00021 
00022 
00024 extern "C" void dsaupd_(int *ido, char *bmat, int *n, char *which,
00025             int *nev, double *tol, double *resid, int *ncv,
00026             double *v, int *ldv, int *iparam, int *ipntr,
00027             double *workd, double *workl, int *lworkl,
00028             int *info);
00029 
00031 extern "C" void dseupd_(int *rvec, char *All, int *select, double *d,
00032             double *v, int *ldv, double *sigma,
00033             char *bmat, int *n, char *which, int *nev,
00034             double *tol, double *resid, int *ncv, double *tv,
00035             int *tldv, int *iparam, int *ipntr, double *workd,
00036             double *workl, int *lworkl, int *ierr);
00037 
00038 using namespace shogun;
00039 
00040 namespace shogun
00041 {
00042 void arpack_dsxupd(double* matrix, double* rhs, bool is_rhs_diag, int n, int nev,
00043                    const char* which, bool use_superlu, int mode, bool pos, bool cov,
00044                    double shift, double tolerance, double* eigenvalues,
00045                    double* eigenvectors, int& status)
00046 {
00047     // loop vars
00048     int i,j;
00049 
00050     if (use_superlu)
00051     {
00052 #ifndef HAVE_SUPERLU
00053         use_superlu=false;
00054         SG_SINFO("Required SUPERLU isn't available in this configuration\n");
00055 #endif
00056     }
00057 
00058     // check if nev is greater than n
00059     if (nev>n)
00060         SG_SERROR("Number of required eigenpairs is greater than order of the matrix\n");
00061 
00062     // check specified mode
00063     if (mode!=1 && mode!=3)
00064         SG_SERROR("Mode not supported yet\n");
00065 
00066     // init ARPACK's reverse communication parameter
00067     // (should be zero initially)
00068     int ido = 0;
00069 
00070     // specify general or non-general eigenproblem will be solved
00071     // w.r.t. to given rhs_diag
00072     char bmat[2] = "I";
00073     if (rhs)
00074         bmat[0] = 'G';
00075 
00076     // init tolerance (zero means machine precision)
00077     double tol = tolerance;
00078 
00079     // allocate array to hold residuals
00080     double* resid = SG_MALLOC(double, n);
00081     // fill residual vector with ones
00082     for (i=0; i<n; i++)
00083         resid[i] = 1.0;
00084 
00085     // set number of Lanczos basis vectors to be used
00086     // (with max(4*nev,n) sufficient for most tasks)
00087     int ncv = nev*4>n ? n : nev*4;
00088 
00089     // allocate array 'v' for dsaupd routine usage
00090     int ldv = n;
00091     double* v = SG_MALLOC(double, ldv*ncv);
00092 
00093     // init array for i/o params for routine
00094     int* iparam = SG_MALLOC(int, 11);
00095     // specify method for selecting implicit shifts (1 - exact shifts)
00096     iparam[0] = 1;
00097     // specify max number of iterations
00098     iparam[2] = 3*n;
00099     // set the computation mode (1 for regular or 3 for shift-inverse)
00100     iparam[6] = mode;
00101 
00102     // init array indicating locations of vectors for routine callback
00103     int* ipntr = SG_CALLOC(int, 11);
00104 
00105     // allocate workaround arrays
00106     double* workd = SG_MALLOC(double, 3*n);
00107     int lworkl = ncv*(ncv+8);
00108     double* workl = SG_MALLOC(double, lworkl);
00109 
00110     // init info holding status (1 means that residual vector is provided initially)
00111     int info = 0;
00112 
00113     // which eigenpairs to find
00114     char* which_ = strdup(which);
00115     // All
00116     char* all_ = strdup("A");
00117 
00118     // ipiv for LUP factorization
00119     int* ipiv = NULL;
00120 
00121 #ifdef HAVE_SUPERLU
00122     char equed[1];
00123     void* work = NULL;
00124     int lwork = 0;
00125     SuperMatrix slu_A, slu_L, slu_U, slu_B, slu_X;
00126     superlu_options_t options;
00127     SuperLUStat_t stat;
00128     mem_usage_t mem_usage;
00129     int *perm_c=NULL, *perm_r=NULL, *etree=NULL;
00130     double *R=NULL, *C=NULL;
00131     if (mode==3 && use_superlu)
00132     {
00133         perm_c = intMalloc(n);
00134         perm_r = intMalloc(n);
00135         etree = intMalloc(n);
00136         R = doubleMalloc(n);
00137         C = doubleMalloc(n);
00138     }
00139     double ferr;
00140     double berr;
00141     double rcond;
00142     double rpg;
00143     int slu_info;
00144     double* slu_Bv=NULL;
00145     double* slu_Xv=NULL;
00146 #endif
00147 
00148     // shift-invert mode init
00149     if (mode==3)
00150     {
00151         // subtract shift from main diagonal if necessary
00152         if (shift!=0.0)
00153         {
00154             SG_SDEBUG("ARPACK: Subtracting shift\n");
00155             // if right hand side diagonal matrix is provided
00156 
00157             if (rhs && is_rhs_diag)
00158                 // subtract I*diag(rhs_diag)
00159                 for (i=0; i<n; i++)
00160                     matrix[i*n+i] -= rhs[i]*shift;
00161 
00162             else
00163                 // subtract I
00164                 for (i=0; i<n; i++)
00165                     matrix[i*n+i] -= shift;
00166         }
00167 
00168         if (use_superlu)
00169         {
00170 #ifdef HAVE_SUPERLU
00171             SG_SDEBUG("SUPERLU: Constructing sparse matrix.\n");
00172             int nnz = 0;
00173             // get number of non-zero elements
00174             for (i=0; i<n*n; i++)
00175             {
00176                 if (matrix[i]!=0.0)
00177                     nnz++;
00178             }
00179             // allocate arrays to store sparse matrix
00180             double* val = doubleMalloc(nnz);
00181             int* rowind = intMalloc(nnz);
00182             int* colptr = intMalloc(n+1);
00183             nnz = 0;
00184             // construct sparse matrix
00185             for (i=0; i<n; i++)
00186             {
00187                 colptr[i] = nnz;
00188                 for (j=0; j<n; j++)
00189                 {
00190                     if (matrix[i*n+j]!=0.0)
00191                     {
00192                         val[nnz] = matrix[i*n+j];
00193                         rowind[nnz] = j;
00194                         nnz++;
00195                     }
00196                 }
00197             }
00198             colptr[i] = nnz;
00199             // create CCS matrix
00200             dCreate_CompCol_Matrix(&slu_A,n,n,nnz,val,rowind,colptr,SLU_NC,SLU_D,SLU_GE);
00201 
00202             // initialize options
00203             set_default_options(&options);
00204             //options.Equil = YES;
00205             //options.SymmetricMode = YES;
00206 
00207             options.SymmetricMode = YES;
00208             options.ColPerm = MMD_AT_PLUS_A;
00209             options.DiagPivotThresh = 0.001;
00210             StatInit(&stat);
00211 
00212             // factorize
00213             slu_info = 0;
00214             slu_Bv = doubleMalloc(n);
00215             slu_Xv = doubleMalloc(n);
00216             dCreate_Dense_Matrix(&slu_B,n,1,slu_Bv,n,SLU_DN,SLU_D,SLU_GE);
00217             dCreate_Dense_Matrix(&slu_X,n,1,slu_Xv,n,SLU_DN,SLU_D,SLU_GE);
00218             slu_B.ncol = 0;
00219             SG_SDEBUG("SUPERLU: Factorizing\n");
00220             dgssvx(&options, &slu_A, perm_c, perm_r, etree, equed, R, C,
00221                    &slu_L, &slu_U, work, lwork, &slu_B, &slu_X, &rpg, &rcond, &ferr, &berr,
00222                    &mem_usage,&stat,&slu_info);
00223             slu_B.ncol = 1;
00224             if (slu_info)
00225             {
00226                 SG_SERROR("SUPERLU: Failed to factorize matrix, got %d code\n", slu_info);
00227             }
00228             options.Fact = FACTORED;
00229 #endif
00230         }
00231         else
00232         {
00233             // compute factorization according to pos value
00234             if (pos)
00235             {
00236                 // with Cholesky
00237                 SG_SDEBUG("ARPACK: Using Cholesky factorization.\n");
00238                 clapack_dpotrf(CblasColMajor,CblasUpper,n,matrix,n);
00239             }
00240             else
00241             {
00242                 // with LUP
00243                 SG_SDEBUG("ARPACK: Using LUP factorization.\n");
00244                 ipiv = SG_CALLOC(int, n);
00245                 clapack_dgetrf(CblasColMajor,n,n,matrix,n,ipiv);
00246             }
00247         }
00248     }
00249     // main computation loop
00250     SG_SDEBUG("ARPACK: Starting main computation DSAUPD loop.\n");
00251     do
00252     {
00253         dsaupd_(&ido, bmat, &n, which_, &nev, &tol, resid,
00254                 &ncv, v, &ldv, iparam, ipntr, workd, workl,
00255                 &lworkl, &info);
00256 
00257         if ((ido==1)||(ido==-1)||(ido==2))
00258         {
00259             if (mode==1)
00260             {
00261                 if (!cov)
00262                 {
00263                     // compute (workd+ipntr[1]-1) = A*(workd+ipntr[0]-1)
00264                     cblas_dsymv(CblasColMajor,CblasUpper,
00265                                 n,1.0,matrix,n,
00266                                 (workd+ipntr[0]-1),1,
00267                                 0.0,(workd+ipntr[1]-1),1);
00268                 }
00269                 else
00270                 {
00271                     cblas_dsymv(CblasColMajor,CblasUpper,
00272                                 n,1.0,matrix,n,
00273                                 (workd+ipntr[0]-1),1,
00274                                 0.0,(workd+ipntr[1]-1),1);
00275                     cblas_dsymv(CblasColMajor,CblasUpper,
00276                                 n,1.0,matrix,n,
00277                                 (workd+ipntr[1]-1),1,
00278                                 0.0,(workd+ipntr[0]-1),1);
00279                     cblas_dcopy(n,workd+ipntr[0]-1,1,workd+ipntr[1]-1,1);
00280                 }
00281             }
00282             if (mode==3)
00283             {
00284                 if (!rhs)
00285                 {
00286                     if (use_superlu)
00287                     {
00288 #ifdef HAVE_SUPERLU
00289                         // treat workd+ipntr(0) as B
00290                         for (i=0; i<n; i++)
00291                             slu_Bv[i] = (workd+ipntr[0]-1)[i];
00292                         slu_info = 0;
00293                         // solve
00294                         dgssvx(&options, &slu_A, perm_c, perm_r, etree, equed, R, C,
00295                                &slu_L, &slu_U, work, lwork, &slu_B, &slu_X, &rpg, &rcond,
00296                                &ferr, &berr, &mem_usage, &stat, &slu_info);
00297                         if (slu_info)
00298                             SG_SERROR("SUPERLU: GOT %d\n", slu_info);
00299                         // move elements from resulting X to workd+ipntr(1)
00300                         for (i=0; i<n; i++)
00301                             (workd+ipntr[1]-1)[i] = slu_Xv[i];
00302 #endif
00303                     }
00304                     else
00305                     {
00306                         for (i=0; i<n; i++)
00307                             (workd+ipntr[1]-1)[i] = (workd+ipntr[0]-1)[i];
00308                             if (pos)
00309                             clapack_dpotrs(CblasColMajor,CblasUpper,n,1,matrix,n,(workd+ipntr[1]-1),n);
00310                         else
00311                             clapack_dgetrs(CblasColMajor,CblasNoTrans,n,1,matrix,n,ipiv,(workd+ipntr[1]-1),n);
00312                     }
00313                 }
00314                 else
00315                 // HAVE RHS
00316                 {
00317                     if (ido==-1)
00318                     {
00319                         if (use_superlu)
00320                         {
00321 #ifdef HAVE_SUPERLU
00322                             for (i=0; i<n; i++)
00323                                 slu_Bv[i] = (workd+ipntr[0]-1)[i];
00324                             slu_info = 0;
00325                             dgssvx(&options, &slu_A, perm_c, perm_r, etree, equed, R, C,
00326                                    &slu_L, &slu_U, work, lwork, &slu_B, &slu_X, &rpg, &rcond,
00327                                    &ferr, &berr, &mem_usage, &stat, &slu_info);
00328                             if (slu_info)
00329                                 SG_SERROR("SUPERLU: GOT %d\n", slu_info);
00330                             for (i=0; i<n; i++)
00331                                 (workd+ipntr[1]-1)[i] = slu_Xv[i];
00332 #endif
00333                         }
00334                         else
00335                         {
00336                             for (i=0; i<n; i++)
00337                                 (workd+ipntr[1]-1)[i] = rhs[i]*(workd+ipntr[0]-1)[i];
00338                             if (pos)
00339                                 clapack_dpotrs(CblasColMajor,CblasUpper,n,1,matrix,n,(workd+ipntr[1]-1),n);
00340                             else
00341                                 clapack_dgetrs(CblasColMajor,CblasNoTrans,n,1,matrix,n,ipiv,(workd+ipntr[1]-1),n);
00342                         }
00343                     }
00344                     if (ido==1)
00345                     {
00346                         if (use_superlu)
00347                         {
00348 #ifdef HAVE_SUPERLU
00349                             for (i=0; i<n; i++)
00350                                 slu_Bv[i] = (workd+ipntr[2]-1)[i];
00351                             slu_info = 0;
00352                             dgssvx(&options, &slu_A, perm_c, perm_r, etree, equed, R, C,
00353                                    &slu_L, &slu_U, work, lwork, &slu_B, &slu_X, &rpg, &rcond,
00354                                    &ferr, &berr, &mem_usage, &stat, &slu_info);
00355                             if (slu_info)
00356                                 SG_SERROR("SUPERLU: GOT %d\n", slu_info);
00357                             for (i=0; i<n; i++)
00358                                 (workd+ipntr[1]-1)[i] = slu_Xv[i];
00359 #endif
00360                         }
00361                         else
00362                         {
00363                             for (i=0; i<n; i++)
00364                                 (workd+ipntr[1]-1)[i] = (workd+ipntr[2]-1)[i];
00365                             if (pos)
00366                                 clapack_dpotrs(CblasColMajor,CblasUpper,n,1,matrix,n,(workd+ipntr[1]-1),n);
00367                             else
00368                                 clapack_dgetrs(CblasColMajor,CblasNoTrans,n,1,matrix,n,ipiv,(workd+ipntr[1]-1),n);
00369                         }
00370                     }
00371                     if (ido==2)
00372                     {
00373                         if (is_rhs_diag)
00374                         {
00375                             for (i=0; i<n; i++)
00376                                 (workd+ipntr[1]-1)[i] = rhs[i]*(workd+ipntr[0]-1)[i];
00377                         }
00378                         else
00379                         {
00380                             cblas_dsymv(CblasColMajor,CblasUpper,
00381                                         n,1.0,rhs,n,
00382                                         (workd+ipntr[0]-1),1,
00383                                         0.0,(workd+ipntr[1]-1),1);
00384                         }
00385                     }
00386                 }
00387             }
00388         }
00389     } while ((ido==1)||(ido==-1)||(ido==2));
00390 
00391     if (!pos && mode==3) SG_FREE(ipiv);
00392 
00393     if (mode==3 && use_superlu)
00394     {
00395 #ifdef HAVE_SUPERLU
00396         SUPERLU_FREE(slu_Bv);
00397         SUPERLU_FREE(slu_Xv);
00398         SUPERLU_FREE(perm_r);
00399         SUPERLU_FREE(perm_c);
00400         SUPERLU_FREE(R);
00401         SUPERLU_FREE(C);
00402         SUPERLU_FREE(etree);
00403         if (lwork!=0) SUPERLU_FREE(work);
00404         Destroy_CompCol_Matrix(&slu_A);
00405         StatFree(&stat);
00406         Destroy_SuperMatrix_Store(&slu_B);
00407         Destroy_SuperMatrix_Store(&slu_X);
00408         Destroy_SuperNode_Matrix(&slu_L);
00409         Destroy_CompCol_Matrix(&slu_U);
00410 #endif
00411     }
00412 
00413     // check if DSAUPD failed
00414     if (info<0)
00415     {
00416         if ((info<=-1)&&(info>=-6))
00417             SG_SWARNING("ARPACK: DSAUPD failed. Wrong parameter passed.\n");
00418         else if (info==-7)
00419             SG_SWARNING("ARPACK: DSAUPD failed. Workaround array size is not sufficient.\n");
00420         else
00421             SG_SWARNING("ARPACK: DSAUPD failed. Error code: %d.\n", info);
00422 
00423         status = info;
00424     }
00425     else
00426     {
00427         if (info==1)
00428             SG_SWARNING("ARPACK: Maximum number of iterations reached.\n");
00429 
00430         // allocate select for dseupd
00431         int* select = SG_MALLOC(int, ncv);
00432         // allocate d to hold eigenvalues
00433         double* d = SG_MALLOC(double, 2*ncv);
00434         // sigma for dseupd
00435         double sigma = shift;
00436 
00437         // init ierr indicating dseupd possible errors
00438         int ierr = 0;
00439 
00440         // specify that eigenvectors are going to be computed too
00441         int rvec = 1;
00442 
00443         SG_SDEBUG("APRACK: Starting DSEUPD.\n");
00444 
00445         // call dseupd_ routine
00446         dseupd_(&rvec, all_, select, d, v, &ldv, &sigma, bmat,
00447                 &n, which_, &nev, &tol, resid, &ncv, v, &ldv,
00448                 iparam, ipntr, workd, workl, &lworkl, &ierr);
00449 
00450         // check for errors
00451         if (ierr!=0)
00452         {
00453             SG_SWARNING("ARPACK: DSEUPD failed with status %d.\n", ierr);
00454             status = -1;
00455         }
00456         else
00457         {
00458             SG_SDEBUG("ARPACK: Storing eigenpairs.\n");
00459 
00460             // store eigenpairs to specified arrays
00461             for (i=0; i<nev; i++)
00462             {
00463                 eigenvalues[i] = d[i];
00464 
00465                 for (j=0; j<n; j++)
00466                     eigenvectors[j*nev+i] = v[i*n+j];
00467             }
00468         }
00469 
00470         // cleanup
00471         SG_FREE(select);
00472         SG_FREE(d);
00473     }
00474 
00475     // cleanup
00476     SG_FREE(all_);
00477     SG_FREE(which_);
00478     SG_FREE(resid);
00479     SG_FREE(v);
00480     SG_FREE(iparam);
00481     SG_FREE(ipntr);
00482     SG_FREE(workd);
00483     SG_FREE(workl);
00484 };
00485 }
00486 #endif /* HAVE_LAPACK */
00487 #endif /* HAVE_ARPACK */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

SHOGUN Machine Learning Toolbox - Documentation