/* * Copyright © 2004-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 . */ #include #include #include "dynare3.hh" #include "dynare_exception.hh" #include "planner_builder.hh" #include "forw_subst_builder.hh" #include "utils/cc/exception.hh" #include "parser/cc/parser_exception.hh" #include "parser/cc/atom_substitutions.hh" #include "../tl/cc/tl_exception.hh" #include "../kord/kord_exception.hh" /**************************************************************************************/ /* DynareNameList class */ /**************************************************************************************/ std::vector DynareNameList::selectIndices(const std::vector &ns) const { std::vector res; for (const auto &n : ns) { int j = 0; while (j < getNum() && n != getName(j)) j++; if (j == getNum()) throw DynareException(__FILE__, __LINE__, std::string("Couldn't find name for ") + n +" in DynareNameList::selectIndices"); res.push_back(j); } return res; } /**************************************************************************************/ /* Dynare class */ /**************************************************************************************/ Dynare::Dynare(const std::string &modname, int ord, double sstol, Journal &jr) : journal(jr), md(1), ss_tol(sstol) { std::ifstream f{modname}; if (f.fail()) throw DynareException(__FILE__, __LINE__, std::string{"Could not open model file "}+modname); std::ostringstream buffer; buffer << f.rdbuf(); std::string contents{buffer.str()}; try { model = std::make_unique(contents, ord); } catch (const ogp::ParserException &pe) { // Compute line and column, given the offset in the file int line = 1; int col = 0; size_t i = 0; while (i < contents.length() && i < static_cast(pe.offset())) { if (contents[i] == '\n') { line++; col = 0; } i++; col++; } throw DynareException(pe.message(), modname, line, col); } ysteady = std::make_unique(model->getAtoms().ny()); dnl = std::make_unique(*this); denl = std::make_unique(*this); dsnl = std::make_unique(*this, *dnl, *denl); fe = std::make_unique(model->getParser()); fde = std::make_unique(model->getParser()); writeModelInfo(journal); } Dynare::Dynare(const std::vector &endo, const std::vector &exo, const std::vector &par, const std::string &equations, int ord, double sstol, Journal &jr) : journal(jr), md(1), ss_tol(sstol) { try { model = std::make_unique(endo, exo, par, equations, ord); } catch (const ogp::ParserException &pe) { throw DynareException(pe.message(), pe.offset()); } ysteady = std::make_unique(model->getAtoms().ny()); dnl = std::make_unique(*this); denl = std::make_unique(*this); dsnl = std::make_unique(*this, *dnl, *denl); fe = std::make_unique(model->getParser()); fde = std::make_unique(model->getParser()); writeModelInfo(journal); } Dynare::Dynare(const Dynare &dynare) : journal(dynare.journal), md(dynare.md), ss_tol(dynare.ss_tol) { model = dynare.model->clone(); ysteady = std::make_unique(*(dynare.ysteady)); dnl = std::make_unique(*this); denl = std::make_unique(*this); dsnl = std::make_unique(*this, *dnl, *denl); fe = std::make_unique(model->getParser()); fde = std::make_unique(model->getParser()); } void Dynare::writeMat(mat_t *fd, const std::string &prefix) const { getAllEndoNames().writeMat(fd, prefix + "_vars"); getAllEndoNames().writeMatIndices(fd, prefix); getStateNames().writeMat(fd, prefix + "_state_vars"); getExogNames().writeMat(fd, prefix + "_shocks"); getExogNames().writeMatIndices(fd, prefix); model->getVcov().writeMat(fd, prefix + "_vcov_exo"); TwoDMatrix aux(1, 1); aux.get(0, 0) = nstat(); aux.writeMat(fd, prefix + "_nstat"); aux.get(0, 0) = npred(); aux.writeMat(fd, prefix + "_npred"); aux.get(0, 0) = nboth(); aux.writeMat(fd, prefix + "_nboth"); aux.get(0, 0) = nforw(); aux.writeMat(fd, prefix + "_nforw"); } void Dynare::writeDump(const std::string &basename) const { std::string fname(basename + ".dump"); std::ofstream out(fname); model->dump_model(out); out.close(); } void Dynare::solveDeterministicSteady(Vector &steady) { JournalRecordPair pa(journal); pa << "Non-linear solver for deterministic steady state" << endrec; steady = const_cast(model->getInit()); DynareVectorFunction dvf(*this); DynareJacobian dj(*this); ogu::NLSolver nls(dvf, dj, 500, ss_tol, journal); int iter; if (!nls.solve(steady, iter)) throw DynareException(__FILE__, __LINE__, "Could not obtain convergence in non-linear solver"); } // Evaluate system at given yₜ=yₜ₊₁=yₜ₋₁, and given shocks xₜ void Dynare::evaluateSystem(Vector &out, const ConstVector &yy, const Vector &xx) { ConstVector yym(yy, nstat(), nys()); ConstVector yyp(yy, nstat()+npred(), nyss()); evaluateSystem(out, yym, yy, yyp, xx); } /* Evaluate system at given y*ₜ₋₁, yₜ, y**ₜ₊₁ and at exogenous xₜ, all three vectors yym, yy, and yyp have the respective lengths of y*ₜ₋₁, yₜ, y**ₜ₊₁ */ void Dynare::evaluateSystem(Vector &out, const ConstVector &yym, const ConstVector &yy, const ConstVector &yyp, const Vector &xx) { ogdyn::DynareAtomValues dav(model->getAtoms(), model->getParams(), yym, yy, yyp, xx); DynareEvalLoader del(model->getAtoms(), out); fe->eval(dav, del); } void Dynare::calcDerivatives(const Vector &yy, const Vector &xx) { ConstVector yym(yy, nstat(), nys()); ConstVector yyp(yy, nstat()+npred(), nyss()); ogdyn::DynareAtomValues dav(model->getAtoms(), model->getParams(), yym, yy, yyp, xx); DynareDerEvalLoader ddel(model->getAtoms(), md, model->getOrder()); for (int iord = 1; iord <= model->getOrder(); iord++) fde->eval(dav, ddel, iord); } void Dynare::calcDerivativesAtSteady() { Vector xx(nexog()); xx.zeros(); calcDerivatives(*ysteady, xx); } void Dynare::writeModelInfo(Journal &jr) const { // write info on variables { JournalRecordPair rp(journal); rp << "Information on variables" << endrec; JournalRecord rec1(journal); rec1 << "Number of endogenous: " << ny() << endrec; JournalRecord rec2(journal); rec2 << "Number of exogenous: " << nexog() << endrec; JournalRecord rec3(journal); rec3 << "Number of static: " << nstat() << endrec; JournalRecord rec4(journal); rec4 << "Number of predetermined: " << npred()+nboth() << endrec; JournalRecord rec5(journal); rec5 << "Number of forward looking: " << nforw()+nboth() << endrec; JournalRecord rec6(journal); rec6 << "Number of both: " << nboth() << endrec; } // write info on planner variables const ogdyn::PlannerInfo *pinfo = model->get_planner_info(); if (pinfo) { JournalRecordPair rp(journal); rp << "Information on planner variables" << endrec; JournalRecord rec1(journal); rec1 << "Number of Lagrange multipliers: " << pinfo->num_lagrange_mults << endrec; JournalRecord rec2(journal); rec2 << "Number of auxiliary variables: " << pinfo->num_aux_variables << endrec; JournalRecord rec3(journal); rec3 << "Number of new terms in the tree: " << pinfo->num_new_terms << endrec; } // write info on forward substitutions const ogdyn::ForwSubstInfo *finfo = model->get_forw_subst_info(); if (finfo) { JournalRecordPair rp(journal); rp << "Information on forward substitutions" << endrec; JournalRecord rec1(journal); rec1 << "Number of affected equations: " << finfo->num_affected_equations << endrec; JournalRecord rec2(journal); rec2 << "Number of substituted terms: " << finfo->num_subst_terms << endrec; JournalRecord rec3(journal); rec3 << "Number of auxiliary variables: " << finfo->num_aux_variables << endrec; JournalRecord rec4(journal); rec4 << "Number of new terms in the tree: " << finfo->num_new_terms << endrec; } // write info on substitutions const ogp::SubstInfo *sinfo = model->get_subst_info(); if (sinfo) { JournalRecordPair rp(journal); rp << "Information on substitutions" << endrec; JournalRecord rec1(journal); rec1 << "Number of substitutions: " << sinfo->num_substs << endrec; } } DynareNameList::DynareNameList(const Dynare &dynare) { for (int i = 0; i < dynare.ny(); i++) { int j = dynare.model->getAtoms().y2outer_endo()[i]; const std::string &name = dynare.model->getAtoms().get_endovars()[j]; names.push_back(name); } } DynareStateNameList::DynareStateNameList(const Dynare &dynare, const DynareNameList &dnl, const DynareExogNameList &denl) { for (int i = 0; i < dynare.nys(); i++) names.push_back(dnl.getName(i+dynare.nstat())); for (int i = 0; i < dynare.nexog(); i++) names.push_back(denl.getName(i)); } DynareExogNameList::DynareExogNameList(const Dynare &dynare) { for (int i = 0; i < dynare.nexog(); i++) { int j = dynare.model->getAtoms().y2outer_exo()[i]; const std::string &name = dynare.model->getAtoms().get_exovars()[j]; names.push_back(name); } } DynareEvalLoader::DynareEvalLoader(const ogp::FineAtoms &a, Vector &out) : Vector(out) { if (a.ny() != out.length()) throw DynareException(__FILE__, __LINE__, "Wrong length of out vector in DynareEvalLoader constructor"); } /* This clears the container of model derivatives and initializes it inserting empty sparse tensors up to the given order. */ DynareDerEvalLoader::DynareDerEvalLoader(const ogp::FineAtoms &a, TensorContainer &mod_ders, int order) : atoms(a), md(mod_ders) { md.clear(); for (int iord = 1; iord <= order; iord++) { auto t = std::make_unique(iord, atoms.ny()+atoms.nys()+atoms.nyss()+atoms.nexo(), atoms.ny()); md.insert(std::move(t)); } } void DynareDerEvalLoader::load(int i, int iord, const int *vars, double res) { FSSparseTensor &t = md.get(Symmetry{iord}); IntSequence s(iord, 0); for (int j = 0; j < iord; j++) s[j] = atoms.get_pos_of_all(vars[j]); t.insert(s, i, res); } DynareJacobian::DynareJacobian(Dynare &dyn) : Jacobian(dyn.ny()), d(dyn) { zeros(); } void DynareJacobian::eval(const Vector &yy) { ogdyn::DynareSteadyAtomValues dav(d.getModel().getAtoms(), d.getModel().getParams(), yy); zeros(); d.fde->eval(dav, *this, 1); } void DynareJacobian::load(int i, int iord, const int *vars, double res) { if (iord != 1) throw DynareException(__FILE__, __LINE__, "Derivative order different from order=1 in DynareJacobian::load"); int t = vars[0]; int j = d.getModel().getAtoms().get_pos_of_all(t); if (j < d.nyss()) get(i, j+d.nstat()+d.npred()) += res; else if (j < d.nyss()+d.ny()) get(i, j-d.nyss()) += res; else if (j < d.nyss()+d.ny()+d.nys()) get(i, j-d.nyss()-d.ny()+d.nstat()) += res; } void DynareVectorFunction::eval(const ConstVector &in, Vector &out) { check_for_eval(in, out); Vector xx(d.nexog()); xx.zeros(); d.evaluateSystem(out, in, xx); }