k_order_perturbation MEX: improve input handling and sanitization

time-shift
Sébastien Villemot 2019-07-09 17:12:23 +02:00
parent f8af21819e
commit aaaa897a2a
No known key found for this signature in database
GPG Key ID: 2CECE9350ECEBE4A
3 changed files with 98 additions and 81 deletions

View File

@ -28,10 +28,10 @@
KordpDynare::KordpDynare(const std::vector<std::string> &endo,
const std::vector<std::string> &exo, int nexog, int npar,
Vector &ysteady, TwoDMatrix &vcov, Vector &inParams, int nstat,
int npred, int nforw, int nboth, const Vector &nnzd,
int npred, int nforw, int nboth, const ConstVector &nnzd,
int nsteps, int norder,
Journal &jr, std::unique_ptr<DynamicModelAC> dynamicModelFile_arg,
const std::vector<int> &dr_order, const TwoDMatrix &llincidence) :
const std::vector<int> &dr_order, const ConstTwoDMatrix &llincidence) :
nStat{nstat}, nBoth{nboth}, nPred{npred}, nForw{nforw}, nExog{nexog}, nPar{npar},
nYs{npred + nboth}, nYss{nboth + nforw}, nY{nstat + npred + nboth + nforw},
nJcols{nExog+nY+nYs+nYss}, NNZD{nnzd}, nSteps{nsteps},

View File

@ -86,8 +86,8 @@ public:
const int nYss; // = nboth + nforw
const int nY; // = nstat + npred + nboth + nforw
const int nJcols; // nb of jacobian columns = nExog+nY+nYs+nYss
const Vector &NNZD; /* the total number of non-zero derivative elements
where hessian is 2nd : NNZD(order=2) */
const ConstVector &NNZD; /* the total number of non-zero derivative elements
where hessian is 2nd : NNZD(order=2) */
const int nSteps;
const int nOrder;
private:
@ -99,7 +99,7 @@ private:
TensorContainer<FSSparseTensor> md; // Model derivatives, in Dynare++ form
DynareNameList dnl, denl;
DynareStateNameList dsnl;
const TwoDMatrix &ll_Incidence;
const ConstTwoDMatrix &ll_Incidence;
std::vector<int> dynppToDyn; // Maps Dynare++ jacobian variable indices to Dynare ones
std::vector<int> dynToDynpp; // Maps Dynare jacobian variable indices to Dynare++ ones
@ -108,10 +108,10 @@ public:
KordpDynare(const std::vector<std::string> &endo,
const std::vector<std::string> &exo, int num_exo, int num_par,
Vector &ySteady, TwoDMatrix &vCov, Vector &params, int nstat, int nPred,
int nforw, int nboth, const Vector &NNZD,
int nforw, int nboth, const ConstVector &NNZD,
int nSteps, int ord,
Journal &jr, std::unique_ptr<DynamicModelAC> dynamicModelFile_arg,
const std::vector<int> &varOrder, const TwoDMatrix &ll_Incidence);
const std::vector<int> &varOrder, const ConstTwoDMatrix &ll_Incidence);
int
nstat() const override

View File

@ -54,7 +54,12 @@ DynareMxArrayToString(const mxArray *mxFldp)
assert(mxIsCell(mxFldp));
std::vector<std::string> r;
for (size_t i = 0; i < mxGetNumberOfElements(mxFldp); i++)
r.emplace_back(mxArrayToString(mxGetCell(mxFldp, i)));
{
const mxArray *cell_mx = mxGetCell(mxFldp, i);
if (!(cell_mx && mxIsChar(cell_mx)))
mexErrMsgTxt("Cell is not a character array");
r.emplace_back(mxArrayToString(cell_mx));
}
return r;
}
@ -77,108 +82,120 @@ extern "C" {
mexFunction(int nlhs, mxArray *plhs[],
int nrhs, const mxArray *prhs[])
{
if (nrhs < 3 || nlhs < 2)
DYN_MEX_FUNC_ERR_MSG_TXT("Must have at least 3 input parameters and takes at least 2 output parameters.");
if (nrhs < 3 || nlhs < 2 || nlhs > 3)
DYN_MEX_FUNC_ERR_MSG_TXT("Must have at least 3 input parameters and takes 2 or 3 output parameters.");
const mxArray *dr = prhs[0];
const mxArray *M_ = prhs[1];
const mxArray *options_ = prhs[2];
bool use_dll = mxGetScalar(mxGetField(options_, 0, "use_dll")) != 0;
// Give explicit names to input arguments
const mxArray *dr_mx = prhs[0];
const mxArray *M_mx = prhs[1];
const mxArray *options_mx = prhs[2];
mxArray *mFname = mxGetField(M_, 0, "fname");
if (!mxIsChar(mFname))
DYN_MEX_FUNC_ERR_MSG_TXT("Input must be of type char.");
auto get_int_field = [](const mxArray *struct_mx, const std::string &fieldname)
{
mxArray *field_mx = mxGetField(struct_mx, 0, fieldname.c_str());
if (!(field_mx && mxIsScalar(field_mx) && mxIsNumeric(field_mx)))
mexErrMsgTxt(("Field `" + fieldname + "' should be a numeric scalar").c_str());
return static_cast<int>(mxGetScalar(field_mx));
};
std::string fName = mxArrayToString(mFname);
int kOrder;
mxArray *mxFldp = mxGetField(options_, 0, "order");
if (!mxIsNumeric(mxFldp))
DYN_MEX_FUNC_ERR_MSG_TXT("options_.order must be a numeric value");
kOrder = static_cast<int>(mxGetScalar(mxFldp));
// Extract various fields from options_
const int kOrder = get_int_field(options_mx, "order");
if (kOrder < 1)
DYN_MEX_FUNC_ERR_MSG_TXT("options_.order must be at least 1");
const mxArray *use_dll_mx = mxGetField(options_mx, 0, "use_dll");
if (!(use_dll_mx && mxIsLogicalScalar(use_dll_mx)))
DYN_MEX_FUNC_ERR_MSG_TXT("options_.use_dll should be a logical scalar");
bool use_dll = static_cast<bool>(mxGetScalar(use_dll_mx));
double qz_criterium = 1+1e-6;
mxFldp = mxGetField(options_, 0, "qz_criterium");
if (mxGetNumberOfElements(mxFldp) > 0 && mxIsNumeric(mxFldp))
qz_criterium = mxGetScalar(mxFldp);
const mxArray *qz_criterium_mx = mxGetField(options_mx, 0, "qz_criterium");
if (qz_criterium_mx && mxIsScalar(qz_criterium_mx) && mxIsNumeric(qz_criterium_mx))
qz_criterium = mxGetScalar(qz_criterium_mx);
mxFldp = mxGetField(M_, 0, "params");
Vector modParams{mxFldp};
// Extract various fields from M_
const mxArray *fname_mx = mxGetField(M_mx, 0, "fname");
if (!(fname_mx && mxIsChar(fname_mx) && mxGetM(fname_mx) == 1))
DYN_MEX_FUNC_ERR_MSG_TXT("M_.fname should be a character string");
std::string fname{mxArrayToString(fname_mx)};
const mxArray *params_mx = mxGetField(M_mx, 0, "params");
if (!(params_mx && mxIsDouble(params_mx)))
DYN_MEX_FUNC_ERR_MSG_TXT("M_.params should be a double precision array");
Vector modParams{ConstVector{params_mx}};
if (!modParams.isFinite())
DYN_MEX_FUNC_ERR_MSG_TXT("The parameters vector contains NaN or Inf");
DYN_MEX_FUNC_ERR_MSG_TXT("M_.params contains NaN or Inf");
mxFldp = mxGetField(M_, 0, "Sigma_e");
int dim = static_cast<int>(mxGetN(mxFldp));
TwoDMatrix vCov(dim, dim, Vector{mxFldp});
const mxArray *sigma_e_mx = mxGetField(M_mx, 0, "Sigma_e");
if (!(sigma_e_mx && mxIsDouble(sigma_e_mx) && mxGetM(sigma_e_mx) == mxGetN(sigma_e_mx)))
DYN_MEX_FUNC_ERR_MSG_TXT("M_.Sigma_e should be a double precision square matrix");
TwoDMatrix vCov{ConstTwoDMatrix{sigma_e_mx}};
if (!vCov.isFinite())
DYN_MEX_FUNC_ERR_MSG_TXT("The covariance matrix of shocks contains NaN or Inf");
DYN_MEX_FUNC_ERR_MSG_TXT("M_.Sigma_e contains NaN or Inf");
mxFldp = mxGetField(dr, 0, "ys"); // and not in order of dr.order_var
Vector ySteady{mxFldp};
if (!ySteady.isFinite())
DYN_MEX_FUNC_ERR_MSG_TXT("The steady state vector contains NaN or Inf");
const int nStat = get_int_field(M_mx, "nstatic");
const int nPred = get_int_field(M_mx, "npred");
const int nBoth = get_int_field(M_mx, "nboth");
const int nForw = get_int_field(M_mx, "nfwrd");
mxFldp = mxGetField(M_, 0, "nstatic");
const int nStat = static_cast<int>(mxGetScalar(mxFldp));
mxFldp = mxGetField(M_, 0, "npred");
const int nPred = static_cast<int>(mxGetScalar(mxFldp));
mxFldp = mxGetField(M_, 0, "nboth");
const int nBoth = static_cast<int>(mxGetScalar(mxFldp));
mxFldp = mxGetField(M_, 0, "nfwrd");
const int nForw = static_cast<int>(mxGetScalar(mxFldp));
const int nExog = get_int_field(M_mx, "exo_nbr");
const int nEndo = get_int_field(M_mx, "endo_nbr");
const int nPar = get_int_field(M_mx, "param_nbr");
mxFldp = mxGetField(M_, 0, "exo_nbr");
const int nExog = static_cast<int>(mxGetScalar(mxFldp));
mxFldp = mxGetField(M_, 0, "endo_nbr");
const int nEndo = static_cast<int>(mxGetScalar(mxFldp));
mxFldp = mxGetField(M_, 0, "param_nbr");
const int nPar = static_cast<int>(mxGetScalar(mxFldp));
const mxArray *lead_lag_incidence_mx = mxGetField(M_mx, 0, "lead_lag_incidence");
if (!(lead_lag_incidence_mx && mxIsDouble(lead_lag_incidence_mx) && mxGetM(lead_lag_incidence_mx) == 3
&& mxGetN(lead_lag_incidence_mx) == static_cast<size_t>(nEndo)))
DYN_MEX_FUNC_ERR_MSG_TXT("M_.lead_lag_incidence should be a double precision matrix with 3 rows and M_.endo_nbr columns");
ConstTwoDMatrix llincidence{lead_lag_incidence_mx};
mxFldp = mxGetField(dr, 0, "order_var");
dim = static_cast<int>(mxGetM(mxFldp));
if (dim != nEndo)
DYN_MEX_FUNC_ERR_MSG_TXT("Incorrect size of dr.order_var");
std::vector<int> dr_order(nEndo);
std::transform(mxGetPr(mxFldp), mxGetPr(mxFldp)+dim, dr_order.begin(),
[](double x) { return static_cast<int>(x)-1; });
// the lag, current and lead blocks of the jacobian respectively
TwoDMatrix llincidence(mxGetField(M_, 0, "lead_lag_incidence"));
if (llincidence.nrows() != 3 || llincidence.ncols() != nEndo)
DYN_MEX_FUNC_ERR_MSG_TXT("Incorrect size of M_.lead_lag_incidence");
mxFldp = mxGetField(M_, 0, "NNZDerivatives");
Vector NNZD{mxFldp};
if (NNZD[kOrder-1] == -1)
const mxArray *nnzderivatives_mx = mxGetField(M_mx, 0, "NNZDerivatives");
if (!(nnzderivatives_mx && mxIsDouble(nnzderivatives_mx)))
DYN_MEX_FUNC_ERR_MSG_TXT("M_.NNZDerivatives should be a double precision array");
ConstVector NNZD{nnzderivatives_mx};
if (NNZD.length() < kOrder || NNZD[kOrder-1] == -1)
DYN_MEX_FUNC_ERR_MSG_TXT("The derivatives were not computed for the required order. Make sure that you used the right order option inside the `stoch_simul' command");
mxFldp = mxGetField(M_, 0, "endo_names");
std::vector<std::string> endoNames = DynareMxArrayToString(mxFldp);
const mxArray *endo_names_mx = mxGetField(M_mx, 0, "endo_names");
if (!(endo_names_mx && mxIsCell(endo_names_mx) && mxGetNumberOfElements(endo_names_mx) == static_cast<size_t>(nEndo)))
DYN_MEX_FUNC_ERR_MSG_TXT("M_.endo_names should be a cell array of M_.endo_nbr elements");
std::vector<std::string> endoNames = DynareMxArrayToString(endo_names_mx);
mxFldp = mxGetField(M_, 0, "exo_names");
std::vector<std::string> exoNames = DynareMxArrayToString(mxFldp);
const mxArray *exo_names_mx = mxGetField(M_mx, 0, "exo_names");
if (!(exo_names_mx && mxIsCell(exo_names_mx) && mxGetNumberOfElements(exo_names_mx) == static_cast<size_t>(nExog)))
DYN_MEX_FUNC_ERR_MSG_TXT("M_.exo_names should be a cell array of M_.exo_nbr elements");
std::vector<std::string> exoNames = DynareMxArrayToString(exo_names_mx);
if (nEndo != static_cast<int>(endoNames.size()) || nExog != static_cast<int>(exoNames.size()))
DYN_MEX_FUNC_ERR_MSG_TXT("Incorrect size of M_.endo_names or M_.exo_names");
const mxArray *dynamic_tmp_nbr_mx = mxGetField(M_mx, 0, "dynamic_tmp_nbr");
if (!(dynamic_tmp_nbr_mx && mxIsDouble(dynamic_tmp_nbr_mx) && mxGetNumberOfElements(dynamic_tmp_nbr_mx) >= static_cast<size_t>(kOrder+1)))
DYN_MEX_FUNC_ERR_MSG_TXT("M_.dynamic_tmp_nbr should be a double precision array with strictly more elements than the order of derivation");
int ntt = std::accumulate(mxGetPr(dynamic_tmp_nbr_mx), mxGetPr(dynamic_tmp_nbr_mx)+kOrder+1, 0);
mxFldp = mxGetField(M_, 0, "dynamic_tmp_nbr");
if (static_cast<int>(mxGetM(mxFldp)) < kOrder+1 || mxGetN(mxFldp) != 1)
DYN_MEX_FUNC_ERR_MSG_TXT("Incorrect size of M_.dynamic_tmp_nbr");
int ntt = std::accumulate(mxGetPr(mxFldp), mxGetPr(mxFldp)+kOrder+1, 0);
// Extract various fields from dr
const mxArray *ys_mx = mxGetField(dr_mx, 0, "ys"); // and not in order of dr.order_var
if (!(ys_mx && mxIsDouble(ys_mx)))
DYN_MEX_FUNC_ERR_MSG_TXT("dr.ys should be a double precision array");
Vector ySteady{ConstVector{ys_mx}};
if (!ySteady.isFinite())
DYN_MEX_FUNC_ERR_MSG_TXT("dr.ys contains NaN or Inf");
const mxArray *order_var_mx = mxGetField(dr_mx, 0, "order_var");
if (!(order_var_mx && mxIsDouble(order_var_mx) && mxGetNumberOfElements(order_var_mx) == static_cast<size_t>(nEndo)))
DYN_MEX_FUNC_ERR_MSG_TXT("dr.order_var should be a double precision array of M_.endo_nbr elements");
std::vector<int> dr_order(nEndo);
std::transform(mxGetPr(order_var_mx), mxGetPr(order_var_mx)+nEndo, dr_order.begin(),
[](double x) { return static_cast<int>(x)-1; });
const int nSteps = 0; // Dynare++ solving steps, for time being default to 0 = deterministic steady state
try
{
Journal journal(fName + ".jnl");
Journal journal(fname + ".jnl");
std::unique_ptr<DynamicModelAC> dynamicModelFile;
if (use_dll)
dynamicModelFile = std::make_unique<DynamicModelDLL>(fName, ntt, kOrder);
dynamicModelFile = std::make_unique<DynamicModelDLL>(fname, ntt, kOrder);
else
dynamicModelFile = std::make_unique<DynamicModelMFile>(fName, ntt);
dynamicModelFile = std::make_unique<DynamicModelMFile>(fname, ntt);
// intiate tensor library
TLStatic::init(kOrder, nStat+2*nPred+3*nBoth+2*nForw+nExog);