⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 lufactor.hpp

📁 This collection of C++ templates wraps the FORTRAN or C interfaces for LAPACK so that they integrate
💻 HPP
字号:
/* LU decomposition.
 *
 * Perform LU factorisation, and LU-based inverse and determinant.
 *
 * Notes:
 *	(1) When computing the LU decomposition for row-major matrices, it is necessary
 * to transpose the matrix before and after the operation (to be compatible with the
 * LAPACK column-major routines). However, when computing the matrix inverse, *neither* 
 * transposition is required. This is because trans(inv(A)) is equal to inv(trans(A)).
 *
 * TODOs:
 *	(1) Confirm that the second statement of Note (1) is correct. The results seem
 * reasonable, but there is some discrepancy between a row-major and a column-major
 * answer. This is probably just differing roundoff errors.
 *	(2) Implement reciprocal condition estimator: dgecon_
 *	(3) Implement an LU-based solver: dgetrs_
 *
 * Tim Bailey 2005.
 */

#ifndef ULAPACK_ULFACTOR_HPP_
#define ULAPACK_ULFACTOR_HPP_

#include "lapack_exception.hpp"
#include "errormacros.hpp"

namespace ulapack {
	namespace ublas = boost::numeric::ublas;
	namespace detail {

		extern "C" 		{		// LAPACK function declarations		// LU decomposition of a real square matrix		void dgetrf_( const int *m, const int *n, double *a, 
			const int *lda, const int *ipiv, int *info );

		// Inverse of LU factorised matrix
		void dgetri_(const int *n, double *a, const int *lda, const int *ipiv,
			double *work, const int *lwork, int *info );

		} // extern "C" 

		// LU inversion uses a block algorithm, which is more efficient over 
		// a certain matrix size, CUTOFF.
		enum { CUTOFF=64, BLOCKSIZE=64 }; 

		template<class F, class A>
		int lufactor_basic(ublas::matrix<double,F,A> &m, ublas::vector<int> &pivot)
		// Perform LU decomposition (in-place). Returns 0 if ok, LAPACK info if singular.
		// This "basic" function produces correct LU decompose for column-major matrices only.
		{
			// Call LAPACK routine
			const int M = static_cast<int>(m.size1());
			const int N = static_cast<int>(m.size2());
			int info;
			detail::dgetrf_( &M, &N, &m.data()[0], &M, &pivot.data()[0], &info );
			// Check validity of result			if (info < 0) 				throw LogicalError(ERROR_INFO("Invalid argument"), info);			return info;		}

		template<class F, class A>
		void inverse_factored(ublas::matrix<double,F,A> &m, 
			const ublas::vector<int> &pivot, ublas::vector<double> &work)
		// Compute inverse (in-place) of a matrix that has already been decomposed by lufactor_basic.
		// The pivot vector values are assumed to correspond to m.
		{
			// Set work vector size
			size_t bsize = m.size1();
			if (m.size1() > detail::CUTOFF) // use block-size > 1 if m large
				bsize *= detail::BLOCKSIZE; 
			if (work.size() < bsize)
				work.resize(bsize);

			// Call LAPACK inversion routine
			const int N = static_cast<int>(m.size1());
			const int wsize = static_cast<int>(work.size());
			int info;
			detail::dgetri_(&N, &m.data()[0], &N, &pivot.data()[0],
				&work.data()[0], &wsize, &info);

			// Check validity of result			if (info < 0) 				throw LogicalError(ERROR_INFO("Invalid argument"), info);			else if (info > 0) 				throw NumericalError(ERROR_INFO("Matrix is singular"), info);		}

		template<class F, class A>
		double determinant_factored(const ublas::matrix<double,F,A> &m,
			const ublas::vector<int> &pivot)
		// Compute matrix determinant of a matrix that has already been decomposed by lufactor_basic.
		// The pivot vector values are assumed to correspond to m.
		{
			double det = 1.;
			for (size_t i=0; i < m.size1(); ++i) {
				if (pivot(i) != i+1) 
					det = -det;
				det *= m(i,i);
			}

			return det;
		}

	} // namespace detail

	// --------------------------------------------------------------------------
	// --------------------------- In-place algorithms --------------------------
	// --------------------------------------------------------------------------

	template<class A>
	bool lufactor(ublas::matrix<double, ublas::column_major, A> &m, 
		ublas::vector<int> &pivot)
	// Perform LU decomposition for column-major matrices (in-place).
	// Returns false if matrix is singular.
	{
		const size_t N = std::min(m.size1(), m.size2());
		if (pivot.size() < N)
			pivot.resize(N);
		return detail::lufactor_basic(m, pivot) == 0;	}

	template<class A>
	bool lufactor(ublas::matrix<double, ublas::row_major, A> &m, 
		ublas::vector<int> &pivot)
	// Perform LU decomposition for row-major matrices (in-place).
	// Returns false if matrix is singular.
	// Warning: row-major lu-decomposition is less efficient than column-major.
	{
		ublas::matrix<double, ublas::column_major, A> tmp(m);		bool status = lufactor(tmp, pivot); // call col-major version		m = tmp;		return status;		// Note, we copy to column-major here rather than perform transposes explicitly,		// as this gives us the correct size1() and size2() values for lufactor_basic()		// whereas the m=trans(m) operations would not. Also, it gives us the right pivot.	} 

	template<class F, class A>
	void inv_inplace(ublas::matrix<double,F,A> &m)
	// Compute matrix inverse (in-place).
	{
		if (m.size1() != m.size2())
			throw LogicalError(ERROR_INFO("Matrix is not square"));

		// LU decompose
		ublas::vector<int> pivot(m.size1());		int info = detail::lufactor_basic(m, pivot);		if (info != 0) 			throw NumericalError(ERROR_INFO("Matrix is singular"), info);
		// Invert
		ublas::vector<double> work;
		detail::inverse_factored(m, pivot, work);

		// Check that we are using an optimal block size (useful check in pre-release builds)		assert(work.size() >= work.data()[0]); 	}

	// --------------------------------------------------------------------------
	// ------------------------- Non-in-place algorithms ------------------------
	// --------------------------------------------------------------------------

	template<class F, class A>
	ublas::matrix<double,F,A> inv(const ublas::matrix<double,F,A> &m)
	// Compute matrix inverse.
	{
		ublas::matrix<double,F,A> tmp(m);
		inv_inplace(tmp);
		return tmp;
	}

	template<class F, class A>
	double det(const ublas::matrix<double,F,A> &m)
	// Compute matrix determinant
	{
		if (m.size1() != m.size2())
			throw LogicalError(ERROR_INFO("Matrix is not square"));

		ublas::matrix<double,F,A> lu(m);
		ublas::vector<int> pivot(lu.size1());		if (detail::lufactor_basic(lu, pivot) != 0)			return 0.; // matrix singular implies det == 0
		return detail::determinant_factored(lu, pivot);
	}

	// Wrap everything up as a class, templatised by M - the matrix type.
	// Class caches the essential storage: LU matrix, pivot, and working.
	template<class M>
	class LU {
	public:
		LU(const M &m) : lu_(m), pivot_(m.size1()), 
			info(detail::lufactor_basic(lu_, pivot_)) {}

// Unsafe operations: (they give incorrect results if M is row-major)
//		const M &lu() const { return lu_; }	// Warning, only correct for M column-major
//		const ublas::vector<int> &pivot() const { return pivot_; } // Warning, only correct for M column-major

		bool is_singular() const { return info != 0; }

		M inv() const {
			if (lu_.size1() != lu_.size2())
				throw LogicalError(ERROR_INFO("Matrix is not square"));
			if (is_singular()) 				throw NumericalError(ERROR_INFO("Upper matrix is singular"), info);

			M invmat(lu_);
			ublas::vector<double> work;
			detail::inverse_factored(invmat, pivot_, work);
			return invmat;
		}

		double det() const 
		{ 
			if (lu_.size1() != lu_.size2())
				throw LogicalError(ERROR_INFO("Matrix is not square"));
			if (is_singular()) 				return 0.; 
			return detail::determinant_factored(lu_, pivot_);
		}

	private:
		M lu_;
		ublas::vector<int> pivot_;
		const int info;
	};

} // namespace ulapack

#endif

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -