Remove more unused Dynare++ code
In particular, the libkorder library no longer depends on MatIO.mr#2134
parent
99cd06c9fd
commit
73e4ced39a
|
@ -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)
|
- [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)
|
- [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)
|
- [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)
|
- [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),
|
- A decent LaTeX distribution (if you want to compile PDF documentation),
|
||||||
ideally with Beamer
|
ideally with Beamer
|
||||||
- For building the reference manual:
|
- For building the reference manual:
|
||||||
|
|
|
@ -302,9 +302,8 @@ Copyright: 2010 Yannick Kalantzis
|
||||||
License: GPL-3+
|
License: GPL-3+
|
||||||
|
|
||||||
Files: mex/sources/gensylv/gensylv.cc
|
Files: mex/sources/gensylv/gensylv.cc
|
||||||
mex/sources/libkorder/kord/* mex/sources/libkorder/integ/*
|
mex/sources/libkorder/kord/* mex/sources/libkorder/sylv/*
|
||||||
mex/sources/libkorder/sylv/* mex/sources/libkorder/tl/*
|
mex/sources/libkorder/tl/* mex/sources/libkorder/utils/*
|
||||||
mex/sources/libkorder/utils/*
|
|
||||||
Copyright: 2004-2011 Ondra Kamenik
|
Copyright: 2004-2011 Ondra Kamenik
|
||||||
2019-2023 Dynare Team
|
2019-2023 Dynare Team
|
||||||
License: GPL-3+
|
License: GPL-3+
|
||||||
|
|
|
@ -2,7 +2,7 @@ noinst_LIBRARIES = libkorder.a
|
||||||
|
|
||||||
TOPDIR = $(top_srcdir)/../../sources/libkorder
|
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
|
# TODO: fix the codebase so that the following line is no longer needed
|
||||||
libkorder_a_CXXFLAGS = $(AM_CXXFLAGS) -Wno-unused-parameter
|
libkorder_a_CXXFLAGS = $(AM_CXXFLAGS) -Wno-unused-parameter
|
||||||
|
@ -13,12 +13,9 @@ KORD_SRCS = \
|
||||||
dynamic_model.cc \
|
dynamic_model.cc \
|
||||||
faa_di_bruno.cc \
|
faa_di_bruno.cc \
|
||||||
first_order.cc \
|
first_order.cc \
|
||||||
global_check.cc \
|
|
||||||
korder.cc \
|
korder.cc \
|
||||||
korder_stoch.cc \
|
korder_stoch.cc \
|
||||||
journal.cc \
|
journal.cc
|
||||||
normal_conjugate.cc \
|
|
||||||
seed_generator.cc
|
|
||||||
|
|
||||||
SYLV_SRCS = \
|
SYLV_SRCS = \
|
||||||
BlockDiagonal.cc \
|
BlockDiagonal.cc \
|
||||||
|
@ -61,18 +58,10 @@ TL_SRCS = \
|
||||||
tl_static.cc \
|
tl_static.cc \
|
||||||
twod_matrix.cc
|
twod_matrix.cc
|
||||||
|
|
||||||
INTEG_SRCS = \
|
|
||||||
quadrature.cc \
|
|
||||||
quasi_mcarlo.cc \
|
|
||||||
product.cc \
|
|
||||||
smolyak.cc \
|
|
||||||
vector_function.cc
|
|
||||||
|
|
||||||
UTILS_SRCS = \
|
UTILS_SRCS = \
|
||||||
pascal_triangle.cc \
|
pascal_triangle.cc \
|
||||||
int_power.cc \
|
int_power.cc \
|
||||||
sthread.cc \
|
sthread.cc
|
||||||
nlsolve.cc
|
|
||||||
|
|
||||||
TOPDIR_SRCS = \
|
TOPDIR_SRCS = \
|
||||||
k_ord_dynare.cc \
|
k_ord_dynare.cc \
|
||||||
|
@ -83,7 +72,6 @@ nodist_libkorder_a_SOURCES = \
|
||||||
$(KORD_SRCS) \
|
$(KORD_SRCS) \
|
||||||
$(TL_SRCS) \
|
$(TL_SRCS) \
|
||||||
$(SYLV_SRCS) \
|
$(SYLV_SRCS) \
|
||||||
$(INTEG_SRCS) \
|
|
||||||
$(UTILS_SRCS) \
|
$(UTILS_SRCS) \
|
||||||
$(TOPDIR_SRCS)
|
$(TOPDIR_SRCS)
|
||||||
|
|
||||||
|
@ -98,7 +86,5 @@ $(TL_SRCS): %.cc: $(TOPDIR)/tl/%.cc
|
||||||
$(LN_S) -f $< $@
|
$(LN_S) -f $< $@
|
||||||
$(SYLV_SRCS): %.cc: $(TOPDIR)/sylv/%.cc
|
$(SYLV_SRCS): %.cc: $(TOPDIR)/sylv/%.cc
|
||||||
$(LN_S) -f $< $@
|
$(LN_S) -f $< $@
|
||||||
$(INTEG_SRCS): %.cc: $(TOPDIR)/integ/%.cc
|
|
||||||
$(LN_S) -f $< $@
|
|
||||||
$(UTILS_SRCS): %.cc: $(TOPDIR)/utils/%.cc
|
$(UTILS_SRCS): %.cc: $(TOPDIR)/utils/%.cc
|
||||||
$(LN_S) -f $< $@
|
$(LN_S) -f $< $@
|
||||||
|
|
|
@ -1,11 +1,6 @@
|
||||||
ACLOCAL_AMFLAGS = -I ../../../m4
|
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
|
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
|
||||||
|
|
||||||
# libkorder must come first
|
|
||||||
if ENABLE_MEX_GENSYLV_KORDER
|
|
||||||
SUBDIRS += libkorder gensylv k_order_perturbation k_order_welfare
|
|
||||||
endif
|
|
||||||
|
|
||||||
if ENABLE_MEX_MS_SBVAR
|
if ENABLE_MEX_MS_SBVAR
|
||||||
SUBDIRS += ms_sbvar
|
SUBDIRS += ms_sbvar
|
||||||
|
|
|
@ -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])
|
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])
|
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])
|
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])
|
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.])
|
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
|
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
|
# Check for libslicot, needed by kalman_steady_state
|
||||||
if test "$enable_mex_kalman_steady_state" = yes; then
|
if test "$enable_mex_kalman_steady_state" = yes; then
|
||||||
# FCFLAGS must be temporarily modified, because otherwise -fno-underscoring is not
|
# FCFLAGS must be temporarily modified, because otherwise -fno-underscoring is not
|
||||||
|
@ -98,7 +89,6 @@ fi
|
||||||
case ${host_os} in
|
case ${host_os} in
|
||||||
*mingw32*)
|
*mingw32*)
|
||||||
GSL_LIBS="-Wl,-Bstatic $GSL_LIBS -Wl,-Bdynamic"
|
GSL_LIBS="-Wl,-Bstatic $GSL_LIBS -Wl,-Bdynamic"
|
||||||
LIBADD_MATIO="-Wl,-Bstatic $LIBADD_MATIO -Wl,-Bdynamic"
|
|
||||||
LIBADD_SLICOT="-Wl,-Bstatic $LIBADD_SLICOT -Wl,-Bdynamic"
|
LIBADD_SLICOT="-Wl,-Bstatic $LIBADD_SLICOT -Wl,-Bdynamic"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
@ -115,12 +105,6 @@ AC_SUBST([M2HTML])
|
||||||
AM_CONDITIONAL([HAVE_M2HTML], [test "x$M2HTML" != "x"])
|
AM_CONDITIONAL([HAVE_M2HTML], [test "x$M2HTML" != "x"])
|
||||||
|
|
||||||
# Construct final output message
|
# 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
|
if test "$enable_mex_kalman_steady_state" = yes; then
|
||||||
BUILD_KALMAN_STEADY_STATE_MATLAB="yes"
|
BUILD_KALMAN_STEADY_STATE_MATLAB="yes"
|
||||||
else
|
else
|
||||||
|
@ -139,7 +123,6 @@ Dynare is now configured for building the following components...
|
||||||
|
|
||||||
Binaries (with "make"):
|
Binaries (with "make"):
|
||||||
MEX files for MATLAB (except those listed below): yes
|
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
|
MS-SBVAR MEX files for MATLAB: $BUILD_MS_SBVAR_MEX_MATLAB
|
||||||
Kalman Steady State MEX file for MATLAB: $BUILD_KALMAN_STEADY_STATE_MATLAB
|
Kalman Steady State MEX file for MATLAB: $BUILD_KALMAN_STEADY_STATE_MATLAB
|
||||||
M2HTML documentation: $BUILD_M2HTML
|
M2HTML documentation: $BUILD_M2HTML
|
||||||
|
|
|
@ -1,11 +1,6 @@
|
||||||
ACLOCAL_AMFLAGS = -I ../../../m4
|
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
|
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
|
||||||
|
|
||||||
# libkorder must come first
|
|
||||||
if ENABLE_MEX_GENSYLV_KORDER
|
|
||||||
SUBDIRS += libkorder gensylv k_order_perturbation k_order_welfare
|
|
||||||
endif
|
|
||||||
|
|
||||||
if ENABLE_MEX_MS_SBVAR
|
if ENABLE_MEX_MS_SBVAR
|
||||||
SUBDIRS += ms_sbvar
|
SUBDIRS += ms_sbvar
|
||||||
|
|
|
@ -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])
|
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])
|
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])
|
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])
|
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.])
|
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
|
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)
|
# Check for libmatio, needed 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
|
if test "$enable_mex_ms_sbvar" = yes; then
|
||||||
AX_MATIO
|
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
|
fi
|
||||||
|
|
||||||
# Check for libslicot, needed by kalman_steady_state
|
# Check for libslicot, needed by kalman_steady_state
|
||||||
|
@ -127,12 +124,6 @@ esac
|
||||||
AM_CONDITIONAL([LINK_OCTAVE_LIBS], [test ${link_octave_libs} = yes])
|
AM_CONDITIONAL([LINK_OCTAVE_LIBS], [test ${link_octave_libs} = yes])
|
||||||
|
|
||||||
# Construct final output message
|
# 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
|
if test "$enable_mex_kalman_steady_state" = yes; then
|
||||||
BUILD_KALMAN_STEADY_STATE_OCTAVE="yes"
|
BUILD_KALMAN_STEADY_STATE_OCTAVE="yes"
|
||||||
else
|
else
|
||||||
|
@ -151,7 +142,6 @@ Dynare is now configured for building the following components...
|
||||||
|
|
||||||
Binaries (with "make"):
|
Binaries (with "make"):
|
||||||
MEX files for Octave (except those listed below): yes
|
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
|
MS-SBVAR MEX files for Octave: $BUILD_MS_SBVAR_MEX_OCTAVE
|
||||||
Kalman Steady State MEX file for Octave: $BUILD_KALMAN_STEADY_STATE_OCTAVE
|
Kalman Steady State MEX file for Octave: $BUILD_KALMAN_STEADY_STATE_OCTAVE
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
|
||||||
}
|
|
|
@ -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=(Qₖ⊗…⊗Qₖ)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
|
|
|
@ -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)
|
|
||||||
{
|
|
||||||
}
|
|
|
@ -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 ≃ ∑ wᵢxᵢ
|
|
||||||
ⁱ⁼¹
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
+∞
|
|
||||||
∫ xᵏe^{−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
|
|
||||||
|
|
||||||
₁
|
|
||||||
∫ xᵏdx
|
|
||||||
⁰
|
|
||||||
|
|
||||||
for level k.
|
|
||||||
|
|
||||||
Check precalc_quadrature.hh for available levels. */
|
|
||||||
|
|
||||||
class GaussLegendre : public OneDPrecalcQuadrature
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
GaussLegendre();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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^{−½xᵀx}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,…,base−1. */
|
|
||||||
|
|
||||||
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 RadicalInverse’s 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
|
|
|
@ -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+d−1 and all kᵢ are positive integers. This is
|
|
||||||
equivalent to going through all k such that l−d≤|k|≤l−1 and all kᵢ are
|
|
||||||
non-negative integers. This is equivalent to going through d+1 dimensional
|
|
||||||
sequences (k,x) such that |(k,x)|=l−1 and x=0,…,d−1. 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;
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
|
|
||||||
⎛ d−1 ⎞
|
|
||||||
Qᵈf = ∑ (−1)^{l+d−|k|−1} ⎢ ⎥ (Q¹_k₁⊗…⊗Q¹_{k_d})f
|
|
||||||
l≤|k|≤l+d−1 ⎝|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:
|
|
||||||
|
|
||||||
⎛ d−1 ⎞
|
|
||||||
∑ (−1)^{l+d−|k|−1} ⎢ ⎥ (Q¹_k₁⊗…⊗Q¹_{k_d})f
|
|
||||||
l≤|k|≤l+d−1 ⎝|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
|
|
|
@ -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
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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^{−½yᵀy} √(2ⁿ)|A|dy = ───── ∫ f(√2·Ay)e^{−½yᵀy}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
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2004 Ondra Kamenik
|
* Copyright © 2004 Ondra Kamenik
|
||||||
* Copyright © 2019 Dynare Team
|
* Copyright © 2019-2023 Dynare Team
|
||||||
*
|
*
|
||||||
* This file is part of Dynare.
|
* This file is part of Dynare.
|
||||||
*
|
*
|
||||||
|
@ -21,14 +21,9 @@
|
||||||
#include "kord_exception.hh"
|
#include "kord_exception.hh"
|
||||||
#include "decision_rule.hh"
|
#include "decision_rule.hh"
|
||||||
#include "dynamic_model.hh"
|
#include "dynamic_model.hh"
|
||||||
#include "seed_generator.hh"
|
|
||||||
|
|
||||||
#include "SymSchurDecomp.hh"
|
#include "SymSchurDecomp.hh"
|
||||||
|
|
||||||
#include <dynlapack.h>
|
|
||||||
|
|
||||||
#include <limits>
|
|
||||||
#include <utility>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
// FoldDecisionRule conversion from UnfoldDecisionRule
|
// FoldDecisionRule conversion from UnfoldDecisionRule
|
||||||
|
@ -48,539 +43,3 @@ UnfoldDecisionRule::UnfoldDecisionRule(const FoldDecisionRule &fdr)
|
||||||
for (const auto &it : fdr)
|
for (const auto &it : fdr)
|
||||||
insert(std::make_unique<ctraits<Storage::unfold>::Ttensym>(*(it.second)));
|
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];
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2004 Ondra Kamenik
|
* Copyright © 2004 Ondra Kamenik
|
||||||
* Copyright © 2019-2021 Dynare Team
|
* Copyright © 2019-2023 Dynare Team
|
||||||
*
|
*
|
||||||
* This file is part of Dynare.
|
* This file is part of Dynare.
|
||||||
*
|
*
|
||||||
|
@ -35,27 +35,13 @@
|
||||||
#ifndef DECISION_RULE_H
|
#ifndef DECISION_RULE_H
|
||||||
#define DECISION_RULE_H
|
#define DECISION_RULE_H
|
||||||
|
|
||||||
#include <matio.h>
|
|
||||||
|
|
||||||
#include "kord_exception.hh"
|
#include "kord_exception.hh"
|
||||||
#include "korder.hh"
|
#include "korder.hh"
|
||||||
#include "normal_conjugate.hh"
|
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <string>
|
#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
|
/* 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
|
define a common interface for simulation of a decision rule. We need only a
|
||||||
simulate, evaluate, centralized clone and output method. */
|
simulate, evaluate, centralized clone and output method. */
|
||||||
|
@ -65,10 +51,6 @@ public:
|
||||||
enum class emethod { horner, trad };
|
enum class emethod { horner, trad };
|
||||||
virtual ~DecisionRule() = default;
|
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,
|
/* primitive evaluation (it takes a vector of state variables (predetermined,
|
||||||
both and shocks) and returns the next period variables. Both input and
|
both and shocks) and returns the next period variables. Both input and
|
||||||
output are in deviations from the rule's steady. */
|
output are in deviations from the rule's steady. */
|
||||||
|
@ -79,9 +61,6 @@ public:
|
||||||
virtual void evaluate(emethod em, Vector &out, const ConstVector &ys,
|
virtual void evaluate(emethod em, Vector &out, const ConstVector &ys,
|
||||||
const ConstVector &u) const = 0;
|
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
|
/* returns a new copy of the decision rule, which is centralized about
|
||||||
provided fix-point */
|
provided fix-point */
|
||||||
virtual std::unique_ptr<DecisionRule> centralizedClone(const Vector &fixpoint) const = 0;
|
virtual std::unique_ptr<DecisionRule> centralizedClone(const Vector &fixpoint) const = 0;
|
||||||
|
@ -167,12 +146,9 @@ public:
|
||||||
{
|
{
|
||||||
return ysteady;
|
return ysteady;
|
||||||
}
|
}
|
||||||
TwoDMatrix simulate(emethod em, int np, const ConstVector &ystart,
|
|
||||||
ShockRealization &sr) const override;
|
|
||||||
void evaluate(emethod em, Vector &out, const ConstVector &ys,
|
void evaluate(emethod em, Vector &out, const ConstVector &ys,
|
||||||
const ConstVector &u) const override;
|
const ConstVector &u) const override;
|
||||||
std::unique_ptr<DecisionRule> centralizedClone(const Vector &fixpoint) const override;
|
std::unique_ptr<DecisionRule> centralizedClone(const Vector &fixpoint) const override;
|
||||||
void writeMat(mat_t *fd, const std::string &prefix) const override;
|
|
||||||
|
|
||||||
int
|
int
|
||||||
nexog() const override
|
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
|
/* This is one period evaluation of the decision rule. The simulation is a
|
||||||
sequence of repeated one period evaluations with a difference, that the
|
sequence of repeated one period evaluations with a difference, that the
|
||||||
steady state (fix point) is cancelled and added once. Hence we have two
|
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);
|
_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
|
/* This is exactly the same as DecisionRuleImpl<Storage::fold>. The only
|
||||||
difference is that we have a conversion from UnfoldDecisionRule, which is
|
difference is that we have a conversion from UnfoldDecisionRule, which is
|
||||||
exactly DecisionRuleImpl<Storage::unfold>. */
|
exactly DecisionRuleImpl<Storage::unfold>. */
|
||||||
|
@ -786,354 +677,4 @@ DRFixPoint<t>::calcFixPoint(emethod em, Vector &out)
|
||||||
return converged;
|
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 RTSimulationWorker’s 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 SimResultsIRF’s.
|
|
||||||
|
|
||||||
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
|
#endif
|
||||||
|
|
|
@ -29,44 +29,3 @@ NameList::print() const
|
||||||
for (int i = 0; i < getNum(); i++)
|
for (int i = 0; i < getNum(); i++)
|
||||||
std::cout << getName(i) << '\n';
|
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -48,8 +48,6 @@ public:
|
||||||
virtual int getNum() const = 0;
|
virtual int getNum() const = 0;
|
||||||
virtual const std::string &getName(int i) const = 0;
|
virtual const std::string &getName(int i) const = 0;
|
||||||
void print() const;
|
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
|
/* This is the interface to an information on a generic DSGE model. It is
|
||||||
|
|
|
@ -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");
|
|
||||||
}
|
|
|
@ -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 ≈
|
|
||||||
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 DynamicModel’s. */
|
|
||||||
|
|
||||||
class GlobalChecker
|
|
||||||
{
|
|
||||||
const Approximation ≈
|
|
||||||
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
|
|
|
@ -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/(ν−d−1)·Λ, 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();
|
|
||||||
}
|
|
|
@ -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 y₁…yₙ, 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
|
|
||||||
(Jeffrey’s) 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
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -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
|
|
|
@ -75,8 +75,6 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include <matio.h>
|
|
||||||
|
|
||||||
// ltsym predicate
|
// ltsym predicate
|
||||||
/* We need a predicate on strict weak ordering of
|
/* We need a predicate on strict weak ordering of
|
||||||
symmetries. */
|
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. */
|
/* Output to the Memory Map. */
|
||||||
void
|
void
|
||||||
writeMMap(std::map<std::string, ConstTwoDMatrix> &mm, const std::string &prefix) const
|
writeMMap(std::map<std::string, ConstTwoDMatrix> &mm, const std::string &prefix) const
|
||||||
|
|
|
@ -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 &
|
||||||
TwoDMatrix::operator=(const ConstTwoDMatrix &m)
|
TwoDMatrix::operator=(const ConstTwoDMatrix &m)
|
||||||
{
|
{
|
||||||
|
|
|
@ -34,8 +34,6 @@
|
||||||
|
|
||||||
#include "GeneralMatrix.hh"
|
#include "GeneralMatrix.hh"
|
||||||
|
|
||||||
#include <matio.h>
|
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
@ -75,8 +73,6 @@ public:
|
||||||
|
|
||||||
ConstTwoDMatrix &operator=(const ConstTwoDMatrix &v) = delete;
|
ConstTwoDMatrix &operator=(const ConstTwoDMatrix &v) = delete;
|
||||||
ConstTwoDMatrix &operator=(ConstTwoDMatrix &&v) = delete;
|
ConstTwoDMatrix &operator=(ConstTwoDMatrix &&v) = delete;
|
||||||
|
|
||||||
void writeMat(mat_t *fd, const std::string &vname) const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class TwoDMatrix : public GeneralMatrix
|
class TwoDMatrix : public GeneralMatrix
|
||||||
|
@ -200,12 +196,6 @@ public:
|
||||||
|
|
||||||
// Saves the matrix to a text file
|
// Saves the matrix to a text file
|
||||||
void save(const std::string &fname) const;
|
void save(const std::string &fname) const;
|
||||||
|
|
||||||
void
|
|
||||||
writeMat(mat_t *fd, const std::string &vname) const
|
|
||||||
{
|
|
||||||
ConstTwoDMatrix(*this).writeMat(fd, vname);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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+(1−lambda)·xnewton. It is non-const only
|
|
||||||
because it calls func, x, xnewton, xcauchy is not changed. */
|
|
||||||
double eval(double lambda) override;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
Loading…
Reference in New Issue