[eigen] Sample code for MUMPS backend

[ Thread Index | Date Index | More lists.tuxfamily.org/eigen Archives ]


Hi,

Benoit replied to my issue tracker entry I should send a piece of code to this list, what I do now.

I want to provide you with some example code for the MUMPS sparse solver backend. I am not familiar with the development of Eigen, but I hope I can assist one one you such that the adjustments will be quite easy. The code is excerpted from an open source project which will be released under BSD later this year. So there will be no problem if you redistribute it under LGPLv3 or something else.

The idea of a MUMPS backend is the following:

- MUMPS supports single and double precision, real and complex numbers. For each, another interface routine is provided for reverse communication. These routines can be encapsulated in a unified template class named "TMumpsInterface".

- In Eigen 2.0.x, the solvers work in two stages: 1st factorize and store the factorized form of the input matrix. The input matrix can be descarded afterwards. 2nd using the factorized matrix object, one can solve dense vectors or matrices (either SolveInPlace or Solve). MUMPS works in SolveInPlace mode, btw.

- To me it was also important that the same solver object can be used several times, that means multiple times for solving equation systems with equal system matrix and different rhs; and multiple times for solving equation systems with different system matrix. Therefore, the factorized (MUMPS internal) structure must be cleaned before factorizing a new matrix (NOTE: This is not successfully implemented in the other solver backends at the moment!)

- The sample code is provided for LU factorization; MUMPS also supports LDL and LL factorization (then, only the triangular parts must be stored). The flags to be changed are commented.

- In my opinion, errors of the backend should be made accessible to the user, also by string representations. I think, so far this is not implemented in the other backends.

- The sample code is meant as pseudo C++ code. I used some typedefs for matrices, sparse matrices and scalars. TScalar is double, Matrix is a dynamically sized column-major double precision Eigen mantrix, SparseMatrix is a double precision column-major Eigen SparseMatrix.


Best regards
Sebastian


Sample code:


Header:

#!C++

#include <Eigen/Core>
#include <Eigen/Sparse>

extern "C"
{
#include <dmumps_c.h>
#include <smumps_c.h>
}

	template <typename T> struct TMumpsInterface {};
	template <> struct TMumpsInterface<float> 	{
		typedef SMUMPS_STRUC_C  MUMPS_STRUC_C;
		static void mumps_c(MUMPS_STRUC_C &info) { smumps_c(&info); }

	};

	template <> struct TMumpsInterface<double> 	{
		typedef DMUMPS_STRUC_C  MUMPS_STRUC_C;
		static void mumps_c(MUMPS_STRUC_C &info) { dmumps_c(&info); }
	};

	/**
		\brief LU solver for sparse matrices using MUMPS.
	*/
	class SparseLUMUMPS : public SparseLU
	{
		public:
			// constructors ------------------------------------------------------
			//! destructor
			virtual ~SparseLUMUMPS();
			//! default constructor
			SparseLUMUMPS();

			virtual void SetFlags(int flag);
			virtual void SetPrecision(double prec);

virtual bool Solve(const MatrixBase &b, MatrixBase &x); // no const function !!!
			virtual bool SolveInPlace(MatrixBase &b); // no const function !!!
			virtual bool Compute(const SparseMatrixBase &);
		private:
			typedef TMumpsInterface<TScalar> TSolverInterface;
			typedef TSolverInterface::MUMPS_STRUC_C TSolverData;
			TSolverData m_solver;
			bool m_solvercontainsdata;
			void init();
			void clear();
			const std::string mumpserror(int id) const;
	};

Source:

#!C++

	SparseLUMUMPS::SparseLUMUMPS() : SparseLU(), m_solvercontainsdata(false)
	{
		init();
	}
	SparseLUMUMPS::~SparseLUMUMPS()
	{
		clear();
	}
	void SparseLUMUMPS::init()
	{
		m_solver.comm_fortran = -987654; // for MPI (also required if MPI=disabled)
		m_solver.par = 1; // will be used for factorization AND solution
m_solver.sym = 0; // matrix is not symmetric //m_solver.sym = 1; // matrix is symmetric and positive definite
		//m_solver.sym = 2; // matrix is symmetric and not positive definite
		m_solver.job = -1; // initialize solver

		m_solver.irn = 0; // rows
		m_solver.jcn = 0; // columns
		m_solver.a   = 0; // values

		m_solvercontainsdata = false;
		succeeded_factorization() = false;
	}
	void SparseLUMUMPS::clear()
	{
		if (m_solvercontainsdata)
		{
			m_solver.job = -2;
			TSolverInterface::mumps_c(m_solver);
			if (m_solver.info[0] < 0)
			{
				std::stringstream msg;
msg << "MUMPS error at termination: " << mumpserror(m_solver.info[0]) << ".";
				GLOBAL_THROW      (logger_tng_tngmath, msg.str())
			}
			m_solvercontainsdata = false;
			succeeded_factorization() = false;

			// also delete allocated data:
			if (m_solver.irn != 0) delete[] m_solver.irn;
			if (m_solver.jcn != 0) delete[] m_solver.jcn;
			if (m_solver.a   != 0) delete[] m_solver.a;
			m_solver.irn = 0; // rows
			m_solver.jcn = 0; // columns
			m_solver.a   = 0; // values

		}
	}
	const std::string SparseLUMUMPS::mumpserror(int id) const
	{
		switch (id) 		{
			case -6: 			case -10:
				return "matrix is singular";
			case -13 :
				return "not enough memory";
			case -9:
			{
				std::stringstream msg;
				msg << "error " << id << ", increase ICNTL(14)";
				return msg.str();
			}
			default:
			{
				std::stringstream msg;
				msg << "error " << id;
				return msg.str();
			}
		}
	}
	bool SparseLUMUMPS::Compute(const SparseMatrixBase &A)
	{
		#define ICNTL(I) icntl[(I)-1] 		if ( A.rows() != A.cols() )
		{
			std::stringstream msg;
msg << "Can not factorize a nonsquare matrix ("<<A.rows()<<" x "<<A.cols()<<")";
			GLOBAL_THROW      (logger_tng_tngmath, msg.str())
		}
m_dimensions = std::pair<int,int>(A.rows(),A.cols()); // save dimensions of matrix for later checks


		clear(); // delete allocated data from previous run
		init(); // reinit MUMPS engine parameters
		TSolverInterface::mumps_c(m_solver); // let MUMPS initialize itself.

		// create data vectors 		int * rows(0);
		int * cols(0);
		TScalar * values(0);
		if (A.nonZeros() != 0)
		{
			try
			{
				rows = new int[A.nonZeros()];
				cols = new int[A.nonZeros()];
				values = new TScalar[A.nonZeros()];
			}
GLOBAL_CATCH_THROW (logger_tng_tngmath, "Out of memory (could not allocate work space).")
		}

		unsigned long int counter_value(0);
		for (int k=0; k<A.outerSize(); ++k)
		for (TSparseMatrix::InnerIterator it(A,k); it; ++it)
		{
// 			if (it.row() > it.col()) continue; // upper triangular + diagonal
			values[counter_value] = it.value();
			rows  [counter_value] = it.row()+1;
			cols  [counter_value] = it.col()+1;
			++counter_value;
		}


		m_solvercontainsdata = true;

		// prepare 		m_solver.n   = A.rows();
		m_solver.nz  = counter_value;//A.nonZeros();
		m_solver.irn = rows; // row indices
		m_solver.jcn = cols; // column indices
		m_solver.a   = values; // values
		m_solver.rhs = 0; // right hand side will be set later
		// solver params
		m_solver.icntl[0] = 0; // output errors
		m_solver.icntl[1] = 0; // output detailed messages
		m_solver.icntl[2] = 0; // output info messages
		m_solver.icntl[3] = 0; // verbose level
m_solver.icntl[5] = 7; // automatically find out if to scale or permute m_solver.icntl[6] = 7; // automatic pivot order for the factorization m_solver.icntl[7] = 7; // simultaneous row and colum iterative scaling (for not symmetric matrices)
		//m_solver.icntl[7] = 1; // diagonal scaling (for symmetric matrices)

// 		m_solver.icntl[0] = 6; // output errors
// 		m_solver.icntl[1] = 6; // output detailed messages
// 		m_solver.icntl[2] = 6; // output info messages
// 		m_solver.icntl[3] = 0; // verbose level



		// analysis phase
		m_solver.job = 1;
		TSolverInterface::mumps_c(m_solver); // call MUMPS
		if (m_solver.info[0] < 0)
		{
			std::stringstream msg;
msg << "MUMPS error during analysis and reordering: " << mumpserror(m_solver.info[0]) << ".";
			GLOBAL_THROW      (logger_tng_tngmath, msg.str())
		}

		// factorization
		m_solver.job = 2;
		TSolverInterface::mumps_c(m_solver); // call MUMPS
		if (m_solver.info[0] < 0)
		{
			std::stringstream msg;
msg << "MUMPS error during factorization: " << mumpserror(m_solver.info[0]) << ".";
			GLOBAL_THROW      (logger_tng_tngmath, msg.str())
		}

		/* possible strategy: what may happen if ICNTL(14) was not correctly set?
		m_solver.ICNTL(14) /= 2;
		int i_iter = 0;
		do
		{
			++i_iter;
			m_solver.ICNTL(14) *= 2;
			TSolverInterface::mumps_c(m_solver);
		}
		while(m_solver.info[0] == -9 && i_iter < 3);
       */



		// delete temporarily allocated data:
		if (m_solver.irn != 0) delete[] m_solver.irn;
		if (m_solver.jcn != 0) delete[] m_solver.jcn;
		if (m_solver.a   != 0) delete[] m_solver.a;
		m_solver.irn = 0; // rows
		m_solver.jcn = 0; // columns
		m_solver.a   = 0; // values

		m_solver.job = 3; // next job will be "solution"

		succeeded_factorization() = true; 		return succeeded_factorization();
	}
	bool SparseLUMUMPS::Solve(const MatrixBase &b, MatrixBase &x) 	{
		if (b.rows() != m_dimensions.second)
		{
			std::stringstream msg;
			msg << "Incompatible dimensions.";
			GLOBAL_THROW      (logger_tng_tngmath, msg.str())
		}

		if (!succeeded_factorization() || !m_solvercontainsdata) return false;

		x = b;

		// MUMPS solution
		m_solver.job = 3;
		for (int column_rhs = 0; column_rhs != x.cols(); ++column_rhs)
		{
			m_solver.rhs = (TScalar*) (&(x.data()[column_rhs*x.rows()]));
			TSolverInterface::mumps_c(m_solver); // call MUMPS
			if (m_solver.info[0] < 0)
			{
				std::stringstream msg;
msg << "MUMPS error during solution: " << mumpserror(m_solver.info[0]) << ".";
				GLOBAL_THROW      (logger_tng_tngmath, msg.str())
			}
		}
		m_solver.rhs = 0;

		return true;
	}
	bool SparseLUMUMPS::SolveInPlace(MatrixBase &b) 	{
		if (b.rows() != m_dimensions.second)
		{
			std::stringstream msg;
			msg << "Incompatible dimensions.";
			GLOBAL_THROW      (logger_tng_tngmath, msg.str())
		}

		if (!succeeded_factorization() || !m_solvercontainsdata) return false;

		// MUMPS solution
		m_solver.job = 3;
		for (int column_rhs = 0; column_rhs != b.cols(); ++column_rhs)
		{
m_solver.rhs = const_cast<double*>(&(b.data()[((size_t)column_rhs)*b.rows()]));
			TSolverInterface::mumps_c(m_solver); // call MUMPS
			if (m_solver.info[0] < 0)
			{
				std::stringstream msg;
msg << "MUMPS error during solution: " << mumpserror(m_solver.info[0]) << ".";
				GLOBAL_THROW      (logger_tng_tngmath, msg.str())
			}
		}
		m_solver.rhs = 0;

		return true;
	}
	void SparseLUMUMPS::SetFlags(int flag)
	{
// 		solver().setFlags(flag);
	}
	void SparseLUMUMPS::SetPrecision(double prec)
	{
// 		solver().setPrecision(prec);
	}








Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/