Home | History | Annotate | Download | only in spbench
      1 // This file is part of Eigen, a lightweight C++ template library
      2 // for linear algebra.
      3 //
      4 // Copyright (C) 2012 Dsir Nuentsa-Wakam <desire.nuentsa_wakam (at) inria.fr>
      5 //
      6 // This Source Code Form is subject to the terms of the Mozilla
      7 // Public License v. 2.0. If a copy of the MPL was not distributed
      8 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
      9 
     10 
     11 #include <iostream>
     12 #include <fstream>
     13 #include "Eigen/SparseCore"
     14 #include <bench/BenchTimer.h>
     15 #include <cstdlib>
     16 #include <string>
     17 #include <Eigen/Cholesky>
     18 #include <Eigen/Jacobi>
     19 #include <Eigen/Householder>
     20 #include <Eigen/IterativeLinearSolvers>
     21 #include <unsupported/Eigen/IterativeSolvers>
     22 #include <Eigen/LU>
     23 #include <unsupported/Eigen/SparseExtra>
     24 
     25 #ifdef EIGEN_CHOLMOD_SUPPORT
     26 #include <Eigen/CholmodSupport>
     27 #endif
     28 
     29 #ifdef EIGEN_UMFPACK_SUPPORT
     30 #include <Eigen/UmfPackSupport>
     31 #endif
     32 
     33 #ifdef EIGEN_PARDISO_SUPPORT
     34 #include <Eigen/PardisoSupport>
     35 #endif
     36 
     37 #ifdef EIGEN_SUPERLU_SUPPORT
     38 #include <Eigen/SuperLUSupport>
     39 #endif
     40 
     41 #ifdef EIGEN_PASTIX_SUPPORT
     42 #include <Eigen/PaStiXSupport>
     43 #endif
     44 
     45 // CONSTANTS
     46 #define EIGEN_UMFPACK  0
     47 #define EIGEN_SUPERLU  1
     48 #define EIGEN_PASTIX  2
     49 #define EIGEN_PARDISO  3
     50 #define EIGEN_BICGSTAB  4
     51 #define EIGEN_BICGSTAB_ILUT  5
     52 #define EIGEN_GMRES 6
     53 #define EIGEN_GMRES_ILUT 7
     54 #define EIGEN_SIMPLICIAL_LDLT  8
     55 #define EIGEN_CHOLMOD_LDLT  9
     56 #define EIGEN_PASTIX_LDLT  10
     57 #define EIGEN_PARDISO_LDLT  11
     58 #define EIGEN_SIMPLICIAL_LLT  12
     59 #define EIGEN_CHOLMOD_SUPERNODAL_LLT  13
     60 #define EIGEN_CHOLMOD_SIMPLICIAL_LLT  14
     61 #define EIGEN_PASTIX_LLT  15
     62 #define EIGEN_PARDISO_LLT  16
     63 #define EIGEN_CG  17
     64 #define EIGEN_CG_PRECOND  18
     65 #define EIGEN_ALL_SOLVERS  19
     66 
     67 using namespace Eigen;
     68 using namespace std;
     69 
     70 struct Stats{
     71   ComputationInfo info;
     72   double total_time;
     73   double compute_time;
     74   double solve_time;
     75   double rel_error;
     76   int memory_used;
     77   int iterations;
     78   int isavail;
     79   int isIterative;
     80 };
     81 
     82 // Global variables for input parameters
     83 int MaximumIters; // Maximum number of iterations
     84 double RelErr; // Relative error of the computed solution
     85 
     86 template<typename T> inline typename NumTraits<T>::Real test_precision() { return NumTraits<T>::dummy_precision(); }
     87 template<> inline float test_precision<float>() { return 1e-3f; }
     88 template<> inline double test_precision<double>() { return 1e-6; }
     89 template<> inline float test_precision<std::complex<float> >() { return test_precision<float>(); }
     90 template<> inline double test_precision<std::complex<double> >() { return test_precision<double>(); }
     91 
     92 void printStatheader(std::ofstream& out)
     93 {
     94   int LUcnt = 0;
     95   string LUlist =" ", LLTlist = "<TH > LLT", LDLTlist = "<TH > LDLT ";
     96 
     97 #ifdef EIGEN_UMFPACK_SUPPORT
     98   LUlist += "<TH > UMFPACK "; LUcnt++;
     99 #endif
    100 #ifdef EIGEN_SUPERLU_SUPPORT
    101   LUlist += "<TH > SUPERLU "; LUcnt++;
    102 #endif
    103 #ifdef EIGEN_CHOLMOD_SUPPORT
    104   LLTlist += "<TH > CHOLMOD SP LLT<TH > CHOLMOD LLT";
    105   LDLTlist += "<TH>CHOLMOD LDLT";
    106 #endif
    107 #ifdef EIGEN_PARDISO_SUPPORT
    108   LUlist += "<TH > PARDISO LU";  LUcnt++;
    109   LLTlist += "<TH > PARDISO LLT";
    110   LDLTlist += "<TH > PARDISO LDLT";
    111 #endif
    112 #ifdef EIGEN_PASTIX_SUPPORT
    113   LUlist += "<TH > PASTIX LU";  LUcnt++;
    114   LLTlist += "<TH > PASTIX LLT";
    115   LDLTlist += "<TH > PASTIX LDLT";
    116 #endif
    117 
    118   out << "<TABLE border=\"1\" >\n ";
    119   out << "<TR><TH>Matrix <TH> N <TH> NNZ <TH> ";
    120   if (LUcnt) out << LUlist;
    121   out << " <TH >BiCGSTAB <TH >BiCGSTAB+ILUT"<< "<TH >GMRES+ILUT" <<LDLTlist << LLTlist <<  "<TH> CG "<< std::endl;
    122 }
    123 
    124 
    125 template<typename Solver, typename Scalar>
    126 Stats call_solver(Solver &solver, const typename Solver::MatrixType& A, const Matrix<Scalar, Dynamic, 1>& b, const Matrix<Scalar, Dynamic, 1>& refX)
    127 {
    128   Stats stat;
    129   Matrix<Scalar, Dynamic, 1> x;
    130   BenchTimer timer;
    131   timer.reset();
    132   timer.start();
    133   solver.compute(A);
    134   if (solver.info() != Success)
    135   {
    136     stat.info = NumericalIssue;
    137     std::cerr << "Solver failed ... \n";
    138     return stat;
    139   }
    140   timer.stop();
    141   stat.compute_time = timer.value();
    142 
    143   timer.reset();
    144   timer.start();
    145   x = solver.solve(b);
    146   if (solver.info() == NumericalIssue)
    147   {
    148     stat.info = NumericalIssue;
    149     std::cerr << "Solver failed ... \n";
    150     return stat;
    151   }
    152 
    153   timer.stop();
    154   stat.solve_time = timer.value();
    155   stat.total_time = stat.solve_time + stat.compute_time;
    156   stat.memory_used = 0;
    157   // Verify the relative error
    158   if(refX.size() != 0)
    159     stat.rel_error = (refX - x).norm()/refX.norm();
    160   else
    161   {
    162     // Compute the relative residual norm
    163     Matrix<Scalar, Dynamic, 1> temp;
    164     temp = A * x;
    165     stat.rel_error = (b-temp).norm()/b.norm();
    166   }
    167   if ( stat.rel_error > RelErr )
    168   {
    169     stat.info = NoConvergence;
    170     return stat;
    171   }
    172   else
    173   {
    174     stat.info = Success;
    175     return stat;
    176   }
    177 }
    178 
    179 template<typename Solver, typename Scalar>
    180 Stats call_directsolver(Solver& solver, const typename Solver::MatrixType& A, const Matrix<Scalar, Dynamic, 1>& b, const Matrix<Scalar, Dynamic, 1>& refX)
    181 {
    182     Stats stat;
    183     stat = call_solver(solver, A, b, refX);
    184     return stat;
    185 }
    186 
    187 template<typename Solver, typename Scalar>
    188 Stats call_itersolver(Solver &solver, const typename Solver::MatrixType& A, const Matrix<Scalar, Dynamic, 1>& b, const Matrix<Scalar, Dynamic, 1>& refX)
    189 {
    190   Stats stat;
    191   solver.setTolerance(RelErr);
    192   solver.setMaxIterations(MaximumIters);
    193 
    194   stat = call_solver(solver, A, b, refX);
    195   stat.iterations = solver.iterations();
    196   return stat;
    197 }
    198 
    199 inline void printStatItem(Stats *stat, int solver_id, int& best_time_id, double& best_time_val)
    200 {
    201   stat[solver_id].isavail = 1;
    202 
    203   if (stat[solver_id].info == NumericalIssue)
    204   {
    205     cout << " SOLVER FAILED ... Probably a numerical issue \n";
    206     return;
    207   }
    208   if (stat[solver_id].info == NoConvergence){
    209     cout << "REL. ERROR " <<  stat[solver_id].rel_error;
    210     if(stat[solver_id].isIterative == 1)
    211       cout << " (" << stat[solver_id].iterations << ") \n";
    212     return;
    213   }
    214 
    215   // Record the best CPU time
    216   if (!best_time_val)
    217   {
    218     best_time_val = stat[solver_id].total_time;
    219     best_time_id = solver_id;
    220   }
    221   else if (stat[solver_id].total_time < best_time_val)
    222   {
    223     best_time_val = stat[solver_id].total_time;
    224     best_time_id = solver_id;
    225   }
    226   // Print statistics to standard output
    227   if (stat[solver_id].info == Success){
    228     cout<< "COMPUTE TIME : " << stat[solver_id].compute_time<< " \n";
    229     cout<< "SOLVE TIME : " << stat[solver_id].solve_time<< " \n";
    230     cout<< "TOTAL TIME : " << stat[solver_id].total_time<< " \n";
    231     cout << "REL. ERROR : " << stat[solver_id].rel_error ;
    232     if(stat[solver_id].isIterative == 1) {
    233       cout << " (" << stat[solver_id].iterations << ") ";
    234     }
    235     cout << std::endl;
    236   }
    237 
    238 }
    239 
    240 
    241 /* Print the results from all solvers corresponding to a particular matrix
    242  * The best CPU time is printed in bold
    243  */
    244 inline void printHtmlStatLine(Stats *stat, int best_time_id, string& statline)
    245 {
    246 
    247   string markup;
    248   ostringstream compute,solve,total,error;
    249   for (int i = 0; i < EIGEN_ALL_SOLVERS; i++)
    250   {
    251     if (stat[i].isavail == 0) continue;
    252     if(i == best_time_id)
    253       markup = "<TD style=\"background-color:red\">";
    254     else
    255       markup = "<TD>";
    256 
    257     if (stat[i].info == Success){
    258       compute << markup << stat[i].compute_time;
    259       solve << markup << stat[i].solve_time;
    260       total << markup << stat[i].total_time;
    261       error << " <TD> " << stat[i].rel_error;
    262       if(stat[i].isIterative == 1) {
    263         error << " (" << stat[i].iterations << ") ";
    264       }
    265     }
    266     else {
    267       compute << " <TD> -" ;
    268       solve << " <TD> -" ;
    269       total << " <TD> -" ;
    270       if(stat[i].info == NoConvergence){
    271         error << " <TD> "<< stat[i].rel_error ;
    272         if(stat[i].isIterative == 1)
    273           error << " (" << stat[i].iterations << ") ";
    274       }
    275       else    error << " <TD> - ";
    276     }
    277   }
    278 
    279   statline = "<TH>Compute Time " + compute.str() + "\n"
    280                         +  "<TR><TH>Solve Time " + solve.str() + "\n"
    281                         +  "<TR><TH>Total Time " + total.str() + "\n"
    282                         +"<TR><TH>Error(Iter)" + error.str() + "\n";
    283 
    284 }
    285 
    286 template <typename Scalar>
    287 int SelectSolvers(const SparseMatrix<Scalar>&A, unsigned int sym, Matrix<Scalar, Dynamic, 1>& b, const Matrix<Scalar, Dynamic, 1>& refX, Stats *stat)
    288 {
    289   typedef SparseMatrix<Scalar, ColMajor> SpMat;
    290   // First, deal with Nonsymmetric and symmetric matrices
    291   int best_time_id = 0;
    292   double best_time_val = 0.0;
    293   //UMFPACK
    294   #ifdef EIGEN_UMFPACK_SUPPORT
    295   {
    296     cout << "Solving with UMFPACK LU ... \n";
    297     UmfPackLU<SpMat> solver;
    298     stat[EIGEN_UMFPACK] = call_directsolver(solver, A, b, refX);
    299     printStatItem(stat, EIGEN_UMFPACK, best_time_id, best_time_val);
    300   }
    301   #endif
    302     //SuperLU
    303   #ifdef EIGEN_SUPERLU_SUPPORT
    304   {
    305     cout << "\nSolving with SUPERLU ... \n";
    306     SuperLU<SpMat> solver;
    307     stat[EIGEN_SUPERLU] = call_directsolver(solver, A, b, refX);
    308     printStatItem(stat, EIGEN_SUPERLU, best_time_id, best_time_val);
    309   }
    310   #endif
    311 
    312    // PaStix LU
    313   #ifdef EIGEN_PASTIX_SUPPORT
    314   {
    315     cout << "\nSolving with PASTIX LU ... \n";
    316     PastixLU<SpMat> solver;
    317     stat[EIGEN_PASTIX] = call_directsolver(solver, A, b, refX) ;
    318     printStatItem(stat, EIGEN_PASTIX, best_time_id, best_time_val);
    319   }
    320   #endif
    321 
    322    //PARDISO LU
    323   #ifdef EIGEN_PARDISO_SUPPORT
    324   {
    325     cout << "\nSolving with PARDISO LU ... \n";
    326     PardisoLU<SpMat>  solver;
    327     stat[EIGEN_PARDISO] = call_directsolver(solver, A, b, refX);
    328     printStatItem(stat, EIGEN_PARDISO, best_time_id, best_time_val);
    329   }
    330   #endif
    331 
    332 
    333 
    334   //BiCGSTAB
    335   {
    336     cout << "\nSolving with BiCGSTAB ... \n";
    337     BiCGSTAB<SpMat> solver;
    338     stat[EIGEN_BICGSTAB] = call_itersolver(solver, A, b, refX);
    339     stat[EIGEN_BICGSTAB].isIterative = 1;
    340     printStatItem(stat, EIGEN_BICGSTAB, best_time_id, best_time_val);
    341   }
    342   //BiCGSTAB+ILUT
    343   {
    344     cout << "\nSolving with BiCGSTAB and ILUT ... \n";
    345     BiCGSTAB<SpMat, IncompleteLUT<Scalar> > solver;
    346     stat[EIGEN_BICGSTAB_ILUT] = call_itersolver(solver, A, b, refX);
    347     stat[EIGEN_BICGSTAB_ILUT].isIterative = 1;
    348     printStatItem(stat, EIGEN_BICGSTAB_ILUT, best_time_id, best_time_val);
    349   }
    350 
    351 
    352   //GMRES
    353 //   {
    354 //     cout << "\nSolving with GMRES ... \n";
    355 //     GMRES<SpMat> solver;
    356 //     stat[EIGEN_GMRES] = call_itersolver(solver, A, b, refX);
    357 //     stat[EIGEN_GMRES].isIterative = 1;
    358 //     printStatItem(stat, EIGEN_GMRES, best_time_id, best_time_val);
    359 //   }
    360   //GMRES+ILUT
    361   {
    362     cout << "\nSolving with GMRES and ILUT ... \n";
    363     GMRES<SpMat, IncompleteLUT<Scalar> > solver;
    364     stat[EIGEN_GMRES_ILUT] = call_itersolver(solver, A, b, refX);
    365     stat[EIGEN_GMRES_ILUT].isIterative = 1;
    366     printStatItem(stat, EIGEN_GMRES_ILUT, best_time_id, best_time_val);
    367   }
    368 
    369   // Hermitian and not necessarily positive-definites
    370   if (sym != NonSymmetric)
    371   {
    372     // Internal Cholesky
    373     {
    374       cout << "\nSolving with Simplicial LDLT ... \n";
    375       SimplicialLDLT<SpMat, Lower> solver;
    376       stat[EIGEN_SIMPLICIAL_LDLT] = call_directsolver(solver, A, b, refX);
    377       printStatItem(stat, EIGEN_SIMPLICIAL_LDLT, best_time_id, best_time_val);
    378     }
    379 
    380     // CHOLMOD
    381     #ifdef EIGEN_CHOLMOD_SUPPORT
    382     {
    383       cout << "\nSolving with CHOLMOD LDLT ... \n";
    384       CholmodDecomposition<SpMat, Lower> solver;
    385       solver.setMode(CholmodLDLt);
    386       stat[EIGEN_CHOLMOD_LDLT] =  call_directsolver(solver, A, b, refX);
    387       printStatItem(stat,EIGEN_CHOLMOD_LDLT, best_time_id, best_time_val);
    388     }
    389     #endif
    390 
    391     //PASTIX LLT
    392     #ifdef EIGEN_PASTIX_SUPPORT
    393     {
    394       cout << "\nSolving with PASTIX LDLT ... \n";
    395       PastixLDLT<SpMat, Lower> solver;
    396       stat[EIGEN_PASTIX_LDLT] = call_directsolver(solver, A, b, refX);
    397       printStatItem(stat,EIGEN_PASTIX_LDLT, best_time_id, best_time_val);
    398     }
    399     #endif
    400 
    401     //PARDISO LLT
    402     #ifdef EIGEN_PARDISO_SUPPORT
    403     {
    404       cout << "\nSolving with PARDISO LDLT ... \n";
    405       PardisoLDLT<SpMat, Lower> solver;
    406       stat[EIGEN_PARDISO_LDLT] = call_directsolver(solver, A, b, refX);
    407       printStatItem(stat,EIGEN_PARDISO_LDLT, best_time_id, best_time_val);
    408     }
    409     #endif
    410   }
    411 
    412    // Now, symmetric POSITIVE DEFINITE matrices
    413   if (sym == SPD)
    414   {
    415 
    416     //Internal Sparse Cholesky
    417     {
    418       cout << "\nSolving with SIMPLICIAL LLT ... \n";
    419       SimplicialLLT<SpMat, Lower> solver;
    420       stat[EIGEN_SIMPLICIAL_LLT] = call_directsolver(solver, A, b, refX);
    421       printStatItem(stat,EIGEN_SIMPLICIAL_LLT, best_time_id, best_time_val);
    422     }
    423 
    424     // CHOLMOD
    425     #ifdef EIGEN_CHOLMOD_SUPPORT
    426     {
    427       // CholMOD SuperNodal LLT
    428       cout << "\nSolving with CHOLMOD LLT (Supernodal)... \n";
    429       CholmodDecomposition<SpMat, Lower> solver;
    430       solver.setMode(CholmodSupernodalLLt);
    431       stat[EIGEN_CHOLMOD_SUPERNODAL_LLT] = call_directsolver(solver, A, b, refX);
    432       printStatItem(stat,EIGEN_CHOLMOD_SUPERNODAL_LLT, best_time_id, best_time_val);
    433       // CholMod Simplicial LLT
    434       cout << "\nSolving with CHOLMOD LLT (Simplicial) ... \n";
    435       solver.setMode(CholmodSimplicialLLt);
    436       stat[EIGEN_CHOLMOD_SIMPLICIAL_LLT] = call_directsolver(solver, A, b, refX);
    437       printStatItem(stat,EIGEN_CHOLMOD_SIMPLICIAL_LLT, best_time_id, best_time_val);
    438     }
    439     #endif
    440 
    441     //PASTIX LLT
    442     #ifdef EIGEN_PASTIX_SUPPORT
    443     {
    444       cout << "\nSolving with PASTIX LLT ... \n";
    445       PastixLLT<SpMat, Lower> solver;
    446       stat[EIGEN_PASTIX_LLT] =  call_directsolver(solver, A, b, refX);
    447       printStatItem(stat,EIGEN_PASTIX_LLT, best_time_id, best_time_val);
    448     }
    449     #endif
    450 
    451     //PARDISO LLT
    452     #ifdef EIGEN_PARDISO_SUPPORT
    453     {
    454       cout << "\nSolving with PARDISO LLT ... \n";
    455       PardisoLLT<SpMat, Lower> solver;
    456       stat[EIGEN_PARDISO_LLT] = call_directsolver(solver, A, b, refX);
    457       printStatItem(stat,EIGEN_PARDISO_LLT, best_time_id, best_time_val);
    458     }
    459     #endif
    460 
    461     // Internal CG
    462     {
    463       cout << "\nSolving with CG ... \n";
    464       ConjugateGradient<SpMat, Lower> solver;
    465       stat[EIGEN_CG] = call_itersolver(solver, A, b, refX);
    466       stat[EIGEN_CG].isIterative = 1;
    467       printStatItem(stat,EIGEN_CG, best_time_id, best_time_val);
    468     }
    469     //CG+IdentityPreconditioner
    470 //     {
    471 //       cout << "\nSolving with CG and IdentityPreconditioner ... \n";
    472 //       ConjugateGradient<SpMat, Lower, IdentityPreconditioner> solver;
    473 //       stat[EIGEN_CG_PRECOND] = call_itersolver(solver, A, b, refX);
    474 //       stat[EIGEN_CG_PRECOND].isIterative = 1;
    475 //       printStatItem(stat,EIGEN_CG_PRECOND, best_time_id, best_time_val);
    476 //     }
    477   } // End SPD matrices
    478 
    479   return best_time_id;
    480 }
    481 
    482 /* Browse all the matrices available in the specified folder
    483  * and solve the associated linear system.
    484  * The results of each solve are printed in the standard output
    485  * and optionally in the provided html file
    486  */
    487 template <typename Scalar>
    488 void Browse_Matrices(const string folder, bool statFileExists, std::string& statFile, int maxiters, double tol)
    489 {
    490   MaximumIters = maxiters; // Maximum number of iterations, global variable
    491   RelErr = tol;  //Relative residual error  as stopping criterion for iterative solvers
    492   MatrixMarketIterator<Scalar> it(folder);
    493   Stats stat[EIGEN_ALL_SOLVERS];
    494   for ( ; it; ++it)
    495   {
    496     for (int i = 0; i < EIGEN_ALL_SOLVERS; i++)
    497     {
    498       stat[i].isavail = 0;
    499       stat[i].isIterative = 0;
    500     }
    501 
    502     int best_time_id;
    503     cout<< "\n\n===================================================== \n";
    504     cout<< " ======  SOLVING WITH MATRIX " << it.matname() << " ====\n";
    505     cout<< " =================================================== \n\n";
    506     Matrix<Scalar, Dynamic, 1> refX;
    507     if(it.hasrefX()) refX = it.refX();
    508     best_time_id = SelectSolvers<Scalar>(it.matrix(), it.sym(), it.rhs(), refX, &stat[0]);
    509 
    510     if(statFileExists)
    511     {
    512       string statline;
    513       printHtmlStatLine(&stat[0], best_time_id, statline);
    514       std::ofstream statbuf(statFile.c_str(), std::ios::app);
    515       statbuf << "<TR><TH rowspan=\"4\">" << it.matname() << " <TD rowspan=\"4\"> "
    516       << it.matrix().rows() << " <TD rowspan=\"4\"> " << it.matrix().nonZeros()<< " "<< statline ;
    517       statbuf.close();
    518     }
    519   }
    520 }
    521 
    522 bool get_options(int argc, char **args, string option, string* value=0)
    523 {
    524   int idx = 1, found=false;
    525   while (idx<argc && !found){
    526     if (option.compare(args[idx]) == 0){
    527       found = true;
    528       if(value) *value = args[idx+1];
    529     }
    530     idx+=2;
    531   }
    532   return found;
    533 }
    534