dynare/mex/sources/estimation/libmat/DiscLyapFast.hh

125 lines
3.5 KiB
C++

/*
* Copyright (C) 2010-2017 Dynare Team
*
* This file is part of Dynare.
*
* Dynare is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Dynare is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Dynare. If not, see <http://www.gnu.org/licenses/>.
*/
/****************************************************************
% a wrapper class for function X=disclyap_fast(G,V,ch)
%
% Solve the discrete Lyapunov Equation
% X=G*X*G'+V
% Using the Doubling Algorithm
%
% If ch is defined then the code will check if the resulting X
% is positive definite and generate an error message if it is not
%
% based on work of Joe Pearlman and Alejandro Justiniano
% 3/5/2005
% C++ version 28/07/09 by Dynare team
****************************************************************/
#if !defined(DiscLyapFast_INCLUDE)
#define DiscLyapFast_INCLUDE
#include "dynlapack.h"
#include "Matrix.hh"
#include "BlasBindings.hh"
class DiscLyapFast
{
Matrix A0, A1, Ptmp, P0, P1, I;
public:
class DLPException
{
public:
const int info;
std::string message;
DLPException(int info_arg, std::string message_arg) :
info(info_arg), message(message_arg)
{
};
};
DiscLyapFast(size_t n) :
A0(n), A1(n), Ptmp(n), P0(n), P1(n), I(n)
{
mat::set_identity(I);
};
virtual ~DiscLyapFast()
{
};
template <class MatG, class MatV, class MatX >
void solve_lyap(const MatG &G, const MatV &V, MatX &X, double tol = 1e-16, size_t flag_ch = 0) throw (DLPException);
};
template <class MatG, class MatV, class MatX >
void
DiscLyapFast::solve_lyap(const MatG &G, const MatV &V, MatX &X, double tol, size_t flag_ch) throw (DLPException)
{
P0 = V;
P1 = V;
A0 = G;
const double alpha = 1.0;
const double half = 0.5;
const double omega = 0.0;
bool matd = true;
while (matd) // matrix diff > tol
{
//P1=P0+A0*P0*A0';
// first step Ptmp=P0*A0';
// DGEMM: C := alpha*op( A )*op( B ) + beta*C,
blas::gemm("N", "T", alpha, P0, A0, omega, Ptmp);
// P1=P0+A0*Ptmp;
blas::gemm("N", "N", alpha, A0, Ptmp, alpha, P1);
// A1=A0*A0;
blas::gemm("N", "N", alpha, A0, A0, omega, A1);
// ensure symmetry of P1=(P1+P1')/2;
Ptmp = P1;
blas::gemm("T", "N", half, Ptmp, I, half, P1);
// check if max( max( abs( P1 - P0 ) ) )>tol
matd = mat::isDiffSym(P1, P0, tol);
P0 = P1;
A0 = A1;
} //end while
// ensure symmetry of X=P0=(P0+P0')/2;
blas::gemm("T", "N", half, P1, I, half, P0);
X = P0;
// Check that X is positive definite
if (flag_ch == 1) // calc NormCholesky (P0)
{
lapack_int lpinfo = 0;
lapack_int lrows = P0.getRows();
lapack_int ldl = P0.getLd();
dpotrf("L", &lrows, P0.getData(), &ldl, &lpinfo);
if (lpinfo < 0)
throw DLPException((int) lpinfo, std::string("DiscLyapFast:Internal error in NormCholesky calculator"));
else if (lpinfo > 0)
throw DLPException((int) lpinfo, std::string("DiscLyapFast:The matrix is not positive definite in NormCholesky calculator"));
}
}
#endif //if !defined(DiscLyapFast_INCLUDE)