Files
PoissonRecon/Src/LinearSolvers.h
T
DESKTOP-P4IRLQG\mkazh 514b259540 no message
2018-04-14 23:00:29 -04:00

570 lines
22 KiB
C++

#ifndef LINEAR_SOLVERS_INCLUDE
#define LINEAR_SOLVERS_INCLUDE
#ifdef USE_CHOLMOD
#include <Cholmod/cholmod.h>
#if defined( WIN32 ) || defined( _WIN64 )
#pragma message( "[WARNING] Need to explicitly exclude VCOMP.lib" )
#pragma comment( lib , "CHOLMOD_FULL.lib" )
#endif // WIN32 || _WIN64
#ifdef DLONG
typedef long long SOLVER_LONG;
#define CHOLMOD( name ) cholmod_l_ ## name
#else // !DLONG
typedef int SOLVER_LONG;
#define CHOLMOD( name ) cholmod_ ## name
#endif // DLONG
#elif defined(EIGEN_USE_MKL_ALL)
#pragma comment( lib , "mkl_core.lib" )
#pragma comment( lib , "mkl_intel_lp64.lib" )
#pragma comment( lib , "mkl_intel_thread.lib" )
#pragma comment( lib , "mkl_blas95_lp64.lib" )
#pragma comment( lib , "libiomp5md.lib" )
#endif // USE_CHOLMOD
#include "SparseMatrixInterface.h"
inline double SquareNorm( const double* values , int dim ){ double norm2 = 0 ; for( int i=0 ; i<dim ; i++ ) norm2 += values[i] * values[i] ; return norm2; }
inline double SquareNorm( const float* values , int dim ){ double norm2 = 0 ; for( int i=0 ; i<dim ; i++ ) norm2 += values[i] * values[i] ; return norm2; }
template< class Type > inline double SquareNorm( const Type* values , int dim ){ double norm2 = 0 ; for( int i=0 ; i<dim ; i++ ) norm2 += values[dim].squareNorm() ; return norm2 ; }
inline double SquareDifference( const double* values1 , const double* values2 , int dim ){ double norm2 = 0 ; for( int i=0 ; i<dim ; i++ ) norm2 += ( values1[i] - values2[i] ) * ( values1[i] - values2[i] ) ; return norm2; }
inline double SquareDifference( const float* values1 , const float* values2 , int dim ){ double norm2 = 0 ; for( int i=0 ; i<dim ; i++ ) norm2 += ( values1[i] - values2[i] ) * ( values1[i] - values2[i] ) ; return norm2; }
template< class Type > inline double SquareDifference( const Type* values1 , const Type* values2 , int dim ){ double norm2 = 0 ; for( int i=0 ; i<dim ; i++ ) norm2 += ( values1[dim] - values2[dim] ).squareNorm() ; return norm2 ; }
// This is the conjugate gradients solver.
// The assumption is that the class SPDOperator defines a method operator()( const Real* , Real* ) which corresponds to applying a symmetric positive-definite operator.
template< class Real >
struct CGScratch
{
Real *r , *d , *q;
CGScratch( void ) : r(NULL) , d(NULL) , q(NULL) , _dim(0){ ; }
CGScratch( int dim ) : r(NULL) , d(NULL) , q(NULL){ resize(dim); }
~CGScratch( void ){ resize(0); }
void resize( int dim )
{
if( dim!=_dim )
{
if( r ) delete[] r ; r = NULL;
if( d ) delete[] d ; d = NULL;
if( q ) delete[] q ; q = NULL;
if( dim ) r = new Real[dim] , d = new Real[dim] , q = new Real[dim];
_dim = dim;
}
}
protected:
int _dim;
};
template< class Real >
struct PreconditionedCGScratch : public CGScratch< Real >
{
Real *s;
PreconditionedCGScratch( void ) : CGScratch< Real >() , s(NULL){ ; }
PreconditionedCGScratch( int dim ) : CGScratch< Real >() { resize(dim); }
~PreconditionedCGScratch( void ){ resize(0); }
void resize( int dim )
{
if( dim!=CGScratch< Real >::_dim )
{
if( s ) delete[] s; s = NULL;
if( dim ) s = new Real[dim];
}
CGScratch< Real >::resize( dim );
}
};
template< class Real >
struct DiagonalPreconditioner
{
Real* iDiagonal;
DiagonalPreconditioner( void ) : iDiagonal(NULL) , _dim(0){ ; }
~DiagonalPreconditioner( void ){ if( iDiagonal ) delete[] iDiagonal ; iDiagonal = NULL; }
template< class MatrixRowIterator >
void set( const SparseMatrixInterface< Real , MatrixRowIterator >& M )
{
if( _dim!=M.rows() )
{
_dim = (int)M.rows();
if( iDiagonal ) delete[] iDiagonal , iDiagonal = NULL;
if( _dim>0 ) iDiagonal = new Real[_dim];
}
memset( iDiagonal , 0 , sizeof(Real)*_dim );
#pragma omp parallel for
for( int i=0 ; i<M.rows() ; i++ )
{
for( MatrixRowIterator iter=M.begin(i) ; iter!=M.end(i) ; iter++ ) if( iter->N==i ) iDiagonal[i] += iter->Value;
iDiagonal[i] = (Real)1./iDiagonal[i];
}
}
void operator()( const Real* in , Real* out ) const
{
#pragma omp parallel for
for( int i=0 ; i<_dim ; i++ ) out[i] = in[i] * iDiagonal[i];
}
protected:
int _dim;
};
template< class Real , class SPDOperator >
int SolveCG( SPDOperator& L , int iters , int dim , const Real* b , Real* x , CGScratch< Real >* scratch=NULL , double eps=1e-8 , int threads=1 , bool verbose=false )
{
eps *= eps;
Real *r , *d , *q;
if( scratch ) r = scratch->r , d = scratch->d , q = scratch->q;
else r = new Real[dim] , d = new Real[dim] , q = new Real[dim];
memset( r , 0 , sizeof(Real)*dim ) , memset( d , 0 , sizeof(Real)*dim ) , memset( q , 0 , sizeof(Real)*dim );
double delta_new = 0 , delta_0;
L( x , r );
#pragma omp parallel for num_threads( threads ) reduction( + : delta_new )
for( int i=0 ; i<dim ; i++ ) d[i] = r[i] = b[i] - r[i] , delta_new += r[i] * r[i];
delta_0 = delta_new;
if( delta_new<eps )
{
if( !scratch ) delete[] r , delete[] d , delete[] q;
return 0;
}
int ii;
for( ii=0 ; ii<iters && delta_new>eps*delta_0 ; ii++ )
{
L( d , q );
double dDotQ = 0;
#pragma omp parallel for num_threads( threads ) reduction( + : dDotQ )
for( int i=0 ; i<dim ; i++ ) dDotQ += d[i] * q[i];
Real alpha = Real( delta_new / dDotQ );
double delta_old = delta_new;
delta_new = 0;
const int RESET_COUNT = 50;
if( (ii%RESET_COUNT)==(RESET_COUNT-1) )
{
#pragma omp parallel for num_threads( threads )
for( int i=0 ; i<dim ; i++ ) x[i] += d[i] * alpha;
L( x , r );
#pragma omp parallel for num_threads( threads ) reduction ( + : delta_new )
for( int i=0 ; i<dim ; i++ ) r[i] = b[i] - r[i] , delta_new += r[i] * r[i];
}
else
#pragma omp parallel for num_threads( threads ) reduction( + : delta_new )
for( int i=0 ; i<dim ; i++ ) r[i] -= q[i] * alpha , delta_new += r[i] * r[i] , x[i] += d[i] * alpha;
Real beta = Real( delta_new / delta_old );
#pragma omp parallel for num_threads( threads )
for( int i=0 ; i<dim ; i++ ) d[i] = r[i] + d[i] * beta;
}
if( verbose )
{
L( x , r );
#pragma omp parallel for num_threads( threads )
for( int i=0 ; i<dim ; i++ ) r[i] -= b[i];
printf( "CG: %d %g -> %g\n" , ii , SquareNorm( b , dim ) , SquareNorm( r , dim ) );
}
if( !scratch ) delete[] r , delete[] d , delete[] q;
return ii;
}
template< class Real , class SPDOperator , class SPDPreconditioner >
int SolvePreconditionedCG( SPDOperator& L , SPDPreconditioner& Pinverse , int iters , int dim , const Real* b , Real* x , PreconditionedCGScratch< Real >* scratch=NULL , double eps=1e-8 , int threads=1 , bool verbose=false )
{
eps *= eps;
Real *r , *d , *q , *s;
if( scratch ) r = scratch->r , d = scratch->d , q = scratch->q , s = scratch->s;
else r = new Real[dim] , d = new Real[dim] , q = new Real[dim] , s = new Real[dim];
memset( r , 0 , sizeof(Real)*dim ) , memset( d , 0 , sizeof(Real)*dim ) , memset( q , 0 , sizeof(Real)*dim ) , memset( s , 0 , sizeof(Real)*dim );
double delta_new = 0 , delta_0;
L( x , r );
#pragma omp parallel for num_threads( threads )
for( int i=0 ; i<dim ; i++ ) r[i] = b[i] - r[i];
Pinverse( r , d );
#pragma omp parallel for num_threads( threads ) reduction( + : delta_new )
for( int i=0 ; i<dim ; i++ ) delta_new += r[i] * d[i];
delta_0 = delta_new;
if( delta_new<eps )
{
if( !scratch ) delete[] r , delete[] d , delete[] q;
return 0;
}
int ii;
for( ii=0 ; ii<iters && delta_new>eps*delta_0 ; ii++ )
{
L( d , q );
double dDotQ = 0;
#pragma omp parallel for num_threads( threads ) reduction( + : dDotQ )
for( int i=0 ; i<dim ; i++ ) dDotQ += d[i] * q[i];
Real alpha = Real( delta_new / dDotQ );
const int RESET_COUNT = 50;
#pragma omp parallel for num_threads( threads )
for( int i=0 ; i<dim ; i++ ) x[i] += d[i] * alpha;
if( (ii%RESET_COUNT)==(RESET_COUNT-1) )
{
L( x , r );
#pragma omp parallel for num_threads( threads )
for( int i=0 ; i<dim ; i++ ) r[i] = b[i] - r[i];
}
else
#pragma omp parallel for num_threads( threads ) reduction( + : delta_new )
for( int i=0 ; i<dim ; i++ ) r[i] -= q[i] * alpha;
Pinverse( r , s );
double delta_old = delta_new;
delta_new = 0;
#pragma omp parallel for num_threads( threads ) reduction( + : delta_new )
for( int i=0 ; i<dim ; i++ ) delta_new += r[i] * s[i];
Real beta = Real( delta_new / delta_old );
#pragma omp parallel for num_threads( threads )
for( int i=0 ; i<dim ; i++ ) d[i] = s[i] + d[i] * beta;
}
if( verbose )
{
L( x , r );
#pragma omp parallel for num_threads( threads )
for( int i=0 ; i<dim ; i++ ) r[i] -= b[i];
printf( "PCCG: %d %g -> %g\n" , ii , SquareNorm( b , dim ) , SquareNorm( r , dim ) );
}
if( !scratch ) delete[] r , delete[] d , delete[] q , delete[] s;
return ii;
}
#ifdef USE_EIGEN
#define STORE_EIGEN_MATRIX
#ifdef EIGEN_USE_MKL_ALL
#include <Eigen/PardisoSupport>
#else // !EIGEN_USE_MKL_ALL
#include <Eigen/Sparse>
#endif // EIGEN_USE_MKL_ALL
template< class Real , class MatrixRowIterator >
struct EigenSolver
{
virtual void update( const SparseMatrixInterface< Real , MatrixRowIterator >& M ) = 0;
virtual void solve( ConstPointer( Real ) b , Pointer( Real ) x ) = 0;
virtual size_t dimension( void ) const = 0;
};
template< class Real , class MatrixRowIterator >
class EigenSolverCholeskyLLt : public EigenSolver< Real , MatrixRowIterator >
{
#ifdef EIGEN_USE_MKL_ALL
typedef Eigen::PardisoLLT< Eigen::SparseMatrix< double > > Eigen_Solver;
typedef Eigen::VectorXd Eigen_Vector;
#else // !EIGEN_USE_MKL_ALL
typedef Eigen::SimplicialLLT< Eigen::SparseMatrix< double > > Eigen_Solver;
typedef Eigen::VectorXd Eigen_Vector;
#endif // EIGEN_USE_MKL_ALL
Eigen_Solver _solver;
Eigen_Vector _eigenB;
#ifdef STORE_EIGEN_MATRIX
Eigen::SparseMatrix< double > _eigenM;
#endif // STORE_EIGEN_MATRIX
public:
EigenSolverCholeskyLLt( const SparseMatrixInterface< Real , MatrixRowIterator >& M , bool analyzeOnly=false )
{
#ifdef STORE_EIGEN_MATRIX
_eigenM.resize( int( M.rows() ) , int( M.rows() ) );
#else // !STORE_EIGEN_MATRIX
Eigen::SparseMatrix< double > eigenM( int( M.rows() ) , int( M.rows() ) );
#endif // STORE_EIGEN_MATRIX
std::vector< Eigen::Triplet< double > > triplets;
triplets.reserve( M.entries() );
for( int i=0 ; i<M.rows() ; i++ ) for( MatrixRowIterator iter=M.begin(i) ; iter!=M.end(i) ; iter++ ) triplets.push_back( Eigen::Triplet< double >( i , iter->N , iter->Value ) );
#ifdef STORE_EIGEN_MATRIX
_eigenM.setFromTriplets( triplets.begin() , triplets.end() );
_solver.analyzePattern( _eigenM );
#else // !STORE_EIGEN_MATRIX
eigenM.setFromTriplets( triplets.begin() , triplets.end() );
_solver.analyzePattern( eigenM );
#endif // STORE_EIGEN_MATRIX
if( !analyzeOnly )
{
#ifdef STORE_EIGEN_MATRIX
_solver.factorize( _eigenM );
#else // !STORE_EIGEN_MATRIX
_solver.factorize( eigenM );
#endif // STORE_EIGEN_MATRIX
if( _solver.info()!=Eigen::Success ) fprintf( stderr , "[ERROR] EigenSolverCholeskyLLt::EigenSolverCholeskyLLt Failed to factorize matrix\n" ) , exit(0);
}
_eigenB.resize( M.rows() );
}
void update( const SparseMatrixInterface< Real , MatrixRowIterator >& M )
{
#ifdef STORE_EIGEN_MATRIX
#pragma omp parallel for
for( int i=0 ; i<M.rows() ; i++ ) for( MatrixRowIterator iter=M.begin(i) ; iter!=M.end(i) ; iter++ ) _eigenM.coeffRef( i , iter->N ) = iter->Value;
_solver.factorize( _eigenM );
#else // !STORE_EIGEN_MATRIX
Eigen::SparseMatrix< double > eigenM( int( M.rows() ) , int( M.rows() ) );
std::vector< Eigen::Triplet< double > > triplets;
triplets.reserve( M.entries() );
for( int i=0 ; i<M.rows() ; i++ ) for( MatrixRowIterator iter=M.begin(i) ; iter!=M.end(i) ; iter++ ) triplets.push_back( Eigen::Triplet< double >( i , iter->N , iter->Value ) );
eigenM.setFromTriplets( triplets.begin() , triplets.end() );
_solver.factorize( eigenM );
#endif // STORE_EIGEN_MATRIX
switch( _solver.info() )
{
case Eigen::Success: break;
case Eigen::NumericalIssue: fprintf( stderr , "[ERROR] EigenSolverCholeskyLLt::update Failed to factorize matrix (numerical issue)\n" ) , exit(0);
case Eigen::NoConvergence: fprintf( stderr , "[ERROR] EigenSolverCholeskyLLt::update Failed to factorize matrix (no convergence)\n" ) , exit(0);
case Eigen::InvalidInput: fprintf( stderr , "[ERROR] EigenSolverCholeskyLLt::update Failed to factorize matrix (invalid input)\n" ) , exit(0);
default: fprintf( stderr , "[ERROR] EigenSolverCholeskyLLt::update Failed to factorize matrix\n" ) , exit(0);
}
}
void solve( ConstPointer( Real ) b , Pointer( Real ) x )
{
#pragma omp parallel for
for( int i=0 ; i<_eigenB.size() ; i++ ) _eigenB[i] = b[i];
Eigen_Vector eigenX = _solver.solve( _eigenB );
#pragma omp parallel for
for( int i=0 ; i<eigenX.size() ; i++ ) x[i] = (Real)eigenX[i];
}
size_t dimension( void ) const { return _eigenB.size(); }
static void Solve( const SparseMatrixInterface< Real , MatrixRowIterator >& M , ConstPointer( Real ) b , Pointer( Real ) x ){ EigenSolverCholeskyLLt solver( M ) ; solver.solve( b , x ); }
};
template< class Real , class MatrixRowIterator >
class EigenSolverCholeskyLDLt : public EigenSolver< Real , MatrixRowIterator >
{
#ifdef EIGEN_USE_MKL_ALL
typedef Eigen::PardisoLDLT< Eigen::SparseMatrix< double > > Eigen_Solver;
typedef Eigen::VectorXd Eigen_Vector;
#else // !EIGEN_USE_MKL_ALL
typedef Eigen::SimplicialLDLT< Eigen::SparseMatrix< double > > Eigen_Solver;
typedef Eigen::VectorXd Eigen_Vector;
#endif // EIGEN_USE_MKL_ALL
Eigen_Solver _solver;
Eigen_Vector _eigenB;
public:
EigenSolverCholeskyLDLt( const SparseMatrixInterface< Real , MatrixRowIterator >& M , bool analyzeOnly=false )
{
Eigen::SparseMatrix< double > eigenM( int( M.rows() ) , int( M.rows() ) );
std::vector< Eigen::Triplet<double> > triplets;
triplets.reserve( M.entries() );
for( int i=0 ; i<M.rows() ; i++ ) for( MatrixRowIterator iter=M.begin(i) ; iter!=M.end(i) ; iter++ ) triplets.push_back( Eigen::Triplet< double >( i , iter->N , iter->Value ) );
eigenM.setFromTriplets( triplets.begin() , triplets.end() );
_solver.analyzePattern( eigenM );
if( !analyzeOnly )
{
_solver.factorize( eigenM );
if( _solver.info()!=Eigen::Success ) fprintf( stderr , "[ERROR] EigenSolverCholeskyLDLt::EigenSolverCholeskyLDLt Failed to factorize matrix\n" ) , exit(0);
}
_eigenB.resize( M.rows() );
}
void update( const SparseMatrixInterface< Real , MatrixRowIterator >& M )
{
Eigen::SparseMatrix< double > eigenM( int( M.rows() ) , int( M.rows() ) );
std::vector< Eigen::Triplet<double> > triplets;
triplets.reserve( M.entries() );
for( int i=0 ; i<M.rows() ; i++ ) for( MatrixRowIterator iter=M.begin(i) ; iter!=M.end(i) ; iter++ ) triplets.push_back( Eigen::Triplet< double >( i , iter->N , iter->Value ) );
eigenM.setFromTriplets( triplets.begin() , triplets.end() );
_solver.factorize( eigenM );
if( _solver.info()!=Eigen::Success ) fprintf( stderr , "[ERROR] EigenSolverCholeskyLDLt::update Failed to factorize matrix\n" ) , exit(0);
}
void solve( ConstPointer( Real ) b , Pointer( Real ) x )
{
#pragma omp parallel for
for( int i=0 ; i<_eigenB.size() ; i++ ) _eigenB[i] = b[i];
Eigen_Vector eigenX = _solver.solve( _eigenB );
#pragma omp parallel for
for( int i=0 ; i<eigenX.size() ; i++ ) x[i] = (Real)eigenX[i];
}
size_t dimension( void ) const { return _eigenB.size(); }
static void Solve( const SparseMatrixInterface< Real , MatrixRowIterator >& M , ConstPointer( Real ) b , Pointer( Real ) x ){ EigenSolverCholeskyLDLt solver( M ) ; solver.solve( b , x ); }
};
template< class Real , class MatrixRowIterator >
class EigenSolverCG : public EigenSolver< Real , MatrixRowIterator >
{
#if 1
// Eigen::ConjugateGradient< Eigen::SparseMatrix< double > , Eigen::Lower , Eigen::IncompleteLUT< double > > _solver;
Eigen::ConjugateGradient< Eigen::SparseMatrix< double > > _solver;
#else
Eigen::BiCGSTAB< Eigen::SparseMatrix< double > > _solver;
#endif
Eigen::VectorXd _eigenB , _eigenX;
Eigen::SparseMatrix< double > _eigenM;
public:
EigenSolverCG( const SparseMatrixInterface< Real , MatrixRowIterator >& M , int iters=20 , double tolerance=0. )
{
_eigenM.resize( (int)M.rows() , (int)M.rows() );
std::vector< Eigen::Triplet< double > > triplets;
triplets.reserve( M.entries() );
for( int i=0 ; i<M.rows() ; i++ ) for( MatrixRowIterator iter=M.begin(i) ; iter!=M.end(i) ; iter++ ) triplets.push_back( Eigen::Triplet< double >( i , iter->N , iter->Value ) );
_eigenM.setFromTriplets( triplets.begin() , triplets.end() );
_solver.compute( _eigenM );
_solver.analyzePattern( _eigenM );
if( _solver.info()!=Eigen::Success ) fprintf( stderr , "[ERROR] EigenSolverCG::EigenSolverCG Failed to factorize matrix\n" ) , exit(0);
_eigenB.resize( M.rows() ) , _eigenX.resize( M.rows() );
_solver.setMaxIterations( iters );
_solver.setTolerance( tolerance );
}
void update( const SparseMatrixInterface< Real , MatrixRowIterator >& M )
{
#pragma omp parallel for
for( int i=0 ; i<M.rows() ; i++ ) for( MatrixRowIterator iter=M.begin(i) ; iter!=M.end(i) ; iter++ ) _eigenM.coeffRef( i , iter->N ) = iter->Value;
_solver.compute( _eigenM );
_solver.analyzePattern( _eigenM );
if( _solver.info()!=Eigen::Success ) fprintf( stderr , "[ERROR] EigenSolverCG::update Failed to factorize matrix\n" ) , exit(0);
}
void setIters( int iters ){ _solver.setMaxIterations( iters ); }
void solve( ConstPointer( Real ) b , Pointer( Real ) x )
{
#pragma omp parallel for
for( int i=0 ; i<_eigenB.size() ; i++ ) _eigenB[i] = b[i] , _eigenX[i] = x[i];
_eigenX = _solver.solveWithGuess( _eigenB , _eigenX );
#pragma omp parallel for
for( int i=0 ; i<_eigenX.size() ; i++ ) x[i] = _eigenX[i];
}
size_t dimension( void ) const { return _eigenB.size(); }
static void Solve( const SparseMatrixInterface< Real , MatrixRowIterator >& M , const Real* b , Real* x , int iters ){ EigenSolverCG solver( M , iters ) ; solver.solve( b , x ); }
};
#endif // USE_EIGEN
#ifdef USE_CHOLMOD
class CholmodSolver
{
const static bool LOWER_TRIANGULAR = true;
int dim;
cholmod_factor* cholmod_L;
cholmod_dense* cholmod_b;
cholmod_sparse* cholmod_M;
std::vector< bool > flaggedValues;
template< class Real , class MatrixRowIterator > void _init( const SparseMatrixInterface< Real , MatrixRowIterator >& M );
public:
static cholmod_common cholmod_C;
static bool cholmod_C_set;
template< class Real , class MatrixRowIterator >
CholmodSolver( const SparseMatrixInterface< Real , MatrixRowIterator >& M , bool analyzeOnly=false );
~CholmodSolver( void );
template< class Real > void solve( ConstPointer( Real ) b , Pointer( Real ) x );
template< class Real , class MatrixRowIterator > bool update( const SparseMatrixInterface< Real , MatrixRowIterator >& M );
int nonZeros( void ) const;
};
bool CholmodSolver::cholmod_C_set = false;
cholmod_common CholmodSolver::cholmod_C;
template< class Real , class MatrixRowIterator > CholmodSolver::CholmodSolver( const SparseMatrixInterface< Real , MatrixRowIterator >& M , bool analyzeOnly ){ _init( M ) ; if( !analyzeOnly ) update( M ); }
template< class Real , class MatrixRowIterator >
void CholmodSolver::_init( const SparseMatrixInterface< Real , MatrixRowIterator >& M )
{
{
if( !cholmod_C_set ) CHOLMOD(start)( &cholmod_C );
cholmod_C_set = true;
}
dim = (int)M.rows();
int maxEntries;
if( LOWER_TRIANGULAR )
{
maxEntries = (int)( ( M.entries()-M.rows() ) / 2 + M.rows() );
cholmod_M = CHOLMOD(allocate_sparse)( dim , dim , maxEntries , 0 , 1 , -1 , CHOLMOD_REAL , &cholmod_C );
}
else
{
maxEntries = (int)M.entries();
cholmod_M = CHOLMOD(allocate_sparse)( dim , dim , maxEntries , 0 , 1 , 0 , CHOLMOD_REAL , &cholmod_C );
}
cholmod_M->i = malloc( sizeof( SOLVER_LONG ) * maxEntries );
cholmod_M->x = malloc( sizeof( double ) * maxEntries );
SOLVER_LONG *_p = (SOLVER_LONG*)cholmod_M->p;
SOLVER_LONG *_i = (SOLVER_LONG*)cholmod_M->i;
int off = 0;
dim = 0;
for( int i=0 ; i<M.rows() ; i++ )
{
_p[dim++] = off;
for( MatrixRowIterator iter=M.begin(i) ; iter!=M.end(i) ; iter++ ) if( !LOWER_TRIANGULAR || iter->N>=i ) _i[off++] = iter->N;
}
_p[dim] = off;
cholmod_L = CHOLMOD(analyze)( cholmod_M , &cholmod_C );
cholmod_b = CHOLMOD(allocate_dense)( dim , 1 , dim , cholmod_M->xtype , &cholmod_C );
}
template< class Real , class MatrixRowIterator >
bool CholmodSolver::update( const SparseMatrixInterface< Real , MatrixRowIterator >& M )
{
double *_x = (double*)cholmod_M->x;
int off = 0;
SOLVER_LONG *_p = (SOLVER_LONG*)cholmod_M->p;
#pragma omp parallel for
for( int i=0 ; i<M.rows() ; i++ )
{
int off = (int)_p[i];
for( MatrixRowIterator iter=M.begin(i) ; iter!=M.end(i) ; iter++ )if( !LOWER_TRIANGULAR || iter->N>=i ) _x[off++] = double( iter->Value );
}
cholmod_C.print = 0;
CHOLMOD(factorize)( cholmod_M , cholmod_L , &cholmod_C );
if( cholmod_C.status==CHOLMOD_NOT_POSDEF )
{
fprintf( stderr , "[WARNING] CholmodSolver::update: Matrix not positive-definite\n" );
return false;
}
else if( cholmod_C.status==CHOLMOD_OUT_OF_MEMORY )
{
fprintf( stderr , "[WARNING] CholmodSolver::update: CHOLMOD ran out of memory\n" );
return false;
}
else if( cholmod_C.status!=CHOLMOD_OK )
{
fprintf( stderr , "[WARNING] CholmodSolver::update: CHOLMOD status not OK: %d\n" , cholmod_C.status );
return false;
}
return true;
}
CholmodSolver::~CholmodSolver( void )
{
if( cholmod_L ) CHOLMOD(free_factor)( &cholmod_L , &cholmod_C ) , cholmod_L = NULL;
if( cholmod_b ) CHOLMOD(free_dense )( &cholmod_b , &cholmod_C ) , cholmod_b = NULL;
if( cholmod_M ) CHOLMOD(free_sparse)( &cholmod_M , &cholmod_C ) , cholmod_M = NULL;
}
template< class Real >
void CholmodSolver::solve( ConstPointer( Real ) b , Pointer( Real ) x )
{
double* _b = (double*)cholmod_b->x;
for( int i=0 ; i<dim ; i++ ) _b[i] = (double)b[i];
cholmod_dense* cholmod_x = CHOLMOD(solve)( CHOLMOD_A , cholmod_L , cholmod_b , &cholmod_C );
double* _x = (double*)cholmod_x->x;
for( int i=0 ; i<dim ; i++ ) x[i] = (Real)_x[i];
CHOLMOD(free_dense)( &cholmod_x , &cholmod_C );
}
int CholmodSolver::nonZeros( void ) const
{
long long nz = 0;
if( cholmod_L->xtype != CHOLMOD_PATTERN && !(cholmod_L->is_super ) ) for( int i=0 ; i<cholmod_L->n ; i++ ) nz += ((SOLVER_LONG*)cholmod_L->nz)[i];
bool examine_super = false;
if( cholmod_L->xtype != CHOLMOD_PATTERN ) examine_super = true ;
else examine_super = ( ((int*)cholmod_L->s)[0] != (-1));
if( examine_super )
{
/* check and print each supernode */
for (int s = 0 ; s < cholmod_L->nsuper ; s++)
{
int k1 = ((int*)cholmod_L->super) [s] ;
int k2 = ((int*)cholmod_L->super) [s+1] ;
int psi = ((int*)cholmod_L->pi)[s] ;
int psend = ((int*)cholmod_L->pi)[s+1] ;
int nsrow = psend - psi ;
int nscol = k2 - k1 ;
nz += nscol * nsrow - (nscol*nscol - nscol)/2 ;
}
}
return (int)nz;
}
#endif // USE_CHOLMOD
#endif // LINEAR_SOLVERS_INCLUDE