/* * Copyright © 2005-2011 Ondra Kamenik * Copyright © 2019 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 . */ #ifndef OGDYN_DYNARE_MODEL #define OGDYN_DYNARE_MODEL #include "parser/cc/matrix_parser.hh" #include "parser/cc/atom_assignings.hh" #include "dynare_atoms.hh" #include "twod_matrix.hh" #include "planner_builder.hh" #include "forw_subst_builder.hh" #include "Vector.hh" #include "GeneralMatrix.hh" #include #include #include #include #include namespace ogdyn { using std::unordered_set; using std::map; /* This represents an interval in a string by the pair of positions (including the first, excluding the second). A position is given by the line and the column within the line (both starting from 1). */ struct PosInterval { int fl; int fc; int ll; int lc; PosInterval() = default; PosInterval(int ifl, int ifc, int ill, int ilc) : fl(ifl), fc(ifc), ll(ill), lc(ilc) { } PosInterval &operator=(const PosInterval &pi) = default; /* Debug print. */ void print() const { std::cout << "fl=" << fl << " fc=" << fc << " ll=" << ll << " lc=" << lc << '\n'; } }; /* This class is basically a GeneralMatrix but is created from parsed matrix data. */ class ParsedMatrix : public TwoDMatrix { public: /* Construct the object from the parsed data of ogp::MatrixParser. */ ParsedMatrix(const ogp::MatrixParser &mp); }; class PlannerBuilder; class PlannerInfo; class ForwSubstBuilder; class ForwSubstInfo; class MultInitSS; class ModelSSWriter; /* A subclass is responsible for creating param_vals, init_vals, and vcov_mat. */ class DynareModel { friend class PlannerBuilder; friend class ForwSubstBuilder; friend class MultInitSS; friend class ModelSSWriter; protected: /* All atoms for whole model. */ DynareDynamicAtoms atoms; /* Parsed model equations. */ ogp::FormulaParser eqs; /* Order of approximation. */ int order{-1}; /* A vector of parameters values created by a subclass. It is stored with natural ordering (outer) of the parameters given by atoms. */ std::unique_ptr param_vals; /* A vector of initial values created by a subclass. It is stored with internal ordering given by atoms. */ std::unique_ptr init_vals; /* A matrix for vcov. It is created by a subclass. */ std::unique_ptr vcov_mat; /* Tree index of the planner objective. If there was no planner objective keyword, the value is set to −1. */ int t_plobjective{-1}; /* Tree index of the planner discount. If there was no planner discount keyword, the value is set to −1. */ int t_pldiscount{-1}; /* Pointer to PlannerBuilder, which is created only if the planner's FOC are added to the model. */ std::unique_ptr pbuilder; /* Pointer to an object which builds auxiliary variables and equations to rewrite a model containing multiple leads to an equivalent model having only +1 leads. */ std::unique_ptr fbuilder; /* Pointer to AtomSubstitutions which are created when the atoms are being substituted because of multiple lags etc. It uses also an old copy of atoms, which is created. */ std::unique_ptr atom_substs; /* Pointer to a copy of original atoms before substitutions took place. */ std::unique_ptr old_atoms; public: /* Initializes the object to an empty state. */ DynareModel(); /* Construct a new deep copy. */ DynareModel(const DynareModel &dm); virtual ~DynareModel() = default; virtual std::unique_ptr clone() const = 0; const DynareDynamicAtoms & getAtoms() const { return atoms; } const ogp::FormulaParser & getParser() const { return eqs; } int getOrder() const { return order; } /* Return the vector of parameter values. */ const Vector & getParams() const { return *param_vals; } Vector & getParams() { return *param_vals; } /* Return the vector of initial values of endo variables. */ const Vector & getInit() const { return *init_vals; } Vector & getInit() { return *init_vals; } /* Return the vcov matrix. */ const TwoDMatrix & getVcov() const { return *vcov_mat; } TwoDMatrix & getVcov() { return *vcov_mat; } /* Return planner info. */ const PlannerInfo *get_planner_info() const; /* Return forward substitutions info. */ const ForwSubstInfo *get_forw_subst_info() const; /* Return substitutions info. */ const ogp::SubstInfo *get_subst_info() const; /* This sets initial values given in outer ordering. */ void setInitOuter(const Vector &x); /* This returns true if the given term is a function of hardwired constants, numerical constants and parameters. */ bool is_constant_term(int t) const; /* Debug print. */ void print() const; /* Dump the model to the output stream. This includes variable declarations, parameter values, model code, initval, vcov and order. */ void dump_model(std::ostream &os) const; protected: /* Adds a name of endogenous, exogenous or a parameter. The sort is governed by the flag. See dynglob.yy for values of the flag. This is used by a subclass when declaring the names. */ void add_name(std::string name, int flag); /* This checks the model consistency. Thus includes: number of endo variables and number of equations, min and max lag of endogenous variables and occurrrences of exogenous variables. It throws an exception, if there is a problem. */ void check_model() const; /* This shifts the given variable identified by the tree index in time. So if the given tree index represents a(+3) and the tshift is −4, the method returns tree index of the a(-1). If a(-1) doesn't exist, it is added to the tree. If it exists, its tree index is returned. If the tree index doesn't correspond to an endogenous nor exogenous variable, an exception is thrown. */ int variable_shift(int t, int tshift); /* For the given set of atoms identified by tree indices and given time shift, this method returns a map mapping each variable in the given set to its time shifted variable. The map is passed through the reference and is cleared in the beginning. */ void variable_shift_map(const unordered_set &a_set, int tshift, map &s_map); /* This returns maximum lead and minimum lag of an endogenous or exogenous variable in the given term. If there are no endo or exo variables, than it returns the least integer as max lead and the greatest integer as min lag. */ void termspan(int t, int &mlead, int &mlag) const; /* This function returns a set of non-linear subterms of the given term, these are terms whose linear combination constitutes the given term. */ unordered_set get_nonlinear_subterms(int t) const; /* This method assigns already used tree index of some term to the not-yet used atom name with the given lead/lag. In this way, all occurrences of term t are substituted with the atom name(ll). The method handles also rewriting operation tree including derivatives of the term t. */ void substitute_atom_for_term(const string &name, int ll, int t); /* This performs a final job after the model is parsed. It creates the PlannerBuilder object if the planner's FOC are needed, then it creates ForwSubstBuilder handling multiple leads and finally it creates the substitution object saving old atoms and performs the substitutions. */ void final_job(); }; /* This class constructs DynareModel from dynare++ model file. It parses variable declarations, model equations, parameter assignments, initval assignments, vcov matrix and order of approximation. */ class DynareParser : public DynareModel { protected: /* Static atoms for parameter assignments. */ DynareStaticAtoms pa_atoms; /* Assignments for the parameters. */ ogp::AtomAssignings paramset; /* Static atoms for initval assignments. */ DynareStaticAtoms ia_atoms; /* Assignments for the initval. */ ogp::AtomAssignings initval; /* Matrix parser for vcov. */ ogp::MatrixParser vcov; public: /* This, in fact, creates DynareModel from the given string of the given length corresponding to the Dynare++ model file. If the given ord is not −1, then it overrides setting in the model file. */ DynareParser(const string &str, int ord); DynareParser(const DynareParser &dp); std::unique_ptr clone() const override { return std::make_unique(*this); } /* Adds a name of endogenous, exogenous or a parameter. This addss the name to the parent class DynareModel and also registers the name to either paramset, or initval. */ void add_name(string name, int flag); /* Sets position of the model section. Called from dynglob.yy. */ void set_model_pos(int off1, int off2) { model_beg = off1; model_end = off2; } /* Sets position of the section setting parameters. Called from dynglob.yy. */ void set_paramset_pos(int off1, int off2) { paramset_beg = off1; paramset_end = off2; } /* Sets position of the initval section. Called from dynglob.yy. */ void set_initval_pos(int off1, int off2) { initval_beg = off1; initval_end = off2; } /* Sets position of the vcov section. Called from dynglob.yy. */ void set_vcov_pos(int off1, int off2) { vcov_beg = off1; vcov_end = off2; } /* Parser the given string as integer and set to as the order. */ void set_order_pos(int off1, int off2) { order_beg = off1; order_end = off2; } /* Sets position of the planner_objective section. Called from dynglob.yy. */ void set_pl_objective_pos(int off1, int off2) { plobjective_beg = off1; plobjective_end = off2; } /* Sets position of the planner_discount section. Called from dynglob.yy. */ void set_pl_discount_pos(int off1, int off2) { pldiscount_beg = off1; pldiscount_end = off2; } /* Processes a syntax error from bison. */ void error(string mes); /* Debug print. */ void print() const; protected: void parse_glob(const string &stream); int parse_order(const string &stream); int parse_pldiscount(const string &stream); /* Evaluate paramset assignings and set param_vals. */ void calc_params(); /* Evaluate initval assignings and set init_vals. */ void calc_init(); /* Do the final job. This includes building the planner problem (if any) and substituting for multiple lags, and one period leads of exogenous variables, and calculating initial guess of lagrange multipliers in the social planner problem. Precondtion: everything parsed and calculated parameters, postcondition: calculated initvals vector and parsing_finished for expanded vectors. */ void final_job(); private: int model_beg, model_end; int paramset_beg, paramset_end; int initval_beg, initval_end; int vcov_beg, vcov_end; int order_beg, order_end; int plobjective_beg, plobjective_end; int pldiscount_beg, pldiscount_end; }; /* Semiparsed model. The equations are given by a string, everything other by C++ objects. The initial values are set manually after the creation of this object. This implies that no automatic substitutions cannot be done here, which in turn implies that we cannot do here a social planner nor substitutions of multiple lags. */ class DynareSPModel : public DynareModel { public: DynareSPModel(const std::vector &endo, const std::vector &exo, const std::vector &par, const string &equations, int ord); DynareSPModel(const DynareSPModel &dm) = default; ~DynareSPModel() override = default; std::unique_ptr clone() const override { return std::make_unique(*this); } }; /* This class implements a selector of operations which correspond to non-linear functions. This inherits from ogp::opselector and is used to calculate non-linear subterms in DynareModel::get_nonlinear_subterms(). */ class NLSelector : public ogp::opselector { private: const DynareModel &model; public: NLSelector(const DynareModel &m) : model(m) { } bool operator()(int t) const override; }; /* This class writes a mathematical code evaluating the system of equations and the first derivatives at zero shocks and at the given (static) state. Static means that lags and leads are ignored. */ class ModelSSWriter : public ogp::DefaultOperationFormatter { protected: const DynareModel &model; public: ModelSSWriter(const DynareModel &m) : DefaultOperationFormatter(m.eqs.getTree()), model(m) { } /* This writes the evaluation of the system. It calls pure virtual methods for writing a preamble, then assignment of atoms, and then assignment for resulting object. These are language dependent and are implemented in the subclass. */ void write_der0(std::ostream &os); /* This writes the evaluation of the first order derivative of the system. It calls pure virtual methods for writing a preamble, assignment, and assignemnt of the resulting objects. */ void write_der1(std::ostream &os); protected: virtual void write_der0_preamble(std::ostream &os) const = 0; virtual void write_der1_preamble(std::ostream &os) const = 0; virtual void write_atom_assignment(std::ostream &os) const = 0; virtual void write_der0_assignment(std::ostream &os) const = 0; virtual void write_der1_assignment(std::ostream &os) const = 0; }; class MatlabSSWriter : public ModelSSWriter { protected: /* Identifier used in function names. */ std::string id; public: MatlabSSWriter(const DynareModel &dm, std::string id_arg); protected: // from ModelSSWriter void write_der0_preamble(std::ostream &os) const override; void write_der1_preamble(std::ostream &os) const override; /* This writes atom assignments. We have four kinds of atoms set here: endogenous vars coming from one parameter, parameter values given by the second parameter, constants, and the OperationTree::num_constants hardwired constants in ogp::OperationTree. */ void write_atom_assignment(std::ostream &os) const override; void write_der0_assignment(std::ostream &os) const override; void write_der1_assignment(std::ostream &os) const override; /* This prints t10 for t=10. */ void format_term(int t, std::ostream &os) const override; /* This prints a10 for t=10. The atoms a10 are supposed to be set by write_atom_assignments(). */ void format_nulary(int t, std::ostream &os) const override; private: void write_common1_preamble(std::ostream &os) const; void write_common2_preamble(std::ostream &os) const; }; /* This class implements OperationFormatter for debugging purposes. It renders atoms in a more friendly way than the ogp::DefaulOperationFormatter. */ class DebugOperationFormatter : public ogp::DefaultOperationFormatter { protected: const DynareModel &model; public: DebugOperationFormatter(const DynareModel &m) : DefaultOperationFormatter(m.getParser().getTree()), model(m) { } void format_nulary(int t, std::ostream &os) const override; }; }; #endif