/* * 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!"); } }