dynare/mex/sources/libkorder/k_ord_dynare.cc

255 lines
8.3 KiB
C++
Raw Normal View History

/*
* Copyright © 2008-2023 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 <https://www.gnu.org/licenses/>.
*/
// GP, based on work by O.Kamenik
2019-04-02 16:33:15 +02:00
#include "k_ord_dynare.hh"
#include "dynamic_abstract_class.hh"
2019-04-02 16:33:15 +02:00
#include "dynare_exception.hh"
#include <cassert>
#include <utility>
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 ConstVector& nnzd,
int nsteps, int norder, Journal& jr,
std::unique_ptr<DynamicModelAC> dynamicModelFile_arg,
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},
nOrder {norder},
journal {jr},
ySteady {ysteady},
params {inParams},
vCov {vcov},
md {1},
dnl {endo},
denl {exo},
dsnl {*this, dnl, denl},
ll_Incidence {llincidence},
dynamicModelFile {std::move(dynamicModelFile_arg)}
{
computeJacobianPermutation(dr_order);
}
void
KordpDynare::solveDeterministicSteady()
{
JournalRecordPair pa(journal);
2019-04-02 16:33:15 +02:00
pa << "Non-linear solver for deterministic steady state skipped" << endrec;
}
void
KordpDynare::evaluateSystem(Vector& out, [[maybe_unused]] const ConstVector& yy,
[[maybe_unused]] const Vector& xx)
{
// This method is only called when checking the residuals at steady state (Approximation::check),
// so return zero residuals
out.zeros();
}
void
KordpDynare::evaluateSystem(Vector& out, [[maybe_unused]] const ConstVector& yym,
[[maybe_unused]] const ConstVector& yy,
[[maybe_unused]] const ConstVector& yyp,
[[maybe_unused]] const Vector& xx)
{
// This method is only called when checking the residuals at steady state (Approximation::check),
// so return zero residuals
out.zeros();
}
void
KordpDynare::calcDerivativesAtSteady()
{
assert(md.begin() == md.end());
std::vector<TwoDMatrix> dyn_md; // Model derivatives, in Dynare form
dyn_md.emplace_back(nY, nJcols); // Allocate Jacobian
dyn_md.back().zeros();
for (int i = 2; i <= nOrder; i++)
{
// Higher order derivatives, as sparse (3-column) matrices
dyn_md.emplace_back(static_cast<int>(NNZD[i - 1]), 3);
dyn_md.back().zeros();
}
2017-05-16 16:30:27 +02:00
Vector xx(nexog());
xx.zeros();
Vector out(nY);
out.zeros();
Vector llxSteady(nJcols - nExog);
LLxSteady(ySteady, llxSteady);
2017-05-16 16:30:27 +02:00
dynamicModelFile->eval(llxSteady, xx, params, ySteady, out, dyn_md);
for (int i = 1; i <= nOrder; i++)
populateDerivativesContainer(dyn_md, i);
}
void
KordpDynare::populateDerivativesContainer(const std::vector<TwoDMatrix>& dyn_md, int ord)
{
const TwoDMatrix& g = dyn_md[ord - 1];
// model derivatives FSSparseTensor instance
auto mdTi = std::make_unique<FSSparseTensor>(ord, nJcols, nY);
IntSequence s(ord, 0);
if (ord == 1)
for (int i = 0; i < g.ncols(); i++)
{
for (int j = 0; j < g.nrows(); j++)
{
2019-04-02 16:33:15 +02:00
double x = g.get(j, dynppToDyn[s[0]]);
if (x != 0.0)
mdTi->insert(s, j, x);
}
s[0]++;
}
else // ord ≥ 2
for (int i = 0; i < g.nrows(); i++)
{
int j = static_cast<int>(g.get(i, 0)) - 1;
int i1 = static_cast<int>(g.get(i, 1)) - 1;
if (j < 0 || i1 < 0)
continue; // Discard empty entries (see comment in DynamicModelAC::unpackSparseMatrix())
for (int k = 0; k < ord; k++)
{
s[k] = dynToDynpp[i1 % nJcols];
i1 /= nJcols;
}
if (ord == 2 && !s.isSorted())
continue; // Skip symmetric elements (only needed at order 2)
else if (ord > 2)
s.sort(); // For higher order, canonicalize the multi-index
double x = g.get(i, 2);
mdTi->insert(s, j, x);
}
md.insert(std::move(mdTi));
}
2019-04-02 16:33:15 +02:00
/* Returns ySteady extended with leads and lags suitable for passing to
<model>_dynamic */
void
KordpDynare::LLxSteady(const Vector& yS, Vector& llxSteady)
{
if (yS.length() == nJcols - nExog)
throw DynareException(__FILE__, __LINE__, "ySteady already of right size");
2019-04-02 16:33:15 +02:00
/* Create temporary square 2D matrix size nEndo×nEndo (sparse)
for the lag, current and lead blocks of the jacobian */
if (llxSteady.length() != nJcols - nExog)
throw DynareException(__FILE__, __LINE__, "llxSteady has wrong size");
for (int ll_row = 0; ll_row < ll_Incidence.nrows(); ll_row++)
// populate (non-sparse) vector with ysteady values
for (int i = 0; i < nY; i++)
2019-04-02 16:33:15 +02:00
if (ll_Incidence.get(ll_row, i))
llxSteady[static_cast<int>(ll_Incidence.get(ll_row, i)) - 1] = yS[i];
}
/*
Computes mapping between Dynare and Dynare++ orderings of the (dynamic)
variable indices in derivatives.
If one defines:
y (resp. x) as the vector of all endogenous (size nY), in DR-order (resp.
declaration order)
y (resp. x) as the vector of endogenous that appear at previous period (size nYs),
in DR-order (resp. declaration order)
y (resp. x) as the vector of endogenous that appear at future period (size nYss) in
DR-order (resp. declaration order)
u as the vector of exogenous (size nExog)
In Dynare, the ordering is (x, x, x, u).
In Dynare++, the ordering is (y, y, y, u).
dr_order is typically equal to oo_.dr.order_var.
*/
void
KordpDynare::computeJacobianPermutation(const std::vector<int>& dr_order)
{
// Compute restricted inverse DR-orderings: x⁻→y⁻ and x⁺→y⁺
std::vector<int> dr_inv_order_forw(nBoth + nForw), dr_inv_order_pred(nBoth + nPred);
std::iota(dr_inv_order_forw.begin(), dr_inv_order_forw.end(), 0);
std::sort(dr_inv_order_forw.begin(), dr_inv_order_forw.end(), [&](int i, int j) {
return dr_order[nStat + nPred + i] < dr_order[nStat + nPred + j];
});
std::iota(dr_inv_order_pred.begin(), dr_inv_order_pred.end(), 0);
std::sort(dr_inv_order_pred.begin(), dr_inv_order_pred.end(),
[&](int i, int j) { return dr_order[nStat + i] < dr_order[nStat + j]; });
// Compute restricted DR-orderings: y⁻→x⁻ and y⁺→x⁺
std::vector<int> dr_order_forw(nBoth + nForw), dr_order_pred(nBoth + nPred);
for (int i = 0; i < nBoth + nForw; i++)
dr_order_forw[dr_inv_order_forw[i]] = i;
for (int i = 0; i < nBoth + nPred; i++)
dr_order_pred[dr_inv_order_pred[i]] = i;
// Compute Dynare++ → Dynare ordering
2019-04-02 16:33:15 +02:00
dynppToDyn.resize(nJcols);
int j = 0;
for (; j < nYss; j++)
dynppToDyn[j] = dr_order_forw[j] + nYs + nY; // Forward variables
for (; j < nYss + nY; j++)
dynppToDyn[j] = dr_order[j - nYss] + nYs; // Variables in current period
for (; j < nYss + nY + nYs; j++)
dynppToDyn[j] = dr_order_pred[j - nY - nYss]; // Predetermined variables
for (; j < nJcols; j++)
dynppToDyn[j] = j; // Exogenous
// Compute Dynare → Dynare++ ordering
2019-04-02 16:33:15 +02:00
dynToDynpp.resize(nJcols);
for (int i = 0; i < nJcols; i++)
dynToDynpp[dynppToDyn[i]] = i;
}
DynareNameList::DynareNameList(std::vector<std::string> names_arg) : names(std::move(names_arg))
{
}
DynareStateNameList::DynareStateNameList(const KordpDynare& dynare, const DynareNameList& dnl,
const DynareNameList& denl)
{
2019-04-02 16:33:15 +02:00
for (int i = 0; i < dynare.nYs; i++)
names.emplace_back(dnl.getName(i + dynare.nStat));
for (int i = 0; i < dynare.nExog; i++)
names.emplace_back(denl.getName(i));
}