You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

554 lines
17 KiB

  1. // This file is part of Eigen, a lightweight C++ template library
  2. // for linear algebra.
  3. //
  4. // Copyright (C) 2012 Désiré Nuentsa-Wakam <desire.nuentsa_wakam@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. #include <iostream>
  10. #include <fstream>
  11. #include <Eigen/SparseCore>
  12. #include <bench/BenchTimer.h>
  13. #include <cstdlib>
  14. #include <string>
  15. #include <Eigen/Cholesky>
  16. #include <Eigen/Jacobi>
  17. #include <Eigen/Householder>
  18. #include <Eigen/IterativeLinearSolvers>
  19. #include <unsupported/Eigen/IterativeSolvers>
  20. #include <Eigen/LU>
  21. #include <unsupported/Eigen/SparseExtra>
  22. #include <Eigen/SparseLU>
  23. #include "spbenchstyle.h"
  24. #ifdef EIGEN_METIS_SUPPORT
  25. #include <Eigen/MetisSupport>
  26. #endif
  27. #ifdef EIGEN_CHOLMOD_SUPPORT
  28. #include <Eigen/CholmodSupport>
  29. #endif
  30. #ifdef EIGEN_UMFPACK_SUPPORT
  31. #include <Eigen/UmfPackSupport>
  32. #endif
  33. #ifdef EIGEN_PARDISO_SUPPORT
  34. #include <Eigen/PardisoSupport>
  35. #endif
  36. #ifdef EIGEN_SUPERLU_SUPPORT
  37. #include <Eigen/SuperLUSupport>
  38. #endif
  39. #ifdef EIGEN_PASTIX_SUPPORT
  40. #include <Eigen/PaStiXSupport>
  41. #endif
  42. // CONSTANTS
  43. #define EIGEN_UMFPACK 10
  44. #define EIGEN_SUPERLU 20
  45. #define EIGEN_PASTIX 30
  46. #define EIGEN_PARDISO 40
  47. #define EIGEN_SPARSELU_COLAMD 50
  48. #define EIGEN_SPARSELU_METIS 51
  49. #define EIGEN_BICGSTAB 60
  50. #define EIGEN_BICGSTAB_ILUT 61
  51. #define EIGEN_GMRES 70
  52. #define EIGEN_GMRES_ILUT 71
  53. #define EIGEN_SIMPLICIAL_LDLT 80
  54. #define EIGEN_CHOLMOD_LDLT 90
  55. #define EIGEN_PASTIX_LDLT 100
  56. #define EIGEN_PARDISO_LDLT 110
  57. #define EIGEN_SIMPLICIAL_LLT 120
  58. #define EIGEN_CHOLMOD_SUPERNODAL_LLT 130
  59. #define EIGEN_CHOLMOD_SIMPLICIAL_LLT 140
  60. #define EIGEN_PASTIX_LLT 150
  61. #define EIGEN_PARDISO_LLT 160
  62. #define EIGEN_CG 170
  63. #define EIGEN_CG_PRECOND 180
  64. using namespace Eigen;
  65. using namespace std;
  66. // Global variables for input parameters
  67. int MaximumIters; // Maximum number of iterations
  68. double RelErr; // Relative error of the computed solution
  69. double best_time_val; // Current best time overall solvers
  70. int best_time_id; // id of the best solver for the current system
  71. template<typename T> inline typename NumTraits<T>::Real test_precision() { return NumTraits<T>::dummy_precision(); }
  72. template<> inline float test_precision<float>() { return 1e-3f; }
  73. template<> inline double test_precision<double>() { return 1e-6; }
  74. template<> inline float test_precision<std::complex<float> >() { return test_precision<float>(); }
  75. template<> inline double test_precision<std::complex<double> >() { return test_precision<double>(); }
  76. void printStatheader(std::ofstream& out)
  77. {
  78. // Print XML header
  79. // NOTE It would have been much easier to write these XML documents using external libraries like tinyXML or Xerces-C++.
  80. out << "<?xml version='1.0' encoding='UTF-8'?> \n";
  81. out << "<?xml-stylesheet type='text/xsl' href='#stylesheet' ?> \n";
  82. out << "<!DOCTYPE BENCH [\n<!ATTLIST xsl:stylesheet\n id\t ID #REQUIRED>\n]>";
  83. out << "\n\n<!-- Generated by the Eigen library -->\n";
  84. out << "\n<BENCH> \n" ; //root XML element
  85. // Print the xsl style section
  86. printBenchStyle(out);
  87. // List all available solvers
  88. out << " <AVAILSOLVER> \n";
  89. #ifdef EIGEN_UMFPACK_SUPPORT
  90. out <<" <SOLVER ID='" << EIGEN_UMFPACK << "'>\n";
  91. out << " <TYPE> LU </TYPE> \n";
  92. out << " <PACKAGE> UMFPACK </PACKAGE> \n";
  93. out << " </SOLVER> \n";
  94. #endif
  95. #ifdef EIGEN_SUPERLU_SUPPORT
  96. out <<" <SOLVER ID='" << EIGEN_SUPERLU << "'>\n";
  97. out << " <TYPE> LU </TYPE> \n";
  98. out << " <PACKAGE> SUPERLU </PACKAGE> \n";
  99. out << " </SOLVER> \n";
  100. #endif
  101. #ifdef EIGEN_CHOLMOD_SUPPORT
  102. out <<" <SOLVER ID='" << EIGEN_CHOLMOD_SIMPLICIAL_LLT << "'>\n";
  103. out << " <TYPE> LLT SP</TYPE> \n";
  104. out << " <PACKAGE> CHOLMOD </PACKAGE> \n";
  105. out << " </SOLVER> \n";
  106. out <<" <SOLVER ID='" << EIGEN_CHOLMOD_SUPERNODAL_LLT << "'>\n";
  107. out << " <TYPE> LLT</TYPE> \n";
  108. out << " <PACKAGE> CHOLMOD </PACKAGE> \n";
  109. out << " </SOLVER> \n";
  110. out <<" <SOLVER ID='" << EIGEN_CHOLMOD_LDLT << "'>\n";
  111. out << " <TYPE> LDLT </TYPE> \n";
  112. out << " <PACKAGE> CHOLMOD </PACKAGE> \n";
  113. out << " </SOLVER> \n";
  114. #endif
  115. #ifdef EIGEN_PARDISO_SUPPORT
  116. out <<" <SOLVER ID='" << EIGEN_PARDISO << "'>\n";
  117. out << " <TYPE> LU </TYPE> \n";
  118. out << " <PACKAGE> PARDISO </PACKAGE> \n";
  119. out << " </SOLVER> \n";
  120. out <<" <SOLVER ID='" << EIGEN_PARDISO_LLT << "'>\n";
  121. out << " <TYPE> LLT </TYPE> \n";
  122. out << " <PACKAGE> PARDISO </PACKAGE> \n";
  123. out << " </SOLVER> \n";
  124. out <<" <SOLVER ID='" << EIGEN_PARDISO_LDLT << "'>\n";
  125. out << " <TYPE> LDLT </TYPE> \n";
  126. out << " <PACKAGE> PARDISO </PACKAGE> \n";
  127. out << " </SOLVER> \n";
  128. #endif
  129. #ifdef EIGEN_PASTIX_SUPPORT
  130. out <<" <SOLVER ID='" << EIGEN_PASTIX << "'>\n";
  131. out << " <TYPE> LU </TYPE> \n";
  132. out << " <PACKAGE> PASTIX </PACKAGE> \n";
  133. out << " </SOLVER> \n";
  134. out <<" <SOLVER ID='" << EIGEN_PASTIX_LLT << "'>\n";
  135. out << " <TYPE> LLT </TYPE> \n";
  136. out << " <PACKAGE> PASTIX </PACKAGE> \n";
  137. out << " </SOLVER> \n";
  138. out <<" <SOLVER ID='" << EIGEN_PASTIX_LDLT << "'>\n";
  139. out << " <TYPE> LDLT </TYPE> \n";
  140. out << " <PACKAGE> PASTIX </PACKAGE> \n";
  141. out << " </SOLVER> \n";
  142. #endif
  143. out <<" <SOLVER ID='" << EIGEN_BICGSTAB << "'>\n";
  144. out << " <TYPE> BICGSTAB </TYPE> \n";
  145. out << " <PACKAGE> EIGEN </PACKAGE> \n";
  146. out << " </SOLVER> \n";
  147. out <<" <SOLVER ID='" << EIGEN_BICGSTAB_ILUT << "'>\n";
  148. out << " <TYPE> BICGSTAB_ILUT </TYPE> \n";
  149. out << " <PACKAGE> EIGEN </PACKAGE> \n";
  150. out << " </SOLVER> \n";
  151. out <<" <SOLVER ID='" << EIGEN_GMRES_ILUT << "'>\n";
  152. out << " <TYPE> GMRES_ILUT </TYPE> \n";
  153. out << " <PACKAGE> EIGEN </PACKAGE> \n";
  154. out << " </SOLVER> \n";
  155. out <<" <SOLVER ID='" << EIGEN_SIMPLICIAL_LDLT << "'>\n";
  156. out << " <TYPE> LDLT </TYPE> \n";
  157. out << " <PACKAGE> EIGEN </PACKAGE> \n";
  158. out << " </SOLVER> \n";
  159. out <<" <SOLVER ID='" << EIGEN_SIMPLICIAL_LLT << "'>\n";
  160. out << " <TYPE> LLT </TYPE> \n";
  161. out << " <PACKAGE> EIGEN </PACKAGE> \n";
  162. out << " </SOLVER> \n";
  163. out <<" <SOLVER ID='" << EIGEN_CG << "'>\n";
  164. out << " <TYPE> CG </TYPE> \n";
  165. out << " <PACKAGE> EIGEN </PACKAGE> \n";
  166. out << " </SOLVER> \n";
  167. out <<" <SOLVER ID='" << EIGEN_SPARSELU_COLAMD << "'>\n";
  168. out << " <TYPE> LU_COLAMD </TYPE> \n";
  169. out << " <PACKAGE> EIGEN </PACKAGE> \n";
  170. out << " </SOLVER> \n";
  171. #ifdef EIGEN_METIS_SUPPORT
  172. out <<" <SOLVER ID='" << EIGEN_SPARSELU_METIS << "'>\n";
  173. out << " <TYPE> LU_METIS </TYPE> \n";
  174. out << " <PACKAGE> EIGEN </PACKAGE> \n";
  175. out << " </SOLVER> \n";
  176. #endif
  177. out << " </AVAILSOLVER> \n";
  178. }
  179. template<typename Solver, typename Scalar>
  180. void call_solver(Solver &solver, const int solver_id, const typename Solver::MatrixType& A, const Matrix<Scalar, Dynamic, 1>& b, const Matrix<Scalar, Dynamic, 1>& refX,std::ofstream& statbuf)
  181. {
  182. double total_time;
  183. double compute_time;
  184. double solve_time;
  185. double rel_error;
  186. Matrix<Scalar, Dynamic, 1> x;
  187. BenchTimer timer;
  188. timer.reset();
  189. timer.start();
  190. solver.compute(A);
  191. if (solver.info() != Success)
  192. {
  193. std::cerr << "Solver failed ... \n";
  194. return;
  195. }
  196. timer.stop();
  197. compute_time = timer.value();
  198. statbuf << " <TIME>\n";
  199. statbuf << " <COMPUTE> " << timer.value() << "</COMPUTE>\n";
  200. std::cout<< "COMPUTE TIME : " << timer.value() <<std::endl;
  201. timer.reset();
  202. timer.start();
  203. x = solver.solve(b);
  204. if (solver.info() == NumericalIssue)
  205. {
  206. std::cerr << "Solver failed ... \n";
  207. return;
  208. }
  209. timer.stop();
  210. solve_time = timer.value();
  211. statbuf << " <SOLVE> " << timer.value() << "</SOLVE>\n";
  212. std::cout<< "SOLVE TIME : " << timer.value() <<std::endl;
  213. total_time = solve_time + compute_time;
  214. statbuf << " <TOTAL> " << total_time << "</TOTAL>\n";
  215. std::cout<< "TOTAL TIME : " << total_time <<std::endl;
  216. statbuf << " </TIME>\n";
  217. // Verify the relative error
  218. if(refX.size() != 0)
  219. rel_error = (refX - x).norm()/refX.norm();
  220. else
  221. {
  222. // Compute the relative residual norm
  223. Matrix<Scalar, Dynamic, 1> temp;
  224. temp = A * x;
  225. rel_error = (b-temp).norm()/b.norm();
  226. }
  227. statbuf << " <ERROR> " << rel_error << "</ERROR>\n";
  228. std::cout<< "REL. ERROR : " << rel_error << "\n\n" ;
  229. if ( rel_error <= RelErr )
  230. {
  231. // check the best time if convergence
  232. if(!best_time_val || (best_time_val > total_time))
  233. {
  234. best_time_val = total_time;
  235. best_time_id = solver_id;
  236. }
  237. }
  238. }
  239. template<typename Solver, typename Scalar>
  240. void call_directsolver(Solver& solver, const int solver_id, const typename Solver::MatrixType& A, const Matrix<Scalar, Dynamic, 1>& b, const Matrix<Scalar, Dynamic, 1>& refX, std::string& statFile)
  241. {
  242. std::ofstream statbuf(statFile.c_str(), std::ios::app);
  243. statbuf << " <SOLVER_STAT ID='" << solver_id <<"'>\n";
  244. call_solver(solver, solver_id, A, b, refX,statbuf);
  245. statbuf << " </SOLVER_STAT>\n";
  246. statbuf.close();
  247. }
  248. template<typename Solver, typename Scalar>
  249. void call_itersolver(Solver &solver, const int solver_id, const typename Solver::MatrixType& A, const Matrix<Scalar, Dynamic, 1>& b, const Matrix<Scalar, Dynamic, 1>& refX, std::string& statFile)
  250. {
  251. solver.setTolerance(RelErr);
  252. solver.setMaxIterations(MaximumIters);
  253. std::ofstream statbuf(statFile.c_str(), std::ios::app);
  254. statbuf << " <SOLVER_STAT ID='" << solver_id <<"'>\n";
  255. call_solver(solver, solver_id, A, b, refX,statbuf);
  256. statbuf << " <ITER> "<< solver.iterations() << "</ITER>\n";
  257. statbuf << " </SOLVER_STAT>\n";
  258. std::cout << "ITERATIONS : " << solver.iterations() <<"\n\n\n";
  259. }
  260. template <typename Scalar>
  261. void SelectSolvers(const SparseMatrix<Scalar>&A, unsigned int sym, Matrix<Scalar, Dynamic, 1>& b, const Matrix<Scalar, Dynamic, 1>& refX, std::string& statFile)
  262. {
  263. typedef SparseMatrix<Scalar, ColMajor> SpMat;
  264. // First, deal with Nonsymmetric and symmetric matrices
  265. best_time_id = 0;
  266. best_time_val = 0.0;
  267. //UMFPACK
  268. #ifdef EIGEN_UMFPACK_SUPPORT
  269. {
  270. cout << "Solving with UMFPACK LU ... \n";
  271. UmfPackLU<SpMat> solver;
  272. call_directsolver(solver, EIGEN_UMFPACK, A, b, refX,statFile);
  273. }
  274. #endif
  275. //SuperLU
  276. #ifdef EIGEN_SUPERLU_SUPPORT
  277. {
  278. cout << "\nSolving with SUPERLU ... \n";
  279. SuperLU<SpMat> solver;
  280. call_directsolver(solver, EIGEN_SUPERLU, A, b, refX,statFile);
  281. }
  282. #endif
  283. // PaStix LU
  284. #ifdef EIGEN_PASTIX_SUPPORT
  285. {
  286. cout << "\nSolving with PASTIX LU ... \n";
  287. PastixLU<SpMat> solver;
  288. call_directsolver(solver, EIGEN_PASTIX, A, b, refX,statFile) ;
  289. }
  290. #endif
  291. //PARDISO LU
  292. #ifdef EIGEN_PARDISO_SUPPORT
  293. {
  294. cout << "\nSolving with PARDISO LU ... \n";
  295. PardisoLU<SpMat> solver;
  296. call_directsolver(solver, EIGEN_PARDISO, A, b, refX,statFile);
  297. }
  298. #endif
  299. // Eigen SparseLU METIS
  300. cout << "\n Solving with Sparse LU AND COLAMD ... \n";
  301. SparseLU<SpMat, COLAMDOrdering<int> > solver;
  302. call_directsolver(solver, EIGEN_SPARSELU_COLAMD, A, b, refX, statFile);
  303. // Eigen SparseLU METIS
  304. #ifdef EIGEN_METIS_SUPPORT
  305. {
  306. cout << "\n Solving with Sparse LU AND METIS ... \n";
  307. SparseLU<SpMat, MetisOrdering<int> > solver;
  308. call_directsolver(solver, EIGEN_SPARSELU_METIS, A, b, refX, statFile);
  309. }
  310. #endif
  311. //BiCGSTAB
  312. {
  313. cout << "\nSolving with BiCGSTAB ... \n";
  314. BiCGSTAB<SpMat> solver;
  315. call_itersolver(solver, EIGEN_BICGSTAB, A, b, refX,statFile);
  316. }
  317. //BiCGSTAB+ILUT
  318. {
  319. cout << "\nSolving with BiCGSTAB and ILUT ... \n";
  320. BiCGSTAB<SpMat, IncompleteLUT<Scalar> > solver;
  321. call_itersolver(solver, EIGEN_BICGSTAB_ILUT, A, b, refX,statFile);
  322. }
  323. //GMRES
  324. // {
  325. // cout << "\nSolving with GMRES ... \n";
  326. // GMRES<SpMat> solver;
  327. // call_itersolver(solver, EIGEN_GMRES, A, b, refX,statFile);
  328. // }
  329. //GMRES+ILUT
  330. {
  331. cout << "\nSolving with GMRES and ILUT ... \n";
  332. GMRES<SpMat, IncompleteLUT<Scalar> > solver;
  333. call_itersolver(solver, EIGEN_GMRES_ILUT, A, b, refX,statFile);
  334. }
  335. // Hermitian and not necessarily positive-definites
  336. if (sym != NonSymmetric)
  337. {
  338. // Internal Cholesky
  339. {
  340. cout << "\nSolving with Simplicial LDLT ... \n";
  341. SimplicialLDLT<SpMat, Lower> solver;
  342. call_directsolver(solver, EIGEN_SIMPLICIAL_LDLT, A, b, refX,statFile);
  343. }
  344. // CHOLMOD
  345. #ifdef EIGEN_CHOLMOD_SUPPORT
  346. {
  347. cout << "\nSolving with CHOLMOD LDLT ... \n";
  348. CholmodDecomposition<SpMat, Lower> solver;
  349. solver.setMode(CholmodLDLt);
  350. call_directsolver(solver,EIGEN_CHOLMOD_LDLT, A, b, refX,statFile);
  351. }
  352. #endif
  353. //PASTIX LLT
  354. #ifdef EIGEN_PASTIX_SUPPORT
  355. {
  356. cout << "\nSolving with PASTIX LDLT ... \n";
  357. PastixLDLT<SpMat, Lower> solver;
  358. call_directsolver(solver,EIGEN_PASTIX_LDLT, A, b, refX,statFile);
  359. }
  360. #endif
  361. //PARDISO LLT
  362. #ifdef EIGEN_PARDISO_SUPPORT
  363. {
  364. cout << "\nSolving with PARDISO LDLT ... \n";
  365. PardisoLDLT<SpMat, Lower> solver;
  366. call_directsolver(solver,EIGEN_PARDISO_LDLT, A, b, refX,statFile);
  367. }
  368. #endif
  369. }
  370. // Now, symmetric POSITIVE DEFINITE matrices
  371. if (sym == SPD)
  372. {
  373. //Internal Sparse Cholesky
  374. {
  375. cout << "\nSolving with SIMPLICIAL LLT ... \n";
  376. SimplicialLLT<SpMat, Lower> solver;
  377. call_directsolver(solver,EIGEN_SIMPLICIAL_LLT, A, b, refX,statFile);
  378. }
  379. // CHOLMOD
  380. #ifdef EIGEN_CHOLMOD_SUPPORT
  381. {
  382. // CholMOD SuperNodal LLT
  383. cout << "\nSolving with CHOLMOD LLT (Supernodal)... \n";
  384. CholmodDecomposition<SpMat, Lower> solver;
  385. solver.setMode(CholmodSupernodalLLt);
  386. call_directsolver(solver,EIGEN_CHOLMOD_SUPERNODAL_LLT, A, b, refX,statFile);
  387. // CholMod Simplicial LLT
  388. cout << "\nSolving with CHOLMOD LLT (Simplicial) ... \n";
  389. solver.setMode(CholmodSimplicialLLt);
  390. call_directsolver(solver,EIGEN_CHOLMOD_SIMPLICIAL_LLT, A, b, refX,statFile);
  391. }
  392. #endif
  393. //PASTIX LLT
  394. #ifdef EIGEN_PASTIX_SUPPORT
  395. {
  396. cout << "\nSolving with PASTIX LLT ... \n";
  397. PastixLLT<SpMat, Lower> solver;
  398. call_directsolver(solver,EIGEN_PASTIX_LLT, A, b, refX,statFile);
  399. }
  400. #endif
  401. //PARDISO LLT
  402. #ifdef EIGEN_PARDISO_SUPPORT
  403. {
  404. cout << "\nSolving with PARDISO LLT ... \n";
  405. PardisoLLT<SpMat, Lower> solver;
  406. call_directsolver(solver,EIGEN_PARDISO_LLT, A, b, refX,statFile);
  407. }
  408. #endif
  409. // Internal CG
  410. {
  411. cout << "\nSolving with CG ... \n";
  412. ConjugateGradient<SpMat, Lower> solver;
  413. call_itersolver(solver,EIGEN_CG, A, b, refX,statFile);
  414. }
  415. //CG+IdentityPreconditioner
  416. // {
  417. // cout << "\nSolving with CG and IdentityPreconditioner ... \n";
  418. // ConjugateGradient<SpMat, Lower, IdentityPreconditioner> solver;
  419. // call_itersolver(solver,EIGEN_CG_PRECOND, A, b, refX,statFile);
  420. // }
  421. } // End SPD matrices
  422. }
  423. /* Browse all the matrices available in the specified folder
  424. * and solve the associated linear system.
  425. * The results of each solve are printed in the standard output
  426. * and optionally in the provided html file
  427. */
  428. template <typename Scalar>
  429. void Browse_Matrices(const string folder, bool statFileExists, std::string& statFile, int maxiters, double tol)
  430. {
  431. MaximumIters = maxiters; // Maximum number of iterations, global variable
  432. RelErr = tol; //Relative residual error as stopping criterion for iterative solvers
  433. MatrixMarketIterator<Scalar> it(folder);
  434. for ( ; it; ++it)
  435. {
  436. //print the infos for this linear system
  437. if(statFileExists)
  438. {
  439. std::ofstream statbuf(statFile.c_str(), std::ios::app);
  440. statbuf << "<LINEARSYSTEM> \n";
  441. statbuf << " <MATRIX> \n";
  442. statbuf << " <NAME> " << it.matname() << " </NAME>\n";
  443. statbuf << " <SIZE> " << it.matrix().rows() << " </SIZE>\n";
  444. statbuf << " <ENTRIES> " << it.matrix().nonZeros() << "</ENTRIES>\n";
  445. if (it.sym()!=NonSymmetric)
  446. {
  447. statbuf << " <SYMMETRY> Symmetric </SYMMETRY>\n" ;
  448. if (it.sym() == SPD)
  449. statbuf << " <POSDEF> YES </POSDEF>\n";
  450. else
  451. statbuf << " <POSDEF> NO </POSDEF>\n";
  452. }
  453. else
  454. {
  455. statbuf << " <SYMMETRY> NonSymmetric </SYMMETRY>\n" ;
  456. statbuf << " <POSDEF> NO </POSDEF>\n";
  457. }
  458. statbuf << " </MATRIX> \n";
  459. statbuf.close();
  460. }
  461. cout<< "\n\n===================================================== \n";
  462. cout<< " ====== SOLVING WITH MATRIX " << it.matname() << " ====\n";
  463. cout<< " =================================================== \n\n";
  464. Matrix<Scalar, Dynamic, 1> refX;
  465. if(it.hasrefX()) refX = it.refX();
  466. // Call all suitable solvers for this linear system
  467. SelectSolvers<Scalar>(it.matrix(), it.sym(), it.rhs(), refX, statFile);
  468. if(statFileExists)
  469. {
  470. std::ofstream statbuf(statFile.c_str(), std::ios::app);
  471. statbuf << " <BEST_SOLVER ID='"<< best_time_id
  472. << "'></BEST_SOLVER>\n";
  473. statbuf << " </LINEARSYSTEM> \n";
  474. statbuf.close();
  475. }
  476. }
  477. }
  478. bool get_options(int argc, char **args, string option, string* value=0)
  479. {
  480. int idx = 1, found=false;
  481. while (idx<argc && !found){
  482. if (option.compare(args[idx]) == 0){
  483. found = true;
  484. if(value) *value = args[idx+1];
  485. }
  486. idx+=2;
  487. }
  488. return found;
  489. }