/* * Copyright © 2009-2020 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 . * * This mex file calls fortran routines from the Slicot library. */ /* ++ INPUTS ++ ====== ++ ++ ++ [0] T (double) n×n transition matrix. ++ ++ [1] QQ (double) n×n matrix (=R·Q·Rᵀ, where Q is the covariance matrix of the structural innovations). ++ ++ [2] Z (double) n×p selection matrix. ++ ++ [3] H (double) p×p covariance matrix of the measurement errors. ++ ++ ++ ++ ++ OUTPUTS ++ ======= ++ ++ ++ [0] P (double) n×n covariance matrix of the state vector. ++ ++ ++ NOTES ++ ===== ++ ++ [1] T = transpose(dynare transition matrix) and Z = transpose(dynare selection matrix). */ #include #include #include #include #define sb02od FORTRAN_WRAPPER(sb02od) extern "C" { /* Note: matrices q, r and l may be modified internally (though they are restored on exit), hence their pointers are not declared as const */ int sb02od(const char* dico, const char* jobb, const char* fact, const char* uplo, const char* jobl, const char* sort, const lapack_int* n, const lapack_int* m, const lapack_int* p, const double* a, const lapack_int* lda, const double* b, const lapack_int* ldb, double* q, const lapack_int* ldq, double* r, const lapack_int* ldr, double* l, const lapack_int* ldl, double* rcond, double* x, const lapack_int* ldx, double* alfar, double* alfai, double* beta, double* s, const lapack_int* lds, double* t, const lapack_int* ldt, double* u, const lapack_int* ldu, const double* tol, lapack_int* iwork, double* dwork, const lapack_int* ldwork, lapack_int* bwork, lapack_int* info); } void mexFunction(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // Check the number of arguments and set some flags. bool measurement_error_flag = true; if (nrhs < 3 || 4 < nrhs) mexErrMsgTxt("kalman_steady_state accepts either 3 or 4 input arguments!"); if (nlhs != 1) mexErrMsgTxt("kalman_steady_state accepts exactly one output argument!"); if (nrhs == 3) measurement_error_flag = false; // Check the type of the input arguments and get the size of the matrices. lapack_int n = mxGetM(prhs[0]); if (static_cast(n) != mxGetN(prhs[0])) mexErrMsgTxt("kalman_steady_state: The first input argument (T) must be a square matrix!"); if (mxIsNumeric(prhs[0]) == 0 || mxIsComplex(prhs[0]) == 1) mexErrMsgTxt("kalman_steady_state: The first input argument (T) must be a real matrix!"); lapack_int q = mxGetM(prhs[1]); if (static_cast(q) != mxGetN(prhs[1])) mexErrMsgTxt("kalman_steady_state: The second input argument (QQ) must be a square matrix!"); if (mxIsNumeric(prhs[1]) == 0 || mxIsComplex(prhs[1]) == 1) mexErrMsgTxt("kalman_steady_state: The second input argument (QQ) must be a real matrix!"); if (q != n) mexErrMsgTxt("kalman_steady_state: The size of the second input argument (QQ) must match the " "size of the first argument (T)!"); lapack_int p = mxGetN(prhs[2]); if (mxGetM(prhs[2]) != static_cast(n)) mexErrMsgTxt("kalman_steady_state: The number of rows of the third argument (Z) must match the " "number of rows of the first argument (T)!"); if (mxIsNumeric(prhs[2]) == 0 || mxIsComplex(prhs[2]) == 1) mexErrMsgTxt("kalman_steady_state: The third input argument (Z) must be a real matrix!"); if (measurement_error_flag) { if (mxGetM(prhs[3]) != mxGetN(prhs[3])) mexErrMsgTxt("kalman_steady_state: The fourth input argument (H) must be a square matrix!"); if (mxGetM(prhs[3]) != static_cast(p)) mexErrMsgTxt("kalman_steady_state: The number of rows of the fourth input argument (H) " "must match the number of rows of the third input argument!"); if (mxIsNumeric(prhs[3]) == 0 || mxIsComplex(prhs[3]) == 1) mexErrMsgTxt("kalman_steady_state: The fifth input argument (H) must be a real matrix!"); } // Get input matrices. const double* T = mxGetPr(prhs[0]); auto QQ = std::make_unique(n * n); std::copy_n(mxGetPr(prhs[1]), n * n, QQ.get()); const double* Z = mxGetPr(prhs[2]); auto H = std::make_unique(p * p); if (measurement_error_flag) std::copy_n(mxGetPr(prhs[3]), p * p, H.get()); // L will not be used. auto L = std::make_unique(n * p); lapack_int nn = 2 * n; lapack_int LDA = std::max(static_cast(1), n); lapack_int LDQ = LDA; lapack_int LDU = std::max(static_cast(1), nn); lapack_int LDS = std::max(static_cast(1), nn + p); lapack_int LIWORK = std::max(static_cast(1), std::max(p, nn)); lapack_int LDR = std::max(static_cast(1), p); lapack_int LDB = LDA, LDL = LDA, LDT = LDS, LDX = LDA; lapack_int LDWORK = std::max( static_cast(7) * (static_cast(2) * n + static_cast(1)) + static_cast(16), static_cast(16) * n); LDWORK = std::max(LDWORK, std::max(static_cast(2) * n + p, static_cast(3) * p)); double tolerance = 1e-16; lapack_int INFO; // Outputs of subroutine sb02OD double rcond; auto WR = std::make_unique(nn); auto WI = std::make_unique(nn); auto BETA = std::make_unique(nn); auto S = std::make_unique(LDS * (nn + p)); auto TT = std::make_unique(LDT * nn); auto UU = std::make_unique(LDU * nn); // Working arrays auto IWORK = std::make_unique(LIWORK); auto DWORK = std::make_unique(LDWORK); auto BWORK = std::make_unique(nn); // Initialize the output of the mex file plhs[0] = mxCreateDoubleMatrix(n, n, mxREAL); double* P = mxGetPr(plhs[0]); // Call the slicot routine sb02od("D", // We want to solve a discrete Riccati equation. "B", // Matrices Z and H are given. "N", // Given matrices H and QQ are not factored. "U", // Upper triangle of matrix H is stored. "Z", // L matrix is zero. "S", // Stable eigenvalues come first. &n, &p, &p, T, &LDA, Z, &LDB, QQ.get(), &LDQ, H.get(), &LDR, L.get(), &LDL, &rcond, P, &LDX, WR.get(), WI.get(), BETA.get(), S.get(), &LDS, TT.get(), &LDT, UU.get(), &LDU, &tolerance, IWORK.get(), DWORK.get(), &LDWORK, BWORK.get(), &INFO); switch (INFO) { case 0: break; case 1: mexErrMsgTxt( "The computed extended matrix pencil is singular, possibly due to rounding errors."); break; case 2: mexErrMsgTxt("The QZ (or QR) algorithm failed!"); break; case 3: mexErrMsgTxt("The reordering of the (generalized) eigenvalues failed!"); break; case 4: mexErrMsgTxt("After reordering, roundoff changed values of some complex eigenvalues so that " "leading eigenvalues\n in the (generalized) Schur form no longer satisfy the " "stability condition; this could also be caused due to scaling."); break; case 5: mexErrMsgTxt("The computed dimension of the solution does not equal n!"); break; case 6: mexErrMsgTxt( "A singular matrix was encountered during the computation of the solution matrix P!"); break; default: mexErrMsgTxt("Unknown problem!"); } }