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

📄 cholesky.hpp

📁 This collection of C++ templates wraps the FORTRAN or C interfaces for LAPACK so that they integrate
💻 HPP
字号:
/* Perform Cholesky decomposition.
 *
 * For positive definite matrices, these functions compute:
 *	- cholesky decomposition (upper or lower triangular as specified)
 *	- inverse
 *	- determinant
 % Each operation comes in an in-place form and a copying form.
 * 
 * Notes:  * (1) The cholesky decomposition is for symmetric matrices, therefore it does not
 * matter whether the matrix is row-major or column major. However, for row-major
 * matrices, the result format (lower/upper triangular) is reversed.
 % (2) The LAPACK function dpotrf_() stores the result in the requested lower/upper
 * triangle, but does not zero-out the opposite triangular part.
 *
 * TODOs:
 *	(1) Implement reciprocal condition estimator: dpocon_
 *	(2) Implement a solver: dpotrs_
 *
 * Tim Bailey 2005.
 */

#ifndef ULAPACK_CHOLESKY_HPP_
#define ULAPACK_CHOLESKY_HPP_

#include "scalar_fill.hpp"
#include "symmetry.hpp"
#include "lapack_exception.hpp"
#include "errormacros.hpp"

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

		extern "C" 		{		// LAPACK function declarations		// Cholesky factorization of a real symmetric positive definite matrix		void dpotrf_( const char *uplo, const int *n, double *a, 
			const int *lda, int *info );

		// Inverse of Cholesky factorised matrix
		void dpotri_( const char *uplo, const int *n, double *a, 
			const int *lda, int *info );
		
		} // extern "C" 

		template <class A>
		char uplo_flag(const ublas::matrix<double, ublas::row_major, A> &m, const bool upper)
		// Compute "reversed" upper/lower flags for row-major matrices
		{
			return (upper) ? 'L' : 'U';
		}

		template <class A>
		char uplo_flag(const ublas::matrix<double, ublas::column_major, A> &m, const bool upper)
		// Compute normal upper/lower flags for column-major matrices
		{
			return (upper) ? 'U' : 'L';
		}

		template<class F, class A>
		int cholesky_basic_checked(ublas::matrix<double,F,A> &m, const bool upper)
		// Perform Cholesky decomposition, but do not zero out other-triangular part.
		// Returns 0 if matrix was actual positive-definite, otherwise it returns a
		// LAPACK info value.
		{
			if (m.size1() != m.size2())
				throw LogicalError(ERROR_INFO("Matrix is not square"));
			assert(is_symmetric(m)); 

			// Call LAPACK routine
			int info;
			char uplo = detail::uplo_flag(m, upper);
			int size = static_cast<int>(m.size1());
			detail::dpotrf_( &uplo, &size, &m.data()[0], &size, &info );
			// Check validity of result			if (info < 0) 				throw LogicalError(ERROR_INFO("Invalid argument"), info);			return info;		}

		template<class F, class A>
		void cholesky_basic(ublas::matrix<double,F,A> &m, const bool upper)
		// Perform Cholesky decomposition, but do not zero out other-triangular part.
		{
			int info = cholesky_basic_checked(m, upper);
			if (info > 0) 				throw NumericalError(ERROR_INFO("Matrix is not positive definite"), info);		}

		template<class F, class A>
		void zero_strict_triangular(ublas::matrix<double,F,A> &m, const bool upper)
		// Zero out strict-other triangular part of matrix.
		{
			if (upper) 
				scalar_fill<ublas::strict_lower>(m, 0.);
			else 
				scalar_fill<ublas::strict_upper>(m, 0.);
		}

	} // namespace detail

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

	template<class F, class A>
	bool chol_checked_inplace(ublas::matrix<double,F,A> &m, const bool upper=true)
	// Compute Cholesky decomposition (in-place).
	// Returns false if matrix is not positive-definite.
	{
		if (detail::cholesky_basic_checked(m, upper))
			return false;
		detail::zero_strict_triangular(m, upper);
		return true;
	}

	template<class F, class A>
	void chol_inplace(ublas::matrix<double,F,A> &m, const bool upper=true)
	// Compute Cholesky decomposition (in-place).
	{
		detail::cholesky_basic(m, upper);
		detail::zero_strict_triangular(m, upper);
	}

	template<class F, class A>
	void inv_chol_inplace(ublas::matrix<double,F,A> &m, const bool upper=true)
	// Compute inverse (in-place) of a pos. def. matrix that has previously been 
	// Cholesky decomposed (ie, "m" is already a Cholesky matrix). 
	// WARNINGS: 
	//	(1) The value of "upper" must match the actual form of "m". This function does not check.
	//	(2) This function does *not* return the inverse of a Cholesky matrix.
	{
		// Call LAPACK routine
		int info;
		char uplo = detail::uplo_flag(m, upper);
		int size = static_cast<int>(m.size1());
		detail::dpotri_( &uplo, &size, m.data().begin(), &size, &info );
		// Check validity of result		if (info < 0) 			throw LogicalError(ERROR_INFO("Invalid argument"), info);		else if (info > 0) 			throw NumericalError(ERROR_INFO("Inverse does not exist"), info);
		// Copy result to other triangular part (ie, make a symmetric inverse matrix)
		force_symmetry(m, upper);
	}

	template<class F, class A>
	void inv_pd_inplace(ublas::matrix<double,F,A> &m)
	// Compute inverse of a positive definite matrix (in-place).
	{
		detail::cholesky_basic(m, true);
		inv_chol_inplace(m, true);
	}

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

	template<class F, class A>
	ublas::matrix<double,F,A> 
	chol(const ublas::matrix<double,F,A> &m, const bool upper=true)
	// Compute Cholesky decomposition, and return result.
	{
		ublas::matrix<double,F,A> c(m);
		chol_inplace(c, upper);
		return c;
	}

	template<class F, class A>
	ublas::matrix<double,F,A> inv_pd(const ublas::matrix<double,F,A> &m)
	// Compute inverse of a positive definite matrix, and return the result.
	{
		ublas::matrix<double,F,A> inv(m);
		inv_pd_inplace(inv);
		return inv;
	}

	template<class F, class A>
	double det_chol(const ublas::matrix<double,F,A> &m)
	// Compute determinant of Cholesky matrix. 
	{
		assert(m.size1() == m.size2());
		double d = 1.;
		for (size_t i=0; i < m.size1(); ++i)
			d *= m(i,i);
		return d;
	}

	template<class F, class A>
	double det_pd_sqrt(const ublas::matrix<double,F,A> &m)
	// Compute sqrt of determinant of pos. def. matrix. 
	{
		ublas::matrix<double,F,A> c(m);
		detail::cholesky_basic(c, true);
		double dsqrt = det_chol(c);
		assert(dsqrt > 0.);
		return dsqrt;
	}

	template<class F, class A>
	double det_pd(const ublas::matrix<double,F,A> &m)
	// Compute determinant of pos. def. matrix.
	{
		double d = det_pd_sqrt(m);
		return d*d;
	}

	// Wrap everything up as a class, templatised by M - the matrix type.
	template<class M>
	class Cholesky {
	public:
		Cholesky(const M &m, const bool upper=true) :
		  cholmat(m), first(true), is_upper(upper), 
			  info(detail::cholesky_basic_checked(cholmat, is_upper))
		  { }

		bool is_posdef() const { return info == 0; }

		const M& chol() const {
			check_posdef();
			if (first) { 
				first = false;
				detail::zero_strict_triangular(cholmat, is_upper);
			}
			return cholmat;
		}

		M inv() const {
			check_posdef();
			M invmat(cholmat);
			inv_chol_inplace(invmat, is_upper);
			return invmat;
		}

		double det_sqrt() const {
			check_posdef();
			return det_chol(cholmat);
		}

		double det() const {
			double d = det_sqrt();
			return d*d;
		}

	private:
		void check_posdef() const {
			if (info != 0)
				throw NumericalError(ERROR_INFO("Matrix is not positive definite"), info);
		}

		mutable M cholmat;
		mutable bool first;
		const bool is_upper;
		const int info;
	}; 

} // namespace ulapack

#endif

⌨️ 快捷键说明

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