Remove more unused Dynare++ code

In particular, the libkorder library no longer depends on MatIO.
mr#2134
Sébastien Villemot 2023-04-17 17:19:43 +02:00
parent 99cd06c9fd
commit 73e4ced39a
No known key found for this signature in database
GPG Key ID: 2CECE9350ECEBE4A
35 changed files with 14 additions and 6533 deletions

View File

@ -78,9 +78,9 @@ A number of tools and libraries are needed in order to recompile everything. You
- [Flex](https://github.com/westes/flex), version 2.5.4 or later (only if you get the source through Git)
- [Autoconf](https://www.gnu.org/software/autoconf/), version 2.62 or later (only if you get the source through Git)
- [Automake](https://www.gnu.org/software/automake/), version 1.11.2 or later (only if you get the source through Git)
- [MAT File I/O library](https://sourceforge.net/projects/matio/), version 1.5 or later (if you want to compile Markov-Switching code, the estimation DLL, and the k-order DLL)
- [MAT File I/O library](https://sourceforge.net/projects/matio/), version 1.5 or later (if you want to compile Markov-Switching SBVAR code)
- [SLICOT](http://www.slicot.org) (if you want to compile the Kalman steady state DLL)
- [GSL library](https://www.gnu.org/software/gsl/) (if you want to compile Markov-Switching code)
- [GSL library](https://www.gnu.org/software/gsl/) (if you want to compile Markov-Switching SBVAR code)
- A decent LaTeX distribution (if you want to compile PDF documentation),
ideally with Beamer
- For building the reference manual:

View File

@ -302,9 +302,8 @@ Copyright: 2010 Yannick Kalantzis
License: GPL-3+
Files: mex/sources/gensylv/gensylv.cc
mex/sources/libkorder/kord/* mex/sources/libkorder/integ/*
mex/sources/libkorder/sylv/* mex/sources/libkorder/tl/*
mex/sources/libkorder/utils/*
mex/sources/libkorder/kord/* mex/sources/libkorder/sylv/*
mex/sources/libkorder/tl/* mex/sources/libkorder/utils/*
Copyright: 2004-2011 Ondra Kamenik
2019-2023 Dynare Team
License: GPL-3+

View File

@ -2,7 +2,7 @@ noinst_LIBRARIES = libkorder.a
TOPDIR = $(top_srcdir)/../../sources/libkorder
libkorder_a_CPPFLAGS = $(AM_CPPFLAGS) -I$(TOPDIR) -I$(TOPDIR)/tl -I$(TOPDIR)/sylv -I$(TOPDIR)/integ -I$(TOPDIR)/kord -I$(TOPDIR)/utils $(CPPFLAGS_MATIO)
libkorder_a_CPPFLAGS = $(AM_CPPFLAGS) -I$(TOPDIR) -I$(TOPDIR)/tl -I$(TOPDIR)/sylv -I$(TOPDIR)/kord -I$(TOPDIR)/utils $(CPPFLAGS_MATIO)
# TODO: fix the codebase so that the following line is no longer needed
libkorder_a_CXXFLAGS = $(AM_CXXFLAGS) -Wno-unused-parameter
@ -13,12 +13,9 @@ KORD_SRCS = \
dynamic_model.cc \
faa_di_bruno.cc \
first_order.cc \
global_check.cc \
korder.cc \
korder_stoch.cc \
journal.cc \
normal_conjugate.cc \
seed_generator.cc
journal.cc
SYLV_SRCS = \
BlockDiagonal.cc \
@ -61,18 +58,10 @@ TL_SRCS = \
tl_static.cc \
twod_matrix.cc
INTEG_SRCS = \
quadrature.cc \
quasi_mcarlo.cc \
product.cc \
smolyak.cc \
vector_function.cc
UTILS_SRCS = \
pascal_triangle.cc \
int_power.cc \
sthread.cc \
nlsolve.cc
sthread.cc
TOPDIR_SRCS = \
k_ord_dynare.cc \
@ -83,7 +72,6 @@ nodist_libkorder_a_SOURCES = \
$(KORD_SRCS) \
$(TL_SRCS) \
$(SYLV_SRCS) \
$(INTEG_SRCS) \
$(UTILS_SRCS) \
$(TOPDIR_SRCS)
@ -98,7 +86,5 @@ $(TL_SRCS): %.cc: $(TOPDIR)/tl/%.cc
$(LN_S) -f $< $@
$(SYLV_SRCS): %.cc: $(TOPDIR)/sylv/%.cc
$(LN_S) -f $< $@
$(INTEG_SRCS): %.cc: $(TOPDIR)/integ/%.cc
$(LN_S) -f $< $@
$(UTILS_SRCS): %.cc: $(TOPDIR)/utils/%.cc
$(LN_S) -f $< $@

View File

@ -1,11 +1,6 @@
ACLOCAL_AMFLAGS = -I ../../../m4
SUBDIRS = mjdgges kronecker bytecode sobol perfect_foresight_problem num_procs block_trust_region disclyap_fast libkordersim local_state_space_iterations folded_to_unfolded_dr k_order_simul k_order_mean cycle_reduction logarithmic_reduction riccati_update
# libkorder must come first
if ENABLE_MEX_GENSYLV_KORDER
SUBDIRS += libkorder gensylv k_order_perturbation k_order_welfare
endif
SUBDIRS = mjdgges kronecker bytecode sobol perfect_foresight_problem num_procs block_trust_region disclyap_fast libkordersim local_state_space_iterations folded_to_unfolded_dr k_order_simul k_order_mean cycle_reduction logarithmic_reduction riccati_update libkorder gensylv k_order_perturbation k_order_welfare
if ENABLE_MEX_MS_SBVAR
SUBDIRS += ms_sbvar

View File

@ -64,9 +64,6 @@ AC_SUBST([LIBADD_DLOPEN])
AC_ARG_ENABLE([mex-ms-sbvar], AS_HELP_STRING([--disable-mex-ms-sbvar], [disable compilation of the MS-SBVAR MEX]), [], [enable_mex_ms_sbvar=yes])
AM_CONDITIONAL([ENABLE_MEX_MS_SBVAR], [test "$enable_mex_ms_sbvar" = yes])
AC_ARG_ENABLE([mex-gensylv-korder], AS_HELP_STRING([--disable-mex-gensylv-korder], [disable compilation of gensylv and k-order MEX]), [], [enable_mex_gensylv_korder=yes])
AM_CONDITIONAL([ENABLE_MEX_GENSYLV_KORDER], [test "$enable_mex_gensylv_korder" = yes])
AC_ARG_ENABLE([mex-kalman-steady-state], AS_HELP_STRING([--disable-mex-kalman-steady-state], [disable compilation of the kalman_steady_state MEX]), [], [enable_mex_kalman_steady_state=yes])
AM_CONDITIONAL([ENABLE_MEX_KALMAN_STEADY_STATE], [test "$enable_mex_kalman_steady_state" = yes])
@ -76,12 +73,6 @@ if test "$enable_mex_ms_sbvar" = yes; then
test "$has_gsl" != yes && AC_MSG_ERROR([GSL cannot be found. If you want to skip the compilation of the MS-SBVAR MEX, pass the --disable-mex-ms-sbvar flag.])
fi
# Check for libmatio, needed by MEX files using Dynare++ code
if test "$enable_mex_gensylv_korder" = yes; then
AX_MATIO
test "$has_matio" != yes && AC_MSG_ERROR([libmatio cannot be found. If you want to skip the compilation of gensylv and k-order MEX, pass the --disable-mex-gensylv-korder flag.])
fi
# Check for libslicot, needed by kalman_steady_state
if test "$enable_mex_kalman_steady_state" = yes; then
# FCFLAGS must be temporarily modified, because otherwise -fno-underscoring is not
@ -98,7 +89,6 @@ fi
case ${host_os} in
*mingw32*)
GSL_LIBS="-Wl,-Bstatic $GSL_LIBS -Wl,-Bdynamic"
LIBADD_MATIO="-Wl,-Bstatic $LIBADD_MATIO -Wl,-Bdynamic"
LIBADD_SLICOT="-Wl,-Bstatic $LIBADD_SLICOT -Wl,-Bdynamic"
;;
esac
@ -115,12 +105,6 @@ AC_SUBST([M2HTML])
AM_CONDITIONAL([HAVE_M2HTML], [test "x$M2HTML" != "x"])
# Construct final output message
if test "$enable_mex_gensylv_korder" = yes; then
BUILD_GENSYLV_KORDER_MEX_MATLAB="yes"
else
BUILD_GENSYLV_KORDER_MEX_MATLAB="no"
fi
if test "$enable_mex_kalman_steady_state" = yes; then
BUILD_KALMAN_STEADY_STATE_MATLAB="yes"
else
@ -139,7 +123,6 @@ Dynare is now configured for building the following components...
Binaries (with "make"):
MEX files for MATLAB (except those listed below): yes
Gensylv and k-order MEX files for MATLAB: $BUILD_GENSYLV_KORDER_MEX_MATLAB
MS-SBVAR MEX files for MATLAB: $BUILD_MS_SBVAR_MEX_MATLAB
Kalman Steady State MEX file for MATLAB: $BUILD_KALMAN_STEADY_STATE_MATLAB
M2HTML documentation: $BUILD_M2HTML

View File

@ -1,11 +1,6 @@
ACLOCAL_AMFLAGS = -I ../../../m4
SUBDIRS = mjdgges kronecker bytecode sobol perfect_foresight_problem num_procs block_trust_region disclyap_fast libkordersim local_state_space_iterations folded_to_unfolded_dr k_order_simul k_order_mean cycle_reduction logarithmic_reduction riccati_update
# libkorder must come first
if ENABLE_MEX_GENSYLV_KORDER
SUBDIRS += libkorder gensylv k_order_perturbation k_order_welfare
endif
SUBDIRS = mjdgges kronecker bytecode sobol perfect_foresight_problem num_procs block_trust_region disclyap_fast libkordersim local_state_space_iterations folded_to_unfolded_dr k_order_simul k_order_mean cycle_reduction logarithmic_reduction riccati_update libkorder gensylv k_order_perturbation k_order_welfare
if ENABLE_MEX_MS_SBVAR
SUBDIRS += ms_sbvar

View File

@ -66,9 +66,6 @@ AC_SUBST([LIBADD_DLOPEN])
AC_ARG_ENABLE([mex-ms-sbvar], AS_HELP_STRING([--disable-mex-ms-sbvar], [disable compilation of the MS-SBVAR MEX]), [], [enable_mex_ms_sbvar=yes])
AM_CONDITIONAL([ENABLE_MEX_MS_SBVAR], [test "$enable_mex_ms_sbvar" = yes])
AC_ARG_ENABLE([mex-gensylv-korder], AS_HELP_STRING([--disable-mex-gensylv-korder], [disable compilation of gensylv and k-order MEX]), [], [enable_mex_gensylv_korder=yes])
AM_CONDITIONAL([ENABLE_MEX_GENSYLV_KORDER], [test "$enable_mex_gensylv_korder" = yes])
AC_ARG_ENABLE([mex-kalman-steady-state], AS_HELP_STRING([--disable-mex-kalman-steady-state], [disable compilation of the kalman_steady_state MEX]), [], [enable_mex_kalman_steady_state=yes])
AM_CONDITIONAL([ENABLE_MEX_KALMAN_STEADY_STATE], [test "$enable_mex_kalman_steady_state" = yes])
@ -78,10 +75,10 @@ if test "$enable_mex_ms_sbvar" = yes; then
test "$has_gsl" != yes && AC_MSG_ERROR([GSL cannot be found. If you want to skip the compilation of the MS-SBVAR MEX, pass the --disable-mex-ms-sbvar flag.])
fi
# Check for libmatio, needed by MEX files using Dynare++ code, and by ms-sbvar (the latter only under Octave, as an alternative to MATLAB's libmat)
if test "$enable_mex_gensylv_korder" = yes -o "$enable_mex_ms_sbvar" = yes; then
# Check for libmatio, needed by ms-sbvar (the latter only under Octave, as an alternative to MATLAB's libmat)
if test "$enable_mex_ms_sbvar" = yes; then
AX_MATIO
test "$has_matio" != yes && AC_MSG_ERROR([libmatio cannot be found. If you want to skip the compilation of MS-SBVAR, gensylv and k-order MEX, pass the --disable-mex-gensylv-korder and --disable-mex-ms-sbvar flags.])
test "$has_matio" != yes && AC_MSG_ERROR([libmatio cannot be found. If you want to skip the compilation of MS-SBVAR, pass the --disable-mex-ms-sbvar flags.])
fi
# Check for libslicot, needed by kalman_steady_state
@ -127,12 +124,6 @@ esac
AM_CONDITIONAL([LINK_OCTAVE_LIBS], [test ${link_octave_libs} = yes])
# Construct final output message
if test "$enable_mex_gensylv_korder" = yes; then
BUILD_GENSYLV_KORDER_MEX_OCTAVE="yes"
else
BUILD_GENSYLV_KORDER_MEX_OCTAVE="no"
fi
if test "$enable_mex_kalman_steady_state" = yes; then
BUILD_KALMAN_STEADY_STATE_OCTAVE="yes"
else
@ -151,7 +142,6 @@ Dynare is now configured for building the following components...
Binaries (with "make"):
MEX files for Octave (except those listed below): yes
Gensylv and k-order MEX for Octave: $BUILD_GENSYLV_KORDER_MEX_OCTAVE
MS-SBVAR MEX files for Octave: $BUILD_MS_SBVAR_MEX_OCTAVE
Kalman Steady State MEX file for Octave: $BUILD_KALMAN_STEADY_STATE_OCTAVE

File diff suppressed because it is too large Load Diff

View File

@ -1,143 +0,0 @@
/*
* Copyright © 2005 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 <https://www.gnu.org/licenses/>.
*/
#include "product.hh"
#include "symmetry.hh"
#include <iostream>
#include <iomanip>
/* This constructs a product iterator corresponding to index (j0,0,…,0). */
prodpit::prodpit(const ProductQuadrature &q, int j0, int l)
: prodq(q), level(l), npoints(q.uquad.numPoints(l)),
jseq(q.dimen(), 0),
end_flag(false),
sig{q.dimen()},
p{q.dimen()}
{
if (j0 < npoints)
{
jseq[0] = j0;
setPointAndWeight();
}
else
end_flag = true;
}
bool
prodpit::operator==(const prodpit &ppit) const
{
return &prodq == &ppit.prodq && end_flag == ppit.end_flag && jseq == ppit.jseq;
}
prodpit &
prodpit::operator++()
{
int i = prodq.dimen()-1;
jseq[i]++;
while (i >= 0 && jseq[i] == npoints)
{
jseq[i] = 0;
i--;
if (i >= 0)
jseq[i]++;
}
sig.signalAfter(std::max(i, 0));
if (i == -1)
end_flag = true;
if (!end_flag)
setPointAndWeight();
return *this;
}
/* This calculates the weight and sets point coordinates from the indices. */
void
prodpit::setPointAndWeight()
{
w = 1.0;
for (int i = 0; i < prodq.dimen(); i++)
{
p[i] = (prodq.uquad).point(level, jseq[i]);
w *= (prodq.uquad).weight(level, jseq[i]);
}
}
/* Debug print. */
void
prodpit::print() const
{
auto ff = std::cout.flags();
std::cout << "j=[";
for (int i = 0; i < prodq.dimen(); i++)
std::cout << std::setw(2) << jseq[i];
std::cout << std::showpos << std::fixed << std::setprecision(3)
<< "] " << std::setw(4) << w << "*(";
for (int i = 0; i < prodq.dimen()-1; i++)
std::cout << std::setw(4) << p[i] << ' ';
std::cout << std::setw(4) << p[prodq.dimen()-1] << ')' << std::endl;
std::cout.flags(ff);
}
ProductQuadrature::ProductQuadrature(int d, const OneDQuadrature &uq)
: QuadratureImpl<prodpit>(d), uquad(uq)
{
// TODO: check d≥1
}
/* This calls prodpit constructor to return an iterator which points
approximatelly at ti-th portion out of tn portions. First we find
out how many points are in the level, and then construct an interator
(j0,0,,0) where j0=ti·npoints/tn. */
prodpit
ProductQuadrature::begin(int ti, int tn, int l) const
{
// TODO: raise if l<dimen()
// TODO: check l ≤ uquad.numLevels()
int npoints = uquad.numPoints(l);
return prodpit(*this, ti*npoints/tn, l);
}
/* This just starts at the first level and goes to a higher level as long as a
number of evaluations (which is n for k being the level) is less than the
given number of evaluations. */
void
ProductQuadrature::designLevelForEvals(int max_evals, int &lev, int &evals) const
{
int last_evals;
evals = 1;
lev = 1;
do
{
lev++;
last_evals = evals;
evals = numEvals(lev);
}
while (lev < uquad.numLevels()-2 && evals < max_evals);
lev--;
evals = last_evals;
}

View File

@ -1,131 +0,0 @@
/*
* Copyright © 2005 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 <https://www.gnu.org/licenses/>.
*/
// Product quadrature.
/* This file defines a product multidimensional quadrature. If Qₖ$ denotes the
one dimensional quadrature, then the product quadrature Q of k level and
dimension d takes the form:
n n
Qf = w_i··w_{i_d} f(x_i,,x_{i_d})
i=1 i_d=1
which can be written in terms of the one dimensional quadrature Q as
Qf=(QQ)f
Here we define the product quadrature iterator prodpit and plug it into
QuadratureImpl to obtains ProductQuadrature. */
#ifndef PRODUCT_H
#define PRODUCT_H
#include "int_sequence.hh"
#include "vector_function.hh"
#include "quadrature.hh"
/* This defines a product point iterator. We have to maintain the following: a
pointer to product quadrature in order to know the dimension and the
underlying one dimensional quadrature, then level, number of points in the
level, integer sequence of indices, signal, the coordinates of the point and
the weight.
The point indices, signal, and point coordinates are implmented as pointers
in order to allow for empty constructor.
The constructor prodpit(const ProductQuadrature& q, int j0, int l)
constructs an iterator pointing to (j0,0,,0), which is used by begin()
dictated by QuadratureImpl. */
class ProductQuadrature;
class prodpit
{
protected:
const ProductQuadrature &prodq;
int level{0};
int npoints{0};
IntSequence jseq;
bool end_flag{true};
ParameterSignal sig;
Vector p;
double w;
public:
prodpit() = default;
prodpit(const ProductQuadrature &q, int j0, int l);
prodpit(const prodpit &ppit) = default;
~prodpit() = default;
bool operator==(const prodpit &ppit) const;
bool
operator!=(const prodpit &ppit) const
{
return !operator==(ppit);
}
prodpit &operator=(const prodpit &spit) = delete;
prodpit &operator++();
const ParameterSignal &
signal() const
{
return sig;
}
const Vector &
point() const
{
return p;
}
double
weight() const
{
return w;
}
void print() const;
protected:
void setPointAndWeight();
};
/* The product quadrature is just QuadratureImpl with the product iterator
plugged in. The object is constructed by just giving the underlying one
dimensional quadrature, and the dimension. The only extra method is
designLevelForEvals() which for the given maximum number of evaluations (and
dimension and underlying quadrature from the object) returns a maximum level
yeilding number of evaluations less than the given number. */
class ProductQuadrature : public QuadratureImpl<prodpit>
{
friend class prodpit;
const OneDQuadrature &uquad;
public:
ProductQuadrature(int d, const OneDQuadrature &uq);
~ProductQuadrature() override = default;
int
numEvals(int l) const override
{
int res = 1;
for (int i = 0; i < dimen(); i++)
res *= uquad.numPoints(l);
return res;
}
void designLevelForEvals(int max_eval, int &lev, int &evals) const;
protected:
prodpit begin(int ti, int tn, int level) const override;
};
#endif

View File

@ -1,42 +0,0 @@
/*
* Copyright © 2005 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 <https://www.gnu.org/licenses/>.
*/
#include "quadrature.hh"
#include "precalc_quadrature.hh"
#include <cmath>
void
OneDPrecalcQuadrature::calcOffsets()
{
offsets[0] = 0;
for (int i = 1; i < num_levels; i++)
offsets[i] = offsets[i-1] + num_points[i-1];
}
GaussHermite::GaussHermite()
: OneDPrecalcQuadrature(gh_num_levels, gh_num_points, gh_weights, gh_points)
{
}
GaussLegendre::GaussLegendre()
: OneDPrecalcQuadrature(gl_num_levels, gl_num_points, gl_weights, gl_points)
{
}

View File

@ -1,329 +0,0 @@
/*
* Copyright © 2005 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 <https://www.gnu.org/licenses/>.
*/
// Quadrature.
/* This file defines an interface for one dimensional (non-nested) quadrature
OneDQuadrature, and a parent for all multi-dimensional quadratures. This
parent class Quadrature presents a general concept of quadrature, this is
N
f(x)dx wx
¹
The class Quadrature just declares this concept. The concept is implemented
by class QuadratureImpl which paralelizes the summation. All implementations
therefore wishing to use the parallel implementation should inherit from
QuadratureImpl and integration is done.
The integration concept relies on a point iterator, which goes through all
x and w for i=1,,N. All the iterators must be able to go through only a
portion of the set i=1,,N. This enables us to implement paralelism, for two
threads for example, one iterator goes from the beginning to the
(approximately) half, and the other goes from the half to the end.
Besides this concept of the general quadrature, this file defines also one
dimensional quadrature, which is basically a scheme of points and weights
for different levels. The class OneDQuadrature is a parent of all such
objects, the classes GaussHermite and GaussLegendre are specific
implementations for Gauss-Hermite and Gauss-Legendre quadratures resp. */
#ifndef QUADRATURE_H
#define QUADRATURE_H
#include <iostream>
#include <fstream>
#include <iomanip>
#include "vector_function.hh"
#include "int_sequence.hh"
#include "sthread.hh"
/* This pure virtual class represents a concept of one-dimensional (non-nested)
quadrature. So, one dimensional quadrature must return number of levels,
number of points in a given level, and then a point and a weight in a given
level and given order. */
class OneDQuadrature
{
public:
virtual ~OneDQuadrature() = default;
virtual int numLevels() const = 0;
virtual int numPoints(int level) const = 0;
virtual double point(int level, int i) const = 0;
virtual double weight(int lelel, int i) const = 0;
};
/* This is a general concept of multidimensional quadrature. at this general
level, we maintain only a dimension, and declare virtual functions for
integration. The function take two forms; first takes a constant
VectorFunction as an argument, creates locally VectorFunctionSet and do
calculation, second one takes as an argument VectorFunctionSet.
Part of the interface is a method returning a number of evaluations for a
specific level. Note two things: this assumes that the number of evaluations
is known apriori and thus it is not applicable for adaptive quadratures,
second for Monte Carlo type of quadrature, the level is a number of
evaluations. */
class Quadrature
{
protected:
int dim;
public:
Quadrature(int d) : dim(d)
{
}
virtual ~Quadrature() = default;
int
dimen() const
{
return dim;
}
virtual void integrate(const VectorFunction &func, int level,
int tn, Vector &out) const = 0;
virtual void integrate(VectorFunctionSet &fs, int level, Vector &out) const = 0;
virtual int numEvals(int level) const = 0;
};
/* This is just an integration worker, which works over a given QuadratureImpl.
It also needs the function, level, a specification of the subgroup of
points, and output vector.
See QuadratureImpl class declaration for details. */
template<typename _Tpit>
class QuadratureImpl;
template<typename _Tpit>
class IntegrationWorker : public sthread::detach_thread
{
const QuadratureImpl<_Tpit> &quad;
VectorFunction &func;
int level;
int ti;
int tn;
Vector &outvec;
public:
IntegrationWorker(const QuadratureImpl<_Tpit> &q, VectorFunction &f, int l,
int tii, int tnn, Vector &out)
: quad(q), func(f), level(l), ti(tii), tn(tnn), outvec(out)
{
}
/* This integrates the given portion of the integral. We obtain first and
last iterators for the portion (beg and end). Then we iterate through
the portion. and finally we add the intermediate result to the result
outvec.
This method just everything up as it is coming. This might be imply large
numerical errors, perhaps in future something smarter should be implemented. */
void
operator()(std::mutex &mut) override
{
_Tpit beg = quad.begin(ti, tn, level);
_Tpit end = quad.begin(ti+1, tn, level);
Vector tmpall(outvec.length());
tmpall.zeros();
Vector tmp(outvec.length());
/* Note that since beg came from begin(), it has empty signal and first
evaluation gets no signal */
for (_Tpit run = beg; run != end; ++run)
{
func.eval(run.point(), run.signal(), tmp);
tmpall.add(run.weight(), tmp);
}
{
std::unique_lock<std::mutex> lk{mut};
outvec.add(1.0, tmpall);
}
}
};
/* This is the class which implements the integration. The class is templated
by the iterator type. We declare a method begin() returning an iterator to
the beginnning of the ti-th portion out of total tn portions for a given
level.
In addition, we define a method which saves all the points to a given file.
Only for debugging purposes. */
template<typename _Tpit>
class QuadratureImpl : public Quadrature
{
friend class IntegrationWorker<_Tpit>;
public:
QuadratureImpl(int d) : Quadrature(d)
{
}
/* Just fill a thread group with workes and run it. */
void
integrate(VectorFunctionSet &fs, int level, Vector &out) const override
{
// TODO: out.length()==func.outdim()
// TODO: dim == func.indim()
out.zeros();
sthread::detach_thread_group gr;
for (int ti = 0; ti < fs.getNum(); ti++)
gr.insert(std::make_unique<IntegrationWorker<_Tpit>>(*this, fs.getFunc(ti),
level, ti, fs.getNum(), out));
gr.run();
}
void
integrate(const VectorFunction &func,
int level, int tn, Vector &out) const override
{
VectorFunctionSet fs(func, tn);
integrate(fs, level, out);
}
/* Just for debugging. */
void
savePoints(const std::string &fname, int level) const
{
std::ofstream fd{fname, std::ios::out | std::ios::trunc};
if (fd.fail())
{
// TODO: raise
std::cerr << "Cannot open file " << fname << " for writing." << std::endl;
exit(EXIT_FAILURE);
}
_Tpit beg = begin(0, 1, level);
_Tpit end = begin(1, 1, level);
fd << std::setprecision(12);
for (_Tpit run = beg; run != end; ++run)
{
fd << std::setw(16) << run.weight();
for (int i = 0; i < dimen(); i++)
fd << '\t' << std::setw(16) << run.point()[i];
fd << std::endl;
}
fd.close();
}
_Tpit
start(int level) const
{
return begin(0, 1, level);
}
_Tpit
end(int level) const
{
return begin(1, 1, level);
}
protected:
virtual _Tpit begin(int ti, int tn, int level) const = 0;
};
/* This is only an interface to a precalculated data in file
precalc_quadrature.hh which is basically C coded static data. It implements
OneDQuadrature. The data file is supposed to define the following data:
number of levels, array of number of points at each level, an array of
weights and array of points. The both latter array store data level by
level. An offset for a specific level is stored in offsets integer
sequence.
The implementing subclasses just fill the necessary data from the file, the
rest is calculated here. */
class OneDPrecalcQuadrature : public OneDQuadrature
{
int num_levels;
const int *num_points;
const double *weights;
const double *points;
IntSequence offsets;
public:
OneDPrecalcQuadrature(int nlevels, const int *npoints,
const double *wts, const double *pts)
: num_levels(nlevels), num_points(npoints),
weights(wts), points(pts), offsets(num_levels)
{
calcOffsets();
}
~OneDPrecalcQuadrature() override = default;
int
numLevels() const override
{
return num_levels;
}
int
numPoints(int level) const override
{
return num_points[level-1];
}
double
point(int level, int i) const override
{
return points[offsets[level-1]+i];
}
double
weight(int level, int i) const override
{
return weights[offsets[level-1]+i];
}
protected:
void calcOffsets();
};
/* Just precalculated Gauss-Hermite quadrature. This quadrature integrates
exactly integrals
+
xe^{x²}dx
for level k.
Note that if pluging this one-dimensional quadrature to product or Smolyak
rule in order to integrate a function f through normally distributed inputs,
one has to wrap f to GaussConverterFunction and apply the product or Smolyak
rule to the new function.
Check precalc_quadrature.hh for available levels. */
class GaussHermite : public OneDPrecalcQuadrature
{
public:
GaussHermite();
};
/* Just precalculated Gauss-Legendre quadrature. This quadrature integrates
exactly integrals
xdx
for level k.
Check precalc_quadrature.hh for available levels. */
class GaussLegendre : public OneDPrecalcQuadrature
{
public:
GaussLegendre();
};
#endif

View File

@ -1,214 +0,0 @@
/*
* Copyright © 2005 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 <https://www.gnu.org/licenses/>.
*/
#include "quasi_mcarlo.hh"
#include <cmath>
#include <iostream>
#include <iomanip>
#include <array>
/* Here in the constructor, we have to calculate a maximum length of coeff
array for a given base and given maximum maxn. After allocation, we
calculate the coefficients. */
RadicalInverse::RadicalInverse(int n, int b, int mxn)
: num(n), base(b), maxn(mxn),
coeff(static_cast<int>(floor(log(static_cast<double>(maxn))/log(static_cast<double>(b)))+2), 0)
{
int nr = num;
j = -1;
do
{
j++;
coeff[j] = nr % base;
nr = nr / base;
}
while (nr > 0);
}
/* This evaluates the radical inverse. If there was no permutation, we have to
calculate:
c c c
+ + +
b b² bʲ¹
which is evaluated as:
c 1 c 1 c 1 c
· + · + · +
b b b b b b b
Now with permutation π, we have:
π(c) 1 π(c) 1 π(c) 1 π(c)
· + · + · +
b b b b b b b
*/
double
RadicalInverse::eval(const PermutationScheme &p) const
{
double res = 0;
for (int i = j; i >= 0; i--)
{
int cper = p.permute(i, base, coeff[i]);
res = (cper + res)/base;
}
return res;
}
/* We just add 1 to the lowest coefficient and check for overflow with respect
to the base. */
void
RadicalInverse::increase()
{
// TODO: raise if num+1 > maxn
num++;
int i = 0;
coeff[i]++;
while (coeff[i] == base)
{
coeff[i] = 0;
coeff[++i]++;
}
if (i > j)
j = i;
}
/* Debug print. */
void
RadicalInverse::print() const
{
std::cout << "n=" << num << " b=" << base << " c=";
coeff.print();
}
/* Here we have the first 170 primes. This means that we are not able to
integrate dimensions greater than 170. */
std::array<int, 170> HaltonSequence::primes =
{
2, 3, 5, 7, 11, 13, 17, 19, 23, 29,
31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
73, 79, 83, 89, 97, 101, 103, 107, 109, 113,
127, 131, 137, 139, 149, 151, 157, 163, 167, 173,
179, 181, 191, 193, 197, 199, 211, 223, 227, 229,
233, 239, 241, 251, 257, 263, 269, 271, 277, 281,
283, 293, 307, 311, 313, 317, 331, 337, 347, 349,
353, 359, 367, 373, 379, 383, 389, 397, 401, 409,
419, 421, 431, 433, 439, 443, 449, 457, 461, 463,
467, 479, 487, 491, 499, 503, 509, 521, 523, 541,
547, 557, 563, 569, 571, 577, 587, 593, 599, 601,
607, 613, 617, 619, 631, 641, 643, 647, 653, 659,
661, 673, 677, 683, 691, 701, 709, 719, 727, 733,
739, 743, 751, 757, 761, 769, 773, 787, 797, 809,
811, 821, 823, 827, 829, 839, 853, 857, 859, 863,
877, 881, 883, 887, 907, 911, 919, 929, 937, 941,
947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013
};
/* This takes first dim primes and constructs dim radical inverses and
calls eval(). */
HaltonSequence::HaltonSequence(int n, int mxn, int dim, const PermutationScheme &p)
: num(n), maxn(mxn), per(p), pt(dim)
{
// TODO: raise if dim > num_primes
// TODO: raise if n > mxn
for (int i = 0; i < dim; i++)
ri.emplace_back(num, primes[i], maxn);
eval();
}
/* This calls RadicalInverse::increase() for all radical inverses and calls
eval(). */
void
HaltonSequence::increase()
{
for (auto &i : ri)
i.increase();
num++;
if (num <= maxn)
eval();
}
/* This sets point pt to radical inverse evaluations in each dimension. */
void
HaltonSequence::eval()
{
for (unsigned int i = 0; i < ri.size(); i++)
pt[i] = ri[i].eval(per);
}
/* Debug print. */
void
HaltonSequence::print() const
{
auto ff = std::cout.flags();
for (const auto &i : ri)
i.print();
std::cout << "point=[ "
<< std::fixed << std::setprecision(6);
for (unsigned int i = 0; i < ri.size(); i++)
std::cout << std::setw(7) << pt[i] << ' ';
std::cout << ']' << std::endl;
std::cout.flags(ff);
}
qmcpit::qmcpit(const QMCSpecification &s, int n)
: spec(s), halton{n, s.level(), s.dimen(), s.getPerScheme()},
sig{s.dimen()}
{
}
bool
qmcpit::operator==(const qmcpit &qpit) const
{
return &spec == &qpit.spec && halton.getNum() == qpit.halton.getNum();
}
qmcpit &
qmcpit::operator++()
{
halton.increase();
return *this;
}
double
qmcpit::weight() const
{
return 1.0/spec.level();
}
int
WarnockPerScheme::permute(int i, int base, int c) const
{
return (c+i) % base;
}
int
ReversePerScheme::permute(int i, int base, int c) const
{
return (base-c) % base;
}

View File

@ -1,256 +0,0 @@
/*
* Copyright © 2005 Ondra Kamenik
* Copyright © 2019-2022 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/>.
*/
// Quasi Monte Carlo quadrature.
/* This defines quasi Monte Carlo quadratures for cube and for a function
multiplied by normal density. The quadrature for a cube is named
QMCarloCubeQuadrature and integrates:
f(x)dx
[0,1]
The quadrature for a function of normally distributed parameters is named
QMCarloNormalQuadrature and integrates:
1
f(x)e^{½xx}dx
{(2π)} [,+]
For a cube we define qmcpit as iterator of QMCarloCubeQuadrature, and for
the normal density multiplied function we define qmcnpit as iterator of
QMCarloNormalQuadrature.
The quasi Monte Carlo method generates low discrepancy points with equal
weights. The one dimensional low discrepancy sequences are generated by
RadicalInverse class, the sequences are combined for higher dimensions by
HaltonSequence class. The Halton sequence can use a permutation scheme;
PermutattionScheme is an abstract class for all permutaton schemes. We have
three implementations: WarnockPerScheme, ReversePerScheme, and
IdentityPerScheme. */
#ifndef QUASI_MCARLO_H
#define QUASI_MCARLO_H
#include "int_sequence.hh"
#include "quadrature.hh"
#include "Vector.hh"
#include <vector>
/* This abstract class declares permute() method which permutes coefficient c
having index of i fro the base base and returns the permuted coefficient
which must be in 0,,base1. */
class PermutationScheme
{
public:
PermutationScheme() = default;
virtual ~PermutationScheme() = default;
virtual int permute(int i, int base, int c) const = 0;
};
/* This class represents an integer number num as c₀+c₁b+c₂b²+…+cⱼbʲ, where b
is base and c,,c is stored in coeff. The size of IntSequence coeff
does not grow with growing num, but is fixed from the very beginning and
is set according to supplied maximum maxn.
The basic method is eval() which evaluates the RadicalInverse with a given
permutation scheme and returns the point, and increase() which increases
num and recalculates the coefficients. */
class RadicalInverse
{
int num;
int base;
int maxn;
int j;
IntSequence coeff;
public:
RadicalInverse(int n, int b, int mxn);
RadicalInverse(const RadicalInverse &ri) = default;
RadicalInverse &operator=(const RadicalInverse &radi) = default;
double eval(const PermutationScheme &p) const;
void increase();
void print() const;
};
/* This is a vector of RadicalInverses, each RadicalInverse has a different
prime as its base. The static members primes and num_primes define a
precalculated array of primes. The increase() method of the class increases
indices in all RadicalInverses and sets point pt to contain the points in
each dimension. */
class HaltonSequence
{
private:
static std::array<int, 170> primes;
protected:
int num;
int maxn;
std::vector<RadicalInverse> ri;
const PermutationScheme &per;
Vector pt;
public:
HaltonSequence(int n, int mxn, int dim, const PermutationScheme &p);
HaltonSequence(const HaltonSequence &hs) = default;
HaltonSequence &operator=(const HaltonSequence &hs) = delete;
void increase();
const Vector &
point() const
{
return pt;
}
int
getNum() const
{
return num;
}
void print() const;
protected:
void eval();
};
/* This is a specification of quasi Monte Carlo quadrature. It consists of
dimension dim, number of points (or level) lev, and the permutation
scheme. This class is common to all quasi Monte Carlo classes. */
class QMCSpecification
{
protected:
int dim;
int lev;
const PermutationScheme &per_scheme;
public:
QMCSpecification(int d, int l, const PermutationScheme &p)
: dim(d), lev(l), per_scheme(p)
{
}
virtual ~QMCSpecification() = default;
int
dimen() const
{
return dim;
}
int
level() const
{
return lev;
}
const PermutationScheme &
getPerScheme() const
{
return per_scheme;
}
};
/* This is an iterator for quasi Monte Carlo over a cube QMCarloCubeQuadrature.
The iterator maintains HaltonSequence of the same dimension as given by the
specification. An iterator can be constructed from a given number n, or by
a copy constructor. */
class qmcpit
{
protected:
const QMCSpecification &spec;
HaltonSequence halton;
ParameterSignal sig;
public:
qmcpit(const QMCSpecification &s, int n);
qmcpit(const qmcpit &qpit) = default;
virtual ~qmcpit() = default;
bool operator==(const qmcpit &qpit) const;
bool
operator!=(const qmcpit &qpit) const
{
return !operator==(qpit);
}
qmcpit &operator=(const qmcpit &qpit) = delete;
qmcpit &operator++();
const ParameterSignal &
signal() const
{
return sig;
}
const Vector &
point() const
{
return halton.point();
}
double weight() const;
void
print() const
{
halton.print();
}
};
/* This is an easy declaration of quasi Monte Carlo quadrature for a cube.
Everything important has been done in its iterator qmcpit, so we only
inherit from general Quadrature and reimplement begin() and numEvals(). */
class QMCarloCubeQuadrature : public QuadratureImpl<qmcpit>, public QMCSpecification
{
public:
QMCarloCubeQuadrature(int d, int l, const PermutationScheme &p)
: QuadratureImpl<qmcpit>(d), QMCSpecification(d, l, p)
{
}
~QMCarloCubeQuadrature() override = default;
int
numEvals(int l) const override
{
return l;
}
protected:
qmcpit
begin(int ti, int tn, int lev) const override
{
return qmcpit(*this, ti*level()/tn + 1);
}
};
/* Declares Warnock permutation scheme. */
class WarnockPerScheme : public PermutationScheme
{
public:
int permute(int i, int base, int c) const override;
};
/* Declares reverse permutation scheme. */
class ReversePerScheme : public PermutationScheme
{
public:
int permute(int i, int base, int c) const override;
};
/* Declares no permutation (identity) scheme. */
class IdentityPerScheme : public PermutationScheme
{
public:
int
permute(int i, int base, int c) const override
{
return c;
}
};
#endif

View File

@ -1,220 +0,0 @@
/*
* Copyright © 2005 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 <https://www.gnu.org/licenses/>.
*/
#include "smolyak.hh"
#include "symmetry.hh"
#include <iostream>
#include <iomanip>
/* This constructs a beginning of isum summand in smolq. We must be careful
here, since isum can be past-the-end, so no reference to vectors in
smolq by isum must be done in this case. */
smolpit::smolpit(const SmolyakQuadrature &q, unsigned int isum)
: smolq(q), isummand(isum),
jseq(q.dimen(), 0),
sig{q.dimen()},
p{q.dimen()}
{
if (isummand < q.numSummands())
setPointAndWeight();
}
bool
smolpit::operator==(const smolpit &spit) const
{
return &smolq == &spit.smolq && isummand == spit.isummand && jseq == spit.jseq;
}
/* We first try to increase index within the current summand. If we are at
maximum, we go to a subsequent summand. Note that in this case all indices
in jseq will be zero, so no change is needed. */
smolpit &
smolpit::operator++()
{
const IntSequence &levpts = smolq.levpoints[isummand];
int i = smolq.dimen()-1;
jseq[i]++;
while (i >= 0 && jseq[i] == levpts[i])
{
jseq[i] = 0;
i--;
if (i >= 0)
jseq[i]++;
}
sig.signalAfter(std::max(i, 0));
if (i < 0)
isummand++;
if (isummand < smolq.numSummands())
setPointAndWeight();
return *this;
}
/* Here we set the point coordinates according to jseq and
isummand. Also the weight is set here. */
void
smolpit::setPointAndWeight()
{
// todo: raise if isummand ≥ smolq.numSummands()
int l = smolq.level;
int d = smolq.dimen();
int sumk = (smolq.levels[isummand]).sum();
int m1exp = l + d - sumk - 1;
w = (2*(m1exp/2) == m1exp) ? 1.0 : -1.0;
w *= PascalTriangle::noverk(d-1, sumk-l);
for (int i = 0; i < d; i++)
{
int ki = (smolq.levels[isummand])[i];
p[i] = (smolq.uquad).point(ki, jseq[i]);
w *= (smolq.uquad).weight(ki, jseq[i]);
}
}
/* Debug print. */
void
smolpit::print() const
{
auto ff = std::cout.flags();
std::cout << "isum=" << std::left << std::setw(3) << isummand << std::right << ": [";
for (int i = 0; i < smolq.dimen(); i++)
std::cout << std::setw(2) << (smolq.levels[isummand])[i] << ' ';
std::cout << "] j=[";
for (int i = 0; i < smolq.dimen(); i++)
std::cout << std::setw(2) << jseq[i] << ' ';
std::cout << std::showpos << std::fixed << std::setprecision(3)
<< "] " << std::setw(4) << w << "*(";
for (int i = 0; i < smolq.dimen()-1; i++)
std::cout << std::setw(4) << p[i] << ' ';
std::cout << std::setw(4) << p[smolq.dimen()-1] << ')' << std::endl;
std::cout.flags(ff);
}
/* Here is the constructor of SmolyakQuadrature. We have to setup levels,
levpoints and cumevals. We have to go through all d-dimensional
sequences k, such that l|k|l+d1 and all k are positive integers. This is
equivalent to going through all k such that ld|k|l1 and all k are
non-negative integers. This is equivalent to going through d+1 dimensional
sequences (k,x) such that |(k,x)|=l1 and x=0,,d1. The resulting sequence
of positive integers is obtained by adding 1 to all k. */
SmolyakQuadrature::SmolyakQuadrature(int d, int l, const OneDQuadrature &uq)
: QuadratureImpl<smolpit>(d), level(l), uquad(uq)
{
// TODO: check l>1, l≥d
// TODO: check l≥uquad.miLevel(), l≤uquad.maxLevel()
int cum = 0;
for (const auto &si : SymmetrySet(l-1, d+1))
{
if (si[d] <= d-1)
{
IntSequence lev(si, 0, d);
lev.add(1);
levels.push_back(lev);
IntSequence levpts(d);
for (int i = 0; i < d; i++)
levpts[i] = uquad.numPoints(lev[i]);
levpoints.push_back(levpts);
cum += levpts.mult();
cumevals.push_back(cum);
}
}
}
/* Here we return a number of evalutions of the quadrature for the given level.
If the given level is the current one, we simply return the maximum
cumulative number of evaluations. Otherwise we call costly
calcNumEvaluations() method. */
int
SmolyakQuadrature::numEvals(int l) const
{
if (l != level)
return calcNumEvaluations(l);
else
return cumevals[numSummands()-1];
}
/* This divides all the evaluations to tn approximately equal groups, and
returns the beginning of the specified group ti. The granularity of
divisions are summands as listed by levels. */
smolpit
SmolyakQuadrature::begin(int ti, int tn, int l) const
{
// TODO: raise is level≠l
if (ti == tn)
return smolpit(*this, numSummands());
int totevals = cumevals[numSummands()-1];
int evals = (totevals*ti)/tn;
unsigned int isum = 0;
while (isum+1 < numSummands() && cumevals[isum+1] < evals)
isum++;
return smolpit(*this, isum);
}
/* This is the same in a structure as SmolyakQuadrature constructor. We have to
go through all summands and calculate a number of evaluations in each
summand. */
int
SmolyakQuadrature::calcNumEvaluations(int lev) const
{
int cum = 0;
for (const auto &si : SymmetrySet(lev-1, dim+1))
{
if (si[dim] <= dim-1)
{
IntSequence lev(si, 0, dim);
lev.add(1);
IntSequence levpts(dim);
for (int i = 0; i < dim; i++)
levpts[i] = uquad.numPoints(lev[i]);
cum += levpts.mult();
}
}
return cum;
}
/* This returns a maximum level such that the number of evaluations is less
than the given number. */
void
SmolyakQuadrature::designLevelForEvals(int max_evals, int &lev, int &evals) const
{
int last_evals;
evals = 1;
lev = 1;
do
{
lev++;
last_evals = evals;
evals = calcNumEvaluations(lev);
}
while (lev < uquad.numLevels() && evals <= max_evals);
lev--;
evals = last_evals;
}

View File

@ -1,149 +0,0 @@
/*
* Copyright © 2005 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 <https://www.gnu.org/licenses/>.
*/
// Smolyak quadrature.
/* This file defines Smolyak (sparse grid) multidimensional quadrature for
non-nested underlying one dimensional quadrature. Let Q¹ denote the one
dimensional quadrature of l level. Let n denote a number of points in the l
level. Than the Smolyak quadrature can be defined as
d1
Qf = (1)^{l+d|k|1} (Q¹_kQ¹_{k_d})f
l|k|l+d1 |k|l
where d is the dimension, k is d-dimensional sequence of integers, and |k|
denotes the sum of the sequence.
Here we define smolpit as Smolyak iterator and SmolyakQuadrature. */
#ifndef SMOLYAK_H
#define SMOLYAK_H
#include "int_sequence.hh"
#include "tl_static.hh"
#include "vector_function.hh"
#include "quadrature.hh"
#include "pascal_triangle.hh"
/* Here we define the Smolyak point iterator. The Smolyak formula can be broken
to a sum of product quadratures with various combinations of levels. The
iterator follows this pattern. It maintains an index to a summand and then a
point coordinates within the summand (product quadrature). The array of
summands to which the isummand points is maintained by the
SmolyakQuadrature class to which the object knows the pointer smolq.
We provide a constructor which points to the beginning of the given summand.
This constructor is used in SmolyakQuadrature::begin() method which
approximately divideds all the iterators to subsets of equal size. */
class SmolyakQuadrature;
class smolpit
{
protected:
const SmolyakQuadrature &smolq;
unsigned int isummand{0};
IntSequence jseq;
ParameterSignal sig;
Vector p;
double w;
public:
smolpit(const SmolyakQuadrature &q, unsigned int isum);
smolpit(const smolpit &spit) = default;
~smolpit() = default;
bool operator==(const smolpit &spit) const;
bool
operator!=(const smolpit &spit) const
{
return !operator==(spit);
}
smolpit &operator=(const smolpit &spit) = delete;
smolpit &operator++();
const ParameterSignal &
signal() const
{
return sig;
}
const Vector &
point() const
{
return p;
}
double
weight() const
{
return w;
}
void print() const;
protected:
void setPointAndWeight();
};
/* Here we define the class SmolyakQuadrature. It maintains an array of
summands of the Smolyak quadrature formula:
d1
(1)^{l+d|k|1} (Q¹_kQ¹_{k_d})f
l|k|l+d1 |k|l
Each summand is fully specified by sequence k. The summands are here
represented (besides k) also by sequence of number of points in each level
selected by k, and also by a cummulative number of evaluations. The latter
two are added only for conveniency.
The summands in the code are given by levels, which is a vector of
k sequences, further by levpoints which is a vector of sequences
of nuber of points in each level, and by cumevals which is the
cumulative number of points, this is:
d
n_k
¹
where the sum is done through all k before the current.
The levels and levpoints vectors are used by smolpit. */
class SmolyakQuadrature : public QuadratureImpl<smolpit>
{
friend class smolpit;
int level;
const OneDQuadrature &uquad;
std::vector<IntSequence> levels;
std::vector<IntSequence> levpoints;
std::vector<int> cumevals;
public:
SmolyakQuadrature(int d, int l, const OneDQuadrature &uq);
~SmolyakQuadrature() override = default;
int numEvals(int level) const override;
void designLevelForEvals(int max_eval, int &lev, int &evals) const;
protected:
smolpit begin(int ti, int tn, int level) const override;
unsigned int
numSummands() const
{
return levels.size();
}
private:
int calcNumEvaluations(int level) const;
};
#endif

View File

@ -1,9 +0,0 @@
check_PROGRAMS = tests
tests_SOURCES = tests.cc
tests_CPPFLAGS = -I../cc -I../../tl/cc -I../../sylv/cc -I../../utils/cc -I$(top_srcdir)/mex/sources
tests_LDFLAGS = $(AM_LDFLAGS) $(LDFLAGS_MATIO)
tests_LDADD = ../../sylv/cc/libsylv.a ../cc/libinteg.a ../../tl/cc/libtl.a ../../utils/cc/libutils.a $(LAPACK_LIBS) $(BLAS_LIBS) $(LIBS) $(FLIBS) $(LIBADD_MATIO)
check-local:
./tests

View File

@ -1,550 +0,0 @@
/*
* Copyright © 2005 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 <https://www.gnu.org/licenses/>.
*/
#include "GeneralMatrix.hh"
#include <dynlapack.h>
#include "SylvException.hh"
#include "rfs_tensor.hh"
#include "normal_moments.hh"
#include "vector_function.hh"
#include "quadrature.hh"
#include "smolyak.hh"
#include "product.hh"
#include "quasi_mcarlo.hh"
#include <iomanip>
#include <chrono>
#include <cmath>
#include <iostream>
#include <utility>
#include <array>
#include <memory>
#include <cstdlib>
/* Evaluates unfolded (Dx)ᵏ power, where x is a vector, D is a Cholesky factor
(lower triangular) */
class MomentFunction : public VectorFunction
{
GeneralMatrix D;
int k;
public:
MomentFunction(const GeneralMatrix &inD, int kk)
: VectorFunction(inD.nrows(), UFSTensor::calcMaxOffset(inD.nrows(), kk)),
D(inD), k(kk)
{
}
MomentFunction(const MomentFunction &func) = default;
std::unique_ptr<VectorFunction>
clone() const override
{
return std::make_unique<MomentFunction>(*this);
}
void eval(const Vector &point, const ParameterSignal &sig, Vector &out) override;
};
void
MomentFunction::eval(const Vector &point, const ParameterSignal &sig, Vector &out)
{
if (point.length() != indim() || out.length() != outdim())
{
std::cerr << "Wrong length of vectors in MomentFunction::eval" << std::endl;
std::exit(EXIT_FAILURE);
}
Vector y(point);
y.zeros();
D.multaVec(y, point);
URSingleTensor ypow(y, k);
out.zeros();
out.add(1.0, ypow.getData());
}
class TensorPower : public VectorFunction
{
int k;
public:
TensorPower(int nvar, int kk)
: VectorFunction(nvar, UFSTensor::calcMaxOffset(nvar, kk)), k(kk)
{
}
TensorPower(const TensorPower &func) = default;
std::unique_ptr<VectorFunction>
clone() const override
{
return std::make_unique<TensorPower>(*this);
}
void eval(const Vector &point, const ParameterSignal &sig, Vector &out) override;
};
void
TensorPower::eval(const Vector &point, const ParameterSignal &sig, Vector &out)
{
if (point.length() != indim() || out.length() != outdim())
{
std::cerr << "Wrong length of vectors in TensorPower::eval" << std::endl;
std::exit(EXIT_FAILURE);
}
URSingleTensor ypow(point, k);
out.zeros();
out.add(1.0, ypow.getData());
}
/* Evaluates (1+1/d)ᵈ(x₁·…·x_d)^(1/d), its integral over [0,1]ᵈ
is 1.0, and its variation grows exponentially */
class Function1 : public VectorFunction
{
int dim;
public:
Function1(int d)
: VectorFunction(d, 1), dim(d)
{
}
Function1(const Function1 &f)
: VectorFunction(f.indim(), f.outdim()), dim(f.dim)
{
}
std::unique_ptr<VectorFunction>
clone() const override
{
return std::make_unique<Function1>(*this);
}
void eval(const Vector &point, const ParameterSignal &sig, Vector &out) override;
};
void
Function1::eval(const Vector &point, const ParameterSignal &sig, Vector &out)
{
if (point.length() != dim || out.length() != 1)
{
std::cerr << "Wrong length of vectors in Function1::eval" << std::endl;
std::exit(EXIT_FAILURE);
}
double r = 1;
for (int i = 0; i < dim; i++)
r *= point[i];
r = pow(r, 1.0/dim);
r *= pow(1.0 + 1.0/dim, static_cast<double>(dim));
out[0] = r;
}
// Evaluates Function1 but with transformation xᵢ=0.5(yᵢ+1)
// This makes the new function integrate over [1,1]ᵈ to 1.0
class Function1Trans : public Function1
{
public:
Function1Trans(int d)
: Function1(d)
{
}
Function1Trans(const Function1Trans &func) = default;
std::unique_ptr<VectorFunction>
clone() const override
{
return std::make_unique<Function1Trans>(*this);
}
void eval(const Vector &point, const ParameterSignal &sig, Vector &out) override;
};
void
Function1Trans::eval(const Vector &point, const ParameterSignal &sig, Vector &out)
{
Vector p(point.length());
for (int i = 0; i < p.length(); i++)
p[i] = 0.5*(point[i]+1);
Function1::eval(p, sig, out);
out.mult(pow(0.5, indim()));
}
/* WallTimer class. Constructor saves the wall time, destructor cancels the
current time from the saved, and prints the message with time information */
class WallTimer
{
std::string mes;
std::chrono::time_point<std::chrono::high_resolution_clock> start;
bool new_line;
public:
WallTimer(std::string m, bool nl = true)
: mes{m}, start{std::chrono::high_resolution_clock::now()}, new_line{nl}
{
}
~WallTimer()
{
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> duration = end - start;
std::cout << mes << std::setw(8) << std::setprecision(4) << duration.count();
if (new_line)
std::cout << std::endl;
}
};
/****************************************************/
/* declaration of TestRunnable class */
/****************************************************/
class TestRunnable
{
public:
const std::string name;
int dim; // dimension of the solved problem
int nvar; // number of variables of the solved problem
TestRunnable(std::string name_arg, int d, int nv)
: name{move(name_arg)}, dim(d), nvar(nv)
{
}
virtual ~TestRunnable() = default;
bool test() const;
virtual bool run() const = 0;
protected:
static bool smolyak_normal_moments(const GeneralMatrix &m, int imom, int level);
static bool product_normal_moments(const GeneralMatrix &m, int imom, int level);
static bool qmc_normal_moments(const GeneralMatrix &m, int imom, int level);
static bool smolyak_product_cube(const VectorFunction &func, const Vector &res,
double tol, int level);
static bool qmc_cube(const VectorFunction &func, double res, double tol, int level);
};
bool
TestRunnable::test() const
{
std::cout << "Running test <" << name << ">" << std::endl;
bool passed;
{
WallTimer tim("Wall clock time ", false);
passed = run();
}
if (passed)
{
std::cout << "............................ passed" << std::endl << std::endl;
return passed;
}
else
{
std::cout << "............................ FAILED" << std::endl << std::endl;
return passed;
}
}
/****************************************************/
/* definition of TestRunnable static methods */
/****************************************************/
bool
TestRunnable::smolyak_normal_moments(const GeneralMatrix &m, int imom, int level)
{
// First make m·mᵀ and then Cholesky factor
GeneralMatrix msq(m * transpose(m));
// Make vector function
int dim = m.nrows();
TensorPower tp(dim, imom);
GaussConverterFunction func(tp, msq);
// Smolyak quadrature
Vector smol_out(UFSTensor::calcMaxOffset(dim, imom));
{
WallTimer tim("\tSmolyak quadrature time: ");
GaussHermite gs;
SmolyakQuadrature quad(dim, level, gs);
quad.integrate(func, level, sthread::detach_thread_group::max_parallel_threads, smol_out);
std::cout << "\tNumber of Smolyak evaluations: " << quad.numEvals(level) << std::endl;
}
// Check against theoretical moments
UNormalMoments moments(imom, msq);
smol_out.add(-1.0, moments.get(Symmetry{imom}).getData());
std::cout << "\tError: " << std::setw(16) << std::setprecision(12) << smol_out.getMax() << std::endl;
return smol_out.getMax() < 1.e-7;
}
bool
TestRunnable::product_normal_moments(const GeneralMatrix &m, int imom, int level)
{
// First make m·mᵀ and then Cholesky factor
GeneralMatrix msq(m * transpose(m));
// Make vector function
int dim = m.nrows();
TensorPower tp(dim, imom);
GaussConverterFunction func(tp, msq);
// Product quadrature
Vector prod_out(UFSTensor::calcMaxOffset(dim, imom));
{
WallTimer tim("\tProduct quadrature time: ");
GaussHermite gs;
ProductQuadrature quad(dim, gs);
quad.integrate(func, level, sthread::detach_thread_group::max_parallel_threads, prod_out);
std::cout << "\tNumber of product evaluations: " << quad.numEvals(level) << std::endl;
}
// Check against theoretical moments
UNormalMoments moments(imom, msq);
prod_out.add(-1.0, moments.get(Symmetry{imom}).getData());
std::cout << "\tError: " << std::setw(16) << std::setprecision(12) << prod_out.getMax() << std::endl;
return prod_out.getMax() < 1.e-7;
}
bool
TestRunnable::smolyak_product_cube(const VectorFunction &func, const Vector &res,
double tol, int level)
{
if (res.length() != func.outdim())
{
std::cerr << "Incompatible dimensions of check value and function." << std::endl;
std::exit(EXIT_FAILURE);
}
GaussLegendre glq;
Vector out(func.outdim());
double smol_error;
double prod_error;
{
WallTimer tim("\tSmolyak quadrature time: ");
SmolyakQuadrature quad(func.indim(), level, glq);
quad.integrate(func, level, sthread::detach_thread_group::max_parallel_threads, out);
out.add(-1.0, res);
smol_error = out.getMax();
std::cout << "\tNumber of Smolyak evaluations: " << quad.numEvals(level) << std::endl;
std::cout << "\tError: " << std::setw(16) << std::setprecision(12) << smol_error << std::endl;
}
{
WallTimer tim("\tProduct quadrature time: ");
ProductQuadrature quad(func.indim(), glq);
quad.integrate(func, level, sthread::detach_thread_group::max_parallel_threads, out);
out.add(-1.0, res);
prod_error = out.getMax();
std::cout << "\tNumber of product evaluations: " << quad.numEvals(level) << std::endl;
std::cout << "\tError: " << std::setw(16) << std::setprecision(12) << prod_error << std::endl;
}
return smol_error < tol && prod_error < tol;
}
bool
TestRunnable::qmc_cube(const VectorFunction &func, double res, double tol, int level)
{
Vector r(1);
double error1;
{
WallTimer tim("\tQuasi-Monte Carlo (Warnock scrambling) time: ");
WarnockPerScheme wps;
QMCarloCubeQuadrature qmc(func.indim(), level, wps);
qmc.integrate(func, level, sthread::detach_thread_group::max_parallel_threads, r);
error1 = std::max(res - r[0], r[0] - res);
std::cout << "\tQuasi-Monte Carlo (Warnock scrambling) error: " << std::setw(16) << std::setprecision(12) << error1 << std::endl;
}
double error2;
{
WallTimer tim("\tQuasi-Monte Carlo (reverse scrambling) time: ");
ReversePerScheme rps;
QMCarloCubeQuadrature qmc(func.indim(), level, rps);
qmc.integrate(func, level, sthread::detach_thread_group::max_parallel_threads, r);
error2 = std::max(res - r[0], r[0] - res);
std::cout << "\tQuasi-Monte Carlo (reverse scrambling) error: " << std::setw(16) << std::setprecision(12) << error2 << std::endl;
}
double error3;
{
WallTimer tim("\tQuasi-Monte Carlo (no scrambling) time: ");
IdentityPerScheme ips;
QMCarloCubeQuadrature qmc(func.indim(), level, ips);
qmc.integrate(func, level, sthread::detach_thread_group::max_parallel_threads, r);
error3 = std::max(res - r[0], r[0] - res);
std::cout << "\tQuasi-Monte Carlo (no scrambling) error: " << std::setw(16) << std::setprecision(12) << error3 << std::endl;
}
return error1 < tol && error2 < tol && error3 < tol;
}
/****************************************************/
/* definition of TestRunnable subclasses */
/****************************************************/
class SmolyakNormalMom1 : public TestRunnable
{
public:
SmolyakNormalMom1()
: TestRunnable("Smolyak normal moments (dim=2, level=4, order=4)", 4, 2)
{
}
bool
run() const override
{
GeneralMatrix m(2, 2);
m.zeros();
m.get(0, 0) = 1;
m.get(1, 1) = 1;
return smolyak_normal_moments(m, 4, 4);
}
};
class SmolyakNormalMom2 : public TestRunnable
{
public:
SmolyakNormalMom2()
: TestRunnable("Smolyak normal moments (dim=3, level=8, order=8)", 8, 3)
{
}
bool
run() const override
{
GeneralMatrix m(3, 3);
m.zeros();
m.get(0, 0) = 1;
m.get(0, 2) = 0.5;
m.get(1, 1) = 1;
m.get(1, 0) = 0.5;
m.get(2, 2) = 2;
m.get(2, 1) = 4;
return smolyak_normal_moments(m, 8, 8);
}
};
class ProductNormalMom1 : public TestRunnable
{
public:
ProductNormalMom1()
: TestRunnable("Product normal moments (dim=2, level=4, order=4)", 4, 2)
{
}
bool
run() const override
{
GeneralMatrix m(2, 2);
m.zeros();
m.get(0, 0) = 1;
m.get(1, 1) = 1;
return product_normal_moments(m, 4, 4);
}
};
class ProductNormalMom2 : public TestRunnable
{
public:
ProductNormalMom2()
: TestRunnable("Product normal moments (dim=3, level=8, order=8)", 8, 3)
{
}
bool
run() const override
{
GeneralMatrix m(3, 3);
m.zeros();
m.get(0, 0) = 1;
m.get(0, 2) = 0.5;
m.get(1, 1) = 1;
m.get(1, 0) = 0.5;
m.get(2, 2) = 2;
m.get(2, 1) = 4;
return product_normal_moments(m, 8, 8);
}
};
// Note that here we pass 1,1 to tls since smolyak has its own PascalTriangle
class F1GaussLegendre : public TestRunnable
{
public:
F1GaussLegendre()
: TestRunnable("Function1 Gauss-Legendre (dim=6, level=13", 1, 1)
{
}
bool
run() const override
{
Function1Trans f1(6);
Vector res(1);
res[0] = 1.0;
return smolyak_product_cube(f1, res, 1e-2, 13);
}
};
class F1QuasiMCarlo : public TestRunnable
{
public:
F1QuasiMCarlo()
: TestRunnable("Function1 Quasi-Monte Carlo (dim=6, level=1000000)", 1, 1)
{
}
bool
run() const override
{
Function1 f1(6);
return qmc_cube(f1, 1.0, 1.e-4, 1000000);
}
};
int
main()
{
std::vector<std::unique_ptr<TestRunnable>> all_tests;
// Fill in vector of all tests
all_tests.push_back(std::make_unique<SmolyakNormalMom1>());
all_tests.push_back(std::make_unique<SmolyakNormalMom2>());
all_tests.push_back(std::make_unique<ProductNormalMom1>());
all_tests.push_back(std::make_unique<ProductNormalMom2>());
all_tests.push_back(std::make_unique<F1GaussLegendre>());
all_tests.push_back(std::make_unique<F1QuasiMCarlo>());
// Find maximum dimension and maximum nvar
int dmax = 0;
int nvmax = 0;
for (const auto &test : all_tests)
{
dmax = std::max(dmax, test->dim);
nvmax = std::max(nvmax, test->nvar);
}
TLStatic::init(dmax, nvmax); // initialize library
// Launch the tests
int success = 0;
for (const auto &test : all_tests)
{
try
{
if (test->test())
success++;
}
catch (const TLException &e)
{
std::cout << "Caught TL exception in <" << test->name << ">:" << std::endl;
e.print();
}
catch (SylvException &e)
{
std::cout << "Caught Sylv exception in <" << test->name << ">:" << std::endl;
e.printMessage();
}
}
int nfailed = all_tests.size() - success;
std::cout << "There were " << nfailed << " tests that failed out of "
<< all_tests.size() << " tests run." << std::endl;
if (nfailed)
return EXIT_FAILURE;
else
return EXIT_SUCCESS;
}

View File

@ -1,147 +0,0 @@
/*
* Copyright © 2005 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 <https://www.gnu.org/licenses/>.
*/
#include "vector_function.hh"
#include <dynlapack.h>
#include <cmath>
#include <algorithm>
/* Just an easy constructor of sequence of booleans defaulting to change
everywhere. */
ParameterSignal::ParameterSignal(int n)
: data(n, true)
{
}
/* This sets false (no change) before a given parameter, and true (change)
after the given parameter (including). */
void
ParameterSignal::signalAfter(int l)
{
for (size_t i = 0; i < std::min(static_cast<size_t>(l), data.size()); i++)
data[i] = false;
for (size_t i = l; i < data.size(); i++)
data[i] = true;
}
/* This constructs a function set hardcopying also the first. */
VectorFunctionSet::VectorFunctionSet(const VectorFunction &f, int n)
: funcs(n)
{
for (int i = 0; i < n; i++)
{
func_copies.push_back(f.clone());
funcs[i] = func_copies.back().get();
}
}
/* This constructs a function set with shallow copy in the first and hard
copies in others. */
VectorFunctionSet::VectorFunctionSet(VectorFunction &f, int n)
: funcs(n)
{
if (n > 0)
funcs[0] = &f;
for (int i = 1; i < n; i++)
{
func_copies.push_back(f.clone());
funcs[i] = func_copies.back().get();
}
}
/* Here we construct the object from the given function f and given
variance-covariance matrix Σ=vcov. The matrix A is calculated as lower
triangular and yields Σ=AA. */
GaussConverterFunction::GaussConverterFunction(VectorFunction &f, const GeneralMatrix &vcov)
: VectorFunction(f), func(&f), A(vcov.nrows(), vcov.nrows()),
multiplier(calcMultiplier())
{
// TODO: raise if A.nrows() ≠ indim()
calcCholeskyFactor(vcov);
}
GaussConverterFunction::GaussConverterFunction(std::unique_ptr<VectorFunction> f, const GeneralMatrix &vcov)
: VectorFunction(*f), func_storage{move(f)}, func{func_storage.get()}, A(vcov.nrows(), vcov.nrows()),
multiplier(calcMultiplier())
{
// TODO: raise if A.nrows() ≠ indim()
calcCholeskyFactor(vcov);
}
GaussConverterFunction::GaussConverterFunction(const GaussConverterFunction &f)
: VectorFunction(f), func_storage{f.func->clone()}, func{func_storage.get()}, A(f.A),
multiplier(f.multiplier)
{
}
/* Here we evaluate the function
g(y) = 1/(π) f(2·Ay).
Since the matrix A is lower triangular, the change signal for the function f
will look like (0,,0,1,,1) where the first 1 is in the same position as
the first change in the given signal sig of the input y=point. */
void
GaussConverterFunction::eval(const Vector &point, const ParameterSignal &sig, Vector &out)
{
ParameterSignal s(sig);
int i = 0;
while (i < indim() && !sig[i])
i++;
s.signalAfter(i);
Vector x(indim());
x.zeros();
A.multaVec(x, point);
x.mult(sqrt(2.0));
func->eval(x, s, out);
out.mult(multiplier);
}
/* This returns 1/√(πⁿ). */
double
GaussConverterFunction::calcMultiplier() const
{
return sqrt(pow(M_PI, -1*indim()));
}
void
GaussConverterFunction::calcCholeskyFactor(const GeneralMatrix &vcov)
{
A = vcov;
lapack_int rows = A.nrows(), lda = A.getLD();
for (int i = 0; i < rows; i++)
for (int j = i+1; j < rows; j++)
A.get(i, j) = 0.0;
lapack_int info;
dpotrf("L", &rows, A.base(), &lda, &info);
// TODO: raise if info≠1
}

View File

@ -1,186 +0,0 @@
/*
* Copyright © 2005 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 <https://www.gnu.org/licenses/>.
*/
// Vector function.
/* This file defines interface for functions taking a vector as an input and
returning a vector (with a different size) as an output. We are also
introducing a parameter signalling; it is a boolean vector which tracks
parameters which were changed from the previous call. The VectorFunction
implementation can exploit this information and evaluate the function more
efficiently. The information can be completely ignored.
From the signalling reason, and from other reasons, the function evaluation
is not const. */
#ifndef VECTOR_FUNCTION_H
#define VECTOR_FUNCTION_H
#include "Vector.hh"
#include "GeneralMatrix.hh"
#include <vector>
#include <memory>
/* This is a simple class representing a vector of booleans. The items night be
retrieved or changed, or can be set true after some point. This is useful
when we multiply the vector with lower triangular matrix.
true means that a parameter was changed. */
class ParameterSignal
{
protected:
std::vector<bool> data;
public:
ParameterSignal(int n);
ParameterSignal(const ParameterSignal &sig) = default;
~ParameterSignal() = default;
void signalAfter(int l);
bool
operator[](int i) const
{
return data[i];
}
std::vector<bool>::reference
operator[](int i)
{
return data[i];
}
};
/* This is the abstract class for vector function. At this level of abstraction
we only need to know size of input vector and a size of output vector.
The important thing here is a clone method, we will need to make hard copies
of vector functions since the evaluations are not const. The hardcopies
apply for parallelization. */
class VectorFunction
{
protected:
int in_dim;
int out_dim;
public:
VectorFunction(int idim, int odim)
: in_dim(idim), out_dim(odim)
{
}
VectorFunction(const VectorFunction &func) = default;
virtual ~VectorFunction() = default;
virtual std::unique_ptr<VectorFunction> clone() const = 0;
virtual void eval(const Vector &point, const ParameterSignal &sig, Vector &out) = 0;
int
indim() const
{
return in_dim;
}
int
outdim() const
{
return out_dim;
}
};
/* This makes n copies of VectorFunction. The first constructor make exactly
n new copies, the second constructor copies only the pointer to the first
and others are hard (real) copies.
The class is useful for making a given number of copies at once, and this
set can be reused many times if we need mupliple copis of the function (for
example for paralelizing the code). */
class VectorFunctionSet
{
private:
// Stores the hard copies made by the class
std::vector<std::unique_ptr<VectorFunction>> func_copies;
protected:
std::vector<VectorFunction *> funcs;
public:
VectorFunctionSet(const VectorFunction &f, int n);
VectorFunctionSet(VectorFunction &f, int n);
~VectorFunctionSet() = default;
VectorFunction &
getFunc(int i)
{
return *(funcs[i]);
}
int
getNum() const
{
return funcs.size();
}
};
/* This class wraps another VectorFunction to allow integration of a function
through normally distributed inputs. Namely, if one wants to integrate
1
f(x)e^{½x|Σ|¹x}dx
{(2π)|Σ|}
then if we write Σ=AA and x=2·Ay, we get integral
1 1
f(2·Ay)e^{½yy} (2)|A|dy = f(2·Ay)e^{½yy}dy
{(2π)|Σ|} (π)
which means that a given function f we have to wrap to yield a function
g(y) = 1/(π) f(2·Ay).
This is exactly what this class is doing. This transformation is useful
since the Gauss-Hermite points and weights are defined for weighting
function e^{y²}, so this transformation allows using Gauss-Hermite
quadratures seemlessly in a context of integration through normally
distributed inputs.
The class maintains a pointer to the function f. When the object is
constructed by the first constructor, the f is assumed to be owned by the
caller. If the object of this class is copied, then f is copied and hence
stored in a std::unique_ptr. The second constructor takes a smart pointer to
the function and in that case the class takes ownership of f. */
class GaussConverterFunction : public VectorFunction
{
private:
std::unique_ptr<VectorFunction> func_storage;
protected:
VectorFunction *func;
GeneralMatrix A;
double multiplier;
public:
GaussConverterFunction(VectorFunction &f, const GeneralMatrix &vcov);
GaussConverterFunction(std::unique_ptr<VectorFunction> f, const GeneralMatrix &vcov);
GaussConverterFunction(const GaussConverterFunction &f);
~GaussConverterFunction() override = default;
std::unique_ptr<VectorFunction>
clone() const override
{
return std::make_unique<GaussConverterFunction>(*this);
}
void eval(const Vector &point, const ParameterSignal &sig, Vector &out) override;
private:
double calcMultiplier() const;
void calcCholeskyFactor(const GeneralMatrix &vcov);
};
#endif

View File

@ -1,6 +1,6 @@
/*
* Copyright © 2004 Ondra Kamenik
* Copyright © 2019 Dynare Team
* Copyright © 2019-2023 Dynare Team
*
* This file is part of Dynare.
*
@ -21,14 +21,9 @@
#include "kord_exception.hh"
#include "decision_rule.hh"
#include "dynamic_model.hh"
#include "seed_generator.hh"
#include "SymSchurDecomp.hh"
#include <dynlapack.h>
#include <limits>
#include <utility>
#include <memory>
// FoldDecisionRule conversion from UnfoldDecisionRule
@ -48,539 +43,3 @@ UnfoldDecisionRule::UnfoldDecisionRule(const FoldDecisionRule &fdr)
for (const auto &it : fdr)
insert(std::make_unique<ctraits<Storage::unfold>::Ttensym>(*(it.second)));
}
/* This runs simulations with an output to journal file. Note that we
report how many simulations had to be thrown out due to Nan or Inf. */
void
SimResults::simulate(int num_sim, const DecisionRule &dr, const Vector &start,
const TwoDMatrix &vcov, Journal &journal)
{
JournalRecordPair paa(journal);
paa << "Performing " << num_sim << " stochastic simulations for "
<< num_per << " periods burning " << num_burn << " initial periods" << endrec;
simulate(num_sim, dr, start, vcov);
int thrown = num_sim - data.size();
if (thrown > 0)
{
JournalRecord rec(journal);
rec << "I had to throw " << thrown << " simulations away due to Nan or Inf" << endrec;
}
}
/* This runs a given number of simulations by creating
SimulationWorker for each simulation and inserting them to the
thread group. */
void
SimResults::simulate(int num_sim, const DecisionRule &dr, const Vector &start,
const TwoDMatrix &vcov)
{
std::vector<RandomShockRealization> rsrs;
rsrs.reserve(num_sim);
sthread::detach_thread_group gr;
for (int i = 0; i < num_sim; i++)
{
RandomShockRealization sr(vcov, seed_generator::get_new_seed());
rsrs.push_back(sr);
gr.insert(std::make_unique<SimulationWorker>(*this, dr, DecisionRule::emethod::horner,
num_per+num_burn, start, rsrs.back()));
}
gr.run();
}
/* This adds the data with the realized shocks. It takes only periods
which are not to be burnt. If the data is not finite, the both data
and shocks are thrown away. */
bool
SimResults::addDataSet(const TwoDMatrix &d, const ExplicitShockRealization &sr, const ConstVector &st)
{
KORD_RAISE_IF(d.nrows() != num_y,
"Incompatible number of rows for SimResults::addDataSets");
KORD_RAISE_IF(d.ncols() != num_per+num_burn,
"Incompatible number of cols for SimResults::addDataSets");
bool ret = false;
if (d.isFinite())
{
data.emplace_back(d, num_burn, num_per);
shocks.emplace_back(ConstTwoDMatrix(sr.getShocks(), num_burn, num_per));
if (num_burn == 0)
start.emplace_back(st);
else
start.emplace_back(d.getCol(num_burn-1));
ret = true;
}
return ret;
}
void
SimResults::writeMat(const std::string &base, const std::string &lname) const
{
std::string matfile_name = base + ".mat";
mat_t *matfd = Mat_Create(matfile_name.c_str(), nullptr);
if (matfd)
{
writeMat(matfd, lname);
Mat_Close(matfd);
}
}
/* This save the results as matrices with given prefix and with index
appended. If there is only one matrix, the index is not appended. */
void
SimResults::writeMat(mat_t *fd, const std::string &lname) const
{
for (int i = 0; i < getNumSets(); i++)
{
std::string tmp = lname + "_data";
if (getNumSets() > 1)
tmp += std::to_string(i+1);
data[i].writeMat(fd, tmp);
}
}
void
SimResultsStats::simulate(int num_sim, const DecisionRule &dr,
const Vector &start,
const TwoDMatrix &vcov, Journal &journal)
{
SimResults::simulate(num_sim, dr, start, vcov, journal);
{
JournalRecordPair paa(journal);
paa << "Calculating means from the simulations." << endrec;
calcMean();
}
{
JournalRecordPair paa(journal);
paa << "Calculating covariances from the simulations." << endrec;
calcVcov();
}
}
/* Here we do not save the data itself, we save only mean and vcov. */
void
SimResultsStats::writeMat(mat_t *fd, const std::string &lname) const
{
ConstTwoDMatrix(num_y, 1, mean).writeMat(fd, lname + "_mean");;
vcov.writeMat(fd, lname + "_vcov");
}
void
SimResultsStats::calcMean()
{
mean.zeros();
if (data.size()*num_per > 0)
{
double mult = 1.0/data.size()/num_per;
for (const auto &i : data)
{
for (int j = 0; j < num_per; j++)
{
ConstVector col{i.getCol(j)};
mean.add(mult, col);
}
}
}
}
void
SimResultsStats::calcVcov()
{
if (data.size()*num_per > 1)
{
vcov.zeros();
double mult = 1.0/(data.size()*num_per - 1);
for (const auto &d : data)
for (int j = 0; j < num_per; j++)
for (int m = 0; m < num_y; m++)
for (int n = m; n < num_y; n++)
{
double s = (d.get(m, j)-mean[m])*(d.get(n, j)-mean[n]);
vcov.get(m, n) += mult*s;
if (m != n)
vcov.get(n, m) += mult*s;
}
}
else
vcov.infs();
}
void
SimResultsDynamicStats::simulate(int num_sim, const DecisionRule &dr,
const Vector &start,
const TwoDMatrix &vcov, Journal &journal)
{
SimResults::simulate(num_sim, dr, start, vcov, journal);
{
JournalRecordPair paa(journal);
paa << "Calculating means of the conditional simulations." << endrec;
calcMean();
}
{
JournalRecordPair paa(journal);
paa << "Calculating variances of the conditional simulations." << endrec;
calcVariance();
}
}
void
SimResultsDynamicStats::writeMat(mat_t *fd, const std::string &lname) const
{
mean.writeMat(fd, lname + "_cond_mean");
variance.writeMat(fd, lname + "_cond_variance");
}
void
SimResultsDynamicStats::calcMean()
{
mean.zeros();
if (data.size() > 0)
{
double mult = 1.0/data.size();
for (int j = 0; j < num_per; j++)
{
Vector meanj{mean.getCol(j)};
for (const auto &i : data)
{
ConstVector col{i.getCol(j)};
meanj.add(mult, col);
}
}
}
}
void
SimResultsDynamicStats::calcVariance()
{
if (data.size() > 1)
{
variance.zeros();
double mult = 1.0/(data.size()-1);
for (int j = 0; j < num_per; j++)
{
ConstVector meanj{mean.getCol(j)};
Vector varj{variance.getCol(j)};
for (const auto &i : data)
{
Vector col{i.getCol(j)};
col.add(-1.0, meanj);
for (int k = 0; k < col.length(); k++)
col[k] = col[k]*col[k];
varj.add(mult, col);
}
}
}
else
variance.infs();
}
void
SimResultsIRF::simulate(const DecisionRule &dr, Journal &journal)
{
JournalRecordPair paa(journal);
paa << "Performing " << control.getNumSets() << " IRF simulations for "
<< num_per << " periods; shock=" << ishock << ", impulse=" << imp << endrec;
simulate(dr);
int thrown = control.getNumSets() - data.size();
if (thrown > 0)
{
JournalRecord rec(journal);
rec << "I had to throw " << thrown
<< " simulations away due to Nan or Inf" << endrec;
}
calcMeans();
calcVariances();
}
void
SimResultsIRF::simulate(const DecisionRule &dr)
{
sthread::detach_thread_group gr;
for (int idata = 0; idata < control.getNumSets(); idata++)
gr.insert(std::make_unique<SimulationIRFWorker>(*this, dr, DecisionRule::emethod::horner,
num_per, idata, ishock, imp));
gr.run();
}
void
SimResultsIRF::calcMeans()
{
means.zeros();
if (data.size() > 0)
{
for (const auto &i : data)
means.add(1.0, i);
means.mult(1.0/data.size());
}
}
void
SimResultsIRF::calcVariances()
{
if (data.size() > 1)
{
variances.zeros();
for (const auto &i : data)
{
TwoDMatrix d(i);
d.add(-1.0, means);
for (int j = 0; j < d.nrows(); j++)
for (int k = 0; k < d.ncols(); k++)
variances.get(j, k) += d.get(j, k)*d.get(j, k);
d.mult(1.0/(data.size()-1));
}
}
else
variances.infs();
}
void
SimResultsIRF::writeMat(mat_t *fd, const std::string &lname) const
{
means.writeMat(fd, lname + "_mean");
variances.writeMat(fd, lname + "_var");
}
void
RTSimResultsStats::simulate(int num_sim, const DecisionRule &dr, const Vector &start,
const TwoDMatrix &v, Journal &journal)
{
JournalRecordPair paa(journal);
paa << "Performing " << num_sim << " real-time stochastic simulations for "
<< num_per << " periods" << endrec;
simulate(num_sim, dr, start, v);
mean = nc.getMean();
mean.add(1.0, dr.getSteady());
nc.getVariance(vcov);
if (thrown_periods > 0)
{
JournalRecord rec(journal);
rec << "I had to throw " << thrown_periods << " periods away due to Nan or Inf" << endrec;
JournalRecord rec1(journal);
rec1 << "This affected " << incomplete_simulations << " out of "
<< num_sim << " simulations" << endrec;
}
}
void
RTSimResultsStats::simulate(int num_sim, const DecisionRule &dr, const Vector &start,
const TwoDMatrix &vcov)
{
std::vector<RandomShockRealization> rsrs;
rsrs.reserve(num_sim);
sthread::detach_thread_group gr;
for (int i = 0; i < num_sim; i++)
{
RandomShockRealization sr(vcov, seed_generator::get_new_seed());
rsrs.push_back(sr);
gr.insert(std::make_unique<RTSimulationWorker>(*this, dr, DecisionRule::emethod::horner,
num_per, start, rsrs.back()));
}
gr.run();
}
void
RTSimResultsStats::writeMat(mat_t *fd, const std::string &lname)
{
ConstTwoDMatrix(nc.getDim(), 1, mean).writeMat(fd, lname + "_rt_mean");
vcov.writeMat(fd, lname + "_rt_vcov");
}
IRFResults::IRFResults(const DynamicModel &mod, const DecisionRule &dr,
const SimResults &control, std::vector<int> ili,
Journal &journal)
: model(mod), irf_list_ind(std::move(ili))
{
int num_per = control.getNumPer();
JournalRecordPair pa(journal);
pa << "Calculating IRFs against control for " << static_cast<int>(irf_list_ind.size()) << " shocks and for "
<< num_per << " periods" << endrec;
const TwoDMatrix &vcov = mod.getVcov();
for (int ishock : irf_list_ind)
{
double stderror = sqrt(vcov.get(ishock, ishock));
irf_res.emplace_back(control, model.numeq(), num_per,
ishock, stderror);
irf_res.emplace_back(control, model.numeq(), num_per,
ishock, -stderror);
}
for (unsigned int ii = 0; ii < irf_list_ind.size(); ii++)
{
irf_res[2*ii].simulate(dr, journal);
irf_res[2*ii+1].simulate(dr, journal);
}
}
void
IRFResults::writeMat(mat_t *fd, const std::string &prefix) const
{
for (unsigned int i = 0; i < irf_list_ind.size(); i++)
{
int ishock = irf_list_ind[i];
auto shockname = model.getExogNames().getName(ishock);
irf_res[2*i].writeMat(fd, prefix + "_irfp_" + shockname);
irf_res[2*i+1].writeMat(fd, prefix + "_irfm_" + shockname);
}
}
void
SimulationWorker::operator()(std::mutex &mut)
{
ExplicitShockRealization esr(sr, np);
TwoDMatrix m{dr.simulate(em, np, st, esr)};
{
std::unique_lock<std::mutex> lk{mut};
res.addDataSet(m, esr, st);
}
}
/* Here we create a new instance of ExplicitShockRealization of the
corresponding control, add the impulse, and simulate. */
void
SimulationIRFWorker::operator()(std::mutex &mut)
{
ExplicitShockRealization esr(res.control.getShocks(idata));
esr.addToShock(ishock, 0, imp);
TwoDMatrix m{dr.simulate(em, np, res.control.getStart(idata), esr)};
m.add(-1.0, res.control.getData(idata));
{
std::unique_lock<std::mutex> lk{mut};
res.addDataSet(m, esr, res.control.getStart(idata));
}
}
void
RTSimulationWorker::operator()(std::mutex &mut)
{
NormalConj nc(res.nc.getDim());
const PartitionY &ypart = dr.getYPart();
int nu = dr.nexog();
const Vector &ysteady = dr.getSteady();
// initialize vectors and subvectors for simulation
Vector dyu(ypart.nys()+nu);
ConstVector ystart_pred(ystart, ypart.nstat, ypart.nys());
ConstVector ysteady_pred(ysteady, ypart.nstat, ypart.nys());
Vector dy(dyu, 0, ypart.nys());
Vector u(dyu, ypart.nys(), nu);
Vector y(nc.getDim());
ConstVector ypred(y, ypart.nstat, ypart.nys());
// simulate the first real-time period
int ip = 0;
dy = ystart_pred;
dy.add(-1.0, ysteady_pred);
sr.get(ip, u);
dr.eval(em, y, dyu);
if (ip >= res.num_burn)
nc.update(y);
// simulate other real-time periods
while (y.isFinite() && ip < res.num_burn + res.num_per)
{
ip++;
dy = ypred;
sr.get(ip, u);
dr.eval(em, y, dyu);
if (ip >= res.num_burn)
nc.update(y);
}
{
std::unique_lock<std::mutex> lk{mut};
res.nc.update(nc);
if (res.num_per-ip > 0)
{
res.incomplete_simulations++;
res.thrown_periods += res.num_per-ip;
}
}
}
/* This calculates factorization FFᵀ=V in the Cholesky way. It does
not work for semidefinite matrices. */
void
RandomShockRealization::choleskyFactor(const ConstTwoDMatrix &v)
{
factor = v;
lapack_int rows = factor.nrows(), lda = factor.getLD();
for (int i = 0; i < rows; i++)
for (int j = i+1; j < rows; j++)
factor.get(i, j) = 0.0;
lapack_int info;
dpotrf("L", &rows, factor.base(), &lda, &info);
KORD_RAISE_IF(info != 0,
"Info!=0 in RandomShockRealization::choleskyFactor");
}
/* This calculates FFᵀ=V factorization by symmetric Schur
decomposition. It works for semidefinite matrices. */
void
RandomShockRealization::schurFactor(const ConstTwoDMatrix &v)
{
SymSchurDecomp(v).getFactor(factor);
}
void
RandomShockRealization::get(int n, Vector &out)
{
KORD_RAISE_IF(out.length() != numShocks(),
"Wrong length of out vector in RandomShockRealization::get");
Vector d(out.length());
for (int i = 0; i < d.length(); i++)
d[i] = dis(mtwister);
out.zeros();
factor.multaVec(out, ConstVector(d));
}
ExplicitShockRealization::ExplicitShockRealization(ShockRealization &sr,
int num_per)
: shocks(sr.numShocks(), num_per)
{
for (int j = 0; j < num_per; j++)
{
Vector jcol{shocks.getCol(j)};
sr.get(j, jcol);
}
}
void
ExplicitShockRealization::get(int n, Vector &out)
{
KORD_RAISE_IF(out.length() != numShocks(),
"Wrong length of out vector in ExplicitShockRealization::get");
int i = n % shocks.ncols();
ConstVector icol{shocks.getCol(i)};
out = icol;
}
void
ExplicitShockRealization::addToShock(int ishock, int iper, double val)
{
KORD_RAISE_IF(ishock < 0 || ishock > numShocks(),
"Wrong index of shock in ExplicitShockRealization::addToShock");
int j = iper % shocks.ncols();
shocks.get(ishock, j) += val;
}
void
GenShockRealization::get(int n, Vector &out)
{
KORD_RAISE_IF(out.length() != numShocks(),
"Wrong length of out vector in GenShockRealization::get");
ExplicitShockRealization::get(n, out);
Vector r(numShocks());
RandomShockRealization::get(n, r);
for (int j = 0; j < numShocks(); j++)
if (!std::isfinite(out[j]))
out[j] = r[j];
}

View File

@ -1,6 +1,6 @@
/*
* Copyright © 2004 Ondra Kamenik
* Copyright © 2019-2021 Dynare Team
* Copyright © 2019-2023 Dynare Team
*
* This file is part of Dynare.
*
@ -35,27 +35,13 @@
#ifndef DECISION_RULE_H
#define DECISION_RULE_H
#include <matio.h>
#include "kord_exception.hh"
#include "korder.hh"
#include "normal_conjugate.hh"
#include <memory>
#include <random>
#include <string>
/* This is a general interface to a shock realizations. The interface has only
one method returning the shock realizations at the given time. This method
is not constant, since it may change a state of the object. */
class ShockRealization
{
public:
virtual ~ShockRealization() = default;
virtual void get(int n, Vector &out) = 0;
virtual int numShocks() const = 0;
};
/* This class is an abstract interface to decision rule. Its main purpose is to
define a common interface for simulation of a decision rule. We need only a
simulate, evaluate, centralized clone and output method. */
@ -65,10 +51,6 @@ public:
enum class emethod { horner, trad };
virtual ~DecisionRule() = default;
// simulates the rule for a given realization of the shocks
virtual TwoDMatrix simulate(emethod em, int np, const ConstVector &ystart,
ShockRealization &sr) const = 0;
/* primitive evaluation (it takes a vector of state variables (predetermined,
both and shocks) and returns the next period variables. Both input and
output are in deviations from the rule's steady. */
@ -79,9 +61,6 @@ public:
virtual void evaluate(emethod em, Vector &out, const ConstVector &ys,
const ConstVector &u) const = 0;
// writes the decision rule to the MAT file
virtual void writeMat(mat_t *fd, const std::string &prefix) const = 0;
/* returns a new copy of the decision rule, which is centralized about
provided fix-point */
virtual std::unique_ptr<DecisionRule> centralizedClone(const Vector &fixpoint) const = 0;
@ -167,12 +146,9 @@ public:
{
return ysteady;
}
TwoDMatrix simulate(emethod em, int np, const ConstVector &ystart,
ShockRealization &sr) const override;
void evaluate(emethod em, Vector &out, const ConstVector &ys,
const ConstVector &u) const override;
std::unique_ptr<DecisionRule> centralizedClone(const Vector &fixpoint) const override;
void writeMat(mat_t *fd, const std::string &prefix) const override;
int
nexog() const override
@ -352,79 +328,6 @@ DecisionRuleImpl<t>::centralize(const DecisionRuleImpl &dr)
}
}
/* Here we evaluate repeatedly the polynomial storing results in the created
matrix. For exogenous shocks, we use ShockRealization class, for
predetermined variables, we use ystart as the first state. The ystart
vector is required to be all state variables ypart.ny(), although only the
predetermined part of ystart is used.
We simulate in terms of Δy, this is, at the beginning the ysteady is
canceled from ystart, we simulate, and at the end ysteady is added to
all columns of the result. */
template<Storage t>
TwoDMatrix
DecisionRuleImpl<t>::simulate(emethod em, int np, const ConstVector &ystart,
ShockRealization &sr) const
{
KORD_RAISE_IF(ysteady.length() != ystart.length(),
"Start and steady lengths differ in DecisionRuleImpl::simulate");
TwoDMatrix res(ypart.ny(), np);
// initialize vectors and subvectors for simulation
/* Here allocate the stack vector (Δy*,u), define the subvectors dy, and
u, then we pickup predetermined parts of ystart and ysteady. */
Vector dyu(ypart.nys()+nu);
ConstVector ystart_pred(ystart, ypart.nstat, ypart.nys());
ConstVector ysteady_pred(ysteady, ypart.nstat, ypart.nys());
Vector dy(dyu, 0, ypart.nys());
Vector u(dyu, ypart.nys(), nu);
// perform the first step of simulation
/* We cancel ysteady from ystart, get realization to u, and evaluate
the polynomial. */
dy = ystart_pred;
dy.add(-1.0, ysteady_pred);
sr.get(0, u);
Vector out{res.getCol(0)};
eval(em, out, dyu);
// perform all other steps of simulations
/* Also clear. If the result at some period is not finite, we pad the rest of
the matrix with zeros. */
int i = 1;
while (i < np)
{
ConstVector ym{res.getCol(i-1)};
ConstVector dym(ym, ypart.nstat, ypart.nys());
dy = dym;
sr.get(i, u);
Vector out{res.getCol(i)};
eval(em, out, dyu);
if (!out.isFinite())
{
if (i+1 < np)
{
TwoDMatrix rest(res, i+1, np-i-1);
rest.zeros();
}
break;
}
i++;
}
// add the steady state to columns of res
/* Even clearer. We add the steady state to the numbers computed above and
leave the padded columns to zero. */
for (int j = 0; j < i; j++)
{
Vector col{res.getCol(j)};
col.add(1.0, ysteady);
}
return res;
}
/* This is one period evaluation of the decision rule. The simulation is a
sequence of repeated one period evaluations with a difference, that the
steady state (fix point) is cancelled and added once. Hence we have two
@ -473,18 +376,6 @@ DecisionRuleImpl<t>::eval(emethod em, Vector &out, const ConstVector &v) const
_Tpol::evalTrad(out, v);
}
/* Write the decision rule and steady state to the MAT file. */
template<Storage t>
void
DecisionRuleImpl<t>::writeMat(mat_t *fd, const std::string &prefix) const
{
ctraits<t>::Tpol::writeMat(fd, prefix);
TwoDMatrix dum(ysteady.length(), 1);
dum.getData() = ysteady;
ConstTwoDMatrix(dum).writeMat(fd, prefix + "_ss");
}
/* This is exactly the same as DecisionRuleImpl<Storage::fold>. The only
difference is that we have a conversion from UnfoldDecisionRule, which is
exactly DecisionRuleImpl<Storage::unfold>. */
@ -786,354 +677,4 @@ DRFixPoint<t>::calcFixPoint(emethod em, Vector &out)
return converged;
}
/* This is a basically a number of matrices of the same dimensions, which can
be obtained as simulation results from a given decision rule and shock
realizations. We also store the realizations of shocks and the starting
point of each simulation. */
class ExplicitShockRealization;
class SimResults
{
protected:
int num_y;
int num_per;
int num_burn;
std::vector<TwoDMatrix> data;
std::vector<ExplicitShockRealization> shocks;
std::vector<ConstVector> start;
public:
SimResults(int ny, int nper, int nburn = 0)
: num_y(ny), num_per(nper), num_burn(nburn)
{
}
void simulate(int num_sim, const DecisionRule &dr, const Vector &start,
const TwoDMatrix &vcov, Journal &journal);
void simulate(int num_sim, const DecisionRule &dr, const Vector &start,
const TwoDMatrix &vcov);
int
getNumPer() const
{
return num_per;
}
int
getNumBurn() const
{
return num_burn;
}
int
getNumSets() const
{
return static_cast<int>(data.size());
}
const TwoDMatrix &
getData(int i) const
{
return data[i];
}
const ExplicitShockRealization &
getShocks(int i) const
{
return shocks[i];
}
const ConstVector &
getStart(int i) const
{
return start[i];
}
bool addDataSet(const TwoDMatrix &d, const ExplicitShockRealization &sr, const ConstVector &st);
void writeMat(const std::string &base, const std::string &lname) const;
void writeMat(mat_t *fd, const std::string &lname) const;
};
/* This does the same as SimResults plus it calculates means and covariances of
the simulated data. */
class SimResultsStats : public SimResults
{
protected:
Vector mean;
TwoDMatrix vcov;
public:
SimResultsStats(int ny, int nper, int nburn = 0)
: SimResults(ny, nper, nburn), mean(ny), vcov(ny, ny)
{
}
void simulate(int num_sim, const DecisionRule &dr, const Vector &start,
const TwoDMatrix &vcov, Journal &journal);
void writeMat(mat_t *fd, const std::string &lname) const;
protected:
void calcMean();
void calcVcov();
};
/* This does the similar thing as SimResultsStats but the statistics are not
calculated over all periods but only within each period. Then we do not
calculate covariances with periods but only variances. */
class SimResultsDynamicStats : public SimResults
{
protected:
TwoDMatrix mean;
TwoDMatrix variance;
public:
SimResultsDynamicStats(int ny, int nper, int nburn = 0)
: SimResults(ny, nper, nburn), mean(ny, nper), variance(ny, nper)
{
}
void simulate(int num_sim, const DecisionRule &dr, const Vector &start,
const TwoDMatrix &vcov, Journal &journal);
void writeMat(mat_t *fd, const std::string &lname) const;
protected:
void calcMean();
void calcVariance();
};
/* This goes through control simulation results, and for each control it adds a
given impulse to a given shock and runs a simulation. The control simulation
is then cancelled and the result is stored. After that these results are
averaged with variances calculated.
The means and the variances are then written to the MAT file. */
class SimulationIRFWorker;
class SimResultsIRF : public SimResults
{
friend class SimulationIRFWorker;
protected:
const SimResults &control;
int ishock;
double imp;
TwoDMatrix means;
TwoDMatrix variances;
public:
SimResultsIRF(const SimResults &cntl, int ny, int nper, int i, double impulse)
: SimResults(ny, nper, 0), control(cntl),
ishock(i), imp(impulse),
means(ny, nper), variances(ny, nper)
{
}
void simulate(const DecisionRule &dr, Journal &journal);
void simulate(const DecisionRule &dr);
void writeMat(mat_t *fd, const std::string &lname) const;
protected:
void calcMeans();
void calcVariances();
};
/* This simulates and gathers all statistics from the real time simulations. In
the simulate() method, it runs RTSimulationWorkers which accummulate
information from their own estimates. The estimation is done by means of
NormalConj class, which is a conjugate family of densities for normal
distibutions. */
class RTSimulationWorker;
class RTSimResultsStats
{
friend class RTSimulationWorker;
protected:
Vector mean;
TwoDMatrix vcov;
int num_per;
int num_burn;
NormalConj nc;
int incomplete_simulations;
int thrown_periods;
public:
RTSimResultsStats(int ny, int nper, int nburn = 0)
: mean(ny), vcov(ny, ny),
num_per(nper), num_burn(nburn), nc(ny),
incomplete_simulations(0), thrown_periods(0)
{
}
void simulate(int num_sim, const DecisionRule &dr, const Vector &start,
const TwoDMatrix &vcov, Journal &journal);
void simulate(int num_sim, const DecisionRule &dr, const Vector &start,
const TwoDMatrix &vcov);
void writeMat(mat_t *fd, const std::string &lname);
};
/* For each shock, this simulates plus and minus impulse. The class maintains a
vector of simulation results, each gets a particular shock and sign
(positive/negative). The results of type SimResultsIRF are stored in a
vector so that even ones are positive, odd ones are negative.
The constructor takes a reference to the control simulations, which must be
finished before the constructor is called. The control simulations are
passed to all SimResultsIRFs.
The constructor also takes the vector of indices of exogenous variables
(ili) for which the IRFs are generated. The list is kept (as
irf_list_ind) for other methods. */
class DynamicModel;
class IRFResults
{
std::vector<SimResultsIRF> irf_res;
const DynamicModel &model;
std::vector<int> irf_list_ind;
public:
IRFResults(const DynamicModel &mod, const DecisionRule &dr,
const SimResults &control, std::vector<int> ili,
Journal &journal);
void writeMat(mat_t *fd, const std::string &prefix) const;
};
/* This worker simulates the given decision rule and inserts the result to
SimResults. */
class SimulationWorker : public sthread::detach_thread
{
protected:
SimResults &res;
const DecisionRule &dr;
DecisionRule::emethod em;
int np;
const Vector &st;
ShockRealization &sr;
public:
SimulationWorker(SimResults &sim_res,
const DecisionRule &dec_rule,
DecisionRule::emethod emet, int num_per,
const Vector &start, ShockRealization &shock_r)
: res(sim_res), dr(dec_rule), em(emet), np(num_per), st(start), sr(shock_r)
{
}
void operator()(std::mutex &mut) override;
};
/* This worker simulates a given impulse imp to a given shock ishock based
on a given control simulation with index idata. The control simulations
are contained in SimResultsIRF which is passed to the constructor. */
class SimulationIRFWorker : public sthread::detach_thread
{
SimResultsIRF &res;
const DecisionRule &dr;
DecisionRule::emethod em;
int np;
int idata;
int ishock;
double imp;
public:
SimulationIRFWorker(SimResultsIRF &sim_res,
const DecisionRule &dec_rule,
DecisionRule::emethod emet, int num_per,
int id, int ishck, double impulse)
: res(sim_res), dr(dec_rule), em(emet), np(num_per),
idata(id), ishock(ishck), imp(impulse)
{
}
void operator()(std::mutex &mut) override;
};
/* This class does the real time simulation job for RTSimResultsStats. It
simulates the model period by period. It accummulates the information in
RTSimResultsStats::nc. If NaN or Inf is observed, it ends the simulation
and adds to the thrown_periods of RTSimResultsStats. */
class RTSimulationWorker : public sthread::detach_thread
{
protected:
RTSimResultsStats &res;
const DecisionRule &dr;
DecisionRule::emethod em;
int np;
const Vector &ystart;
ShockRealization &sr;
public:
RTSimulationWorker(RTSimResultsStats &sim_res,
const DecisionRule &dec_rule,
DecisionRule::emethod emet, int num_per,
const Vector &start, ShockRealization &shock_r)
: res(sim_res), dr(dec_rule), em(emet), np(num_per), ystart(start), sr(shock_r)
{
}
void operator()(std::mutex &mut) override;
};
/* This class generates draws from Gaussian distribution with zero mean and the
given variance-covariance matrix. It stores the factor of vcov V matrix,
yielding FF = V. */
class RandomShockRealization : virtual public ShockRealization
{
protected:
std::mt19937 mtwister;
std::normal_distribution<> dis;
TwoDMatrix factor;
public:
RandomShockRealization(const ConstTwoDMatrix &v, decltype(mtwister)::result_type iseed)
: mtwister(iseed), factor(v.nrows(), v.nrows())
{
schurFactor(v);
}
void get(int n, Vector &out) override;
int
numShocks() const override
{
return factor.nrows();
}
protected:
void choleskyFactor(const ConstTwoDMatrix &v);
void schurFactor(const ConstTwoDMatrix &v);
};
/* This is just a matrix of finite numbers. It can be constructed from any
ShockRealization with a given number of periods. */
class ExplicitShockRealization : virtual public ShockRealization
{
TwoDMatrix shocks;
public:
explicit ExplicitShockRealization(const ConstTwoDMatrix &sh)
: shocks(sh)
{
}
ExplicitShockRealization(ShockRealization &sr, int num_per);
void get(int n, Vector &out) override;
int
numShocks() const override
{
return shocks.nrows();
}
const TwoDMatrix &
getShocks() const
{
return shocks;
}
void addToShock(int ishock, int iper, double val);
void
print() const
{
shocks.print();
}
};
/* This represents a user given shock realization. The first matrix of the
constructor is a covariance matrix of shocks, the second matrix is a
rectangular matrix, where columns correspond to periods, rows to shocks. If
an element of the matrix is NaN or ±, then the random shock is taken
instead of that element.
In this way it is a generalization of both RandomShockRealization and
ExplicitShockRealization. */
class GenShockRealization : public RandomShockRealization, public ExplicitShockRealization
{
public:
GenShockRealization(const ConstTwoDMatrix &v, const ConstTwoDMatrix &sh, int seed)
: RandomShockRealization(v, seed), ExplicitShockRealization(sh)
{
KORD_RAISE_IF(sh.nrows() != v.nrows() || v.nrows() != v.ncols(),
"Wrong dimension of input matrix in GenShockRealization constructor");
}
void get(int n, Vector &out) override;
int
numShocks() const override
{
return RandomShockRealization::numShocks();
}
};
#endif

View File

@ -29,44 +29,3 @@ NameList::print() const
for (int i = 0; i < getNum(); i++)
std::cout << getName(i) << '\n';
}
void
NameList::writeMat(mat_t *fd, const std::string &vname) const
{
int maxlen = 0;
for (int i = 0; i < getNum(); i++)
maxlen = std::max(maxlen, static_cast<int>(getName(i).size()));
if (maxlen == 0)
return;
auto m = std::make_unique<char[]>(getNum()*maxlen);
for (int i = 0; i < getNum(); i++)
for (int j = 0; j < maxlen; j++)
if (j < static_cast<int>(getName(i).size()))
m[j*getNum()+i] = getName(i)[j];
else
m[j*getNum()+i] = ' ';
size_t dims[2];
dims[0] = getNum();
dims[1] = maxlen;
matvar_t *v = Mat_VarCreate(vname.c_str(), MAT_C_CHAR, MAT_T_UINT8, 2, dims, m.get(), 0);
Mat_VarWrite(fd, v, MAT_COMPRESSION_NONE);
Mat_VarFree(v);
}
void
NameList::writeMatIndices(mat_t *fd, const std::string &prefix) const
{
TwoDMatrix aux(1, 1);
for (int i = 0; i < getNum(); i++)
{
aux.get(0, 0) = i+1;
aux.writeMat(fd, prefix + "_i_" + getName(i));
}
}

View File

@ -48,8 +48,6 @@ public:
virtual int getNum() const = 0;
virtual const std::string &getName(int i) const = 0;
void print() const;
void writeMat(mat_t *fd, const std::string &vname) const;
void writeMatIndices(mat_t *fd, const std::string &prefix) const;
};
/* This is the interface to an information on a generic DSGE model. It is

View File

@ -1,384 +0,0 @@
/*
* Copyright © 2005 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 <https://www.gnu.org/licenses/>.
*/
#include "SymSchurDecomp.hh"
#include "global_check.hh"
#include "seed_generator.hh"
#include "smolyak.hh"
#include "product.hh"
#include "quasi_mcarlo.hh"
#include <utility>
#include <cmath>
/* Here we just set a reference to the approximation, and create a new
DynamicModel. */
ResidFunction::ResidFunction(const Approximation &app)
: VectorFunction(app.getModel().nexog(), app.getModel().numeq()), approx(app),
model(app.getModel().clone())
{
}
ResidFunction::ResidFunction(const ResidFunction &rf)
: VectorFunction(rf), approx(rf.approx), model(rf.model->clone())
{
if (rf.yplus)
yplus = std::make_unique<Vector>(*(rf.yplus));
if (rf.ystar)
ystar = std::make_unique<Vector>(*(rf.ystar));
if (rf.u)
u = std::make_unique<Vector>(*(rf.u));
if (rf.hss)
hss = std::make_unique<FTensorPolynomial>(*(rf.hss));
}
/* This sets y* and u. We have to create ystar, u, yplus and hss. */
void
ResidFunction::setYU(const ConstVector &ys, const ConstVector &xx)
{
ystar = std::make_unique<Vector>(ys);
u = std::make_unique<Vector>(xx);
yplus = std::make_unique<Vector>(model->numeq());
approx.getFoldDecisionRule().evaluate(DecisionRule::emethod::horner,
*yplus, *ystar, *u);
// make a tensor polynomial of in-place subtensors from decision rule
/* Note that the non-const polynomial will be used for a construction of
hss and will be used in a const context. So this const cast is safe.
Note, that there is always a folded decision rule in Approximation. */
const FoldDecisionRule &dr = approx.getFoldDecisionRule();
FTensorPolynomial dr_ss(model->nstat()+model->npred(), model->nboth()+model->nforw(),
const_cast<FoldDecisionRule &>(dr));
// make ytmp_star be a difference of yplus from steady
Vector ytmp_star(ConstVector(*yplus, model->nstat(), model->npred()+model->nboth()));
ConstVector ysteady_star(dr.getSteady(), model->nstat(),
model->npred()+model->nboth());
ytmp_star.add(-1.0, ysteady_star);
// make hss and add steady to it
/* Here is the const context of dr_ss. */
hss = std::make_unique<FTensorPolynomial>(dr_ss, ytmp_star);
ConstVector ysteady_ss(dr.getSteady(), model->nstat()+model->npred(),
model->nboth()+model->nforw());
if (hss->check(Symmetry{0}))
hss->get(Symmetry{0}).getData().add(1.0, ysteady_ss);
else
{
auto ten = std::make_unique<FFSTensor>(hss->nrows(), hss->nvars(), 0);
ten->getData() = ysteady_ss;
hss->insert(std::move(ten));
}
}
/* Here we evaluate the residual F(y*,u,u). We have to evaluate hss for
u=point and then we evaluate the system f. */
void
ResidFunction::eval(const Vector &point, const ParameterSignal &sig, Vector &out)
{
KORD_RAISE_IF(point.length() != hss->nvars(),
"Wrong dimension of input vector in ResidFunction::eval");
KORD_RAISE_IF(out.length() != model->numeq(),
"Wrong dimension of output vector in ResidFunction::eval");
Vector yss(hss->nrows());
hss->evalHorner(yss, point);
model->evaluateSystem(out, *ystar, *yplus, yss, *u);
}
/* This checks the 𝔼[F(y*,u,u)] for a given y* and u by integrating with a
given quadrature. Note that the input ys is y* not whole y. */
void
GlobalChecker::check(const Quadrature &quad, int level,
const ConstVector &ys, const ConstVector &x, Vector &out)
{
for (int ifunc = 0; ifunc < vfs.getNum(); ifunc++)
dynamic_cast<GResidFunction &>(vfs.getFunc(ifunc)).setYU(ys, x);
quad.integrate(vfs, level, out);
}
/* This method is a bulk version of GlobalChecker::check() vector code. It
decides between Smolyak and product quadrature according to max_evals
constraint.
Note that y can be either full (all endogenous variables including static
and forward looking), or just y* (state variables). The method is able to
recognize it. */
void
GlobalChecker::check(int max_evals, const ConstTwoDMatrix &y,
const ConstTwoDMatrix &x, TwoDMatrix &out)
{
JournalRecordPair pa(journal);
pa << "Checking approximation error for " << y.ncols()
<< " states with at most " << max_evals << " evaluations" << endrec;
// Decide about which type of quadrature
GaussHermite gh;
SmolyakQuadrature dummy_sq(model.nexog(), 1, gh);
int smol_evals;
int smol_level;
dummy_sq.designLevelForEvals(max_evals, smol_level, smol_evals);
ProductQuadrature dummy_pq(model.nexog(), gh);
int prod_evals;
int prod_level;
dummy_pq.designLevelForEvals(max_evals, prod_level, prod_evals);
bool take_smolyak = (smol_evals < prod_evals) && (smol_level >= prod_level-1);
std::unique_ptr<Quadrature> quad;
int lev;
// Create the quadrature and report the decision
if (take_smolyak)
{
quad = std::make_unique<SmolyakQuadrature>(model.nexog(), smol_level, gh);
lev = smol_level;
JournalRecord rec(journal);
rec << "Selected Smolyak (level,evals)=(" << smol_level << ","
<< smol_evals << ") over product (" << prod_level << ","
<< prod_evals << ")" << endrec;
}
else
{
quad = std::make_unique<ProductQuadrature>(model.nexog(), gh);
lev = prod_level;
JournalRecord rec(journal);
rec << "Selected product (level,evals)=(" << prod_level << ","
<< prod_evals << ") over Smolyak (" << smol_level << ","
<< smol_evals << ")" << endrec;
}
// Check all columns of y and x
int first_row = (y.nrows() == model.numeq()) ? model.nstat() : 0;
ConstTwoDMatrix ysmat(y, first_row, 0, model.npred()+model.nboth(), y.ncols());
for (int j = 0; j < y.ncols(); j++)
{
ConstVector yj{ysmat.getCol(j)};
ConstVector xj{x.getCol(j)};
Vector outj{out.getCol(j)};
check(*quad, lev, yj, xj, outj);
}
}
/* This method checks an error of the approximation by evaluating residual
𝔼[F(y*,u,u) | y*,u] for y* equal to the steady state, and changing u. We go
through all elements of u and vary them from mult·σ to mult·σ in m
steps. */
void
GlobalChecker::checkAlongShocksAndSave(mat_t *fd, const std::string &prefix,
int m, double mult, int max_evals)
{
JournalRecordPair pa(journal);
pa << "Calculating errors along shocks +/- "
<< mult << " std errors, granularity " << m << endrec;
// Setup y_mat of steady states for checking
TwoDMatrix y_mat(model.numeq(), 2*m*model.nexog()+1);
for (int j = 0; j < 2*m*model.nexog()+1; j++)
{
Vector yj{y_mat.getCol(j)};
yj = model.getSteady();
}
// Setup exo_mat for checking
TwoDMatrix exo_mat(model.nexog(), 2*m*model.nexog()+1);
exo_mat.zeros();
for (int ishock = 0; ishock < model.nexog(); ishock++)
{
double max_sigma = sqrt(model.getVcov().get(ishock, ishock));
for (int j = 0; j < 2*m; j++)
{
int jmult = (j < m) ? j-m : j-m+1;
exo_mat.get(ishock, 1+2*m*ishock+j) = mult*jmult*max_sigma/m;
}
}
TwoDMatrix errors(model.numeq(), 2*m*model.nexog()+1);
check(max_evals, y_mat, exo_mat, errors);
// Report errors along shock and save them
TwoDMatrix res(model.nexog(), 2*m+1);
JournalRecord rec(journal);
rec << "Shock value error" << endrec;
ConstVector err0{errors.getCol(0)};
for (int ishock = 0; ishock < model.nexog(); ishock++)
{
TwoDMatrix err_out(model.numeq(), 2*m+1);
for (int j = 0; j < 2*m+1; j++)
{
int jj;
Vector error{err_out.getCol(j)};
if (j != m)
{
if (j < m)
jj = 1 + 2*m*ishock+j;
else
jj = 1 + 2*m*ishock+j-1;
ConstVector coljj{errors.getCol(jj)};
error = coljj;
}
else
{
jj = 0;
error = err0;
}
JournalRecord rec1(journal);
std::string shockname{model.getExogNames().getName(ishock)};
shockname.resize(8, ' ');
rec1 << shockname << ' ' << exo_mat.get(ishock, jj)
<< "\t" << error.getMax() << endrec;
}
err_out.writeMat(fd, prefix + "_shock_" + model.getExogNames().getName(ishock) + "_errors");
}
}
/* This method checks errors on ellipse of endogenous states (predetermined
variables). The ellipse is shaped according to covariance matrix of
endogenous variables based on the first order approximation and scaled by
mult. The points on the ellipse are chosen as polar images of the low
discrepancy grid in a cube.
The method works as follows. First we calculate symmetric Schur factor of
covariance matrix of the states. Second we generate low discrepancy points
on the unit sphere. Third we transform the sphere with the
variance-covariance matrix factor and multiplier mult and initialize
matrix of u to zeros. Fourth we run the check() method and save the
results. */
void
GlobalChecker::checkOnEllipseAndSave(mat_t *fd, const std::string &prefix,
int m, double mult, int max_evals)
{
JournalRecordPair pa(journal);
pa << "Calculating errors at " << m
<< " ellipse points scaled by " << mult << endrec;
// Make factor of covariance of variables
/* Here we set ycovfac to the symmetric Schur decomposition factor of a
submatrix of covariances of all endogenous variables. The submatrix
corresponds to state variables (predetermined plus both). */
TwoDMatrix ycov{approx.calcYCov()};
TwoDMatrix ycovpred(const_cast<const TwoDMatrix &>(ycov), model.nstat(), model.nstat(),
model.npred()+model.nboth(), model.npred()+model.nboth());
SymSchurDecomp ssd(ycovpred);
ssd.correctDefinitness(1.e-05);
TwoDMatrix ycovfac(ycovpred.nrows(), ycovpred.ncols());
KORD_RAISE_IF(!ssd.isPositiveSemidefinite(),
"Covariance matrix of the states not positive \
semidefinite in GlobalChecker::checkOnEllipseAndSave");
ssd.getFactor(ycovfac);
// Put low discrepancy sphere points to ymat
/* Here we first calculate dimension d of the sphere, which is a number of
state variables minus one. We go through the d-dimensional cube [0,1]
by QMCarloCubeQuadrature and make a polar transformation to the sphere.
The polar transformation f can be written recursively w.r.t. the
dimension i as:
f() = [1]
cos(2πx)·f¹(x,,x)
f(x,,x) = sin(2πx)
*/
int d = model.npred()+model.nboth()-1;
TwoDMatrix ymat(model.npred()+model.nboth(), (d == 0) ? 2 : m);
if (d == 0)
{
ymat.get(0, 0) = 1;
ymat.get(0, 1) = -1;
}
else
{
int icol = 0;
ReversePerScheme ps;
QMCarloCubeQuadrature qmc(d, m, ps);
qmcpit beg = qmc.start(m);
qmcpit end = qmc.end(m);
for (qmcpit run = beg; run != end; ++run, icol++)
{
Vector ycol{ymat.getCol(icol)};
Vector x(run.point());
x.mult(2*M_PI);
ycol[0] = 1;
for (int i = 0; i < d; i++)
{
Vector subsphere(ycol, 0, i+1);
subsphere.mult(cos(x[i]));
ycol[i+1] = sin(x[i]);
}
}
}
// Transform sphere ymat and prepare umat for checking
/* Here we multiply the sphere points in ymat with the Cholesky factor to
obtain the ellipse, scale the ellipse by the given mult, and initialize
matrix of shocks umat to zero. */
TwoDMatrix umat(model.nexog(), ymat.ncols());
umat.zeros();
ymat.mult(mult);
ymat.multLeft(ycovfac);
ConstVector ys(model.getSteady(), model.nstat(),
model.npred()+model.nboth());
for (int icol = 0; icol < ymat.ncols(); icol++)
{
Vector ycol{ymat.getCol(icol)};
ycol.add(1.0, ys);
}
// Check on ellipse and save
/* Here we check the points and save the results to MAT-4 file. */
TwoDMatrix out(model.numeq(), ymat.ncols());
check(max_evals, ymat, umat, out);
ymat.writeMat(fd, prefix + "_ellipse_points");
out.writeMat(fd, prefix + "_ellipse_errors");
}
/* Here we check the errors along a simulation. We simulate, then set x to
zeros, check and save results. */
void
GlobalChecker::checkAlongSimulationAndSave(mat_t *fd, const std::string &prefix,
int m, int max_evals)
{
JournalRecordPair pa(journal);
pa << "Calculating errors at " << m
<< " simulated points" << endrec;
RandomShockRealization sr(model.getVcov(), seed_generator::get_new_seed());
TwoDMatrix y{approx.getFoldDecisionRule().simulate(DecisionRule::emethod::horner,
m, model.getSteady(), sr)};
TwoDMatrix x(model.nexog(), m);
x.zeros();
TwoDMatrix out(model.numeq(), m);
check(max_evals, y, x, out);
y.writeMat(fd, prefix + "_simul_points");
out.writeMat(fd, prefix + "_simul_errors");
}

View File

@ -1,171 +0,0 @@
/*
* Copyright © 2005 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 <https://www.gnu.org/licenses/>.
*/
// Global check
/* The purpose of this file is to provide classes for checking error of
approximation. If y=g(y*,u) is an approximate solution, then we check
for the error of residuals of the system equations. Let
F(y*,u,u)=f(g**(g*(y*,u),u),g(y*,u),y*,u), then we calculate the integral:
𝔼[F(y*,u,u)]
which we want to be zero for all y* and u.
There are a few possibilities how and where the integral is evaluated.
Currently we offer the following ones:
Along shocks. The y* is set to the steady state, and u is set to zero but
one element is going from minus through plus shocks in few steps. The user
gives the scaling factor, for instance the interval [3σ,3σ] (where σ is
one standard error of the shock), and a number of steps. This is repeated
for each shock (element of the u vector).
Along simulation. Some random simulation is run, and for each realization
of y* and u along the path we evaluate the residual.
On ellipse. Let V=AA be a covariance matrix of the predetermined
variables y* based on linear approximation, then we calculate integral for
points on the ellipse { Ax | x=1 }. The points are selected by means of
low discrepancy method and polar transformation. The shock u are zeros.
Unconditional distribution.
*/
#ifndef GLOBAL_CHECK_H
#define GLOBAL_CHECK_H
#include <matio.h>
#include <memory>
#include "vector_function.hh"
#include "quadrature.hh"
#include "dynamic_model.hh"
#include "journal.hh"
#include "approximation.hh"
/* This is a class for implementing the VectorFunction interface evaluating the
residual of equations, this is F(y*,u,u)=f(g**(g*(y*,u),u),y*,u) is
written as a function of u.
When the object is constructed, one has to specify (y*,u), this is done by
the setYU() method. The object has basically two states. One is after
construction and before the call to setYU(). The second is after the call to
setYU(). We distinguish between the two states, an object in the second
state contains yplus, ystar, u, and hss.
The vector yplus is g*(y*,u). ystar is y*, and polynomial hss is
partially evaluated g**(yplus, u).
The pointer to DynamicModel is important, since the DynamicModel evaluates
the function f. When copying the object, we have to make also a copy of
DynamicModel. */
class ResidFunction : public VectorFunction
{
protected:
const Approximation &approx;
std::unique_ptr<DynamicModel> model;
std::unique_ptr<Vector> yplus, ystar, u;
std::unique_ptr<FTensorPolynomial> hss;
public:
ResidFunction(const Approximation &app);
ResidFunction(const ResidFunction &rf);
std::unique_ptr<VectorFunction>
clone() const override
{
return std::make_unique<ResidFunction>(*this);
}
void eval(const Vector &point, const ParameterSignal &sig, Vector &out) override;
void setYU(const ConstVector &ys, const ConstVector &xx);
};
/* This is a ResidFunction wrapped with GaussConverterFunction. */
class GResidFunction : public GaussConverterFunction
{
public:
GResidFunction(const Approximation &app)
: GaussConverterFunction(std::make_unique<ResidFunction>(app), app.getModel().getVcov())
{
}
std::unique_ptr<VectorFunction>
clone() const override
{
return std::make_unique<GResidFunction>(*this);
}
void
setYU(const ConstVector &ys, const ConstVector &xx)
{
dynamic_cast<ResidFunction *>(func)->setYU(ys, xx);
}
};
/* This is a class encapsulating checking algorithms. Its core routine is
check(), which calculates integral 𝔼[F(y*,u,u) | y*,u] for given
realizations of y* and u. The both are given in matrices. The methods
checking along shocks, on ellipse and anlong a simulation path, just fill
the matrices and call the core check().
The method checkUnconditionalAndSave() evaluates unconditional 𝔼[F(y,u,u)].
The object also maintains a set of GResidFunction functions vfs in order
to save (possibly expensive) copying of DynamicModels. */
class GlobalChecker
{
const Approximation &approx;
const DynamicModel &model;
Journal &journal;
GResidFunction rf;
VectorFunctionSet vfs;
public:
GlobalChecker(const Approximation &app, int n, Journal &jr)
: approx(app), model(approx.getModel()), journal(jr),
rf(approx), vfs(rf, n)
{
}
void check(int max_evals, const ConstTwoDMatrix &y,
const ConstTwoDMatrix &x, TwoDMatrix &out);
void checkAlongShocksAndSave(mat_t *fd, const std::string &prefix,
int m, double mult, int max_evals);
void checkOnEllipseAndSave(mat_t *fd, const std::string &prefix,
int m, double mult, int max_evals);
void checkAlongSimulationAndSave(mat_t *fd, const std::string &prefix,
int m, int max_evals);
void checkUnconditionalAndSave(mat_t *fd, const std::string &prefix,
int m, int max_evals);
protected:
void check(const Quadrature &quad, int level,
const ConstVector &y, const ConstVector &x, Vector &out);
};
/* Signalled resid function. Not implemented yet. todo: */
class ResidFunctionSig : public ResidFunction
{
public:
ResidFunctionSig(const Approximation &app, const Vector &ys, const Vector &xx);
};
#endif

View File

@ -1,123 +0,0 @@
/*
* Copyright © 2007 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 <https://www.gnu.org/licenses/>.
*/
#include "normal_conjugate.hh"
#include "kord_exception.hh"
// NormalConj diffuse prior constructor
NormalConj::NormalConj(int d)
: mu(d), kappa(0), nu(-1), lambda(d, d)
{
mu.zeros();
lambda.zeros();
}
// NormalConj data update constructor
NormalConj::NormalConj(const ConstTwoDMatrix &ydata)
: mu(ydata.nrows()), kappa(ydata.ncols()), nu(ydata.ncols()-1),
lambda(ydata.nrows(), ydata.nrows())
{
mu.zeros();
for (int i = 0; i < ydata.ncols(); i++)
mu.add(1.0/ydata.ncols(), ydata.getCol(i));
lambda.zeros();
for (int i = 0; i < ydata.ncols(); i++)
{
Vector diff{ydata.getCol(i)};
diff.add(-1, mu);
lambda.addOuter(diff);
}
}
// NormalConj::update() one observation code
/* The method performs the following:
κ 1
μ = μ + y
κ+1 κ+1
κ = κ + 1
ν = ν + 1
κ
Λ = Λ + (y μ)(y μ)
κ+1
*/
void
NormalConj::update(const ConstVector &y)
{
KORD_RAISE_IF(y.length() != mu.length(),
"Wrong length of a vector in NormalConj::update");
mu.mult(kappa/(1.0+kappa));
mu.add(1.0/(1.0+kappa), y);
Vector diff(y);
diff.add(-1, mu);
lambda.addOuter(diff, kappa/(1.0+kappa));
kappa++;
nu++;
}
// NormalConj::update() multiple observations code
/* The method evaluates the formula in the header file. */
void
NormalConj::update(const ConstTwoDMatrix &ydata)
{
NormalConj nc(ydata);
update(nc);
}
// NormalConj::update() with NormalConj code
void
NormalConj::update(const NormalConj &nc)
{
double wold = static_cast<double>(kappa)/(kappa+nc.kappa);
double wnew = 1-wold;
mu.mult(wold);
mu.add(wnew, nc.mu);
Vector diff(nc.mu);
diff.add(-1, mu);
lambda.add(1.0, nc.lambda);
lambda.addOuter(diff);
kappa = kappa + nc.kappa;
nu = nu + nc.kappa;
}
/* This returns 1/(νd1)·Λ, which is the mean of the variance in the posterior
distribution. If the number of degrees of freedom is less than d, then NaNs
are returned. */
void
NormalConj::getVariance(TwoDMatrix &v) const
{
if (nu > getDim()+1)
{
v = const_cast<const TwoDMatrix &>(lambda);
v.mult(1.0/(nu-getDim()-1));
}
else
v.nans();
}

View File

@ -1,105 +0,0 @@
/*
* Copyright © 2007 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 <https://www.gnu.org/licenses/>.
*/
// Conjugate family for normal distribution
/* The main purpose here is to implement a class representing conjugate
distributions for mean and variance of the normal distribution. The class
has two main methods: the first one is to update itself with respect to one
observation, the second one is to update itself with respect to anothe
object of the class. In the both methods, the previous state of the class
corresponds to the prior distribution, and the final state corresponds to
the posterior distribution.
The algebra can be found in Gelman, Carlin, Stern, Rubin (p.87). It goes as
follows. Prior conjugate distribution takes the following form:
Σ InvWishart_ν(Λ¹)
μ|Σ 𝒩(μ,Σ/κ)
If the observations are yy, then the posterior distribution has the same
form with the following parameters:
κ n
μ = μ + ȳ
κ+n κ+n
κ = κ + n
ν = ν + n
κ·n
Λ = Λ + S + (ȳ μ)(ȳ μ)
κ+n
where
1
ȳ = y
n ¹
S = (y ȳ)(y ȳ)
¹
*/
#ifndef NORMAL_CONJUGATE_H
#define NORMAL_CONJUGATE_H
#include "twod_matrix.hh"
/* The class is described by the four parameters: μ, κ, ν and Λ. */
class NormalConj
{
protected:
Vector mu;
int kappa;
int nu;
TwoDMatrix lambda;
public:
/* We provide the following constructors: The first constructs diffuse
(Jeffreys) prior. It sets κ and Λ to zeros, ν to 1 and also the mean μ
to zero (it should not be referenced). The second constructs the posterior
using the diffuse prior and the observed data (columnwise). The third is a
copy constructor. */
NormalConj(int d);
NormalConj(const ConstTwoDMatrix &ydata);
NormalConj(const NormalConj &) = default;
NormalConj(NormalConj &&) = default;
virtual ~NormalConj() = default;
void update(const ConstVector &y);
void update(const ConstTwoDMatrix &ydata);
void update(const NormalConj &nc);
int
getDim() const
{
return mu.length();
}
const Vector &
getMean() const
{
return mu;
}
void getVariance(TwoDMatrix &v) const;
};
#endif

View File

@ -1,47 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
#include "seed_generator.hh"
#include <limits>
#include <mutex>
namespace seed_generator
{
std::mutex mut;
std::mt19937 rng;
std::uniform_int_distribution<std::mt19937::result_type> seed_generator(std::numeric_limits<std::mt19937::result_type>::min(),
std::numeric_limits<std::mt19937::result_type>::max());
std::mt19937::result_type
get_new_seed()
{
std::lock_guard<std::mutex> lk{mut};
return seed_generator(rng);
}
void
set_meta_seed(std::mt19937::result_type s)
{
std::lock_guard<std::mutex> lk{mut};
rng.seed(s);
}
};

View File

@ -1,34 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
#ifndef RANDOM_H
#define RANDOM_H
#include <random>
namespace seed_generator
{
// Produces seeds that can be used with Mersenne-Twister generators (thread-safe)
std::mt19937::result_type get_new_seed();
// Sets the seed for the seed generator (!)
void set_meta_seed(std::mt19937::result_type s);
};
#endif

View File

@ -75,8 +75,6 @@
#include <memory>
#include <utility>
#include <matio.h>
// ltsym predicate
/* We need a predicate on strict weak ordering of
symmetries. */
@ -225,21 +223,6 @@ public:
}
}
/* Output to the MAT file. */
void
writeMat(mat_t *fd, const std::string &prefix) const
{
for (auto &it : *this)
{
std::string lname = prefix + "_g";
const Symmetry &sym = it.first;
for (int i = 0; i < sym.num(); i++)
lname += '_' + std::to_string(sym[i]);
ConstTwoDMatrix m(*(it.second));
m.writeMat(fd, lname);
}
}
/* Output to the Memory Map. */
void
writeMMap(std::map<std::string, ConstTwoDMatrix> &mm, const std::string &prefix) const

View File

@ -51,25 +51,6 @@ ConstTwoDMatrix::ConstTwoDMatrix(int first_row, int num, const ConstTwoDMatrix &
{
}
void
ConstTwoDMatrix::writeMat(mat_t *fd, const std::string &vname) const
{
size_t dims[2];
dims[0] = nrows();
dims[1] = ncols();
auto data = std::make_unique<double[]>(nrows()*ncols());
for (int j = 0; j < ncols(); j++)
for (int i = 0; i < nrows(); i++)
data[j*nrows()+i] = get(i, j);
matvar_t *v = Mat_VarCreate(vname.c_str(), MAT_C_DOUBLE, MAT_T_DOUBLE, 2, dims, data.get(), 0);
Mat_VarWrite(fd, v, MAT_COMPRESSION_NONE);
Mat_VarFree(v);
}
TwoDMatrix &
TwoDMatrix::operator=(const ConstTwoDMatrix &m)
{

View File

@ -34,8 +34,6 @@
#include "GeneralMatrix.hh"
#include <matio.h>
#include <string>
#include <utility>
@ -75,8 +73,6 @@ public:
ConstTwoDMatrix &operator=(const ConstTwoDMatrix &v) = delete;
ConstTwoDMatrix &operator=(ConstTwoDMatrix &&v) = delete;
void writeMat(mat_t *fd, const std::string &vname) const;
};
class TwoDMatrix : public GeneralMatrix
@ -200,12 +196,6 @@ public:
// Saves the matrix to a text file
void save(const std::string &fname) const;
void
writeMat(mat_t *fd, const std::string &vname) const
{
ConstTwoDMatrix(*this).writeMat(fd, vname);
}
};
#endif

View File

@ -1,277 +0,0 @@
/*
* Copyright © 2006 Ondra Kamenik
* Copyright © 2019-2022 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/>.
*/
#include "nlsolve.hh"
#include "dynare_exception.hh"
#include <sstream>
#include <iomanip>
using namespace ogu;
double GoldenSectionSearch::golden = (3.-std::sqrt(5.))/2;
double
GoldenSectionSearch::search(OneDFunction &f, double x1, double x2)
{
double b;
if (init_bracket(f, x1, x2, b))
{
double fb = f.eval(b);
double f1 = f.eval(x1);
f.eval(x2);
double dx;
do
{
double w = (b-x1)/(x2-x1);
dx = std::abs((1-2*w)*(x2-x1));
double x;
if (b-x1 > x2-b)
x = b - dx;
else
x = b + dx;
double fx = f.eval(x);
if (!std::isfinite(fx))
return x1;
if (b-x1 > x2-b)
{
// x is on the left from b
if (f1 > fx && fx < fb)
{
// pickup bracket [f1,fx,fb]
x2 = b;
fb = fx;
b = x;
}
else
{
// pickup bracket [fx,fb,fx2]
f1 = fx;
x1 = x;
}
}
else
{
// x is on the right from b
if (f1 > fb && fb < fx)
// pickup bracket [f1,fb,fx]
x2 = x;
else
{
// pickup bracket [fb,fx,f2]
f1 = fb;
x1 = b;
fb = fx;
b = x;
}
}
}
while (dx > tol);
}
return b;
}
bool
GoldenSectionSearch::init_bracket(OneDFunction &f, double x1, double &x2, double &b)
{
double f1 = f.eval(x1);
if (!std::isfinite(f1))
throw DynareException(__FILE__, __LINE__,
"Safer point not finite in GoldenSectionSearch::init_bracket");
int cnt = 0;
bool bracket_found = false;
do
{
bool finite_found = search_for_finite(f, x1, x2, b);
if (!finite_found)
{
b = x1;
return false;
}
double f2 = f.eval(x2);
double fb = f.eval(b);
double bsym = 2*x2 - b;
double fbsym = f.eval(bsym);
// now we know that f1, f2, and fb are finite
if (std::isfinite(fbsym))
{
/* we have four numbers f1, fb, f2, fbsym, we test for the following
combinations to find the bracket: [f1,f2,fbsym], [f1,fb,fbsym] and
[f1,fb,fbsym] */
if (f1 > f2 && f2 < fbsym)
{
bracket_found = true;
b = x2;
x2 = bsym;
}
else if (f1 > fb && fb < fbsym)
{
bracket_found = true;
x2 = bsym;
}
else if (f1 > fb && fb < f2)
bracket_found = true;
else
{
double newx2 = b;
// choose the smallest value in case we end
if (f1 > fbsym)
{
/* the smallest value is on the other end, we do not want to
continue */
b = bsym;
return false;
}
else
b = x1;
// move x2 to b in case we continue
x2 = newx2;
}
}
else
{
/* we have only three numbers, we test for the bracket, and if not
found, we set b as potential result and shorten x2 as potential
init value for next cycle */
if (f1 > fb && fb < f2)
bracket_found = true;
else
{
double newx2 = b;
// choose the smaller value in case we end
if (f1 > f2)
b = x2;
else
b = x1;
// move x2 to b in case we continue
x2 = newx2;
}
}
cnt++;
}
while (!bracket_found && cnt < 5);
return bracket_found;
}
/* This moves x2 toward to x1 until the function at x2 is finite and b as a
golden section between x1 and x2 yields also finite f. */
bool
GoldenSectionSearch::search_for_finite(OneDFunction &f, double x1, double &x2, double &b)
{
int cnt = 0;
bool found = false;
do
{
double f2 = f.eval(x2);
b = (1-golden)*x1 + golden*x2;
double fb = f.eval(b);
found = std::isfinite(f2) && std::isfinite(fb);
if (!found)
x2 = b;
cnt++;
}
while (!found && cnt < 5);
return found;
}
void
VectorFunction::check_for_eval(const ConstVector &in, Vector &out) const
{
if (inDim() != in.length() || outDim() != out.length())
throw DynareException(__FILE__, __LINE__,
"Wrong dimensions in VectorFunction::check_for_eval");
}
double
NLSolver::eval(double lambda)
{
Vector xx(const_cast<const Vector &>(x));
xx.add(1-lambda, xcauchy);
xx.add(lambda, xnewton);
Vector ff(func.outDim());
func.eval(xx, ff);
return ff.dot(ff);
}
bool
NLSolver::solve(Vector &xx, int &iter)
{
JournalRecord rec(journal);
rec << "Iter lambda residual" << endrec;
JournalRecord rec1(journal);
rec1 << "───────────────────────────" << endrec;
x = const_cast<const Vector &>(xx);
iter = 0;
// setup fx
Vector fx(func.outDim());
func.eval(x, fx);
if (!fx.isFinite())
throw DynareException(__FILE__, __LINE__,
"Initial guess does not yield finite residual in NLSolver::solve");
bool converged = fx.getMax() < tol;
JournalRecord rec2(journal);
auto format_double = [](double v)
{
std::ostringstream buf;
buf << std::setw(11) << v;
return buf.str();
};
rec2 << iter << " N/A " << format_double(fx.getMax()) << endrec;
while (!converged && iter < max_iter)
{
// setup Jacobian
jacob.eval(x);
// calculate cauchy step
Vector g(func.inDim());
g.zeros();
ConstTwoDMatrix(jacob).multaVecTrans(g, fx);
Vector Jg(func.inDim());
Jg.zeros();
ConstTwoDMatrix(jacob).multaVec(Jg, g);
double m = -g.dot(g)/Jg.dot(Jg);
xcauchy = const_cast<const Vector &>(g);
xcauchy.mult(m);
// calculate newton step
xnewton = const_cast<const Vector &>(fx);
ConstTwoDMatrix(jacob).multInvLeft(xnewton);
xnewton.mult(-1);
// line search
double lambda = GoldenSectionSearch::search(*this, 0, 1);
x.add(1-lambda, xcauchy);
x.add(lambda, xnewton);
// evaluate func
func.eval(x, fx);
converged = fx.getMax() < tol;
// iter
iter++;
JournalRecord rec3(journal);
rec3 << iter << " " << lambda << " " << format_double(fx.getMax()) << endrec;
}
xx = const_cast<const Vector &>(x);
return converged;
}

View File

@ -1,113 +0,0 @@
/*
* Copyright © 2006 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 <https://www.gnu.org/licenses/>.
*/
#ifndef OGU_NLSOLVE_H
#define OGU_NLSOLVE_H
#include "twod_matrix.hh"
#include "journal.hh"
#include <cmath>
namespace ogu
{
class OneDFunction
{
public:
virtual ~OneDFunction() = default;
virtual double eval(double) = 0;
};
class GoldenSectionSearch
{
protected:
constexpr static double tol = 1.e-4;
/* This is equal to the golden section ratio. */
static double golden;
public:
static double search(OneDFunction &f, double x1, double x2);
protected:
/* This initializes a bracket by moving x2 and b (as a golden section of
x1,x2) so that f(x1)>f(b) && f(b)<f(x2). The point x1 is not moved,
since it is considered as reliable and f(x1) is supposed to be finite.
If initialization of a bracket succeeded, then [x1,b,x2] is the bracket
and true is returned. Otherwise, b is the minimum found and false is
returned. */
static bool init_bracket(OneDFunction &f, double x1, double &x2, double &b);
/* This supposes that f(x1) is finite and it moves x2 toward x1 until x2
and b (as a golden section of x1,x2) are finite. If succeeded, the
routine returns true and x2, and b. Otherwise, it returns false. */
static bool search_for_finite(OneDFunction &f, double x1, double &x2, double &b);
};
class VectorFunction
{
public:
VectorFunction() = default;
virtual ~VectorFunction() = default;
virtual int inDim() const = 0;
virtual int outDim() const = 0;
/* Check dimensions of eval parameters. */
void check_for_eval(const ConstVector &in, Vector &out) const;
/* Evaluate the vector function. */
virtual void eval(const ConstVector &in, Vector &out) = 0;
};
class Jacobian : public TwoDMatrix
{
public:
Jacobian(int n) : TwoDMatrix(n, n)
{
}
~Jacobian() override = default;
virtual void eval(const Vector &in) = 0;
};
class NLSolver : public OneDFunction
{
protected:
Journal &journal;
VectorFunction &func;
Jacobian &jacob;
const int max_iter;
const double tol;
private:
Vector xnewton;
Vector xcauchy;
Vector x;
public:
NLSolver(VectorFunction &f, Jacobian &j, int maxit, double tl, Journal &jr)
: journal(jr), func(f), jacob(j), max_iter(maxit), tol(tl),
xnewton(f.inDim()), xcauchy(f.inDim()), x(f.inDim())
{
xnewton.zeros(); xcauchy.zeros(); x.zeros();
}
~NLSolver() override = default;
/* Returns true if the problem has converged. xx as input is the starting
value, as output it is a solution. */
bool solve(Vector &xx, int &iter);
/* To implement OneDFunction interface. It returns func(xx)ᵀ·func(xx),
where xx=x+lambda·xcauchy+(1lambda)·xnewton. It is non-const only
because it calls func, x, xnewton, xcauchy is not changed. */
double eval(double lambda) override;
};
};
#endif