diff --git a/edo/src/edoSamplerNormalMulti.h b/edo/src/edoSamplerNormalMulti.h index e2ce880c..6099ed95 100644 --- a/edo/src/edoSamplerNormalMulti.h +++ b/edo/src/edoSamplerNormalMulti.h @@ -70,6 +70,8 @@ public: standard, //! use the algorithm using absolute value within the square root @see factorize_LLT_abs absolute, + //! use the method that set negative square roots to zero @see factorize_LLT_zero + zeroing, //! use the robust algorithm, without square root @see factorize_LDLT robust }; @@ -157,6 +159,8 @@ public: factorize_LLT( V ); } else if( _use == absolute ) { factorize_LLT_abs( V ); + } else if( _use == zeroing ) { + factorize_LLT_zero( V ); } else if( _use == robust ) { factorize_LDLT( V ); } @@ -254,6 +258,54 @@ public: } + /** This standard algorithm makes use of square root but do not fail + * if the covariance matrix is very ill-conditioned. + * Here, if the diagonal difference ir negative, we set it to zero. + * + * Be aware that this increase round-off errors, this is just a ugly + * hack to avoid crash. + */ + void factorize_LLT_zero( const CovarMat & V) + { + unsigned int N = assert_properties( V ); + + unsigned int i=0, j=0, k; + + _L(0, 0) = sqrt( V(0, 0) ); + + // end of the column + for ( j = 1; j < N; ++j ) { + _L(j, 0) = V(0, j) / _L(0, 0); + } + + // end of the matrix + for ( i = 1; i < N; ++i ) { // each column + // diagonal + double sum = 0.0; + for ( k = 0; k < i; ++k) { + sum += _L(i, k) * _L(i, k); + } + + if( V(i,i) - sum >= 0 ) { + _L(i,i) = sqrt( V(i,i) - sum); + } else { + _L(i,i) = 0; + } + + for ( j = i + 1; j < N; ++j ) { // rows + // one element + sum = 0.0; + for ( k = 0; k < i; ++k ) { + sum += _L(j, k) * _L(i, k); + } + + _L(j, i) = (V(j, i) - sum) / _L(i, i); + + } // for j in ]i,N[ + } // for i in [1,N[ + } + + /** This alternative algorithm do not use square root in an inner loop, * but only for some diagonal elements of the matrix D. * diff --git a/edo/test/t-cholesky.cpp b/edo/test/t-cholesky.cpp index 06a93aee..b141e6e4 100644 --- a/edo/test/t-cholesky.cpp +++ b/edo/test/t-cholesky.cpp @@ -158,9 +158,10 @@ int main(int argc, char** argv) edoSamplerNormalMulti::Cholesky LLT( edoSamplerNormalMulti::Cholesky::standard ); edoSamplerNormalMulti::Cholesky LLTa( edoSamplerNormalMulti::Cholesky::absolute ); + edoSamplerNormalMulti::Cholesky LLTz( edoSamplerNormalMulti::Cholesky::zeroing ); edoSamplerNormalMulti::Cholesky LDLT( edoSamplerNormalMulti::Cholesky::robust ); - std::vector s0,s1,s2; + std::vector s0,s1,s2,s3; for( unsigned int n=0; n= 4 ) {