BUGFIX: factorized matrix are not symetric, cholesky factorization should process different types for covariance and decomposition + better format output for cholesky test
This commit is contained in:
parent
e0f691c148
commit
fe2cebc0e8
2 changed files with 110 additions and 54 deletions
|
|
@ -49,7 +49,7 @@ public:
|
||||||
|
|
||||||
|
|
||||||
/** Cholesky decomposition, given a matrix V, return a matrix L
|
/** Cholesky decomposition, given a matrix V, return a matrix L
|
||||||
* such as V = L Lt (Lt being the conjugate transpose of L).
|
* such as V = L L^T (L^T being the transposed of L).
|
||||||
*
|
*
|
||||||
* Need a symmetric and positive definite matrix as an input, which
|
* Need a symmetric and positive definite matrix as an input, which
|
||||||
* should be the case of a non-ill-conditionned covariance matrix.
|
* should be the case of a non-ill-conditionned covariance matrix.
|
||||||
|
|
@ -58,7 +58,8 @@ public:
|
||||||
class Cholesky
|
class Cholesky
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef ublas::symmetric_matrix< AtomType, ublas::lower > MatrixType;
|
typedef ublas::symmetric_matrix< AtomType, ublas::lower > CovarMat;
|
||||||
|
typedef ublas::matrix< AtomType > FactorMat;
|
||||||
|
|
||||||
enum Method {
|
enum Method {
|
||||||
//! use the standard algorithm, with square root @see factorize_LLT
|
//! use the standard algorithm, with square root @see factorize_LLT
|
||||||
|
|
@ -82,7 +83,8 @@ public:
|
||||||
*
|
*
|
||||||
* Use the standard unstable method by default.
|
* Use the standard unstable method by default.
|
||||||
*/
|
*/
|
||||||
Cholesky(const MatrixType& V, Cholesky::Method use = standard ) : _use(use)
|
Cholesky(const CovarMat& V, Cholesky::Method use = standard ) :
|
||||||
|
_use(use), _L(ublas::zero_matrix<AtomType>(V.size1(),V.size2()))
|
||||||
{
|
{
|
||||||
factorize( V );
|
factorize( V );
|
||||||
}
|
}
|
||||||
|
|
@ -90,14 +92,14 @@ public:
|
||||||
|
|
||||||
/** Compute the factorization and return the result
|
/** Compute the factorization and return the result
|
||||||
*/
|
*/
|
||||||
const MatrixType& operator()( const MatrixType& V )
|
const FactorMat& operator()( const CovarMat& V )
|
||||||
{
|
{
|
||||||
factorize( V );
|
factorize( V );
|
||||||
return decomposition();
|
return decomposition();
|
||||||
}
|
}
|
||||||
|
|
||||||
//! The decomposition of the covariance matrix
|
//! The decomposition of the covariance matrix
|
||||||
const MatrixType & decomposition() const
|
const FactorMat & decomposition() const
|
||||||
{
|
{
|
||||||
return _L;
|
return _L;
|
||||||
}
|
}
|
||||||
|
|
@ -105,18 +107,18 @@ public:
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
//! The decomposition is a (lower) symetric matrix, just like the covariance matrix
|
//! The decomposition is a (lower) symetric matrix, just like the covariance matrix
|
||||||
MatrixType _L;
|
FactorMat _L;
|
||||||
|
|
||||||
/** Assert that the covariance matrix have the required properties and returns its dimension.
|
/** Assert that the covariance matrix have the required properties and returns its dimension.
|
||||||
*
|
*
|
||||||
* Note: if compiled with NDEBUG, will not assert anything and just return the dimension.
|
* Note: if compiled with NDEBUG, will not assert anything and just return the dimension.
|
||||||
*/
|
*/
|
||||||
unsigned assert_properties( const MatrixType& V )
|
unsigned assert_properties( const CovarMat& V )
|
||||||
{
|
{
|
||||||
unsigned int Vl = V.size1(); // number of lines
|
unsigned int Vl = V.size1(); // number of lines
|
||||||
|
|
||||||
// the result goes in _L
|
// the result goes in _L
|
||||||
_L.resize(Vl);
|
_L = ublas::zero_matrix<AtomType>(Vl,Vl);
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
assert(Vl > 0);
|
assert(Vl > 0);
|
||||||
|
|
@ -135,7 +137,6 @@ public:
|
||||||
* perform the cholesky factorization
|
* perform the cholesky factorization
|
||||||
* check if all eigenvalues are positives
|
* check if all eigenvalues are positives
|
||||||
* check if all of the leading principal minors are positive
|
* check if all of the leading principal minors are positive
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -146,7 +147,7 @@ public:
|
||||||
/** Actually performs the factorization with the method given at
|
/** Actually performs the factorization with the method given at
|
||||||
* instanciation. Results are cached in _L.
|
* instanciation. Results are cached in _L.
|
||||||
*/
|
*/
|
||||||
void factorize( const MatrixType& V )
|
void factorize( const CovarMat& V )
|
||||||
{
|
{
|
||||||
if( _use == standard ) {
|
if( _use == standard ) {
|
||||||
factorize_LLT( V );
|
factorize_LLT( V );
|
||||||
|
|
@ -161,10 +162,12 @@ public:
|
||||||
/** This standard algorithm makes use of square root and is thus subject
|
/** This standard algorithm makes use of square root and is thus subject
|
||||||
* to round-off errors if the covariance matrix is very ill-conditioned.
|
* to round-off errors if the covariance matrix is very ill-conditioned.
|
||||||
*
|
*
|
||||||
|
* Compute L such that V = L L^T
|
||||||
|
*
|
||||||
* When compiled in debug mode and called on ill-conditionned matrix,
|
* When compiled in debug mode and called on ill-conditionned matrix,
|
||||||
* will raise an assert before calling the square root on a negative number.
|
* will raise an assert before calling the square root on a negative number.
|
||||||
*/
|
*/
|
||||||
void factorize_LLT( const MatrixType& V)
|
void factorize_LLT( const CovarMat& V)
|
||||||
{
|
{
|
||||||
unsigned int N = assert_properties( V );
|
unsigned int N = assert_properties( V );
|
||||||
|
|
||||||
|
|
@ -210,7 +213,7 @@ public:
|
||||||
* Be aware that this increase round-off errors, this is just a ugly
|
* Be aware that this increase round-off errors, this is just a ugly
|
||||||
* hack to avoid crash.
|
* hack to avoid crash.
|
||||||
*/
|
*/
|
||||||
void factorize_LLT_abs( const MatrixType & V)
|
void factorize_LLT_abs( const CovarMat & V)
|
||||||
{
|
{
|
||||||
unsigned int N = assert_properties( V );
|
unsigned int N = assert_properties( V );
|
||||||
|
|
||||||
|
|
@ -247,19 +250,21 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** This alternative algorithm do not use square root.
|
/** This alternative algorithm do not use square root in an inner loop,
|
||||||
|
* but only for some diagonal elements of the matrix D.
|
||||||
*
|
*
|
||||||
* Computes L and D such as V = L D Lt
|
* Computes L and D such as V = L D L^T.
|
||||||
|
* The factorized matrix is (L D^1/2), because V = (L D^1/2) (L D^1/2)^T
|
||||||
*/
|
*/
|
||||||
void factorize_LDLT( const MatrixType& V)
|
void factorize_LDLT( const CovarMat& V)
|
||||||
{
|
{
|
||||||
// use "int" everywhere, because of the "j-1" operation
|
// use "int" everywhere, because of the "j-1" operation
|
||||||
int N = assert_properties( V );
|
int N = assert_properties( V );
|
||||||
// example of an invertible matrix whose decomposition is undefined
|
// example of an invertible matrix whose decomposition is undefined
|
||||||
assert( V(0,0) != 0 );
|
assert( V(0,0) != 0 );
|
||||||
|
|
||||||
MatrixType L(N,N);
|
FactorMat L = ublas::zero_matrix<AtomType>(N,N);
|
||||||
MatrixType D = ublas::zero_matrix<AtomType>(N,N);
|
FactorMat D = ublas::zero_matrix<AtomType>(N,N);
|
||||||
D(0,0) = V(0,0);
|
D(0,0) = V(0,0);
|
||||||
|
|
||||||
for( int j=0; j<N; ++j ) { // each columns
|
for( int j=0; j<N; ++j ) { // each columns
|
||||||
|
|
@ -281,12 +286,12 @@ public:
|
||||||
} // for i in rows
|
} // for i in rows
|
||||||
} // for j in columns
|
} // for j in columns
|
||||||
|
|
||||||
// now compute the final symetric matrix: from _LD_LT to _L_LT
|
// now compute the final symetric matrix: _L = L D^1/2
|
||||||
// remember that V = (_LD^1/2)(_LD^1/2)^T
|
// remember that V = ( L D^1/2) ( L D^1/2)^T
|
||||||
|
|
||||||
// square root of a diagonal matrix is the square root of all its
|
// fortunately, the square root of a diagonal matrix is the square
|
||||||
// scalars
|
// root of all its elements
|
||||||
MatrixType D12 = D;
|
FactorMat D12 = D;
|
||||||
for(int i=0; i<N; ++i) {
|
for(int i=0; i<N; ++i) {
|
||||||
D12(i,i) = sqrt(D(i,i));
|
D12(i,i) = sqrt(D(i,i));
|
||||||
}
|
}
|
||||||
|
|
@ -295,7 +300,6 @@ public:
|
||||||
_L = ublas::prod( L, D12);
|
_L = ublas::prod( L, D12);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}; // class Cholesky
|
}; // class Cholesky
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -309,14 +313,10 @@ public:
|
||||||
unsigned int size = distrib.size();
|
unsigned int size = distrib.size();
|
||||||
assert(size > 0);
|
assert(size > 0);
|
||||||
|
|
||||||
// Cholesky factorisation gererating matrix L from covariance
|
|
||||||
// matrix V.
|
|
||||||
// We must use cholesky.decomposition() to get the resulting matrix.
|
|
||||||
//
|
|
||||||
// L = cholesky decomposition of varcovar
|
// L = cholesky decomposition of varcovar
|
||||||
const typename Cholesky::MatrixType& L = _cholesky( distrib.varcovar() );
|
const typename Cholesky::FactorMat& L = _cholesky( distrib.varcovar() );
|
||||||
|
|
||||||
// T = vector of size elements drawn in N(0,1) rng.normal(1.0)
|
// T = vector of size elements drawn in N(0,1)
|
||||||
ublas::vector< AtomType > T( size );
|
ublas::vector< AtomType > T( size );
|
||||||
for ( unsigned int i = 0; i < size; ++i ) {
|
for ( unsigned int i = 0; i < size; ++i ) {
|
||||||
T( i ) = rng.normal();
|
T( i ) = rng.normal();
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,9 @@ Authors:
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <limits>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
#include <eo>
|
#include <eo>
|
||||||
#include <es.h>
|
#include <es.h>
|
||||||
|
|
@ -36,26 +39,73 @@ Authors:
|
||||||
typedef eoReal< eoMinimizingFitness > EOT;
|
typedef eoReal< eoMinimizingFitness > EOT;
|
||||||
typedef edoNormalMulti<EOT> EOD;
|
typedef edoNormalMulti<EOT> EOD;
|
||||||
|
|
||||||
std::ostream& operator<< (std::ostream& out, const ublas::symmetric_matrix< double, ublas::lower >& mat )
|
|
||||||
|
void setformat( std::ostream& out )
|
||||||
{
|
{
|
||||||
|
out << std::right;
|
||||||
|
out << std::setfill(' ');
|
||||||
|
out << std::setw( 5 + std::numeric_limits<double>::digits10);
|
||||||
|
out << std::setprecision(std::numeric_limits<double>::digits10);
|
||||||
|
out << std::setiosflags(std::ios_base::showpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename MT>
|
||||||
|
std::string format(const MT& mat )
|
||||||
|
{
|
||||||
|
std::ostringstream out;
|
||||||
|
setformat(out);
|
||||||
|
|
||||||
for( unsigned int i=0; i<mat.size1(); ++i) {
|
for( unsigned int i=0; i<mat.size1(); ++i) {
|
||||||
for( unsigned int j=0; j<=i; ++j) {
|
for( unsigned int j=0; j<mat.size2(); ++j) {
|
||||||
out << mat(i,j) << "\t";
|
out << mat(i,j) << "\t";
|
||||||
} // columns
|
} // columns
|
||||||
out << std::endl;
|
out << std::endl;
|
||||||
} // rows
|
} // rows
|
||||||
|
|
||||||
return out;
|
return out.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template< typename T >
|
||||||
|
T round( T val, T prec = 1.0 )
|
||||||
|
{
|
||||||
|
return (val > 0.0) ?
|
||||||
|
floor(val * prec + 0.5) / prec :
|
||||||
|
ceil(val * prec - 0.5) / prec ;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename MT>
|
||||||
|
bool equal( const MT& M1, const MT& M2, double prec /* = 1/std::numeric_limits<double>::digits10 ???*/ )
|
||||||
|
{
|
||||||
|
if( M1.size1() != M2.size1() || M1.size2() != M2.size2() ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for( unsigned int i=0; i<M1.size1(); ++i ) {
|
||||||
|
for( unsigned int j=0; j<M1.size2(); ++j ) {
|
||||||
|
if( round(M1(i,j),prec) != round(M2(i,j),prec) ) {
|
||||||
|
std::cout << "round(M(" << i << "," << j << "," << prec << ") == "
|
||||||
|
<< round(M1(i,j),prec) << " != " << round(M2(i,j),prec) << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
unsigned int N = 4;
|
unsigned int N = 4;
|
||||||
|
|
||||||
typedef edoSamplerNormalMulti<EOT,EOD>::Cholesky::MatrixType MatrixType;
|
typedef edoSamplerNormalMulti<EOT,EOD>::Cholesky::CovarMat CovarMat;
|
||||||
|
typedef edoSamplerNormalMulti<EOT,EOD>::Cholesky::FactorMat FactorMat;
|
||||||
|
|
||||||
// a variance-covariance matrix of size N*N
|
// a variance-covariance matrix of size N*N
|
||||||
MatrixType V(N,N);
|
CovarMat V(N,N);
|
||||||
|
|
||||||
// random covariance matrix
|
// random covariance matrix
|
||||||
for( unsigned int i=0; i<N; ++i) {
|
for( unsigned int i=0; i<N; ++i) {
|
||||||
|
|
@ -65,29 +115,35 @@ int main(int argc, char** argv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "Covariance matrix" << std::endl << V << std::endl;
|
double precision = 1e-15;
|
||||||
std::cout << "-----------------------------------------------------------" << std::endl;
|
setformat(std::cout);
|
||||||
|
std::string linesep = "--------------------------------------------------------------------------------------------";
|
||||||
|
|
||||||
|
std::cout << "Covariance matrix" << std::endl << format(V) << std::endl;
|
||||||
|
std::cout << linesep << std::endl;
|
||||||
|
|
||||||
edoSamplerNormalMulti<EOT,EOD>::Cholesky LLT( edoSamplerNormalMulti<EOT,EOD>::Cholesky::standard );
|
edoSamplerNormalMulti<EOT,EOD>::Cholesky LLT( edoSamplerNormalMulti<EOT,EOD>::Cholesky::standard );
|
||||||
|
FactorMat L0 = LLT(V);
|
||||||
|
std::cout << "LLT" << std::endl << format(L0) << std::endl;
|
||||||
|
CovarMat V0 = ublas::prod( L0, ublas::trans(L0) );
|
||||||
|
std::cout << "LLT covar" << std::endl << format(V0) << std::endl;
|
||||||
|
assert( equal(V0,V,precision) );
|
||||||
|
std::cout << linesep << std::endl;
|
||||||
|
|
||||||
edoSamplerNormalMulti<EOT,EOD>::Cholesky LLTa( edoSamplerNormalMulti<EOT,EOD>::Cholesky::absolute );
|
edoSamplerNormalMulti<EOT,EOD>::Cholesky LLTa( edoSamplerNormalMulti<EOT,EOD>::Cholesky::absolute );
|
||||||
|
FactorMat L1 = LLTa(V);
|
||||||
|
std::cout << "LLT abs" << std::endl << format(L1) << std::endl;
|
||||||
|
CovarMat V1 = ublas::prod( L1, ublas::trans(L1) );
|
||||||
|
std::cout << "LLT covar" << std::endl << format(V1) << std::endl;
|
||||||
|
assert( equal(V1,V,precision) );
|
||||||
|
std::cout << linesep << std::endl;
|
||||||
|
|
||||||
edoSamplerNormalMulti<EOT,EOD>::Cholesky LDLT( edoSamplerNormalMulti<EOT,EOD>::Cholesky::robust );
|
edoSamplerNormalMulti<EOT,EOD>::Cholesky LDLT( edoSamplerNormalMulti<EOT,EOD>::Cholesky::robust );
|
||||||
|
FactorMat L2 = LDLT(V);
|
||||||
MatrixType L0 = LLT(V);
|
std::cout << "LDLT" << std::endl << format(L2) << std::endl;
|
||||||
std::cout << "LLT" << std::endl << L0 << std::endl;
|
CovarMat V2 = ublas::prod( L2, ublas::trans(L2) );
|
||||||
MatrixType V0 = ublas::prod( L0, ublas::trans(L0) );
|
std::cout << "LDLT covar" << std::endl << format(V2) << std::endl;
|
||||||
std::cout << "LLT covar" << std::endl << V0 << std::endl;
|
assert( equal(V2,V,precision) );
|
||||||
std::cout << "-----------------------------------------------------------" << std::endl;
|
std::cout << linesep << std::endl;
|
||||||
|
|
||||||
MatrixType L1 = LLTa(V);
|
|
||||||
std::cout << "LLT abs" << std::endl << L1 << std::endl;
|
|
||||||
MatrixType V1 = ublas::prod( L1, ublas::trans(L1) );
|
|
||||||
std::cout << "LLT covar" << std::endl << V1 << std::endl;
|
|
||||||
std::cout << "-----------------------------------------------------------" << std::endl;
|
|
||||||
|
|
||||||
MatrixType L2 = LDLT(V);
|
|
||||||
std::cout << "LDLT: L" << std::endl << L2 << std::endl;
|
|
||||||
MatrixType V2 = ublas::prod( L2, ublas::trans(L2) );
|
|
||||||
std::cout << "LDLT covar" << std::endl << V2 << std::endl;
|
|
||||||
std::cout << "-----------------------------------------------------------" << std::endl;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Reference in a new issue