/Elasticity/src/SparseMatrix.inl

https://bitbucket.org/xsli1112/dec · C++ Header · 825 lines · 639 code · 136 blank · 50 comment · 65 complexity · 5bfef984fe530b5eb6602df5f51cf265 MD5 · raw file

  1. #include <algorithm>
  2. #include <cassert>
  3. #include <iostream>
  4. #include <cmath>
  5. #include <complex>
  6. using namespace std;
  7. #include <SuiteSparseQR.hpp>
  8. #include <umfpack.h>
  9. #include "Real.h"
  10. #include "Complex.h"
  11. #include "SparseMatrix.h"
  12. #include "DenseMatrix.h"
  13. #include "LinearContext.h"
  14. #include "Utility.h"
  15. namespace DDG
  16. {
  17. extern LinearContext context;
  18. const int maxEigIter = 20;
  19. // number of iterations used to solve eigenvalue problems
  20. template <class T>
  21. SparseMatrix<T> :: SparseMatrix( int m_, int n_ )
  22. // initialize an mxn matrix
  23. : m( m_ ),
  24. n( n_ ),
  25. cData( NULL )
  26. {}
  27. template <class T>
  28. SparseMatrix<T> :: SparseMatrix( const SparseMatrix<T>& B )
  29. // copy constructor
  30. : cData( NULL )
  31. {
  32. *this = B;
  33. }
  34. template <class T>
  35. SparseMatrix<T> :: ~SparseMatrix( void )
  36. // destructor
  37. {
  38. if( cData )
  39. {
  40. cholmod_l_free_sparse( &cData, context );
  41. }
  42. }
  43. template <class T>
  44. const SparseMatrix<T>& SparseMatrix<T> :: operator=( const SparseMatrix<T>& B )
  45. // copies B
  46. {
  47. if( cData )
  48. {
  49. cholmod_l_free_sparse( &cData, context );
  50. cData = NULL;
  51. }
  52. m = B.m;
  53. n = B.n;
  54. data = B.data;
  55. return *this;
  56. }
  57. template <class T>
  58. SparseMatrix<T> SparseMatrix<T> :: transpose( void ) const
  59. {
  60. SparseMatrix<T> AT( n, m );
  61. for( const_iterator e = begin();
  62. e != end();
  63. e++ )
  64. {
  65. int i = e->first.second;
  66. int j = e->first.first;
  67. T Aij = e->second;
  68. AT(j,i) = Aij.conj();
  69. }
  70. return AT;
  71. }
  72. template <class T>
  73. SparseMatrix<T> SparseMatrix<T> :: operator*( const SparseMatrix<T>& B ) const
  74. // returns product of this matrix with sparse B
  75. {
  76. const SparseMatrix<T>& A( *this );
  77. // make sure matrix dimensions agree
  78. assert( A.nColumns() == B.nRows() );
  79. // collect nonzeros in each row
  80. vector< vector< int > > Bcol( B.nRows() );
  81. vector< vector< T > > Bval( B.nRows() );
  82. for( const_iterator e = B.begin();
  83. e != B.end();
  84. e ++ )
  85. {
  86. int row = e->first.second;
  87. int col = e->first.first;
  88. T val = e->second;
  89. Bcol[ row ].push_back( col );
  90. Bval[ row ].push_back( val );
  91. }
  92. // multiply C = A*B
  93. SparseMatrix<T> C( A.nRows(), B.nColumns() );
  94. for( const_iterator e = begin();
  95. e != end();
  96. e ++ )
  97. {
  98. int i = e->first.second;
  99. int j = e->first.first;
  100. for( size_t n = 0; n < Bcol[j].size(); n++ )
  101. {
  102. int k = Bcol[j][n];
  103. C( i, k ) += e->second * Bval[j][n];
  104. }
  105. }
  106. return C;
  107. }
  108. template <class T>
  109. DenseMatrix<T> SparseMatrix<T> :: operator*( const DenseMatrix<T>& B ) const
  110. // returns product of this matrix with dense B
  111. {
  112. const SparseMatrix<T>& A( *this );
  113. // make sure matrix dimensions agree
  114. assert( A.nColumns() == B.nRows() );
  115. // multiply C = A*B
  116. DenseMatrix<T> C( A.nRows(), B.nColumns() );
  117. for( const_iterator e = begin();
  118. e != end();
  119. e ++ )
  120. {
  121. int i = e->first.second;
  122. int j = e->first.first;
  123. for( int k = 0; k < B.nColumns(); k++ )
  124. {
  125. C( i, k ) += e->second * B( j, k );
  126. }
  127. }
  128. return C;
  129. }
  130. template <class T>
  131. void SparseMatrix<T> :: operator*=( const T& c )
  132. {
  133. for( iterator e = begin();
  134. e != end();
  135. e++ )
  136. {
  137. e->second *= c;
  138. }
  139. }
  140. template <class T>
  141. void SparseMatrix<T> :: operator/=( const T& c )
  142. {
  143. for( iterator e = begin();
  144. e != end();
  145. e++ )
  146. {
  147. e->second /= c;
  148. }
  149. }
  150. template <class T>
  151. void SparseMatrix<T> :: operator+=( const SparseMatrix<T>& B )
  152. // adds B to this matrix
  153. {
  154. SparseMatrix<T>& A( *this );
  155. // make sure matrix dimensions agree
  156. assert( A.nRows() == B.nRows() );
  157. assert( A.nColumns() == B.nColumns() );
  158. for( const_iterator e = B.begin();
  159. e != B.end();
  160. e++ )
  161. {
  162. int i = e->first.second;
  163. int j = e->first.first;
  164. const T& Bij( e->second );
  165. A( i, j ) += Bij;
  166. }
  167. }
  168. template <class T>
  169. void SparseMatrix<T> :: operator-=( const SparseMatrix<T>& B )
  170. // subtracts B from this matrix
  171. {
  172. SparseMatrix<T>& A( *this );
  173. // make sure matrix dimensions agree
  174. assert( A.nRows() == B.nRows() );
  175. assert( A.nColumns() == B.nColumns() );
  176. for( const_iterator e = B.begin();
  177. e != B.end();
  178. e++ )
  179. {
  180. int i = e->first.second;
  181. int j = e->first.first;
  182. const T& Bij( e->second );
  183. A( i, j ) -= Bij;
  184. }
  185. }
  186. template <class T>
  187. SparseMatrix<T> SparseMatrix<T> :: operator+( const SparseMatrix<T>& B ) const
  188. // returns sum of this matrix with B
  189. {
  190. SparseMatrix<T> C( nRows(), nColumns() );
  191. C += *this;
  192. C += B;
  193. return C;
  194. }
  195. template <class T>
  196. SparseMatrix<T> SparseMatrix<T> :: operator-( const SparseMatrix<T>& B ) const
  197. // returns sum of this matrix with B
  198. {
  199. SparseMatrix<T> C( nRows(), nColumns() );
  200. C += *this;
  201. C -= B;
  202. return C;
  203. }
  204. template <class T>
  205. SparseMatrix<T> operator*( const T& c, const SparseMatrix<T>& A )
  206. {
  207. SparseMatrix<T> cA = A;
  208. for( typename SparseMatrix<T>::iterator e = cA.begin();
  209. e != cA.end();
  210. e++ )
  211. {
  212. e->second = c * e->second;
  213. }
  214. return cA;
  215. }
  216. template <class T>
  217. SparseMatrix<T> operator*( const SparseMatrix<T>& A, const T& c )
  218. {
  219. SparseMatrix<T> Ac = A;
  220. Ac *= c;
  221. return Ac;
  222. }
  223. template <class T>
  224. SparseMatrix<T> operator/( const SparseMatrix<T>& A, T c )
  225. {
  226. SparseMatrix<T> Ac = A;
  227. Ac /= c;
  228. return Ac;
  229. }
  230. template <class T>
  231. void SparseMatrix<T> :: resize( int m_, int n_ )
  232. {
  233. m = m_;
  234. n = n_;
  235. data.clear();
  236. }
  237. template <class T>
  238. int SparseMatrix<T> :: nRows( void ) const
  239. // returns the number of rows
  240. {
  241. return m;
  242. }
  243. template <class T>
  244. int SparseMatrix<T> :: nColumns( void ) const
  245. // returns the number of columns
  246. {
  247. return n;
  248. }
  249. template <class T>
  250. int SparseMatrix<T> :: length( void ) const
  251. // returns the size of the largest dimension
  252. {
  253. return max( m, n );
  254. }
  255. template <class T>
  256. void SparseMatrix<T> :: zero( const T& val )
  257. // sets all nonzero elements val
  258. {
  259. for( iterator i = begin(); i != end(); i++ )
  260. {
  261. i->second = val;
  262. }
  263. }
  264. template <class T>
  265. SparseMatrix<T> SparseMatrix<T> :: inverse( void ) const
  266. // returns inverse -- for diagonal matrices only
  267. {
  268. assert( m == n ); // matrix must be square
  269. const SparseMatrix<T>& A( *this );
  270. SparseMatrix<T> Ainv( m, m );
  271. for( const_iterator e = begin(); e != end(); e++ )
  272. {
  273. int r = e->first.second;
  274. int c = e->first.first;
  275. assert( r == c ); // matrix must be diagonal
  276. Ainv( r, c ) = A( r, c ).inv();
  277. }
  278. return Ainv;
  279. }
  280. template <class T>
  281. SparseMatrix<T> SparseMatrix<T> :: identity( int N )
  282. {
  283. SparseMatrix<T> I( N, N );
  284. for( int i = 0; i < N; i++ )
  285. {
  286. I( i, i ) = 1.;
  287. }
  288. return I;
  289. }
  290. template <class T>
  291. DenseMatrix<T> SparseMatrix<T> :: full( void ) const
  292. // converts to a dense matrix
  293. {
  294. const int maxSize = 1048576;
  295. if( m*n > maxSize )
  296. {
  297. cerr << "Error: refusing to convert sparse to dense (too big!)" << "\n";
  298. exit( 1 );
  299. }
  300. const SparseMatrix<T>& A( *this );
  301. DenseMatrix<T> B( m, n );
  302. for( int i = 0; i < m; i++ )
  303. for( int j = 0; j < m; j++ )
  304. {
  305. B( i, j ) = A( i, j );
  306. }
  307. return B;
  308. }
  309. template <class T>
  310. cholmod_sparse* SparseMatrix<T> :: to_cholmod( void )
  311. {
  312. if( cData )
  313. {
  314. cholmod_l_free_sparse( &cData, context );
  315. cData = NULL;
  316. }
  317. allocateSparse();
  318. // build compressed matrix (note that EntryMap stores entries in column-major order)
  319. double* pr = (double*) cData->x;
  320. UF_long* ir = (UF_long*) cData->i;
  321. UF_long* jc = (UF_long*) cData->p;
  322. int i = 0;
  323. int j = -1;
  324. for( const_iterator e = begin();
  325. e != end();
  326. e ++ )
  327. {
  328. int c = e->first.first;
  329. if( c != j )
  330. {
  331. for( int k = j+1; k <= c; k++ )
  332. {
  333. jc[k] = i;
  334. }
  335. j = c;
  336. }
  337. ir[i] = e->first.second;
  338. setEntry( e, i, pr );
  339. i++;
  340. }
  341. for( int k = j+1; k <= n; k++ )
  342. {
  343. jc[k] = i;
  344. }
  345. return cData;
  346. }
  347. template <>
  348. cholmod_sparse* SparseMatrix<Quaternion> :: to_cholmod( void );
  349. template <class T>
  350. T& SparseMatrix<T> :: operator()( int row, int col )
  351. {
  352. EntryIndex index( col, row );
  353. const_iterator entry = data.find( index );
  354. if( entry == end())
  355. {
  356. data[ index ] = T( 0. );
  357. }
  358. return data[ index ];
  359. }
  360. template <class T>
  361. T SparseMatrix<T> :: operator()( int row, int col ) const
  362. {
  363. EntryIndex index( col, row );
  364. const_iterator entry = data.find( index );
  365. if( entry == end())
  366. {
  367. return T( 0. );
  368. }
  369. return entry->second;
  370. }
  371. template <class T>
  372. typename SparseMatrix<T>::iterator SparseMatrix<T> :: begin( void )
  373. {
  374. return data.begin();
  375. }
  376. template <class T>
  377. typename SparseMatrix<T>::const_iterator SparseMatrix<T> :: begin( void ) const
  378. {
  379. return data.begin();
  380. }
  381. template <class T>
  382. typename SparseMatrix<T>::iterator SparseMatrix<T> :: end( void )
  383. {
  384. return data.end();
  385. }
  386. template <class T>
  387. typename SparseMatrix<T>::const_iterator SparseMatrix<T> :: end( void ) const
  388. {
  389. return data.end();
  390. }
  391. template <class T>
  392. void SparseMatrix<T> :: shift( double c )
  393. // adds c times the identity matrix to this matrix
  394. {
  395. assert( m == n );
  396. SparseMatrix<T>& A( *this );
  397. for( int i = 0; i < m; i++ )
  398. {
  399. A( i, i ) += c;
  400. }
  401. }
  402. template <class T>
  403. void solveSymmetric( SparseMatrix<T>& A,
  404. DenseMatrix<T>& x,
  405. DenseMatrix<T>& b )
  406. // solves the sparse linear system Ax = b using sparse LU factorization
  407. {
  408. int t0 = clock();
  409. cholmod_sparse* Ac = A.to_cholmod();
  410. int n = Ac->nrow;
  411. UF_long* Ap = (UF_long*) Ac->p;
  412. UF_long* Ai = (UF_long*) Ac->i;
  413. double* Ax = (double*) Ac->x;
  414. void* Symbolic;
  415. void* Numeric;
  416. umfpack_dl_symbolic( n, n, Ap, Ai, Ax, &Symbolic, NULL, NULL );
  417. umfpack_dl_numeric( Ap, Ai, Ax, Symbolic, &Numeric, NULL, NULL );
  418. umfpack_dl_solve( UMFPACK_A, Ap, Ai, Ax, (double*) &x(0), (double*) &b(0), Numeric, NULL, NULL );
  419. umfpack_dl_free_symbolic( &Symbolic );
  420. umfpack_dl_free_numeric( &Numeric );
  421. int t1 = clock();
  422. cout << "[lu] time: " << seconds( t0, t1 ) << "s" << "\n";
  423. cout << "[lu] max residual: " << residual( A, x, b ) << "\n";
  424. }
  425. template <class T>
  426. void solvePositiveDefinite( SparseMatrix<T>& A,
  427. DenseMatrix<T>& x,
  428. DenseMatrix<T>& b )
  429. // solves the positive definite sparse linear system Ax = b using sparse Cholesky factorization
  430. {
  431. int t0 = clock();
  432. cholmod_sparse* Ac = A.to_cholmod();
  433. Ac->stype = 1;
  434. cholmod_factor* L = cholmod_l_analyze( Ac, context );
  435. cholmod_l_factorize( Ac, L, context );
  436. x = cholmod_l_solve( CHOLMOD_A, L, b.to_cholmod(), context );
  437. if( L ) cholmod_l_free_factor( &L, context );
  438. int t1 = clock();
  439. cout << "[chol] time: " << seconds( t0, t1 ) << "s" << "\n";
  440. cout << "[chol] max residual: " << residual( A, x, b ) << "\n";
  441. }
  442. template <class T>
  443. void backsolvePositiveDefinite( SparseFactor<T>& L,
  444. DenseMatrix<T>& x,
  445. DenseMatrix<T>& b )
  446. // backsolves the prefactored positive definite sparse linear system LL'x = b
  447. {
  448. x = cholmod_l_solve( CHOLMOD_A, L.to_cholmod(), b.to_cholmod(), context );
  449. }
  450. template <class T>
  451. void smallestEig( SparseMatrix<T>& A,
  452. DenseMatrix<T>& x,
  453. bool ignoreConstantVector )
  454. // solves A x = lambda x for the smallest nonzero eigenvalue lambda
  455. // A must be symmetric; x is used as an initial guess
  456. {
  457. int t0 = clock();
  458. for( int iter = 0; iter < maxEigIter; iter++ )
  459. {
  460. solve( A, x, x );
  461. if( ignoreConstantVector )
  462. {
  463. x.removeMean();
  464. }
  465. x.normalize();
  466. }
  467. int t1 = clock();
  468. cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n";
  469. cout << "[eig] max residual: " << residual( A, x ) << "\n";
  470. }
  471. template <class T>
  472. void smallestEig( SparseMatrix<T>& A,
  473. SparseMatrix<T>& B,
  474. DenseMatrix<T>& x )
  475. // solves A x = lambda B x for the smallest nonzero generalized eigenvalue lambda
  476. // A and B must be symmetric; x is used as an initial guess
  477. {
  478. // TODO use a symmetric matrix decomposition instead of QR
  479. int t0 = clock();
  480. // create vector e that has unit norm w.r.t. B
  481. int n = A.length();
  482. DenseMatrix<T> e( n, 1 );
  483. e.zero( 1. );
  484. e /= dot( e, B*e ).norm();
  485. DenseMatrix<T> Be = B*e;
  486. for( int iter = 0; iter < maxEigIter; iter++ )
  487. {
  488. x = B*x;
  489. solve( A, x, x );
  490. x -= dot( x, Be )*e;
  491. x.normalize();
  492. }
  493. int t1 = clock();
  494. cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n";
  495. cout << "[eig] max residual: " << residual( A, B, x ) << "\n";
  496. }
  497. template <class T>
  498. void smallestEigPositiveDefinite( SparseMatrix<T>& A,
  499. DenseMatrix<T>& x,
  500. bool ignoreConstantVector )
  501. // solves A x = lambda x for the smallest nonzero eigenvalue lambda
  502. // A must be positive (semi-)definite; x is used as an initial guess
  503. {
  504. int t0 = clock();
  505. SparseFactor<T> L;
  506. L.build( A );
  507. for( int iter = 0; iter < maxEigIter; iter++ )
  508. {
  509. backsolvePositiveDefinite( L, x, x );
  510. if( ignoreConstantVector )
  511. {
  512. x.removeMean();
  513. }
  514. x.normalize();
  515. }
  516. int t1 = clock();
  517. cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n";
  518. cout << "[eig] max residual: " << residual( A, x ) << "\n";
  519. }
  520. template <class T>
  521. void smallestEigPositiveDefinite( SparseMatrix<T>& A,
  522. SparseMatrix<T>& B,
  523. DenseMatrix<T>& x )
  524. // solves A x = lambda x for the smallest nonzero eigenvalue lambda
  525. // A must be positive (semi-)definite; x is used as an initial guess
  526. {
  527. int t0 = clock();
  528. SparseFactor<T> L;
  529. L.build( A );
  530. // create vector e that has unit norm w.r.t. B
  531. int n = A.length();
  532. DenseMatrix<T> e( n, 1 );
  533. e.zero( 1. );
  534. e /= sqrt( dot( e, B*e ).norm() );
  535. for( int iter = 0; iter < maxEigIter; iter++ )
  536. {
  537. x = B*x;
  538. backsolvePositiveDefinite( L, x, x );
  539. x -= dot( x, B*e )*e;
  540. x /= sqrt( dot( x, B*x ).norm() );
  541. }
  542. int t1 = clock();
  543. cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n";
  544. cout << "[eig] max residual: " << residual( A, B, x ) << "\n";
  545. }
  546. template <class T>
  547. void smallestEigPositiveDefinite( SparseMatrix<T>& A,
  548. SparseMatrix<T>& B,
  549. DenseMatrix<T>& E,
  550. DenseMatrix<T>& x )
  551. // solves A x = lambda (B - EE^T) x for the smallest nonzero eigenvalue lambda
  552. // A must be positive (semi-)definite, B must be symmetric; EE^T is a low-rank matrix, and
  553. // x is used as an initial guess
  554. {
  555. int iter;
  556. int t0 = clock();
  557. DenseMatrix<T> ET = E.transpose();
  558. SparseFactor<T> L;
  559. L.build( A );
  560. for( iter = 0; iter < maxEigIter; iter++ )
  561. {
  562. x = B*x - E*(ET*x);
  563. backsolvePositiveDefinite( L, x, x );
  564. x.normalize();
  565. }
  566. int t1 = clock();
  567. cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n";
  568. cout << "[eig] max residual: " << residual( A, B, E, x ) << "\n";
  569. }
  570. template <class T>
  571. double residual( const SparseMatrix<T>& A,
  572. const DenseMatrix<T>& x,
  573. const DenseMatrix<T>& b )
  574. // returns the max residual of the linear problem A x = b relative to the largest entry of the solution
  575. {
  576. return ( A*x - b ).norm() / b.norm();
  577. }
  578. template <class T>
  579. double residual( const SparseMatrix<T>& A,
  580. const DenseMatrix<T>& x )
  581. // returns the max residual of the eigenvalue problem A x = lambda x relative to the largest entry of the solution
  582. {
  583. T lambda = rayleighQuotient( A, x );
  584. return (A*x-lambda*x).norm() / x.norm();
  585. }
  586. template <class T>
  587. double residual( const SparseMatrix<T>& A,
  588. const SparseMatrix<T>& B,
  589. const DenseMatrix<T>& x )
  590. // returns the max residual of the generalized eigenvalue problem A x = lambda x relative to the largest entry of the solution
  591. {
  592. T lambda = rayleighQuotient( A, B, x );
  593. return (A*x-lambda*(B*x)).norm() / x.norm();
  594. }
  595. template <class T>
  596. double residual( const SparseMatrix<T>& A,
  597. const SparseMatrix<T>& B,
  598. const DenseMatrix<T>& E,
  599. const DenseMatrix<T>& x )
  600. // returns the max residual of the generalized eigenvalue problem A x = lambda (B - EE^T) x relative to the largest entry of the solution
  601. {
  602. T lambda = rayleighQuotient( A, B, E, x );
  603. return (A*x-lambda*(B*x-E*(E.transpose()*x))).norm() / x.norm();
  604. }
  605. template <class T>
  606. T rayleighQuotient( const SparseMatrix<T>& A,
  607. const DenseMatrix<T>& x )
  608. // returns <x,Ax>/<x,x>
  609. {
  610. return (x.transpose()*(A*x))(0) * (x.transpose()*x)(0).inv();
  611. }
  612. template <class T>
  613. T rayleighQuotient( const SparseMatrix<T>& A,
  614. const SparseMatrix<T>& B,
  615. const DenseMatrix<T>& x )
  616. // returns <Ax,x>/<Bx,x>
  617. {
  618. return (x.transpose()*(A*x))(0) * (x.transpose()*(B*x))(0).inv();
  619. }
  620. template <class T>
  621. T rayleighQuotient( const SparseMatrix<T>& A,
  622. const SparseMatrix<T>& B,
  623. const DenseMatrix<T>& E,
  624. const DenseMatrix<T>& x )
  625. // returns <Ax,x>/<(B-EE^T)x,x>
  626. {
  627. return (x.transpose()*(A*x))(0) * (x.transpose()*(B*x-E*(E.transpose()*x)))(0).inv();
  628. }
  629. template <class T>
  630. std::ostream& operator<<( std::ostream& os, const SparseMatrix<T>& o)
  631. {
  632. os.precision( 3 );
  633. for( typename SparseMatrix<T>::const_iterator e = o.begin();
  634. e != o.end();
  635. e ++ )
  636. {
  637. int row = e->first.second;
  638. int col = e->first.first;
  639. os << "( " << row << ", " << col << " ): " << e->second << "\n";
  640. }
  641. return os;
  642. }
  643. template <class T>
  644. SparseFactor<T> :: SparseFactor( void )
  645. : L( NULL )
  646. {}
  647. template <class T>
  648. SparseFactor<T> :: ~SparseFactor( void )
  649. {
  650. if( L )
  651. {
  652. cholmod_l_free_factor( &L, context );
  653. }
  654. }
  655. template <class T>
  656. void SparseFactor<T> :: build( SparseMatrix<T>& A )
  657. {
  658. if( L )
  659. {
  660. cholmod_l_free_factor( &L, context );
  661. L = NULL;
  662. }
  663. int t0, t1;
  664. cholmod_sparse* Ac = A.to_cholmod();
  665. Ac->stype = 1;
  666. t0 = clock();
  667. L = cholmod_l_analyze( Ac, context );
  668. t1 = clock();
  669. cerr << "analyze: " << seconds(t0,t1) << "s" << endl;
  670. t0 = clock();
  671. cholmod_l_factorize( Ac, L, context );
  672. t1 = clock();
  673. cerr << "factorize: " << seconds(t0,t1) << "s" << endl;
  674. }
  675. template <class T>
  676. bool SparseFactor<T> :: valid( void ) const
  677. {
  678. if( L == NULL )
  679. {
  680. return false;
  681. }
  682. return true;
  683. }
  684. template <class T>
  685. cholmod_factor* SparseFactor<T> :: to_cholmod( void )
  686. {
  687. return L;
  688. }
  689. }