Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • normann/preprocessor
  • Dynare/preprocessor
  • FerhatMihoubi/preprocessor
  • MichelJuillard/preprocessor
  • sebastien/preprocessor
  • lnsongxf/preprocessor
  • albop/preprocessor
  • DoraK/preprocessor
  • amg/preprocessor
  • wmutschl/preprocessor
  • JohannesPfeifer/preprocessor
11 results
Show changes
Commits on Source (205)
Showing
with 5722 additions and 4037 deletions
# This file should be kept in sync with the one in dynare.git (which also
# contains more explanations).
Language: Cpp
Standard: c++20
ColumnLimit: 100
BasedOnStyle: GNU
AllowShortFunctionsOnASingleLine: None
AlwaysBreakTemplateDeclarations: Yes
BreakConstructorInitializers: AfterColon
BreakInheritanceList: AfterColon
Cpp11BracedListStyle: true
DeriveLineEnding: false
IndentPPDirectives: AfterHash
InsertNewlineAtEOF: true
PackConstructorInitializers: NextLine
PPIndentWidth: 1
PointerAlignment: Left
# RemoveParentheses: ReturnStatement
# RemoveSemicolon: true
SpaceAfterTemplateKeyword: false
SpaceBeforeParens: ControlStatements
SpaceBeforeCpp11BracedList: true
# TODO: add the following check families:
# - bugprone-*
# - cppcoreguidelines-
# NB: as of clang-tidy 16, we get several false positives inside boost, notably this one:
# https://github.com/llvm/llvm-project/issues/40486
Checks: 'performance-*,modernize-*,-modernize-use-trailing-return-type,-clang-diagnostic-unqualified-std-cast-call'
variables:
TERM: linux
MINGW32_BOOST_VERSION: 1.81.0-7
MINGW64_BOOST_VERSION: 1.81.0-7
MINGW64_BOOST_VERSION: 1.86.0-7
WGET_OPTIONS: '--no-verbose --no-use-server-timestamps --retry-connrefused --retry-on-host-error'
# To ensure that "false && true" fails, see https://gitlab.com/gitlab-org/gitlab-runner/-/issues/25394#note_412609647
FF_ENABLE_BASH_EXIT_CODE_CHECK: 'true'
build_linux_x86_64:
stage: build
......@@ -46,6 +47,9 @@ build_macos_x86_64:
tags:
- macOS
script:
# Workaround for bug in Xcode 15.3 which does not include m4
# See https://github.com/Homebrew/homebrew-core/issues/165388 and https://trac.macports.org/ticket/69639
- export PATH="/usr/local/opt/m4/bin/:$PATH"
- arch -x86_64 meson setup -D buildtype=release --native-file scripts/homebrew-native-x86_64.ini build
- arch -x86_64 meson compile -C build -v
artifacts:
......@@ -58,8 +62,31 @@ build_macos_arm64:
- macOS
script:
- export PATH="/opt/homebrew/bin:$PATH"
# Workaround for bug in Xcode 15.3 which does not include m4
# See https://github.com/Homebrew/homebrew-core/issues/165388 and https://trac.macports.org/ticket/69639
- export PATH="/opt/homebrew/opt/m4/bin/:$PATH"
- arch -arm64 meson setup -D buildtype=release --native-file scripts/homebrew-native-arm64.ini build
- arch -arm64 meson compile -C build -v
artifacts:
paths:
- build/src/dynare-preprocessor
test_clang_format:
stage: test
script:
- meson setup build-clang-format
- ninja -C build-clang-format clang-format-check
needs: []
test_clang_tidy:
stage: test
script:
# Hack needed for meson < 1.6.0 which only looks for unversioned clang-tidy
- mkdir -p ~/.local/bin && ln -s /usr/bin/clang-tidy-19 ~/.local/bin/clang-tidy
- export PATH="$HOME/.local/bin:$PATH"
- meson setup build-clang-tidy
# Generate Flex and Bison files
- meson compile -C build-clang-tidy
- ninja -C build-clang-tidy clang-tidy
needs: []
when: manual
......@@ -15,7 +15,7 @@
\pgfdeclareimage[height=0.8cm]{logo}{dlogo}
\institute[Dynare Team]{\pgfuseimage{logo}}
\date{23 May 2023}
\date{31 May 2024}
\AtBeginSection[]
{
......@@ -156,9 +156,9 @@
\item comparison operators: \texttt{< > <= >= == !=}
\item logical operators: \verb+&& || !+
\item range with unit increment: \texttt{1:4} is equivalent to
real array \texttt{[1, 2, 3, 4]}. (NB: \texttt{[1:4]} is equivalent to an
real array \texttt{[1, 2, 3, 4]} \\ (NB: \texttt{[1:4]} is equivalent to an
array containing an array of reals, \textit{i.e.} \texttt{[[1, 2, 3, 4]]})
\item range with user-defined increment: \texttt{4:-1.1:-1} is equivalent to real array \texttt{[4, 2.9, 1.8, 0.7, -0.4]}.
\item range with user-defined increment: \\ \texttt{4:-1.1:-1} is equivalent to real array \texttt{[4, 2.9, 1.8, 0.7, -0.4]}
\end{itemize}
\end{block}
......@@ -314,7 +314,7 @@ Then \texttt{distance(3, 4)} will be equivalent to \texttt{5}.
\end{frame}
\begin{frame}[fragile=singleslide]
\frametitle{Defining macro-variables}
\frametitle{Defining macro-variables (1/2)}
The value of a macro-variable can be defined with the \verb+@#define+
directive.
......@@ -335,9 +335,31 @@ Then \texttt{distance(3, 4)} will be equivalent to \texttt{5}.
@#define t = ("US" in w) // Equals true
\end{verbatim}
\end{block}
NB: You can define macro variables on the Dynare command line by using the \texttt{-D} option
\end{frame}
\begin{frame}[fragile=singleslide]
\frametitle{Defining macro-variables (2/2)}
Macro variables can also be defined on the Dynare command line by using the
\texttt{-D} option, for easily switching between different flavours of a model.
\begin{block}{Example 1}
\begin{verbatim}
dynare myfile.mod -Dx=5
\end{verbatim}
The macro-variable \texttt{x} will be equal to \texttt{5} when running \texttt{myfile.mod}.
\end{block}
\begin{block}{Example 2}
Use single quotes around the \texttt{-D} option when there are spaces or
special characters in the variable definition.
\begin{verbatim}
dynare myfile.mod '-DA=[ i in [1,2,3] when i > 1 ]'
\end{verbatim}
The macro-variable \texttt{A} will be equal to \texttt{[2,3]} when running \texttt{myfile.mod}.
\end{block}
\end{frame}
\begin{frame}[fragile=singleslide]
\frametitle{Expression substitution}
\framesubtitle{Dummy example}
......@@ -611,7 +633,7 @@ end;
\begin{frame}
\frametitle{Macro-related command line options}
\begin{itemize}
\item \texttt{savemacro}: Useful for debugging or learning purposes, saves the output of the macro processor. If your \texttt{.mod} file is called \texttt{file.mod}, the output is saved to \texttt{file-macroexp.mod}.
\item \texttt{savemacro}: Useful for debugging or learning purposes, saves the output of the macro processor. If your \texttt{.mod} file is called \texttt{file.mod}, the output is saved to \texttt{file\_macroexp.mod}.
\item NB: \texttt{savemacro=filename} allows a user-defined file name
\item \texttt{linemacro}: In the output of \texttt{savemacro}, print line numbers where the macro directives were placed.
\item \texttt{onlymacro}: Stops processing after the macro processing step.
......@@ -866,7 +888,7 @@ rhos = [ 0.8, 0.9, 1];
\ccbysa
\column{0.71\textwidth}
\tiny
Copyright © 2008-2023 Dynare Team \\
Copyright © 2008-2024 Dynare Team \\
License: \href{http://creativecommons.org/licenses/by-sa/4.0/}{Creative
Commons Attribution-ShareAlike 4.0}
\end{columns}
......
\documentclass{beamer}
\documentclass[aspectratio=169]{beamer}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{lmodern}
......@@ -9,6 +9,8 @@
\usepackage[english]{babel}
\usepackage[utf8]{inputenc}
\newenvironment{witemize}{\itemize\addtolength{\itemsep}{8pt}}{\enditemize}
\newenvironment{wenumerate}{\enumerate\addtolength{\itemsep}{8pt}}{\enditemize}
\graphicspath{{../logos}}
......@@ -69,7 +71,7 @@
\ccbysa
\column{0.71\textwidth}
\tiny
Copyright © 2007--2019 Dynare Team \\
Copyright © 2007--2023 Dynare Team \\
Licence: \href{http://creativecommons.org/licenses/by-sa/4.0/}{Creative
Commons Attribution-ShareAlike 4.0}
\end{columns}
......@@ -90,35 +92,106 @@
\begin{frame}
\frametitle{Calling Dynare}
\begin{itemize}
\begin{witemize}
\item Dynare is called from the host language platform with the syntax \texttt{dynare <<filename>>.mod}
\item This call can be followed by certain options:
\begin{itemize}
\item Some of these options impact host language platform functionality, \textit{e.g.} \texttt{nograph} prevents graphs from being displayed in MATLAB
\item Some cause differences in the output created by default, \textit{e.g.} \texttt{notmpterms} prevents temporary terms from being written to the static/dynamic files
\item While others impact the functionality of the macroprocessor or the preprocessor, \textit{e.g.} \texttt{nostrict} shuts off certain checks that the preprocessor does by defalut
\item Some of these options impact host language platform functionality\\
$\rightarrow$ \textit{e.g.} \texttt{nograph} prevents graphs from being displayed in MATLAB
\item Some cause differences in the output created by default\\
$\rightarrow$ \texttt{notmpterms} prevents temporary terms from being written to the static/dynamic files
\item While others impact the functionality of the macroprocessor or the preprocessor\\
$\rightarrow$ \textit{e.g.} \texttt{nostrict} shuts off certain checks that the preprocessor does by default
\end{itemize}
\item MATLAB command line supports syntax completion since Dynare 6.x
\end{witemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Command line options in the mod-file}
\begin{witemize}
\item Command line options can alternatively be defined in the first line of the .mod file
\item Avoids to always have to invoke an option at the command line\\
$\rightarrow$ particularly useful for the \texttt{nostrict} option during the development phase of a model
\item Definition must be
\begin{itemize}
\item a one-line Dynare comment, i.e. begin //
\item the options must be enclosed in \texttt{----+ options:} and \texttt{+----} and must be whitespace separated
\item As in the command line, if an option admits a value, the equal symbol must not be surrounded by spaces
\end{itemize}
\begin{block}{Example}
\begin{verbatim}
// --+ options: json=compute, stochastic, nostrict +--
\end{verbatim}
\end{block}
\end{witemize}
\end{frame}
\section{Macro processing}
\begin{frame}
\frametitle{Macro processing}
\begin{witemize}
\item The Dynare macro language provides a set of macro commands that can be used in \texttt{mod} files
\item The macro processor employs text expansions/inclusions to transform a \texttt{mod} file with macro commands into a \texttt{mod} file without macro commands\\
$\rightarrow$ result can be stored using \texttt{savemacro} option
\item The result is fed to the parser\\
$\rightarrow$ use \texttt{onlymacro} to stop after macro processing before parsing
\item Attention: the macro processor only does text substitution; objects computed in later steps are not available yet and cannot be conditioned on for that reason
\end{witemize}
\end{frame}
\section{Parsing}
\begin{frame}
\frametitle{Parsing overview}
\begin{itemize}
\begin{witemize}
\item Parsing is the action of transforming an input text (a \texttt{mod} file in our case) into a data structure suitable for computation
\item The parser consists of three components:
\begin{itemize}
\begin{wenumerate}
\item the \alert{lexical analyzer}, which recognizes the ``words'' of the \texttt{mod} file (analog to the \textit{vocabulary} of a language)
\item the \alert{syntax analyzer}, which recognizes the ``sentences'' of the \texttt{mod} file (analog to the \textit{grammar} of a language)
\item the \alert{parsing driver}, which coordinates the whole process and constructs the data structure using the results of the lexical and syntax analyses
\end{itemize}
\end{itemize}
\end{wenumerate}
\end{witemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Undesired Parsing}
\begin{witemize}
\item Dynare will try to parse all tokens it recognizes
\item Code not recognized by the parser is directly passed to the preprocessor output\\
$\rightarrow$ allows using MATLAB/Octave commands directly in the mod-file
\item Causes problems when one wants to invoke commands containing recognized tokens
\end{witemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Bypassing the parsing}
\begin{witemize}
\item The \texttt{verbatim} block instructs Dynare not to parse text contained in it
\item In the following example, \texttt{stoch\_simul} would otherwise be recognized as a Dynare command during parsing
\end{witemize}
\begin{block}{Verbatim example}
\begin{verbatim}
verbatim;
rhos = [ 0.8, 0.9, 1];
for iter = 1:length(rhos)
set_param_value('rho',rhos(iter));
[info, oo_, options_, M_] = stoch_simul(M_, options_, oo_, var_list_)
if info(1)~=0
error('Simulation failed for parameter draw')
end
end
end;
\end{verbatim}
\end{block}
\end{frame}
\begin{frame}
\frametitle{Lexical analysis}
\begin{itemize}
\frametitle{1.\ Lexical analysis}
\begin{witemize}
\item The lexical analyzer recognizes the ``words'' (or \alert{lexemes}) of the language
\item Defined in \texttt{DynareFlex.ll}, it is transformed into C++ source code by the program \texttt{flex}
\item This file details the list of known lexemes (described by regular expressions) and the associated \alert{token} for each of them
......@@ -126,7 +199,7 @@
\item For variable names or numbers, the token also contains the associated string for further processing
%\item \textit{Note:} the list of tokens can be found at the beginning of \texttt{DynareBison.yy}
\item When invoked, the lexical analyzer reads the next characters of the input, tries to recognize a lexeme, and either produces an error or returns the associated token
\end{itemize}
\end{witemize}
\end{frame}
\begin{frame}[fragile]
......@@ -161,7 +234,7 @@ SEMICOLON
\end{frame}
\begin{frame}
\frametitle{Syntax analysis}
\frametitle{2.\ Syntax analysis}
\framesubtitle{In Dynare}
\begin{itemize}
\item The \texttt{mod} file grammar is described in \texttt{DynareBison.yy}, which is transformed into C++ source code by the program \texttt{bison}
......@@ -203,13 +276,13 @@ expression := expression PLUS expression
\begin{frame}
\frametitle{Semantic actions}
\begin{itemize}
\item So far we have only described how to accept valid \texttt{mod} files and to reject others
\begin{witemize}
\item So far we have only described how to accept valid \texttt{mod} files and reject others
\item But validating is not enough: one needs to do something with the parsed \texttt{mod} file
\item Every grammar rule can have a \alert{semantic action} associated with it: C/C++ code enclosed by curly braces
\item Every rule can return a semantic value (referenced by \texttt{\$\$} in the action)
\item In the action, it is possible to refer to semantic values returned by components of the rule (using \texttt{\$1}, \texttt{\$2}, \ldots)
\end{itemize}
\end{witemize}
\end{frame}
\begin{frame}[fragile]
......@@ -240,15 +313,15 @@ expression := expression PLUS expression
\end{frame}
\begin{frame}
\frametitle{Parsing driver}
\frametitle{3.\ Parsing driver}
The class \texttt{ParsingDriver} has the following roles:
\begin{itemize}
\begin{witemize}
\item It opens the \texttt{mod} file and launches the lexical and syntaxic analyzers on it
\item It implements most of the semantic actions of the grammar
\item By doing so, it creates an object of type \texttt{ModFile}, which is the data structure representing the \texttt{mod} file
\item Or, if there is a parsing error (unknown keyword, undeclared symbol, syntax error), it displays the line and column numbers where the error occurred and exits
\end{itemize}
\end{witemize}
\end{frame}
\section{Data structure representing a \texttt{mod} file}
......@@ -272,7 +345,7 @@ The class \texttt{ParsingDriver} has the following roles:
\begin{frame}
\frametitle{The symbol table (1/3)}
\begin{itemize}
\begin{witemize}
\item A \alert{symbol} is simply the name of a variable (endogenous, exogenous, local, auxiliary, etc), parameter, external function, \ldots basically everything that is not recognized as a Dynare keyword
\item \alert{SymbolTable} is a simple class used to maintain the list of the symbols used in the \texttt{mod} file
\item For each symbol, it stores:
......@@ -281,7 +354,7 @@ The class \texttt{ParsingDriver} has the following roles:
\item its type (an enumerator defined in \texttt{CodeInterpreter.hh})
\item a unique integer identifier (also has a unique identifier by type)
\end{itemize}
\end{itemize}
\end{witemize}
\end{frame}
\begin{frame}
......@@ -303,20 +376,20 @@ The class \texttt{ParsingDriver} has the following roles:
\begin{frame}
\frametitle{The symbol table (3/3)}
\begin{itemize}
\begin{witemize}
\item Symbol table filled in:
\begin{itemize}
\begin{witemize}
\item using the \texttt{var}, \texttt{varexo}, \texttt{varexo\_det}, \texttt{parameter}, \texttt{external\_function}, \texttt{trend\_var}, and \texttt{log\_trend\_var} declarations
\item using pound sign (\#) constructions in the model block
\item using pound sign (\#) constructions (``model-local variables'') in the model block
\item on the fly during parsing: local variables outside models or unknown functions when an undeclared symbol is encountered
\item during the creation of auxiliary variables in the transform pass
\end{itemize}
\end{witemize}
\item Roles of the symbol table:
\begin{itemize}
\item permits parcimonious and more efficient representation of expressions (no need to duplicate or compare strings, only handle a pair of integers)
\begin{witemize}
\item permits parsimonious and more efficient representation of expressions (no need to duplicate or compare strings, only handle a pair of integers)
\item ensures that a given symbol is used with only one type
\end{itemize}
\end{itemize}
\end{witemize}
\end{witemize}
\end{frame}
\begin{frame}
......@@ -335,30 +408,30 @@ The class \texttt{ParsingDriver} has the following roles:
\begin{frame}
\frametitle{Expression trees (2/3)}
\begin{itemize}
\begin{witemize}
\item A tree node is represented by an instance of the abstract class \texttt{ExprNode}
\item This class has 5 sub-classes, corresponding to the 5 types of non-external-function nodes:
\begin{itemize}
\begin{wenumerate}
\item \texttt{NumConstNode} for constant nodes: contains the identifier of the numerical constants it represents
\item \texttt{VariableNode} for variable/parameters nodes: contains the identifier of the variable or parameter it represents
\item \texttt{UnaryOpNode} for unary operators (\textit{e.g.} unary minus, $\log$, $\sin$): contains an enumerator representing the operator, and a pointer to its child
\item \texttt{BinaryOpNode} for binary operators (\textit{e.g.} $+$, $*$, pow): contains an enumerator representing the operator, and pointers to its two children
\item \texttt{TrinaryOpNode} for trinary operators (\textit{e.g.} $normcdf$, $normpdf$): contains an enumerator representing the operator and pointers to its three children
\end{itemize}
\end{itemize}
\end{wenumerate}
\end{witemize}
\end{frame}
\begin{frame}
\frametitle{Expression trees (3/3)}
\begin{itemize}
\begin{witemize}
\item The abstract class \texttt{ExprNode} has an abstract sub-class called \texttt{AbstractExternalFunctionNode}
\item This abstract sub-class has 3 sub-classes, corresponding to the 3 types of external function nodes:
\begin{itemize}
\begin{wenumerate}
\item \texttt{ExternalFunctionNode} for external functions. Contains the identifier of the external function and a vector of its arguments
\item \texttt{FirstDerivExternalFunctionNode} for the first derivative of an external function. In addition to the information contained in \texttt{ExternalFunctionNode}, contains the index w.r.t. which this node is the derivative.
\item \texttt{SecondDerivExternalFunctionNode} for the second derivative of an external function. In addition to the information contained in \texttt{FirstDerivExternalFunctionNode}, contains the index w.r.t. which this node is the second derivative.
\end{itemize}
\end{itemize}
\end{wenumerate}
\end{witemize}
\end{frame}
......@@ -385,28 +458,28 @@ The class \texttt{ParsingDriver} has the following roles:
\begin{frame}
\frametitle{Constructing expression trees}
\begin{itemize}
\begin{witemize}
\item Class \texttt{DataTree} contains a set of methods for constructing expression trees
\item Construction is done bottom-up, node by node:
\begin{itemize}
\begin{witemize}
\item one method for adding a constant node (\texttt{AddPossiblyNegativeConstant(double)})
\item one method for a log node (\texttt{AddLog(arg)})
\item one method for a plus node (\texttt{AddPlus(arg1, arg2)})
\end{itemize}
\end{witemize}
\item These methods take pointers to \texttt{ExprNode}, allocate the memory for the node, construct it, and return its pointer
\item These methods are called:
\begin{itemize}
\begin{witemize}
\item from \texttt{ParsingDriver} in the semantic actions associated to the parsing of expressions
\item during symbolic derivation, to create derivatives expressions
\item during symbolic derivation to create derivatives expressions
\item when creating the static model from the dynamic model
\item \ldots
\end{itemize}
\end{itemize}
\end{witemize}
\end{witemize}
\end{frame}
\begin{frame}
\frametitle{Reduction of constants and symbolic simplifications}
\begin{itemize}
\begin{witemize}
\item The construction methods compute constants whenever possible
\begin{itemize}
\item Suppose you ask to construct the node $1+1$
......@@ -423,19 +496,20 @@ The class \texttt{ParsingDriver} has the following roles:
\item $x^0 = 1$
\end{itemize}
\item When a simplification rule applies, no new node is created
\end{itemize}
\item Attention: this may cause the program to detect issues not directly visible to users like a division by 0
\end{witemize}
\end{frame}
\begin{frame}
\frametitle{Sub-expression sharing (1/2)}
\begin{itemize}
\begin{witemize}
\item Consider the two following expressions: $(1+z)*\log(y)$ and $2^{(1+z)}$
\item Expressions share a common sub-expression: $1+z$
\item The internal representation of these expressions is:
\begin{center}
\includegraphics[width=7cm]{expr-sharing.png}
\end{center}
\end{itemize}
\end{witemize}
\end{frame}
\begin{frame}
......@@ -456,107 +530,107 @@ The class \texttt{ParsingDriver} has the following roles:
\begin{frame}
\frametitle{Final remarks about expressions}
\begin{itemize}
\begin{witemize}
\item Storage of negative constants
\begin{itemize}
\begin{witemize}
\item class \texttt{NumConstNode} only accepts positive constants
\item a negative constant is stored as a unary minus applied to a positive constant
\item this is a kind of identification constraint to avoid having two ways of representing negative constants: $(-2)$ and $-(2)$
\end{itemize}
\end{witemize}
\item Widely used constants
\begin{itemize}
\begin{witemize}
\item class \texttt{DataTree} has attributes containing pointers to constants: $0$, $1$, $2$, $-1$, \texttt{NaN}, $\infty$, $-\infty$, and $\pi$
\item these constants are used in many places (in simplification rules, in derivation algorithm\ldots)
\item sub-expression sharing algorithm ensures that these constants will never be duplicated
\end{itemize}
\end{itemize}
\end{witemize}
\end{witemize}
\end{frame}
\begin{frame}
\frametitle{List of statements}
\begin{itemize}
\begin{witemize}
\item A statement is represented by an instance of a subclass of the abstract class \texttt{Statement}
\item Three groups of statements:
\begin{itemize}
\item initialization statements (parameter initialization with $p = \ldots$, \texttt{initval}, \texttt{histval}, or \texttt{endval} block)
\item shocks blocks (\texttt{shocks}, \texttt{mshocks}, \ldots)
\item computing tasks (\texttt{steady}, \texttt{check}, \texttt{simul}, \ldots)
\item computing tasks (\texttt{steady}, \texttt{check}, \texttt{perfect\_foresight\_solver}, \ldots)
\end{itemize}
\item Each type of statement has its own class (\textit{e.g.} \texttt{InitValStatement}, \texttt{SimulStatement}, \ldots)
\item Each type of statement has its own class (\textit{e.g.} \texttt{InitValStatement}, \texttt{PerfectForesightSolverStatement}, \ldots)
\item The class \texttt{ModFile} stores a list of pointers of type \texttt{Statement*}, corresponding to the statements of the \texttt{mod} file, in their order of declaration
\item Heavy use of polymorphism in the check pass, computing pass, and when writing outputs: abstract class \texttt{Statement} provides a virtual method for these 3 actions
\end{itemize}
\end{witemize}
\end{frame}
\begin{frame}
\frametitle{Evaluation context}
\begin{itemize}
\begin{witemize}
\item The \texttt{ModFile} class contains an \alert{evaluation context}
\item It is a map associating a numerical value to some symbols
\item Filled in with \texttt{initval} block values and parameter initializations
\item Used during equation normalization (in the block decomposition), for finding non-zero entries in the jacobian
\item Used in testing that trends are compatible with a balanced growth path, for finding non-zero cross partials of equations with respect to trend variables and endogenous varibales
\end{itemize}
\item Used during equation normalization (in the block decomposition), for finding non-zero entries in the Jacobian
\item Used in testing that trends are compatible with a balanced growth path, for finding non-zero cross partials of equations with respect to trend variables and endogenous variables
\end{witemize}
\end{frame}
\section{Check pass}
\begin{frame}
\frametitle{Error checking during parsing}
\begin{itemize}
\begin{witemize}
\item Some errors in the \texttt{mod} file can be detected during parsing:
\begin{itemize}
\begin{witemize}
\item syntax errors
\item use of undeclared symbols in model block, initval block\ldots
\item use of a symbol incompatible with its type (\textit{e.g.} parameter in initval, local variable used both in model and outside model)
\item multiple shock declarations for the same variable
\end{itemize}
\end{witemize}
\item But some other checks can only be done when parsing is completed\ldots
\end{itemize}
\end{witemize}
\end{frame}
\begin{frame}
\frametitle{Check pass}
\begin{itemize}
\begin{witemize}
\item The check pass is implemented through the method \texttt{ModFile::checkPass()}
\item Performs many checks. Examples include:
\begin{itemize}
\begin{witemize}
\item check there is at least one equation in the model (except if doing a standalone BVAR estimation)
\item checks for coherence in statements (\textit{e.g.} options passed to statements do not conflict with each other, required options have been passed)
\item checks for coherence among statements (\textit{e.g.} if \texttt{osr} statement is present, ensure \texttt{osr\_params} and \texttt{optim\_weights} statements are present)
\item checks for coherence between statements and attributes of \texttt{mod} file (\textit{e.g.} \texttt{use\_dll} is not used with \texttt{block} or \texttt{bytecode})
\end{itemize}
\end{itemize}
\end{witemize}
\end{witemize}
\end{frame}
\section{Transform pass}
\begin{frame}
\frametitle{Transform pass (1/2)}
\begin{itemize}
\begin{witemize}
\item The transform pass is implemented through the method \texttt{ModFile::transformPass(bool nostrict)}
\item It makes necessary transformations (notably to the dynamic model, symbol table, and statements list) preparing the \texttt{ModFile} object for the computing pass. Examples of transformations include:
\begin{itemize}
\item It makes necessary transformations (notably to the dynamic model, symbol table, and statements list), preparing the \texttt{ModFile} object for the computing pass. Examples of transformations include:
\begin{witemize}
\item creation of auxiliary variables and equations for leads, lags, expectation operator, differentiated forward variables, etc.
\item detrending of model equations if nonstationary variables are present
\item decreasing leads/lags of predetermined variables by one period
\item addition of FOCs of Langrangian for Ramsey problem
\item addition of FOCs of Lagrangian for Ramsey problem
\item addition of \texttt{dsge\_prior\_weight} initialization before all other statements if estimating a DSGE-VAR where the weight of the DSGE prior of the VAR is calibrated
\end{itemize}
\end{itemize}
\end{witemize}
\end{witemize}
\end{frame}
\begin{frame}
\frametitle{Transform pass (2/2)}
\begin{itemize}
\begin{witemize}
\item It then freezes the symbol table, meaning that no more symbols can be created on the \texttt{ModFile} object
\item Finally checks are performed on the transformed model. Examples include:
\begin{itemize}
\item same number of endogenous varibables as equations (not done in certain situations, \textit{e.g.} Ramsey, discretionary policy, etc.)
\item correspondence among variables and statements, \textit{e.g.} Ramsey policy, identification, perfect foresight solver, and simul are incompatible with deterministic exogenous variables
\item correspondence among statements, \textit{e.g.} for DSGE-VAR without \texttt{bayesian\_irf} option, the number of shocks must be greater than or equal to the number of observed variables
\end{itemize}
\end{itemize}
\item Finally, checks are performed on the transformed model. Examples include:
\begin{witemize}
\item same number of endogenous variables as equations (not done in certain situations, \textit{e.g.} Ramsey, discretionary policy, where checks are done in MATLAB)
\item correspondence among variables and statements, \textit{e.g.} Ramsey policy, identification, perfect foresight solver, and perfect foresight solver are incompatible with deterministic exogenous variables
\item correspondence among statements, \textit{e.g.} for DSGE-VAR with \texttt{bayesian\_irf} option, the number of shocks must be equal to the number of observed variables
\end{witemize}
\end{witemize}
\end{frame}
......@@ -566,9 +640,9 @@ The class \texttt{ParsingDriver} has the following roles:
\frametitle{Overview of the computing pass}
\begin{itemize}
\item Computing pass implemented in \texttt{ModFile::computingPass()}
\item Creates Static model from Dynamic (by removing leads/lags)
\item Creates static model from dynamic one (by removing leads/lags)
\item Determines which derivatives to compute
\item Then calls \texttt{DynamicModel::computingPass()} which computes:
\item Then calls \texttt{DynamicModel::computingPass()}, which computes:
\begin{itemize}
\item leag/lag variable incidence matrix
\item symbolic derivatives w.r.t. endogenous, exogenous, and parameters, if needed
......@@ -576,34 +650,34 @@ The class \texttt{ParsingDriver} has the following roles:
\item temporary terms
\item computes equation cross references, if desired
\end{itemize}
\item NB: analagous operations for Static model are performed by \texttt{StaticModel::computingPass()}
\item Asserts that equations declared linear are indeed linear (by checking that Hessian == 0)
\item NB: analogous operations for static model are performed by \texttt{StaticModel::computingPass()}
\item Asserts that equations declared \texttt{linear} are indeed linear (by checking that Hessian == 0)
\item Finally, calls \texttt{Statement::computingPass()} on all statements
\end{itemize}
\end{frame}
\begin{frame}
\frametitle{Model Variables}
\begin{itemize}
\begin{witemize}
\item In the context of class \texttt{ModelTree}, a \alert{variable} is a pair (symbol, lag)
\item The symbol must correspond to a variable of type endogenous, exogenous, deterministic exogenous variable, or parameter
\item The \texttt{SymbolTable} class keeps track of valid symbols while the \texttt{variable\_node\_map} keeps track of model variables (symbol, lag pairs stored in \texttt{VariableNode} objects)
\item The \texttt{SymbolTable} class keeps track of valid symbols, while the \texttt{variable\_node\_map} keeps track of model variables (symbol, lag pairs stored in \texttt{VariableNode} objects)
\item After the computing pass, the \texttt{DynamicModel} class writes the leag/lag incidence matrix:
\begin{itemize}
\item three rows: the first row indicates $t-1$, the second row $t$, and the third row $t+1$
\begin{witemize}
\item \texttt{max\_lag + max\_lead + 1} rows (usually 3): the first row indicates $t-1$ (if applicable), the second row $t$, and the third row $t+1$ (if applicable)
\item one column for every endogenous symbol in order of declaration; NB: includes endogenous auxiliary variables created during the transform pass
\item elements of the matrix are either 0 (if the variable does not appear in the model) or correspond to the variable's column in the Jacobian of the dynamic model
\end{itemize}
\end{itemize}
\end{witemize}
\end{witemize}
\end{frame}
\begin{frame}
\frametitle{Static versus dynamic model}
\begin{itemize}
\begin{witemize}
\item The static model is simply the dynamic model without leads and lags
\item Static model used to characterize the steady state
\item The jacobian of the static model is used in the (MATLAB) solver for determining the steady state
\end{itemize}
\item The Jacobian of the static model is used in the (MATLAB) solver for determining the steady state
\end{witemize}
\begin{block}{Example}
\begin{itemize}
\item suppose dynamic model is $2x_t \cdot x_{t-1} = 0$
......@@ -616,41 +690,41 @@ The class \texttt{ParsingDriver} has the following roles:
\begin{frame}
\frametitle{Which derivatives to compute?}
\begin{itemize}
\begin{witemize}
\item In deterministic mode:
\begin{itemize}
\item static jacobian w.r.t. endogenous variables only
\item dynamic jacobian w.r.t. endogenous variables only
\item static Jacobian w.r.t. endogenous variables only
\item dynamic Jacobian w.r.t. endogenous variables only
\end{itemize}
\item In stochastic mode:
\begin{itemize}
\item static jacobian w.r.t. endogenous variables only
\item dynamic jacobian w.r.t. endogenous, exogenous, and deterministic exogenous variables
\item dynamic hessian w.r.t. endogenous, exogenous, and deterministic exogenous variables
\item static Jacobian w.r.t. endogenous variables only
\item dynamic Jacobian w.r.t. endogenous, exogenous, and deterministic exogenous variables
\item dynamic Hessian w.r.t. endogenous, exogenous, and deterministic exogenous variables
\item possibly dynamic 3rd derivatives (if \texttt{order} option $\geq 3$)
\item possibly dynamic jacobian and/or hessian w.r.t. parameters (if \texttt{identification} or analytic derivs needed for \texttt{estimation} and \texttt{params\_derivs\_order} $>0$)
\item possibly dynamic Jacobian and/or Hessian w.r.t. parameters (if \texttt{identification} or analytic derivs needed for \texttt{estimation} and \texttt{params\_derivs\_order} $>0$)
\end{itemize}
\item For Ramsey policy: the same as above, but with one further order of derivation than declared by the user with \texttt{order} option (the derivation order is determined in the check pass, see \texttt{RamseyPolicyStatement::checkPass()})
\end{itemize}
\end{witemize}
\end{frame}
\begin{frame}
\frametitle{Derivation algorithm (1/2)}
\begin{itemize}
\begin{witemize}
\item Derivation of the model implemented in \texttt{ModelTree::computeJacobian()}, \texttt{ModelTree::computeHessian()}, \texttt{ModelTree::computeThirdDerivatives()}, and \texttt{ModelTree::computeParamsDerivatives()}
\item Simply call \texttt{ExprNode::getDerivative(deriv\_id)} on each equation node
\item Use of polymorphism:
\begin{itemize}
\begin{witemize}
\item for a constant or variable node, derivative is straightforward ($0$ or $1$)
\item for a unary, binary, trinary op nodes and external function nodes, recursively calls method \texttt{computeDerivative()} on children to construct derivative
\end{itemize}
\end{itemize}
\end{witemize}
\end{witemize}
\end{frame}
\begin{frame}
\frametitle{Derivation algorithm (2/2)}
\framesubtitle{Optimizations}
\begin{itemize}
\begin{witemize}
\item Caching of derivation results
\begin{itemize}
\item method \texttt{ExprNode::getDerivative(deriv\_id)} memorizes its result in a member attribute (\texttt{derivatives}) the first time it is called
......@@ -660,12 +734,12 @@ The class \texttt{ParsingDriver} has the following roles:
\item Efficiently finds symbolic derivatives equal to $0$
\begin{itemize}
\item consider the expression $x+y^2$
\item without any computation, you know its derivative w.r.t. $z$ is zero
\item without any computation, you know its derivative w.r.t.\ $z$ is zero
\item each node stores in an attribute (\texttt{non\_null\_derivatives}) the set of variables which appear in the expression it represents ($\{x,y\}$ in the example)
\item this set is computed in \texttt{prepareForDerivation()}
\item when \texttt{getDerivative(deriv\_id)} is called, immediately returns zero if \texttt{deriv\_id} is not in that set
\end{itemize}
\end{itemize}
\end{witemize}
\end{frame}
\begin{frame}[fragile]
......@@ -674,7 +748,7 @@ The class \texttt{ParsingDriver} has the following roles:
\item When the preprocessor writes equations and derivatives in its outputs, it takes advantage of sub-expression sharing
\item In MATLAB static and dynamic output files, equations are preceded by a list of \alert{temporary terms}
\item These terms are variables containing expressions shared by several equations or derivatives
\item Using these terms greatly enhances the computing speed of the model residual, jacobian, hessian, or third derivative
\item Using these terms greatly enhances the computing speed of the model residual, Jacobian, Hessian, or third derivative
\end{itemize}
\begin{block}{Example}
\begin{columns}[t]
......@@ -699,26 +773,27 @@ residual(1)=3*T1+1;
\begin{frame}
\frametitle{Temporary terms (2/2)}
\begin{itemize}
\item Expression storage in the preprocessor implements maximal sharing but this is not optimal for the MATLAB output files, because creating a temporary variable also has a cost (in terms of CPU and of memory)
\begin{witemize}
\item Expression storage in the preprocessor implements maximal sharing, but this is not optimal for the MATLAB output files, because creating a temporary variable also has a cost (in terms of CPU and of memory)
\item Computation of temporary terms implements a trade-off between:
\begin{itemize}
\begin{witemize}
\item cost of duplicating sub-expressions
\item cost of creating new variables
\end{itemize}
\end{witemize}
\item Algorithm uses a recursive cost calculation, which marks some nodes as being ``temporary''
\item \textit{Problem}: redundant with optimizations done by the C/C++ compiler (when Dynare is in DLL mode) $\Rightarrow$ compilation very slow on big models
\end{itemize}
\item \textit{Problem}: redundant with optimizations done by the C/C++ compiler (when Dynare is in DLL mode)\\
$\Rightarrow$ compilation very slow on big models
\end{witemize}
\end{frame}
\begin{frame}
\frametitle{The special case of Ramsey policy}
\begin{itemize}
\frametitle{The special case of Ramsey model}
\begin{witemize}
\item For most statements, the method \texttt{computingPass()} is a no-op\ldots
\item \ldots except for \texttt{planner\_objective} statement, which serves to declare planner objective when doing optimal policy under commitment
\item Class \texttt{PlannerObjectiveStatement} contains an instance of \texttt{ModelTree}, which stores the objective function (\texttt{i.e.} only one equation in the tree)
\item Class \texttt{PlannerObjectiveStatement} contains an instance of \texttt{ModelTree} which stores the objective function (\texttt{i.e.} only one equation in the tree)
\item During the computing pass, triggers the computation of the first and second order (static) derivatives of the objective
\end{itemize}
\end{witemize}
\end{frame}
\section{Writing outputs}
......@@ -727,8 +802,8 @@ residual(1)=3*T1+1;
\frametitle{Output overview}
\begin{itemize}
\item Implemented in \texttt{ModFile::writeOutputFiles()}
\item If \texttt{mod} file is \texttt{model.mod}, all created filenames will begin with \texttt{model}
\item Main output file is \texttt{model.m}, containing:
\item If \texttt{mod} file is \texttt{mymodel.mod}, all created filenames will begin with \texttt{mymodel}
\item Main output file is \texttt{mymodel.driver}, containing:
\begin{itemize}
\item general initialization commands
\item symbol table output (from \texttt{SymbolTable::writeOutput()})
......@@ -737,11 +812,12 @@ residual(1)=3*T1+1;
\end{itemize}
\item Subsidiary output files:
\begin{itemize}
\item one for the static model
\item one for the dynamic model
\item one for the auxiliary variables
\item for the static model (residuals, temporary terms, derivatives)
\item for the dynamic model (residuals, temporary terms, derivatives)
\item one for the auxiliary variables in the dynamic model (if relevant)
\item one for the steady state file (if relevant)
\item one for the planner objective (if relevant)
\item for the planner objective and Lagrange multipliers (static residuals and derivatives, if relevant)
\item one each for the static and dynamic parameter derivatives (if required)
\end{itemize}
\end{itemize}
\end{frame}
......@@ -831,7 +907,7 @@ residual(1)=3*T1+1;
\item No divergence of codebase: don't need to repeat code (checks, transformations, etc.) across platforms
\item Creates \texttt{mod} files that can be used on other host language platforms
\item Adds one more HLP library to distribute
\item Need to design/implement classes that will store processed dynare \texttt{mod} file in various HLPs
\item Need to design/implement classes that will store processed Dynare \texttt{mod} file in various HLPs
\end{itemize}
\end{frame}
......
......@@ -2,7 +2,8 @@
# It is not used when building Dynare as a whole.
project('dynare-preprocessor', 'cpp',
version : '6-unstable',
version : '7-unstable',
# NB: update C++ standard in .clang-format whenever the following is modified
default_options : [ 'cpp_std=gnu++20', 'warning_level=2' ],
meson_version : '>=0.64.0')
......
# Meson native file for compiling under Homebrew / arm64
[binaries]
cpp = '/opt/homebrew/bin/g++-13'
cpp = '/opt/homebrew/bin/g++-14'
flex = '/opt/homebrew/opt/flex/bin/flex'
bison = '/opt/homebrew/opt/bison/bin/bison'
......
# Meson native file for compiling under Homebrew / x86-64
[binaries]
cpp = '/usr/local/bin/g++-13'
cpp = '/usr/local/bin/g++-14'
flex = '/usr/local/opt/flex/bin/flex'
bison = '/usr/local/opt/bison/bin/bison'
......
# Meson cross file for creating a WebAssembly version of the preprocessor.
#
# Requires emscripten to be installed.
# Was successfully tested with emscripten 3.1.69 installed through emsdk
# tool, as described on: https://emscripten.org/docs/getting_started/downloads.html
# Don’t forget to source script snippet in current shell before running meson.
#
# Compilation creates a .wasm and .js wrapper under <builddir>/src/
#
# Can be run locally with node.js using:
# node dynare-preprocessor.js file.mod
# NB: a version of node.js is shipped with emscripten (under the node/
# subdirectory), but another version should also work.
[binaries]
cpp = 'em++'
[host_machine]
system = 'emscripten'
# Could be changed to wasm64 if 4GB memory constraint is hit
# Some background: https://v8.dev/blog/4gb-wasm-memory
cpu_family = 'wasm32'
cpu = 'wasm32'
endian = 'little'
[built-in options]
# Never do a debug build, because otherwise the lack of optimisations can
# overflow the memory capacities.
buildtype = 'release'
# The -fexceptions flag (for both compilation and linking) is needed for an
# unknown reason (C++ compilers are supposed to always add exception support).
# The -Wno-unqualified-std-cast-call flag removes many warnings about “move”
# not being qualified with “std::” namespace.
# The -fexperimental-library flag is needed to get std::jthread support (it was
# supposed to no longer be necessary for LLVM 20, but for some reason we still
# need it).
cpp_args = [ '-fexceptions', '-Wno-unqualified-std-cast-call', '-fexperimental-library' ]
# NODERAWFS=1 is needed for accessing the local filesystem
cpp_link_args = [ '-s', 'NODERAWFS=1', '-fexceptions' ]
[properties]
# It’s necessary to use a different copy of Boost than the one under
# /usr/include, because otherwise GCC headers confuse Clang
boost_root = '/tmp/boost_1_86_0'
/*
* Copyright © 2022-2023 Dynare Team
* Copyright © 2022-2024 Dynare Team
*
* This file is part of Dynare.
*
......@@ -17,14 +17,17 @@
* along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/
#include <iostream>
#include <ios>
#include <cstdlib>
#include <algorithm>
#include <cstdlib>
#include <ios>
#include <iostream>
#include "Bytecode.hh"
BytecodeWriter::BytecodeWriter(const filesystem::path &filename)
namespace Bytecode
{
Writer::Writer(const filesystem::path& filename)
{
open(filename, ios::out | ios::binary);
if (!is_open())
......@@ -35,17 +38,16 @@ BytecodeWriter::BytecodeWriter(const filesystem::path &filename)
}
template<>
BytecodeWriter &
operator<<(BytecodeWriter &code_file, const FCALL_ &instr)
Writer&
operator<<(Writer& code_file, const FCALL& instr)
{
code_file.instructions_positions.push_back(code_file.tellp());
auto write_member = [&code_file](const auto &member)
{
code_file.write(reinterpret_cast<const char *>(&member), sizeof member);
auto write_member = [&code_file](const auto& member) {
code_file.write(reinterpret_cast<const char*>(&member), sizeof member);
};
write_member(instr.op_code);
write_member(instr.tag);
write_member(instr.nb_output_arguments);
write_member(instr.nb_input_arguments);
write_member(instr.indx);
......@@ -56,33 +58,32 @@ operator<<(BytecodeWriter &code_file, const FCALL_ &instr)
int size = static_cast<int>(instr.func_name.size());
write_member(size);
code_file.write(instr.func_name.c_str(), size+1);
code_file.write(instr.func_name.c_str(), size + 1);
size = static_cast<int>(instr.arg_func_name.size());
write_member(size);
code_file.write(instr.arg_func_name.c_str(), size+1);
code_file.write(instr.arg_func_name.c_str(), size + 1);
return code_file;
}
template<>
BytecodeWriter &
operator<<(BytecodeWriter &code_file, const FBEGINBLOCK_ &instr)
Writer&
operator<<(Writer& code_file, const FBEGINBLOCK& instr)
{
code_file.instructions_positions.push_back(code_file.tellp());
auto write_member = [&code_file](const auto &member)
{
code_file.write(reinterpret_cast<const char *>(&member), sizeof member);
auto write_member = [&code_file](const auto& member) {
code_file.write(reinterpret_cast<const char*>(&member), sizeof member);
};
write_member(instr.op_code);
write_member(instr.tag);
write_member(instr.size);
write_member(instr.type);
for (int i = 0; i < instr.size; i++)
{
write_member(instr.variable[i]);
write_member(instr.equation[i]);
write_member(instr.variables[i]);
write_member(instr.equations[i]);
}
if (instr.type == BlockSimulationType::solveTwoBoundariesSimple
|| instr.type == BlockSimulationType::solveTwoBoundariesComplete
......@@ -96,8 +97,10 @@ operator<<(BytecodeWriter &code_file, const FBEGINBLOCK_ &instr)
write_member(instr.det_exo_size);
write_member(instr.exo_size);
for_each_n(instr.det_exogenous.begin(), instr.det_exo_size, write_member);
for_each_n(instr.exogenous.begin(), instr.exo_size, write_member);
ranges::for_each_n(instr.det_exogenous.begin(), instr.det_exo_size, write_member);
ranges::for_each_n(instr.exogenous.begin(), instr.exo_size, write_member);
return code_file;
}
}
/*
* Copyright © 2007-2023 Dynare Team
* Copyright © 2007-2024 Dynare Team
*
* This file is part of Dynare.
*
......@@ -17,748 +17,447 @@
* along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef _BYTECODE_HH
#define _BYTECODE_HH
#ifndef BYTECODE_HH
#define BYTECODE_HH
#include <concepts>
#include <filesystem>
#include <fstream>
#include <vector>
#include <utility>
#include <ios>
#include <filesystem>
#include <type_traits>
#include <utility>
#include <vector>
#include "CommonEnums.hh"
using namespace std;
// The different opcodes of bytecode
enum class Tags
{
FLDZ, // Loads a zero onto the stack
FLDC, // Loads a constant term onto the stack
FDIMT, // Defines the number of temporary terms - dynamic context (the period has to be indicated)
FDIMST, // Defines the number of temporary terms - static context (the period hasn’t to be indicated)
FLDT, // Loads a temporary term onto the stack - dynamic context (the period has to be indicated)
FLDST, // Loads a temporary term onto the stack - static context (the period hasn’t to be indicated)
FSTPT, // Stores a temporary term from the stack - dynamic context (the period has to be indicated)
FSTPST, // Stores a temporary term from the stack - static context (the period hasn’t to be indicated)
FLDU, // Loads an element of the vector U onto the stack - dynamic context (the period has to be indicated)
FLDSU, // Loads an element of the vector U onto the stack - static context (the period hasn’t to be indicated)
FSTPU, // Stores an element of the vector U from the stack - dynamic context (the period has to be indicated)
FSTPSU, // Stores an element of the vector U from the stack - static context (the period hasn’t to be indicated)
FLDV, // Loads a variable (described in SymbolType) onto the stack - dynamic context (the period has to be indicated)
FLDSV, // Loads a variable (described in SymbolType) onto the stack - static context (the period hasn’t to be indicated)
FLDVS, // Loads a variable (described in SymbolType) onto the stack - dynamic context but inside the STEADY_STATE operator (the period hasn’t to be indicated)
FSTPV, // Stores a variable (described in SymbolType) from the stack - dynamic context (the period has to be indicated)
FSTPSV, // Stores a variable (described in SymbolType) from the stack - static context (the period hasn’t to be indicated)
FLDR, // Loads a residual onto the stack
FSTPR, // Stores a residual from the stack
FSTPG, // Stores a derivative from the stack
FSTPG2, // Stores a derivative matrix for a static model from the stack
FSTPG3, // Stores a derivative matrix for a dynamic model from the stack
FUNARY, // A unary operator
FBINARY, // A binary operator
FTRINARY, // A trinary operator
FJMPIFEVAL, // Jump if evaluate = true
FJMP, // Jump
FBEGINBLOCK, // Marks the beginning of a model block
FENDBLOCK, // Marks the end of a model block
FENDEQU, // Marks the last equation of the block; for a block that has to be solved, the derivatives appear just after this flag
FEND, // Marks the end of the model code
FNUMEXPR, // Stores the expression type and references
FCALL, // Call an external function
FLDTEF, // Loads the result of an external function onto the stack
FSTPTEF, // Stores the result of an external function from the stack
FLDTEFD, // Loads the result of the 1st derivative of an external function onto the stack
FSTPTEFD, // Stores the result of the 1st derivative of an external function from the stack
FLDTEFDD, // Loads the result of the 2nd derivative of an external function onto the stack
FSTPTEFDD // Stores the result of the 2nd derivative of an external function from the stack
};
namespace Bytecode
{
// The different tags encoding a bytecode instruction
enum class Tag
{
FLDZ, // Loads a zero onto the stack
FLDC, // Loads a constant term onto the stack
FDIMT, // Defines the number of temporary terms - dynamic context (the period has to be indicated)
FDIMST, // Defines the number of temporary terms - static context (the period hasn’t to be
// indicated)
FLDT, // Loads a temporary term onto the stack - dynamic context (the period has to be indicated)
FLDST, // Loads a temporary term onto the stack - static context (the period hasn’t to be
// indicated)
FSTPT, // Stores a temporary term from the stack - dynamic context (the period has to be
// indicated)
FSTPST, // Stores a temporary term from the stack - static context (the period hasn’t to be
// indicated)
FLDU, // Loads an element of the vector U onto the stack - dynamic context (the period has to be
// indicated)
FLDSU, // Loads an element of the vector U onto the stack - static context (the period hasn’t to
// be indicated)
FSTPU, // Stores an element of the vector U from the stack - dynamic context (the period has to be
// indicated)
FSTPSU, // Stores an element of the vector U from the stack - static context (the period hasn’t to
// be indicated)
FLDV, // Loads a variable (described in SymbolType) onto the stack - dynamic context (the period
// has to be indicated)
FLDSV, // Loads a variable (described in SymbolType) onto the stack - static context (the period
// hasn’t to be indicated)
FLDVS, // Loads a variable (described in SymbolType) onto the stack - dynamic context but inside
// the STEADY_STATE operator (the period hasn’t to be indicated)
FSTPV, // Stores a variable (described in SymbolType) from the stack - dynamic context (the period
// has to be indicated)
FSTPSV, // Stores a variable (described in SymbolType) from the stack - static context (the period
// hasn’t to be indicated)
FLDR, // Loads a residual onto the stack
FSTPR, // Stores a residual from the stack
FSTPG, // Stores the derivative of a simple (single-equation) block in simulate mode
FSTPG2, // Stores the derivative matrix of a block in evaluate mode
FUNARY, // A unary operator
FBINARY, // A binary operator
FTRINARY, // A trinary operator
FJMPIFEVAL, // Jump if evaluate = true
FJMP, // Jump
FBEGINBLOCK, // Marks the beginning of a model block
FENDBLOCK, // Marks the end of a model block
FENDEQU, // Marks the last equation of the block; for a block that has to be solved, the
// derivatives appear just after this flag
FEND, // Marks the end of the model code
FNUMEXPR, // Stores the expression type and references
FCALL, // Call an external function
FLDTEF, // Loads the result of an external function onto the stack
FSTPTEF, // Stores the result of an external function from the stack
FLDTEFD, // Loads the result of the 1st derivative of an external function onto the stack
FSTPTEFD, // Stores the result of the 1st derivative of an external function from the stack
FLDTEFDD, // Loads the result of the 2nd derivative of an external function onto the stack
FSTPTEFDD // Stores the result of the 2nd derivative of an external function from the stack
};
enum class ExpressionType
{
TemporaryTerm,
ModelEquation,
FirstEndoDerivative,
FirstExoDerivative,
FirstExodetDerivative,
};
{
TemporaryTerm,
ModelEquation,
FirstEndoDerivative,
FirstExoDerivative,
FirstExodetDerivative,
};
enum class ExternalFunctionCallType
{
levelWithoutDerivative,
levelWithFirstDerivative,
levelWithFirstAndSecondDerivative,
separatelyProvidedFirstDerivative,
numericalFirstDerivative,
separatelyProvidedSecondDerivative,
numericalSecondDerivative
};
struct Block_contain_type
{
int Equation, Variable, Own_Derivative;
levelWithoutDerivative,
levelWithFirstDerivative,
levelWithFirstAndSecondDerivative,
separatelyProvidedFirstDerivative,
numericalFirstDerivative,
separatelyProvidedSecondDerivative,
numericalSecondDerivative
};
class BytecodeWriter;
class Writer;
struct BytecodeInstruction
struct Instruction
{
const Tags op_code;
explicit BytecodeInstruction(Tags op_code_arg) :
op_code {op_code_arg}
const Tag tag;
explicit Instruction(Tag tag_arg) : tag {tag_arg}
{
}
protected:
/* This is a base class, so the destructor should be either public+virtual or
protected+non-virtual. We opt for the latter, because otherwise this class
would no longer be POD; its memory representation would also include
runtime type information, and our crude serialization technique (copying the
whole object from memory) would thus not work. */
~BytecodeInstruction() = default;
~Instruction() = default;
};
template<typename T1>
class TagWithOneArgument : public BytecodeInstruction
{
protected:
T1 arg1;
public:
TagWithOneArgument(Tags op_code_arg, T1 arg_arg1) : BytecodeInstruction{op_code_arg},
arg1{arg_arg1}
{
};
protected:
// See BytecodeInstruction destructor for the rationale
~TagWithOneArgument() = default;
};
template<typename T>
concept IsInstruction = derived_from<T, Instruction>;
template<typename T1, typename T2>
class TagWithTwoArguments : public BytecodeInstruction
struct FLDZ final : public Instruction
{
protected:
T1 arg1;
T2 arg2;
public:
TagWithTwoArguments(Tags op_code_arg, T1 arg_arg1, T2 arg_arg2) :
BytecodeInstruction{op_code_arg}, arg1{arg_arg1}, arg2{arg_arg2}
FLDZ() : Instruction {Tag::FLDZ}
{
};
protected:
// See BytecodeInstruction destructor for the rationale
~TagWithTwoArguments() = default;
};
template<typename T1, typename T2, typename T3>
class TagWithThreeArguments : public BytecodeInstruction
{
protected:
T1 arg1;
T2 arg2;
T3 arg3;
public:
TagWithThreeArguments(Tags op_code_arg, T1 arg_arg1, T2 arg_arg2, T3 arg_arg3) :
BytecodeInstruction{op_code_arg}, arg1{arg_arg1}, arg2{arg_arg2}, arg3{arg_arg3}
{
};
protected:
// See BytecodeInstruction destructor for the rationale
~TagWithThreeArguments() = default;
};
template<typename T1, typename T2, typename T3, typename T4>
class TagWithFourArguments : public BytecodeInstruction
{
protected:
T1 arg1;
T2 arg2;
T3 arg3;
T4 arg4;
public:
TagWithFourArguments(Tags op_code_arg, T1 arg_arg1, T2 arg_arg2, T3 arg_arg3, T4 arg_arg4) :
BytecodeInstruction{op_code_arg}, arg1{arg_arg1}, arg2{arg_arg2},
arg3{move(arg_arg3)}, arg4{arg_arg4}
{
};
protected:
// See BytecodeInstruction destructor for the rationale
~TagWithFourArguments() = default;
}
};
class FLDZ_ final : public BytecodeInstruction
struct FEND final : public Instruction
{
public:
FLDZ_() : BytecodeInstruction{Tags::FLDZ}
FEND() : Instruction {Tag::FEND}
{
};
}
};
class FEND_ final : public BytecodeInstruction
struct FENDBLOCK final : public Instruction
{
public:
FEND_() : BytecodeInstruction{Tags::FEND}
FENDBLOCK() : Instruction {Tag::FENDBLOCK}
{
};
}
};
class FENDBLOCK_ final : public BytecodeInstruction
struct FENDEQU final : public Instruction
{
public:
FENDBLOCK_() : BytecodeInstruction{Tags::FENDBLOCK}
FENDEQU() : Instruction {Tag::FENDEQU}
{
};
}
};
class FENDEQU_ final : public BytecodeInstruction
struct FDIMT final : public Instruction
{
public:
FENDEQU_() : BytecodeInstruction{Tags::FENDEQU}
const int size;
explicit FDIMT(int size_arg) : Instruction {Tag::FDIMT}, size {size_arg}
{
};
}
};
class FDIMT_ final : public TagWithOneArgument<int>
struct FDIMST final : public Instruction
{
public:
explicit FDIMT_(int size_arg) : TagWithOneArgument::TagWithOneArgument{Tags::FDIMT, size_arg}
const int size;
explicit FDIMST(int size_arg) : Instruction {Tag::FDIMST}, size {size_arg}
{
};
int
get_size()
{
return arg1;
};
}
};
class FDIMST_ final : public TagWithOneArgument<int>
struct FLDC final : public Instruction
{
public:
explicit FDIMST_(int size_arg) : TagWithOneArgument::TagWithOneArgument{Tags::FDIMST, size_arg}
{
};
int
get_size()
const double value;
explicit FLDC(double value_arg) : Instruction {Tag::FLDC}, value {value_arg}
{
return arg1;
};
};
class FLDC_ final : public TagWithOneArgument<double>
{
public:
explicit FLDC_(double value_arg) : TagWithOneArgument::TagWithOneArgument{Tags::FLDC, value_arg}
{
};
double
get_value()
{
return arg1;
};
}
};
class FLDU_ final : public TagWithOneArgument<int>
struct FLDU final : public Instruction
{
public:
explicit FLDU_(int pos_arg) : TagWithOneArgument::TagWithOneArgument{Tags::FLDU, pos_arg}
const int pos;
explicit FLDU(int pos_arg) : Instruction {Tag::FLDU}, pos {pos_arg}
{
};
int
get_pos()
{
return arg1;
};
}
};
class FLDSU_ final : public TagWithOneArgument<int>
struct FLDSU final : public Instruction
{
public:
explicit FLDSU_(int pos_arg) : TagWithOneArgument::TagWithOneArgument{Tags::FLDSU, pos_arg}
const int pos;
explicit FLDSU(int pos_arg) : Instruction {Tag::FLDSU}, pos {pos_arg}
{
};
int
get_pos()
{
return arg1;
};
}
};
class FLDR_ final : public TagWithOneArgument<int>
struct FLDR final : public Instruction
{
public:
explicit FLDR_(int pos_arg) : TagWithOneArgument::TagWithOneArgument{Tags::FLDR, pos_arg}
{
};
int
get_pos()
const int pos;
explicit FLDR(int pos_arg) : Instruction {Tag::FLDR}, pos {pos_arg}
{
return arg1;
};
}
};
class FLDT_ final : public TagWithOneArgument<int>
struct FLDT final : public Instruction
{
public:
explicit FLDT_(int pos_arg) : TagWithOneArgument::TagWithOneArgument{Tags::FLDT, pos_arg}
{
};
int
get_pos()
const int pos;
explicit FLDT(int pos_arg) : Instruction {Tag::FLDT}, pos {pos_arg}
{
return arg1;
};
}
};
class FLDST_ final : public TagWithOneArgument<int>
struct FLDST final : public Instruction
{
public:
explicit FLDST_(int pos_arg) : TagWithOneArgument::TagWithOneArgument{Tags::FLDST, pos_arg}
{
};
int
get_pos()
const int pos;
explicit FLDST(int pos_arg) : Instruction {Tag::FLDST}, pos {pos_arg}
{
return arg1;
};
}
};
class FSTPT_ final : public TagWithOneArgument<int>
struct FSTPT final : public Instruction
{
public:
explicit FSTPT_(int pos_arg) : TagWithOneArgument::TagWithOneArgument{Tags::FSTPT, pos_arg}
{
};
int
get_pos()
const int pos;
explicit FSTPT(int pos_arg) : Instruction {Tag::FSTPT}, pos {pos_arg}
{
return arg1;
};
}
};
class FSTPST_ final : public TagWithOneArgument<int>
struct FSTPST final : public Instruction
{
public:
explicit FSTPST_(int pos_arg) : TagWithOneArgument::TagWithOneArgument{Tags::FSTPST, pos_arg}
{
};
int
get_pos()
const int pos;
explicit FSTPST(int pos_arg) : Instruction {Tag::FSTPST}, pos {pos_arg}
{
return arg1;
};
}
};
class FSTPR_ final : public TagWithOneArgument<int>
struct FSTPR final : public Instruction
{
public:
explicit FSTPR_(int pos_arg) : TagWithOneArgument::TagWithOneArgument{Tags::FSTPR, pos_arg}
{
};
int
get_pos()
const int pos;
explicit FSTPR(int pos_arg) : Instruction {Tag::FSTPR}, pos {pos_arg}
{
return arg1;
};
}
};
class FSTPU_ final : public TagWithOneArgument<int>
struct FSTPU final : public Instruction
{
public:
explicit FSTPU_(int pos_arg) : TagWithOneArgument::TagWithOneArgument{Tags::FSTPU, pos_arg}
const int pos;
explicit FSTPU(int pos_arg) : Instruction {Tag::FSTPU}, pos {pos_arg}
{
};
int
get_pos()
{
return arg1;
};
}
};
class FSTPSU_ final : public TagWithOneArgument<int>
struct FSTPSU final : public Instruction
{
public:
explicit FSTPSU_(int pos_arg) : TagWithOneArgument::TagWithOneArgument{Tags::FSTPSU, pos_arg}
{
};
int
get_pos()
const int pos;
explicit FSTPSU(int pos_arg) : Instruction {Tag::FSTPSU}, pos {pos_arg}
{
return arg1;
};
}
};
class FSTPG_ final : public TagWithOneArgument<int>
struct FSTPG final : public Instruction
{
public:
explicit FSTPG_(int pos_arg) : TagWithOneArgument::TagWithOneArgument{Tags::FSTPG, pos_arg}
{
};
int
get_pos()
explicit FSTPG() : Instruction {Tag::FSTPG}
{
return arg1;
};
}
};
class FSTPG2_ final : public TagWithTwoArguments<int, int>
struct FSTPG2 final : public Instruction
{
public:
FSTPG2_(int row_arg, int col_arg) : TagWithTwoArguments::TagWithTwoArguments{Tags::FSTPG2, row_arg, col_arg}
{
};
int
get_row()
const int row, col;
FSTPG2(int row_arg, int col_arg) : Instruction {Tag::FSTPG2}, row {row_arg}, col {col_arg}
{
return arg1;
};
int
get_col()
{
return arg2;
};
}
};
class FSTPG3_ final : public TagWithFourArguments<int, int, int, int>
struct FUNARY final : public Instruction
{
public:
FSTPG3_(int row_arg, int col_arg, int lag_arg, int col_pos_arg) : TagWithFourArguments::TagWithFourArguments{Tags::FSTPG3, row_arg, col_arg, lag_arg, col_pos_arg}
const UnaryOpcode op_code;
explicit FUNARY(UnaryOpcode op_code_arg) : Instruction {Tag::FUNARY}, op_code {op_code_arg}
{
};
int
get_row()
{
return arg1;
};
int
get_col()
{
return arg2;
};
int
get_lag()
{
return arg2;
};
int
get_col_pos()
{
return arg4;
};
}
};
class FUNARY_ final : public TagWithOneArgument<UnaryOpcode>
struct FBINARY final : public Instruction
{
public:
explicit FUNARY_(UnaryOpcode op_type_arg) : TagWithOneArgument::TagWithOneArgument{Tags::FUNARY, op_type_arg}
const BinaryOpcode op_code;
explicit FBINARY(BinaryOpcode op_code_arg) : Instruction {Tag::FBINARY}, op_code {op_code_arg}
{
};
UnaryOpcode
get_op_type()
{
return arg1;
};
}
};
class FBINARY_ final : public TagWithOneArgument<BinaryOpcode>
struct FTRINARY final : public Instruction
{
public:
explicit FBINARY_(BinaryOpcode op_type_arg) : TagWithOneArgument::TagWithOneArgument{Tags::FBINARY, op_type_arg}
const TrinaryOpcode op_code;
explicit FTRINARY(TrinaryOpcode op_code_arg) : Instruction {Tag::FTRINARY}, op_code {op_code_arg}
{
};
BinaryOpcode
get_op_type()
{
return arg1;
};
}
};
class FTRINARY_ final : public TagWithOneArgument<TrinaryOpcode>
struct FJMPIFEVAL final : public Instruction
{
public:
explicit FTRINARY_(TrinaryOpcode op_type_arg) : TagWithOneArgument::TagWithOneArgument{Tags::FTRINARY, op_type_arg}
{
};
TrinaryOpcode
get_op_type()
const int pos;
explicit FJMPIFEVAL(int pos_arg) : Instruction {Tag::FJMPIFEVAL}, pos {pos_arg}
{
return arg1;
};
}
};
class FJMPIFEVAL_ final : public TagWithOneArgument<int>
struct FJMP final : public Instruction
{
public:
explicit FJMPIFEVAL_(int arg_pos) : TagWithOneArgument::TagWithOneArgument{Tags::FJMPIFEVAL, arg_pos}
const int pos;
explicit FJMP(int pos_arg) : Instruction {Tag::FJMP}, pos {pos_arg}
{
};
int
get_pos()
{
return arg1;
}
};
class FJMP_ final : public TagWithOneArgument<int>
struct FLDTEF final : public Instruction
{
public:
explicit FJMP_(int arg_pos) : TagWithOneArgument::TagWithOneArgument{Tags::FJMP, arg_pos}
{
};
int
get_pos()
const int number;
explicit FLDTEF(int number_arg) : Instruction {Tag::FLDTEF}, number {number_arg}
{
return arg1;
}
};
class FLDTEF_ final : public TagWithOneArgument<int>
struct FSTPTEF final : public Instruction
{
public:
explicit FLDTEF_(int number) : TagWithOneArgument::TagWithOneArgument{Tags::FLDTEF, number}
{
};
int
get_number()
const int number;
explicit FSTPTEF(int number_arg) : Instruction {Tag::FSTPTEF}, number {number_arg}
{
return arg1;
}
};
class FSTPTEF_ final : public TagWithOneArgument<int>
struct FLDTEFD final : public Instruction
{
public:
explicit FSTPTEF_(int number) : TagWithOneArgument::TagWithOneArgument{Tags::FSTPTEF, number}
{
};
int
get_number()
const int indx, row;
FLDTEFD(int indx_arg, int row_arg) : Instruction {Tag::FLDTEFD}, indx {indx_arg}, row {row_arg}
{
return arg1;
}
};
class FLDTEFD_ final : public TagWithTwoArguments<int, int>
struct FSTPTEFD final : public Instruction
{
public:
FLDTEFD_(int indx, int row) : TagWithTwoArguments::TagWithTwoArguments{Tags::FLDTEFD, indx, row}
const int indx, row;
FSTPTEFD(int indx_arg, int row_arg) : Instruction {Tag::FSTPTEFD}, indx {indx_arg}, row {row_arg}
{
};
int
get_indx()
{
return arg1;
};
int
get_row()
{
return arg2;
};
}
};
class FSTPTEFD_ final : public TagWithTwoArguments<int, int>
struct FLDTEFDD final : public Instruction
{
public:
FSTPTEFD_(int indx, int row) : TagWithTwoArguments::TagWithTwoArguments{Tags::FSTPTEFD, indx, row}
{
};
int
get_indx()
{
return arg1;
};
int
get_row()
const int indx, row, col;
FLDTEFDD(int indx_arg, int row_arg, int col_arg) :
Instruction {Tag::FLDTEFDD}, indx {indx_arg}, row {row_arg}, col {col_arg}
{
return arg2;
};
}
};
class FLDTEFDD_ final : public TagWithThreeArguments<int, int, int>
struct FSTPTEFDD final : public Instruction
{
public:
FLDTEFDD_(int indx, int row, int col) : TagWithThreeArguments::TagWithThreeArguments{Tags::FLDTEFDD, indx, row, col}
{
};
int
get_indx()
{
return arg1;
};
int
get_row()
const int indx, row, col;
FSTPTEFDD(int indx_arg, int row_arg, int col_arg) :
Instruction {Tag::FSTPTEF}, indx {indx_arg}, row {row_arg}, col {col_arg}
{
return arg2;
};
int
get_col()
{
return arg3;
};
}
};
class FSTPTEFDD_ final : public TagWithThreeArguments<int, int, int>
struct FLDVS final : public Instruction
{
public:
FSTPTEFDD_(int indx, int row, int col) : TagWithThreeArguments::TagWithThreeArguments{Tags::FSTPTEF, indx, row, col}
{
};
int
get_indx()
const SymbolType type;
const int pos;
FLDVS(SymbolType type_arg, int pos_arg) : Instruction {Tag::FLDVS}, type {type_arg}, pos {pos_arg}
{
return arg1;
};
int
get_row()
{
return arg2;
};
int
get_col()
{
return arg3;
};
}
};
class FLDVS_ final : public TagWithTwoArguments<SymbolType, int>
struct FLDSV final : public Instruction
{
public:
FLDVS_(SymbolType type_arg, int pos_arg) : TagWithTwoArguments::TagWithTwoArguments{Tags::FLDVS, type_arg, pos_arg}
const SymbolType type;
const int pos;
FLDSV(SymbolType type_arg, int pos_arg) : Instruction {Tag::FLDSV}, type {type_arg}, pos {pos_arg}
{
};
SymbolType
get_type()
{
return arg1;
};
int
get_pos()
{
return arg2;
};
}
};
class FLDSV_ final : public TagWithTwoArguments<SymbolType, int>
struct FSTPSV final : public Instruction
{
public:
FLDSV_(SymbolType type_arg, int pos_arg) : TagWithTwoArguments::TagWithTwoArguments{Tags::FLDSV, type_arg, pos_arg}
{
};
SymbolType
get_type()
{
return arg1;
};
int
get_pos()
const SymbolType type;
const int pos;
FSTPSV(SymbolType type_arg, int pos_arg) :
Instruction {Tag::FSTPSV}, type {type_arg}, pos {pos_arg}
{
return arg2;
};
}
};
class FSTPSV_ final : public TagWithTwoArguments<SymbolType, int>
struct FLDV final : public Instruction
{
public:
FSTPSV_(SymbolType type_arg, int pos_arg) : TagWithTwoArguments::TagWithTwoArguments{Tags::FSTPSV, type_arg, pos_arg}
{
};
SymbolType
get_type()
const SymbolType type;
const int pos, lead_lag;
FLDV(SymbolType type_arg, int pos_arg, int lead_lag_arg) :
Instruction {Tag::FLDV}, type {type_arg}, pos {pos_arg}, lead_lag {lead_lag_arg}
{
return arg1;
};
int
get_pos()
{
return arg2;
};
}
};
class FLDV_ final : public TagWithThreeArguments<SymbolType, int, int>
struct FSTPV final : public Instruction
{
public:
FLDV_(SymbolType type_arg, int pos_arg, int lead_lag_arg) :
TagWithThreeArguments::TagWithThreeArguments{Tags::FLDV, type_arg, pos_arg, lead_lag_arg}
const SymbolType type;
const int pos, lead_lag;
FSTPV(SymbolType type_arg, int pos_arg, int lead_lag_arg) :
Instruction {Tag::FSTPV}, type {type_arg}, pos {pos_arg}, lead_lag {lead_lag_arg}
{
};
SymbolType
get_type()
{
return arg1;
};
int
get_pos()
{
return arg2;
};
int
get_lead_lag()
{
return arg3;
};
}
};
class FSTPV_ final : public TagWithThreeArguments<SymbolType, int, int>
class FCALL final : public Instruction
{
public:
FSTPV_(SymbolType type_arg, int pos_arg, int lead_lag_arg) :
TagWithThreeArguments::TagWithThreeArguments{Tags::FSTPV, type_arg, pos_arg, lead_lag_arg}
{
};
SymbolType
get_type()
{
return arg1;
};
int
get_pos()
{
return arg2;
};
int
get_lead_lag()
{
return arg3;
};
};
template<IsInstruction B>
friend Writer& operator<<(Writer& code_file, const B& instr);
class FCALL_ final : public BytecodeInstruction
{
template<typename B>
friend BytecodeWriter &operator<<(BytecodeWriter &code_file, const B &instr);
private:
int nb_output_arguments, nb_input_arguments, indx;
string func_name;
string arg_func_name;
int add_input_arguments{0}, row{0}, col{0};
int add_input_arguments {0}, row {0}, col {0};
ExternalFunctionCallType call_type;
public:
FCALL_(int nb_output_arguments_arg, int nb_input_arguments_arg, string func_name_arg, int indx_arg, ExternalFunctionCallType call_type_arg) :
BytecodeInstruction{Tags::FCALL},
nb_output_arguments{nb_output_arguments_arg},
nb_input_arguments{nb_input_arguments_arg},
indx{indx_arg},
func_name{move(func_name_arg)},
call_type{call_type_arg}
{
};
FCALL(int nb_output_arguments_arg, int nb_input_arguments_arg, string func_name_arg, int indx_arg,
ExternalFunctionCallType call_type_arg) :
Instruction {Tag::FCALL},
nb_output_arguments {nb_output_arguments_arg},
nb_input_arguments {nb_input_arguments_arg},
indx {indx_arg},
func_name {move(func_name_arg)},
call_type {call_type_arg}
{
}
/* Deserializing constructor.
Updates the code pointer to point beyond the bytes read. */
FCALL_(char *&code) :
BytecodeInstruction{Tags::FCALL}
FCALL(char*& code) : Instruction {Tag::FCALL}
{
code += sizeof(op_code);
code += sizeof(tag);
auto read_member = [&code](auto &member)
{
auto read_member = [&code](auto& member) {
member = *reinterpret_cast<add_pointer_t<decltype(member)>>(code);
code += sizeof member;
};
......@@ -774,59 +473,59 @@ public:
int size;
read_member(size);
func_name = code;
code += size+1;
code += size + 1;
read_member(size);
arg_func_name = code;
code += size+1;
code += size + 1;
}
string
get_function_name()
{
//printf("get_function_name => func_name=%s\n",func_name.c_str());fflush(stdout);
// printf("get_function_name => func_name=%s\n",func_name.c_str());fflush(stdout);
return func_name;
};
}
int
get_nb_output_arguments()
{
return nb_output_arguments;
};
}
int
get_nb_input_arguments()
{
return nb_input_arguments;
};
}
int
get_indx()
{
return indx;
};
}
void
set_arg_func_name(string arg_arg_func_name)
{
arg_func_name = arg_arg_func_name;
};
arg_func_name = move(arg_arg_func_name);
}
string
get_arg_func_name()
{
return arg_func_name;
};
}
void
set_nb_add_input_arguments(int arg_add_input_arguments)
{
add_input_arguments = arg_add_input_arguments;
};
}
int
get_nb_add_input_arguments()
{
return add_input_arguments;
};
}
void
set_row(int arg_row)
{
row = arg_row;
};
}
int
get_row()
{
......@@ -836,12 +535,12 @@ public:
set_col(int arg_col)
{
col = arg_col;
};
}
int
get_col()
{
return col;
};
}
ExternalFunctionCallType
get_call_type()
{
......@@ -849,38 +548,40 @@ public:
}
};
class FNUMEXPR_ final : public BytecodeInstruction
class FNUMEXPR final : public Instruction
{
private:
ExpressionType expression_type;
int equation; // Equation number (non-block-specific) (or temporary term number for ExpressionType::TemporaryTerm)
int equation; // Equation number (non-block-specific) (or temporary term number for
// ExpressionType::TemporaryTerm)
int dvariable1; // For derivatives, type-specific ID of the derivation variable
int lag1; // For derivatives, lead/lag of the derivation variable
int lag1; // For derivatives, lead/lag of the derivation variable
public:
FNUMEXPR_(const ExpressionType expression_type_arg, int equation_arg) :
BytecodeInstruction{Tags::FNUMEXPR},
expression_type{expression_type_arg},
equation{equation_arg},
dvariable1{0},
lag1{0}
{
};
FNUMEXPR_(const ExpressionType expression_type_arg, int equation_arg, int dvariable1_arg) :
BytecodeInstruction{Tags::FNUMEXPR},
expression_type{expression_type_arg},
equation{equation_arg},
dvariable1{dvariable1_arg},
lag1{0}
{
};
FNUMEXPR_(const ExpressionType expression_type_arg, int equation_arg, int dvariable1_arg, int lag1_arg) :
BytecodeInstruction{Tags::FNUMEXPR},
expression_type{expression_type_arg},
equation{equation_arg},
dvariable1{dvariable1_arg},
lag1{lag1_arg}
{
};
FNUMEXPR(const ExpressionType expression_type_arg, int equation_arg) :
Instruction {Tag::FNUMEXPR},
expression_type {expression_type_arg},
equation {equation_arg},
dvariable1 {0},
lag1 {0}
{
}
FNUMEXPR(const ExpressionType expression_type_arg, int equation_arg, int dvariable1_arg) :
Instruction {Tag::FNUMEXPR},
expression_type {expression_type_arg},
equation {equation_arg},
dvariable1 {dvariable1_arg},
lag1 {0}
{
}
FNUMEXPR(const ExpressionType expression_type_arg, int equation_arg, int dvariable1_arg,
int lag1_arg) :
Instruction {Tag::FNUMEXPR},
expression_type {expression_type_arg},
equation {equation_arg},
dvariable1 {dvariable1_arg},
lag1 {lag1_arg}
{
}
ExpressionType
get_expression_type()
{
......@@ -890,95 +591,97 @@ public:
get_equation()
{
return equation;
};
}
int
get_dvariable1()
{
return dvariable1;
};
}
int
get_lag1()
{
return lag1;
};
}
};
class FBEGINBLOCK_ final : public BytecodeInstruction
class FBEGINBLOCK final : public Instruction
{
template<typename B>
friend BytecodeWriter &operator<<(BytecodeWriter &code_file, const B &instr);
template<IsInstruction B>
friend Writer& operator<<(Writer& code_file, const B& instr);
private:
int size{0};
int size {0};
BlockSimulationType type;
vector<int> variable;
vector<int> equation;
vector<int> variables;
vector<int> equations;
vector<int> exogenous;
vector<int> det_exogenous;
bool is_linear{false};
vector<Block_contain_type> Block_Contain_;
int u_count_int{0};
int nb_col_jacob{0};
bool is_linear {false};
int u_count_int {0};
int nb_col_jacob {0};
int det_exo_size, exo_size;
public:
/* Constructor when derivatives w.r.t. exogenous are present (only makes
sense when there is no block-decomposition, since there is no provision for
derivatives w.r.t. endogenous not belonging to the block) */
FBEGINBLOCK_(int size_arg, BlockSimulationType type_arg, int first_element, int block_size,
const vector<int> &variable_arg, const vector<int> &equation_arg,
bool is_linear_arg, int u_count_int_arg, int nb_col_jacob_arg,
int det_exo_size_arg, int exo_size_arg,
vector<int> det_exogenous_arg, vector<int> exogenous_arg) :
BytecodeInstruction{Tags::FBEGINBLOCK},
size{size_arg},
type{type_arg},
variable{variable_arg.begin()+first_element, variable_arg.begin()+(first_element+block_size)},
equation{equation_arg.begin()+first_element, equation_arg.begin()+(first_element+block_size)},
exogenous{move(exogenous_arg)},
det_exogenous{move(det_exogenous_arg)},
is_linear{is_linear_arg},
u_count_int{u_count_int_arg},
nb_col_jacob{nb_col_jacob_arg},
det_exo_size{det_exo_size_arg},
exo_size{exo_size_arg}
FBEGINBLOCK(int size_arg, BlockSimulationType type_arg, int first_element, int block_size,
const vector<int>& variables_arg, const vector<int>& equations_arg,
bool is_linear_arg, int u_count_int_arg, int nb_col_jacob_arg, int det_exo_size_arg,
int exo_size_arg, vector<int> det_exogenous_arg, vector<int> exogenous_arg) :
Instruction {Tag::FBEGINBLOCK},
size {size_arg},
type {type_arg},
variables {variables_arg.begin() + first_element,
variables_arg.begin() + (first_element + block_size)},
equations {equations_arg.begin() + first_element,
equations_arg.begin() + (first_element + block_size)},
exogenous {move(exogenous_arg)},
det_exogenous {move(det_exogenous_arg)},
is_linear {is_linear_arg},
u_count_int {u_count_int_arg},
nb_col_jacob {nb_col_jacob_arg},
det_exo_size {det_exo_size_arg},
exo_size {exo_size_arg}
{
}
// Constructor when derivatives w.r.t. exogenous are absent
FBEGINBLOCK_(int size_arg, BlockSimulationType type_arg, int first_element, int block_size,
const vector<int> &variable_arg, const vector<int> &equation_arg,
bool is_linear_arg, int u_count_int_arg, int nb_col_jacob_arg) :
BytecodeInstruction{Tags::FBEGINBLOCK},
size{size_arg},
type{type_arg},
variable{variable_arg.begin()+first_element, variable_arg.begin()+(first_element+block_size)},
equation{equation_arg.begin()+first_element, equation_arg.begin()+(first_element+block_size)},
is_linear{is_linear_arg},
u_count_int{u_count_int_arg},
nb_col_jacob{nb_col_jacob_arg},
det_exo_size{0},
exo_size{0}
FBEGINBLOCK(int size_arg, BlockSimulationType type_arg, int first_element, int block_size,
const vector<int>& variables_arg, const vector<int>& equations_arg,
bool is_linear_arg, int u_count_int_arg, int nb_col_jacob_arg) :
Instruction {Tag::FBEGINBLOCK},
size {size_arg},
type {type_arg},
variables {variables_arg.begin() + first_element,
variables_arg.begin() + (first_element + block_size)},
equations {equations_arg.begin() + first_element,
equations_arg.begin() + (first_element + block_size)},
is_linear {is_linear_arg},
u_count_int {u_count_int_arg},
nb_col_jacob {nb_col_jacob_arg},
det_exo_size {0},
exo_size {0}
{
}
/* Deserializing constructor.
Updates the code pointer to point beyond the bytes read. */
FBEGINBLOCK_(char *&code) :
BytecodeInstruction{Tags::FBEGINBLOCK}
FBEGINBLOCK(char*& code) : Instruction {Tag::FBEGINBLOCK}
{
code += sizeof(op_code);
code += sizeof(tag);
auto read_member = [&code](auto &member)
{
auto read_member = [&code](auto& member) {
member = *reinterpret_cast<add_pointer_t<decltype(member)>>(code);
code += sizeof member;
};
read_member(size);
read_member(type);
variables.resize(size);
equations.resize(size);
for (int i {0}; i < size; i++)
{
Block_contain_type bc;
read_member(bc.Variable);
read_member(bc.Equation);
Block_Contain_.push_back(move(bc));
read_member(variables[i]);
read_member(equations[i]);
}
if (type == BlockSimulationType::solveTwoBoundariesSimple
|| type == BlockSimulationType::solveTwoBoundariesComplete
......@@ -1010,46 +713,46 @@ public:
get_size()
{
return size;
};
}
BlockSimulationType
get_type()
{
return type;
};
}
bool
get_is_linear()
{
return is_linear;
};
}
int
get_u_count_int()
{
return u_count_int;
};
vector<Block_contain_type>
get_Block_Contain()
}
vector<int>
get_variables()
{
return Block_Contain_;
};
return variables;
}
vector<int>
get_equations()
{
return equations;
}
int
get_nb_col_jacob()
{
return nb_col_jacob;
};
}
int
get_exo_size()
{
return exo_size;
};
}
int
get_det_exo_size()
{
return det_exo_size;
};
vector<int>
get_endogenous()
{
return variable;
}
vector<int>
get_exogenous()
......@@ -1059,15 +762,17 @@ public:
};
// Superclass of std::ofstream for writing a sequence of bytecode instructions
class BytecodeWriter : private ofstream
class Writer : private ofstream
{
template<typename B>
friend BytecodeWriter &operator<<(BytecodeWriter &code_file, const B &instr);
template<IsInstruction B>
friend Writer& operator<<(Writer& code_file, const B& instr);
private:
// Stores the positions of all instructions in the byte stream
vector<pos_type> instructions_positions;
public:
BytecodeWriter(const filesystem::path &filename);
Writer(const filesystem::path& filename);
// Returns the number of the next instruction to be written
int
getInstructionCounter() const
......@@ -1077,9 +782,8 @@ public:
/* Overwrites an existing instruction, given its number.
It is the responsibility of the caller to ensure that the new instruction
occupies exactly as many bytes as the former one. */
template<typename B>
void
overwriteInstruction(int instruction_number, const B &new_instruction)
overwriteInstruction(int instruction_number, const IsInstruction auto& new_instruction)
{
seekp(instructions_positions.at(instruction_number));
*this << new_instruction;
......@@ -1090,19 +794,21 @@ public:
// Overloads of operator<< for writing bytecode instructions
template<typename B>
BytecodeWriter &
operator<<(BytecodeWriter &code_file, const B &instr)
template<IsInstruction B>
Writer&
operator<<(Writer& code_file, const B& instr)
{
code_file.instructions_positions.push_back(code_file.tellp());
code_file.write(reinterpret_cast<const char *>(&instr), sizeof(B));
code_file.write(reinterpret_cast<const char*>(&instr), sizeof(B));
return code_file;
}
template<>
BytecodeWriter &operator<<(BytecodeWriter &code_file, const FCALL_ &instr);
Writer& operator<<(Writer& code_file, const FCALL& instr);
template<>
BytecodeWriter &operator<<(BytecodeWriter &code_file, const FBEGINBLOCK_ &instr);
Writer& operator<<(Writer& code_file, const FBEGINBLOCK& instr);
}
#endif // _BYTECODE_HH
#endif
/*
* Copyright © 2007-2022 Dynare Team
* Copyright © 2007-2024 Dynare Team
*
* This file is part of Dynare.
*
......@@ -17,133 +17,149 @@
* along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef _COMMON_ENUMS_HH
#define _COMMON_ENUMS_HH
#ifndef COMMON_ENUMS_HH
#define COMMON_ENUMS_HH
//! Enumeration of possible symbol types
/*! Warning: do not to change existing values for 0 to 4: the values matter for homotopy_setup command */
/*! Warning: do not to change existing values for 0 to 4: the values matter for homotopy_setup
* command */
enum class SymbolType
{
endogenous = 0, //!< Endogenous
exogenous = 1, //!< Exogenous
exogenousDet = 2, //!< Exogenous deterministic
parameter = 4, //!< Parameter
modelLocalVariable = 10, //!< Local variable whose scope is model (pound expression)
modFileLocalVariable = 11, //!< Local variable whose scope is mod file (model excluded)
externalFunction = 12, //!< External (user-defined) function
trend = 13, //!< Trend variable
statementDeclaredVariable = 14, //!< Local variable assigned within a Statement (see subsample statement for example)
logTrend = 15, //!< Log-trend variable
unusedEndogenous = 16, //!< Type to mark unused endogenous variables when `nostrict` option is passed
{
endogenous = 0, // Endogenous (non-heterogeneous)
exogenous = 1, // Exogenous (non-heterogeneous)
exogenousDet = 2, // Exogenous deterministic (non-heterogeneous)
parameter = 4, // Parameter (non-heterogeneous)
heterogeneousEndogenous = 5, // Endogenous that is heterogeneous across some dimension
heterogeneousExogenous = 6, // Exogenous that is heterogeneous across some dimension
heterogeneousParameter = 7, // Parameter that is heterogeneous across some dimension
modelLocalVariable = 10, // Local variable whose scope is model (pound expression)
modFileLocalVariable = 11, // Local variable whose scope is mod file (model excluded)
externalFunction = 12, // External (user-defined) function
trend = 13, // Trend variable
statementDeclaredVariable
= 14, //!< Local variable assigned within a Statement (see subsample statement for example)
logTrend = 15, //!< Log-trend variable
unusedEndogenous
= 16, //!< Type to mark unused endogenous variables when `nostrict` option is passed
// Value 17 is unused for the time being (but could be reused)
// Value 17 is unused for the time being (but could be reused)
epilogue = 18, //!< Variables created in epilogue block
excludedVariable = 19 //!< Variable excluded via model_remove/var_remove/include_eqs/exclude_eqs
};
epilogue = 18, //!< Variables created in epilogue block
excludedVariable = 19 //!< Variable excluded via model_remove/var_remove/include_eqs/exclude_eqs
};
constexpr bool
isHeterogeneous(SymbolType type)
{
return type == SymbolType::heterogeneousEndogenous || type == SymbolType::heterogeneousExogenous
|| type == SymbolType::heterogeneousParameter;
}
enum class UnaryOpcode
{
uminus,
exp,
log,
log10,
cos,
sin,
tan,
acos,
asin,
atan,
cosh,
sinh,
tanh,
acosh,
asinh,
atanh,
sqrt,
cbrt,
abs,
sign,
steadyState,
steadyStateParamDeriv, // for the derivative of the STEADY_STATE operator w.r.t. to a parameter
steadyStateParam2ndDeriv, // for the 2nd derivative of the STEADY_STATE operator w.r.t. to a parameter
expectation,
erf,
erfc,
diff,
adl
};
{
uminus,
exp,
log,
log10,
cos,
sin,
tan,
acos,
asin,
atan,
cosh,
sinh,
tanh,
acosh,
asinh,
atanh,
sqrt,
cbrt,
abs,
sign,
steadyState,
steadyStateParamDeriv, // for the derivative of the STEADY_STATE operator w.r.t. to a parameter
steadyStateParam2ndDeriv, // for the 2nd derivative of the STEADY_STATE operator w.r.t. to a
// parameter
expectation,
erf,
erfc,
diff,
adl,
sum
};
enum class BinaryOpcode
{
plus,
minus,
times,
divide,
power,
powerDeriv, // for the derivative of the power function (see trac ticket #78)
equal,
max,
min,
less,
greater,
lessEqual,
greaterEqual,
equalEqual,
different
};
{
plus,
minus,
times,
divide,
power,
powerDeriv, // for the derivative of the power function (see trac ticket #78)
equal,
max,
min,
less,
greater,
lessEqual,
greaterEqual,
equalEqual,
different
};
// Small number value used when evaluating powerDeriv opcodes.
// Put here instead of inside BinaryOpNode class, because needed by bytecode MEX.
constexpr double power_deriv_near_zero{1e-12};
constexpr double power_deriv_near_zero {1e-12};
enum class TrinaryOpcode
{
normcdf,
normpdf
};
{
normcdf,
normpdf
};
enum class PriorDistributions
{
noShape = 0,
beta = 1,
gamma = 2,
normal = 3,
invGamma = 4,
invGamma1 = 4,
uniform = 5,
invGamma2 = 6,
dirichlet = 7,
weibull = 8
};
{
noShape = 0,
beta = 1,
gamma = 2,
normal = 3,
invGamma = 4,
invGamma1 = 4,
uniform = 5,
invGamma2 = 6,
dirichlet = 7,
weibull = 8
};
enum class EquationType
{
unknown, //!< Unknown equation type
evaluate, //!< Simple evaluation, normalized variable on left-hand side (written as such by the user)
evaluateRenormalized, //!< Simple evaluation, normalized variable on left-hand side (normalization computed by the preprocessor)
solve //!< No simple evaluation of the equation, it has to be solved
};
{
evaluate, //!< Simple evaluation, normalized variable on left-hand side (written as such by the
//!< user)
evaluateRenormalized, //!< Simple evaluation, normalized variable on left-hand side (normalization
//!< computed by the preprocessor)
solve //!< No simple evaluation of the equation, it has to be solved
};
enum class BlockSimulationType
{
unknown, //!< Unknown simulation type
evaluateForward, //!< Simple evaluation, normalized variable on left-hand side, forward
evaluateBackward, //!< Simple evaluation, normalized variable on left-hand side, backward
solveForwardSimple, //!< Block of one equation, newton solver needed, forward
solveBackwardSimple, //!< Block of one equation, newton solver needed, backward
solveTwoBoundariesSimple, //!< Block of one equation, Newton solver needed, forward and backward
solveForwardComplete, //!< Block of several equations, Newton solver needed, forward
solveBackwardComplete, //!< Block of several equations, Newton solver needed, backward
solveTwoBoundariesComplete //!< Block of several equations, Newton solver needed, forward and backwar
};
{
evaluateForward = 1, //!< Simple evaluation, normalized variable on left-hand side, forward
evaluateBackward, //!< Simple evaluation, normalized variable on left-hand side, backward
solveForwardSimple, //!< Block of one equation, newton solver needed, forward
solveBackwardSimple, //!< Block of one equation, newton solver needed, backward
solveTwoBoundariesSimple, //!< Block of one equation, Newton solver needed, forward and backward
solveForwardComplete, //!< Block of several equations, Newton solver needed, forward
solveBackwardComplete, //!< Block of several equations, Newton solver needed, backward
solveTwoBoundariesComplete //!< Block of several equations, Newton solver needed, forward and
//!< backwar
};
enum class PacTargetKind
{
unspecified, // Must be the first one, because it’s the default initializer
ll,
dl,
dd
};
{
unspecified, // Must be the first one, because it’s the default initializer
ll,
dl,
dd
};
#endif // _COMMON_ENUMS_HH
#endif
Source diff could not be displayed: it is too large. Options to address this: view the blob.
/*
* Copyright © 2003-2023 Dynare Team
* Copyright © 2003-2024 Dynare Team
*
* This file is part of Dynare.
*
......@@ -17,92 +17,100 @@
* along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef _COMPUTINGTASKS_HH
#define _COMPUTINGTASKS_HH
#ifndef COMPUTING_TASKS_HH
#define COMPUTING_TASKS_HH
#include <ostream>
#include <memory>
#include <optional>
#include <ostream>
#include "SymbolList.hh"
#include "SymbolTable.hh"
#include "Statement.hh"
#include "StaticModel.hh"
#include "DynamicModel.hh"
#include "ModelEquationBlock.hh"
#include "Statement.hh"
#include "StaticModel.hh"
#include "SymbolList.hh"
#include "SymbolTable.hh"
class SteadyStatement : public Statement
{
private:
const OptionsList options_list;
public:
explicit SteadyStatement(OptionsList options_list_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class CheckStatement : public Statement
{
private:
const OptionsList options_list;
public:
explicit CheckStatement(OptionsList options_list_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class SimulStatement : public Statement
{
private:
const OptionsList options_list;
public:
explicit SimulStatement(OptionsList options_list_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class PerfectForesightSetupStatement : public Statement
{
private:
const OptionsList options_list;
public:
explicit PerfectForesightSetupStatement(OptionsList options_list_arg);
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class PerfectForesightSolverStatement : public Statement
{
private:
const OptionsList options_list;
public:
explicit PerfectForesightSolverStatement(OptionsList options_list_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class PerfectForesightWithExpectationErrorsSetupStatement : public Statement
{
private:
const OptionsList options_list;
public:
explicit PerfectForesightWithExpectationErrorsSetupStatement(OptionsList options_list_arg);
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class PerfectForesightWithExpectationErrorsSolverStatement : public Statement
{
private:
const OptionsList options_list;
public:
explicit PerfectForesightWithExpectationErrorsSolverStatement(OptionsList options_list_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class PriorPosteriorFunctionStatement : public Statement
......@@ -110,21 +118,23 @@ class PriorPosteriorFunctionStatement : public Statement
private:
const bool prior_func;
const OptionsList options_list;
public:
PriorPosteriorFunctionStatement(const bool prior_func_arg, OptionsList options_list_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class ModelInfoStatement : public Statement
{
private:
const OptionsList options_list;
public:
explicit ModelInfoStatement(OptionsList options_list_arg);
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class StochSimulStatement : public Statement
......@@ -132,13 +142,14 @@ class StochSimulStatement : public Statement
private:
const SymbolList symbol_list;
const OptionsList options_list;
const SymbolTable &symbol_table;
const SymbolTable& symbol_table;
public:
StochSimulStatement(SymbolList symbol_list_arg, OptionsList options_list_arg,
const SymbolTable &symbol_table_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
const SymbolTable& symbol_table_arg);
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class ForecastStatement : public Statement
......@@ -146,44 +157,26 @@ class ForecastStatement : public Statement
private:
const SymbolList symbol_list;
const OptionsList options_list;
const SymbolTable &symbol_table;
const SymbolTable& symbol_table;
public:
ForecastStatement(SymbolList symbol_list_arg, OptionsList options_list_arg,
const SymbolTable &symbol_table_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
const SymbolTable& symbol_table_arg);
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class RamseyModelStatement : public Statement
{
private:
const OptionsList options_list;
public:
explicit RamseyModelStatement(OptionsList options_list_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
};
class RamseyConstraintsStatement : public Statement
{
public:
struct Constraint
{
int endo;
BinaryOpcode code;
expr_t expression;
};
using constraints_t = vector<Constraint>;
private:
const SymbolTable &symbol_table;
const constraints_t constraints;
public:
RamseyConstraintsStatement(const SymbolTable &symbol_table_arg, constraints_t constraints_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
explicit RamseyModelStatement(OptionsList options_list_arg);
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class RamseyPolicyStatement : public Statement
......@@ -191,53 +184,58 @@ class RamseyPolicyStatement : public Statement
private:
const SymbolList symbol_list;
const OptionsList options_list;
const SymbolTable &symbol_table;
const SymbolTable& symbol_table;
public:
RamseyPolicyStatement(SymbolList symbol_list_arg, OptionsList options_list_arg,
const SymbolTable &symbol_table_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
const SymbolTable& symbol_table_arg);
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class EvaluatePlannerObjectiveStatement : public Statement
{
private:
const OptionsList options_list;
public:
explicit EvaluatePlannerObjectiveStatement(OptionsList options_list_arg);
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class OccbinSetupStatement : public Statement
{
private:
const OptionsList options_list;
public:
explicit OccbinSetupStatement(OptionsList options_list_arg);
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class OccbinSolverStatement : public Statement
{
private:
const OptionsList options_list;
public:
explicit OccbinSolverStatement(OptionsList options_list_arg);
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class OccbinWriteRegimesStatement : public Statement
{
private:
const OptionsList options_list;
public:
explicit OccbinWriteRegimesStatement(OptionsList options_list_arg);
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class OccbinGraphStatement : public Statement
......@@ -245,11 +243,11 @@ class OccbinGraphStatement : public Statement
private:
const SymbolList symbol_list;
const OptionsList options_list;
public:
OccbinGraphStatement(SymbolList symbol_list_arg,
OptionsList options_list_arg);
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
OccbinGraphStatement(SymbolList symbol_list_arg, OptionsList options_list_arg);
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class DiscretionaryPolicyStatement : public Statement
......@@ -257,123 +255,134 @@ class DiscretionaryPolicyStatement : public Statement
private:
const SymbolList symbol_list;
const OptionsList options_list;
const SymbolTable &symbol_table;
const SymbolTable& symbol_table;
public:
DiscretionaryPolicyStatement(SymbolList symbol_list_arg, OptionsList options_list_arg,
const SymbolTable &symbol_table_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
const SymbolTable& symbol_table_arg);
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class RplotStatement : public Statement
{
private:
const SymbolList symbol_list;
const SymbolTable &symbol_table;
const SymbolTable& symbol_table;
public:
RplotStatement(SymbolList symbol_list_arg, const SymbolTable &symbol_table_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
RplotStatement(SymbolList symbol_list_arg, const SymbolTable& symbol_table_arg);
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class UnitRootVarsStatement : public Statement
{
public:
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class DsampleStatement : public Statement
{
private:
const int val1, val2;
public:
explicit DsampleStatement(int val1_arg);
DsampleStatement(int val1_arg, int val2_arg);
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class EstimationStatement : public Statement
{
private:
const SymbolTable &symbol_table;
const SymbolTable& symbol_table;
const SymbolList symbol_list;
const OptionsList options_list;
public:
EstimationStatement(const SymbolTable &symbol_table_arg,
SymbolList symbol_list_arg,
EstimationStatement(const SymbolTable& symbol_table_arg, SymbolList symbol_list_arg,
OptionsList options_list_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class DynareSensitivityStatement : public Statement
class SensitivityStatement : public Statement
{
private:
const OptionsList options_list;
public:
explicit DynareSensitivityStatement(OptionsList options_list_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
explicit SensitivityStatement(OptionsList options_list_arg);
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class ObservationTrendsStatement : public Statement
{
public:
using trend_elements_t = map<string, expr_t>;
private:
const trend_elements_t trend_elements;
const SymbolTable &symbol_table;
const SymbolTable& symbol_table;
public:
ObservationTrendsStatement(trend_elements_t trend_elements_arg,
const SymbolTable &symbol_table_arg);
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
const SymbolTable& symbol_table_arg);
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class DeterministicTrendsStatement : public Statement
{
public:
using trend_elements_t = map<string, expr_t>;
private:
const trend_elements_t trend_elements;
const SymbolTable &symbol_table;
const SymbolTable& symbol_table;
public:
DeterministicTrendsStatement(trend_elements_t trend_elements_arg,
const SymbolTable &symbol_table_arg);
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
const SymbolTable& symbol_table_arg);
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class FilterInitialStateStatement : public Statement
{
public:
using filter_initial_state_elements_t = map<pair<int, int>, expr_t>;
private:
const filter_initial_state_elements_t filter_initial_state_elements;
const SymbolTable &symbol_table;
const SymbolTable& symbol_table;
public:
FilterInitialStateStatement(filter_initial_state_elements_t filter_initial_state_elements_arg,
const SymbolTable &symbol_table_arg);
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
const SymbolTable& symbol_table_arg);
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class OsrParamsStatement : public Statement
{
private:
const SymbolList symbol_list;
const SymbolTable &symbol_table;
const SymbolTable& symbol_table;
public:
OsrParamsStatement(SymbolList symbol_list_arg, const SymbolTable &symbol_table_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
OsrParamsStatement(SymbolList symbol_list_arg, const SymbolTable& symbol_table_arg);
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class OsrStatement : public Statement
......@@ -381,13 +390,14 @@ class OsrStatement : public Statement
private:
const SymbolList symbol_list;
const OptionsList options_list;
const SymbolTable &symbol_table;
const SymbolTable& symbol_table;
public:
OsrStatement(SymbolList symbol_list_arg, OptionsList options_list_arg,
const SymbolTable &symbol_table_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
const SymbolTable& symbol_table_arg);
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
//! Temporary structure used when parsing estimation_params* statements
......@@ -398,7 +408,7 @@ public:
expr_t low_bound, up_bound;
void
init(const DataTree &datatree)
init(const DataTree& datatree)
{
name = "";
low_bound = datatree.MinusInfinity;
......@@ -410,11 +420,12 @@ class OsrParamsBoundsStatement : public Statement
{
private:
const vector<OsrParams> osr_params_list;
public:
explicit OsrParamsBoundsStatement(vector<OsrParams> osr_params_list_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class DynaTypeStatement : public Statement
......@@ -422,13 +433,14 @@ class DynaTypeStatement : public Statement
private:
const SymbolList symbol_list;
const string filename;
const SymbolTable &symbol_table;
const SymbolTable& symbol_table;
public:
DynaTypeStatement(SymbolList symbol_list_arg, string filename_arg,
const SymbolTable &symbol_table_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
const SymbolTable& symbol_table_arg);
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class DynaSaveStatement : public Statement
......@@ -436,27 +448,29 @@ class DynaSaveStatement : public Statement
private:
const SymbolList symbol_list;
const string filename;
const SymbolTable &symbol_table;
const SymbolTable& symbol_table;
public:
DynaSaveStatement(SymbolList symbol_list_arg, string filename_arg,
const SymbolTable &symbol_table_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
const SymbolTable& symbol_table_arg);
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class ModelComparisonStatement : public Statement
{
public:
using filename_list_t = vector<pair<string, string>>;
private:
filename_list_t filename_list;
OptionsList options_list;
public:
ModelComparisonStatement(filename_list_t filename_list_arg,
OptionsList options_list_arg);
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
ModelComparisonStatement(filename_list_t filename_list_arg, OptionsList options_list_arg);
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
//! Temporary structure used when parsing estimation_params* statements
......@@ -469,7 +483,7 @@ public:
expr_t init_val, low_bound, up_bound, mean, std, p3, p4, jscale;
void
init(const DataTree &datatree)
init(const DataTree& datatree)
{
type = 0;
name = "";
......@@ -490,10 +504,10 @@ class AbstractEstimatedParamsStatement : public Statement
{
protected:
const vector<EstimationParams> estim_params_list;
const SymbolTable &symbol_table;
const SymbolTable& symbol_table;
AbstractEstimatedParamsStatement(vector<EstimationParams> estim_params_list_arg,
const SymbolTable &symbol_table_arg);
virtual string blockName() const = 0;
const SymbolTable& symbol_table_arg);
[[nodiscard]] virtual string blockName() const = 0;
// Part of the check pass that is common to the three estimated_params{,_init,bounds} blocks
void commonCheckPass() const;
};
......@@ -502,39 +516,51 @@ class EstimatedParamsStatement : public AbstractEstimatedParamsStatement
{
private:
const bool overwrite;
public:
EstimatedParamsStatement(vector<EstimationParams> estim_params_list_arg,
const SymbolTable &symbol_table_arg,
bool overwrite_arg);
string blockName() const override { return "estimated_params"; };
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
const SymbolTable& symbol_table_arg, bool overwrite_arg);
[[nodiscard]] string
blockName() const override
{
return "estimated_params";
}
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class EstimatedParamsInitStatement : public AbstractEstimatedParamsStatement
{
private:
const bool use_calibration;
public:
EstimatedParamsInitStatement(vector<EstimationParams> estim_params_list_arg,
const SymbolTable &symbol_table_arg,
const bool use_calibration_arg);
string blockName() const override { return "estimated_params_init"; };
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
const SymbolTable& symbol_table_arg, const bool use_calibration_arg);
[[nodiscard]] string
blockName() const override
{
return "estimated_params_init";
}
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class EstimatedParamsBoundsStatement : public AbstractEstimatedParamsStatement
{
public:
EstimatedParamsBoundsStatement(vector<EstimationParams> estim_params_list_arg,
const SymbolTable &symbol_table_arg);
string blockName() const override { return "estimated_params_bounds"; };
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
const SymbolTable& symbol_table_arg);
[[nodiscard]] string
blockName() const override
{
return "estimated_params_bounds";
}
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class EstimatedParamsRemoveStatement : public Statement
......@@ -542,11 +568,11 @@ class EstimatedParamsRemoveStatement : public Statement
public:
// Only the type, name and name2 fields of EstimationParams are used
const vector<EstimationParams> estim_params_list;
const SymbolTable &symbol_table;
const SymbolTable& symbol_table;
EstimatedParamsRemoveStatement(vector<EstimationParams> estim_params_list_arg,
const SymbolTable &symbol_table_arg);
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
const SymbolTable& symbol_table_arg);
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class OptimWeightsStatement : public Statement
......@@ -554,35 +580,37 @@ class OptimWeightsStatement : public Statement
public:
using var_weights_t = map<string, expr_t>;
using covar_weights_t = map<pair<string, string>, expr_t>;
private:
const var_weights_t var_weights;
const covar_weights_t covar_weights;
const SymbolTable &symbol_table;
const SymbolTable& symbol_table;
public:
OptimWeightsStatement(var_weights_t var_weights_arg,
covar_weights_t covar_weights_arg,
const SymbolTable &symbol_table_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
OptimWeightsStatement(var_weights_t var_weights_arg, covar_weights_t covar_weights_arg,
const SymbolTable& symbol_table_arg);
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class PlannerObjectiveStatement : public Statement
{
private:
PlannerObjective model_tree;
bool computing_pass_called{false};
unique_ptr<PlannerObjective> model_tree;
bool computing_pass_called {false};
public:
explicit PlannerObjectiveStatement(const PlannerObjective &model_tree_arg);
explicit PlannerObjectiveStatement(unique_ptr<PlannerObjective> model_tree_arg);
/*! \todo check there are only endogenous variables at the current period in the objective
(no exogenous, no lead/lag) */
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
/*! \todo allow for the possibility of disabling temporary terms */
void computingPass(const ModFileStructure &mod_file_struct) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void computingPass(const ModFileStructure& mod_file_struct) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
//! Return a reference the Planner Objective model tree
const PlannerObjective &getPlannerObjective() const;
[[nodiscard]] const PlannerObjective& getPlannerObjective() const;
};
class BVARDensityStatement : public Statement
......@@ -590,11 +618,12 @@ class BVARDensityStatement : public Statement
private:
const int maxnlags;
const OptionsList options_list;
public:
BVARDensityStatement(int maxnlags_arg, OptionsList options_list_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class BVARForecastStatement : public Statement
......@@ -602,11 +631,12 @@ class BVARForecastStatement : public Statement
private:
const int nlags;
const OptionsList options_list;
public:
BVARForecastStatement(int nlags_arg, OptionsList options_list_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class BVARIRFStatement : public Statement
......@@ -614,66 +644,72 @@ class BVARIRFStatement : public Statement
private:
const int nirf;
const string identificationname;
public:
BVARIRFStatement(int nirf_arg, string identificationname_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class SBVARStatement : public Statement
{
private:
const OptionsList options_list;
public:
explicit SBVARStatement(OptionsList options_list_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class MSSBVAREstimationStatement : public Statement
{
private:
const OptionsList options_list;
public:
explicit MSSBVAREstimationStatement(OptionsList options_list_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class MSSBVARSimulationStatement : public Statement
{
private:
const OptionsList options_list;
public:
explicit MSSBVARSimulationStatement(OptionsList options_list_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class MSSBVARComputeMDDStatement : public Statement
{
private:
const OptionsList options_list;
public:
explicit MSSBVARComputeMDDStatement(OptionsList options_list_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class MSSBVARComputeProbabilitiesStatement : public Statement
{
private:
const OptionsList options_list;
public:
explicit MSSBVARComputeProbabilitiesStatement(OptionsList options_list_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class MSSBVARIrfStatement : public Statement
......@@ -681,90 +717,100 @@ class MSSBVARIrfStatement : public Statement
private:
const SymbolList symbol_list;
const OptionsList options_list;
const SymbolTable &symbol_table;
const SymbolTable& symbol_table;
public:
MSSBVARIrfStatement(SymbolList symbol_list_arg, OptionsList options_list_arg,
const SymbolTable &symbol_table_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
const SymbolTable& symbol_table_arg);
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class MSSBVARForecastStatement : public Statement
{
private:
const OptionsList options_list;
public:
explicit MSSBVARForecastStatement(OptionsList options_list_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class MSSBVARVarianceDecompositionStatement : public Statement
{
private:
const OptionsList options_list;
public:
explicit MSSBVARVarianceDecompositionStatement(OptionsList options_list_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class IdentificationStatement : public Statement
{
private:
OptionsList options_list;
public:
explicit IdentificationStatement(OptionsList options_list_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class WriteLatexDynamicModelStatement : public Statement
{
private:
const DynamicModel &dynamic_model;
const DynamicModel& dynamic_model;
const bool write_equation_tags;
public:
WriteLatexDynamicModelStatement(const DynamicModel &dynamic_model_arg, bool write_equation_tags_arg);
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
WriteLatexDynamicModelStatement(const DynamicModel& dynamic_model_arg,
bool write_equation_tags_arg);
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class WriteLatexStaticModelStatement : public Statement
{
private:
const StaticModel &static_model;
const StaticModel& static_model;
const bool write_equation_tags;
public:
WriteLatexStaticModelStatement(const StaticModel &static_model_arg, bool write_equation_tags_arg);
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
WriteLatexStaticModelStatement(const StaticModel& static_model_arg, bool write_equation_tags_arg);
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class WriteLatexOriginalModelStatement : public Statement
{
private:
const DynamicModel &original_model;
const DynamicModel& original_model;
const bool write_equation_tags;
public:
WriteLatexOriginalModelStatement(const DynamicModel &original_model_arg, bool write_equation_tags_arg);
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
WriteLatexOriginalModelStatement(const DynamicModel& original_model_arg,
bool write_equation_tags_arg);
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class WriteLatexSteadyStateModelStatement : public Statement
{
private:
const SteadyStateModel &steady_state_model;
const SteadyStateModel& steady_state_model;
public:
explicit WriteLatexSteadyStateModelStatement(const SteadyStateModel &steady_state_model_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
explicit WriteLatexSteadyStateModelStatement(const SteadyStateModel& steady_state_model_arg);
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class ShockDecompositionStatement : public Statement
......@@ -772,13 +818,14 @@ class ShockDecompositionStatement : public Statement
private:
const SymbolList symbol_list;
const OptionsList options_list;
const SymbolTable &symbol_table;
const SymbolTable& symbol_table;
public:
ShockDecompositionStatement(SymbolList symbol_list_arg, OptionsList options_list_arg,
const SymbolTable &symbol_table_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
const SymbolTable& symbol_table_arg);
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class RealtimeShockDecompositionStatement : public Statement
......@@ -786,13 +833,14 @@ class RealtimeShockDecompositionStatement : public Statement
private:
const SymbolList symbol_list;
const OptionsList options_list;
const SymbolTable &symbol_table;
const SymbolTable& symbol_table;
public:
RealtimeShockDecompositionStatement(SymbolList symbol_list_arg, OptionsList options_list_arg,
const SymbolTable &symbol_table_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
const SymbolTable& symbol_table_arg);
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class PlotShockDecompositionStatement : public Statement
......@@ -800,13 +848,14 @@ class PlotShockDecompositionStatement : public Statement
private:
const SymbolList symbol_list;
const OptionsList options_list;
const SymbolTable &symbol_table;
const SymbolTable& symbol_table;
public:
PlotShockDecompositionStatement(SymbolList symbol_list_arg, OptionsList options_list_arg,
const SymbolTable &symbol_table_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
const SymbolTable& symbol_table_arg);
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class InitialConditionDecompositionStatement : public Statement
......@@ -814,37 +863,40 @@ class InitialConditionDecompositionStatement : public Statement
private:
const SymbolList symbol_list;
const OptionsList options_list;
const SymbolTable &symbol_table;
const SymbolTable& symbol_table;
public:
InitialConditionDecompositionStatement(SymbolList symbol_list_arg, OptionsList options_list_arg,
const SymbolTable &symbol_table_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
const SymbolTable& symbol_table_arg);
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class SqueezeShockDecompositionStatement : public Statement
{
private:
const SymbolList symbol_list;
const SymbolTable &symbol_table;
const SymbolTable& symbol_table;
public:
SqueezeShockDecompositionStatement(SymbolList symbol_list_arg,
const SymbolTable &symbol_table_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
const SymbolTable& symbol_table_arg);
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class ConditionalForecastStatement : public Statement
{
private:
const OptionsList options_list;
public:
explicit ConditionalForecastStatement(OptionsList options_list_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class PlotConditionalForecastStatement : public Statement
......@@ -852,13 +904,14 @@ class PlotConditionalForecastStatement : public Statement
private:
const optional<int> periods; // The user is allowed not to declare periods
const SymbolList symbol_list;
const SymbolTable &symbol_table;
const SymbolTable& symbol_table;
public:
PlotConditionalForecastStatement(optional<int> periods_arg, SymbolList symbol_list_arg,
const SymbolTable &symbol_table_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
PlotConditionalForecastStatement(const optional<int>& periods_arg, SymbolList symbol_list_arg,
const SymbolTable& symbol_table_arg);
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class CalibSmootherStatement : public Statement
......@@ -866,24 +919,26 @@ class CalibSmootherStatement : public Statement
private:
const SymbolList symbol_list;
const OptionsList options_list;
const SymbolTable &symbol_table;
const SymbolTable& symbol_table;
public:
CalibSmootherStatement(SymbolList symbol_list_arg, OptionsList options_list_arg,
const SymbolTable &symbol_table_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
const SymbolTable& symbol_table_arg);
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class ExtendedPathStatement : public Statement
{
private:
const OptionsList options_list;
public:
explicit ExtendedPathStatement(OptionsList options_list_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class SvarIdentificationStatement : public Statement
......@@ -900,20 +955,21 @@ public:
};
using svar_identification_restrictions_t = vector<svar_identification_restriction>;
private:
const svar_identification_restrictions_t restrictions;
const bool upper_cholesky_present, lower_cholesky_present, constants_exclusion_present;
const SymbolTable &symbol_table;
int getMaxLag() const;
const SymbolTable& symbol_table;
[[nodiscard]] int getMaxLag() const;
public:
SvarIdentificationStatement(svar_identification_restrictions_t restrictions_arg,
bool upper_cholesky_present_arg,
bool lower_cholesky_present_arg,
bool upper_cholesky_present_arg, bool lower_cholesky_present_arg,
bool constants_exclusion_present_arg,
const SymbolTable &symbol_table_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
const SymbolTable& symbol_table_arg);
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class MarkovSwitchingStatement : public Statement
......@@ -921,50 +977,54 @@ class MarkovSwitchingStatement : public Statement
private:
const OptionsList options_list;
map<pair<int, int>, double> restriction_map;
public:
explicit MarkovSwitchingStatement(OptionsList options_list_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class SvarStatement : public Statement
{
private:
const OptionsList options_list;
public:
explicit SvarStatement(OptionsList options_list_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class SvarGlobalIdentificationCheckStatement : public Statement
{
public:
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class SetTimeStatement : public Statement
{
private:
const OptionsList options_list;
const string period;
public:
explicit SetTimeStatement(OptionsList options_list_arg);
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
explicit SetTimeStatement(string period_arg);
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class EstimationDataStatement : public Statement
{
private:
const OptionsList options_list;
public:
explicit EstimationDataStatement(OptionsList options_list_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class SubsamplesStatement : public Statement
......@@ -972,32 +1032,31 @@ class SubsamplesStatement : public Statement
public:
//! Storage for declaring subsamples: map<subsample_name, <date1, date2 >
using subsample_declaration_map_t = map<string, pair<string, string>>;
private:
const string name1, name2;
const subsample_declaration_map_t subsample_declaration_map;
const SymbolTable &symbol_table;
const SymbolTable& symbol_table;
public:
SubsamplesStatement(string name1_arg,
string name2_arg,
SubsamplesStatement(string name1_arg, string name2_arg,
subsample_declaration_map_t subsample_declaration_map_arg,
const SymbolTable &symbol_table_arg);
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
const SymbolTable& symbol_table_arg);
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class SubsamplesEqualStatement : public Statement
{
private:
const string to_name1, to_name2, from_name1, from_name2;
const SymbolTable &symbol_table;
const SymbolTable& symbol_table;
public:
SubsamplesEqualStatement(string to_name1_arg,
string to_name2_arg,
string from_name1_arg,
string from_name2_arg,
const SymbolTable &symbol_table_arg);
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
SubsamplesEqualStatement(string to_name1_arg, string to_name2_arg, string from_name1_arg,
string from_name2_arg, const SymbolTable& symbol_table_arg);
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class JointPriorStatement : public Statement
......@@ -1006,14 +1065,14 @@ private:
const vector<string> joint_parameters;
const PriorDistributions prior_shape;
const OptionsList options_list;
void writeOutputHelper(ostream &output, const string &field, const string &lhs_field) const;
void writeOutputHelper(ostream& output, const string& field, const string& lhs_field) const;
public:
JointPriorStatement(vector<string> joint_parameters_arg,
PriorDistributions prior_shape_arg,
JointPriorStatement(vector<string> joint_parameters_arg, PriorDistributions prior_shape_arg,
OptionsList options_list_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class BasicPriorStatement : public Statement
......@@ -1023,68 +1082,58 @@ protected:
const PriorDistributions prior_shape;
const expr_t variance;
const OptionsList options_list;
BasicPriorStatement(string name_arg,
string subsample_name_arg,
PriorDistributions prior_shape_arg,
expr_t variance_arg,
BasicPriorStatement(string name_arg, string subsample_name_arg,
PriorDistributions prior_shape_arg, expr_t variance_arg,
OptionsList options_list_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void get_base_name(const SymbolType symb_type, string &lhs_field) const;
void writeCommonOutput(ostream &output, const string &lhs_field) const;
void writeCommonOutputHelper(ostream &output, const string &field, const string &lhs_field) const;
void writePriorOutput(ostream &output, string &lhs_field, const string &name2) const;
bool is_structural_innovation(const SymbolType symb_type) const;
void writePriorIndex(ostream &output, const string &lhs_field) const;
void writeVarianceOption(ostream &output, const string &lhs_field) const;
void writeOutputHelper(ostream &output, const string &field, const string &lhs_field) const;
void writeShape(ostream &output, const string &lhs_field) const;
void writeJsonShape(ostream &output) const;
void writeJsonPriorOutput(ostream &output) const;
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void get_base_name(const SymbolType symb_type, string& lhs_field) const;
void writeCommonOutput(ostream& output, const string& lhs_field) const;
void writeCommonOutputHelper(ostream& output, const string& field, const string& lhs_field) const;
void writePriorOutput(ostream& output, string& lhs_field, const string& name2) const;
[[nodiscard]] bool is_structural_innovation(const SymbolType symb_type) const;
void writePriorIndex(ostream& output, const string& lhs_field) const;
void writeVarianceOption(ostream& output, const string& lhs_field) const;
void writeOutputHelper(ostream& output, const string& field, const string& lhs_field) const;
void writeShape(ostream& output, const string& lhs_field) const;
void writeJsonShape(ostream& output) const;
void writeJsonPriorOutput(ostream& output) const;
};
class PriorStatement : public BasicPriorStatement
{
public:
PriorStatement(string name_arg,
string subsample_name_arg,
PriorDistributions prior_shape_arg,
expr_t variance_arg,
OptionsList options_list_arg);
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
PriorStatement(string name_arg, string subsample_name_arg, PriorDistributions prior_shape_arg,
expr_t variance_arg, OptionsList options_list_arg);
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class StdPriorStatement : public BasicPriorStatement
{
private:
const SymbolTable &symbol_table;
const SymbolTable& symbol_table;
public:
StdPriorStatement(string name_arg,
string subsample_name_arg,
PriorDistributions prior_shape_arg,
expr_t variance_arg,
OptionsList options_list_arg,
const SymbolTable &symbol_table_arg);
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
StdPriorStatement(string name_arg, string subsample_name_arg, PriorDistributions prior_shape_arg,
expr_t variance_arg, OptionsList options_list_arg,
const SymbolTable& symbol_table_arg);
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class CorrPriorStatement : public BasicPriorStatement
{
private:
const string name1;
const SymbolTable &symbol_table;
const SymbolTable& symbol_table;
public:
CorrPriorStatement(string name_arg1,
string name_arg2,
string subsample_name_arg,
PriorDistributions prior_shape_arg,
expr_t variance_arg,
OptionsList options_list_arg,
const SymbolTable &symbol_table_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
CorrPriorStatement(string name_arg1, string name_arg2, string subsample_name_arg,
PriorDistributions prior_shape_arg, expr_t variance_arg,
OptionsList options_list_arg, const SymbolTable& symbol_table_arg);
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class PriorEqualStatement : public Statement
......@@ -1092,21 +1141,17 @@ class PriorEqualStatement : public Statement
private:
const string to_declaration_type, to_name1, to_name2, to_subsample_name;
const string from_declaration_type, from_name1, from_name2, from_subsample_name;
const SymbolTable &symbol_table;
public:
PriorEqualStatement(string to_declaration_type_arg,
string to_name1_arg,
string to_name2_arg,
string to_subsample_name_arg,
string from_declaration_type_arg,
string from_name1_arg,
string from_name2_arg,
string from_subsample_name_arg,
const SymbolTable &symbol_table_arg);
void get_base_name(const SymbolType symb_type, string &lhs_field) const;
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
const SymbolTable& symbol_table;
public:
PriorEqualStatement(string to_declaration_type_arg, string to_name1_arg, string to_name2_arg,
string to_subsample_name_arg, string from_declaration_type_arg,
string from_name1_arg, string from_name2_arg, string from_subsample_name_arg,
const SymbolTable& symbol_table_arg);
void get_base_name(const SymbolType symb_type, string& lhs_field) const;
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class BasicOptionsStatement : public Statement
......@@ -1114,53 +1159,49 @@ class BasicOptionsStatement : public Statement
protected:
const string name, subsample_name;
const OptionsList options_list;
BasicOptionsStatement(string name_arg,
string subsample_name_arg,
OptionsList options_list_arg);
void get_base_name(const SymbolType symb_type, string &lhs_field) const;
void writeOptionsOutput(ostream &output, string &lhs_field, const string &name2) const;
void writeCommonOutput(ostream &output, const string &lhs_field) const;
void writeCommonOutputHelper(ostream &output, const string &field, const string &lhs_field) const;
bool is_structural_innovation(const SymbolType symb_type) const;
void writeOptionsIndex(ostream &output, const string &lhs_field) const;
void writeOutputHelper(ostream &output, const string &field, const string &lhs_field) const;
void writeJsonOptionsOutput(ostream &output) const;
BasicOptionsStatement(string name_arg, string subsample_name_arg, OptionsList options_list_arg);
void get_base_name(const SymbolType symb_type, string& lhs_field) const;
void writeOptionsOutput(ostream& output, string& lhs_field, const string& name2) const;
void writeCommonOutput(ostream& output, const string& lhs_field) const;
void writeCommonOutputHelper(ostream& output, const string& field, const string& lhs_field) const;
[[nodiscard]] bool is_structural_innovation(const SymbolType symb_type) const;
void writeOptionsIndex(ostream& output, const string& lhs_field) const;
void writeOutputHelper(ostream& output, const string& field, const string& lhs_field) const;
void writeJsonOptionsOutput(ostream& output) const;
};
class OptionsStatement : public BasicOptionsStatement
{
public:
OptionsStatement(string name_arg, string subsample_name_arg, OptionsList options_list_arg);
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class StdOptionsStatement : public BasicOptionsStatement
{
private:
const SymbolTable &symbol_table;
const SymbolTable& symbol_table;
public:
StdOptionsStatement(string name_arg,
string subsample_name_arg,
OptionsList options_list_arg,
const SymbolTable &symbol_table_arg);
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
StdOptionsStatement(string name_arg, string subsample_name_arg, OptionsList options_list_arg,
const SymbolTable& symbol_table_arg);
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class CorrOptionsStatement : public BasicOptionsStatement
{
private:
const string name1;
const SymbolTable &symbol_table;
const SymbolTable& symbol_table;
public:
CorrOptionsStatement(string name_arg1, string name_arg2,
string subsample_name_arg,
OptionsList options_list_arg,
const SymbolTable &symbol_table_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
CorrOptionsStatement(string name_arg1, string name_arg2, string subsample_name_arg,
OptionsList options_list_arg, const SymbolTable& symbol_table_arg);
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class OptionsEqualStatement : public Statement
......@@ -1168,49 +1209,47 @@ class OptionsEqualStatement : public Statement
private:
const string to_declaration_type, to_name1, to_name2, to_subsample_name;
const string from_declaration_type, from_name1, from_name2, from_subsample_name;
const SymbolTable &symbol_table;
public:
OptionsEqualStatement(string to_declaration_type_arg,
string to_name1_arg,
string to_name2_arg,
string to_subsample_name_arg,
string from_declaration_type_arg,
string from_name1_arg,
string from_name2_arg,
string from_subsample_name_arg,
const SymbolTable &symbol_table_arg);
void get_base_name(const SymbolType symb_type, string &lhs_field) const;
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
const SymbolTable& symbol_table;
public:
OptionsEqualStatement(string to_declaration_type_arg, string to_name1_arg, string to_name2_arg,
string to_subsample_name_arg, string from_declaration_type_arg,
string from_name1_arg, string from_name2_arg,
string from_subsample_name_arg, const SymbolTable& symbol_table_arg);
void get_base_name(const SymbolType symb_type, string& lhs_field) const;
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class ModelDiagnosticsStatement : public Statement
{
public:
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class Smoother2histvalStatement : public Statement
{
private:
const OptionsList options_list;
public:
explicit Smoother2histvalStatement(OptionsList options_list_arg);
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class MethodOfMomentsStatement : public Statement
{
private:
private:
const OptionsList options_list;
public:
explicit MethodOfMomentsStatement(OptionsList options_list_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class GenerateIRFsStatement : public Statement
......@@ -1220,50 +1259,84 @@ private:
const OptionsList options_list;
const vector<string> generate_irf_names;
const vector<map<string, double>> generate_irf_elements;
public:
GenerateIRFsStatement(OptionsList options_list_arg,
vector<string> generate_irf_names_arg,
GenerateIRFsStatement(OptionsList options_list_arg, vector<string> generate_irf_names_arg,
vector<map<string, double>> generate_irf_elements_arg);
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class MatchedMomentsStatement : public Statement
{
private:
const SymbolTable &symbol_table;
const SymbolTable& symbol_table;
public:
/* Each moment is represented by a three vectors: symb_ids, lags, powers.
See the definition of ExprNode::matchMatchedMoment() for more details */
const vector<tuple<vector<int>, vector<int>, vector<int>>> moments;
MatchedMomentsStatement(const SymbolTable &symbol_table_arg,
MatchedMomentsStatement(const SymbolTable& symbol_table_arg,
vector<tuple<vector<int>, vector<int>, vector<int>>> moments_arg);
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class OccbinConstraintsStatement : public Statement
{
private:
DataTree data_tree;
public:
// The tuple is (name, bind, relax, error_bind, error_relax) (where relax and error_{bind,relax} can be nullptr)
const vector<tuple<string, BinaryOpNode *, BinaryOpNode *, expr_t, expr_t>> constraints;
OccbinConstraintsStatement(const DataTree &data_tree_arg,
vector<tuple<string, BinaryOpNode *, BinaryOpNode *, expr_t, expr_t>> constraints_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
// The tuple is (name, bind, relax, error_bind, error_relax) (where relax and error_{bind,relax}
// can be nullptr)
const vector<tuple<string, BinaryOpNode*, BinaryOpNode*, expr_t, expr_t>> constraints;
OccbinConstraintsStatement(
const DataTree& data_tree_arg,
vector<tuple<string, BinaryOpNode*, BinaryOpNode*, expr_t, expr_t>> constraints_arg);
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class ResidStatement : public Statement
{
private:
const OptionsList options_list;
public:
explicit ResidStatement(OptionsList options_list_arg);
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
};
class MatchedIrfsStatement : public Statement
{
public:
// (endo name, exo name) → vector of (period start, period end, value, weight)
using matched_irfs_t = map<pair<string, string>, vector<tuple<int, int, expr_t, expr_t>>>;
MatchedIrfsStatement(matched_irfs_t values_weights_arg, bool overwrite_arg);
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
private:
const matched_irfs_t values_weights;
const bool overwrite;
};
class MatchedIrfsWeightsStatement : public Statement
{
public:
/* (endo1 name, period index or range for endo1, exo1 name, endo2 name, period index or range for
endo2, exo2 name) → weight */
using matched_irfs_weights_t = map<tuple<string, string, string, string, string, string>, expr_t>;
MatchedIrfsWeightsStatement(matched_irfs_weights_t weights_arg, bool overwrite_arg);
void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream& output) const override;
private:
const matched_irfs_weights_t weights;
const bool overwrite;
};
#endif
/*
* Copyright © 2010-2023 Dynare Team
* Copyright © 2010-2024 Dynare Team
*
* This file is part of Dynare.
*
......@@ -17,33 +17,25 @@
* along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/
#include <iostream>
#include <fstream>
#include <iostream>
#include <utility>
#include <vector>
#include "ConfigFile.hh"
#ifdef _WIN32
# include <shlobj.h>
#endif
#include "Configuration.hh"
#include "DataTree.hh" // For strsplit()
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
#include <boost/algorithm/string/trim.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <boost/tokenizer.hpp>
#pragma GCC diagnostic pop
using namespace std;
Hook::Hook(string global_init_file_arg)
{
if (global_init_file_arg.empty())
{
cerr << "ERROR: The Hook must have a Global Initialization File argument." << endl;
exit(EXIT_FAILURE);
}
hooks["global_init_file"] = move(global_init_file_arg);
}
Path::Path(vector<string> includepath_arg)
Configuration::Path::Path(vector<string> includepath_arg)
{
if (includepath_arg.empty())
{
......@@ -53,24 +45,27 @@ Path::Path(vector<string> includepath_arg)
paths["include"] = move(includepath_arg);
}
FollowerNode::FollowerNode(string computerName_arg, string port_arg, int minCpuNbr_arg, int maxCpuNbr_arg, string userName_arg,
string password_arg, string remoteDrive_arg, string remoteDirectory_arg,
string programPath_arg, string programConfig_arg, string matlabOctavePath_arg, bool singleCompThread_arg,
int numberOfThreadsPerJob_arg, string operatingSystem_arg) :
computerName{move(computerName_arg)},
port{move(port_arg)},
minCpuNbr{minCpuNbr_arg},
maxCpuNbr{maxCpuNbr_arg},
userName{move(userName_arg)},
password{move(password_arg)},
remoteDrive{move(remoteDrive_arg)},
remoteDirectory{move(remoteDirectory_arg)},
programPath{move(programPath_arg)},
programConfig{move(programConfig_arg)},
matlabOctavePath{move(matlabOctavePath_arg)},
singleCompThread{singleCompThread_arg},
numberOfThreadsPerJob{numberOfThreadsPerJob_arg},
operatingSystem{move(operatingSystem_arg)}
Configuration::FollowerNode::FollowerNode(string computerName_arg, string port_arg,
int minCpuNbr_arg, int maxCpuNbr_arg, string userName_arg,
string password_arg, string remoteDrive_arg,
string remoteDirectory_arg, string programPath_arg,
string programConfig_arg, string matlabOctavePath_arg,
bool singleCompThread_arg, int numberOfThreadsPerJob_arg,
string operatingSystem_arg) :
computerName {move(computerName_arg)},
port {move(port_arg)},
minCpuNbr {minCpuNbr_arg},
maxCpuNbr {maxCpuNbr_arg},
userName {move(userName_arg)},
password {move(password_arg)},
remoteDrive {move(remoteDrive_arg)},
remoteDirectory {move(remoteDirectory_arg)},
programPath {move(programPath_arg)},
programConfig {move(programConfig_arg)},
matlabOctavePath {move(matlabOctavePath_arg)},
singleCompThread {singleCompThread_arg},
numberOfThreadsPerJob {numberOfThreadsPerJob_arg},
operatingSystem {move(operatingSystem_arg)}
{
if (computerName.empty())
{
......@@ -81,13 +76,14 @@ FollowerNode::FollowerNode(string computerName_arg, string port_arg, int minCpuN
if (!operatingSystem.empty())
if (operatingSystem != "windows" && operatingSystem != "unix")
{
cerr << "ERROR: The OperatingSystem must be either 'unix' or 'windows' (Case Sensitive)." << endl;
cerr << "ERROR: The OperatingSystem must be either 'unix' or 'windows' (Case Sensitive)."
<< endl;
exit(EXIT_FAILURE);
}
}
Cluster::Cluster(member_nodes_t member_nodes_arg) :
member_nodes{move(member_nodes_arg)}
Configuration::Cluster::Cluster(member_nodes_t member_nodes_arg) :
member_nodes {move(member_nodes_arg)}
{
if (member_nodes.empty())
{
......@@ -96,88 +92,82 @@ Cluster::Cluster(member_nodes_t member_nodes_arg) :
}
}
ConfigFile::ConfigFile(bool parallel_arg, bool parallel_test_arg,
bool parallel_follower_open_mode_arg, bool parallel_use_psexec_arg,
string cluster_name_arg) :
parallel{parallel_arg}, parallel_test{parallel_test_arg},
parallel_follower_open_mode{parallel_follower_open_mode_arg},
parallel_use_psexec{parallel_use_psexec_arg},
cluster_name{move(cluster_name_arg)}
Configuration::Configuration(bool parallel_arg, bool parallel_test_arg,
bool parallel_follower_open_mode_arg, bool parallel_use_psexec_arg,
string cluster_name_arg) :
parallel {parallel_arg},
parallel_test {parallel_test_arg},
parallel_follower_open_mode {parallel_follower_open_mode_arg},
parallel_use_psexec {parallel_use_psexec_arg},
cluster_name {move(cluster_name_arg)}
{
}
void
ConfigFile::getConfigFileInfo(const filesystem::path &config_file)
Configuration::getConfigFileInfo(const filesystem::path& conffile_option,
WarningConsolidation& warnings)
{
using namespace boost;
ifstream configFile;
filesystem::path config_file {conffile_option};
if (config_file.empty())
{
filesystem::path defaultConfigFile;
// Test OS and try to open default file
#if defined(_WIN32) || defined(__CYGWIN32__)
if (auto appdata = getenv("APPDATA");
appdata)
defaultConfigFile = filesystem::path{appdata} / "dynare.ini";
else
{
if (parallel || parallel_test)
cerr << "ERROR: ";
else
cerr << "WARNING: ";
cerr << "APPDATA environment variable not found." << endl;
config_file = findConfigFile("dynare.ini");
if (parallel || parallel_test)
exit(EXIT_FAILURE);
}
#else
if (auto home = getenv("HOME");
home)
defaultConfigFile = filesystem::path{home} / ".dynare";
else
if (config_file.empty()) // Try old default location (Dynare ⩽ 5) for backward compatibility
{
if (parallel || parallel_test)
cerr << "ERROR: ";
else
cerr << "WARNING: ";
cerr << "HOME environment variable not found." << endl;
if (parallel || parallel_test)
exit(EXIT_FAILURE);
}
filesystem::path old_default_config_file;
#ifdef _WIN32
array<wchar_t, MAX_PATH + 1> appdata;
if (SHGetFolderPathW(nullptr, CSIDL_APPDATA | CSIDL_FLAG_DONT_VERIFY, nullptr,
SHGFP_TYPE_CURRENT, appdata.data())
== S_OK)
old_default_config_file = filesystem::path {appdata.data()} / "dynare.ini";
#else
if (auto home = getenv("HOME"); home)
old_default_config_file = filesystem::path {home} / ".dynare";
#endif
configFile.open(defaultConfigFile, fstream::in);
if (!configFile.is_open())
{
if (parallel || parallel_test)
if (!old_default_config_file.empty() && exists(old_default_config_file))
{
cerr << "ERROR: Could not open the default config file (" << defaultConfigFile.string() << ")" << endl;
exit(EXIT_FAILURE);
warnings << "WARNING: the location " << old_default_config_file.string()
<< " for the configuration file is obsolete; please see the reference"
<< " manual for the new location." << endl;
config_file = old_default_config_file;
}
else
return;
}
}
else
if (config_file.empty())
{
configFile.open(config_file, fstream::in);
if (!configFile.is_open())
if (parallel || parallel_test)
{
cerr << "ERROR: Couldn't open file " << config_file.string() << endl;;
cerr << "ERROR: the parallel or parallel_test option was passed but no configuration "
<< "file was found" << endl;
exit(EXIT_FAILURE);
}
else
return;
}
string name, computerName, port, userName, password, remoteDrive,
remoteDirectory, programPath, programConfig, matlabOctavePath,
operatingSystem, global_init_file;
ifstream configFile;
configFile.open(config_file, fstream::in);
if (!configFile.is_open())
{
cerr << "ERROR: Couldn't open configuration file " << config_file.string() << endl;
exit(EXIT_FAILURE);
}
string name, computerName, port, userName, password, remoteDrive, remoteDirectory, programPath,
programConfig, matlabOctavePath, operatingSystem;
vector<string> includepath;
int minCpuNbr{0}, maxCpuNbr{0};
int numberOfThreadsPerJob{1};
bool singleCompThread{false};
int minCpuNbr {0}, maxCpuNbr {0};
int numberOfThreadsPerJob {1};
bool singleCompThread {false};
member_nodes_t member_nodes;
bool inHooks{false}, inNode{false}, inCluster{false}, inPaths{false};
bool inHooks {false}, inNode {false}, inCluster {false}, inPaths {false};
while (configFile.good())
{
......@@ -187,24 +177,17 @@ ConfigFile::getConfigFileInfo(const filesystem::path &config_file)
if (line.empty() || !line.compare(0, 1, "#"))
continue;
if (line == "[node]"
|| line == "[cluster]"
|| line == "[hooks]"
|| line == "[paths]")
if (line == "[node]" || line == "[cluster]" || line == "[hooks]" || line == "[paths]")
{
if (!global_init_file.empty())
// we were just in [hooks]
addHooksConfFileElement(global_init_file);
else if (!includepath.empty())
if (!includepath.empty())
// we were just in [paths]
addPathsConfFileElement(includepath);
else
// we were just in [node] or [cluster]
addParallelConfFileElement(inNode, inCluster, member_nodes, name,
computerName, port, minCpuNbr, maxCpuNbr, userName,
password, remoteDrive, remoteDirectory,
programPath, programConfig, matlabOctavePath, singleCompThread,
numberOfThreadsPerJob, operatingSystem);
addParallelConfFileElement(
inNode, inCluster, member_nodes, name, computerName, port, minCpuNbr, maxCpuNbr,
userName, password, remoteDrive, remoteDirectory, programPath, programConfig,
matlabOctavePath, singleCompThread, numberOfThreadsPerJob, operatingSystem);
//! Reset communication vars / option defaults
if (line == "[hooks]")
......@@ -236,9 +219,8 @@ ConfigFile::getConfigFileInfo(const filesystem::path &config_file)
inPaths = false;
}
name = userName = computerName = port = password = remoteDrive
= remoteDirectory = programPath = programConfig = matlabOctavePath
= operatingSystem = global_init_file = "";
name = userName = computerName = port = password = remoteDrive = remoteDirectory
= programPath = programConfig = matlabOctavePath = operatingSystem = "";
includepath.clear();
minCpuNbr = maxCpuNbr = 0;
numberOfThreadsPerJob = 1;
......@@ -251,7 +233,8 @@ ConfigFile::getConfigFileInfo(const filesystem::path &config_file)
split(tokenizedLine, line, is_any_of("="));
if (tokenizedLine.size() != 2)
{
cerr << "ERROR (in config file): Options should be formatted as 'option = value'." << endl;
cerr << "ERROR (in config file): Options should be formatted as 'option = value'."
<< endl;
exit(EXIT_FAILURE);
}
trim(tokenizedLine.front());
......@@ -263,12 +246,15 @@ ConfigFile::getConfigFileInfo(const filesystem::path &config_file)
global_init_file = tokenizedLine.back();
else
{
cerr << "ERROR: May not have more than one GlobalInitFile option in [hooks] block." << endl;
cerr
<< "ERROR: May not have more than one GlobalInitFile option in [hooks] block."
<< endl;
exit(EXIT_FAILURE);
}
else
{
cerr << "ERROR: Unrecognized option " << tokenizedLine.front() << " in [hooks] block." << endl;
cerr << "ERROR: Unrecognized option " << tokenizedLine.front()
<< " in [hooks] block." << endl;
exit(EXIT_FAILURE);
}
else if (inPaths)
......@@ -277,7 +263,7 @@ ConfigFile::getConfigFileInfo(const filesystem::path &config_file)
{
vector<string> tokenizedPath;
split(tokenizedPath, tokenizedLine.back(), is_any_of(":"), token_compress_on);
for (auto &it : tokenizedPath)
for (auto& it : tokenizedPath)
if (!it.empty())
{
trim(it);
......@@ -286,173 +272,179 @@ ConfigFile::getConfigFileInfo(const filesystem::path &config_file)
}
else
{
cerr << "ERROR: May not have more than one Include option in [paths] block." << endl;
cerr << "ERROR: May not have more than one Include option in [paths] block."
<< endl;
exit(EXIT_FAILURE);
}
else
{
cerr << "ERROR: Unrecognized option " << tokenizedLine.front() << " in [paths] block." << endl;
cerr << "ERROR: Unrecognized option " << tokenizedLine.front()
<< " in [paths] block." << endl;
exit(EXIT_FAILURE);
}
else
if (tokenizedLine.front() == "Name")
name = tokenizedLine.back();
else if (tokenizedLine.front() == "CPUnbr")
{
vector<string> tokenizedCpuNbr;
split(tokenizedCpuNbr, tokenizedLine.back(), is_any_of(":"));
try
{
if (tokenizedCpuNbr.size() == 1)
{
minCpuNbr = 1;
maxCpuNbr = stoi(tokenizedCpuNbr.front());
}
else if (tokenizedCpuNbr.size() == 2
&& tokenizedCpuNbr[0].at(0) == '['
&& tokenizedCpuNbr[1].at(tokenizedCpuNbr[1].size()-1) == ']')
{
tokenizedCpuNbr[0].erase(0, 1);
tokenizedCpuNbr[1].erase(tokenizedCpuNbr[1].size()-1, 1);
minCpuNbr = stoi(tokenizedCpuNbr[0]);
maxCpuNbr = stoi(tokenizedCpuNbr[1]);
}
}
catch (const invalid_argument &)
{
cerr << "ERROR: Could not convert value to integer for CPUnbr." << endl;
exit(EXIT_FAILURE);
}
if (minCpuNbr <= 0 || maxCpuNbr <= 0)
{
cerr << "ERROR: Syntax for the CPUnbr option is as follows:" << endl
<< " 1) CPUnbr = <int>" << endl
<< " or 2) CPUnbr = [<int>:<int>]" << endl
<< " where <int> is an Integer > 0." << endl;
exit(EXIT_FAILURE);
}
minCpuNbr--;
maxCpuNbr--;
if (minCpuNbr > maxCpuNbr)
{
int tmp = maxCpuNbr;
maxCpuNbr = minCpuNbr;
minCpuNbr = tmp;
}
}
else if (tokenizedLine.front() == "Port")
port = tokenizedLine.back();
else if (tokenizedLine.front() == "ComputerName")
computerName = tokenizedLine.back();
else if (tokenizedLine.front() == "UserName")
userName = tokenizedLine.back();
else if (tokenizedLine.front() == "Password")
password = tokenizedLine.back();
else if (tokenizedLine.front() == "RemoteDrive")
remoteDrive = tokenizedLine.back();
else if (tokenizedLine.front() == "RemoteDirectory")
remoteDirectory = tokenizedLine.back();
else if (tokenizedLine.front() == "DynarePath"
|| tokenizedLine.front() == "ProgramPath")
programPath = tokenizedLine.back();
else if (tokenizedLine.front() == "ProgramConfig")
programConfig = tokenizedLine.back();
else if (tokenizedLine.front() == "MatlabOctavePath")
matlabOctavePath = tokenizedLine.back();
else if (tokenizedLine.front() == "NumberOfThreadsPerJob")
numberOfThreadsPerJob = stoi(tokenizedLine.back());
else if (tokenizedLine.front() == "SingleCompThread")
if (tokenizedLine.back() == "true")
singleCompThread = true;
else if (tokenizedLine.back() == "false")
singleCompThread = false;
else
else if (tokenizedLine.front() == "Name")
name = tokenizedLine.back();
else if (tokenizedLine.front() == "CPUnbr")
{
vector<string> tokenizedCpuNbr;
split(tokenizedCpuNbr, tokenizedLine.back(), is_any_of(":"));
try
{
cerr << "ERROR (in config file): The value passed to SingleCompThread may only be 'true' or 'false'." << endl;
if (tokenizedCpuNbr.size() == 1)
{
minCpuNbr = 1;
maxCpuNbr = stoi(tokenizedCpuNbr.front());
}
else if (tokenizedCpuNbr.size() == 2 && tokenizedCpuNbr[0].at(0) == '['
&& tokenizedCpuNbr[1].at(tokenizedCpuNbr[1].size() - 1) == ']')
{
tokenizedCpuNbr[0].erase(0, 1);
tokenizedCpuNbr[1].erase(tokenizedCpuNbr[1].size() - 1, 1);
minCpuNbr = stoi(tokenizedCpuNbr[0]);
maxCpuNbr = stoi(tokenizedCpuNbr[1]);
}
}
catch (const invalid_argument&)
{
cerr << "ERROR: Could not convert value to integer for CPUnbr." << endl;
exit(EXIT_FAILURE);
}
else if (tokenizedLine.front() == "OperatingSystem")
operatingSystem = tokenizedLine.back();
else if (tokenizedLine.front() == "Members")
{
char_separator sep(" ,;", "()", drop_empty_tokens);
tokenizer tokens(tokenizedLine.back(), sep);
string node_name;
for (bool begin_weight{false};
const auto &token : tokens)
{
if (token == "(")
{
begin_weight = true;
continue;
}
else if (token == ")")
{
node_name.clear();
begin_weight = false;
continue;
}
if (!begin_weight)
{
if (!node_name.empty())
{
if (member_nodes.contains(node_name))
{
cerr << "ERROR (in config file): Node entered twice in specification of cluster." << endl;
exit(EXIT_FAILURE);
}
else
member_nodes[node_name] = 1.0;
}
node_name = token;
}
else
try
if (minCpuNbr <= 0 || maxCpuNbr <= 0)
{
cerr << "ERROR: Syntax for the CPUnbr option is as follows:" << endl
<< " 1) CPUnbr = <int>" << endl
<< " or 2) CPUnbr = [<int>:<int>]" << endl
<< " where <int> is an Integer > 0." << endl;
exit(EXIT_FAILURE);
}
minCpuNbr--;
maxCpuNbr--;
if (minCpuNbr > maxCpuNbr)
{
int tmp = maxCpuNbr;
maxCpuNbr = minCpuNbr;
minCpuNbr = tmp;
}
}
else if (tokenizedLine.front() == "Port")
port = tokenizedLine.back();
else if (tokenizedLine.front() == "ComputerName")
computerName = tokenizedLine.back();
else if (tokenizedLine.front() == "UserName")
userName = tokenizedLine.back();
else if (tokenizedLine.front() == "Password")
password = tokenizedLine.back();
else if (tokenizedLine.front() == "RemoteDrive")
remoteDrive = tokenizedLine.back();
else if (tokenizedLine.front() == "RemoteDirectory")
remoteDirectory = tokenizedLine.back();
else if (tokenizedLine.front() == "DynarePath" || tokenizedLine.front() == "ProgramPath")
programPath = tokenizedLine.back();
else if (tokenizedLine.front() == "ProgramConfig")
programConfig = tokenizedLine.back();
else if (tokenizedLine.front() == "MatlabOctavePath")
matlabOctavePath = tokenizedLine.back();
else if (tokenizedLine.front() == "NumberOfThreadsPerJob")
numberOfThreadsPerJob = stoi(tokenizedLine.back());
else if (tokenizedLine.front() == "SingleCompThread")
if (tokenizedLine.back() == "true")
singleCompThread = true;
else if (tokenizedLine.back() == "false")
singleCompThread = false;
else
{
cerr << "ERROR (in config file): The value passed to SingleCompThread may only be "
"'true' or 'false'."
<< endl;
exit(EXIT_FAILURE);
}
else if (tokenizedLine.front() == "OperatingSystem")
operatingSystem = tokenizedLine.back();
else if (tokenizedLine.front() == "Members")
{
char_separator sep(" ,;", "()", drop_empty_tokens);
tokenizer tokens(tokenizedLine.back(), sep);
string node_name;
for (bool begin_weight {false}; const auto& token : tokens)
{
if (token == "(")
{
begin_weight = true;
continue;
}
else if (token == ")")
{
node_name.clear();
begin_weight = false;
continue;
}
if (!begin_weight)
{
if (!node_name.empty())
{
auto weight = stod(token);
if (weight <= 0)
if (member_nodes.contains(node_name))
{
cerr << "ERROR (in config file): Misspecification of weights passed to Members option." << endl;
cerr << "ERROR (in config file): Node entered twice in specification "
"of cluster."
<< endl;
exit(EXIT_FAILURE);
}
member_nodes[node_name] = weight;
}
catch (const invalid_argument &)
{
cerr << "ERROR (in config file): Misspecification of weights passed to Members option." << endl;
exit(EXIT_FAILURE);
else
member_nodes[node_name] = 1.0;
}
}
if (!node_name.empty())
{
if (!member_nodes.contains(node_name))
member_nodes[node_name] = 1.0;
else
node_name = token;
}
else
try
{
cerr << "ERROR (in config file): Node entered twice in specification of cluster." << endl;
auto weight = stod(token);
if (weight <= 0)
{
cerr << "ERROR (in config file): Misspecification of weights passed to "
"Members option."
<< endl;
exit(EXIT_FAILURE);
}
member_nodes[node_name] = weight;
}
catch (const invalid_argument&)
{
cerr << "ERROR (in config file): Misspecification of weights passed to "
"Members option."
<< endl;
exit(EXIT_FAILURE);
}
}
}
else
{
cerr << "ERROR (in config file): Option " << tokenizedLine.front() << " is invalid." << endl;
exit(EXIT_FAILURE);
}
}
if (!node_name.empty())
{
if (!member_nodes.contains(node_name))
member_nodes[node_name] = 1.0;
else
{
cerr << "ERROR (in config file): Node entered twice in specification of "
"cluster."
<< endl;
exit(EXIT_FAILURE);
}
}
}
else
{
cerr << "ERROR (in config file): Option " << tokenizedLine.front() << " is invalid."
<< endl;
exit(EXIT_FAILURE);
}
}
}
if (!global_init_file.empty())
addHooksConfFileElement(global_init_file);
else if (!includepath.empty())
if (!includepath.empty())
addPathsConfFileElement(includepath);
else
addParallelConfFileElement(inNode, inCluster, member_nodes, name,
computerName, port, minCpuNbr, maxCpuNbr, userName,
password, remoteDrive, remoteDirectory,
addParallelConfFileElement(inNode, inCluster, member_nodes, name, computerName, port, minCpuNbr,
maxCpuNbr, userName, password, remoteDrive, remoteDirectory,
programPath, programConfig, matlabOctavePath, singleCompThread,
numberOfThreadsPerJob, operatingSystem);
......@@ -460,19 +452,7 @@ ConfigFile::getConfigFileInfo(const filesystem::path &config_file)
}
void
ConfigFile::addHooksConfFileElement(string global_init_file)
{
if (global_init_file.empty())
{
cerr << "ERROR: The global initialization file must be passed to the GlobalInitFile option." << endl;
exit(EXIT_FAILURE);
}
else
hooks.emplace_back(move(global_init_file));
}
void
ConfigFile::addPathsConfFileElement(vector<string> includepath)
Configuration::addPathsConfFileElement(vector<string> includepath)
{
if (includepath.empty())
{
......@@ -484,12 +464,15 @@ ConfigFile::addPathsConfFileElement(vector<string> includepath)
}
void
ConfigFile::addParallelConfFileElement(bool inNode, bool inCluster, const member_nodes_t &member_nodes, const string &name,
const string &computerName, const string &port, int minCpuNbr, int maxCpuNbr,
const string &userName, const string &password, const string &remoteDrive,
const string &remoteDirectory, const string &programPath, const string &programConfig,
const string &matlabOctavePath, bool singleCompThread, int numberOfThreadsPerJob,
const string &operatingSystem)
Configuration::addParallelConfFileElement(bool inNode, bool inCluster,
const member_nodes_t& member_nodes, const string& name,
const string& computerName, const string& port,
int minCpuNbr, int maxCpuNbr, const string& userName,
const string& password, const string& remoteDrive,
const string& remoteDirectory, const string& programPath,
const string& programConfig,
const string& matlabOctavePath, bool singleCompThread,
int numberOfThreadsPerJob, const string& operatingSystem)
{
//! ADD NODE
if (inNode)
......@@ -498,24 +481,22 @@ ConfigFile::addParallelConfFileElement(bool inNode, bool inCluster, const member
cerr << "Invalid option passed to [node]." << endl;
exit(EXIT_FAILURE);
}
else if (name.empty() || follower_nodes.contains(name))
{
cerr << "ERROR: Every node must be assigned a unique name." << endl;
exit(EXIT_FAILURE);
}
else
if (name.empty() || follower_nodes.contains(name))
{
cerr << "ERROR: Every node must be assigned a unique name." << endl;
exit(EXIT_FAILURE);
}
else
follower_nodes.try_emplace(name, computerName, port, minCpuNbr, maxCpuNbr, userName,
password, remoteDrive, remoteDirectory, programPath, programConfig,
matlabOctavePath, singleCompThread, numberOfThreadsPerJob,
operatingSystem);
follower_nodes.try_emplace(name, computerName, port, minCpuNbr, maxCpuNbr, userName, password,
remoteDrive, remoteDirectory, programPath, programConfig,
matlabOctavePath, singleCompThread, numberOfThreadsPerJob,
operatingSystem);
//! ADD CLUSTER
else if (inCluster)
{
if (minCpuNbr > 0 || maxCpuNbr > 0 || !userName.empty()
|| !password.empty() || !remoteDrive.empty() || !remoteDirectory.empty()
|| !programPath.empty() || !programConfig.empty()
|| !matlabOctavePath.empty() || !operatingSystem.empty())
if (minCpuNbr > 0 || maxCpuNbr > 0 || !userName.empty() || !password.empty()
|| !remoteDrive.empty() || !remoteDirectory.empty() || !programPath.empty()
|| !programConfig.empty() || !matlabOctavePath.empty() || !operatingSystem.empty())
{
cerr << "Invalid option passed to [cluster]." << endl;
exit(EXIT_FAILURE);
......@@ -535,18 +516,8 @@ ConfigFile::addParallelConfFileElement(bool inNode, bool inCluster, const member
}
void
ConfigFile::checkPass([[maybe_unused]] WarningConsolidation &warnings) const
Configuration::checkPass([[maybe_unused]] WarningConsolidation& warnings) const
{
for (bool global_init_file_declared{false};
const auto &hook : hooks)
for (const auto &mapit : hook.get_hooks())
if (mapit.first == "global_init_file")
if (exchange(global_init_file_declared, true))
{
cerr << "ERROR: Only one global initialization file may be provided." << endl;
exit(EXIT_FAILURE);
}
if (!parallel && !parallel_test)
return;
......@@ -557,10 +528,10 @@ ConfigFile::checkPass([[maybe_unused]] WarningConsolidation &warnings) const
exit(EXIT_FAILURE);
}
for (const auto &follower_node : follower_nodes)
for (const auto& follower_node : follower_nodes)
{
#if !defined(_WIN32) && !defined(__CYGWIN32__)
//For Linux/Mac, check that cpuNbr starts at 0
// For Linux/Mac, check that cpuNbr starts at 0
if (follower_node.second.minCpuNbr != 0)
warnings << "WARNING: On Unix-based operating systems, you cannot specify the CPU that is "
<< "used in parallel processing. This will be adjusted for you such that the "
......@@ -571,21 +542,24 @@ ConfigFile::checkPass([[maybe_unused]] WarningConsolidation &warnings) const
{
stoi(follower_node.second.port);
}
catch (const invalid_argument &)
catch (const invalid_argument&)
{
cerr << "ERROR (node " << follower_node.first << "): the port must be an integer." << endl;
cerr << "ERROR (node " << follower_node.first << "): the port must be an integer."
<< endl;
exit(EXIT_FAILURE);
}
if (follower_node.second.computerName == "localhost") // We are working locally
{
if (!follower_node.second.remoteDrive.empty())
{
cerr << "ERROR (node " << follower_node.first << "): the RemoteDrive option may not be passed for a local node." << endl;
cerr << "ERROR (node " << follower_node.first
<< "): the RemoteDrive option may not be passed for a local node." << endl;
exit(EXIT_FAILURE);
}
if (!follower_node.second.remoteDirectory.empty())
{
cerr << "ERROR (node " << follower_node.first << "): the RemoteDirectory option may not be passed for a local node." << endl;
cerr << "ERROR (node " << follower_node.first
<< "): the RemoteDirectory option may not be passed for a local node." << endl;
exit(EXIT_FAILURE);
}
}
......@@ -593,19 +567,26 @@ ConfigFile::checkPass([[maybe_unused]] WarningConsolidation &warnings) const
{
if (follower_node.second.userName.empty())
{
cerr << "ERROR (node " << follower_node.first << "): the UserName option must be passed for every remote node." << endl;
cerr << "ERROR (node " << follower_node.first
<< "): the UserName option must be passed for every remote node." << endl;
exit(EXIT_FAILURE);
}
if (follower_node.second.operatingSystem == "windows")
{
if (follower_node.second.password.empty())
{
cerr << "ERROR (node " << follower_node.first << "): the Password option must be passed under Windows for every remote node." << endl;
cerr << "ERROR (node " << follower_node.first
<< "): the Password option must be passed under Windows for every remote "
"node."
<< endl;
exit(EXIT_FAILURE);
}
if (follower_node.second.remoteDrive.empty())
{
cerr << "ERROR (node " << follower_node.first << "): the RemoteDrive option must be passed under Windows for every remote node." << endl;
cerr << "ERROR (node " << follower_node.first
<< "): the RemoteDrive option must be passed under Windows for every remote "
"node."
<< endl;
exit(EXIT_FAILURE);
}
}
......@@ -614,19 +595,26 @@ ConfigFile::checkPass([[maybe_unused]] WarningConsolidation &warnings) const
{
if (follower_node.second.password.empty())
{
cerr << "ERROR (node " << follower_node.first << "): the Password option must be passed under Windows for every remote node." << endl;
cerr << "ERROR (node " << follower_node.first
<< "): the Password option must be passed under Windows for every remote "
"node."
<< endl;
exit(EXIT_FAILURE);
}
if (follower_node.second.remoteDrive.empty())
{
cerr << "ERROR (node " << follower_node.first << "): the RemoteDrive option must be passed under Windows for every remote node." << endl;
cerr << "ERROR (node " << follower_node.first
<< "): the RemoteDrive option must be passed under Windows for every remote "
"node."
<< endl;
exit(EXIT_FAILURE);
}
}
#endif
if (follower_node.second.remoteDirectory.empty())
{
cerr << "ERROR (node " << follower_node.first << "): the RemoteDirectory must be specified for every remote node." << endl;
cerr << "ERROR (node " << follower_node.first
<< "): the RemoteDirectory must be specified for every remote node." << endl;
exit(EXIT_FAILURE);
}
}
......@@ -641,28 +629,36 @@ ConfigFile::checkPass([[maybe_unused]] WarningConsolidation &warnings) const
if (!cluster_name.empty() && !clusters.contains(cluster_name))
{
cerr << "ERROR: Cluster Name " << cluster_name << " was not found in the config file." << endl;
cerr << "ERROR: Cluster Name " << cluster_name << " was not found in the config file."
<< endl;
exit(EXIT_FAILURE);
}
for (const auto &cluster : clusters)
for (const auto &itmn : cluster.second.member_nodes)
for (const auto& cluster : clusters)
for (const auto& itmn : cluster.second.member_nodes)
if (!follower_nodes.contains(itmn.first))
{
cerr << "Error: node " << itmn.first << " specified in cluster " << cluster.first << " was not found" << endl;
cerr << "Error: node " << itmn.first << " specified in cluster " << cluster.first
<< " was not found" << endl;
exit(EXIT_FAILURE);
}
}
void
ConfigFile::transformPass()
Configuration::transformPass()
{
/* If the user did not specify the GlobalInitFile option, use global_init.m in configuration
directory if it exists */
if (auto default_global_init_file = findConfigFile("global_init.m");
global_init_file.empty() && !default_global_init_file.empty())
global_init_file = default_global_init_file.string();
if (!parallel && !parallel_test)
return;
#if !defined(_WIN32) && !defined(__CYGWIN32__)
//For Linux/Mac, check that cpuNbr starts at 0
for (auto &it : follower_nodes)
// For Linux/Mac, check that cpuNbr starts at 0
for (auto& it : follower_nodes)
if (it.second.minCpuNbr != 0)
{
it.second.maxCpuNbr = it.second.maxCpuNbr - it.second.minCpuNbr;
......@@ -670,86 +666,73 @@ ConfigFile::transformPass()
}
#endif
auto cluster_it = cluster_name.empty() ? clusters.find(firstClusterName) : clusters.find(cluster_name);
auto& cluster = cluster_name.empty() ? clusters.at(firstClusterName) : clusters.at(cluster_name);
double weight_denominator{0.0};
for (const auto &it : cluster_it->second.member_nodes)
weight_denominator += it.second;
double weight_denominator {0.0};
for (const auto& [name, weight] : cluster.member_nodes)
weight_denominator += weight;
for (auto &member_node : cluster_it->second.member_nodes)
member_node.second /= weight_denominator;
for (auto& [name, weight] : cluster.member_nodes)
weight /= weight_denominator;
}
vector<filesystem::path>
ConfigFile::getIncludePaths() const
Configuration::getIncludePaths() const
{
vector<filesystem::path> include_paths;
for (auto path : paths)
for (const auto &mapit : path.get_paths())
for (const auto &vecit : mapit.second)
for (const auto& path : paths)
for (const auto& mapit : path.get_paths())
for (const auto& vecit : mapit.second)
include_paths.emplace_back(vecit);
return include_paths;
}
void
ConfigFile::writeHooks(ostream &output) const
Configuration::writeHooks(ostream& output) const
{
for (auto hook : hooks)
for (const auto &mapit : hook.get_hooks())
output << "options_." << mapit.first << " = '" << mapit.second << "';" << endl;
if (!global_init_file.empty())
output << "options_.global_init_file = '" << global_init_file << "';" << endl;
}
void
ConfigFile::writeCluster(ostream &output) const
Configuration::writeCluster(ostream& output) const
{
if (!parallel && !parallel_test)
return;
auto cluster_it = cluster_name.empty() ? clusters.find(firstClusterName) : clusters.find(cluster_name);
const auto& cluster
= cluster_name.empty() ? clusters.at(firstClusterName) : clusters.at(cluster_name);
for (int i{1};
const auto &follower_node : follower_nodes)
for (int i {1}; const auto& [name, node] : follower_nodes)
{
bool follower_node_in_member_nodes = false;
for (const auto &itmn : cluster_it->second.member_nodes)
if (follower_node.first == itmn.first)
follower_node_in_member_nodes = true;
if (!follower_node_in_member_nodes)
continue;
if (!cluster.member_nodes.contains(name))
continue; // Skip nodes not in the selected cluster
output << "options_.parallel";
if (i > 1)
output << "(" << i << ")";
i++;
output << " = struct('Local', ";
if (follower_node.second.computerName == "localhost")
output << "1, ";
else
output << "0, ";
output << "'ComputerName', '" << follower_node.second.computerName << "', "
<< "'Port', '" << follower_node.second.port << "', "
<< "'CPUnbr', [" << follower_node.second.minCpuNbr << ":" << follower_node.second.maxCpuNbr << "], "
<< "'UserName', '" << follower_node.second.userName << "', "
<< "'Password', '" << follower_node.second.password << "', "
<< "'RemoteDrive', '" << follower_node.second.remoteDrive << "', "
<< "'RemoteDirectory', '" << follower_node.second.remoteDirectory << "', "
// The following should be switched back to “ProgramPath” once we move to Dragonfly
<< "'DynarePath', '" << follower_node.second.programPath << "', "
<< "'ProgramConfig', '" << follower_node.second.programConfig << "', "
<< "'MatlabOctavePath', '" << follower_node.second.matlabOctavePath << "', "
<< "'OperatingSystem', '" << follower_node.second.operatingSystem << "', "
<< "'NodeWeight', '" << cluster_it->second.member_nodes.at(follower_node.first) << "', "
<< "'NumberOfThreadsPerJob', " << follower_node.second.numberOfThreadsPerJob << ", ";
if (follower_node.second.singleCompThread)
output << "'SingleCompThread', 'true');" << endl;
else
output << "'SingleCompThread', 'false');" << endl;
output << " = struct('Local', " << noboolalpha << (node.computerName == "localhost") << ", "
<< "'ComputerName', '" << node.computerName << "', "
<< "'Port', '" << node.port << "', "
<< "'CPUnbr', [" << node.minCpuNbr << ":" << node.maxCpuNbr << "], "
<< "'UserName', '" << node.userName << "', "
<< "'Password', '" << node.password << "', "
<< "'RemoteDrive', '" << node.remoteDrive << "', "
<< "'RemoteDirectory', '" << node.remoteDirectory
<< "', "
// The following should be switched back to “ProgramPath” once we move to Dragonfly
<< "'DynarePath', '" << node.programPath << "', "
<< "'ProgramConfig', '" << node.programConfig << "', "
<< "'MatlabOctavePath', '" << node.matlabOctavePath << "', "
<< "'OperatingSystem', '" << node.operatingSystem << "', "
<< "'NodeWeight', '" << cluster.member_nodes.at(name) << "', "
<< "'NumberOfThreadsPerJob', " << node.numberOfThreadsPerJob << ", "
<< "'SingleCompThread', '" << boolalpha << node.singleCompThread << "');" << endl;
}
// Default values for the following two are both in DynareMain.cc and matlab/default_option_values.m
// Default values for the following two are both in DynareMain.cc and
// matlab/default_option_values.m
if (parallel_follower_open_mode)
output << "options_.parallel_info.leaveSlaveOpen = 1;" << endl;
if (!parallel_use_psexec)
......@@ -759,14 +742,18 @@ ConfigFile::writeCluster(ostream &output) const
output << "InitializeComputationalEnvironment();" << endl;
if (parallel_test)
output << "ErrorCode = AnalyseComputationalEnvironment(options_.parallel, options_.parallel_info);" << endl
<< "disp(['AnalyseComputationalEnvironment returned with Error Code: ' num2str(ErrorCode)]);" << endl
<< "diary off;" << endl
<< "return;" << endl;
output
<< "ErrorCode = AnalyseComputationalEnvironment(options_.parallel, options_.parallel_info);"
<< endl
<< "disp(['AnalyseComputationalEnvironment returned with Error Code: ' "
"num2str(ErrorCode)]);"
<< endl
<< "diary off;" << endl
<< "return;" << endl;
}
void
ConfigFile::writeEndParallel(ostream &output) const
Configuration::writeEndParallel(ostream& output) const
{
if ((!parallel && !parallel_test) || !parallel_follower_open_mode)
return;
......@@ -775,3 +762,46 @@ ConfigFile::writeEndParallel(ostream &output) const
<< " closeSlave(options_.parallel,options_.parallel_info.RemoteTmpFolder);" << endl
<< "end" << endl;
}
filesystem::path
Configuration::findConfigFile(const string& filename)
{
#ifdef _WIN32
array<wchar_t, MAX_PATH + 1> appdata;
if (SHGetFolderPathW(nullptr, CSIDL_APPDATA | CSIDL_FLAG_DONT_VERIFY, nullptr, SHGFP_TYPE_CURRENT,
appdata.data())
== S_OK)
{
filesystem::path candidate {filesystem::path {appdata.data()} / "dynare" / filename};
if (exists(candidate))
return candidate;
}
#else
filesystem::path xdg_config_home;
if (auto xdg_config_home_env = getenv("XDG_CONFIG_HOME"); xdg_config_home_env)
xdg_config_home = xdg_config_home_env;
if (auto home = getenv("HOME"); xdg_config_home.empty() && home)
xdg_config_home = filesystem::path {home} / ".config";
if (!xdg_config_home.empty())
{
filesystem::path candidate {xdg_config_home / "dynare" / filename};
if (exists(candidate))
return candidate;
}
string xdg_config_dirs;
if (auto xdg_config_dirs_env = getenv("XDG_CONFIG_DIRS"); xdg_config_dirs_env)
xdg_config_dirs = xdg_config_dirs_env;
if (xdg_config_dirs.empty())
xdg_config_dirs = "/etc/xdg";
for (const auto& dir : DataTree::strsplit(xdg_config_dirs, ':'))
{
filesystem::path candidate {filesystem::path {dir} / "dynare" / filename};
if (exists(candidate))
return candidate;
}
#endif
return {};
}
......@@ -17,122 +17,110 @@
* along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef _CONFIG_FILE_HH
#define _CONFIG_FILE_HH
#ifndef CONFIGURATION_HH
#define CONFIGURATION_HH
#include <filesystem>
#include <map>
#include <vector>
#include <filesystem>
#include "WarningConsolidation.hh"
using namespace std;
using member_nodes_t = map<string, double>;
class Hook
/* The abstract representation of the configuration.
Merges information from the command-line and from the configuration file. */
class Configuration
{
public:
explicit Hook(string global_init_file_arg);
Configuration(bool parallel_arg, bool parallel_test_arg, bool parallel_follower_open_mode_arg,
bool parallel_use_psexec_arg, string cluster_name);
private:
map<string, string> hooks;
public:
map<string, string>
get_hooks() const
using member_nodes_t = map<string, double>;
class Path
{
return hooks;
public:
explicit Path(vector<string> includepath_arg);
[[nodiscard]] map<string, vector<string>>
get_paths() const
{
return paths;
}
private:
map<string, vector<string>> paths;
};
};
class Path
{
public:
explicit Path(vector<string> includepath_arg);
private:
map<string, vector<string>> paths;
public:
map<string, vector<string>>
get_paths() const
struct FollowerNode
{
return paths;
FollowerNode(string computerName_arg, string port_arg, int minCpuNbr_arg, int maxCpuNbr_arg,
string userName_arg, string password_arg, string remoteDrive_arg,
string remoteDirectory_arg, string programPath_arg, string programConfig_arg,
string matlabOctavePath_arg, bool singleCompThread_arg,
int numberOfThreadsPerJob_arg, string operatingSystem_arg);
const string computerName, port;
int minCpuNbr, maxCpuNbr;
const string userName, password;
const string remoteDrive, remoteDirectory;
const string programPath, programConfig, matlabOctavePath;
const bool singleCompThread;
const int numberOfThreadsPerJob;
const string operatingSystem;
};
};
class FollowerNode
{
friend class ConfigFile;
public:
FollowerNode(string computerName_arg, string port_arg, int minCpuNbr_arg, int maxCpuNbr_arg, string userName_arg,
string password_arg, string remoteDrive_arg, string remoteDirectory_arg,
string programPath_arg, string programConfig_arg, string matlabOctavePath_arg, bool singleCompThread_arg,
int numberOfThreadsPerJob_arg, string operatingSystem_arg);
protected:
const string computerName, port;
int minCpuNbr, maxCpuNbr;
const string userName, password;
const string remoteDrive, remoteDirectory;
const string programPath, programConfig, matlabOctavePath;
const bool singleCompThread;
const int numberOfThreadsPerJob;
const string operatingSystem;
};
class Cluster
{
friend class ConfigFile;
public:
explicit Cluster(member_nodes_t member_nodes_arg);
protected:
member_nodes_t member_nodes;
};
//! The abstract representation of a "config" file
class ConfigFile
{
public:
ConfigFile(bool parallel_arg, bool parallel_test_arg, bool parallel_follower_open_mode_arg,
bool parallel_use_psexec_arg, string cluster_name);
struct Cluster
{
explicit Cluster(member_nodes_t member_nodes_arg);
member_nodes_t member_nodes;
};
private:
const bool parallel, parallel_test, parallel_follower_open_mode, parallel_use_psexec;
const string cluster_name;
string firstClusterName;
//! Hooks
vector<Hook> hooks;
string global_init_file;
//! Paths
vector<Path> paths;
//! Cluster Table
map<string, Cluster> clusters;
//! Node Map
map<string, FollowerNode> follower_nodes;
//! Add Hooks
void addHooksConfFileElement(string global_init_file);
//! Add Paths
void addPathsConfFileElement(vector<string> includepath);
//! Add a FollowerNode or a Cluster object
void addParallelConfFileElement(bool inNode, bool inCluster, const member_nodes_t &member_nodes, const string &name,
const string &computerName, const string &port, int minCpuNbr, int maxCpuNbr,
const string &userName, const string &password, const string &remoteDrive,
const string &remoteDirectory, const string &programPath, const string &programConfig,
const string &matlabOctavePath, bool singleCompThread, int numberOfThreadsPerJob,
const string &operatingSystem);
void addParallelConfFileElement(bool inNode, bool inCluster, const member_nodes_t& member_nodes,
const string& name, const string& computerName,
const string& port, int minCpuNbr, int maxCpuNbr,
const string& userName, const string& password,
const string& remoteDrive, const string& remoteDirectory,
const string& programPath, const string& programConfig,
const string& matlabOctavePath, bool singleCompThread,
int numberOfThreadsPerJob, const string& operatingSystem);
/* Given a filename (e.g. dynare.ini), looks for it in the configuration directory:
– if under Linux or macOS, look into the “dynare” subdirectory of the XDG
configuration directories (following the default values and the precedence order specified in
the XDG specification)
– if under Windows, look into %APPDATA%\dynare\
The returned path will be empty if the file is not found. */
[[nodiscard]] static filesystem::path findConfigFile(const string& filename);
public:
//! Parse config file
void getConfigFileInfo(const filesystem::path &parallel_config_file);
void getConfigFileInfo(const filesystem::path& conffile_option, WarningConsolidation& warnings);
//! Check Pass
void checkPass(WarningConsolidation &warnings) const;
void checkPass(WarningConsolidation& warnings) const;
//! Check Pass
void transformPass();
//! Get Path Info
vector<filesystem::path> getIncludePaths() const;
[[nodiscard]] vector<filesystem::path> getIncludePaths() const;
//! Write any hooks
void writeHooks(ostream &output) const;
void writeHooks(ostream& output) const;
//! Create options_.parallel structure, write options
void writeCluster(ostream &output) const;
void writeCluster(ostream& output) const;
//! Close follower nodes if needed
void writeEndParallel(ostream &output) const;
void writeEndParallel(ostream& output) const;
};
#endif // ! CONFIG_FILE_HH
#endif
/*
* Copyright © 2003-2023 Dynare Team
* Copyright © 2003-2024 Dynare Team
*
* This file is part of Dynare.
*
......@@ -17,13 +17,14 @@
* along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/
#include <cstdlib>
#include <cassert>
#include <iostream>
#include <algorithm>
#include <iterator>
#include <cassert>
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <iterator>
#include <ranges>
#include "DataTree.hh"
......@@ -46,43 +47,46 @@ DataTree::initConstants()
Pi = AddNonNegativeConstant("3.141592653589793");
}
DataTree::DataTree(SymbolTable &symbol_table_arg,
NumericalConstants &num_constants_arg,
ExternalFunctionsTable &external_functions_table_arg,
bool is_dynamic_arg) :
symbol_table{symbol_table_arg},
num_constants{num_constants_arg},
external_functions_table{external_functions_table_arg},
is_dynamic{is_dynamic_arg}
DataTree::DataTree(SymbolTable& symbol_table_arg, NumericalConstants& num_constants_arg,
ExternalFunctionsTable& external_functions_table_arg,
HeterogeneityTable& heterogeneity_table_arg, bool is_dynamic_arg) :
symbol_table {symbol_table_arg},
num_constants {num_constants_arg},
external_functions_table {external_functions_table_arg},
heterogeneity_table {heterogeneity_table_arg},
is_dynamic {is_dynamic_arg}
{
initConstants();
}
DataTree::DataTree(const DataTree &d) :
symbol_table{d.symbol_table},
num_constants{d.num_constants},
external_functions_table{d.external_functions_table},
is_dynamic{d.is_dynamic},
local_variables_vector{d.local_variables_vector}
DataTree::DataTree(const DataTree& d) :
symbol_table {d.symbol_table},
num_constants {d.num_constants},
external_functions_table {d.external_functions_table},
heterogeneity_table {d.heterogeneity_table},
is_dynamic {d.is_dynamic},
local_variables_vector {d.local_variables_vector}
{
// Constants must be initialized first because they are used in some Add* methods
initConstants();
for (const auto &it : d.node_list)
// See commment in DataTree::operator=() for the rationale
for (int symb_id : d.local_variables_vector)
local_variables_table[symb_id] = d.local_variables_table.at(symb_id)->clone(*this);
for (const auto& it : d.node_list)
it->clone(*this);
assert(node_list.size() == d.node_list.size());
for (const auto &[symb_id, value] : d.local_variables_table)
local_variables_table[symb_id] = value->clone(*this);
}
DataTree &
DataTree::operator=(const DataTree &d)
DataTree&
DataTree::operator=(const DataTree& d)
{
assert(&symbol_table == &d.symbol_table);
assert(&num_constants == &d.num_constants);
assert(&external_functions_table == &d.external_functions_table);
assert(&heterogeneity_table == &d.heterogeneity_table);
assert(is_dynamic == d.is_dynamic);
num_const_node_map.clear();
......@@ -110,7 +114,7 @@ DataTree::operator=(const DataTree &d)
for (int symb_id : d.local_variables_vector)
local_variables_table[symb_id] = d.local_variables_table.at(symb_id)->clone(*this);
for (const auto &it : d.node_list)
for (const auto& it : d.node_list)
it->clone(*this);
assert(node_list.size() == d.node_list.size());
......@@ -120,13 +124,12 @@ DataTree::operator=(const DataTree &d)
return *this;
}
NumConstNode *
DataTree::AddNonNegativeConstant(const string &value)
NumConstNode*
DataTree::AddNonNegativeConstant(const string& value)
{
int id = num_constants.AddNonNegativeConstant(value);
if (auto it = num_const_node_map.find(id);
it != num_const_node_map.end())
if (auto it = num_const_node_map.find(id); it != num_const_node_map.end())
return it->second;
auto sp = make_unique<NumConstNode>(*this, node_list.size(), id);
......@@ -136,7 +139,7 @@ DataTree::AddNonNegativeConstant(const string &value)
return p;
}
VariableNode *
VariableNode*
DataTree::AddVariable(int symb_id, int lag)
{
if (lag != 0 && !is_dynamic)
......@@ -145,24 +148,24 @@ DataTree::AddVariable(int symb_id, int lag)
exit(EXIT_FAILURE);
}
if (auto it = variable_node_map.find({ symb_id, lag });
it != variable_node_map.end())
if (auto it = variable_node_map.find({symb_id, lag}); it != variable_node_map.end())
return it->second;
auto sp = make_unique<VariableNode>(*this, node_list.size(), symb_id, lag);
auto p = sp.get();
node_list.push_back(move(sp));
variable_node_map.try_emplace({ symb_id, lag }, p);
variable_node_map.try_emplace({symb_id, lag}, p);
return p;
}
VariableNode *
VariableNode*
DataTree::getVariable(int symb_id, int lag) const
{
auto it = variable_node_map.find({ symb_id, lag });
auto it = variable_node_map.find({symb_id, lag});
if (it == variable_node_map.end())
{
cerr << "DataTree::getVariable: unknown variable node for symb_id=" << symb_id << " and lag=" << lag << endl;
cerr << "DataTree::getVariable: unknown variable node for symb_id=" << symb_id
<< " and lag=" << lag << endl;
exit(EXIT_FAILURE);
}
return it->second;
......@@ -171,7 +174,7 @@ DataTree::getVariable(int symb_id, int lag) const
bool
DataTree::ParamUsedWithLeadLagInternal() const
{
for (const auto &[symb_lag, expr] : variable_node_map)
for (const auto& [symb_lag, expr] : variable_node_map)
if (symbol_table.getType(symb_lag.first) == SymbolType::parameter && symb_lag.second != 0)
return true;
return false;
......@@ -187,28 +190,28 @@ DataTree::AddPlus(expr_t iArg1, expr_t iArg2)
return iArg2;
// Simplify x+(-y) in x-y
if (auto uarg2 = dynamic_cast<UnaryOpNode *>(iArg2);
if (auto uarg2 = dynamic_cast<UnaryOpNode*>(iArg2);
uarg2 && uarg2->op_code == UnaryOpcode::uminus)
return AddMinus(iArg1, uarg2->arg);
// Simplify (-x)+y in y-x
if (auto uarg1 = dynamic_cast<UnaryOpNode *>(iArg1);
if (auto uarg1 = dynamic_cast<UnaryOpNode*>(iArg1);
uarg1 && uarg1->op_code == UnaryOpcode::uminus)
return AddMinus(iArg2, uarg1->arg);
// Simplify (x-y)+y in x
if (auto barg1 = dynamic_cast<BinaryOpNode *>(iArg1);
if (auto barg1 = dynamic_cast<BinaryOpNode*>(iArg1);
barg1 && barg1->op_code == BinaryOpcode::minus && barg1->arg2 == iArg2)
return barg1->arg1;
// Simplify y+(x-y) in x
if (auto barg2 = dynamic_cast<BinaryOpNode *>(iArg2);
if (auto barg2 = dynamic_cast<BinaryOpNode*>(iArg2);
barg2 && barg2->op_code == BinaryOpcode::minus && barg2->arg2 == iArg1)
return barg2->arg1;
// To treat commutativity of "+"
// Nodes iArg1 and iArg2 are sorted by index
if (iArg1->idx > iArg2->idx && !no_commutativity)
if (iArg1->idx > iArg2->idx && !no_commutativity) // NOLINT(clang-analyzer-core.NullDereference)
swap(iArg1, iArg2);
return AddBinaryOp(iArg1, BinaryOpcode::plus, iArg2);
}
......@@ -226,12 +229,12 @@ DataTree::AddMinus(expr_t iArg1, expr_t iArg2)
return Zero;
// Simplify x-(-y) in x+y
if (auto uarg2 = dynamic_cast<UnaryOpNode *>(iArg2);
if (auto uarg2 = dynamic_cast<UnaryOpNode*>(iArg2);
uarg2 && uarg2->op_code == UnaryOpcode::uminus)
return AddPlus(iArg1, uarg2->arg);
// Simplify (x+y)-y and (y+x)-y in x
if (auto barg1 = dynamic_cast<BinaryOpNode *>(iArg1);
if (auto barg1 = dynamic_cast<BinaryOpNode*>(iArg1);
barg1 && barg1->op_code == BinaryOpcode::plus)
{
if (barg1->arg2 == iArg2)
......@@ -250,8 +253,7 @@ DataTree::AddUMinus(expr_t iArg1)
return Zero;
// Simplify -(-x) in x
if (auto uarg = dynamic_cast<UnaryOpNode *>(iArg1);
uarg && uarg->op_code == UnaryOpcode::uminus)
if (auto uarg = dynamic_cast<UnaryOpNode*>(iArg1); uarg && uarg->op_code == UnaryOpcode::uminus)
return uarg->arg;
return AddUnaryOp(UnaryOpcode::uminus, iArg1);
......@@ -276,18 +278,18 @@ DataTree::AddTimes(expr_t iArg1, expr_t iArg2)
return AddUMinus(iArg1);
// Simplify (x/y)*y in x
if (auto barg1 = dynamic_cast<BinaryOpNode *>(iArg1);
if (auto barg1 = dynamic_cast<BinaryOpNode*>(iArg1);
barg1 && barg1->op_code == BinaryOpcode::divide && barg1->arg2 == iArg2)
return barg1->arg1;
// Simplify y*(x/y) in x
if (auto barg2 = dynamic_cast<BinaryOpNode *>(iArg2);
if (auto barg2 = dynamic_cast<BinaryOpNode*>(iArg2);
barg2 && barg2->op_code == BinaryOpcode::divide && barg2->arg2 == iArg1)
return barg2->arg1;
// To treat commutativity of "*"
// Nodes iArg1 and iArg2 are sorted by index
if (iArg1->idx > iArg2->idx && !no_commutativity)
if (iArg1->idx > iArg2->idx && !no_commutativity) // NOLINT(clang-analyzer-core.NullDereference)
swap(iArg1, iArg2);
return AddBinaryOp(iArg1, BinaryOpcode::times, iArg2);
}
......@@ -312,12 +314,12 @@ DataTree::AddDivide(expr_t iArg1, expr_t iArg2) noexcept(false)
return One;
// Simplify x/(1/y) in x*y
if (auto barg2 = dynamic_cast<BinaryOpNode *>(iArg2);
if (auto barg2 = dynamic_cast<BinaryOpNode*>(iArg2);
barg2 && barg2->op_code == BinaryOpcode::divide && barg2->arg1 == One)
return AddTimes(iArg1, barg2->arg2);
// Simplify (x*y)/y and (y*x)/y in x
if (auto barg1 = dynamic_cast<BinaryOpNode *>(iArg1);
if (auto barg1 = dynamic_cast<BinaryOpNode*>(iArg1);
barg1 && barg1->op_code == BinaryOpcode::times)
{
if (barg1->arg2 == iArg2)
......@@ -401,7 +403,7 @@ DataTree::AddDiff(expr_t iArg1)
}
expr_t
DataTree::AddAdl(expr_t iArg1, const string &name, const vector<int> &lags)
DataTree::AddAdl(expr_t iArg1, const string& name, const vector<int>& lags)
{
return AddUnaryOp(UnaryOpcode::adl, iArg1, 0, 0, 0, name, lags);
}
......@@ -428,7 +430,7 @@ DataTree::AddLog(expr_t iArg1)
}
// Simplify log(1/x) in −log(x)
if (auto barg1 = dynamic_cast<BinaryOpNode *>(iArg1);
if (auto barg1 = dynamic_cast<BinaryOpNode*>(iArg1);
barg1 && barg1->op_code == BinaryOpcode::divide && barg1->arg1 == One)
return AddUMinus(AddLog(barg1->arg2));
......@@ -448,7 +450,7 @@ DataTree::AddLog10(expr_t iArg1)
}
// Simplify log₁₀(1/x) in −log₁₀(x)
if (auto barg1 = dynamic_cast<BinaryOpNode *>(iArg1);
if (auto barg1 = dynamic_cast<BinaryOpNode*>(iArg1);
barg1 && barg1->op_code == BinaryOpcode::divide && barg1->arg1 == One)
return AddUMinus(AddLog10(barg1->arg2));
......@@ -668,7 +670,8 @@ DataTree::AddSteadyStateParamDeriv(expr_t iArg1, int param_symb_id)
expr_t
DataTree::AddSteadyStateParam2ndDeriv(expr_t iArg1, int param1_symb_id, int param2_symb_id)
{
return AddUnaryOp(UnaryOpcode::steadyStateParam2ndDeriv, iArg1, 0, param1_symb_id, param2_symb_id);
return AddUnaryOp(UnaryOpcode::steadyStateParam2ndDeriv, iArg1, 0, param1_symb_id,
param2_symb_id);
}
expr_t
......@@ -678,10 +681,9 @@ DataTree::AddExpectation(int iArg1, expr_t iArg2)
}
expr_t
DataTree::AddVarExpectation(const string &model_name)
DataTree::AddVarExpectation(const string& model_name)
{
if (auto it = var_expectation_node_map.find(model_name);
it != var_expectation_node_map.end())
if (auto it = var_expectation_node_map.find(model_name); it != var_expectation_node_map.end())
return it->second;
auto sp = make_unique<VarExpectationNode>(*this, node_list.size(), model_name);
......@@ -692,10 +694,9 @@ DataTree::AddVarExpectation(const string &model_name)
}
expr_t
DataTree::AddPacExpectation(const string &model_name)
DataTree::AddPacExpectation(const string& model_name)
{
if (auto it = pac_expectation_node_map.find(model_name);
it != pac_expectation_node_map.end())
if (auto it = pac_expectation_node_map.find(model_name); it != pac_expectation_node_map.end())
return it->second;
auto sp = make_unique<PacExpectationNode>(*this, node_list.size(), model_name);
......@@ -706,7 +707,7 @@ DataTree::AddPacExpectation(const string &model_name)
}
expr_t
DataTree::AddPacTargetNonstationary(const string &model_name)
DataTree::AddPacTargetNonstationary(const string& model_name)
{
if (auto it = pac_target_nonstationary_node_map.find(model_name);
it != pac_target_nonstationary_node_map.end())
......@@ -719,12 +720,12 @@ DataTree::AddPacTargetNonstationary(const string &model_name)
return p;
}
BinaryOpNode *
BinaryOpNode*
DataTree::AddEqual(expr_t iArg1, expr_t iArg2)
{
/* We know that we can safely cast to BinaryOpNode because
BinaryOpCode::equal can never be reduced to a constant. */
return dynamic_cast<BinaryOpNode *>(AddBinaryOp(iArg1, BinaryOpcode::equal, iArg2));
return dynamic_cast<BinaryOpNode*>(AddBinaryOp(iArg1, BinaryOpcode::equal, iArg2));
}
void
......@@ -734,67 +735,80 @@ DataTree::AddLocalVariable(int symb_id, expr_t value) noexcept(false)
// Throw an exception if symbol already declared
if (local_variables_table.contains(symb_id))
throw LocalVariableException{symbol_table.getName(symb_id)};
throw LocalVariableException {symbol_table.getName(symb_id)};
local_variables_table.emplace(symb_id, value);
local_variables_vector.push_back(symb_id);
}
expr_t
DataTree::AddExternalFunction(int symb_id, const vector<expr_t> &arguments)
DataTree::AddExternalFunction(int symb_id, const vector<expr_t>& arguments)
{
assert(symbol_table.getType(symb_id) == SymbolType::externalFunction);
if (auto it = external_function_node_map.find({ arguments, symb_id });
if (auto it = external_function_node_map.find({arguments, symb_id});
it != external_function_node_map.end())
return it->second;
auto sp = make_unique<ExternalFunctionNode>(*this, node_list.size(), symb_id, arguments);
auto p = sp.get();
node_list.push_back(move(sp));
external_function_node_map.try_emplace({ arguments, symb_id }, p);
external_function_node_map.try_emplace({arguments, symb_id}, p);
return p;
}
expr_t
DataTree::AddFirstDerivExternalFunction(int top_level_symb_id, const vector<expr_t> &arguments, int input_index)
DataTree::AddFirstDerivExternalFunction(int top_level_symb_id, const vector<expr_t>& arguments,
int input_index)
{
assert(symbol_table.getType(top_level_symb_id) == SymbolType::externalFunction);
if (auto it = first_deriv_external_function_node_map.find({ arguments, input_index, top_level_symb_id });
if (auto it
= first_deriv_external_function_node_map.find({arguments, input_index, top_level_symb_id});
it != first_deriv_external_function_node_map.end())
return it->second;
auto sp = make_unique<FirstDerivExternalFunctionNode>(*this, node_list.size(), top_level_symb_id, arguments, input_index);
auto sp = make_unique<FirstDerivExternalFunctionNode>(*this, node_list.size(), top_level_symb_id,
arguments, input_index);
auto p = sp.get();
node_list.push_back(move(sp));
first_deriv_external_function_node_map.try_emplace({ arguments, input_index, top_level_symb_id }, p);
first_deriv_external_function_node_map.try_emplace({arguments, input_index, top_level_symb_id},
p);
return p;
}
expr_t
DataTree::AddSecondDerivExternalFunction(int top_level_symb_id, const vector<expr_t> &arguments, int input_index1, int input_index2)
DataTree::AddSecondDerivExternalFunction(int top_level_symb_id, const vector<expr_t>& arguments,
int input_index1, int input_index2)
{
assert(symbol_table.getType(top_level_symb_id) == SymbolType::externalFunction);
if (auto it = second_deriv_external_function_node_map.find({ arguments, input_index1, input_index2,
top_level_symb_id });
it != second_deriv_external_function_node_map.end())
if (auto it = second_deriv_external_function_node_map.find(
{arguments, input_index1, input_index2, top_level_symb_id});
it != second_deriv_external_function_node_map.end())
return it->second;
auto sp = make_unique<SecondDerivExternalFunctionNode>(*this, node_list.size(), top_level_symb_id, arguments, input_index1, input_index2);
auto sp = make_unique<SecondDerivExternalFunctionNode>(*this, node_list.size(), top_level_symb_id,
arguments, input_index1, input_index2);
auto p = sp.get();
node_list.push_back(move(sp));
second_deriv_external_function_node_map.try_emplace({ arguments, input_index1, input_index2, top_level_symb_id }, p);
second_deriv_external_function_node_map.try_emplace(
{arguments, input_index1, input_index2, top_level_symb_id}, p);
return p;
}
expr_t
DataTree::AddSum(expr_t arg)
{
return AddUnaryOp(UnaryOpcode::sum, arg);
}
bool
DataTree::isSymbolUsed(int symb_id) const
{
for (const auto &[symb_lag, expr] : variable_node_map)
if (symb_lag.first == symb_id)
return true;
if (ranges::any_of(views::keys(variable_node_map),
[=](const auto& symb_lag) { return symb_lag.first == symb_id; }))
return true;
if (local_variables_table.contains(symb_id))
return true;
......@@ -833,25 +847,25 @@ DataTree::getTypeSpecificIDByDerivID([[maybe_unused]] int deriv_id) const
}
void
DataTree::addAllParamDerivId([[maybe_unused]] set<int> &deriv_id_set)
DataTree::addAllParamDerivId([[maybe_unused]] set<int>& deriv_id_set)
{
}
bool
DataTree::isUnaryOpUsed(UnaryOpcode opcode) const
{
return any_of(unary_op_node_map.begin(), unary_op_node_map.end(),
[=](const auto &it) { return get<1>(it.first) == opcode; });
return ranges::any_of(views::keys(unary_op_node_map),
[=](const auto& key) { return get<1>(key) == opcode; });
}
bool
DataTree::isUnaryOpUsedOnType(SymbolType type, UnaryOpcode opcode) const
{
set<int> var;
for (const auto &it : unary_op_node_map)
if (get<1>(it.first) == opcode)
for (const auto& [key, value] : unary_op_node_map)
if (get<1>(key) == opcode)
{
it.second->collectVariables(type, var);
value->collectVariables(type, var);
if (!var.empty())
return true;
}
......@@ -861,18 +875,18 @@ DataTree::isUnaryOpUsedOnType(SymbolType type, UnaryOpcode opcode) const
bool
DataTree::isBinaryOpUsed(BinaryOpcode opcode) const
{
return any_of(binary_op_node_map.begin(), binary_op_node_map.end(),
[=](const auto &it) { return get<2>(it.first) == opcode; });
return ranges::any_of(views::keys(binary_op_node_map),
[=](const auto& key) { return get<2>(key) == opcode; });
}
bool
DataTree::isBinaryOpUsedOnType(SymbolType type, BinaryOpcode opcode) const
{
set<int> var;
for (const auto &it : binary_op_node_map)
if (get<2>(it.first) == opcode)
for (const auto& [key, value] : binary_op_node_map)
if (get<2>(key) == opcode)
{
it.second->collectVariables(type, var);
value->collectVariables(type, var);
if (!var.empty())
return true;
}
......@@ -883,21 +897,23 @@ int
DataTree::minLagForSymbol(int symb_id) const
{
int r = 0;
for (const auto &[symb_lag, expr] : variable_node_map)
for (const auto& [symb_lag, expr] : variable_node_map)
if (symb_lag.first == symb_id)
r = min(r, symb_lag.second);
return r;
}
void
DataTree::writeCHelpersDefinition(ostream &output) const
DataTree::writeCHelpersDefinition(ostream& output) const
{
if (isBinaryOpUsed(BinaryOpcode::powerDeriv))
output << "// The k-th derivative of x^p" << endl
<< "inline double" << endl
<< "getPowerDeriv(double x, double p, int k)" << endl
<< "{" << endl
<< " if (fabs(x) < " << power_deriv_near_zero << " && p > 0 && k > p && fabs(p-nearbyint(p)) < " << power_deriv_near_zero << ')' << endl
<< " if (fabs(x) < " << power_deriv_near_zero
<< " && p > 0 && k > p && fabs(p-nearbyint(p)) < " << power_deriv_near_zero << ')'
<< endl
<< " return 0.0;" << endl
<< " else" << endl
<< " {" << endl
......@@ -917,7 +933,7 @@ DataTree::writeCHelpersDefinition(ostream &output) const
}
void
DataTree::writeCHelpersDeclaration(ostream &output) const
DataTree::writeCHelpersDeclaration(ostream& output) const
{
if (isBinaryOpUsed(BinaryOpcode::powerDeriv))
output << "extern inline double getPowerDeriv(double x, double p, int k);" << endl;
......@@ -932,44 +948,43 @@ DataTree::strsplit(string_view str, char delim)
while (true)
{
size_t idx {str.find(delim)};
if (auto sub {str.substr(0, idx)};
!sub.empty())
if (auto sub {str.substr(0, idx)}; !sub.empty())
result.emplace_back(sub);
if (idx == string_view::npos)
break;
str.remove_prefix(idx+1);
str.remove_prefix(idx + 1);
}
return result;
}
filesystem::path
DataTree::packageDir(string_view package)
DataTree::packageDir(const string_view& package)
{
filesystem::path d;
for (const auto &it : strsplit(move(package), '.'))
for (const auto& it : strsplit(package, '.'))
d /= "+" + it;
return d;
}
void
DataTree::writeToFileIfModified(stringstream &new_contents, const filesystem::path &filename)
DataTree::writeToFileIfModified(stringstream& new_contents, const filesystem::path& filename)
{
ifstream old_file{filename, ios::in | ios::binary};
ifstream old_file {filename, ios::in | ios::binary};
if (old_file.is_open()
&& equal(istreambuf_iterator<char>{old_file}, istreambuf_iterator<char>{},
istreambuf_iterator<char>{new_contents}, istreambuf_iterator<char>{}))
&& ranges::equal(istreambuf_iterator<char> {old_file}, istreambuf_iterator<char> {},
istreambuf_iterator<char> {new_contents}, istreambuf_iterator<char> {}))
return;
old_file.close();
new_contents.seekg(0);
ofstream new_file{filename, ios::out | ios::binary};
ofstream new_file {filename, ios::out | ios::binary};
if (!new_file.is_open())
{
cerr << "ERROR: Can't open file " << filename.string() << " for writing" << endl;
exit(EXIT_FAILURE);
}
copy(istreambuf_iterator<char>{new_contents}, istreambuf_iterator<char>{},
ostreambuf_iterator<char>{new_file});
ranges::copy(istreambuf_iterator<char> {new_contents}, istreambuf_iterator<char> {},
ostreambuf_iterator<char> {new_file});
new_file.close();
}
/*
* Copyright © 2003-2023 Dynare Team
* Copyright © 2003-2024 Dynare Team
*
* This file is part of Dynare.
*
......@@ -17,25 +17,26 @@
* along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef _DATATREE_HH
#define _DATATREE_HH
#ifndef DATA_TREE_HH
#define DATA_TREE_HH
#include <string>
#include <map>
#include <vector>
#include <sstream>
#include <iomanip>
#include <cmath>
#include <utility>
#include <memory>
#include <filesystem>
#include <iomanip>
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "SymbolTable.hh"
#include "NumericalConstants.hh"
#include "ExternalFunctionsTable.hh"
#include "ExprNode.hh"
#include "ExternalFunctionsTable.hh"
#include "HeterogeneityTable.hh"
#include "NumericalConstants.hh"
#include "SubModel.hh"
#include "SymbolTable.hh"
using namespace std;
......@@ -43,57 +44,65 @@ class DataTree
{
public:
//! A reference to the symbol table
SymbolTable &symbol_table;
SymbolTable& symbol_table;
//! Reference to numerical constants table
NumericalConstants &num_constants;
NumericalConstants& num_constants;
//! A reference to the external functions table
ExternalFunctionsTable &external_functions_table;
ExternalFunctionsTable& external_functions_table;
// A reference to the heterogeneity table
HeterogeneityTable& heterogeneity_table;
//! Is it possible to use leads/lags on variable nodes?
/* NB: This data member cannot be replaced by a virtual method, because this information is needed
in AddVariable(), which itself can be called from the copy constructor. */
const bool is_dynamic;
private:
//! num_constant_id -> NumConstNode
using num_const_node_map_t = map<int, NumConstNode *>;
using num_const_node_map_t = map<int, NumConstNode*>;
num_const_node_map_t num_const_node_map;
//! (symbol_id, lag) -> VariableNode
using variable_node_map_t = map<pair<int, int>, VariableNode *>;
using variable_node_map_t = map<pair<int, int>, VariableNode*>;
variable_node_map_t variable_node_map;
//! (arg, op_code, arg_exp_info_set, param1_symb_id, param2_symb_id, adl_param_name, adl_lags) -> UnaryOpNode
using unary_op_node_map_t = map<tuple<expr_t, UnaryOpcode, int, int, int, string, vector<int>>, UnaryOpNode *>;
//! (arg, op_code, arg_exp_info_set, param1_symb_id, param2_symb_id, adl_param_name, adl_lags) ->
//! UnaryOpNode
using unary_op_node_map_t
= map<tuple<expr_t, UnaryOpcode, int, int, int, string, vector<int>>, UnaryOpNode*>;
unary_op_node_map_t unary_op_node_map;
//! ( arg1, arg2, opCode, order of Power Derivative) -> BinaryOpNode
using binary_op_node_map_t = map<tuple<expr_t, expr_t, BinaryOpcode, int>, BinaryOpNode *>;
using binary_op_node_map_t = map<tuple<expr_t, expr_t, BinaryOpcode, int>, BinaryOpNode*>;
binary_op_node_map_t binary_op_node_map;
//! ( arg1, arg2, arg3, opCode) -> TrinaryOpNode
using trinary_op_node_map_t = map<tuple<expr_t, expr_t, expr_t, TrinaryOpcode>, TrinaryOpNode *>;
using trinary_op_node_map_t = map<tuple<expr_t, expr_t, expr_t, TrinaryOpcode>, TrinaryOpNode*>;
trinary_op_node_map_t trinary_op_node_map;
// (arguments, symb_id) -> ExternalFunctionNode
using external_function_node_map_t = map<pair<vector<expr_t>, int>, ExternalFunctionNode *>;
using external_function_node_map_t = map<pair<vector<expr_t>, int>, ExternalFunctionNode*>;
external_function_node_map_t external_function_node_map;
// (model_name, symb_id, forecast_horizon) -> VarExpectationNode
using var_expectation_node_map_t = map<string, VarExpectationNode *>;
using var_expectation_node_map_t = map<string, VarExpectationNode*>;
var_expectation_node_map_t var_expectation_node_map;
// model_name -> PacExpectationNode
using pac_expectation_node_map_t = map<string, PacExpectationNode *>;
using pac_expectation_node_map_t = map<string, PacExpectationNode*>;
pac_expectation_node_map_t pac_expectation_node_map;
// model_name -> PacTargetNonstationaryNode
using pac_target_nonstationary_node_map_t = map<string, PacTargetNonstationaryNode *>;
using pac_target_nonstationary_node_map_t = map<string, PacTargetNonstationaryNode*>;
pac_target_nonstationary_node_map_t pac_target_nonstationary_node_map;
// (arguments, deriv_idx, symb_id) -> FirstDerivExternalFunctionNode
using first_deriv_external_function_node_map_t = map<tuple<vector<expr_t>, int, int>, FirstDerivExternalFunctionNode *>;
using first_deriv_external_function_node_map_t
= map<tuple<vector<expr_t>, int, int>, FirstDerivExternalFunctionNode*>;
first_deriv_external_function_node_map_t first_deriv_external_function_node_map;
// (arguments, deriv_idx1, deriv_idx2, symb_id) -> SecondDerivExternalFunctionNode
using second_deriv_external_function_node_map_t = map<tuple<vector<expr_t>, int, int, int>, SecondDerivExternalFunctionNode *>;
using second_deriv_external_function_node_map_t
= map<tuple<vector<expr_t>, int, int, int>, SecondDerivExternalFunctionNode*>;
second_deriv_external_function_node_map_t second_deriv_external_function_node_map;
// Flag to disable simplifications related to commutativity of addition and multiplication
......@@ -102,40 +111,44 @@ private:
protected:
//! Stores local variables value (maps symbol ID to corresponding node)
map<int, expr_t> local_variables_table;
//! Stores the order of appearance of local variables in the model block. Needed following change in #563
//! Stores the order of appearance of local variables in the model block. Needed following change
//! in #563
vector<int> local_variables_vector;
//! Internal implementation of ParamUsedWithLeadLag()
bool ParamUsedWithLeadLagInternal() const;
[[nodiscard]] bool ParamUsedWithLeadLagInternal() const;
/* Writes the contents of “new_contents” to the file “filename”. However, if
the file already exists and would not be modified by this operation, then do
nothing. */
static void writeToFileIfModified(stringstream &new_contents, const filesystem::path &filename);
static void writeToFileIfModified(stringstream& new_contents, const filesystem::path& filename);
private:
constexpr static int constants_precision{16};
constexpr static int constants_precision {16};
//! The list of nodes
vector<unique_ptr<ExprNode>> node_list;
inline expr_t AddUnaryOp(UnaryOpcode op_code, expr_t arg, int arg_exp_info_set = 0, int param1_symb_id = 0, int param2_symb_id = 0, const string &adl_param_name = "", const vector<int> &adl_lags = vector<int>());
inline expr_t AddBinaryOp(expr_t arg1, BinaryOpcode op_code, expr_t arg2, int powerDerivOrder = 0);
inline expr_t AddUnaryOp(UnaryOpcode op_code, expr_t arg, int arg_exp_info_set = 0,
int param1_symb_id = 0, int param2_symb_id = 0,
const string& adl_param_name = "",
const vector<int>& adl_lags = vector<int>());
inline expr_t AddBinaryOp(expr_t arg1, BinaryOpcode op_code, expr_t arg2,
int powerDerivOrder = 0);
inline expr_t AddTrinaryOp(expr_t arg1, TrinaryOpcode op_code, expr_t arg2, expr_t arg3);
//! Initializes the predefined constants, used only from the constructors
void initConstants();
public:
DataTree(SymbolTable &symbol_table_arg,
NumericalConstants &num_constants_arg,
ExternalFunctionsTable &external_functions_table_arg,
bool is_static_args = false);
DataTree(SymbolTable& symbol_table_arg, NumericalConstants& num_constants_arg,
ExternalFunctionsTable& external_functions_table_arg,
HeterogeneityTable& heterogeneity_table_arg, bool is_dynamic_arg = false);
virtual ~DataTree() = default;
DataTree(const DataTree &d);
DataTree &operator=(const DataTree &d);
DataTree(const DataTree& d);
DataTree& operator=(const DataTree& d);
//! Some predefined constants
NumConstNode *Zero, *One, *Two, *Three, *NaN, *Infinity, *Pi;
......@@ -153,13 +166,13 @@ public:
inline expr_t AddPossiblyNegativeConstant(double val);
//! Adds a non-negative numerical constant (possibly Inf or NaN)
NumConstNode *AddNonNegativeConstant(const string &value);
NumConstNode* AddNonNegativeConstant(const string& value);
//! Adds a variable
VariableNode *AddVariable(int symb_id, int lag = 0);
VariableNode* AddVariable(int symb_id, int lag = 0);
//! Gets a variable
/*! Same as AddVariable, except that it fails if the variable node has not
already been created */
VariableNode *getVariable(int symb_id, int lag = 0) const;
[[nodiscard]] VariableNode* getVariable(int symb_id, int lag = 0) const;
//! Adds "arg1+arg2" to model tree
expr_t AddPlus(expr_t iArg1, expr_t iArg2);
//! Adds "arg1-arg2" to model tree
......@@ -191,7 +204,7 @@ public:
//! Adds "diff(arg)" to model tree
expr_t AddDiff(expr_t iArg1);
//! Adds "adl(arg1, name, lag/lags)" to model tree
expr_t AddAdl(expr_t iArg1, const string &name, const vector<int> &lags);
expr_t AddAdl(expr_t iArg1, const string& name, const vector<int>& lags);
//! Adds "exp(arg)" to model tree
expr_t AddExp(expr_t iArg1);
//! Adds "log(arg)" to model tree
......@@ -249,43 +262,49 @@ public:
//! Add 2nd derivative of steady state w.r.t. parameter to model tree
expr_t AddSteadyStateParam2ndDeriv(expr_t iArg1, int param1_symb_id, int param2_symb_id);
//! Adds "arg1=arg2" to model tree
BinaryOpNode *AddEqual(expr_t iArg1, expr_t iArg2);
BinaryOpNode* AddEqual(expr_t iArg1, expr_t iArg2);
//! Adds "var_expectation(model_name)" to model tree
expr_t AddVarExpectation(const string &model_name);
expr_t AddVarExpectation(const string& model_name);
//! Adds pac_expectation command to model tree
expr_t AddPacExpectation(const string &model_name);
expr_t AddPacExpectation(const string& model_name);
//! Adds a pac_target_nonstationary node to model tree
expr_t AddPacTargetNonstationary(const string &model_name);
expr_t AddPacTargetNonstationary(const string& model_name);
//! Adds a model local variable with its value
void AddLocalVariable(int symb_id, expr_t value) noexcept(false);
//! Adds an external function node
expr_t AddExternalFunction(int symb_id, const vector<expr_t> &arguments);
expr_t AddExternalFunction(int symb_id, const vector<expr_t>& arguments);
//! Adds an external function node for the first derivative of an external function
expr_t AddFirstDerivExternalFunction(int top_level_symb_id, const vector<expr_t> &arguments, int input_index);
expr_t AddFirstDerivExternalFunction(int top_level_symb_id, const vector<expr_t>& arguments,
int input_index);
//! Adds an external function node for the second derivative of an external function
expr_t AddSecondDerivExternalFunction(int top_level_symb_id, const vector<expr_t> &arguments, int input_index1, int input_index2);
expr_t AddSecondDerivExternalFunction(int top_level_symb_id, const vector<expr_t>& arguments,
int input_index1, int input_index2);
// Adds "SUM(arg)" to model tree
expr_t AddSum(expr_t arg);
//! Checks if a given symbol is used somewhere in the data tree
bool isSymbolUsed(int symb_id) const;
[[nodiscard]] bool isSymbolUsed(int symb_id) const;
//! Checks if a given unary op is used somewhere in the data tree
bool isUnaryOpUsed(UnaryOpcode opcode) const;
[[nodiscard]] bool isUnaryOpUsed(UnaryOpcode opcode) const;
//! Checks if a given unary op is used somewhere in the data tree on an endogenous variable
bool isUnaryOpUsedOnType(SymbolType type, UnaryOpcode opcode) const;
[[nodiscard]] bool isUnaryOpUsedOnType(SymbolType type, UnaryOpcode opcode) const;
//! Checks if a given binary op is used somewhere in the data tree
bool isBinaryOpUsed(BinaryOpcode opcode) const;
[[nodiscard]] bool isBinaryOpUsed(BinaryOpcode opcode) const;
//! Checks if a given binary op is used somewhere in the data tree on an endogenous variable
bool isBinaryOpUsedOnType(SymbolType type, BinaryOpcode opcode) const;
//! Returns the minimum lag (as a negative number) of the given symbol in the whole data tree (and not only in the equations !!)
[[nodiscard]] bool isBinaryOpUsedOnType(SymbolType type, BinaryOpcode opcode) const;
//! Returns the minimum lag (as a negative number) of the given symbol in the whole data tree (and
//! not only in the equations !!)
/*! Returns 0 if the symbol is not used */
int minLagForSymbol(int symb_id) const;
[[nodiscard]] int minLagForSymbol(int symb_id) const;
/* Writes definitions of C function helpers (getPowerDeriv(), sign()) as
inline functions */
void writeCHelpersDefinition(ostream &output) const;
void writeCHelpersDefinition(ostream& output) const;
/* Writes declarations of C function helpers (getPowerDeriv(), sign()) as
extern inline (external definition). Those need to be included in exactly
one translation unit. That external definition will be used or not,
depending on the optimization decision by the compiler.
See https://en.cppreference.com/w/c/language/inline */
void writeCHelpersDeclaration(ostream &output) const;
void writeCHelpersDeclaration(ostream& output) const;
//! Thrown when trying to access an unknown variable by deriv_id
class UnknownDerivIDException
{
......@@ -298,17 +317,17 @@ public:
};
// Returns the derivation ID, or throws an exception if the derivation ID does not exist
virtual int getDerivID(int symb_id, int lag) const noexcept(false);
[[nodiscard]] virtual int getDerivID(int symb_id, int lag) const noexcept(false);
// Get the type corresponding to a derivation ID
virtual SymbolType getTypeByDerivID(int deriv_id) const noexcept(false);
[[nodiscard]] virtual SymbolType getTypeByDerivID(int deriv_id) const noexcept(false);
// Get the lag corresponding to a derivation ID
virtual int getLagByDerivID(int deriv_id) const noexcept(false);
[[nodiscard]] virtual int getLagByDerivID(int deriv_id) const noexcept(false);
// Get the symbol ID corresponding to a derivation ID
virtual int getSymbIDByDerivID(int deriv_id) const noexcept(false);
[[nodiscard]] virtual int getSymbIDByDerivID(int deriv_id) const noexcept(false);
// Get the type-specific ID corresponding to a derivation ID
virtual int getTypeSpecificIDByDerivID(int deriv_id) const;
[[nodiscard]] virtual int getTypeSpecificIDByDerivID(int deriv_id) const;
// Get the symbol name corresponding to a derivation ID
string
[[nodiscard]] string
getNameByDerivID(int deriv_id) const
{
return symbol_table.getName(getSymbIDByDerivID(deriv_id));
......@@ -317,7 +336,7 @@ public:
/* Returns the column of the Jacobian associated to a derivation ID.
The “sparse” argument selects between the legacy representation and the
sparse representation. */
virtual int
[[nodiscard]] virtual int
getJacobianCol([[maybe_unused]] int deriv_id, [[maybe_unused]] bool sparse) const
{
throw UnknownDerivIDException();
......@@ -326,21 +345,14 @@ public:
/* Returns the number of columns of the Jacobian
The “sparse” argument selects between the legacy representation and the
sparse representation. */
virtual int
[[nodiscard]] virtual int
getJacobianColsNbr([[maybe_unused]] bool sparse) const
{
throw UnknownDerivIDException();
}
//! Adds to the set all the deriv IDs corresponding to parameters
virtual void addAllParamDerivId(set<int> &deriv_id_set);
//! Returns bool indicating whether DataTree represents a Dynamic Model (returns true in DynamicModel.hh)
virtual bool
isDynamic() const
{
return false;
};
virtual void addAllParamDerivId(set<int>& deriv_id_set);
struct UnknownLocalVariableException
{
......@@ -348,14 +360,20 @@ public:
int id;
};
expr_t
getLocalVariable(int symb_id) const
[[nodiscard]] expr_t
getLocalVariable(int symb_id, int lead_lag) const
{
auto it = local_variables_table.find(symb_id);
if (it == local_variables_table.end())
throw UnknownLocalVariableException{symb_id};
return it->second;
throw UnknownLocalVariableException {symb_id};
/* In the following, the case without lead/lag is optimized. It makes a difference on models
with many nested model-local variables, see e.g.
https://forum.dynare.org/t/pre-processing-takes-very-long/26865 */
if (lead_lag == 0)
return it->second;
else
return it->second->decreaseLeadsLags(-lead_lag);
}
static void
......@@ -373,7 +391,7 @@ public:
and returns the path to the corresponding filesystem directory.
In practice the package nesting is used for the planner_objective (stored
inside +objective subdir). */
static filesystem::path packageDir(string_view package);
static filesystem::path packageDir(const string_view& package);
};
inline expr_t
......@@ -384,7 +402,7 @@ DataTree::AddPossiblyNegativeConstant(double v)
if (isnan(v))
return NaN;
if (isinf(v))
return (v < 0 ? MinusInfinity : Infinity);
return v < 0 ? MinusInfinity : Infinity;
bool neg = false;
if (v < 0)
......@@ -404,66 +422,72 @@ DataTree::AddPossiblyNegativeConstant(double v)
}
inline expr_t
DataTree::AddUnaryOp(UnaryOpcode op_code, expr_t arg, int arg_exp_info_set, int param1_symb_id, int param2_symb_id, const string &adl_param_name, const vector<int> &adl_lags)
DataTree::AddUnaryOp(UnaryOpcode op_code, expr_t arg, int arg_exp_info_set, int param1_symb_id,
int param2_symb_id, const string& adl_param_name, const vector<int>& adl_lags)
{
// If the node already exists in tree, share it
if (auto it = unary_op_node_map.find({ arg, op_code, arg_exp_info_set, param1_symb_id, param2_symb_id, adl_param_name, adl_lags });
if (auto it = unary_op_node_map.find({arg, op_code, arg_exp_info_set, param1_symb_id,
param2_symb_id, adl_param_name, adl_lags});
it != unary_op_node_map.end())
return it->second;
// Try to reduce to a constant
// Case where arg is a constant and op_code == UnaryOpcode::uminus (i.e. we're adding a negative constant) is skipped
if (auto carg = dynamic_cast<NumConstNode *>(arg);
op_code != UnaryOpcode::uminus || !carg)
// Case where arg is a constant and op_code == UnaryOpcode::uminus (i.e. we're adding a negative
// constant) is skipped
if (auto carg = dynamic_cast<NumConstNode*>(arg); op_code != UnaryOpcode::uminus || !carg)
{
try
{
double argval = arg->eval({});
double argval = arg->eval({}); // NOLINT(clang-analyzer-core.CallAndMessage)
double val = UnaryOpNode::eval_opcode(op_code, argval);
return AddPossiblyNegativeConstant(val);
}
catch (ExprNode::EvalException &e)
catch (ExprNode::EvalException& e)
{
}
}
auto sp = make_unique<UnaryOpNode>(*this, node_list.size(), op_code, arg, arg_exp_info_set, param1_symb_id, param2_symb_id, adl_param_name, adl_lags);
auto sp = make_unique<UnaryOpNode>(*this, node_list.size(), op_code, arg, arg_exp_info_set,
param1_symb_id, param2_symb_id, adl_param_name, adl_lags);
auto p = sp.get();
node_list.push_back(move(sp));
unary_op_node_map.try_emplace({ arg, op_code, arg_exp_info_set, param1_symb_id, param2_symb_id, adl_param_name, adl_lags }, p);
unary_op_node_map.try_emplace(
{arg, op_code, arg_exp_info_set, param1_symb_id, param2_symb_id, adl_param_name, adl_lags},
p);
return p;
}
inline expr_t
DataTree::AddBinaryOp(expr_t arg1, BinaryOpcode op_code, expr_t arg2, int powerDerivOrder)
{
if (auto it = binary_op_node_map.find({ arg1, arg2, op_code, powerDerivOrder });
if (auto it = binary_op_node_map.find({arg1, arg2, op_code, powerDerivOrder});
it != binary_op_node_map.end())
return it->second;
// Try to reduce to a constant
try
{
double argval1 = arg1->eval({});
double argval2 = arg2->eval({});
double argval1 = arg1->eval({}); // NOLINT(clang-analyzer-core.CallAndMessage)
double argval2 = arg2->eval({}); // NOLINT(clang-analyzer-core.CallAndMessage)
double val = BinaryOpNode::eval_opcode(argval1, op_code, argval2, powerDerivOrder);
return AddPossiblyNegativeConstant(val);
}
catch (ExprNode::EvalException &e)
catch (ExprNode::EvalException& e)
{
}
auto sp = make_unique<BinaryOpNode>(*this, node_list.size(), arg1, op_code, arg2, powerDerivOrder);
auto sp
= make_unique<BinaryOpNode>(*this, node_list.size(), arg1, op_code, arg2, powerDerivOrder);
auto p = sp.get();
node_list.push_back(move(sp));
binary_op_node_map.try_emplace({ arg1, arg2, op_code, powerDerivOrder }, p);
binary_op_node_map.try_emplace({arg1, arg2, op_code, powerDerivOrder}, p);
return p;
}
inline expr_t
DataTree::AddTrinaryOp(expr_t arg1, TrinaryOpcode op_code, expr_t arg2, expr_t arg3)
{
if (auto it = trinary_op_node_map.find({ arg1, arg2, arg3, op_code });
if (auto it = trinary_op_node_map.find({arg1, arg2, arg3, op_code});
it != trinary_op_node_map.end())
return it->second;
......@@ -476,14 +500,14 @@ DataTree::AddTrinaryOp(expr_t arg1, TrinaryOpcode op_code, expr_t arg2, expr_t a
double val = TrinaryOpNode::eval_opcode(argval1, op_code, argval2, argval3);
return AddPossiblyNegativeConstant(val);
}
catch (ExprNode::EvalException &e)
catch (ExprNode::EvalException& e)
{
}
auto sp = make_unique<TrinaryOpNode>(*this, node_list.size(), arg1, op_code, arg2, arg3);
auto p = sp.get();
node_list.push_back(move(sp));
trinary_op_node_map.try_emplace({ arg1, arg2, arg3, op_code }, p);
trinary_op_node_map.try_emplace({arg1, arg2, arg3, op_code}, p);
return p;
}
......
/*
* Copyright © 2003-2023 Dynare Team
* Copyright © 2003-2025 Dynare Team
*
* This file is part of Dynare.
*
......@@ -17,12 +17,13 @@
* along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/
#include <iostream>
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdlib>
#include <cassert>
#include <algorithm>
#include <iostream>
#include <numeric>
#include <ranges>
#include <regex>
#include <sstream>
#include <string_view>
......@@ -31,70 +32,79 @@
#include "ParsingDriver.hh"
void
DynamicModel::copyHelper(const DynamicModel &m)
{
auto f = [this](const ExprNode *e) { return e->clone(*this); };
for (const auto &it : m.static_only_equations)
static_only_equations.push_back(dynamic_cast<BinaryOpNode *>(f(it)));
}
DynamicModel::DynamicModel(SymbolTable &symbol_table_arg,
NumericalConstants &num_constants_arg,
ExternalFunctionsTable &external_functions_table_arg,
TrendComponentModelTable &trend_component_model_table_arg,
VarModelTable &var_model_table_arg) :
ModelTree{symbol_table_arg, num_constants_arg, external_functions_table_arg, true},
trend_component_model_table{trend_component_model_table_arg},
var_model_table{var_model_table_arg}
{
}
DynamicModel::DynamicModel(const DynamicModel &m) :
ModelTree{m},
trend_component_model_table{m.trend_component_model_table},
var_model_table{m.var_model_table},
balanced_growth_test_tol{m.balanced_growth_test_tol},
static_only_equations_lineno{m.static_only_equations_lineno},
static_only_equations_equation_tags{m.static_only_equations_equation_tags},
deriv_id_table{m.deriv_id_table},
inv_deriv_id_table{m.inv_deriv_id_table},
dyn_jacobian_cols_table{m.dyn_jacobian_cols_table},
dyn_jacobian_ncols{m.dyn_jacobian_ncols},
max_lag{m.max_lag},
max_lead{m.max_lead},
max_endo_lag{m.max_endo_lag},
max_endo_lead{m.max_endo_lead},
max_exo_lag{m.max_exo_lag},
max_exo_lead{m.max_exo_lead},
max_exo_det_lag{m.max_exo_det_lag},
max_exo_det_lead{m.max_exo_det_lead},
max_lag_orig{m.max_lag_orig},
max_lead_orig{m.max_lead_orig},
max_lag_with_diffs_expanded_orig{m.max_lag_with_diffs_expanded_orig},
max_endo_lag_orig{m.max_endo_lag_orig},
max_endo_lead_orig{m.max_endo_lead_orig},
max_exo_lag_orig{m.max_exo_lag_orig},
max_exo_lead_orig{m.max_exo_lead_orig},
max_exo_det_lag_orig{m.max_exo_det_lag_orig},
max_exo_det_lead_orig{m.max_exo_det_lead_orig},
xrefs{m.xrefs},
xref_param{m.xref_param},
xref_endo{m.xref_endo},
xref_exo{m.xref_exo},
xref_exo_det{m.xref_exo_det},
nonzero_hessian_eqs{m.nonzero_hessian_eqs},
variableMapping{m.variableMapping},
blocks_jacob_cols_endo{m.blocks_jacob_cols_endo},
var_expectation_functions_to_write{m.var_expectation_functions_to_write},
mfs{m.mfs},
static_mfs{m.static_mfs}
DynamicModel::copyHelper(const DynamicModel& m)
{
auto f = [this](const ExprNode* e) { return e ? e->clone(*this) : nullptr; };
for (const auto& it : m.static_only_equations)
static_only_equations.push_back(dynamic_cast<BinaryOpNode*>(f(it)));
for (const auto& it : m.static_only_complementarity_conditions)
if (it)
{
const auto& [symb_id, lb, ub] = *it;
static_only_complementarity_conditions.emplace_back(in_place, symb_id, f(lb), f(ub));
}
else
static_only_complementarity_conditions.emplace_back(nullopt);
}
DynamicModel::DynamicModel(SymbolTable& symbol_table_arg, NumericalConstants& num_constants_arg,
ExternalFunctionsTable& external_functions_table_arg,
HeterogeneityTable& heterogeneity_table_arg,
TrendComponentModelTable& trend_component_model_table_arg,
VarModelTable& var_model_table_arg) :
ModelTree {symbol_table_arg, num_constants_arg, external_functions_table_arg,
heterogeneity_table_arg, true},
trend_component_model_table {trend_component_model_table_arg},
var_model_table {var_model_table_arg}
{
}
DynamicModel::DynamicModel(const DynamicModel& m) :
ModelTree {m},
trend_component_model_table {m.trend_component_model_table},
var_model_table {m.var_model_table},
balanced_growth_test_tol {m.balanced_growth_test_tol},
static_only_equations_lineno {m.static_only_equations_lineno},
static_only_equations_equation_tags {m.static_only_equations_equation_tags},
deriv_id_table {m.deriv_id_table},
inv_deriv_id_table {m.inv_deriv_id_table},
dyn_jacobian_cols_table {m.dyn_jacobian_cols_table},
dyn_jacobian_ncols {m.dyn_jacobian_ncols},
max_lag {m.max_lag},
max_lead {m.max_lead},
max_endo_lag {m.max_endo_lag},
max_endo_lead {m.max_endo_lead},
max_exo_lag {m.max_exo_lag},
max_exo_lead {m.max_exo_lead},
max_exo_det_lag {m.max_exo_det_lag},
max_exo_det_lead {m.max_exo_det_lead},
max_lag_orig {m.max_lag_orig},
max_lead_orig {m.max_lead_orig},
max_lag_with_diffs_expanded_orig {m.max_lag_with_diffs_expanded_orig},
max_endo_lag_orig {m.max_endo_lag_orig},
max_endo_lead_orig {m.max_endo_lead_orig},
max_exo_lag_orig {m.max_exo_lag_orig},
max_exo_lead_orig {m.max_exo_lead_orig},
max_exo_det_lag_orig {m.max_exo_det_lag_orig},
max_exo_det_lead_orig {m.max_exo_det_lead_orig},
xrefs {m.xrefs},
xref_param {m.xref_param},
xref_endo {m.xref_endo},
xref_exo {m.xref_exo},
xref_exo_det {m.xref_exo_det},
nonzero_hessian_eqs {m.nonzero_hessian_eqs},
variableMapping {m.variableMapping},
blocks_jacob_cols_endo {m.blocks_jacob_cols_endo},
var_expectation_functions_to_write {m.var_expectation_functions_to_write},
mfs {m.mfs},
static_mfs {m.static_mfs}
{
copyHelper(m);
}
DynamicModel &
DynamicModel::operator=(const DynamicModel &m)
DynamicModel&
DynamicModel::operator=(const DynamicModel& m)
{
ModelTree::operator=(m);
......@@ -104,6 +114,7 @@ DynamicModel::operator=(const DynamicModel &m)
static_only_equations_lineno = m.static_only_equations_lineno;
static_only_equations_equation_tags = m.static_only_equations_equation_tags;
static_only_complementarity_conditions.clear();
deriv_id_table = m.deriv_id_table;
inv_deriv_id_table = m.inv_deriv_id_table;
dyn_jacobian_cols_table = m.dyn_jacobian_cols_table;
......@@ -144,7 +155,7 @@ DynamicModel::operator=(const DynamicModel &m)
}
void
DynamicModel::writeDynamicBytecode(const string &basename) const
DynamicModel::writeDynamicBytecode(const string& basename) const
{
/* Bytecode only works when there are with as many endogenous as equations.
(e.g. the constructor of FBEGINBLOCK_ makes this assumption) */
......@@ -160,51 +171,48 @@ DynamicModel::writeDynamicBytecode(const string &basename) const
simulation_type = BlockSimulationType::solveBackwardComplete;
// First write the .bin file
int u_count_int { writeBytecodeBinFile(basename + "/model/bytecode/dynamic.bin",
simulation_type == BlockSimulationType::solveTwoBoundariesComplete) };
int u_count_int {
writeBytecodeBinFile(basename + "/model/bytecode/dynamic.bin",
simulation_type == BlockSimulationType::solveTwoBoundariesComplete)};
BytecodeWriter code_file {basename + "/model/bytecode/dynamic.cod"};
Bytecode::Writer code_file {basename + "/model/bytecode/dynamic.cod"};
// Declare temporary terms
code_file << FDIMT_{static_cast<int>(temporary_terms_derivatives[0].size()
+ temporary_terms_derivatives[1].size())};
code_file << Bytecode::FDIMT {static_cast<int>(temporary_terms_derivatives[0].size()
+ temporary_terms_derivatives[1].size())};
// Declare the (single) block
vector<int> exo(symbol_table.exo_nbr()), exo_det(symbol_table.exo_det_nbr());
iota(exo.begin(), exo.end(), 0);
iota(exo_det.begin(), exo_det.end(), 0);
int jacobian_ncols_endo
{ static_cast<int>(count_if(dyn_jacobian_cols_table.begin(), dyn_jacobian_cols_table.end(),
[this](const auto &v)
{ return getTypeByDerivID(v.first) == SymbolType::endogenous; }))
};
vector<int> eq_idx(equations.size());
iota(eq_idx.begin(), eq_idx.end(), 0);
vector<int> endo_idx(symbol_table.endo_nbr());
iota(endo_idx.begin(), endo_idx.end(), 0);
code_file << FBEGINBLOCK_{symbol_table.endo_nbr(),
simulation_type,
0,
symbol_table.endo_nbr(),
endo_idx,
eq_idx,
false,
u_count_int,
jacobian_ncols_endo,
symbol_table.exo_det_nbr(),
symbol_table.exo_nbr(),
exo_det,
exo};
auto exo = views::iota(0, symbol_table.exo_nbr());
auto exo_det = views::iota(0, symbol_table.exo_det_nbr());
auto eq_idx = views::iota(0, static_cast<int>(equations.size()));
auto endo_idx = views::iota(0, symbol_table.endo_nbr());
int jacobian_ncols_endo {
static_cast<int>(ranges::count_if(dyn_jacobian_cols_table, [this](const auto& v) {
return getTypeByDerivID(v.first) == SymbolType::endogenous;
}))};
code_file << Bytecode::FBEGINBLOCK {symbol_table.endo_nbr(),
simulation_type,
0,
symbol_table.endo_nbr(),
{endo_idx.begin(), endo_idx.end()},
{eq_idx.begin(), eq_idx.end()},
false,
u_count_int,
jacobian_ncols_endo,
symbol_table.exo_det_nbr(),
symbol_table.exo_nbr(),
{exo_det.begin(), exo_det.end()},
{exo.begin(), exo.end()}};
writeBytecodeHelper<true>(code_file);
}
void
DynamicModel::writeDynamicBlockBytecode(const string &basename) const
DynamicModel::writeDynamicBlockBytecode(const string& basename) const
{
BytecodeWriter code_file {basename + "/model/bytecode/block/dynamic.cod"};
Bytecode::Writer code_file {basename + "/model/bytecode/block/dynamic.cod"};
const filesystem::path bin_filename {basename + "/model/bytecode/block/dynamic.bin"};
ofstream bin_file {bin_filename, ios::out | ios::binary};
......@@ -215,7 +223,7 @@ DynamicModel::writeDynamicBlockBytecode(const string &basename) const
}
// Temporary variables declaration
code_file << FDIMT_{static_cast<int>(blocks_temporary_terms_idxs.size())};
code_file << Bytecode::FDIMT {static_cast<int>(blocks_temporary_terms_idxs.size())};
temporary_terms_t temporary_terms_written;
......@@ -225,54 +233,57 @@ DynamicModel::writeDynamicBlockBytecode(const string &basename) const
// Write section of .bin file except for evaluate blocks and solve simple blocks
const int u_count {simulation_type == BlockSimulationType::solveTwoBoundariesSimple
|| simulation_type == BlockSimulationType::solveTwoBoundariesComplete
|| simulation_type == BlockSimulationType::solveBackwardComplete
|| simulation_type == BlockSimulationType::solveForwardComplete
? writeBlockBytecodeBinFile(bin_file, block)
: 0};
code_file << FBEGINBLOCK_{blocks[block].mfs_size,
simulation_type,
blocks[block].first_equation,
blocks[block].size,
endo_idx_block2orig,
eq_idx_block2orig,
blocks[block].linear,
u_count,
static_cast<int>(blocks_jacob_cols_endo[block].size())};
|| simulation_type
== BlockSimulationType::solveTwoBoundariesComplete
|| simulation_type == BlockSimulationType::solveBackwardComplete
|| simulation_type == BlockSimulationType::solveForwardComplete
? writeBlockBytecodeBinFile(bin_file, block)
: 0};
code_file << Bytecode::FBEGINBLOCK {blocks[block].mfs_size,
simulation_type,
blocks[block].first_equation,
blocks[block].size,
endo_idx_block2orig,
eq_idx_block2orig,
blocks[block].linear,
u_count,
static_cast<int>(blocks_jacob_cols_endo[block].size())};
writeBlockBytecodeHelper<true>(code_file, block, temporary_terms_written);
}
code_file << FEND_{};
code_file << Bytecode::FEND {};
}
void
DynamicModel::writeDynamicMFile(const string &basename) const
DynamicModel::writeDynamicMFile(const string& basename) const
{
auto [d_output, tt_output] = writeModelFileHelper<ExprNodeOutputType::matlabDynamicModel>();
ostringstream init_output, end_output;
init_output << "residual = zeros(" << equations.size() << ", 1);";
writeDynamicMFileHelper(basename, "dynamic_resid", "residual", "dynamic_resid_tt",
temporary_terms_derivatives[0].size(),
"", init_output, end_output, d_output[0], tt_output[0]);
temporary_terms_derivatives[0].size(), "", init_output, end_output,
d_output[0], tt_output[0]);
init_output.str("");
init_output << "g1 = zeros(" << equations.size() << ", " << getJacobianColsNbr(false) << ");";
writeDynamicMFileHelper(basename, "dynamic_g1", "g1", "dynamic_g1_tt",
temporary_terms_derivatives[0].size() + temporary_terms_derivatives[1].size(),
temporary_terms_derivatives[0].size()
+ temporary_terms_derivatives[1].size(),
"dynamic_resid_tt", init_output, end_output, d_output[1], tt_output[1]);
writeDynamicMWrapperFunction(basename, "g1");
// For order ≥ 2
int ncols{getJacobianColsNbr(false)};
int ntt { static_cast<int>(temporary_terms_derivatives[0].size() + temporary_terms_derivatives[1].size())};
for (size_t i{2}; i < derivatives.size(); i++)
int ncols {getJacobianColsNbr(false)};
int ntt {static_cast<int>(temporary_terms_derivatives[0].size()
+ temporary_terms_derivatives[1].size())};
for (size_t i {2}; i < derivatives.size(); i++)
{
ncols *= getJacobianColsNbr(false);
ntt += temporary_terms_derivatives[i].size();
string gname{"g" + to_string(i)};
string gprevname{"g" + to_string(i-1)};
string gname {"g" + to_string(i)};
string gprevname {"g" + to_string(i - 1)};
init_output.str("");
end_output.str("");
......@@ -281,15 +292,14 @@ DynamicModel::writeDynamicMFile(const string &basename) const
init_output << gname << "_i = zeros(" << NNZDerivatives[i] << ",1);" << endl
<< gname << "_j = zeros(" << NNZDerivatives[i] << ",1);" << endl
<< gname << "_v = zeros(" << NNZDerivatives[i] << ",1);" << endl;
end_output << gname << " = sparse("
<< gname << "_i," << gname << "_j," << gname << "_v,"
end_output << gname << " = sparse(" << gname << "_i," << gname << "_j," << gname << "_v,"
<< equations.size() << "," << ncols << ");";
}
else
init_output << gname << " = sparse([],[],[]," << equations.size() << "," << ncols << ");";
writeDynamicMFileHelper(basename, "dynamic_" + gname, gname, "dynamic_" + gname + "_tt", ntt,
"dynamic_" + gprevname + "_tt", init_output, end_output,
d_output[i], tt_output[i]);
"dynamic_" + gprevname + "_tt", init_output, end_output, d_output[i],
tt_output[i]);
if (i <= 3)
writeDynamicMWrapperFunction(basename, gname);
}
......@@ -298,10 +308,10 @@ DynamicModel::writeDynamicMFile(const string &basename) const
}
string
DynamicModel::reform(const string &name1) const
DynamicModel::reform(const string& name1) const
{
string name = name1;
int pos = name.find(R"(\)", 0);
int pos = name.find('\\', 0);
while (pos >= 0)
{
if (name.substr(pos + 1, 1) != R"(\)")
......@@ -310,18 +320,17 @@ DynamicModel::reform(const string &name1) const
pos++;
}
pos++;
pos = name.find(R"(\)", pos);
pos = name.find('\\', pos);
}
return name;
}
void
DynamicModel::printNonZeroHessianEquations(ostream &output) const
DynamicModel::printNonZeroHessianEquations(ostream& output) const
{
if (nonzero_hessian_eqs.size() != 1)
output << "[";
for (bool printed_something{false};
int it : nonzero_hessian_eqs)
for (bool printed_something {false}; int it : nonzero_hessian_eqs)
{
if (exchange(printed_something, true))
output << " ";
......@@ -332,7 +341,7 @@ DynamicModel::printNonZeroHessianEquations(ostream &output) const
}
void
DynamicModel::writeDynamicMWrapperFunction(const string &basename, const string &ending) const
DynamicModel::writeDynamicMWrapperFunction(const string& basename, const string& ending) const
{
string name;
if (ending == "g1")
......@@ -343,7 +352,7 @@ DynamicModel::writeDynamicMWrapperFunction(const string &basename, const string
name = "dynamic_resid_g1_g2_g3";
filesystem::path filename {packageDir(basename) / (name + ".m")};
ofstream output{filename, ios::out | ios::binary};
ofstream output {filename, ios::out | ios::binary};
if (!output.is_open())
{
cerr << "ERROR: Can't open file " << filename.string() << " for writing" << endl;
......@@ -351,47 +360,59 @@ DynamicModel::writeDynamicMWrapperFunction(const string &basename, const string
}
if (ending == "g1")
output << "function [residual, g1] = " << name << "(T, y, x, params, steady_state, it_, T_flag)" << endl
<< "% function [residual, g1] = " << name << "(T, y, x, params, steady_state, it_, T_flag)" << endl;
output << "function [residual, g1] = " << name << "(T, y, x, params, steady_state, it_, T_flag)"
<< endl
<< "% function [residual, g1] = " << name
<< "(T, y, x, params, steady_state, it_, T_flag)" << endl;
else if (ending == "g2")
output << "function [residual, g1, g2] = " << name << "(T, y, x, params, steady_state, it_, T_flag)" << endl
<< "% function [residual, g1, g2] = " << name << "(T, y, x, params, steady_state, it_, T_flag)" << endl;
output << "function [residual, g1, g2] = " << name
<< "(T, y, x, params, steady_state, it_, T_flag)" << endl
<< "% function [residual, g1, g2] = " << name
<< "(T, y, x, params, steady_state, it_, T_flag)" << endl;
else if (ending == "g3")
output << "function [residual, g1, g2, g3] = " << name << "(T, y, x, params, steady_state, it_, T_flag)" << endl
<< "% function [residual, g1, g2, g3] = " << name << "(T, y, x, params, steady_state, it_, T_flag)" << endl;
output << "function [residual, g1, g2, g3] = " << name
<< "(T, y, x, params, steady_state, it_, T_flag)" << endl
<< "% function [residual, g1, g2, g3] = " << name
<< "(T, y, x, params, steady_state, it_, T_flag)" << endl;
output << "%" << endl
<< "% Wrapper function automatically created by Dynare" << endl
<< "%" << endl
<< endl
<< " if T_flag" << endl
<< " T = " << basename << ".dynamic_" << ending << "_tt(T, y, x, params, steady_state, it_);" << endl
<< " T = " << basename << ".dynamic_" << ending
<< "_tt(T, y, x, params, steady_state, it_);" << endl
<< " end" << endl;
if (ending == "g1")
output << " residual = " << basename << ".dynamic_resid(T, y, x, params, steady_state, it_, false);" << endl
<< " g1 = " << basename << ".dynamic_g1(T, y, x, params, steady_state, it_, false);" << endl;
output << " residual = " << basename
<< ".dynamic_resid(T, y, x, params, steady_state, it_, false);" << endl
<< " g1 = " << basename
<< ".dynamic_g1(T, y, x, params, steady_state, it_, false);" << endl;
else if (ending == "g2")
output << " [residual, g1] = " << basename << ".dynamic_resid_g1(T, y, x, params, steady_state, it_, false);" << endl
<< " g2 = " << basename << ".dynamic_g2(T, y, x, params, steady_state, it_, false);" << endl;
output << " [residual, g1] = " << basename
<< ".dynamic_resid_g1(T, y, x, params, steady_state, it_, false);" << endl
<< " g2 = " << basename
<< ".dynamic_g2(T, y, x, params, steady_state, it_, false);" << endl;
else if (ending == "g3")
output << " [residual, g1, g2] = " << basename << ".dynamic_resid_g1_g2(T, y, x, params, steady_state, it_, false);" << endl
<< " g3 = " << basename << ".dynamic_g3(T, y, x, params, steady_state, it_, false);" << endl;
output << " [residual, g1, g2] = " << basename
<< ".dynamic_resid_g1_g2(T, y, x, params, steady_state, it_, false);" << endl
<< " g3 = " << basename
<< ".dynamic_g3(T, y, x, params, steady_state, it_, false);" << endl;
output << endl << "end" << endl;
output.close();
}
void
DynamicModel::writeDynamicMFileHelper(const string &basename,
const string &name, const string &retvalname,
const string &name_tt, size_t ttlen,
const string &previous_tt_name,
const ostringstream &init_s, const ostringstream &end_s,
const ostringstream &s, const ostringstream &s_tt) const
DynamicModel::writeDynamicMFileHelper(const string& basename, const string& name,
const string& retvalname, const string& name_tt, size_t ttlen,
const string& previous_tt_name, const ostringstream& init_s,
const ostringstream& end_s, const ostringstream& s,
const ostringstream& s_tt) const
{
filesystem::path filename {packageDir(basename) / (name_tt + ".m")};
ofstream output{filename, ios::out | ios::binary};
ofstream output {filename, ios::out | ios::binary};
if (!output.is_open())
{
cerr << "ERROR: Can't open file " << filename.string() << " for writing" << endl;
......@@ -404,27 +425,43 @@ DynamicModel::writeDynamicMFileHelper(const string &basename,
<< "% File created by Dynare Preprocessor from .mod file" << endl
<< "%" << endl
<< "% Inputs:" << endl
<< "% T [#temp variables by 1] double vector of temporary terms to be filled by function" << endl
<< "% y [#dynamic variables by 1] double vector of endogenous variables in the order stored" << endl
<< "% in M_.lead_lag_incidence; see the Manual" << endl
<< "% x [nperiods by M_.exo_nbr] double matrix of exogenous variables (in declaration order)" << endl
<< "% for all simulation periods" << endl
<< "% steady_state [M_.endo_nbr by 1] double vector of steady state values" << endl
<< "% params [M_.param_nbr by 1] double vector of parameter values in declaration order" << endl
<< "% it_ scalar double time period for exogenous variables for which" << endl
<< "% T [#temp variables by 1] double vector of temporary terms to be "
"filled by function"
<< endl
<< "% y [#dynamic variables by 1] double vector of endogenous variables "
"in the order stored"
<< endl
<< "% in M_.lead_lag_incidence; see "
"the Manual"
<< endl
<< "% x [nperiods by M_.exo_nbr] double matrix of exogenous variables "
"(in declaration order)"
<< endl
<< "% for all simulation periods"
<< endl
<< "% steady_state [M_.endo_nbr by 1] double vector of steady state values"
<< endl
<< "% params [M_.param_nbr by 1] double vector of parameter values in "
"declaration order"
<< endl
<< "% it_ scalar double time period for exogenous "
"variables for which"
<< endl
<< "% to evaluate the model" << endl
<< "%" << endl
<< "% Output:" << endl
<< "% T [#temp variables by 1] double vector of temporary terms" << endl
<< "%" << endl << endl
<< "%" << endl
<< endl
<< "assert(length(T) >= " << ttlen << ");" << endl
<< endl;
if (!previous_tt_name.empty())
output << "T = " << basename << "." << previous_tt_name << "(T, y, x, params, steady_state, it_);" << endl << endl;
output << "T = " << basename << "." << previous_tt_name
<< "(T, y, x, params, steady_state, it_);" << endl
<< endl;
output << s_tt.str() << endl
<< "end" << endl;
output << s_tt.str() << endl << "end" << endl;
output.close();
filename = packageDir(basename) / (name + ".m");
......@@ -435,72 +472,94 @@ DynamicModel::writeDynamicMFileHelper(const string &basename,
exit(EXIT_FAILURE);
}
output << "function " << retvalname << " = " << name << "(T, y, x, params, steady_state, it_, T_flag)" << endl
<< "% function " << retvalname << " = " << name << "(T, y, x, params, steady_state, it_, T_flag)" << endl
output << "function " << retvalname << " = " << name
<< "(T, y, x, params, steady_state, it_, T_flag)" << endl
<< "% function " << retvalname << " = " << name
<< "(T, y, x, params, steady_state, it_, T_flag)" << endl
<< "%" << endl
<< "% File created by Dynare Preprocessor from .mod file" << endl
<< "%" << endl
<< "% Inputs:" << endl
<< "% T [#temp variables by 1] double vector of temporary terms to be filled by function" << endl
<< "% y [#dynamic variables by 1] double vector of endogenous variables in the order stored" << endl
<< "% in M_.lead_lag_incidence; see the Manual" << endl
<< "% x [nperiods by M_.exo_nbr] double matrix of exogenous variables (in declaration order)" << endl
<< "% for all simulation periods" << endl
<< "% steady_state [M_.endo_nbr by 1] double vector of steady state values" << endl
<< "% params [M_.param_nbr by 1] double vector of parameter values in declaration order" << endl
<< "% it_ scalar double time period for exogenous variables for which" << endl
<< "% T [#temp variables by 1] double vector of temporary terms to be "
"filled by function"
<< endl
<< "% y [#dynamic variables by 1] double vector of endogenous variables "
"in the order stored"
<< endl
<< "% in M_.lead_lag_incidence; see "
"the Manual"
<< endl
<< "% x [nperiods by M_.exo_nbr] double matrix of exogenous variables "
"(in declaration order)"
<< endl
<< "% for all simulation periods"
<< endl
<< "% steady_state [M_.endo_nbr by 1] double vector of steady state values"
<< endl
<< "% params [M_.param_nbr by 1] double vector of parameter values in "
"declaration order"
<< endl
<< "% it_ scalar double time period for exogenous "
"variables for which"
<< endl
<< "% to evaluate the model" << endl
<< "% T_flag boolean boolean flag saying whether or not to calculate temporary terms" << endl
<< "% T_flag boolean boolean flag saying whether or not to "
"calculate temporary terms"
<< endl
<< "%" << endl
<< "% Output:" << endl
<< "% " << retvalname << endl
<< "%" << endl << endl;
<< "%" << endl
<< endl;
if (!name_tt.empty())
output << "if T_flag" << endl
<< " T = " << basename << "." << name_tt << "(T, y, x, params, steady_state, it_);" << endl
<< " T = " << basename << "." << name_tt << "(T, y, x, params, steady_state, it_);"
<< endl
<< "end" << endl;
output << init_s.str() << endl
<< s.str()
<< end_s.str() << endl
<< "end" << endl;
output << init_s.str() << endl << s.str() << end_s.str() << endl << "end" << endl;
output.close();
}
void
DynamicModel::writeDynamicMCompatFile(const string &basename) const
DynamicModel::writeDynamicMCompatFile(const string& basename) const
{
filesystem::path filename {packageDir(basename) / "dynamic.m"};
ofstream output{filename, ios::out | ios::binary};
ofstream output {filename, ios::out | ios::binary};
if (!output.is_open())
{
cerr << "ERROR: Can't open file " << filename.string() << " for writing" << endl;
exit(EXIT_FAILURE);
}
int ntt { static_cast<int>(temporary_terms_derivatives[0].size() + temporary_terms_derivatives[1].size() + temporary_terms_derivatives[2].size() + temporary_terms_derivatives[3].size()) };
int ntt {static_cast<int>(
temporary_terms_derivatives[0].size() + temporary_terms_derivatives[1].size()
+ temporary_terms_derivatives[2].size() + temporary_terms_derivatives[3].size())};
output << "function [residual, g1, g2, g3] = dynamic(y, x, params, steady_state, it_)" << endl
<< " T = NaN(" << ntt << ", 1);" << endl
<< " if nargout <= 1" << endl
<< " residual = " << basename << ".dynamic_resid(T, y, x, params, steady_state, it_, true);" << endl
<< " residual = " << basename
<< ".dynamic_resid(T, y, x, params, steady_state, it_, true);" << endl
<< " elseif nargout == 2" << endl
<< " [residual, g1] = " << basename << ".dynamic_resid_g1(T, y, x, params, steady_state, it_, true);" << endl
<< " [residual, g1] = " << basename
<< ".dynamic_resid_g1(T, y, x, params, steady_state, it_, true);" << endl
<< " elseif nargout == 3" << endl
<< " [residual, g1, g2] = " << basename << ".dynamic_resid_g1_g2(T, y, x, params, steady_state, it_, true);" << endl
<< " [residual, g1, g2] = " << basename
<< ".dynamic_resid_g1_g2(T, y, x, params, steady_state, it_, true);" << endl
<< " else" << endl
<< " [residual, g1, g2, g3] = " << basename << ".dynamic_resid_g1_g2_g3(T, y, x, params, steady_state, it_, true);" << endl
<< " [residual, g1, g2, g3] = " << basename
<< ".dynamic_resid_g1_g2_g3(T, y, x, params, steady_state, it_, true);" << endl
<< " end" << endl
<< "end" << endl;
output.close();
}
vector<pair<string, string>>
DynamicModel::parseIncludeExcludeEquations(const string &inc_exc_option_value, bool exclude_eqs)
vector<map<string, string>>
DynamicModel::parseIncludeExcludeEquations(const string& inc_exc_option_value, bool exclude_eqs)
{
auto removeLeadingTrailingWhitespace = [](string &str)
{
auto removeLeadingTrailingWhitespace = [](string& str) {
str.erase(0, str.find_first_not_of("\t\n\v\f\r "));
str.erase(str.find_last_not_of("\t\n\v\f\r ") + 1);
};
......@@ -523,12 +582,12 @@ DynamicModel::parseIncludeExcludeEquations(const string &inc_exc_option_value, b
removeLeadingTrailingWhitespace(line);
if (!line.empty())
{
if (tags.empty() && line.find("=") != string::npos)
if (tags.empty() && line.find('=') != string::npos)
{
tagname_on_first_line = true;
tags += line + "(";
}
else if (line.find("'") != string::npos)
else if (line.find('\'') != string::npos)
tags += line + ",";
else
tags += "'" + line + "',";
......@@ -537,7 +596,7 @@ DynamicModel::parseIncludeExcludeEquations(const string &inc_exc_option_value, b
if (!tags.empty())
{
tags = tags.substr(0, tags.size()-1);
tags = tags.substr(0, tags.size() - 1);
if (tagname_on_first_line)
tags += ")";
}
......@@ -570,13 +629,14 @@ DynamicModel::parseIncludeExcludeEquations(const string &inc_exc_option_value, b
tags = tags.substr(1, tags.length() - 2);
removeLeadingTrailingWhitespace(tags);
}
tagname = tagname.substr(0, tagname.size()-1);
tagname = tagname.substr(0, tagname.size() - 1);
removeLeadingTrailingWhitespace(tagname);
}
string quote_regex = "'[^']+'";
string non_quote_regex = R"([^,\s]+)";
regex r(R"((\s*)" + quote_regex + "|" + non_quote_regex + R"(\s*)(,\s*()" + quote_regex + "|" + non_quote_regex + R"()\s*)*)");
regex r(R"((\s*)" + quote_regex + "|" + non_quote_regex + R"(\s*)(,\s*()" + quote_regex + "|"
+ non_quote_regex + R"()\s*)*)");
if (!regex_match(tags, r))
{
cerr << "ERROR: " << (exclude_eqs ? "exclude_eqs" : "include_eqs")
......@@ -584,10 +644,9 @@ DynamicModel::parseIncludeExcludeEquations(const string &inc_exc_option_value, b
exit(EXIT_FAILURE);
}
vector<pair<string, string>> eq_tag_set;
vector<map<string, string>> eq_tag_set;
regex s(quote_regex + "|" + non_quote_regex);
for (auto it = sregex_iterator(tags.begin(), tags.end(), s);
it != sregex_iterator(); ++it)
for (auto it = sregex_iterator(tags.begin(), tags.end(), s); it != sregex_iterator(); ++it)
{
string_view str {it->str()};
if (str.front() == '\'' && str.back() == '\'')
......@@ -595,17 +654,17 @@ DynamicModel::parseIncludeExcludeEquations(const string &inc_exc_option_value, b
str.remove_prefix(1);
str.remove_suffix(1);
}
eq_tag_set.emplace_back(tagname, str);
eq_tag_set.push_back({{tagname, string {str}}});
}
return eq_tag_set;
}
vector<int>
DynamicModel::removeEquationsHelper(set<pair<string, string>> &listed_eqs_by_tag, bool exclude_eqs,
bool excluded_vars_change_type,
vector<BinaryOpNode *> &all_equations,
vector<optional<int>> &all_equations_lineno,
EquationTags &all_equation_tags, bool static_equations) const
DynamicModel::removeEquationsHelper(
set<map<string, string>>& listed_eqs_by_tag, bool exclude_eqs, bool excluded_vars_change_type,
vector<BinaryOpNode*>& all_equations, vector<optional<int>>& all_equations_lineno,
vector<optional<tuple<int, expr_t, expr_t>>>& all_complementarity_conditions,
EquationTags& all_equation_tags, bool static_equations) const
{
if (all_equations.empty())
return {};
......@@ -616,8 +675,7 @@ DynamicModel::removeEquationsHelper(set<pair<string, string>> &listed_eqs_by_tag
the caller knows which tag pairs have not been handled. */
set<int> listed_eqs_by_number;
for (auto it = listed_eqs_by_tag.begin(); it != listed_eqs_by_tag.end();)
if (auto tmp = all_equation_tags.getEqnsByTag(it->first, it->second);
!tmp.empty())
if (auto tmp = all_equation_tags.getEqnsByTags(*it); !tmp.empty())
{
listed_eqs_by_number.insert(tmp.begin(), tmp.end());
it = listed_eqs_by_tag.erase(it);
......@@ -635,8 +693,9 @@ DynamicModel::removeEquationsHelper(set<pair<string, string>> &listed_eqs_by_tag
eqs_to_delete_by_number.insert(i);
// remove from equations, equations_lineno, equation_tags
vector<BinaryOpNode *> new_equations;
vector<BinaryOpNode*> new_equations;
vector<optional<int>> new_equations_lineno;
vector<optional<tuple<int, expr_t, expr_t>>> new_complementarity_conditions;
map<int, int> old_eqn_num_2_new;
vector<int> excluded_vars;
for (size_t i = 0; i < all_equations.size(); i++)
......@@ -654,8 +713,10 @@ DynamicModel::removeEquationsHelper(set<pair<string, string>> &listed_eqs_by_tag
excluded_vars.push_back(*result.begin());
else
{
cerr << "ERROR: Equation " << i+1
<< " has been excluded but it does not have a single variable on its left-hand side or an `endogenous` tag" << endl;
cerr << "ERROR: Equation " << i + 1
<< " has been excluded but it does not have a single variable on its "
"left-hand side or an `endogenous` tag"
<< endl;
exit(EXIT_FAILURE);
}
}
......@@ -666,52 +727,74 @@ DynamicModel::removeEquationsHelper(set<pair<string, string>> &listed_eqs_by_tag
new_equations.emplace_back(all_equations[i]);
old_eqn_num_2_new[i] = new_equations.size() - 1;
new_equations_lineno.emplace_back(all_equations_lineno[i]);
new_complementarity_conditions.emplace_back(all_complementarity_conditions[i]);
}
int n_excl = all_equations.size() - new_equations.size();
all_equations = new_equations;
all_equations_lineno = new_equations_lineno;
all_equations = move(new_equations);
all_equations_lineno = move(new_equations_lineno);
all_complementarity_conditions = move(new_complementarity_conditions);
all_equation_tags.erase(eqs_to_delete_by_number, old_eqn_num_2_new);
if (!static_equations)
for (size_t i = 0; i < excluded_vars.size(); i++)
for (size_t j = i+1; j < excluded_vars.size(); j++)
for (size_t j = i + 1; j < excluded_vars.size(); j++)
if (excluded_vars[i] == excluded_vars[j])
{
cerr << "ERROR: Variable " << symbol_table.getName(i) << " was excluded twice"
<< " via a model_remove or model_replace statement, or via the include_eqs or exclude_eqs option" << endl;
<< " via a model_remove or model_replace statement, or via the include_eqs or "
"exclude_eqs option"
<< endl;
exit(EXIT_FAILURE);
}
cout << "Excluded " << n_excl << (static_equations ? " static " : " dynamic ")
<< "equation" << (n_excl > 1 ? "s" : "") << " via model_remove or model_replace statement, or via include_eqs or exclude_eqs option" << endl;
cout << "Excluded " << n_excl << (static_equations ? " static " : " dynamic ") << "equation"
<< (n_excl > 1 ? "s" : "")
<< " via model_remove or model_replace statement, or via include_eqs or exclude_eqs option"
<< endl;
return excluded_vars;
}
void
DynamicModel::removeEquations(const vector<pair<string, string>> &listed_eqs_by_tag, bool exclude_eqs,
bool excluded_vars_change_type)
DynamicModel::removeEquations(const vector<map<string, string>>& listed_eqs_by_tag,
bool exclude_eqs, bool excluded_vars_change_type)
{
/* Convert the const vector to a (mutable) set */
set listed_eqs_by_tag2(listed_eqs_by_tag.begin(), listed_eqs_by_tag.end());
vector<int> excluded_vars = removeEquationsHelper(listed_eqs_by_tag2, exclude_eqs,
excluded_vars_change_type,
equations, equations_lineno,
equation_tags, false);
vector<int> excluded_vars
= removeEquationsHelper(listed_eqs_by_tag2, exclude_eqs, excluded_vars_change_type, equations,
equations_lineno, complementarity_conditions, equation_tags, false);
// Ignore output because variables are not excluded when equations marked 'static' are excluded
removeEquationsHelper(listed_eqs_by_tag2, exclude_eqs, excluded_vars_change_type,
static_only_equations, static_only_equations_lineno,
static_only_equations_equation_tags, true);
static_only_complementarity_conditions, static_only_equations_equation_tags,
true);
if (!listed_eqs_by_tag2.empty())
{
cerr << "ERROR: model_remove/model_replace/exclude_eqs/include_eqs: The equations specified by" << endl;
for (const auto &[tagname, tagvalue] : listed_eqs_by_tag)
cerr << " " << tagname << "=" << tagvalue << endl;
cerr
<< "ERROR: model_remove/model_replace/exclude_eqs/include_eqs: The equations specified by"
<< endl;
for (const auto& m : listed_eqs_by_tag)
{
cerr << " ";
if (m.size() > 1)
cerr << "[ ";
bool first_printed {false};
for (const auto& [tagname, tagvalue] : m)
{
if (exchange(first_printed, true))
cerr << ", ";
cerr << tagname << "=" << tagvalue;
}
if (m.size() > 1)
cerr << " ]";
cerr << endl;
}
cerr << "were not found." << endl;
exit(EXIT_FAILURE);
}
......@@ -732,18 +815,24 @@ DynamicModel::removeEquations(const vector<pair<string, string>> &listed_eqs_by_
if (eqn_vars.contains(ev))
{
symbol_table.changeType(ev, SymbolType::exogenous);
cerr << "Variable '" << symbol_table.getName(ev) << "' turned into an exogenous, as its defining equation has been removed (but it still appears in an equation)" << endl;
cerr << "Variable '" << symbol_table.getName(ev)
<< "' turned into an exogenous, as its defining equation has been removed (but it "
"still appears in an equation)"
<< endl;
}
else
{
symbol_table.changeType(ev, SymbolType::excludedVariable);
cerr << "Variable '" << symbol_table.getName(ev) << "' has been excluded from the model, as its defining equation has been removed and it appears nowhere else" << endl;
cerr << "Variable '" << symbol_table.getName(ev)
<< "' has been excluded from the model, as its defining equation has been removed "
"and it appears nowhere else"
<< endl;
}
}
}
void
DynamicModel::includeExcludeEquations(const string &inc_exc_option_value, bool exclude_eqs)
DynamicModel::includeExcludeEquations(const string& inc_exc_option_value, bool exclude_eqs)
{
if (inc_exc_option_value.empty())
return;
......@@ -757,68 +846,107 @@ DynamicModel::includeExcludeEquations(const string &inc_exc_option_value, bool e
ModFile::transformPass(), so we must do the check again */
if (staticOnlyEquationsNbr() != dynamicOnlyEquationsNbr())
{
cerr << "ERROR: exclude_eqs/include_eqs: You must remove the same number of equations marked `static` as equations marked `dynamic`." << endl;
cerr << "ERROR: exclude_eqs/include_eqs: You must remove the same number of equations marked "
"`static` as equations marked `dynamic`."
<< endl;
exit(EXIT_FAILURE);
}
}
void
DynamicModel::writeBlockDriverOutput(ostream &output) const
DynamicModel::writeBlockDriverOutput(ostream& output) const
{
output << "M_.block_structure.time_recursive = " << boolalpha << time_recursive_block_decomposition << ";" << endl;
output << "M_.block_structure.time_recursive = " << boolalpha
<< time_recursive_block_decomposition << ";" << endl;
for (int blk = 0; blk < static_cast<int>(blocks.size()); blk++)
{
int block_size = blocks[blk].size;
output << "M_.block_structure.block(" << blk+1 << ").Simulation_Type = " << static_cast<int>(blocks[blk].simulation_type) << ";" << endl
<< "M_.block_structure.block(" << blk+1 << ").endo_nbr = " << block_size << ";" << endl
<< "M_.block_structure.block(" << blk+1 << ").mfs = " << blocks[blk].mfs_size << ";" << endl
<< "M_.block_structure.block(" << blk+1 << ").equation = [";
output << "M_.block_structure.block(" << blk + 1
<< ").Simulation_Type = " << static_cast<int>(blocks[blk].simulation_type) << ";"
<< endl
<< "M_.block_structure.block(" << blk + 1 << ").endo_nbr = " << block_size << ";"
<< endl
<< "M_.block_structure.block(" << blk + 1 << ").mfs = " << blocks[blk].mfs_size << ";"
<< endl
<< "M_.block_structure.block(" << blk + 1 << ").equation = [";
for (int eq = 0; eq < block_size; eq++)
output << " " << getBlockEquationID(blk, eq)+1;
output << "];" << endl
<< "M_.block_structure.block(" << blk+1 << ").variable = [";
output << " " << getBlockEquationID(blk, eq) + 1;
output << "];" << endl << "M_.block_structure.block(" << blk + 1 << ").variable = [";
for (int var = 0; var < block_size; var++)
output << " " << getBlockVariableID(blk, var)+1;
output << " " << getBlockVariableID(blk, var) + 1;
output << "];" << endl
<< "M_.block_structure.block(" << blk+1 << ").is_linear = " << boolalpha << blocks[blk].linear << ';' << endl
<< "M_.block_structure.block(" << blk+1 << ").NNZDerivatives = " << blocks_derivatives[blk].size() << ';' << endl;
<< "M_.block_structure.block(" << blk + 1 << ").is_linear = " << boolalpha
<< blocks[blk].linear << ';' << endl
<< "M_.block_structure.block(" << blk + 1
<< ").NNZDerivatives = " << blocks_derivatives[blk].size() << ';' << endl
<< "M_.block_structure.block(" << blk + 1 << ").bytecode_jacob_cols_to_sparse = [";
const bool one_boundary {
blocks[blk].simulation_type == BlockSimulationType::solveBackwardSimple
|| blocks[blk].simulation_type == BlockSimulationType::solveForwardSimple
|| blocks[blk].simulation_type == BlockSimulationType::solveBackwardComplete
|| blocks[blk].simulation_type == BlockSimulationType::solveForwardComplete};
/* bytecode_jacob_cols_to_sparse is an array that maps column indices in
the Jacobian returned by bytecode MEX in evaluate mode (which only
contains nonzero columns, but also incorporate recursive variables),
into column indices in the sparse representiation (which has 3×n
columns for two-boundaries blocks and n columns for one-boundary
blocks). Columns unused in the sparse representation are indicated by
a zero. */
vector<int> bytecode_jacob_cols_to_sparse(blocks_jacob_cols_endo[blk].size());
for (auto& [key, index] : blocks_jacob_cols_endo[blk])
{
auto [var, lag] {key};
if (var >= blocks[blk].getRecursiveSize() // NB: this check can be removed once Jacobian
// no longer contains columns for derivatives
// w.r.t. recursive variables
&& !(one_boundary && lag != 0))
bytecode_jacob_cols_to_sparse[index]
= static_cast<int>(!one_boundary) * (lag + 1) * blocks[blk].mfs_size + var
- blocks[blk].getRecursiveSize();
else
bytecode_jacob_cols_to_sparse[index] = -1;
}
for (int i : bytecode_jacob_cols_to_sparse)
output << i + 1 << " ";
output << "];" << endl;
}
writeBlockDriverSparseIndicesHelper<true>(output);
output << "M_.block_structure.variable_reordered = [";
for (int i = 0; i < symbol_table.endo_nbr(); i++)
output << " " << endo_idx_block2orig[i]+1;
output << "];" << endl
<< "M_.block_structure.equation_reordered = [";
output << " " << endo_idx_block2orig[i] + 1;
output << "];" << endl << "M_.block_structure.equation_reordered = [";
for (int i = 0; i < symbol_table.endo_nbr(); i++)
output << " " << eq_idx_block2orig[i]+1;
output << " " << eq_idx_block2orig[i] + 1;
output << "];" << endl;
map<int, set<pair<int, int>>> lag_row_incidence;
for (const auto &[indices, d1] : derivatives[1])
if (int deriv_id = indices[1];
getTypeByDerivID(deriv_id) == SymbolType::endogenous)
for (const auto& [indices, d1] : derivatives[1])
if (int deriv_id = indices[1]; getTypeByDerivID(deriv_id) == SymbolType::endogenous)
{
int eq = indices[0];
int var { getTypeSpecificIDByDerivID(deriv_id) };
int var {getTypeSpecificIDByDerivID(deriv_id)};
int lag = getLagByDerivID(deriv_id);
lag_row_incidence[lag].insert({ eq, var });
lag_row_incidence[lag].insert({eq, var});
}
for (auto [lag, eq_var_set] : lag_row_incidence)
{
output << "M_.block_structure.incidence(" << max_endo_lag+lag+1 << ").lead_lag = " << lag << ";" << endl
<< "M_.block_structure.incidence(" << max_endo_lag+lag+1 << ").sparse_IM = [" << endl;
output << "M_.block_structure.incidence(" << max_endo_lag + lag + 1 << ").lead_lag = " << lag
<< ";" << endl
<< "M_.block_structure.incidence(" << max_endo_lag + lag + 1 << ").sparse_IM = ["
<< endl;
for (auto [eq, var] : eq_var_set)
output << " " << eq+1 << " " << var+1 << ";" << endl;
output << " " << eq + 1 << " " << var + 1 << ";" << endl;
output << "];" << endl;
}
output << "M_.block_structure.dyn_tmp_nbr = " << blocks_temporary_terms_idxs.size() << ';' << endl;
output << "M_.block_structure.dyn_tmp_nbr = " << blocks_temporary_terms_idxs.size() << ';'
<< endl;
}
void
DynamicModel::writeDriverOutput(ostream &output, bool compute_xrefs) const
DynamicModel::writeDriverOutput(ostream& output, bool compute_xrefs) const
{
/* Writing initialisation for M_.lead_lag_incidence matrix
M_.lead_lag_incidence is a matrix with as many columns as there are
......@@ -837,20 +965,15 @@ DynamicModel::writeDriverOutput(ostream &output, bool compute_xrefs) const
<< "M_.orig_maximum_exo_det_lead = " << max_exo_det_lead_orig << ";" << endl
<< "M_.orig_maximum_lag = " << max_lag_orig << ";" << endl
<< "M_.orig_maximum_lead = " << max_lead_orig << ";" << endl
<< "M_.orig_maximum_lag_with_diffs_expanded = " << max_lag_with_diffs_expanded_orig << ";" << endl
<< "M_.orig_maximum_lag_with_diffs_expanded = " << max_lag_with_diffs_expanded_orig << ";"
<< endl
<< "M_.lead_lag_incidence = [";
// Loop on endogenous variables
int nstatic = 0,
nfwrd = 0,
npred = 0,
nboth = 0;
int nstatic = 0, nfwrd = 0, npred = 0, nboth = 0;
for (int endoID = 0; endoID < symbol_table.endo_nbr(); endoID++)
{
output << endl;
int sstatic = 1,
sfwrd = 0,
spred = 0,
sboth = 0;
int sstatic = 1, sfwrd = 0, spred = 0, sboth = 0;
// Loop on periods
for (int lag = -max_endo_lag; lag <= max_endo_lead; lag++)
{
......@@ -878,7 +1001,7 @@ DynamicModel::writeDriverOutput(ostream &output, bool compute_xrefs) const
}
}
}
catch (UnknownDerivIDException &e)
catch (UnknownDerivIDException& e)
{
output << " 0";
}
......@@ -891,14 +1014,14 @@ DynamicModel::writeDriverOutput(ostream &output, bool compute_xrefs) const
}
output << "]';" << endl
<< "M_.nstatic = " << nstatic << ";" << endl
<< "M_.nfwrd = " << nfwrd << ";" << endl
<< "M_.npred = " << npred << ";" << endl
<< "M_.nboth = " << nboth << ";" << endl
<< "M_.nsfwrd = " << nfwrd+nboth << ";" << endl
<< "M_.nspred = " << npred+nboth << ";" << endl
<< "M_.ndynamic = " << npred+nboth+nfwrd << ";" << endl
<< "M_.nfwrd = " << nfwrd << ";" << endl
<< "M_.npred = " << npred << ";" << endl
<< "M_.nboth = " << nboth << ";" << endl
<< "M_.nsfwrd = " << nfwrd + nboth << ";" << endl
<< "M_.nspred = " << npred + nboth << ";" << endl
<< "M_.ndynamic = " << npred + nboth + nfwrd << ";" << endl
<< "M_.dynamic_tmp_nbr = [";
for (const auto &tts : temporary_terms_derivatives)
for (const auto& tts : temporary_terms_derivatives)
output << tts.size() << "; ";
output << "];" << endl;
......@@ -906,7 +1029,7 @@ DynamicModel::writeDriverOutput(ostream &output, bool compute_xrefs) const
equation_tags.writeOutput(output);
// Write mapping for variables and equations they are present in
for (const auto &variable : variableMapping)
for (const auto& variable : variableMapping)
{
output << "M_.mapping." << symbol_table.getName(variable.first) << ".eqidx = [";
for (auto equation : variable.second)
......@@ -916,20 +1039,12 @@ DynamicModel::writeDriverOutput(ostream &output, bool compute_xrefs) const
/* Say if static and dynamic models differ (because of [static] and [dynamic]
equation tags) */
output << "M_.static_and_dynamic_models_differ = "
<< boolalpha << (static_only_equations.size() > 0)
<< ";" << endl;
output << "M_.static_and_dynamic_models_differ = " << boolalpha
<< (static_only_equations.size() > 0) << ";" << endl;
// Say if model contains an external function call
bool has_external_function = false;
for (auto equation : equations)
if (equation->containsExternalFunction())
{
has_external_function = true;
break;
}
output << "M_.has_external_function = " << boolalpha << has_external_function
<< ';' << endl;
output << "M_.has_external_function = " << boolalpha
<< ranges::any_of(equations, &ExprNode::containsExternalFunction) << ';' << endl;
// Compute list of state variables, ordered in block-order
vector<int> state_var;
......@@ -939,10 +1054,10 @@ DynamicModel::writeDriverOutput(ostream &output, bool compute_xrefs) const
try
{
getDerivID(symbol_table.getID(SymbolType::endogenous, endo_idx_block2orig[endoID]), lag);
if (find(state_var.begin(), state_var.end(), endo_idx_block2orig[endoID]) == state_var.end())
if (ranges::find(state_var, endo_idx_block2orig[endoID]) == state_var.end())
state_var.push_back(endo_idx_block2orig[endoID]);
}
catch (UnknownDerivIDException &e)
catch (UnknownDerivIDException& e)
{
}
......@@ -952,7 +1067,7 @@ DynamicModel::writeDriverOutput(ostream &output, bool compute_xrefs) const
output << "M_.state_var = [";
for (int it : state_var)
output << it+1 << " ";
output << it + 1 << " ";
output << "];" << endl;
// Writing initialization for some other variables
......@@ -971,28 +1086,33 @@ DynamicModel::writeDriverOutput(ostream &output, bool compute_xrefs) const
{
output << "M_.maximum_exo_det_lag = " << max_exo_det_lag << ";" << endl
<< "M_.maximum_exo_det_lead = " << max_exo_det_lead << ";" << endl
<< "oo_.exo_det_steady_state = zeros(" << symbol_table.exo_det_nbr() << ", 1);" << endl;
<< "oo_.exo_det_steady_state = zeros(" << symbol_table.exo_det_nbr() << ", 1);"
<< endl;
}
output << "M_.params = " << "NaN(" << symbol_table.param_nbr() << ", 1);" << endl;
output << "M_.params = "
<< "NaN(" << symbol_table.param_nbr() << ", 1);" << endl;
string empty_cell = "cell(" + to_string(symbol_table.endo_nbr()) + ", 1)";
output << "M_.endo_trends = struct('deflator', " << empty_cell
<< ", 'log_deflator', " << empty_cell << ", 'growth_factor', " << empty_cell
<< ", 'log_growth_factor', " << empty_cell << ");" << endl;
output << "M_.endo_trends = struct('deflator', " << empty_cell << ", 'log_deflator', "
<< empty_cell << ", 'growth_factor', " << empty_cell << ", 'log_growth_factor', "
<< empty_cell << ");" << endl;
for (int i = 0; i < symbol_table.endo_nbr(); i++)
{
int symb_id = symbol_table.getID(SymbolType::endogenous, i);
if (auto it = nonstationary_symbols_map.find(symb_id); it != nonstationary_symbols_map.end())
{
auto [is_log, deflator] = it->second;
output << "M_.endo_trends(" << i+1 << ")."
<< (is_log ? "log_deflator" : "deflator") << " = '";
output << "M_.endo_trends(" << i + 1 << ")." << (is_log ? "log_deflator" : "deflator")
<< " = '";
deflator->writeJsonOutput(output, {}, {});
output << "';" << endl;
auto growth_factor = const_cast<DynamicModel *>(this)->AddDivide(deflator, deflator->decreaseLeadsLags(1))->removeTrendLeadLag(trend_symbols_map)->replaceTrendVar();
output << "M_.endo_trends(" << i+1 << ")."
auto growth_factor = const_cast<DynamicModel*>(this)
->AddDivide(deflator, deflator->decreaseLeadsLags(1))
->removeTrendLeadLag(trend_symbols_map)
->replaceTrendVar();
output << "M_.endo_trends(" << i + 1 << ")."
<< (is_log ? "log_growth_factor" : "growth_factor") << " = '";
growth_factor->writeJsonOutput(output, {}, {});
output << "';" << endl;
......@@ -1009,7 +1129,7 @@ DynamicModel::writeDriverOutput(ostream &output, bool compute_xrefs) const
output << (i > computed_derivs_order ? -1 : NNZDerivatives[i]) << "; ";
output << "];" << endl;
writeDriverSparseIndicesHelper<true>(output);
writeDriverSparseIndicesHelper("dynamic", output);
// Write LHS of each equation in text form
output << "M_.lhs = {" << endl;
......@@ -1020,10 +1140,15 @@ DynamicModel::writeDriverOutput(ostream &output, bool compute_xrefs) const
output << "'; " << endl;
}
output << "};" << endl;
output << "M_.dynamic_mcp_equations_reordering = [";
for (auto i : mcp_equations_reordering)
output << i + 1 << "; ";
output << "];" << endl;
}
void
DynamicModel::runTrendTest(const eval_context_t &eval_context)
DynamicModel::runTrendTest(const eval_context_t& eval_context)
{
computeDerivIDs();
testTrendDerivativesEqualToZero(eval_context);
......@@ -1032,12 +1157,12 @@ DynamicModel::runTrendTest(const eval_context_t &eval_context)
void
DynamicModel::updateVarAndTrendModel() const
{
for (bool var : { true, false })
for (bool var : {true, false})
{
map<string, vector<optional<int>>> trend_varr;
map<string, vector<set<pair<int, int>>>> rhsr;
for (const auto &[model_name, eqns] : (var ? var_model_table.getEqNums()
: trend_component_model_table.getEqNums()))
for (const auto& [model_name, eqns] :
(var ? var_model_table.getEqNums() : trend_component_model_table.getEqNums()))
{
vector<int> lhs, trend_lhs;
vector<optional<int>> trend_var;
......@@ -1076,25 +1201,28 @@ DynamicModel::updateVarAndTrendModel() const
catch (...)
{
}
optional<int> trend_var_symb_id = equations[eqn]->arg2->findTargetVariable(lhs_symb_id);
optional<int> trend_var_symb_id
= equations[eqn]->arg2->findTargetVariable(lhs_symb_id);
if (trend_var_symb_id)
{
if (symbol_table.isDiffAuxiliaryVariable(*trend_var_symb_id))
try
{
trend_var_symb_id = symbol_table.getOrigSymbIdForAuxVar(*trend_var_symb_id);
trend_var_symb_id
= symbol_table.getOrigSymbIdForAuxVar(*trend_var_symb_id);
}
catch (...)
{
}
if (find(trend_lhs.begin(), trend_lhs.end(), *trend_var_symb_id) == trend_lhs.end())
if (ranges::find(trend_lhs, *trend_var_symb_id) == trend_lhs.end())
{
cerr << "ERROR: trend found in trend_component equation #" << eqn << " ("
<< symbol_table.getName(*trend_var_symb_id) << ") does not correspond to a trend equation" << endl;
<< symbol_table.getName(*trend_var_symb_id)
<< ") does not correspond to a trend equation" << endl;
exit(EXIT_FAILURE);
}
}
trend_var.push_back(move(trend_var_symb_id));
trend_var.push_back(trend_var_symb_id);
}
}
......@@ -1120,16 +1248,16 @@ DynamicModel::fillVarModelTable() const
map<string, vector<expr_t>> lhs_expr_tr;
map<string, vector<set<pair<int, int>>>> rhsr;
for (const auto &[model_name, eqtags] : var_model_table.getEqTags())
for (const auto& [model_name, eqtags] : var_model_table.getEqTags())
{
vector<int> eqnumber, lhs;
vector<expr_t> lhs_expr_t;
vector<set<pair<int, int>>> rhs;
for (const auto &eqtag : eqtags)
for (const auto& eqtag : eqtags)
{
set<pair<int, int>> lhs_set, lhs_tmp_set, rhs_set;
optional<int> eqn { equation_tags.getEqnByTag("name", eqtag) };
optional<int> eqn {equation_tags.getEqnByTag("name", eqtag)};
if (!eqn)
{
cerr << "ERROR: no equation is named '" << eqtag << "'" << endl;
......@@ -1183,7 +1311,7 @@ DynamicModel::fillVarModelTableFromOrigModel() const
map<string, vector<int>> lags;
map<string, vector<optional<int>>> orig_diff_var;
map<string, vector<bool>> diff;
for (const auto &[model_name, eqns] : var_model_table.getEqNums())
for (const auto& [model_name, eqns] : var_model_table.getEqNums())
{
set<expr_t> lhs;
vector<optional<int>> orig_diff_var_vec;
......@@ -1191,37 +1319,41 @@ DynamicModel::fillVarModelTableFromOrigModel() const
for (auto eqn : eqns)
{
// Perform some sanity checks on the RHS
optional<string> eqtag { equation_tags.getTagValueByEqnAndKey(eqn, "name") };
optional<string> eqtag {equation_tags.getTagValueByEqnAndKey(eqn, "name")};
set<pair<int, int>> rhs_endo_set, rhs_exo_set;
equations[eqn]->arg2->collectDynamicVariables(SymbolType::endogenous, rhs_endo_set);
for (const auto &[symb_id, lag] : rhs_endo_set)
for (const auto& [symb_id, lag] : rhs_endo_set)
if (lag > 0)
{
cerr << "ERROR: in Equation " << eqtag.value_or(to_string(eqn+1))
<< ". A VAR model may not have leaded endogenous variables on the RHS. " << endl;
cerr << "ERROR: in Equation " << eqtag.value_or(to_string(eqn + 1))
<< ". A VAR model may not have leaded endogenous variables on the RHS. "
<< endl;
exit(EXIT_FAILURE);
}
else if (!var_model_table.getStructural().at(model_name) && lag == 0)
{
cerr << "ERROR: in Equation " << eqtag.value_or(to_string(eqn+1))
<< ". A non-structural VAR model may not have contemporaneous endogenous variables on the RHS. " << endl;
cerr << "ERROR: in Equation " << eqtag.value_or(to_string(eqn + 1))
<< ". A non-structural VAR model may not have contemporaneous endogenous "
"variables on the RHS. "
<< endl;
exit(EXIT_FAILURE);
}
equations[eqn]->arg2->collectDynamicVariables(SymbolType::exogenous, rhs_exo_set);
for (const auto &[symb_id, lag] : rhs_exo_set)
for (const auto& [symb_id, lag] : rhs_exo_set)
if (lag != 0)
{
cerr << "ERROR: in Equation " << eqtag.value_or(to_string(eqn+1))
<< ". A VAR model may not have lagged or leaded exogenous variables on the RHS. " << endl;
cerr << "ERROR: in Equation " << eqtag.value_or(to_string(eqn + 1))
<< ". A VAR model may not have lagged or leaded exogenous variables on the "
"RHS. "
<< endl;
exit(EXIT_FAILURE);
}
// save lhs variables
equations[eqn]->arg1->collectVARLHSVariable(lhs);
equations[eqn]->arg1->countDiffs() > 0 ?
diff_vec.push_back(true) : diff_vec.push_back(false);
diff_vec.push_back(equations[eqn]->arg1->countDiffs() > 0);
if (diff_vec.back())
{
set<pair<int, int>> diff_set;
......@@ -1233,11 +1365,10 @@ DynamicModel::fillVarModelTableFromOrigModel() const
<< eqn << endl;
exit(EXIT_FAILURE);
}
orig_diff_var_vec.push_back(diff_set.begin()->first);
orig_diff_var_vec.emplace_back(diff_set.begin()->first);
}
else
orig_diff_var_vec.push_back(nullopt);
orig_diff_var_vec.emplace_back(nullopt);
}
if (eqns.size() != lhs.size())
......@@ -1247,7 +1378,7 @@ DynamicModel::fillVarModelTableFromOrigModel() const
}
set<expr_t> lhs_lag_equiv;
for (const auto &lh : lhs)
for (const auto& lh : lhs)
{
auto [lag_equiv_repr, index] = lh->getLagEquivalenceClass();
lhs_lag_equiv.insert(lag_equiv_repr);
......@@ -1271,15 +1402,14 @@ DynamicModel::getVARDerivIDs(int lhs_symb_id, int lead_lag) const
vector<int> deriv_ids;
// First directly look for the variable itself
if (auto it = deriv_id_table.find({ lhs_symb_id, lead_lag });
it != deriv_id_table.end())
if (auto it = deriv_id_table.find({lhs_symb_id, lead_lag}); it != deriv_id_table.end())
deriv_ids.push_back(it->second);
// Then go through auxiliary variables
for (auto &[key, deriv_id2] : deriv_id_table)
for (auto& [key, deriv_id2] : deriv_id_table)
{
auto [symb_id2, lead_lag2] = key;
const AuxVarInfo *avi;
const AuxVarInfo* avi;
try
{
avi = &symbol_table.getAuxVarInfo(symb_id2);
......@@ -1323,16 +1453,16 @@ DynamicModel::fillVarModelTableMatrices()
map<string, map<tuple<int, int, int>, expr_t>> AR;
map<string, map<tuple<int, int>, expr_t>> A0;
map<string, map<int, expr_t>> constants;
for (const auto &[model_name, eqns] : var_model_table.getEqNums())
for (const auto& [model_name, eqns] : var_model_table.getEqNums())
{
const vector<int> &lhs = var_model_table.getLhs(model_name);
const vector<int>& lhs = var_model_table.getLhs(model_name);
int max_lag = var_model_table.getMaxLag(model_name);
for (auto lhs_symb_id : lhs)
{
// Fill autoregressive matrix (AR)
for (int lag = 1; lag <= max_lag; lag++)
{
vector<int> deriv_ids = getVARDerivIDs(lhs_symb_id, -lag);;
vector<int> deriv_ids = getVARDerivIDs(lhs_symb_id, -lag);
for (size_t i = 0; i < eqns.size(); i++)
{
expr_t d = Zero;
......@@ -1342,11 +1472,14 @@ DynamicModel::fillVarModelTableMatrices()
{
if (!d->isConstant())
{
cerr << "ERROR: Equation " << equation_tags.getTagValueByEqnAndKey(eqns[i], "name").value_or(to_string(eqns[i]+1)) << " is not linear" << endl;
cerr << "ERROR: Equation "
<< equation_tags.getTagValueByEqnAndKey(eqns[i], "name")
.value_or(to_string(eqns[i] + 1))
<< " is not linear" << endl;
exit(EXIT_FAILURE);
}
AR[model_name][{ i, lag, lhs_symb_id }] = AddUMinus(d);
AR[model_name][{i, lag, lhs_symb_id}] = AddUMinus(d);
}
}
}
......@@ -1360,11 +1493,14 @@ DynamicModel::fillVarModelTableMatrices()
{
if (!d->isConstant())
{
cerr << "ERROR: Equation " << equation_tags.getTagValueByEqnAndKey(eqns[i], "name").value_or(to_string(eqns[i]+1)) << " is not linear" << endl;
cerr << "ERROR: Equation "
<< equation_tags.getTagValueByEqnAndKey(eqns[i], "name")
.value_or(to_string(eqns[i] + 1))
<< " is not linear" << endl;
exit(EXIT_FAILURE);
}
A0[model_name][{ i, lhs_symb_id }] = d;
A0[model_name][{i, lhs_symb_id}] = d;
}
}
......@@ -1374,8 +1510,10 @@ DynamicModel::fillVarModelTableMatrices()
for (size_t i = 0; i < eqns.size(); i++)
{
auto rhs = equations[eqns[i]]->arg2;
map<VariableNode *, NumConstNode *> subst_table;
auto rhs_vars = var_model_table.getRhs(model_name)[i]; // All the (transformed) endogenous on RHS, as computed by updateVarAndTrendModel()
map<VariableNode*, NumConstNode*> subst_table;
auto rhs_vars = var_model_table.getRhs(
model_name)[i]; // All the (transformed) endogenous on RHS, as computed by
// updateVarAndTrendModel()
rhs->collectDynamicVariables(SymbolType::exogenous, rhs_vars); // Add exos
for (auto [symb_id, lag] : rhs_vars)
subst_table[AddVariable(symb_id, lag)] = Zero;
......@@ -1394,14 +1532,13 @@ map<string, map<tuple<int, int, int>, expr_t>>
DynamicModel::computeAutoregressiveMatrices() const
{
map<string, map<tuple<int, int, int>, expr_t>> ARr;
for (const auto &[model_name, eqns] : trend_component_model_table.getNonTargetEqNums())
for (const auto& [model_name, eqns] : trend_component_model_table.getNonTargetEqNums())
{
map<tuple<int, int, int>, expr_t> AR;
const vector<int> &lhs = trend_component_model_table.getNonTargetLhs(model_name);
for (int i{0};
auto eqn : eqns)
const vector<int>& lhs = trend_component_model_table.getNonTargetLhs(model_name);
for (int i {0}; auto eqn : eqns)
{
auto bopn = dynamic_cast<BinaryOpNode *>(equations[eqn]->arg2);
auto bopn = dynamic_cast<BinaryOpNode*>(equations[eqn]->arg2);
bopn->fillAutoregressiveRow(i++, lhs, AR);
}
ARr[model_name] = AR;
......@@ -1415,12 +1552,12 @@ DynamicModel::fillTrendComponentModelTable() const
map<string, vector<int>> eqnums, trend_eqnums, lhsr;
map<string, vector<expr_t>> lhs_expr_tr;
map<string, vector<set<pair<int, int>>>> rhsr;
for (const auto &[model_name, eqtags] : trend_component_model_table.getTargetEqTags())
for (const auto& [model_name, eqtags] : trend_component_model_table.getTargetEqTags())
{
vector<int> trend_eqnumber;
for (const auto &eqtag : eqtags)
for (const auto& eqtag : eqtags)
{
optional<int> eqn { equation_tags.getEqnByTag("name", eqtag) };
optional<int> eqn {equation_tags.getEqnByTag("name", eqtag)};
if (!eqn)
{
cerr << "ERROR: no equation is named '" << eqtag << "'" << endl;
......@@ -1431,16 +1568,16 @@ DynamicModel::fillTrendComponentModelTable() const
trend_eqnums[model_name] = trend_eqnumber;
}
for (const auto &[model_name, eqtags] : trend_component_model_table.getEqTags())
for (const auto& [model_name, eqtags] : trend_component_model_table.getEqTags())
{
vector<int> eqnumber, lhs;
vector<expr_t> lhs_expr_t;
vector<set<pair<int, int>>> rhs;
for (const auto &eqtag : eqtags)
for (const auto& eqtag : eqtags)
{
set<pair<int, int>> lhs_set, lhs_tmp_set, rhs_set;
optional<int> eqn { equation_tags.getEqnByTag("name", eqtag) };
optional<int> eqn {equation_tags.getEqnByTag("name", eqtag)};
if (!eqn)
{
cerr << "ERROR: no equation is named '" << eqtag << "'" << endl;
......@@ -1454,7 +1591,8 @@ DynamicModel::fillTrendComponentModelTable() const
if (lhs_set.size() != 1 || !lhs_tmp_set.empty())
{
cerr << "ERROR: in Equation " << eqtag
<< ". A trend component model may only have one endogenous variable on the LHS. " << endl;
<< ". A trend component model may only have one endogenous variable on the LHS. "
<< endl;
exit(EXIT_FAILURE);
}
......@@ -1462,7 +1600,8 @@ DynamicModel::fillTrendComponentModelTable() const
if (itlhs->second != 0)
{
cerr << "ERROR: in Equation " << eqtag
<< ". The variable on the LHS of a trend component model may not appear with a lead or a lag. "
<< ". The variable on the LHS of a trend component model may not appear with a "
"lead or a lag. "
<< endl;
exit(EXIT_FAILURE);
}
......@@ -1483,15 +1622,16 @@ DynamicModel::fillTrendComponentModelTable() const
rhsr[model_name] = rhs;
}
trend_component_model_table.setRhs(move(rhsr));
trend_component_model_table.setVals(move(eqnums), move(trend_eqnums), move(lhsr), move(lhs_expr_tr));
trend_component_model_table.setVals(move(eqnums), move(trend_eqnums), move(lhsr),
move(lhs_expr_tr));
}
pair<map<string, map<tuple<int, int>, expr_t>>, map<string, map<tuple<int, int>, expr_t>>>
DynamicModel::computeErrorComponentMatrices(const ExprNode::subst_table_t &diff_subst_table) const
DynamicModel::computeErrorComponentMatrices(const ExprNode::subst_table_t& diff_subst_table) const
{
map<string, map<tuple<int, int>, expr_t>> A0r, A0starr;
for (const auto &[model_name, eqns] : trend_component_model_table.getEqNums())
for (const auto& [model_name, eqns] : trend_component_model_table.getEqNums())
{
map<tuple<int, int>, expr_t> A0, A0star;
vector<int> target_lhs = trend_component_model_table.getTargetLhs(model_name);
......@@ -1499,23 +1639,22 @@ DynamicModel::computeErrorComponentMatrices(const ExprNode::subst_table_t &diff_
vector<int> undiff_nontarget_lhs = getUndiffLHSForPac(model_name, diff_subst_table);
vector<int> parsed_undiff_nontarget_lhs;
for (int i{0};
auto eqn : eqns)
for (int i {0}; auto eqn : eqns)
{
if (find(nontarget_eqnums.begin(), nontarget_eqnums.end(), eqn) != nontarget_eqnums.end())
if (ranges::find(nontarget_eqnums, eqn) != nontarget_eqnums.end())
parsed_undiff_nontarget_lhs.push_back(undiff_nontarget_lhs.at(i));
i++;
}
for (int i{0};
auto eqn : eqns)
if (find(nontarget_eqnums.begin(), nontarget_eqnums.end(), eqn) != nontarget_eqnums.end())
equations[eqn]->arg2->fillErrorCorrectionRow(i++, parsed_undiff_nontarget_lhs, target_lhs, A0, A0star);
for (int i {0}; auto eqn : eqns)
if (ranges::find(nontarget_eqnums, eqn) != nontarget_eqnums.end())
equations[eqn]->arg2->fillErrorCorrectionRow(i++, parsed_undiff_nontarget_lhs, target_lhs,
A0, A0star);
A0r[model_name] = A0;
A0starr[model_name] = A0star;
}
return { A0r, A0starr };
return {A0r, A0starr};
}
void
......@@ -1524,7 +1663,7 @@ DynamicModel::fillTrendComponentModelTableFromOrigModel() const
map<string, vector<int>> lags;
map<string, vector<optional<int>>> orig_diff_var;
map<string, vector<bool>> diff;
for (const auto &[model_name, eqns] : trend_component_model_table.getEqNums())
for (const auto& [model_name, eqns] : trend_component_model_table.getEqNums())
{
set<expr_t> lhs;
vector<optional<int>> orig_diff_var_vec;
......@@ -1532,32 +1671,33 @@ DynamicModel::fillTrendComponentModelTableFromOrigModel() const
for (auto eqn : eqns)
{
// Perform some sanity checks on the RHS
optional<string> eqtag { equation_tags.getTagValueByEqnAndKey(eqn, "name") };
optional<string> eqtag {equation_tags.getTagValueByEqnAndKey(eqn, "name")};
set<pair<int, int>> rhs_endo_set, rhs_exo_set;
equations[eqn]->arg2->collectDynamicVariables(SymbolType::endogenous, rhs_endo_set);
for (const auto &[symb_id, lag] : rhs_endo_set)
for (const auto& [symb_id, lag] : rhs_endo_set)
if (lag >= 0)
{
cerr << "ERROR: in Equation " << eqtag.value_or(to_string(eqn+1))
<< ". A trend component model may not have leaded or contemporaneous endogenous variables on the RHS. " << endl;
cerr << "ERROR: in Equation " << eqtag.value_or(to_string(eqn + 1))
<< ". A trend component model may not have leaded or contemporaneous "
"endogenous variables on the RHS. "
<< endl;
exit(EXIT_FAILURE);
}
equations[eqn]->arg2->collectDynamicVariables(SymbolType::exogenous, rhs_exo_set);
for (const auto &[symb_id, lag] : rhs_exo_set)
for (const auto& [symb_id, lag] : rhs_exo_set)
if (lag != 0)
{
cerr << "ERROR: in Equation " << eqtag.value_or(to_string(eqn+1))
<< ". A trend component model may not have lagged or leaded exogenous variables on the RHS. " << endl;
cerr << "ERROR: in Equation " << eqtag.value_or(to_string(eqn + 1))
<< ". A trend component model may not have lagged or leaded exogenous "
"variables on the RHS. "
<< endl;
exit(EXIT_FAILURE);
}
// save lhs variables
equations[eqn]->arg1->collectVARLHSVariable(lhs);
if (equations[eqn]->arg1->countDiffs() > 0)
diff_vec.push_back(true);
else
diff_vec.push_back(false);
diff_vec.push_back(equations[eqn]->arg1->countDiffs() > 0);
if (diff_vec.back())
{
set<pair<int, int>> diff_set;
......@@ -1569,10 +1709,10 @@ DynamicModel::fillTrendComponentModelTableFromOrigModel() const
<< eqn << endl;
exit(EXIT_FAILURE);
}
orig_diff_var_vec.push_back(diff_set.begin()->first);
orig_diff_var_vec.emplace_back(diff_set.begin()->first);
}
else
orig_diff_var_vec.push_back(nullopt);
orig_diff_var_vec.emplace_back(nullopt);
}
if (eqns.size() != lhs.size())
......@@ -1582,7 +1722,7 @@ DynamicModel::fillTrendComponentModelTableFromOrigModel() const
}
set<expr_t> lhs_lag_equiv;
for (const auto &lh : lhs)
for (const auto& lh : lhs)
{
auto [lag_equiv_repr, index] = lh->getLagEquivalenceClass();
lhs_lag_equiv.insert(lag_equiv_repr);
......@@ -1601,7 +1741,8 @@ DynamicModel::fillTrendComponentModelTableFromOrigModel() const
}
void
DynamicModel::fillTrendComponentModelTableAREC(const ExprNode::subst_table_t &diff_subst_table) const
DynamicModel::fillTrendComponentModelTableAREC(
const ExprNode::subst_table_t& diff_subst_table) const
{
auto ARr = computeAutoregressiveMatrices();
trend_component_model_table.setAR(move(ARr));
......@@ -1610,8 +1751,8 @@ DynamicModel::fillTrendComponentModelTableAREC(const ExprNode::subst_table_t &di
}
vector<int>
DynamicModel::getUndiffLHSForPac(const string &aux_model_name,
const ExprNode::subst_table_t &diff_subst_table) const
DynamicModel::getUndiffLHSForPac(const string& aux_model_name,
const ExprNode::subst_table_t& diff_subst_table) const
{
vector<expr_t> lhs_expr_t = trend_component_model_table.getLhsExprT(aux_model_name);
vector<int> lhs = trend_component_model_table.getLhs(aux_model_name);
......@@ -1621,7 +1762,7 @@ DynamicModel::getUndiffLHSForPac(const string &aux_model_name,
for (auto eqn : nontrend_eqnums)
{
auto i = distance(eqnumber.begin(), find(eqnumber.begin(), eqnumber.end(), eqn));
auto i = distance(eqnumber.begin(), ranges::find(eqnumber, eqn));
if (eqnumber[i] != eqn)
{
......@@ -1629,7 +1770,7 @@ DynamicModel::getUndiffLHSForPac(const string &aux_model_name,
exit(EXIT_FAILURE);
}
if (diff.at(i) != true)
if (!diff.at(i))
{
cerr << "ERROR: the variable on the LHS of equation #" << eqn
<< " does not have the diff operator applied to it yet you are trying to undiff it."
......@@ -1637,10 +1778,9 @@ DynamicModel::getUndiffLHSForPac(const string &aux_model_name,
exit(EXIT_FAILURE);
}
bool printerr = false;
expr_t node = nullptr;
expr_t aux_var = lhs_expr_t.at(i);
for (const auto &it : diff_subst_table)
for (const auto& it : diff_subst_table)
if (it.second == aux_var)
{
node = const_cast<expr_t>(it.first);
......@@ -1654,39 +1794,41 @@ DynamicModel::getUndiffLHSForPac(const string &aux_model_name,
}
node = node->undiff();
auto it1 = diff_subst_table.find(node);
if (it1 == diff_subst_table.end())
printerr = true;
if (printerr)
if (auto it1 = diff_subst_table.find(node); it1 == diff_subst_table.end())
{ // we have undiffed something like diff(x), hence x is not in diff_subst_table
lhs_expr_t.at(i) = node;
lhs.at(i) = dynamic_cast<VariableNode *>(node)->symb_id;
lhs.at(i) = dynamic_cast<VariableNode*>(node)->symb_id;
}
else
{
lhs_expr_t.at(i) = const_cast<expr_t>(it1->first);
lhs.at(i) = const_cast<VariableNode *>(it1->second)->symb_id;
lhs.at(i) = const_cast<VariableNode*>(it1->second)->symb_id;
}
}
return lhs;
}
void
DynamicModel::analyzePacEquationStructure(const string &name, map<string, string> &pac_eq_name, PacModelTable::equation_info_t &pac_equation_info)
DynamicModel::analyzePacEquationStructure(const string& name, map<string, string>& pac_eq_name,
PacModelTable::equation_info_t& pac_equation_info)
{
for (auto &equation : equations)
for (auto& equation : equations)
if (equation->containsPacExpectation(name))
{
if (!pac_eq_name[name].empty())
if (pac_eq_name.contains(name))
{
cerr << "It is not possible to use 'pac_expectation(" << name << ")' in several equations." << endl;
cerr << "It is not possible to use 'pac_expectation(" << name
<< ")' in several equations." << endl;
exit(EXIT_FAILURE);
}
optional<string> eqn { equation_tags.getTagValueByEqnAndKey(&equation - &equations[0], "name") };
optional<string> eqn {
equation_tags.getTagValueByEqnAndKey(&equation - &equations[0], "name")};
if (!eqn)
{
cerr << "Every equation with a 'pac_expectation' operator must have been assigned an equation tag name" << endl;
cerr << "Every equation with a 'pac_expectation' operator must have been assigned an "
"equation tag name"
<< endl;
exit(EXIT_FAILURE);
}
pac_eq_name[name] = *eqn;
......@@ -1705,44 +1847,49 @@ DynamicModel::analyzePacEquationStructure(const string &name, map<string, string
{
}
auto arg2 = dynamic_cast<BinaryOpNode *>(equation->arg2);
auto arg2 = dynamic_cast<BinaryOpNode*>(equation->arg2);
if (!arg2)
{
cerr << "Pac equation in incorrect format" << endl;
exit(EXIT_FAILURE);
}
auto [optim_share_index, optim_part, non_optim_part, additive_part]
= arg2->getPacOptimizingShareAndExprNodes(lhs_orig_symb_id);
= arg2->getPacOptimizingShareAndExprNodes(lhs_orig_symb_id);
pair<int, vector<tuple<int, bool, int>>> ec_params_and_vars;
vector<tuple<optional<int>, optional<int>, int>> ar_params_and_vars;
vector<tuple<int, int, optional<int>, double>> non_optim_vars_params_and_constants, optim_additive_vars_params_and_constants, additive_vars_params_and_constants;
vector<tuple<int, int, optional<int>, double>> non_optim_vars_params_and_constants,
optim_additive_vars_params_and_constants, additive_vars_params_and_constants;
if (!optim_part)
{
auto bopn = dynamic_cast<BinaryOpNode *>(equation->arg2);
auto bopn = dynamic_cast<BinaryOpNode*>(equation->arg2);
if (!bopn)
{
cerr << "Error in PAC equation" << endl;
exit(EXIT_FAILURE);
}
bopn->getPacAREC(lhs_symb_id, lhs_orig_symb_id, ec_params_and_vars, ar_params_and_vars, additive_vars_params_and_constants);
bopn->getPacAREC(lhs_symb_id, lhs_orig_symb_id, ec_params_and_vars, ar_params_and_vars,
additive_vars_params_and_constants);
}
else
{
auto bopn = dynamic_cast<BinaryOpNode *>(optim_part);
auto bopn = dynamic_cast<BinaryOpNode*>(optim_part);
if (!bopn)
{
cerr << "Error in PAC equation" << endl;
exit(EXIT_FAILURE);
}
bopn->getPacAREC(lhs_symb_id, lhs_orig_symb_id, ec_params_and_vars, ar_params_and_vars, optim_additive_vars_params_and_constants);
bopn->getPacAREC(lhs_symb_id, lhs_orig_symb_id, ec_params_and_vars, ar_params_and_vars,
optim_additive_vars_params_and_constants);
try
{
non_optim_vars_params_and_constants = non_optim_part->matchLinearCombinationOfVariables();
non_optim_vars_params_and_constants
= non_optim_part->matchLinearCombinationOfVariables();
if (additive_part)
additive_vars_params_and_constants = additive_part->matchLinearCombinationOfVariables();
additive_vars_params_and_constants
= additive_part->matchLinearCombinationOfVariables();
}
catch (ExprNode::MatchFailureException &e)
catch (ExprNode::MatchFailureException& e)
{
cerr << "Error in parsing non-optimizing agents or additive part of PAC equation: "
<< e.message << endl;
......@@ -1760,16 +1907,25 @@ DynamicModel::analyzePacEquationStructure(const string &name, map<string, string
cerr << "analyzePacEquationStructure: error obtaining RHS parameters." << endl;
exit(EXIT_FAILURE);
}
pac_equation_info[name] = { lhs, move(optim_share_index),
move(ar_params_and_vars), move(ec_params_and_vars),
move(non_optim_vars_params_and_constants),
move(additive_vars_params_and_constants),
move(optim_additive_vars_params_and_constants) };
pac_equation_info[name] = {lhs,
optim_share_index,
move(ar_params_and_vars),
move(ec_params_and_vars),
move(non_optim_vars_params_and_constants),
move(additive_vars_params_and_constants),
move(optim_additive_vars_params_and_constants)};
}
if (!pac_eq_name.contains(name))
{
cerr << "ERROR: the model does not contain the 'pac_expectation(" << name << ")' operator."
<< endl;
exit(EXIT_FAILURE);
}
}
int
DynamicModel::getPacTargetSymbId(const string &pac_model_name) const
DynamicModel::getPacTargetSymbId(const string& pac_model_name) const
{
for (auto equation : equations)
if (equation->containsPacExpectation(pac_model_name))
......@@ -1777,16 +1933,17 @@ DynamicModel::getPacTargetSymbId(const string &pac_model_name) const
set<pair<int, int>> lhss;
equation->arg1->collectDynamicVariables(SymbolType::endogenous, lhss);
if (lhss.size() != 1)
throw PacTargetNotIdentifiedException{pac_model_name, "LHS must contain a single endogenous"};
throw PacTargetNotIdentifiedException {pac_model_name,
"LHS must contain a single endogenous"};
int lhs_symb_id = lhss.begin()->first;
if (!symbol_table.isDiffAuxiliaryVariable(lhs_symb_id))
throw PacTargetNotIdentifiedException{pac_model_name, "LHS must be a diff operator"};
throw PacTargetNotIdentifiedException {pac_model_name, "LHS must be a diff operator"};
int undiff_lhs_symb_id = symbol_table.getOrigSymbIdForAuxVar(lhs_symb_id);
auto barg2 = dynamic_cast<BinaryOpNode *>(equation->arg2);
auto barg2 = dynamic_cast<BinaryOpNode*>(equation->arg2);
if (!barg2)
throw PacTargetNotIdentifiedException{pac_model_name, "RHS must be a binary operator"};
throw PacTargetNotIdentifiedException {pac_model_name, "RHS must be a binary operator"};
auto [optim_share_index, optim_part, non_optim_part, additive_part]
= barg2->getPacOptimizingShareAndExprNodes(undiff_lhs_symb_id);
= barg2->getPacOptimizingShareAndExprNodes(undiff_lhs_symb_id);
/* If there is an optimization part, restrict the search to that part,
since it contains the MCE . */
expr_t mce = optim_part ? optim_part : equation->arg2;
......@@ -1796,34 +1953,33 @@ DynamicModel::getPacTargetSymbId(const string &pac_model_name) const
for (auto [term, sign] : terms)
try
{
auto [param_id, target_id] = term->matchParamTimesTargetMinusVariable(undiff_lhs_symb_id);
auto [param_id, target_id]
= term->matchParamTimesTargetMinusVariable(undiff_lhs_symb_id);
return target_id;
}
catch (ExprNode::MatchFailureException &)
catch (ExprNode::MatchFailureException&)
{
}
throw PacTargetNotIdentifiedException{pac_model_name, "No term of the form parameter*(target-LHS_level)"};
throw PacTargetNotIdentifiedException {pac_model_name,
"No term of the form parameter*(target-LHS_level)"};
}
throw PacTargetNotIdentifiedException{pac_model_name, "No equation with the corresponding pac_expectation operator"};
throw PacTargetNotIdentifiedException {
pac_model_name, "No equation with the corresponding pac_expectation operator"};
}
void
DynamicModel::computePacModelConsistentExpectationSubstitution(const string &name,
int discount_symb_id,
int pac_eq_max_lag,
expr_t growth_correction_term,
string auxname,
ExprNode::subst_table_t &diff_subst_table,
map<string, int> &pac_aux_var_symb_ids,
map<string, vector<int>> &pac_aux_param_symb_ids,
map<string, expr_t> &pac_expectation_substitution)
DynamicModel::computePacModelConsistentExpectationSubstitution(
const string& name, int discount_symb_id, int pac_eq_max_lag, expr_t growth_correction_term,
string auxname, ExprNode::subst_table_t& diff_subst_table,
map<string, int>& pac_aux_var_symb_ids, map<string, vector<int>>& pac_aux_param_symb_ids,
map<string, expr_t>& pac_expectation_substitution)
{
int pac_target_symb_id;
try
{
pac_target_symb_id = getPacTargetSymbId(name);
}
catch (PacTargetNotIdentifiedException &e)
catch (PacTargetNotIdentifiedException& e)
{
cerr << "Can't identify target for PAC model " << name << ": " << e.message;
exit(EXIT_FAILURE);
......@@ -1839,7 +1995,7 @@ DynamicModel::computePacModelConsistentExpectationSubstitution(const string &nam
expr_t A = One;
expr_t fp = Zero;
expr_t beta = AddVariable(discount_symb_id);
for (int i = 1; i <= pac_eq_max_lag+1; i++)
for (int i = 1; i <= pac_eq_max_lag + 1; i++)
{
string param_name = "mce_alpha_" + name + "_" + to_string(i);
try
......@@ -1847,53 +2003,51 @@ DynamicModel::computePacModelConsistentExpectationSubstitution(const string &nam
int alpha_i_symb_id = symbol_table.addSymbol(param_name, SymbolType::parameter);
pac_aux_param_symb_ids[name].push_back(alpha_i_symb_id);
A = AddPlus(A, AddVariable(alpha_i_symb_id));
fp = AddPlus(fp,
AddTimes(AddTimes(AddVariable(alpha_i_symb_id),
AddPower(beta, AddPossiblyNegativeConstant(i))),
AddVariable(mce_z1_symb_id, i)));
fp = AddPlus(fp, AddTimes(AddTimes(AddVariable(alpha_i_symb_id),
AddPower(beta, AddPossiblyNegativeConstant(i))),
AddVariable(mce_z1_symb_id, i)));
}
catch (SymbolTable::AlreadyDeclaredException &e)
catch (SymbolTable::AlreadyDeclaredException& e)
{
cerr << "The variable/parameter '" << param_name << "' conflicts with a parameter that will be generated for the '" << name << "' PAC model. Please rename it." << endl;
cerr << "The variable/parameter '" << param_name
<< "' conflicts with a parameter that will be generated for the '" << name
<< "' PAC model. Please rename it." << endl;
exit(EXIT_FAILURE);
}
}
// Add diff nodes and eqs for pac_target_symb_id
const VariableNode *target_base_diff_node;
auto create_target_lag = [&](int lag)
{
const VariableNode* target_base_diff_node;
auto create_target_lag = [&](int lag) {
if (symbol_table.isAuxiliaryVariable(pac_target_symb_id))
// We know it is a log, see ExprNode::matchParamTimesTargetMinusVariable()
return AddLog(AddVariable(symbol_table.getOrigSymbIdForAuxVar(pac_target_symb_id), lag));
else
return dynamic_cast<ExprNode *>(AddVariable(pac_target_symb_id, lag));
return dynamic_cast<ExprNode*>(AddVariable(pac_target_symb_id, lag));
};
expr_t diff_node_to_search = AddDiff(create_target_lag(0));
if (auto sit = diff_subst_table.find(diff_node_to_search);
sit != diff_subst_table.end())
if (auto sit = diff_subst_table.find(diff_node_to_search); sit != diff_subst_table.end())
target_base_diff_node = sit->second;
else
{
int symb_id = symbol_table.addDiffAuxiliaryVar(diff_node_to_search->idx, diff_node_to_search);
target_base_diff_node = AddVariable(symb_id);
auto neweq = AddEqual(const_cast<VariableNode *>(target_base_diff_node),
AddMinus(create_target_lag(0),
create_target_lag(-1)));
auto neweq = AddEqual(const_cast<VariableNode*>(target_base_diff_node),
AddMinus(create_target_lag(0), create_target_lag(-1)));
addEquation(neweq, nullopt);
addAuxEquation(neweq);
neqs++;
}
map<int, VariableNode *> target_aux_var_to_add;
const VariableNode *last_aux_var = target_base_diff_node;
map<int, VariableNode*> target_aux_var_to_add;
const VariableNode* last_aux_var = target_base_diff_node;
for (int i = 1; i <= pac_eq_max_lag; i++, neqs++)
{
expr_t this_diff_node = AddDiff(create_target_lag(i));
int symb_id = symbol_table.addDiffLeadAuxiliaryVar(this_diff_node->idx, this_diff_node,
last_aux_var->symb_id, 1);
VariableNode *current_aux_var = AddVariable(symb_id);
VariableNode* current_aux_var = AddVariable(symb_id);
auto neweq = AddEqual(current_aux_var, AddVariable(last_aux_var->symb_id, 1));
addEquation(neweq, nullopt);
addAuxEquation(neweq);
......@@ -1905,7 +2059,7 @@ DynamicModel::computePacModelConsistentExpectationSubstitution(const string &nam
for (int k = 1; k <= pac_eq_max_lag; k++)
{
expr_t ssum = Zero;
for (int j = k+1; j <= pac_eq_max_lag+1; j++)
for (int j = k + 1; j <= pac_eq_max_lag + 1; j++)
{
int alpha_j_symb_id = -1;
string param_name = "mce_alpha_" + name + "_" + to_string(j);
......@@ -1913,23 +2067,25 @@ DynamicModel::computePacModelConsistentExpectationSubstitution(const string &nam
{
alpha_j_symb_id = symbol_table.getID(param_name);
}
catch (SymbolTable::UnknownSymbolNameException &e)
catch (SymbolTable::UnknownSymbolNameException& e)
{
alpha_j_symb_id = symbol_table.addSymbol(param_name, SymbolType::parameter);
}
ssum = AddPlus(ssum,
AddTimes(AddVariable(alpha_j_symb_id), AddPower(beta, AddPossiblyNegativeConstant(j))));
ssum = AddPlus(ssum, AddTimes(AddVariable(alpha_j_symb_id),
AddPower(beta, AddPossiblyNegativeConstant(j))));
}
fs = AddPlus(fs, AddTimes(ssum, target_aux_var_to_add[k]));
}
auto neweq = AddEqual(AddVariable(mce_z1_symb_id),
AddMinus(AddTimes(A, AddMinus(const_cast<VariableNode *>(target_base_diff_node), fs)), fp));
auto neweq = AddEqual(
AddVariable(mce_z1_symb_id),
AddMinus(AddTimes(A, AddMinus(const_cast<VariableNode*>(target_base_diff_node), fs)), fp));
addEquation(neweq, nullopt);
/* This equation is not added to the list of auxiliary equations, because it
is recursive, and this would in particular break dynamic_set_auxiliary_series.m */
neqs++;
cout << "PAC Model Consistent Expectation: added " << neqs << " auxiliary variables and equations for model " << name << "." << endl;
cout << "PAC Model Consistent Expectation: added " << neqs
<< " auxiliary variables and equations for model " << name << "." << endl;
/* The growth correction term is not added to the definition of Z₁
because the latter is recursive. Rather put it at the level of the
......@@ -1938,25 +2094,219 @@ DynamicModel::computePacModelConsistentExpectationSubstitution(const string &nam
}
void
DynamicModel::computePacBackwardExpectationSubstitution(const string &name,
const vector<int> &lhs,
int max_lag,
const string &aux_model_type,
expr_t growth_correction_term,
string auxname,
map<string, int> &pac_aux_var_symb_ids,
map<string, vector<int>> &pac_aux_param_symb_ids,
map<string, expr_t> &pac_expectation_substitution)
DynamicModel::computePacModelConsistentExpectationSubstitutionWithComponents(
const string& name, int discount_symb_id, int pac_eq_max_lag,
ExprNode::subst_table_t& diff_subst_table, map<string, vector<int>>& pac_aux_param_symb_ids,
vector<PacModelTable::target_component_t>& pac_target_components,
map<string, expr_t>& pac_expectation_substitution)
{
auto create_aux_param = [&](const string& param_name) {
try
{
return symbol_table.addSymbol(param_name, SymbolType::parameter);
}
catch (SymbolTable::AlreadyDeclaredException)
{
cerr << "ERROR: the variable/parameter '" << param_name
<< "' conflicts with some auxiliary parameter that will be generated for the '" << name
<< "' PAC model. Please rename that parameter." << endl;
exit(EXIT_FAILURE);
}
};
int neqs = 0;
// At the end of this loop:
// ₘ ₘ
// A_1 = 1+∑ αᵢ = A(1) and A_beta = 1+∑ αᵢβⁱ
// ᵢ₌₁ ᵢ₌₁
expr_t A_1 = One;
expr_t A_beta = One;
expr_t beta = AddVariable(discount_symb_id);
for (int i = 1; i <= pac_eq_max_lag + 1; i++)
{
string param_name = "mce_alpha_" + name + "_" + to_string(i);
try
{
int alpha_i_symb_id = symbol_table.addSymbol(param_name, SymbolType::parameter);
pac_aux_param_symb_ids[name].push_back(alpha_i_symb_id);
A_1 = AddPlus(A_1, AddVariable(alpha_i_symb_id));
A_beta = AddPlus(A_beta, AddTimes(AddVariable(alpha_i_symb_id),
AddPower(beta, AddPossiblyNegativeConstant(i))));
}
catch (SymbolTable::AlreadyDeclaredException& e)
{
cerr << "The variable/parameter '" << param_name
<< "' conflicts with a parameter that will be generated for the '" << name
<< "' PAC model. Please rename it." << endl;
exit(EXIT_FAILURE);
}
}
auto create_target_lag = [&](int variable, int lag) {
if (symbol_table.isAuxiliaryVariable(variable))
return AddVariable(symbol_table.getOrigSymbIdForAuxVar(variable), lag);
else
return AddVariable(variable, lag);
};
expr_t substexpr = Zero;
for (int component_idx {1};
auto& [component, growth, auxname, kind, coeff, growth_neutrality_param, h_indices,
original_growth, growth_info] : pac_target_components)
{
// Create the auxiliary variable for this component
int aux_id = symbol_table.addPacExpectationAuxiliaryVar(auxname, component);
// Get the component variable id
int component_id = dynamic_cast<VariableNode*>(component)->symb_id;
// ₘ
// fp = ∑ αᵢβⁱZₜ₊ᵢ
// ᵢ₌₁
expr_t fp = Zero;
for (int i = 1; i <= pac_eq_max_lag + 1; i++)
{
int alpha_i_symb_id = -1;
string param_name = "mce_alpha_" + name + "_" + to_string(i);
try
{
alpha_i_symb_id = symbol_table.getID(param_name);
}
catch (SymbolTable::UnknownSymbolNameException& e)
{
alpha_i_symb_id = symbol_table.addSymbol(param_name, SymbolType::parameter);
}
fp = AddPlus(fp, AddTimes(AddTimes(AddVariable(alpha_i_symb_id),
AddPower(beta, AddPossiblyNegativeConstant(i))),
AddVariable(aux_id, i)));
}
if (kind != PacTargetKind::ll) // non-stationary component y¹ₜ
{
// Add diff nodes and eqs for the non-stationary component
const VariableNode* aux_target_ns_var_diff_node;
expr_t diff_node_to_search = AddDiff(create_target_lag(component_id, 0));
if (auto sit = diff_subst_table.find(diff_node_to_search); sit != diff_subst_table.end())
aux_target_ns_var_diff_node = sit->second;
else
{
int symb_id
= symbol_table.addDiffAuxiliaryVar(diff_node_to_search->idx, diff_node_to_search);
aux_target_ns_var_diff_node = AddVariable(symb_id);
auto neweq = AddEqual(const_cast<VariableNode*>(aux_target_ns_var_diff_node),
AddMinus(create_target_lag(component_id, 0),
create_target_lag(component_id, -1)));
addEquation(neweq, nullopt);
addAuxEquation(neweq);
neqs++;
}
map<int, VariableNode*> target_ns_aux_var_to_add;
const VariableNode* last_aux_var = aux_target_ns_var_diff_node;
for (int i = 1; i <= pac_eq_max_lag; i++, neqs++)
{
expr_t this_diff_node = AddDiff(create_target_lag(component_id, i));
int symb_id = symbol_table.addDiffLeadAuxiliaryVar(
this_diff_node->idx, this_diff_node, last_aux_var->symb_id, 1);
VariableNode* current_aux_var = AddVariable(symb_id);
auto neweq = AddEqual(current_aux_var, AddVariable(last_aux_var->symb_id, 1));
addEquation(neweq, nullopt);
addAuxEquation(neweq);
last_aux_var = current_aux_var;
target_ns_aux_var_to_add[i] = current_aux_var;
}
// At the end of this loop,
// ₘ₋₁ ⎛ ₘ₋₁ ⎞
// fs = ∑ ⎢ ∑ αᵢβⁱ⎥ Δy¹ₜ₊ᵢ
// ₖ₌₁ ⎝ ᵢ₌ₖ ⎠
expr_t fs = Zero;
for (int k = 1; k <= pac_eq_max_lag; k++)
{
expr_t ssum = Zero;
for (int j = k + 1; j <= pac_eq_max_lag + 1; j++)
{
int alpha_j_symb_id = -1;
string param_name = "mce_alpha_" + name + "_" + to_string(j);
try
{
alpha_j_symb_id = symbol_table.getID(param_name);
}
catch (SymbolTable::UnknownSymbolNameException& e)
{
alpha_j_symb_id = symbol_table.addSymbol(param_name, SymbolType::parameter);
}
ssum = AddPlus(ssum, AddTimes(AddVariable(alpha_j_symb_id),
AddPower(beta, AddPossiblyNegativeConstant(j))));
}
fs = AddPlus(fs, AddTimes(ssum, target_ns_aux_var_to_add[k]));
}
// Assembling the equation Zₜ¹ = A_1 ( Δyₜ¹ - fs ) - fp_1
auto neweq_1 = AddEqual(
AddVariable(aux_id),
AddMinus(
AddTimes(A_1,
AddMinus(const_cast<VariableNode*>(aux_target_ns_var_diff_node), fs)),
fp));
addEquation(neweq_1, nullopt);
neqs++;
/* This equation is not added to the list of auxiliary equations, because it
is recursive, and this would in particular break dynamic_set_auxiliary_series.m */
}
else // Stationary component yₜ⁰
{
// Assembling the equation Zₜ⁰ = A_beta A_1 yₜ⁰ - fp
auto neweq_0 = AddEqual(AddVariable(aux_id),
AddMinus(AddTimes(A_beta, AddTimes(A_1, component)), fp));
addEquation(neweq_0, nullopt);
neqs++;
/* This equation is not added to the list of auxiliary equations, because it
is recursive, and this would in particular break dynamic_set_auxiliary_series.m */
}
// If needed, add the growth neutrality correction for this component
expr_t growth_correction_term = Zero;
string name_component = name + "_component" + to_string(component_idx);
if (growth)
{
growth_neutrality_param
= create_aux_param(name_component + "_pac_growth_neutrality_correction");
growth_correction_term = AddTimes(growth, AddVariable(growth_neutrality_param));
}
else
growth_neutrality_param = -1;
substexpr = AddPlus(substexpr,
AddTimes(coeff, AddPlus(AddVariable(aux_id), growth_correction_term)));
component_idx++;
}
cout << "PAC Model Consistent Expectation: added " << neqs
<< " auxiliary variables and equations for model " << name << "." << endl;
/* The growth correction term is not added to the definition of Z₁
because the latter is recursive. Rather put it at the level of the
substition of pac_expectation operator. */
pac_expectation_substitution[name] = substexpr;
}
void
DynamicModel::computePacBackwardExpectationSubstitution(
const string& name, const vector<int>& lhs, int max_lag, const string& aux_model_type,
expr_t growth_correction_term, string auxname, map<string, int>& pac_aux_var_symb_ids,
map<string, vector<int>>& pac_aux_param_symb_ids,
map<string, expr_t>& pac_expectation_substitution)
{
auto create_aux_param = [&](const string &param_name)
{
auto create_aux_param = [&](const string& param_name) {
try
{
return symbol_table.addSymbol(param_name, SymbolType::parameter);
}
catch (SymbolTable::AlreadyDeclaredException)
{
cerr << "ERROR: the variable/parameter '" << param_name << "' conflicts with some auxiliary parameter that will be generated for the '" << name << "' PAC model. Please rename that parameter." << endl;
cerr << "ERROR: the variable/parameter '" << param_name
<< "' conflicts with some auxiliary parameter that will be generated for the '" << name
<< "' PAC model. Please rename that parameter." << endl;
exit(EXIT_FAILURE);
}
};
......@@ -1973,13 +2323,11 @@ DynamicModel::computePacBackwardExpectationSubstitution(const string &name,
for (int i = 1; i < max_lag + 1; i++)
for (auto lhsit : lhs)
{
int new_param_symb_id = create_aux_param("h_" + name + "_var_"
+ symbol_table.getName(lhsit)
int new_param_symb_id = create_aux_param("h_" + name + "_var_" + symbol_table.getName(lhsit)
+ "_lag_" + to_string(i));
pac_aux_param_symb_ids[name].push_back(new_param_symb_id);
subExpr = AddPlus(subExpr,
AddTimes(AddVariable(new_param_symb_id),
AddVariable(lhsit, -i)));
subExpr
= AddPlus(subExpr, AddTimes(AddVariable(new_param_symb_id), AddVariable(lhsit, -i)));
}
subExpr = AddPlus(subExpr, growth_correction_term);
......@@ -1995,30 +2343,30 @@ DynamicModel::computePacBackwardExpectationSubstitution(const string &name,
}
void
DynamicModel::computePacBackwardExpectationSubstitutionWithComponents(const string &name,
const vector<int> &lhs,
int max_lag,
const string &aux_model_type,
vector<PacModelTable::target_component_t> &pac_target_components,
map<string, expr_t> &pac_expectation_substitution)
DynamicModel::computePacBackwardExpectationSubstitutionWithComponents(
const string& name, const vector<int>& lhs, int max_lag, const string& aux_model_type,
vector<PacModelTable::target_component_t>& pac_target_components,
map<string, expr_t>& pac_expectation_substitution)
{
auto create_aux_param = [&](const string &param_name)
{
auto create_aux_param = [&](const string& param_name) {
try
{
return symbol_table.addSymbol(param_name, SymbolType::parameter);
}
catch (SymbolTable::AlreadyDeclaredException)
{
cerr << "ERROR: the variable/parameter '" << param_name << "' conflicts with some auxiliary parameter that will be generated for the '" << name << "' PAC model. Please rename that parameter." << endl;
cerr << "ERROR: the variable/parameter '" << param_name
<< "' conflicts with some auxiliary parameter that will be generated for the '" << name
<< "' PAC model. Please rename that parameter." << endl;
exit(EXIT_FAILURE);
}
};
expr_t substexpr = Zero;
for (int component_idx{1};
auto &[component, growth, auxname, kind, coeff, growth_neutrality_param, h_indices, original_growth, growth_info] : pac_target_components)
for (int component_idx {1};
auto& [component, growth, auxname, kind, coeff, growth_neutrality_param, h_indices,
original_growth, growth_info] : pac_target_components)
{
string name_component = name + "_component" + to_string(component_idx);
......@@ -2035,16 +2383,19 @@ DynamicModel::computePacBackwardExpectationSubstitutionWithComponents(const stri
for (int i = 1; i < max_lag + 1; i++)
for (auto lhsit : lhs)
{
int new_param_symb_id = create_aux_param("h_" + name_component + "_var_" + symbol_table.getName(lhsit) + "_lag_" + to_string(i));
int new_param_symb_id
= create_aux_param("h_" + name_component + "_var_" + symbol_table.getName(lhsit)
+ "_lag_" + to_string(i));
h_indices.push_back(new_param_symb_id);
auxdef = AddPlus(auxdef, AddTimes(AddVariable(new_param_symb_id),
AddVariable(lhsit, -i)));
auxdef
= AddPlus(auxdef, AddTimes(AddVariable(new_param_symb_id), AddVariable(lhsit, -i)));
}
// If needed, add the growth neutrality correction for this component
if (growth)
{
growth_neutrality_param = create_aux_param(name_component + "_pac_growth_neutrality_correction");
growth_neutrality_param
= create_aux_param(name_component + "_pac_growth_neutrality_correction");
auxdef = AddPlus(auxdef, AddTimes(growth, AddVariable(growth_neutrality_param)));
}
else
......@@ -2069,28 +2420,31 @@ DynamicModel::computePacBackwardExpectationSubstitutionWithComponents(const stri
}
void
DynamicModel::substitutePacExpectation(const map<string, expr_t> &pac_expectation_substitution,
const map<string, string> &pac_eq_name)
DynamicModel::substitutePacExpectation(const map<string, expr_t>& pac_expectation_substitution,
const map<string, string>& pac_eq_name)
{
for (auto &[model_name, substexpr] : pac_expectation_substitution)
for (auto& [model_name, substexpr] : pac_expectation_substitution)
{
optional<int> eq { equation_tags.getEqnByTag("name", pac_eq_name.at(model_name)) };
auto substeq = dynamic_cast<BinaryOpNode *>(equations[eq.value()]->substitutePacExpectation(model_name, substexpr));
optional<int> eq {equation_tags.getEqnByTag("name", pac_eq_name.at(model_name))};
auto substeq = dynamic_cast<BinaryOpNode*>(
equations[eq.value()]->substitutePacExpectation(model_name, substexpr));
assert(substeq);
equations[eq.value()] = substeq;
}
}
void
DynamicModel::substitutePacTargetNonstationary(const string &pac_model_name, expr_t substexpr)
DynamicModel::substitutePacTargetNonstationary(const string& pac_model_name, expr_t substexpr)
{
for (auto &eq : equations)
eq = dynamic_cast<BinaryOpNode *>(eq->substitutePacTargetNonstationary(pac_model_name, substexpr));
for (auto& eq : equations)
eq = dynamic_cast<BinaryOpNode*>(
eq->substitutePacTargetNonstationary(pac_model_name, substexpr));
}
void
DynamicModel::computingPass(int derivsOrder, int paramsDerivsOrder, const eval_context_t &eval_context,
bool no_tmp_terms, bool block, bool use_dll)
DynamicModel::computingPass(int derivsOrder, int paramsDerivsOrder,
const eval_context_t& eval_context, bool no_tmp_terms, bool block,
bool use_dll)
{
initializeVariablesAndEquations();
......@@ -2110,18 +2464,19 @@ DynamicModel::computingPass(int derivsOrder, int paramsDerivsOrder, const eval_c
We only do the check for the legacy representation, since the sparse
representation is not affected by this problem (TODO: thus the check can be
removed once the legacy representation is dropped). */
if (log2(getJacobianColsNbr(false))*derivsOrder >= numeric_limits<int>::digits)
if (log2(getJacobianColsNbr(false)) * derivsOrder >= numeric_limits<int>::digits)
{
cerr << "ERROR: The derivatives matrix of the " << modelClassName() << " is too large. Please decrease the approximation order." << endl;
cerr << "ERROR: The derivatives matrix of the " << modelClassName()
<< " is too large. Please decrease the approximation order." << endl;
exit(EXIT_FAILURE);
}
// Compute derivatives w.r. to all endogenous, exogenous and exogenous deterministic
set<int> vars;
for (auto &[symb_lag, deriv_id] : deriv_id_table)
if (SymbolType type = symbol_table.getType(symb_lag.first);
type == SymbolType::endogenous || type == SymbolType::exogenous
|| type == SymbolType::exogenousDet)
for (auto& [symb_lag, deriv_id] : deriv_id_table)
if (SymbolType type = symbol_table.getType(symb_lag.first); type == SymbolType::endogenous
|| type == SymbolType::exogenous
|| type == SymbolType::exogenousDet)
vars.insert(deriv_id);
// Launch computations
......@@ -2130,12 +2485,13 @@ DynamicModel::computingPass(int derivsOrder, int paramsDerivsOrder, const eval_c
computeDerivatives(derivsOrder, vars);
if (derivsOrder > 1)
for (const auto &[indices, d2] : derivatives[2])
for (const auto& [indices, d2] : derivatives[2])
nonzero_hessian_eqs.insert(indices[0]);
if (paramsDerivsOrder > 0)
{
cout << "Computing " << modelClassName() << " derivatives w.r.t. parameters (order " << paramsDerivsOrder << ")." << endl;
cout << "Computing " << modelClassName() << " derivatives w.r.t. parameters (order "
<< paramsDerivsOrder << ")." << endl;
computeParamsDerivatives(paramsDerivsOrder);
}
......@@ -2160,21 +2516,21 @@ DynamicModel::computingPass(int derivsOrder, int paramsDerivsOrder, const eval_c
cerr << "ERROR: Block decomposition requested but failed." << endl;
exit(EXIT_FAILURE);
}
computeMCPEquationsReordering();
}
void
DynamicModel::computeXrefs()
{
for (int i{0};
auto &equation : equations)
for (int i {0}; auto& equation : equations)
{
ExprNode::EquationInfo ei;
equation->computeXrefs(ei);
xrefs[i++] = ei;
}
for (int i{0};
const auto &[eq, eqinfo] : xrefs)
for (int i {0}; const auto& [eq, eqinfo] : xrefs)
{
computeRevXref(xref_param, eqinfo.param, i);
computeRevXref(xref_endo, eqinfo.endo, i);
......@@ -2185,9 +2541,10 @@ DynamicModel::computeXrefs()
}
void
DynamicModel::computeRevXref(map<pair<int, int>, set<int>> &xrefset, const set<pair<int, int>> &eiref, int eqn)
DynamicModel::computeRevXref(map<pair<int, int>, set<int>>& xrefset,
const set<pair<int, int>>& eiref, int eqn)
{
for (const auto &it : eiref)
for (const auto& it : eiref)
{
set<int> eq;
if (xrefset.contains(it))
......@@ -2198,33 +2555,35 @@ DynamicModel::computeRevXref(map<pair<int, int>, set<int>> &xrefset, const set<p
}
void
DynamicModel::writeXrefs(ostream &output) const
DynamicModel::writeXrefs(ostream& output) const
{
output << "M_.xref1.param = cell(1, M_.eq_nbr);" << endl
<< "M_.xref1.endo = cell(1, M_.eq_nbr);" << endl
<< "M_.xref1.exo = cell(1, M_.eq_nbr);" << endl
<< "M_.xref1.exo_det = cell(1, M_.eq_nbr);" << endl;
for (int i{1};
const auto &[eq, eqinfo] : xrefs)
for (int i {1}; const auto& [eq, eqinfo] : xrefs)
{
output << "M_.xref1.param{" << i << "} = [ ";
for (const auto &[id, lag] : eqinfo.param)
for (const auto& [id, lag] : eqinfo.param)
output << symbol_table.getTypeSpecificID(id) + 1 << " ";
output << "];" << endl;
output << "M_.xref1.endo{" << i << "} = [ ";
for (const auto &[id, lag] : eqinfo.endo)
output << "struct('id', " << symbol_table.getTypeSpecificID(id) + 1 << ", 'shift', " << lag << ");";
for (const auto& [id, lag] : eqinfo.endo)
output << "struct('id', " << symbol_table.getTypeSpecificID(id) + 1 << ", 'shift', " << lag
<< ");";
output << "];" << endl;
output << "M_.xref1.exo{" << i << "} = [ ";
for (const auto &[id, lag] : eqinfo.exo)
output << "struct('id', " << symbol_table.getTypeSpecificID(id) + 1 << ", 'shift', " << lag << ");";
for (const auto& [id, lag] : eqinfo.exo)
output << "struct('id', " << symbol_table.getTypeSpecificID(id) + 1 << ", 'shift', " << lag
<< ");";
output << "];" << endl;
output << "M_.xref1.exo_det{" << i << "} = [ ";
for (const auto &[id, lag] : eqinfo.exo_det)
output << "struct('id', " << symbol_table.getTypeSpecificID(id) + 1 << ", 'shift', " << lag << ");";
for (const auto& [id, lag] : eqinfo.exo_det)
output << "struct('id', " << symbol_table.getTypeSpecificID(id) + 1 << ", 'shift', " << lag
<< ");";
output << "];" << endl;
i++;
......@@ -2241,12 +2600,12 @@ DynamicModel::writeXrefs(ostream &output) const
}
void
DynamicModel::writeRevXrefs(ostream &output, const map<pair<int, int>, set<int>> &xrefmap, const string &type) const
DynamicModel::writeRevXrefs(ostream& output, const map<pair<int, int>, set<int>>& xrefmap,
const string& type) const
{
for (int last_tsid{-1};
const auto &[key, eqs] : xrefmap)
for (int last_tsid {-1}; const auto& [key, eqs] : xrefmap)
{
auto &[id, lag] = key;
auto& [id, lag] = key;
int tsid = symbol_table.getTypeSpecificID(id) + 1;
output << "M_.xref2." << type << "{" << tsid << "} = [ ";
if (last_tsid == tsid)
......@@ -2258,12 +2617,11 @@ DynamicModel::writeRevXrefs(ostream &output, const map<pair<int, int>, set<int>>
if (type == "param")
output << eq + 1 << " ";
else
output << "struct('shift', " << lag << ", 'eq', " << eq+1 << ");";
output << "struct('shift', " << lag << ", 'eq', " << eq + 1 << ");";
output << "];" << endl;
}
}
map<tuple<int, int, int>, DynamicModel::BlockDerivativeType>
DynamicModel::determineBlockDerivativesType(int blk)
{
......@@ -2271,32 +2629,30 @@ DynamicModel::determineBlockDerivativesType(int blk)
int size = blocks[blk].size;
int nb_recursive = blocks[blk].getRecursiveSize();
for (int lag {time_recursive_block_decomposition ? 0 : -blocks[blk].max_endo_lag};
lag <= (time_recursive_block_decomposition ? 0 : blocks[blk].max_endo_lead);
lag++)
lag <= (time_recursive_block_decomposition ? 0 : blocks[blk].max_endo_lead); lag++)
for (int eq = 0; eq < size; eq++)
{
set<pair<int, int>> endos_and_lags;
int eq_orig = getBlockEquationID(blk, eq);
equations[eq_orig]->collectEndogenous(endos_and_lags);
for (int var = 0; var < size; var++)
if (int var_orig = getBlockVariableID(blk, var);
endos_and_lags.contains({ var_orig, lag }))
if (int var_orig = getBlockVariableID(blk, var); endos_and_lags.contains({var_orig, lag}))
{
if (getBlockEquationType(blk, eq) == EquationType::evaluateRenormalized
&& eq < nb_recursive)
/* It’s a normalized recursive equation, we have to recompute
the derivative using the chain rule */
derivType[{ lag, eq, var }] = BlockDerivativeType::normalizedChainRule;
else if (!derivType.contains({ lag, eq, var }))
derivType[{ lag, eq, var }] = BlockDerivativeType::standard;
derivType[{lag, eq, var}] = BlockDerivativeType::normalizedChainRule;
else if (!derivType.contains({lag, eq, var}))
derivType[{lag, eq, var}] = BlockDerivativeType::standard;
if (var < nb_recursive)
for (int feedback_var = nb_recursive; feedback_var < size; feedback_var++)
if (derivType.contains({ lag, var, feedback_var }))
if (derivType.contains({lag, var, feedback_var}))
/* A new derivative needs to be computed using the chain rule
(a feedback variable appears in the recursive equation
defining the current variable) */
derivType[{ lag, eq, feedback_var }] = BlockDerivativeType::chainRule;
derivType[{lag, eq, feedback_var}] = BlockDerivativeType::chainRule;
}
}
return derivType;
......@@ -2305,7 +2661,7 @@ DynamicModel::determineBlockDerivativesType(int blk)
void
DynamicModel::computeChainRuleJacobian()
{
size_t nb_blocks { blocks.size() };
size_t nb_blocks {blocks.size()};
blocks_derivatives.resize(nb_blocks);
blocks_jacobian_sparse_column_major_order.resize(nb_blocks);
......@@ -2314,14 +2670,15 @@ DynamicModel::computeChainRuleJacobian()
for (int blk {0}; blk < static_cast<int>(nb_blocks); blk++)
{
int nb_recursives = blocks[blk].getRecursiveSize();
int mfs_size { blocks[blk].mfs_size };
BlockSimulationType simulation_type { blocks[blk].simulation_type };
int mfs_size {blocks[blk].mfs_size};
BlockSimulationType simulation_type {blocks[blk].simulation_type};
// Create a map from recursive vars to their defining (normalized) equation
map<int, BinaryOpNode *> recursive_vars;
map<int, BinaryOpNode*> recursive_vars;
for (int i = 0; i < nb_recursives; i++)
{
int deriv_id = getDerivID(symbol_table.getID(SymbolType::endogenous, getBlockVariableID(blk, i)), 0);
int deriv_id = getDerivID(
symbol_table.getID(SymbolType::endogenous, getBlockVariableID(blk, i)), 0);
if (getBlockEquationType(blk, i) == EquationType::evaluateRenormalized)
recursive_vars[deriv_id] = getBlockEquationRenormalizedExpr(blk, i);
else
......@@ -2331,31 +2688,34 @@ DynamicModel::computeChainRuleJacobian()
// Compute the block derivatives
unordered_map<expr_t, set<int>> non_null_chain_rule_derivatives;
unordered_map<expr_t, map<int, expr_t>> chain_rule_deriv_cache;
for (const auto &[indices, derivType] : determineBlockDerivativesType(blk))
for (const auto& [indices, derivType] : determineBlockDerivativesType(blk))
{
auto [lag, eq, var] = indices;
int eq_orig = getBlockEquationID(blk, eq), var_orig = getBlockVariableID(blk, var);
int deriv_id = getDerivID(symbol_table.getID(SymbolType::endogenous, var_orig), lag);
expr_t d{nullptr};
expr_t d {nullptr};
switch (derivType)
{
case BlockDerivativeType::standard:
if (auto it = derivatives[1].find({ eq_orig, deriv_id });
it != derivatives[1].end())
if (auto it = derivatives[1].find({eq_orig, deriv_id}); it != derivatives[1].end())
d = it->second;
else
d = Zero;
break;
case BlockDerivativeType::chainRule:
d = equations[eq_orig]->getChainRuleDerivative(deriv_id, recursive_vars, non_null_chain_rule_derivatives, chain_rule_deriv_cache);
d = equations[eq_orig]->getChainRuleDerivative(deriv_id, recursive_vars,
non_null_chain_rule_derivatives,
chain_rule_deriv_cache);
break;
case BlockDerivativeType::normalizedChainRule:
d = equation_type_and_normalized_equation[eq_orig].second->getChainRuleDerivative(deriv_id, recursive_vars, non_null_chain_rule_derivatives, chain_rule_deriv_cache);
d = equation_type_and_normalized_equation[eq_orig].second->getChainRuleDerivative(
deriv_id, recursive_vars, non_null_chain_rule_derivatives,
chain_rule_deriv_cache);
break;
}
if (d != Zero)
blocks_derivatives[blk][{ eq, var, lag }] = d;
if (d != Zero && eq >= nb_recursives)
blocks_derivatives[blk][{eq, var, lag}] = d;
}
// Compute the sparse representation of the Jacobian
......@@ -2366,15 +2726,18 @@ DynamicModel::computeChainRuleJacobian()
|| simulation_type == BlockSimulationType::solveForwardSimple
|| simulation_type == BlockSimulationType::solveBackwardComplete
|| simulation_type == BlockSimulationType::solveForwardComplete};
for (const auto &[indices, d1] : blocks_derivatives[blk])
for (const auto& [indices, d1] : blocks_derivatives[blk])
{
auto &[eq, var, lag] { indices };
assert(lag >= -1 && lag <= 1);
if (eq >= nb_recursives && var >= nb_recursives
&& !(one_boundary && lag != 0))
blocks_jacobian_sparse_column_major_order[blk].try_emplace({eq-nb_recursives, var-nb_recursives+static_cast<int>(!one_boundary)*(lag+1)*mfs_size}, d1);
auto& [eq, var, lag] {indices};
assert(lag >= -1 && lag <= 1 && eq >= nb_recursives);
if (var >= nb_recursives && !(one_boundary && lag != 0))
blocks_jacobian_sparse_column_major_order[blk].try_emplace(
{eq - nb_recursives,
var - nb_recursives + static_cast<int>(!one_boundary) * (lag + 1) * mfs_size},
d1);
}
blocks_jacobian_sparse_colptr[blk] = computeCSCColPtr(blocks_jacobian_sparse_column_major_order[blk], (one_boundary ? 1 : 3)*mfs_size);
blocks_jacobian_sparse_colptr[blk] = computeCSCColPtr(
blocks_jacobian_sparse_column_major_order[blk], (one_boundary ? 1 : 3) * mfs_size);
}
}
}
......@@ -2382,48 +2745,32 @@ DynamicModel::computeChainRuleJacobian()
void
DynamicModel::computeBlockDynJacobianCols()
{
size_t nb_blocks { blocks.size() };
// Structures used for lexicographic ordering over (lag, var ID)
vector<set<pair<int, int>>> dynamic_endo(nb_blocks), dynamic_other_endo(nb_blocks),
dynamic_exo(nb_blocks), dynamic_exo_det(nb_blocks);
size_t nb_blocks {blocks.size()};
// Structure used for lexicographic ordering over (lag, var ID)
vector<set<pair<int, int>>> dynamic_endo(nb_blocks);
for (auto & [indices, d1] : derivatives[1])
for (auto& [indices, d1] : derivatives[1])
{
auto [eq_orig, deriv_id] { vectorToTuple<2>(indices) };
int block_eq { eq2block[eq_orig] };
int var { getTypeSpecificIDByDerivID(deriv_id) };
int lag { getLagByDerivID(deriv_id) };
switch (getTypeByDerivID(deriv_id))
{
case SymbolType::endogenous:
if (block_eq == endo2block[var])
dynamic_endo[block_eq].emplace(lag, getBlockInitialVariableID(block_eq, var));
else
dynamic_other_endo[block_eq].emplace(lag, var);
break;
case SymbolType::exogenous:
dynamic_exo[block_eq].emplace(lag, var);
break;
case SymbolType::exogenousDet:
dynamic_exo_det[block_eq].emplace(lag, var);
break;
default:
break;
}
auto [eq_orig, deriv_id] {vectorToTuple<2>(indices)};
int block_eq {eq2block[eq_orig]};
int var {getTypeSpecificIDByDerivID(deriv_id)};
int lag {getLagByDerivID(deriv_id)};
if (getTypeByDerivID(deriv_id) == SymbolType::endogenous && block_eq == endo2block[var])
dynamic_endo[block_eq].emplace(lag, getBlockInitialVariableID(block_eq, var));
}
// Compute Jacobian column indices
blocks_jacob_cols_endo.resize(nb_blocks);
for (size_t blk {0}; blk < nb_blocks; blk++)
for (int index{0};
auto [lag, var] : dynamic_endo[blk])
blocks_jacob_cols_endo[blk][{ var, lag }] = index++;
for (int index {0}; auto [lag, var] : dynamic_endo[blk])
blocks_jacob_cols_endo[blk][{var, lag}] = index++;
}
void
DynamicModel::writeDynamicFile(const string &basename, bool use_dll, const string &mexext, const filesystem::path &matlabroot, bool julia) const
DynamicModel::writeDynamicFile(const string& basename, bool use_dll, const string& mexext,
const filesystem::path& matlabroot, bool julia) const
{
filesystem::path model_dir{basename};
filesystem::path model_dir {basename};
model_dir /= "model";
if (use_dll)
{
......@@ -2474,9 +2821,12 @@ DynamicModel::writeDynamicFile(const string &basename, bool use_dll, const strin
writeSetAuxiliaryVariablesFile<true>(basename, julia);
// Support for model debugging
if (!julia)
writeDebugModelMFiles<true>(basename);
{
writeComplementarityConditionsFile<true>(basename);
// Support for model debugging
writeDebugModelMFiles<true>(basename);
}
}
void
......@@ -2485,21 +2835,34 @@ DynamicModel::clearEquations()
equations.clear();
equations_lineno.clear();
equation_tags.clear();
complementarity_conditions.clear();
}
void
DynamicModel::replaceMyEquations(DynamicModel &dynamic_model) const
DynamicModel::replaceMyEquations(DynamicModel& dynamic_model) const
{
dynamic_model.clearEquations();
auto clone_if_not_null = [&](expr_t e) { return e ? e->clone(dynamic_model) : nullptr; };
for (size_t i = 0; i < equations.size(); i++)
dynamic_model.addEquation(equations[i]->clone(dynamic_model), equations_lineno[i]);
{
optional<tuple<int, expr_t, expr_t>> cc_clone;
if (complementarity_conditions[i])
{
auto& [symb_id, lower_bound, upper_bound] = *complementarity_conditions[i];
cc_clone = {symb_id, clone_if_not_null(lower_bound), clone_if_not_null(upper_bound)};
}
dynamic_model.addEquation(equations[i]->clone(dynamic_model), equations_lineno[i],
move(cc_clone));
}
dynamic_model.equation_tags = equation_tags;
}
int
DynamicModel::computeRamseyPolicyFOCs(const StaticModel &static_model)
DynamicModel::computeRamseyPolicyFOCs(const StaticModel& planner_objective,
map<int, pair<expr_t, expr_t>> cloned_ramsey_constraints)
{
cout << "Ramsey Problem: added " << equations.size() << " multipliers." << endl;
......@@ -2507,23 +2870,23 @@ DynamicModel::computeRamseyPolicyFOCs(const StaticModel &static_model)
// equation[i]->lhs = rhs becomes equation[i]->MULT_(i+1)*(lhs-rhs) = 0
for (int i {0}; i < static_cast<int>(equations.size()); i++)
{
auto substeq = dynamic_cast<BinaryOpNode *>(equations[i]->addMultipliersToConstraints(i));
auto substeq = dynamic_cast<BinaryOpNode*>(equations[i]->addMultipliersToConstraints(i));
assert(substeq);
equations[i] = substeq;
}
// Add Planner Objective to equations so that it appears in Lagrangian
assert(static_model.equations.size() == 1);
addEquation(static_model.equations[0]->clone(*this), nullopt);
assert(planner_objective.equations.size() == 1);
addEquation(planner_objective.equations[0]->clone(*this), nullopt);
// Get max endo lead and max endo lag
set<pair<int, int>> dynvars;
int max_eq_lead = 0;
int max_eq_lag = 0;
for (auto &equation : equations)
for (auto& equation : equations)
equation->collectDynamicVariables(SymbolType::endogenous, dynvars);
for (const auto &[symb_id, lag] : dynvars)
for (const auto& [symb_id, lag] : dynvars)
{
max_eq_lead = max(lag, max_eq_lead);
max_eq_lag = max(-lag, max_eq_lag);
......@@ -2550,13 +2913,16 @@ DynamicModel::computeRamseyPolicyFOCs(const StaticModel &static_model)
else
dfpower = AddMinus(Zero, AddNonNegativeConstant(lagstream.str()));
lagrangian = AddPlus(AddTimes(AddPower(discount_factor_node, dfpower),
equations[i]->getNonZeroPartofEquation()->decreaseLeadsLags(lag)), lagrangian);
lagrangian
= AddPlus(AddTimes(AddPower(discount_factor_node, dfpower),
equations[i]->getNonZeroPartofEquation()->decreaseLeadsLags(lag)),
lagrangian);
}
// Save line numbers and tags, see below
// Save line numbers, tags and complementarity conditions, see below
auto old_equations_lineno = equations_lineno;
auto old_equation_tags = equation_tags;
auto old_complementarity_conditions = complementarity_conditions;
// Prepare derivation of the Lagrangian
clearEquations();
......@@ -2573,24 +2939,37 @@ DynamicModel::computeRamseyPolicyFOCs(const StaticModel &static_model)
vector<expr_t> neweqs;
vector<optional<int>> neweqs_lineno;
map<int, map<string, string>> neweqs_tags;
map<int, optional<tuple<int, expr_t, expr_t>>> new_complementarity_conditions;
int orig_endo_nbr {0};
for (auto &[symb_id_and_lag, deriv_id] : deriv_id_table)
for (auto& [symb_id_and_lag, deriv_id] : deriv_id_table)
{
auto &[symb_id, lag] = symb_id_and_lag;
auto& [symb_id, lag] = symb_id_and_lag;
if (symbol_table.getType(symb_id) == SymbolType::endogenous && lag == 0)
{
neweqs.push_back(AddEqual(equations[0]->getNonZeroPartofEquation()->getDerivative(deriv_id), Zero));
if (optional<int> i = symbol_table.getEquationNumberForMultiplier(symb_id);
i)
neweqs.push_back(
AddEqual(equations[0]->getNonZeroPartofEquation()->getDerivative(deriv_id), Zero));
if (optional<int> i = symbol_table.getEquationNumberForMultiplier(symb_id); i)
{
// This is a derivative w.r.t. a Lagrange multiplier
neweqs_lineno.push_back(old_equations_lineno[*i]);
neweqs_tags[neweqs.size()-1] = old_equation_tags.getTagsByEqn(*i);
neweqs_tags.emplace(neweqs.size() - 1, old_equation_tags.getTagsByEqn(*i));
new_complementarity_conditions.emplace(neweqs.size() - 1,
old_complementarity_conditions.at(*i));
}
else
{
orig_endo_nbr++;
neweqs_lineno.push_back(nullopt);
neweqs_lineno.emplace_back(nullopt);
if (string eqname {"Ramsey FOC w.r.t. "s + symbol_table.getName(symb_id)};
!equation_tags.exists("name", eqname))
neweqs_tags.emplace(neweqs.size() - 1, map<string, string> {{"name", eqname}});
if (cloned_ramsey_constraints.contains(symb_id))
{
auto& [lower_bound, upper_bound] = cloned_ramsey_constraints.at(symb_id);
new_complementarity_conditions.emplace(neweqs.size() - 1,
tuple {symb_id, lower_bound, upper_bound});
}
}
}
}
......@@ -2598,7 +2977,7 @@ DynamicModel::computeRamseyPolicyFOCs(const StaticModel &static_model)
// Overwrite equations with the Lagrangian derivatives
clearEquations();
for (size_t i = 0; i < neweqs.size(); i++)
addEquation(neweqs[i], neweqs_lineno[i], neweqs_tags[i]);
addEquation(neweqs[i], neweqs_lineno[i], new_complementarity_conditions[i], neweqs_tags[i]);
return orig_endo_nbr;
}
......@@ -2629,15 +3008,16 @@ DynamicModel::expandEqTags()
for (int eq = 0; eq < static_cast<int>(equations.size()); eq++)
if (!existing_tags.contains(eq))
{
if (auto lhs_expr = dynamic_cast<VariableNode *>(equations[eq]->arg1);
lhs_expr
&& !equation_tags.exists("name", symbol_table.getName(lhs_expr->symb_id)))
equation_tags.add(eq, "name", symbol_table.getName(lhs_expr->symb_id));
else if (!equation_tags.exists("name", to_string(eq+1)))
equation_tags.add(eq, "name", to_string(eq+1));
if (auto lhs_expr = dynamic_cast<VariableNode*>(equations[eq]->arg1);
lhs_expr && !equation_tags.exists("name", lhs_expr->getName()))
equation_tags.add(eq, "name", lhs_expr->getName());
else if (!equation_tags.exists("name", to_string(eq + 1)))
equation_tags.add(eq, "name", to_string(eq + 1));
else
{
cerr << "Error creating default equation tag: cannot assign default tag to equation number " << eq+1 << " because it is already in use" << endl;
cerr << "Error creating default equation tag: cannot assign default tag to equation "
"number "
<< eq + 1 << " because it is already in use" << endl;
exit(EXIT_FAILURE);
}
}
......@@ -2647,14 +3027,12 @@ set<int>
DynamicModel::findUnusedEndogenous()
{
set<int> usedEndo, unusedEndo;
for (auto &equation : equations)
for (auto& equation : equations)
equation->collectVariables(SymbolType::endogenous, usedEndo);
for (auto &equation : static_only_equations)
for (auto& equation : static_only_equations)
equation->collectVariables(SymbolType::endogenous, usedEndo);
set<int> allEndo = symbol_table.getEndogenous();
set_difference(allEndo.begin(), allEndo.end(),
usedEndo.begin(), usedEndo.end(),
inserter(unusedEndo, unusedEndo.begin()));
ranges::set_difference(allEndo, usedEndo, inserter(unusedEndo, unusedEndo.begin()));
return unusedEndo;
}
......@@ -2662,18 +3040,14 @@ set<int>
DynamicModel::findUnusedExogenous()
{
set<int> usedExo, unusedExo, unobservedExo;
for (auto &equation : equations)
for (auto& equation : equations)
equation->collectVariables(SymbolType::exogenous, usedExo);
for (auto &equation : static_only_equations)
for (auto& equation : static_only_equations)
equation->collectVariables(SymbolType::exogenous, usedExo);
set<int> observedExo = symbol_table.getObservedExogenous();
set<int> allExo = symbol_table.getExogenous();
set_difference(allExo.begin(), allExo.end(),
observedExo.begin(), observedExo.end(),
inserter(unobservedExo, unobservedExo.begin()));
set_difference(unobservedExo.begin(), unobservedExo.end(),
usedExo.begin(), usedExo.end(),
inserter(unusedExo, unusedExo.begin()));
ranges::set_difference(allExo, observedExo, inserter(unobservedExo, unobservedExo.begin()));
ranges::set_difference(unobservedExo, usedExo, inserter(unusedExo, unusedExo.begin()));
return unusedExo;
}
......@@ -2682,17 +3056,17 @@ DynamicModel::setLeadsLagsOrig()
{
set<pair<int, int>> dynvars;
for (auto &equation : equations)
for (auto& equation : equations)
{
equation->collectDynamicVariables(SymbolType::endogenous, dynvars);
equation->collectDynamicVariables(SymbolType::exogenous, dynvars);
equation->collectDynamicVariables(SymbolType::exogenousDet, dynvars);
max_lag_with_diffs_expanded_orig = max(equation->maxLagWithDiffsExpanded(),
max_lag_with_diffs_expanded_orig);
max_lag_with_diffs_expanded_orig
= max(equation->maxLagWithDiffsExpanded(), max_lag_with_diffs_expanded_orig);
}
for (const auto &[symb_id, lag] : dynvars)
for (const auto& [symb_id, lag] : dynvars)
{
SymbolType type = symbol_table.getType(symb_id);
......@@ -2724,7 +3098,7 @@ DynamicModel::computeDerivIDs()
{
set<pair<int, int>> dynvars;
for (auto &equation : equations)
for (auto& equation : equations)
{
equation->collectDynamicVariables(SymbolType::endogenous, dynvars);
equation->collectDynamicVariables(SymbolType::exogenous, dynvars);
......@@ -2734,7 +3108,7 @@ DynamicModel::computeDerivIDs()
equation->collectDynamicVariables(SymbolType::logTrend, dynvars);
}
for (const auto &[symb_id, lag] : dynvars)
for (const auto& [symb_id, lag] : dynvars)
{
SymbolType type = symbol_table.getType(symb_id);
......@@ -2808,15 +3182,14 @@ DynamicModel::getTypeSpecificIDByDerivID(int deriv_id) const
int
DynamicModel::getDerivID(int symb_id, int lag) const noexcept(false)
{
if (auto it = deriv_id_table.find({ symb_id, lag });
it == deriv_id_table.end())
if (auto it = deriv_id_table.find({symb_id, lag}); it == deriv_id_table.end())
throw UnknownDerivIDException();
else
return it->second;
}
void
DynamicModel::addAllParamDerivId(set<int> &deriv_id_set)
DynamicModel::addAllParamDerivId(set<int>& deriv_id_set)
{
for (size_t i = 0; i < inv_deriv_id_table.size(); i++)
if (symbol_table.getType(inv_deriv_id_table[i].first) == SymbolType::parameter)
......@@ -2826,26 +3199,25 @@ DynamicModel::addAllParamDerivId(set<int> &deriv_id_set)
void
DynamicModel::computeDynJacobianCols()
{
// Sort the dynamic endogenous variables by lexicographic order over (lag, type_specific_symbol_id)
// Sort the dynamic endogenous variables by lexicographic order over (lag,
// type_specific_symbol_id)
map<pair<int, int>, int> ordered_dyn_endo;
for (const auto &[symb_lag, deriv_id] : deriv_id_table)
if (const auto &[symb_id, lag] = symb_lag;
for (const auto& [symb_lag, deriv_id] : deriv_id_table)
if (const auto& [symb_id, lag] = symb_lag;
symbol_table.getType(symb_id) == SymbolType::endogenous)
ordered_dyn_endo[{ lag, symbol_table.getTypeSpecificID(symb_id) }] = deriv_id;
ordered_dyn_endo[{lag, symbol_table.getTypeSpecificID(symb_id)}] = deriv_id;
// Fill the dynamic jacobian columns for endogenous (legacy representation)
for (int sorted_id{0};
const auto &[ignore, deriv_id] : ordered_dyn_endo)
for (int sorted_id {0}; const auto& [ignore, deriv_id] : ordered_dyn_endo)
dyn_jacobian_cols_table[deriv_id] = sorted_id++;
/* Fill the dynamic columns for exogenous and exogenous deterministic (legacy
representation) */
for (const auto &[symb_lag, deriv_id] : deriv_id_table)
for (const auto& [symb_lag, deriv_id] : deriv_id_table)
{
int symb_id{symb_lag.first};
int tsid{symbol_table.getTypeSpecificID(symb_id)}; // At this point, there is no trend_var
if (SymbolType type{symbol_table.getType(symb_id)};
type == SymbolType::exogenous)
int symb_id {symb_lag.first};
int tsid {symbol_table.getTypeSpecificID(symb_id)}; // At this point, there is no trend_var
if (SymbolType type {symbol_table.getType(symb_id)}; type == SymbolType::exogenous)
dyn_jacobian_cols_table[deriv_id] = ordered_dyn_endo.size() + tsid;
else if (type == SymbolType::exogenousDet)
dyn_jacobian_cols_table[deriv_id] = ordered_dyn_endo.size() + symbol_table.exo_nbr() + tsid;
......@@ -2853,40 +3225,46 @@ DynamicModel::computeDynJacobianCols()
/* NB: the following could differ from dyn_jacobian_cols_table.size() if
there are unused exogenous (and “nostrict” option is given) */
dyn_jacobian_ncols = ordered_dyn_endo.size() + symbol_table.exo_nbr() + symbol_table.exo_det_nbr();
dyn_jacobian_ncols
= ordered_dyn_endo.size() + symbol_table.exo_nbr() + symbol_table.exo_det_nbr();
}
void
DynamicModel::testTrendDerivativesEqualToZero(const eval_context_t &eval_context)
DynamicModel::testTrendDerivativesEqualToZero(const eval_context_t& eval_context)
{
for (auto &[symb_lag1, deriv_id1] : deriv_id_table)
if (auto &[symb_id1, lag1] = symb_lag1;
for (auto& [symb_lag1, deriv_id1] : deriv_id_table)
if (auto& [symb_id1, lag1] = symb_lag1;
symbol_table.getType(symb_id1) == SymbolType::trend
|| symbol_table.getType(symb_id1) == SymbolType::logTrend)
for (int eq = 0; eq < static_cast<int>(equations.size()); eq++)
{
expr_t homogeneq = AddMinus(equations[eq]->arg1,
equations[eq]->arg2);
expr_t homogeneq = AddMinus(equations[eq]->arg1, equations[eq]->arg2);
// Do not run the test if the term inside the log is zero
if (fabs(homogeneq->eval(eval_context)) > zero_band)
{
expr_t testeq = AddLog(homogeneq); // F = log(lhs-rhs)
expr_t testeq = AddLog(homogeneq); // F = log(lhs-rhs)
testeq = testeq->getDerivative(deriv_id1); // d F / d Trend
for (auto &[symb_lag2, deriv_id2] : deriv_id_table)
if (auto &[symb_id2, lag2] = symb_lag2;
for (auto& [symb_lag2, deriv_id2] : deriv_id_table)
if (auto& [symb_id2, lag2] = symb_lag2;
symbol_table.getType(symb_id2) == SymbolType::endogenous)
{
double nearZero = testeq->getDerivative(deriv_id2)->eval(eval_context); // eval d F / d Trend d Endog
double nearZero = testeq->getDerivative(deriv_id2)->eval(
eval_context); // eval d F / d Trend d Endog
if (fabs(nearZero) > balanced_growth_test_tol)
{
cerr << "ERROR: trends not compatible with balanced growth path; the second-order cross partial of equation " << eq + 1;
cerr << "ERROR: trends not compatible with balanced growth path; the "
"second-order cross partial of equation "
<< eq + 1;
if (equations_lineno[eq])
cerr << " (line " << *equations_lineno[eq] << ") ";
cerr << "w.r.t. trend variable "
<< symbol_table.getName(symb_id1) << " and endogenous variable "
<< symbol_table.getName(symb_id2) << " is not null (abs. value = "
<< fabs(nearZero) << "). If you are confident that your trends are correctly specified, you can raise the value of option 'balanced_growth_test_tol' in the 'model' block." << endl;
cerr << "w.r.t. trend variable " << symbol_table.getName(symb_id1)
<< " and endogenous variable " << symbol_table.getName(symb_id2)
<< " is not null (abs. value = " << fabs(nearZero)
<< "). If you are confident that your trends are correctly specified, "
"you can raise the value of option 'balanced_growth_test_tol' in "
"the 'model' block."
<< endl;
exit(EXIT_FAILURE);
}
}
......@@ -2895,15 +3273,17 @@ DynamicModel::testTrendDerivativesEqualToZero(const eval_context_t &eval_context
}
void
DynamicModel::writeLatexFile(const string &basename, bool write_equation_tags) const
DynamicModel::writeLatexFile(const string& basename, bool write_equation_tags) const
{
writeLatexModelFile(basename, "dynamic", ExprNodeOutputType::latexDynamicModel, write_equation_tags);
writeLatexModelFile(basename, "dynamic", ExprNodeOutputType::latexDynamicModel,
write_equation_tags);
}
void
DynamicModel::writeLatexOriginalFile(const string &basename, bool write_equation_tags) const
DynamicModel::writeLatexOriginalFile(const string& basename, bool write_equation_tags) const
{
writeLatexModelFile(basename, "original", ExprNodeOutputType::latexDynamicModel, write_equation_tags);
writeLatexModelFile(basename, "original", ExprNodeOutputType::latexDynamicModel,
write_equation_tags);
}
void
......@@ -2931,14 +3311,15 @@ DynamicModel::substituteExoLag(bool deterministic_model)
}
void
DynamicModel::substituteLeadLagInternal(AuxVarType type, bool deterministic_model, const vector<string> &subset)
DynamicModel::substituteLeadLagInternal(AuxVarType type, bool deterministic_model,
const vector<string>& subset)
{
ExprNode::subst_table_t subst_table;
vector<BinaryOpNode *> neweqs;
vector<BinaryOpNode*> neweqs;
// Substitute in used model local variables
set<int> used_local_vars;
for (auto &equation : equations)
for (auto& equation : equations)
equation->collectVariables(SymbolType::modelLocalVariable, used_local_vars);
for (int used_local_var : used_local_vars)
......@@ -2970,13 +3351,14 @@ DynamicModel::substituteLeadLagInternal(AuxVarType type, bool deterministic_mode
}
// Substitute in equations
for (auto &equation : equations)
for (auto& equation : equations)
{
expr_t subst;
switch (type)
{
case AuxVarType::endoLead:
subst = equation->substituteEndoLeadGreaterThanTwo(subst_table, neweqs, deterministic_model);
subst = equation->substituteEndoLeadGreaterThanTwo(subst_table, neweqs,
deterministic_model);
break;
case AuxVarType::endoLag:
subst = equation->substituteEndoLagGreaterThanTwo(subst_table, neweqs);
......@@ -2994,13 +3376,13 @@ DynamicModel::substituteLeadLagInternal(AuxVarType type, bool deterministic_mode
cerr << "DynamicModel::substituteLeadLagInternal: impossible case" << endl;
exit(EXIT_FAILURE);
}
auto substeq = dynamic_cast<BinaryOpNode *>(subst);
auto substeq = dynamic_cast<BinaryOpNode*>(subst);
assert(substeq);
equation = substeq;
}
// Add new equations
for (auto &neweq : neweqs)
for (auto& neweq : neweqs)
{
addEquation(neweq, nullopt);
addAuxEquation(neweq);
......@@ -3044,24 +3426,24 @@ DynamicModel::substituteAdl()
definitions here, instead of doing it at the ExprNode method level,
because otherwise this would substitute MLV in the original model (see
#65). */
for (auto &[id, definition] : local_variables_table)
for (auto& [id, definition] : local_variables_table)
definition = definition->substituteAdl();
for (auto &equation : equations)
equation = dynamic_cast<BinaryOpNode *>(equation->substituteAdl());
for (auto& equation : equations)
equation = dynamic_cast<BinaryOpNode*>(equation->substituteAdl());
for (auto &equation : static_only_equations)
equation = dynamic_cast<BinaryOpNode *>(equation->substituteAdl());
for (auto& equation : static_only_equations)
equation = dynamic_cast<BinaryOpNode*>(equation->substituteAdl());
}
void
DynamicModel::substituteModelLocalVariables()
{
for (auto &equation : equations)
equation = dynamic_cast<BinaryOpNode *>(equation->substituteModelLocalVariables());
for (auto& equation : equations)
equation = dynamic_cast<BinaryOpNode*>(equation->substituteModelLocalVariables());
for (auto &equation : static_only_equations)
equation = dynamic_cast<BinaryOpNode *>(equation->substituteModelLocalVariables());
for (auto& equation : static_only_equations)
equation = dynamic_cast<BinaryOpNode*>(equation->substituteModelLocalVariables());
/* We can’t clear local_variables_table at this point, because in case of
ramsey_policy, the original model is saved via DynamicModel::operator=()
......@@ -3072,10 +3454,10 @@ DynamicModel::substituteModelLocalVariables()
}
set<int>
DynamicModel::getEquationNumbersFromTags(const set<string> &eqtags) const
DynamicModel::getEquationNumbersFromTags(const set<string>& eqtags) const
{
set<int> eqnumbers;
for (auto &eqtag : eqtags)
for (auto& eqtag : eqtags)
{
set<int> tmp = equation_tags.getEqnsByTag("name", eqtag);
if (tmp.empty())
......@@ -3092,8 +3474,7 @@ set<int>
DynamicModel::findPacExpectationEquationNumbers() const
{
set<int> eqnumbers;
for (int i{0};
auto &equation : equations)
for (int i {0}; auto& equation : equations)
{
if (equation->containsPacExpectation())
eqnumbers.insert(i);
......@@ -3103,15 +3484,17 @@ DynamicModel::findPacExpectationEquationNumbers() const
}
pair<lag_equivalence_table_t, ExprNode::subst_table_t>
DynamicModel::substituteUnaryOps(VarExpectationModelTable &var_expectation_model_table, PacModelTable &pac_model_table)
DynamicModel::substituteUnaryOps(VarExpectationModelTable& var_expectation_model_table,
PacModelTable& pac_model_table)
{
vector<int> eqnumbers(equations.size());
iota(eqnumbers.begin(), eqnumbers.end(), 0);
return substituteUnaryOps({ eqnumbers.begin(), eqnumbers.end() }, var_expectation_model_table, pac_model_table);
auto v = views::iota(0, static_cast<int>(equations.size()));
return substituteUnaryOps({v.begin(), v.end()}, var_expectation_model_table, pac_model_table);
}
pair<lag_equivalence_table_t, ExprNode::subst_table_t>
DynamicModel::substituteUnaryOps(const set<int> &eqnumbers, VarExpectationModelTable &var_expectation_model_table, PacModelTable &pac_model_table)
DynamicModel::substituteUnaryOps(const set<int>& eqnumbers,
VarExpectationModelTable& var_expectation_model_table,
PacModelTable& pac_model_table)
{
lag_equivalence_table_t nodes;
ExprNode::subst_table_t subst_table;
......@@ -3128,15 +3511,16 @@ DynamicModel::substituteUnaryOps(const set<int> &eqnumbers, VarExpectationModelT
equations[eqnumber]->findUnaryOpNodesForAuxVarCreation(nodes);
// Substitute in model local variables
vector<BinaryOpNode *> neweqs;
vector<BinaryOpNode*> neweqs;
for (int mlv : used_local_vars)
local_variables_table[mlv] = local_variables_table[mlv]->substituteUnaryOpNodes(nodes, subst_table, neweqs);
local_variables_table[mlv]
= local_variables_table[mlv]->substituteUnaryOpNodes(nodes, subst_table, neweqs);
// Substitute in equations
for (int eq : eqnumbers)
{
auto substeq = dynamic_cast<BinaryOpNode *>(equations[eq]->
substituteUnaryOpNodes(nodes, subst_table, neweqs));
auto substeq = dynamic_cast<BinaryOpNode*>(
equations[eq]->substituteUnaryOpNodes(nodes, subst_table, neweqs));
assert(substeq);
equations[eq] = substeq;
}
......@@ -3147,20 +3531,22 @@ DynamicModel::substituteUnaryOps(const set<int> &eqnumbers, VarExpectationModelT
pac_model_table.substituteUnaryOpsInGrowth(nodes, subst_table, neweqs);
// Add new equations
for (auto &neweq : neweqs)
for (auto& neweq : neweqs)
{
addEquation(neweq, nullopt);
addAuxEquation(neweq);
}
if (subst_table.size() > 0)
cout << "Substitution of Unary Ops: added " << neweqs.size() << " auxiliary variables and equations." << endl;
cout << "Substitution of Unary Ops: added " << neweqs.size()
<< " auxiliary variables and equations." << endl;
return { nodes, subst_table };
return {nodes, subst_table};
}
pair<lag_equivalence_table_t, ExprNode::subst_table_t>
DynamicModel::substituteDiff(VarExpectationModelTable &var_expectation_model_table, PacModelTable &pac_model_table)
DynamicModel::substituteDiff(VarExpectationModelTable& var_expectation_model_table,
PacModelTable& pac_model_table)
{
/* Note: at this point, we know that there is no diff operator with a lead,
because they have been expanded by DataTree::AddDiff().
......@@ -3174,7 +3560,7 @@ DynamicModel::substituteDiff(VarExpectationModelTable &var_expectation_model_tab
set<int> used_local_vars;
for (auto equation : equations)
equation->collectVariables(SymbolType::modelLocalVariable, used_local_vars);
for (auto &[symb_id, expr] : local_variables_table)
for (auto& [symb_id, expr] : local_variables_table)
if (used_local_vars.contains(symb_id))
expr->findDiffNodes(diff_nodes);
......@@ -3185,15 +3571,15 @@ DynamicModel::substituteDiff(VarExpectationModelTable &var_expectation_model_tab
pac_model_table.findDiffNodesInGrowth(diff_nodes);
// Substitute in model local variables
vector<BinaryOpNode *> neweqs;
for (auto &[symb_id, expr] : local_variables_table)
vector<BinaryOpNode*> neweqs;
for (auto& [symb_id, expr] : local_variables_table)
expr = expr->substituteDiff(diff_nodes, diff_subst_table, neweqs);
// Substitute in equations
for (auto &equation : equations)
for (auto& equation : equations)
{
auto substeq = dynamic_cast<BinaryOpNode *>(equation->
substituteDiff(diff_nodes, diff_subst_table, neweqs));
auto substeq = dynamic_cast<BinaryOpNode*>(
equation->substituteDiff(diff_nodes, diff_subst_table, neweqs));
assert(substeq);
equation = substeq;
}
......@@ -3209,25 +3595,27 @@ DynamicModel::substituteDiff(VarExpectationModelTable &var_expectation_model_tab
}
if (diff_subst_table.size() > 0)
cout << "Substitution of Diff operator: added " << neweqs.size() << " auxiliary variables and equations." << endl;
cout << "Substitution of Diff operator: added " << neweqs.size()
<< " auxiliary variables and equations." << endl;
return { diff_nodes, diff_subst_table };
return {diff_nodes, diff_subst_table};
}
void
DynamicModel::substituteExpectation(bool partial_information_model)
{
ExprNode::subst_table_t subst_table;
vector<BinaryOpNode *> neweqs;
vector<BinaryOpNode*> neweqs;
// Substitute in model local variables
for (auto &[symb_id, expr] : local_variables_table)
for (auto& [symb_id, expr] : local_variables_table)
expr = expr->substituteExpectation(subst_table, neweqs, partial_information_model);
// Substitute in equations
for (auto &equation : equations)
for (auto& equation : equations)
{
equation = dynamic_cast<BinaryOpNode *>(equation->substituteExpectation(subst_table, neweqs, partial_information_model));
equation = dynamic_cast<BinaryOpNode*>(
equation->substituteExpectation(subst_table, neweqs, partial_information_model));
assert(equation);
}
......@@ -3244,21 +3632,23 @@ DynamicModel::substituteExpectation(bool partial_information_model)
if (subst_table.size() > 0)
{
if (partial_information_model)
cout << "Substitution of Expectation operator: added " << subst_table.size() << " auxiliary variables and " << neweqs.size() << " auxiliary equations." << endl;
cout << "Substitution of Expectation operator: added " << subst_table.size()
<< " auxiliary variables and " << neweqs.size() << " auxiliary equations." << endl;
else
cout << "Substitution of Expectation operator: added " << neweqs.size() << " auxiliary variables and equations." << endl;
cout << "Substitution of Expectation operator: added " << neweqs.size()
<< " auxiliary variables and equations." << endl;
}
}
void
DynamicModel::transformPredeterminedVariables()
{
for (auto &[id, definition] : local_variables_table)
for (auto& [id, definition] : local_variables_table)
definition = definition->decreaseLeadsLagsPredeterminedVariables();
for (auto &equation : equations)
for (auto& equation : equations)
{
equation = dynamic_cast<BinaryOpNode *>(equation->decreaseLeadsLagsPredeterminedVariables());
equation = dynamic_cast<BinaryOpNode*>(equation->decreaseLeadsLagsPredeterminedVariables());
assert(equation);
}
......@@ -3273,14 +3663,16 @@ DynamicModel::substituteLogTransform()
expr_t aux_def = AddLog(AddVariable(symb_id));
int aux_symb_id = symbol_table.addLogTransformAuxiliaryVar(symb_id, 0, aux_def);
for (auto &[id, definition] : local_variables_table)
for (auto& [id, definition] : local_variables_table)
definition = definition->substituteLogTransform(symb_id, aux_symb_id);
for (auto &equation : equations)
equation = dynamic_cast<BinaryOpNode *>(equation->substituteLogTransform(symb_id, aux_symb_id));
for (auto& equation : equations)
equation
= dynamic_cast<BinaryOpNode*>(equation->substituteLogTransform(symb_id, aux_symb_id));
for (auto &equation : static_only_equations)
equation = dynamic_cast<BinaryOpNode *>(equation->substituteLogTransform(symb_id, aux_symb_id));
for (auto& equation : static_only_equations)
equation
= dynamic_cast<BinaryOpNode*>(equation->substituteLogTransform(symb_id, aux_symb_id));
/*
We add the following new equations:
......@@ -3296,27 +3688,55 @@ DynamicModel::substituteLogTransform()
block of type “evaluate”, or maybe even the epilogue)
*/
addAuxEquation(AddEqual(AddVariable(aux_symb_id), aux_def));
addEquation(AddEqual(AddVariable(symb_id), AddExp(AddVariable(aux_symb_id))),
nullopt, {});
addEquation(AddEqual(AddVariable(symb_id), AddExp(AddVariable(aux_symb_id))), nullopt, {});
}
}
void
DynamicModel::substituteAggregationOperators()
{
ExprNode::subst_table_t subst_table;
vector<BinaryOpNode*> neweqs;
for (auto& [symb_id, expr] : local_variables_table)
expr = expr->substituteAggregationOperators(subst_table, neweqs);
for (auto& equation : equations)
{
equation = dynamic_cast<BinaryOpNode*>(
equation->substituteAggregationOperators(subst_table, neweqs));
assert(equation);
}
for (auto& equation : static_only_equations)
{
equation = dynamic_cast<BinaryOpNode*>(
equation->substituteAggregationOperators(subst_table, neweqs));
assert(equation);
}
for (auto neweq : neweqs)
{
addEquation(neweq, nullopt);
addAuxEquation(neweq);
}
}
void
DynamicModel::checkNoWithLogTransform(const set<int> &eqnumbers)
DynamicModel::checkNoWithLogTransform(const set<int>& eqnumbers)
{
set<int> endos;
for (int eq : eqnumbers)
equations[eq]->collectVariables(SymbolType::endogenous, endos);
const set<int> &with_log_transform = symbol_table.getVariablesWithLogTransform();
const set<int>& with_log_transform = symbol_table.getVariablesWithLogTransform();
vector<int> intersect;
set_intersection(endos.begin(), endos.end(),
with_log_transform.begin(), with_log_transform.end(),
back_inserter(intersect));
ranges::set_intersection(endos, with_log_transform, back_inserter(intersect));
if (!intersect.empty())
{
cerr << "ERROR: the following variables are declared with var(log) and therefore cannot appear in a VAR/TCM/PAC equation: ";
cerr << "ERROR: the following variables are declared with var(log) and therefore cannot "
"appear in a VAR/TCM/PAC equation: ";
for (int symb_id : intersect)
cerr << symbol_table.getName(symb_id) << " ";
cerr << endl;
......@@ -3328,29 +3748,30 @@ void
DynamicModel::detrendEquations()
{
// We go backwards in the list of trend_vars, to deal correctly with I(2) processes
for (auto it = nonstationary_symbols_map.crbegin();
it != nonstationary_symbols_map.crend(); ++it)
for (const auto& [symb_id, deflator] : std::ranges::reverse_view(nonstationary_symbols_map))
{
for (auto &equation : equations)
for (auto& equation : equations)
{
equation = dynamic_cast<BinaryOpNode *>(equation->detrend(it->first, it->second.first, it->second.second));
equation = dynamic_cast<BinaryOpNode*>(
equation->detrend(symb_id, deflator.first, deflator.second));
assert(equation);
}
for (auto &equation : static_only_equations)
for (auto& equation : static_only_equations)
{
equation = dynamic_cast<BinaryOpNode *>(equation->detrend(it->first, it->second.first, it->second.second));
equation = dynamic_cast<BinaryOpNode*>(
equation->detrend(symb_id, deflator.first, deflator.second));
assert(equation);
}
}
for (auto &equation : equations)
for (auto& equation : equations)
{
equation = dynamic_cast<BinaryOpNode *>(equation->removeTrendLeadLag(trend_symbols_map));
equation = dynamic_cast<BinaryOpNode*>(equation->removeTrendLeadLag(trend_symbols_map));
assert(equation);
}
for (auto &equation : static_only_equations)
for (auto& equation : static_only_equations)
{
equation = dynamic_cast<BinaryOpNode *>(equation->removeTrendLeadLag(trend_symbols_map));
equation = dynamic_cast<BinaryOpNode*>(equation->removeTrendLeadLag(trend_symbols_map));
assert(equation);
}
}
......@@ -3358,72 +3779,75 @@ DynamicModel::detrendEquations()
void
DynamicModel::removeTrendVariableFromEquations()
{
for (auto &equation : equations)
for (auto& equation : equations)
{
equation = dynamic_cast<BinaryOpNode *>(equation->replaceTrendVar());
equation = dynamic_cast<BinaryOpNode*>(equation->replaceTrendVar());
assert(equation);
}
for (auto &equation : static_only_equations)
for (auto& equation : static_only_equations)
{
equation = dynamic_cast<BinaryOpNode *>(equation->replaceTrendVar());
equation = dynamic_cast<BinaryOpNode*>(equation->replaceTrendVar());
assert(equation);
}
}
void
DynamicModel::differentiateForwardVars(const vector<string> &subset)
DynamicModel::differentiateForwardVars(const vector<string>& subset)
{
substituteLeadLagInternal(AuxVarType::diffForward, true, subset);
}
void
DynamicModel::fillEvalContext(eval_context_t &eval_context) const
DynamicModel::fillEvalContext(eval_context_t& eval_context) const
{
// First, auxiliary variables
for (auto aux_equation : aux_equations)
{
assert(aux_equation->op_code == BinaryOpcode::equal);
auto auxvar = dynamic_cast<VariableNode *>(aux_equation->arg1);
auto auxvar = dynamic_cast<VariableNode*>(aux_equation->arg1);
assert(auxvar);
try
{
double val = aux_equation->arg2->eval(eval_context);
eval_context[auxvar->symb_id] = val;
}
catch (ExprNode::EvalException &e)
catch (ExprNode::EvalException& e)
{
// Do nothing
}
}
// Second, model local variables
for (auto &[symb_id, expression] : local_variables_table)
for (auto& [symb_id, expression] : local_variables_table)
{
try
{
double val = expression->eval(eval_context);
eval_context[symb_id] = val;
}
catch (ExprNode::EvalException &e)
catch (ExprNode::EvalException& e)
{
// Do nothing
}
}
//Third, trend variables
// Third, trend variables
for (int trendVar : symbol_table.getTrendVarIds())
eval_context[trendVar] = 2; //not <= 0 bc of log, not 1 bc of powers
eval_context[trendVar] = 2; // not <= 0 bc of log, not 1 bc of powers
}
void
DynamicModel::addStaticOnlyEquation(expr_t eq, optional<int> lineno, const map<string, string> &eq_tags)
DynamicModel::addStaticOnlyEquation(expr_t eq, const optional<int>& lineno,
optional<tuple<int, expr_t, expr_t>> complementarity_condition,
map<string, string> eq_tags)
{
auto beq = dynamic_cast<BinaryOpNode *>(eq);
auto beq = dynamic_cast<BinaryOpNode*>(eq);
assert(beq && beq->op_code == BinaryOpcode::equal);
static_only_equations_equation_tags.add(static_only_equations.size(), eq_tags);
static_only_equations_equation_tags.add(static_only_equations.size(), move(eq_tags));
static_only_equations.push_back(beq);
static_only_equations_lineno.push_back(move(lineno));
static_only_equations_lineno.push_back(lineno);
static_only_complementarity_conditions.push_back(move(complementarity_condition));
}
size_t
......@@ -3439,30 +3863,34 @@ DynamicModel::dynamicOnlyEquationsNbr() const
}
void
DynamicModel::addOccbinEquation(expr_t eq, optional<int> lineno, const map<string, string> &eq_tags, const vector<string> &regimes_bind, const vector<string> &regimes_relax)
DynamicModel::addOccbinEquation(expr_t eq, const optional<int>& lineno, map<string, string> eq_tags,
const vector<string>& constraints_bind,
const vector<string>& constraints_relax)
{
auto beq = dynamic_cast<BinaryOpNode *>(eq);
auto beq = dynamic_cast<BinaryOpNode*>(eq);
assert(beq && beq->op_code == BinaryOpcode::equal);
occbin_regime_trackers[eq_tags.at("name")].addRegime(constraints_bind, constraints_relax);
// Construct the term to be added to the corresponding equation
expr_t basic_term = AddMinus(beq->arg1, beq->arg2);
expr_t term = basic_term;
for (auto &regime : regimes_bind)
for (auto& constraint : constraints_bind)
{
int param_id = symbol_table.getID(ParsingDriver::buildOccbinBindParamName(regime));
int param_id = symbol_table.getID(ParsingDriver::buildOccbinBindParamName(constraint));
term = AddTimes(term, AddVariable(param_id));
}
for (auto &regime : regimes_relax)
for (auto& constraint : constraints_relax)
{
int param_id = symbol_table.getID(ParsingDriver::buildOccbinBindParamName(regime));
int param_id = symbol_table.getID(ParsingDriver::buildOccbinBindParamName(constraint));
term = AddTimes(term, AddMinus(One, AddVariable(param_id)));
}
// Create or update the dynamic equation
optional<int> eqn { equation_tags.getEqnByTag("name", eq_tags.at("name")) };
optional<int> eqn {equation_tags.getEqnByTag("name", eq_tags.at("name"))};
if (eqn)
{
BinaryOpNode *orig_eq { equations[*eqn] };
BinaryOpNode* orig_eq {equations[*eqn]};
/* In the following, we could have kept only orig_eq->arg1, but the
following adds a (somewhat bizarre) support for equation snippets
without “bind” nor “relax” */
......@@ -3473,57 +3901,57 @@ DynamicModel::addOccbinEquation(expr_t eq, optional<int> lineno, const map<strin
{
auto eq_tags_dynamic = eq_tags;
eq_tags_dynamic["dynamic"] = "";
addEquation(AddEqual(term, Zero), lineno, eq_tags_dynamic);
addEquation(AddEqual(term, Zero), lineno, nullopt, eq_tags_dynamic);
}
// Create or update the static equation (corresponding to the pure relax regime)
if (regimes_bind.empty())
if (constraints_bind.empty())
{
/* Similar remark as above. We could have entirely skipped this
equation updating, since normally there is only one such clause,
but the following adds a (somewhat bizarre) support for equation
snippets without “bind” nor “relax” */
optional<int> eqn_static { static_only_equations_equation_tags.getEqnByTag("name", eq_tags.at("name")) };
optional<int> eqn_static {
static_only_equations_equation_tags.getEqnByTag("name", eq_tags.at("name"))};
if (eqn_static)
{
BinaryOpNode *orig_eq { static_only_equations[*eqn_static] };
static_only_equations[*eqn_static] = AddEqual(AddPlus(AddMinus(orig_eq->arg1, orig_eq->arg2), basic_term), Zero);
BinaryOpNode* orig_eq {static_only_equations[*eqn_static]};
static_only_equations[*eqn_static]
= AddEqual(AddPlus(AddMinus(orig_eq->arg1, orig_eq->arg2), basic_term), Zero);
// It’s unclear how to update lineno and tags, so don’t do it
}
else
{
auto eq_tags_static = eq_tags;
eq_tags_static["static"] = "";
addStaticOnlyEquation(AddEqual(basic_term, Zero), lineno, eq_tags_static);
eq_tags["static"] = "";
addStaticOnlyEquation(AddEqual(basic_term, Zero), lineno, nullopt, move(eq_tags));
}
}
}
bool
DynamicModel::isChecksumMatching(const string &basename) const
DynamicModel::isChecksumMatching(const string& basename) const
{
stringstream buffer;
// Write equation tags
equation_tags.writeCheckSumInfo(buffer);
constexpr ExprNodeOutputType buffer_type{ExprNodeOutputType::CDynamicModel};
constexpr ExprNodeOutputType buffer_type {ExprNodeOutputType::CDynamicModel};
deriv_node_temp_terms_t tef_terms;
temporary_terms_t temp_term_union;
writeTemporaryTerms<buffer_type>(temporary_terms_derivatives[0],
temp_term_union, temporary_terms_idxs,
buffer, tef_terms);
writeTemporaryTerms<buffer_type>(temporary_terms_derivatives[0], temp_term_union,
temporary_terms_idxs, buffer, tef_terms);
writeModelEquations<buffer_type>(buffer, temp_term_union);
size_t result = hash<string>{}(buffer.str());
size_t result = hash<string> {}(buffer.str());
// check whether basename directory exist. If not, create it.
// If it does, read old checksum if it exists, return if equal to result
fstream checksum_file;
auto filename = filesystem::path{basename} / "checksum";
auto filename = filesystem::path {basename} / "checksum";
if (!filesystem::create_directory(basename))
{
checksum_file.open(filename, ios::in | ios::binary);
......@@ -3550,7 +3978,7 @@ DynamicModel::isChecksumMatching(const string &basename) const
}
void
DynamicModel::writeJsonOutput(ostream &output) const
DynamicModel::writeJsonOutput(ostream& output) const
{
deriv_node_temp_terms_t tef_terms;
writeJsonModelLocalVariables(output, false, tef_terms);
......@@ -3563,8 +3991,7 @@ DynamicModel::writeJsonOutput(ostream &output) const
output << ", ";
writeJsonVariableMapping(output);
output << R"(, "dynamic_tmp_nbr": [)";
for (bool printed_something {false};
const auto &tts : temporary_terms_derivatives)
for (bool printed_something {false}; const auto& tts : temporary_terms_derivatives)
{
if (exchange(printed_something, true))
output << ", ";
......@@ -3575,7 +4002,7 @@ DynamicModel::writeJsonOutput(ostream &output) const
}
void
DynamicModel::writeJsonAST(ostream &output) const
DynamicModel::writeJsonAST(ostream& output) const
{
vector<pair<string, string>> eqtags;
output << R"("abstract_syntax_tree":[)" << endl;
......@@ -3598,19 +4025,16 @@ DynamicModel::writeJsonAST(ostream &output) const
}
void
DynamicModel::writeJsonVariableMapping(ostream &output) const
DynamicModel::writeJsonVariableMapping(ostream& output) const
{
output << R"("variable_mapping":[)" << endl;
for (bool printed_something{false};
const auto &[var, eqs] : variableMapping)
for (bool printed_something {false}; const auto& [var, eqs] : variableMapping)
{
if (exchange(printed_something, true))
output << ", ";
output << R"({"name": ")" << symbol_table.getName(var) << R"(", "equations":[)";
for (bool printed_something2{false};
int it2 : eqs)
if (auto tmp = equation_tags.getTagValueByEqnAndKey(it2, "name");
tmp)
for (bool printed_something2 {false}; int it2 : eqs)
if (auto tmp = equation_tags.getTagValueByEqnAndKey(it2, "name"); tmp)
{
if (exchange(printed_something2, true))
output << ", ";
......@@ -3622,18 +4046,16 @@ DynamicModel::writeJsonVariableMapping(ostream &output) const
}
void
DynamicModel::writeJsonXrefsHelper(ostream &output, const map<pair<int, int>, set<int>> &xrefmap) const
DynamicModel::writeJsonXrefsHelper(ostream& output,
const map<pair<int, int>, set<int>>& xrefmap) const
{
for (bool printed_something{false};
const auto &[symb_lag, eqs] : xrefmap)
for (bool printed_something {false}; const auto& [symb_lag, eqs] : xrefmap)
{
if (exchange(printed_something, true))
output << ", ";
output << R"({"name": ")" << symbol_table.getName(symb_lag.first) << R"(")"
<< R"(, "shift": )" << symb_lag.second
<< R"(, "equations": [)";
for (bool printed_something2{false};
int eq : eqs)
<< R"(, "shift": )" << symb_lag.second << R"(, "equations": [)";
for (bool printed_something2 {false}; int eq : eqs)
{
if (exchange(printed_something2, true))
output << ", ";
......@@ -3644,7 +4066,7 @@ DynamicModel::writeJsonXrefsHelper(ostream &output, const map<pair<int, int>, se
}
void
DynamicModel::writeJsonXrefs(ostream &output) const
DynamicModel::writeJsonXrefs(ostream& output) const
{
output << R"("xrefs": {)"
<< R"("parameters": [)";
......@@ -3662,7 +4084,7 @@ DynamicModel::writeJsonXrefs(ostream &output) const
}
void
DynamicModel::writeJsonOriginalModelOutput(ostream &output) const
DynamicModel::writeJsonOriginalModelOutput(ostream& output) const
{
writeJsonModelEquations(output, false);
output << ", ";
......@@ -3670,24 +4092,18 @@ DynamicModel::writeJsonOriginalModelOutput(ostream &output) const
}
void
DynamicModel::writeJsonDynamicModelInfo(ostream &output) const
DynamicModel::writeJsonDynamicModelInfo(ostream& output) const
{
output << R"("model_info": {)"
<< R"("lead_lag_incidence": [)";
// Loop on endogenous variables
int nstatic = 0,
nfwrd = 0,
npred = 0,
nboth = 0;
int nstatic = 0, nfwrd = 0, npred = 0, nboth = 0;
for (int endoID = 0; endoID < symbol_table.endo_nbr(); endoID++)
{
if (endoID != 0)
output << ",";
output << "[";
int sstatic = 1,
sfwrd = 0,
spred = 0,
sboth = 0;
int sstatic = 1, sfwrd = 0, spred = 0, sboth = 0;
// Loop on periods
for (int lag = -max_endo_lag; lag <= max_endo_lead; lag++)
{
......@@ -3717,7 +4133,7 @@ DynamicModel::writeJsonDynamicModelInfo(ostream &output) const
}
}
}
catch (UnknownDerivIDException &e)
catch (UnknownDerivIDException& e)
{
output << " 0";
}
......@@ -3733,9 +4149,9 @@ DynamicModel::writeJsonDynamicModelInfo(ostream &output) const
<< R"("nfwrd": )" << nfwrd << ", " << endl
<< R"("npred": )" << npred << ", " << endl
<< R"("nboth": )" << nboth << ", " << endl
<< R"("nsfwrd": )" << nfwrd+nboth << ", " << endl
<< R"("nspred": )" << npred+nboth << ", " << endl
<< R"("ndynamic": )" << npred+nboth+nfwrd << ", " << endl
<< R"("nsfwrd": )" << nfwrd + nboth << ", " << endl
<< R"("nspred": )" << npred + nboth << ", " << endl
<< R"("ndynamic": )" << npred + nboth + nfwrd << ", " << endl
<< R"("maximum_endo_lag": )" << max_endo_lag << ", " << endl
<< R"("maximum_endo_lead": )" << max_endo_lead << ", " << endl
<< R"("maximum_exo_lag": )" << max_exo_lag << ", " << endl
......@@ -3752,63 +4168,56 @@ DynamicModel::writeJsonDynamicModelInfo(ostream &output) const
<< R"("orig_maximum_exo_det_lead": )" << max_exo_det_lead_orig << "," << endl
<< R"("orig_maximum_lag": )" << max_lag_orig << "," << endl
<< R"("orig_maximum_lead": )" << max_lead_orig << "," << endl
<< R"("orig_maximum_lag_with_diffs_expanded": )" << max_lag_with_diffs_expanded_orig
<< "," <<endl
<< R"("NNZDerivatives": [)";
<< R"("orig_maximum_lag_with_diffs_expanded": )" << max_lag_with_diffs_expanded_orig << ","
<< endl
<< R"("NNZDerivatives": [)";
for (int i = 1; i < static_cast<int>(NNZDerivatives.size()); i++)
{
output << (i > computed_derivs_order ? -1 : NNZDerivatives[i]);
if (i < static_cast<int>(NNZDerivatives.size()) - 1)
output << ", ";
output << ", ";
}
output << "]}"
<< endl;
output << "]}" << endl;
}
void
DynamicModel::writeJsonComputingPassOutput(ostream &output, bool writeDetails) const
DynamicModel::writeJsonComputingPassOutput(ostream& output, bool writeDetails) const
{
auto [mlv_output, d_output] { writeJsonComputingPassOutputHelper<true>(writeDetails) };
auto [mlv_output, d_output] {writeJsonComputingPassOutputHelper<true>(writeDetails)};
if (writeDetails)
output << R"("dynamic_model": {)";
else
output << R"("dynamic_model_simple": {)";
output << mlv_output.str();
for (const auto &it : d_output)
for (const auto& it : d_output)
output << ", " << it.str();
output << "}";
}
void
DynamicModel::writeJsonParamsDerivatives(ostream &output, bool writeDetails) const
DynamicModel::writeJsonParamsDerivatives(ostream& output, bool writeDetails) const
{
if (!params_derivatives.size())
return;
auto [mlv_output, tt_output, rp_output, gp_output, rpp_output, gpp_output, hp_output, g3p_output]
{ writeJsonParamsDerivativesHelper<true>(writeDetails) };
auto [mlv_output, tt_output, rp_output, gp_output, rpp_output, gpp_output, hp_output,
g3p_output] {writeJsonParamsDerivativesHelper<true>(writeDetails)};
if (writeDetails)
output << R"("dynamic_model_params_derivative": {)";
else
output << R"("dynamic_model_params_derivatives_simple": {)";
output << mlv_output.str()
<< ", " << tt_output.str()
<< ", " << rp_output.str()
<< ", " << gp_output.str()
<< ", " << rpp_output.str()
<< ", " << gpp_output.str()
<< ", " << hp_output.str()
<< ", " << g3p_output.str()
<< "}";
output << mlv_output.str() << ", " << tt_output.str() << ", " << rp_output.str() << ", "
<< gp_output.str() << ", " << rpp_output.str() << ", " << gpp_output.str() << ", "
<< hp_output.str() << ", " << g3p_output.str() << "}";
}
void
DynamicModel::substituteVarExpectation(const map<string, expr_t> &subst_table)
DynamicModel::substituteVarExpectation(const map<string, expr_t>& subst_table)
{
for (auto &equation : equations)
equation = dynamic_cast<BinaryOpNode *>(equation->substituteVarExpectation(subst_table));
for (auto& equation : equations)
equation = dynamic_cast<BinaryOpNode*>(equation->substituteVarExpectation(subst_table));
}
void
......@@ -3817,7 +4226,8 @@ DynamicModel::checkNoRemainingPacExpectation() const
for (size_t eq = 0; eq < equations.size(); eq++)
if (equations[eq]->containsPacExpectation())
{
cerr << "ERROR: in equation " << equation_tags.getTagValueByEqnAndKey(eq, "name").value_or(to_string(eq+1))
cerr << "ERROR: in equation "
<< equation_tags.getTagValueByEqnAndKey(eq, "name").value_or(to_string(eq + 1))
<< ", the pac_expectation operator references an unknown pac_model" << endl;
exit(EXIT_FAILURE);
}
......@@ -3827,20 +4237,20 @@ void
DynamicModel::simplifyEquations()
{
size_t last_subst_table_size = 0;
map<VariableNode *, NumConstNode *> subst_table;
// Equations with “mcp” tag are excluded, see dynare#1697
findConstantEquationsWithoutMcpTag(subst_table);
map<VariableNode*, NumConstNode*> subst_table;
// Equations with a complementarity condition are excluded, see dynare#1697
findConstantEquationsWithoutComplementarityCondition(subst_table);
while (subst_table.size() != last_subst_table_size)
{
last_subst_table_size = subst_table.size();
for (auto &[id, definition] : local_variables_table)
for (auto& [id, definition] : local_variables_table)
definition = definition->replaceVarsInEquation(subst_table);
for (auto &equation : equations)
equation = dynamic_cast<BinaryOpNode *>(equation->replaceVarsInEquation(subst_table));
for (auto &equation : static_only_equations)
equation = dynamic_cast<BinaryOpNode *>(equation->replaceVarsInEquation(subst_table));
for (auto& equation : equations)
equation = dynamic_cast<BinaryOpNode*>(equation->replaceVarsInEquation(subst_table));
for (auto& equation : static_only_equations)
equation = dynamic_cast<BinaryOpNode*>(equation->replaceVarsInEquation(subst_table));
subst_table.clear();
findConstantEquationsWithoutMcpTag(subst_table);
findConstantEquationsWithoutComplementarityCondition(subst_table);
}
}
......@@ -3850,8 +4260,11 @@ DynamicModel::checkNoRemainingPacTargetNonstationary() const
for (size_t eq = 0; eq < equations.size(); eq++)
if (equations[eq]->containsPacTargetNonstationary())
{
cerr << "ERROR: in equation " << equation_tags.getTagValueByEqnAndKey(eq, "name").value_or(to_string(eq+1))
<< ", the pac_target_nonstationary operator does not match a corresponding 'pac_target_info' block" << endl;
cerr << "ERROR: in equation "
<< equation_tags.getTagValueByEqnAndKey(eq, "name").value_or(to_string(eq + 1))
<< ", the pac_target_nonstationary operator does not match a corresponding "
"'pac_target_info' block"
<< endl;
exit(EXIT_FAILURE);
}
}
......@@ -3861,16 +4274,161 @@ DynamicModel::checkIsLinear() const
{
if (!nonzero_hessian_eqs.empty())
{
cerr << "ERROR: If the model is declared linear the second derivatives must be equal to zero." << endl
cerr << "ERROR: If the model is declared linear the second derivatives must be equal to zero."
<< endl
<< " The following equations have non-zero second derivatives:" << endl;
for (auto it : nonzero_hessian_eqs)
{
cerr << " * Eq # " << it+1;
if (optional<string> eqname { equation_tags.getTagValueByEqnAndKey(it, "name") };
eqname)
cerr << " * Eq # " << it + 1;
if (optional<string> eqname {equation_tags.getTagValueByEqnAndKey(it, "name")}; eqname)
cerr << " [" << *eqname << "]";
cerr << endl;
}
exit(EXIT_FAILURE);
}
}
void
DynamicModel::checkOccbinRegimes() const
{
for (const auto& [eq_name, tracker] : occbin_regime_trackers)
{
try
{
tracker.checkAllRegimesPresent();
}
catch (OccbinRegimeTracker::MissingRegimeException& e)
{
cerr << "ERROR: for equation '" << eq_name << "', the regime corresponding to ";
if (!e.constraints_bind.empty())
{
cerr << "bind='";
for (bool first_printed {false}; const auto& r : e.constraints_bind)
{
if (exchange(first_printed, true))
cerr << ",";
cerr << r;
}
}
if (!e.constraints_bind.empty() && !e.constraints_relax.empty())
cerr << "' and ";
if (!e.constraints_relax.empty())
{
cerr << "relax='";
for (bool first_printed {false}; const auto& r : e.constraints_relax)
{
if (exchange(first_printed, true))
cerr << ",";
cerr << r;
}
}
cerr << "' is not defined" << endl;
exit(EXIT_FAILURE);
}
}
}
void
DynamicModel::OccbinRegimeTracker::addRegime(const vector<string>& constraints_bind,
const vector<string>& constraints_relax)
{
// Check that no constraint appears in both bind and relax
set constraints_bind_sorted(constraints_bind.begin(), constraints_bind.end()),
constraints_relax_sorted(constraints_relax.begin(), constraints_relax.end());
vector<string> constraints_intersect;
ranges::set_intersection(constraints_bind_sorted, constraints_relax_sorted,
back_inserter(constraints_intersect));
if (!constraints_intersect.empty())
throw ConstraintInBothBindAndRelaxException {constraints_intersect.front()};
// If a constraint has never been mentioned before, add it to the list and adapt the regimes
vector<string> constraints_union;
ranges::set_union(constraints_bind_sorted, constraints_relax_sorted,
back_inserter(constraints_union));
for (const auto& c : constraints_union)
if (ranges::find(constraints, c) == constraints.end())
{
constraints.push_back(c);
auto regimes_copy = regimes_present;
regimes_present.clear();
for (const auto& r : regimes_copy)
{
auto r0 = r, r1 = r;
r0.push_back(false);
r1.push_back(true);
regimes_present.insert(r0);
regimes_present.insert(r1);
}
}
// Create the bit vector(s) corresponding to the function arguments
vector<bool> new_regime_template(constraints.size(), false);
for (const auto& c : constraints_bind)
{
int i = distance(constraints.begin(), ranges::find(constraints, c));
new_regime_template[i] = true;
}
set<vector<bool>> new_regimes {new_regime_template};
set all_constraints_sorted(constraints.begin(), constraints.end());
vector<string> constraints_not_mentioned;
ranges::set_difference(all_constraints_sorted, constraints_union,
back_inserter(constraints_not_mentioned));
for (const auto& c : constraints_not_mentioned)
{
int i = distance(constraints.begin(), ranges::find(constraints, c));
auto new_regimes_copy = new_regimes;
for (const auto& r : new_regimes_copy)
{
auto r2 = r;
r2[i] = true;
new_regimes.insert(move(r2));
}
}
// Add the new bit vector(s)
for (const auto& r : new_regimes)
{
auto [it, success] = regimes_present.insert(r);
if (!success)
{
auto [constraints_bind_duplicate, constraints_relax_duplicate]
= convertBitVectorToRegimes(r);
throw RegimeAlreadyPresentException {constraints_bind_duplicate,
constraints_relax_duplicate};
}
}
}
void
DynamicModel::OccbinRegimeTracker::checkAllRegimesPresent() const
{
vector<bool> r(constraints.size(), false);
do
{
if (!regimes_present.contains(r))
{
auto [constraints_bind, constraints_relax] = convertBitVectorToRegimes(r);
throw MissingRegimeException {constraints_bind, constraints_relax};
}
auto it = ranges::find(r, false);
if (it == r.end())
break;
*it = true;
/* NB: cannot use ranges::fill since vector<bool>::iterator does not satisfy
indirectly_writable concept. Should be fixed in C++23: https://wg21.link/p2321 */
fill(r.begin(), it, false);
}
while (true);
}
pair<vector<string>, vector<string>>
DynamicModel::OccbinRegimeTracker::convertBitVectorToRegimes(const vector<bool>& r) const
{
vector<string> constraints_bind, constraints_relax;
for (size_t i = 0; i < constraints.size(); i++)
if (r[i])
constraints_bind.push_back(constraints[i]);
else
constraints_relax.push_back(constraints[i]);
return {constraints_bind, constraints_relax};
}
/*
* Copyright © 2003-2023 Dynare Team
* Copyright © 2003-2024 Dynare Team
*
* This file is part of Dynare.
*
......@@ -17,14 +17,14 @@
* along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef _DYNAMICMODEL_HH
#define _DYNAMICMODEL_HH
#ifndef DYNAMIC_MODEL_HH
#define DYNAMIC_MODEL_HH
#include <fstream>
#include <filesystem>
#include <fstream>
#include "StaticModel.hh"
#include "Bytecode.hh"
#include "StaticModel.hh"
using namespace std;
......@@ -34,23 +34,58 @@ class DynamicModel : public ModelTree
friend class StaticModel; // For reading static_mfs from converting constructor
public:
//! A reference to the trend component model table
TrendComponentModelTable &trend_component_model_table;
TrendComponentModelTable& trend_component_model_table;
//! A reference to the VAR model table
VarModelTable &var_model_table;
VarModelTable& var_model_table;
/* Used in the balanced growth test, for determining whether the
cross-derivative of a given equation, w.r.t. an endogenous and a trend
variable is zero. Controlled by option “balanced_growth_test_tol” of the
“model” block. The default should not be too small (see dynare#1389). */
double balanced_growth_test_tol{1e-6};
double balanced_growth_test_tol {1e-6};
/* For a given equation, tracks all the regimes and the declared alternatives with combinations of
bind and relax tags */
class OccbinRegimeTracker
{
private:
// The list of constraints used for this equation
vector<string> constraints;
/* The list of regimes present for this equation; each regime is a vector of boolean, of same
length as “constraints”; each boolean represents a constraint (in the order of
“constraints”): false for relax, true for bind */
set<vector<bool>> regimes_present;
public:
struct ConstraintInBothBindAndRelaxException
{
const string constraint;
};
struct RegimeAlreadyPresentException
{
const vector<string> constraints_bind, constraints_relax;
};
void addRegime(const vector<string>& constraints_bind,
const vector<string>& constraints_relax) noexcept(false);
struct MissingRegimeException
{
const vector<string> constraints_bind, constraints_relax;
};
void checkAllRegimesPresent() const noexcept(false);
private:
[[nodiscard]] pair<vector<string>, vector<string>>
convertBitVectorToRegimes(const vector<bool>& r) const;
};
private:
/* Used in the balanced growth test, for skipping equations where the test
cannot be performed (i.e. when LHS=RHS at the initial values). Should not
be too large, otherwise the test becomes less powerful. */
constexpr static double zero_band{1e-8};
constexpr static double zero_band {1e-8};
//! Stores equations declared as [static]
/*! They will be used in the conversion to StaticModel to replace equations marked as [dynamic] */
vector<BinaryOpNode *> static_only_equations;
vector<BinaryOpNode*> static_only_equations;
//! Stores line numbers of equations declared as [static]
vector<optional<int>> static_only_equations_lineno;
......@@ -58,6 +93,9 @@ private:
//! Stores the equation tags of equations declared as [static]
EquationTags static_only_equations_equation_tags;
// Complementarity conditions of equations declared as [static]
vector<optional<tuple<int, expr_t, expr_t>>> static_only_complementarity_conditions;
using deriv_id_table_t = map<pair<int, int>, int>;
//! Maps a pair (symbol_id, lag) to a deriv ID
deriv_id_table_t deriv_id_table;
......@@ -73,24 +111,25 @@ private:
//! Maximum lag and lead over all types of variables (positive values)
/*! Set by computeDerivIDs() */
int max_lag{0}, max_lead{0};
int max_lag {0}, max_lead {0};
//! Maximum lag and lead over endogenous variables (positive values)
/*! Set by computeDerivIDs() */
int max_endo_lag{0}, max_endo_lead{0};
int max_endo_lag {0}, max_endo_lead {0};
//! Maximum lag and lead over exogenous variables (positive values)
/*! Set by computeDerivIDs() */
int max_exo_lag{0}, max_exo_lead{0};
int max_exo_lag {0}, max_exo_lead {0};
//! Maximum lag and lead over deterministic exogenous variables (positive values)
/*! Set by computeDerivIDs() */
int max_exo_det_lag{0}, max_exo_det_lead{0};
int max_exo_det_lag {0}, max_exo_det_lead {0};
//! Maximum lag and lead over all types of variables (positive values) of original model
int max_lag_orig{0}, max_lead_orig{0}, max_lag_with_diffs_expanded_orig{0};
int max_lag_orig {0}, max_lead_orig {0}, max_lag_with_diffs_expanded_orig {0};
//! Maximum lag and lead over endogenous variables (positive values) of original model
int max_endo_lag_orig{0}, max_endo_lead_orig{0};
int max_endo_lag_orig {0}, max_endo_lead_orig {0};
//! Maximum lag and lead over exogenous variables (positive values) of original model
int max_exo_lag_orig{0}, max_exo_lead_orig{0};
//! Maximum lag and lead over deterministic exogenous variables (positive values) of original model
int max_exo_det_lag_orig{0}, max_exo_det_lead_orig{0};
int max_exo_lag_orig {0}, max_exo_lead_orig {0};
//! Maximum lag and lead over deterministic exogenous variables (positive values) of original
//! model
int max_exo_det_lag_orig {0}, max_exo_det_lead_orig {0};
// Cross reference information: eq → set of (symb_id, lag) for each symbol type
map<int, ExprNode::EquationInfo> xrefs;
......@@ -111,30 +150,30 @@ private:
map<string, set<int>> var_expectation_functions_to_write;
// Value of the “mfs” option of “model” block (or ”model_options” command)
int mfs{1};
int mfs {1};
/* Value of the “static_mfs” option of “model” block (or the “model_options”
command).
Only used when converting to StaticModel class. */
int static_mfs{0};
int static_mfs {0};
// Writes dynamic model file (MATLAB/Octave version, legacy representation)
void writeDynamicMFile(const string &basename) const;
void writeDynamicMFile(const string& basename) const;
//! Writes the code of the block-decomposed model in virtual machine bytecode
void writeDynamicBlockBytecode(const string &basename) const;
void writeDynamicBlockBytecode(const string& basename) const;
//! Writes the code of the model in virtual machine bytecode
void writeDynamicBytecode(const string &basename) const;
void writeDynamicBytecode(const string& basename) const;
// Write the block structure of the model in the driver file
void writeBlockDriverOutput(ostream &output) const;
void writeBlockDriverOutput(ostream& output) const;
// Used by determineBlockDerivativesType()
enum class BlockDerivativeType
{
standard,
chainRule,
normalizedChainRule
};
{
standard,
chainRule,
normalizedChainRule
};
/* For each tuple (lag, eq, var) within the given block, determine the type
of the derivative to be computed. Indices are within the block (i.e.
......@@ -143,7 +182,7 @@ private:
void computeChainRuleJacobian() override;
string reform(const string &name) const;
string reform(const string& name) const;
SymbolType getTypeByDerivID(int deriv_id) const noexcept(false) override;
int getLagByDerivID(int deriv_id) const noexcept(false) override;
......@@ -153,10 +192,11 @@ private:
//! Compute the column indices of the dynamic Jacobian
void computeDynJacobianCols();
//! Computes derivatives of the Jacobian w.r. to trend vars and tests that they are equal to zero
void testTrendDerivativesEqualToZero(const eval_context_t &eval_context);
void testTrendDerivativesEqualToZero(const eval_context_t& eval_context);
//! Allocates the derivation IDs for all dynamic variables of the model
/*! Also computes max_{endo,exo}_{lead_lag}, and initializes dynJacobianColsNbr to the number of dynamic endos */
/*! Also computes max_{endo,exo}_{lead_lag}, and initializes dynJacobianColsNbr to the number of
* dynamic endos */
void computeDerivIDs();
/* Compute the Jacobian column indices in the block decomposition case
......@@ -167,37 +207,39 @@ private:
//! Factorized code for substitutions of leads/lags
/*! \param[in] type determines which type of variables is concerned
\param[in] deterministic_model whether we are in a deterministic model (only for exogenous leads/lags)
\param[in] subset variables to which to apply the transformation (only for diff of forward vars)
\param[in] deterministic_model whether we are in a deterministic model (only for exogenous
leads/lags) \param[in] subset variables to which to apply the transformation (only for diff of
forward vars)
*/
void substituteLeadLagInternal(AuxVarType type, bool deterministic_model, const vector<string> &subset);
void substituteLeadLagInternal(AuxVarType type, bool deterministic_model,
const vector<string>& subset);
//! Help computeXrefs to compute the reverse references (i.e. param->eqs, endo->eqs, etc)
void computeRevXref(map<pair<int, int>, set<int>> &xrefset, const set<pair<int, int>> &eiref, int eqn);
void computeRevXref(map<pair<int, int>, set<int>>& xrefset, const set<pair<int, int>>& eiref,
int eqn);
//! Write reverse cross references
void writeRevXrefs(ostream &output, const map<pair<int, int>, set<int>> &xrefmap, const string &type) const;
void writeRevXrefs(ostream& output, const map<pair<int, int>, set<int>>& xrefmap,
const string& type) const;
/* Writes MATLAB/Octave wrapper function for computing residuals and
derivatives at the same time (legacy representation) */
void writeDynamicMWrapperFunction(const string &name, const string &ending) const;
void writeDynamicMWrapperFunction(const string& name, const string& ending) const;
/* Helper for writing MATLAB/Octave functions for residuals/derivatives and
their temporary terms (legacy representation) */
void writeDynamicMFileHelper(const string &basename,
const string &name, const string &retvalname,
const string &name_tt, size_t ttlen,
const string &previous_tt_name,
const ostringstream &init_s, const ostringstream &end_s,
const ostringstream &s, const ostringstream &s_tt) const;
void writeDynamicMFileHelper(const string& basename, const string& name, const string& retvalname,
const string& name_tt, size_t ttlen, const string& previous_tt_name,
const ostringstream& init_s, const ostringstream& end_s,
const ostringstream& s, const ostringstream& s_tt) const;
/* Create the compatibility dynamic.m file for MATLAB/Octave not yet using
the temporary terms array interface (legacy representation) */
void writeDynamicMCompatFile(const string &basename) const;
void writeDynamicMCompatFile(const string& basename) const;
//! Internal helper for the copy constructor and assignment operator
/*! Copies all the structures that contain ExprNode*, by the converting the
pointers into their equivalent in the new tree */
void copyHelper(const DynamicModel &m);
void copyHelper(const DynamicModel& m);
/* Handles parsing of argument passed to exclude_eqs/include_eqs.
......@@ -221,11 +263,13 @@ private:
Returns a set of pairs (tag name, tag value) corresponding to the set of
equations to be included or excluded.
*/
static vector<pair<string, string>> parseIncludeExcludeEquations(const string &inc_exc_option_value, bool exclude_eqs);
static vector<map<string, string>>
parseIncludeExcludeEquations(const string& inc_exc_option_value, bool exclude_eqs);
/* Helper for the removeEquations() method.
listed_eqs_by_tag is the list of (tag name, tag value) pairs corresponding
to the option value, exclude_eqs is a boolean indicating whether we’re
listed_eqs_by_tag describes a list of equations to remove (identified by
one or more tags; if multiple tags are present for a single equation, they
are understood as a conjunction), exclude_eqs is a boolean indicating whether we’re
excluding or including, and excluded_vars_change_type is a boolean
indicating whether to compute variables to be excluded.
......@@ -233,18 +277,17 @@ private:
equations. They are either the main structures for storing equations in
ModelTree, or their counterpart for static-only equations. The
static_equations boolean indicates when we are in the latter case.
The listed_eqs_by_tag structure will be updated by removing those tag
pairs that have been matched with equations in the all_equations*
argument*.
The listed_eqs_by_tag structure will be updated by removing the tags
matched with equations in the all_equations* argument*.
Returns a list of excluded variables (empty if
excluded_vars_change_type=false) */
vector<int> removeEquationsHelper(set<pair<string, string>> &listed_eqs_by_tag,
bool exclude_eqs, bool excluded_vars_change_type,
vector<BinaryOpNode *> &all_equations,
vector<optional<int>> &all_equations_lineno,
EquationTags &all_equation_tags,
bool static_equations) const;
vector<int> removeEquationsHelper(
set<map<string, string>>& listed_eqs_by_tag, bool exclude_eqs, bool excluded_vars_change_type,
vector<BinaryOpNode*>& all_equations, vector<optional<int>>& all_equations_lineno,
vector<optional<tuple<int, expr_t, expr_t>>>& all_complementarity_conditions,
EquationTags& all_equation_tags, bool static_equations) const;
//! Compute autoregressive matrices of trend component models
/* The algorithm uses matching rules over expression trees. It cannot handle
......@@ -253,7 +296,8 @@ private:
//! Compute error component matrices of trend component_models
/*! Returns a pair (A0r, A0starr) */
pair<map<string, map<tuple<int, int>, expr_t>>, map<string, map<tuple<int, int>, expr_t>>> computeErrorComponentMatrices(const ExprNode::subst_table_t &diff_subst_table) const;
pair<map<string, map<tuple<int, int>, expr_t>>, map<string, map<tuple<int, int>, expr_t>>>
computeErrorComponentMatrices(const ExprNode::subst_table_t& diff_subst_table) const;
/* For a VAR model, given the symbol ID of a LHS variable, and a (negative)
lag, returns all the corresponding deriv_ids (by properly dealing with two
......@@ -266,9 +310,12 @@ private:
int
getBlockJacobianEndoCol(int blk, int var, int lag) const override
{
return blocks_jacob_cols_endo[blk].at({ var, lag });
return blocks_jacob_cols_endo[blk].at({var, lag});
}
// Used to check consistency of bind/relax tags; the keys are equation names
map<string, OccbinRegimeTracker> occbin_regime_trackers;
protected:
string
modelClassName() const override
......@@ -277,60 +324,61 @@ protected:
}
public:
DynamicModel(SymbolTable &symbol_table_arg,
NumericalConstants &num_constants_arg,
ExternalFunctionsTable &external_functions_table_arg,
TrendComponentModelTable &trend_component_model_table_arg,
VarModelTable &var_model_table_arg);
DynamicModel(SymbolTable& symbol_table_arg, NumericalConstants& num_constants_arg,
ExternalFunctionsTable& external_functions_table_arg,
HeterogeneityTable& heterogeneity_table_arg,
TrendComponentModelTable& trend_component_model_table_arg,
VarModelTable& var_model_table_arg);
DynamicModel(const DynamicModel &m);
DynamicModel &operator=(const DynamicModel &m);
DynamicModel(const DynamicModel& m);
DynamicModel& operator=(const DynamicModel& m);
//! Compute cross references
void computeXrefs();
//! Write cross references
void writeXrefs(ostream &output) const;
void writeXrefs(ostream& output) const;
//! Execute computations (variable sorting + derivation + block decomposition)
/*!
\param derivsOrder order of derivatives w.r. to exo, exo_det and endo should be computed (implies jacobianExo = true when order >= 2)
\param paramsDerivsOrder order of derivatives w.r. to a pair (endo/exo/exo_det, parameter) to be computed (>0 implies jacobianExo = true)
\param eval_context evaluation context for normalization
\param no_tmp_terms if true, no temporary terms will be computed in the dynamic files
\param derivsOrder order of derivatives w.r. to exo, exo_det and endo should be computed
(implies jacobianExo = true when order >= 2) \param paramsDerivsOrder order of derivatives w.r.
to a pair (endo/exo/exo_det, parameter) to be computed (>0 implies jacobianExo = true) \param
eval_context evaluation context for normalization \param no_tmp_terms if true, no temporary
terms will be computed in the dynamic files
*/
void computingPass(int derivsOrder, int paramsDerivsOrder, const eval_context_t &eval_context,
void computingPass(int derivsOrder, int paramsDerivsOrder, const eval_context_t& eval_context,
bool no_tmp_terms, bool block, bool use_dll);
//! Writes information about the dynamic model to the driver file
void writeDriverOutput(ostream &output, bool compute_xrefs) const;
void writeDriverOutput(ostream& output, bool compute_xrefs) const;
//! Write JSON AST
void writeJsonAST(ostream &output) const;
void writeJsonAST(ostream& output) const;
//! Write JSON variable mapping
void writeJsonVariableMapping(ostream &output) const;
void writeJsonVariableMapping(ostream& output) const;
//! Write JSON Output
void writeJsonOutput(ostream &output) const;
void writeJsonOutput(ostream& output) const;
//! Write JSON Output representation of original dynamic model
void writeJsonOriginalModelOutput(ostream &output) const;
void writeJsonOriginalModelOutput(ostream& output) const;
//! Write JSON Output representation of model info (useful stuff from M_)
void writeJsonDynamicModelInfo(ostream &output) const;
void writeJsonDynamicModelInfo(ostream& output) const;
//! Write JSON Output representation of dynamic model after computing pass
void writeJsonComputingPassOutput(ostream &output, bool writeDetails) const;
void writeJsonComputingPassOutput(ostream& output, bool writeDetails) const;
//! Write JSON params derivatives
void writeJsonParamsDerivatives(ostream &output, bool writeDetails) const;
void writeJsonParamsDerivatives(ostream& output, bool writeDetails) const;
//! Write cross reference output if the xref maps have been filed
void writeJsonXrefs(ostream &output) const;
void writeJsonXrefsHelper(ostream &output, const map<pair<int, int>, set<int>> &xrefmap) const;
void writeJsonXrefs(ostream& output) const;
void writeJsonXrefsHelper(ostream& output, const map<pair<int, int>, set<int>>& xrefmap) const;
//! Print equations that have non-zero second derivatives
void printNonZeroHessianEquations(ostream &output) const;
void printNonZeroHessianEquations(ostream& output) const;
//! Tells whether Hessian has been computed
/*! This is needed to know whether no non-zero equation in Hessian means a
......@@ -356,7 +404,7 @@ public:
components, available from the transformed model. Needs to be called after
fillTrendComponentModelTableFromOrigModel() has been called on the
original model */
void fillTrendComponentModelTableAREC(const ExprNode::subst_table_t &diff_subst_table) const;
void fillTrendComponentModelTableAREC(const ExprNode::subst_table_t& diff_subst_table) const;
//! Fill the VAR model table with information available from the transformed model
// NB: Does not fill the AR and A0 matrices
......@@ -373,16 +421,18 @@ public:
void updateVarAndTrendModel() const;
//! Writes dynamic model file (+ bytecode)
void writeDynamicFile(const string &basename, bool use_dll, const string &mexext, const filesystem::path &matlabroot, bool julia) const;
void writeDynamicFile(const string& basename, bool use_dll, const string& mexext,
const filesystem::path& matlabroot, bool julia) const;
//! Writes file containing parameters derivatives
template<bool julia>
void writeParamsDerivativesFile(const string &basename) const;
void writeParamsDerivativesFile(const string& basename) const;
//! Creates mapping for variables and equations they are present in
void createVariableMapping();
//! Expands equation tags with default equation names (available "name" tag or LHS variable or equation ID)
//! Expands equation tags with default equation names (available "name" tag or LHS variable or
//! equation ID)
void expandEqTags();
//! Find endogenous variables not used in model
......@@ -394,12 +444,14 @@ public:
void setLeadsLagsOrig();
//! Implements the include_eqs/exclude_eqs options
void includeExcludeEquations(const string &inc_exc_option_value, bool exclude_eqs);
void includeExcludeEquations(const string& inc_exc_option_value, bool exclude_eqs);
/* Removes equations from the model (identified by their name tags).
/* Removes equations from the model (identified by one or more tags; if
multiple tags are present for a single equation, they are understood as a
conjunction).
Used for include_eqs/exclude_eqs options and for model_remove and
model_replace blocks */
void removeEquations(const vector<pair<string, string>> &listed_eqs_by_tag, bool exclude_eqs,
void removeEquations(const vector<map<string, string>>& listed_eqs_by_tag, bool exclude_eqs,
bool excluded_vars_change_type);
/* Replaces model equations with derivatives of Lagrangian w.r.t. endogenous.
......@@ -408,16 +460,19 @@ public:
Returns the number of optimality FOCs, which is by construction equal to
the number of endogenous before adding the Lagrange multipliers
(internally called ramsey_endo_nbr). */
int computeRamseyPolicyFOCs(const StaticModel &static_model);
int computeRamseyPolicyFOCs(const StaticModel& planner_objective,
map<int, pair<expr_t, expr_t>> cloned_ramsey_constraints);
//! Clears all equations
void clearEquations();
//! Replaces the model equations in dynamic_model with those in this model
void replaceMyEquations(DynamicModel &dynamic_model) const;
void replaceMyEquations(DynamicModel& dynamic_model) const;
//! Adds an equation marked as [static]
void addStaticOnlyEquation(expr_t eq, optional<int> lineno, const map<string, string> &eq_tags);
void addStaticOnlyEquation(expr_t eq, const optional<int>& lineno,
optional<tuple<int, expr_t, expr_t>> complementarity_condition,
map<string, string> eq_tags);
//! Returns number of static only equations
size_t staticOnlyEquationsNbr() const;
......@@ -430,13 +485,15 @@ public:
auxiliary parameters have already been added to the symbol table.
It also assumes that the “bind” and “relax” tags have been cleared from
eq_tags. */
void addOccbinEquation(expr_t eq, optional<int> lineno, const map<string, string> &eq_tags, const vector<string> &regimes_bind, const vector<string> &regimes_relax);
void addOccbinEquation(expr_t eq, const optional<int>& lineno, map<string, string> eq_tags,
const vector<string>& constraints_bind,
const vector<string>& constraints_relax);
//! Writes LaTeX file with the equations of the dynamic model
void writeLatexFile(const string &basename, bool write_equation_tags) const;
void writeLatexFile(const string& basename, bool write_equation_tags) const;
//! Writes LaTeX file with the equations of the dynamic model (for the original model)
void writeLatexOriginalFile(const string &basename, bool write_equation_tags) const;
void writeLatexOriginalFile(const string& basename, bool write_equation_tags) const;
int getDerivID(int symb_id, int lag) const noexcept(false) override;
......@@ -451,25 +508,24 @@ public:
if (type == SymbolType::endogenous)
{
assert(lag >= -1 && lag <= 1);
return tsid+(lag+1)*symbol_table.endo_nbr();
return tsid + (lag + 1) * symbol_table.endo_nbr();
}
else if (type == SymbolType::exogenous)
{
assert(lag == 0);
return tsid+3*symbol_table.endo_nbr();
return tsid + 3 * symbol_table.endo_nbr();
}
else if (type == SymbolType::exogenousDet)
{
assert(lag == 0);
return tsid+3*symbol_table.endo_nbr()+symbol_table.exo_nbr();
return tsid + 3 * symbol_table.endo_nbr() + symbol_table.exo_nbr();
}
else
throw UnknownDerivIDException();
}
else
{
if (auto it = dyn_jacobian_cols_table.find(deriv_id);
it == dyn_jacobian_cols_table.end())
if (auto it = dyn_jacobian_cols_table.find(deriv_id); it == dyn_jacobian_cols_table.end())
throw UnknownDerivIDException();
else
return it->second;
......@@ -478,22 +534,15 @@ public:
int
getJacobianColsNbr(bool sparse) const override
{
return sparse ?
3*symbol_table.endo_nbr() + symbol_table.exo_nbr() + symbol_table.exo_det_nbr() :
dyn_jacobian_ncols;
return sparse
? 3 * symbol_table.endo_nbr() + symbol_table.exo_nbr() + symbol_table.exo_det_nbr()
: dyn_jacobian_ncols;
}
void addAllParamDerivId(set<int> &deriv_id_set) override;
//! Returns true indicating that this is a dynamic model
bool
isDynamic() const override
{
return true;
};
void addAllParamDerivId(set<int>& deriv_id_set) override;
//! Drive test of detrended equations
void runTrendTest(const eval_context_t &eval_context);
void runTrendTest(const eval_context_t& eval_context);
//! Transforms the model by removing all leads greater or equal than 2 on endos
/*! Note that this can create new lags on endos and exos */
......@@ -512,25 +561,30 @@ public:
//! Transforms the model by removing all UnaryOpcode::expectation
void substituteExpectation(bool partial_information_model);
//! Transforms the model by decreasing the lead/lag of predetermined variables in model equations by one
//! Transforms the model by decreasing the lead/lag of predetermined variables in model equations
//! by one
void transformPredeterminedVariables();
// Performs the transformations associated to variables declared with “var(log)”
void substituteLogTransform();
/* Performs the transformations associated to aggregation operators in heterogeneous models, such
as SUM(…) */
void substituteAggregationOperators();
// Check that no variable was declared with “var(log)” in the given equations
void checkNoWithLogTransform(const set<int> &eqnumbers);
void checkNoWithLogTransform(const set<int>& eqnumbers);
//! Transforms the model by removing trends specified by the user
void detrendEquations();
const nonstationary_symbols_map_t &
const nonstationary_symbols_map_t&
getNonstationarySymbolsMap() const
{
return nonstationary_symbols_map;
}
const map<int, expr_t> &
const map<int, expr_t>&
getTrendSymbolsMap() const
{
return trend_symbols_map;
......@@ -545,20 +599,28 @@ public:
/* Creates aux vars for all unary operators in all equations. Also makes the
substitution in growth terms of pac_model/pac_target_info and in
expressions of var_expectation_model. */
pair<lag_equivalence_table_t, ExprNode::subst_table_t> substituteUnaryOps(VarExpectationModelTable &var_expectation_model_table, PacModelTable &pac_model_table);
pair<lag_equivalence_table_t, ExprNode::subst_table_t>
substituteUnaryOps(VarExpectationModelTable& var_expectation_model_table,
PacModelTable& pac_model_table);
/* Creates aux vars for all unary operators in specified equations. Also makes the
substitution in growth terms of pac_model/pac_target_info and in
expressions of var_expectation_model. */
pair<lag_equivalence_table_t, ExprNode::subst_table_t> substituteUnaryOps(const set<int> &eqnumbers, VarExpectationModelTable &var_expectation_model_table, PacModelTable &pac_model_table);
pair<lag_equivalence_table_t, ExprNode::subst_table_t>
substituteUnaryOps(const set<int>& eqnumbers,
VarExpectationModelTable& var_expectation_model_table,
PacModelTable& pac_model_table);
//! Substitutes diff operator
pair<lag_equivalence_table_t, ExprNode::subst_table_t> substituteDiff(VarExpectationModelTable &var_expectation_model_table, PacModelTable &pac_model_table);
pair<lag_equivalence_table_t, ExprNode::subst_table_t>
substituteDiff(VarExpectationModelTable& var_expectation_model_table,
PacModelTable& pac_model_table);
//! Substitute VarExpectation operators
void substituteVarExpectation(const map<string, expr_t> &subst_table);
void substituteVarExpectation(const map<string, expr_t>& subst_table);
void analyzePacEquationStructure(const string &name, map<string, string> &pac_eq_name, PacModelTable::equation_info_t &pac_equation_info);
void analyzePacEquationStructure(const string& name, map<string, string>& pac_eq_name,
PacModelTable::equation_info_t& pac_equation_info);
// Exception thrown by getPacTargetSymbId()
struct PacTargetNotIdentifiedException
......@@ -567,7 +629,7 @@ public:
};
//! Return target of the pac equation
int getPacTargetSymbId(const string &pac_model_name) const;
int getPacTargetSymbId(const string& pac_model_name) const;
/* For a PAC MCE model, fill pac_expectation_substitution with the
expression that will be substituted for the pac_expectation operator.
......@@ -575,29 +637,34 @@ public:
The symbol IDs of the new endogenous are added to pac_aux_var_symb_ids,
and the new auxiliary parameters to pac_mce_alpha_symb_ids.
*/
void computePacModelConsistentExpectationSubstitution(const string &name,
int discount_symb_id, int pac_eq_max_lag,
expr_t growth_correction_term,
string auxname,
ExprNode::subst_table_t &diff_subst_table,
map<string, int> &pac_aux_var_symb_ids,
map<string, vector<int>> &pac_aux_param_symb_ids,
map<string, expr_t> &pac_expectation_substitution);
void computePacModelConsistentExpectationSubstitution(
const string& name, int discount_symb_id, int pac_eq_max_lag, expr_t growth_correction_term,
string auxname, ExprNode::subst_table_t& diff_subst_table,
map<string, int>& pac_aux_var_symb_ids, map<string, vector<int>>& pac_aux_param_symb_ids,
map<string, expr_t>& pac_expectation_substitution);
/* For a PAC MCE model with an associated pac_target_info, fill pac_expectation_substitution with
the expression that will be substituted for the pac_expectation operator. In the process, add
the variables and the equations defining Z₁ and Z₀ for each component. The new auxiliary
parameters are added to pac_mce_alpha_symb_ids. The routine also creates the auxiliary
variables for the components, and adds the corresponding equations.
*/
void computePacModelConsistentExpectationSubstitutionWithComponents(
const string& name, int discount_symb_id, int pac_eq_max_lag,
ExprNode::subst_table_t& diff_subst_table, map<string, vector<int>>& pac_aux_param_symb_ids,
vector<PacModelTable::target_component_t>& pac_target_components,
map<string, expr_t>& pac_expectation_substitution);
/* For a PAC backward model, fill pac_expectation_substitution with the
expression that will be substituted for the pac_expectation operator.
The symbol IDs of the new parameters are also added to pac_aux_param_symb_ids.
The symbol ID of the new auxiliary variable is added to pac_aux_var_symb_ids. */
void computePacBackwardExpectationSubstitution(const string &name,
const vector<int> &lhs,
int max_lag,
const string &aux_model_type,
expr_t growth_correction_term,
string auxname,
map<string, int> &pac_aux_var_symb_ids,
map<string, vector<int>> &pac_aux_param_symb_ids,
map<string, expr_t> &pac_expectation_substitution);
void computePacBackwardExpectationSubstitution(const string& name, const vector<int>& lhs,
int max_lag, const string& aux_model_type,
expr_t growth_correction_term, string auxname,
map<string, int>& pac_aux_var_symb_ids,
map<string, vector<int>>& pac_aux_param_symb_ids,
map<string, expr_t>& pac_expectation_substitution);
/* Same as above, but for PAC models which have an associated
pac_target_info.
......@@ -607,23 +674,21 @@ public:
in target_components.
The routine also creates the auxiliary variables for the components, and
adds the corresponding equations. */
void computePacBackwardExpectationSubstitutionWithComponents(const string &name,
const vector<int> &lhs,
int max_lag,
const string &aux_model_type,
vector<PacModelTable::target_component_t> &pac_target_components,
map<string, expr_t> &pac_expectation_substitution);
void computePacBackwardExpectationSubstitutionWithComponents(
const string& name, const vector<int>& lhs, int max_lag, const string& aux_model_type,
vector<PacModelTable::target_component_t>& pac_target_components,
map<string, expr_t>& pac_expectation_substitution);
//! Substitutes pac_expectation operator with expectation based on auxiliary model
void substitutePacExpectation(const map<string, expr_t> &pac_expectation_substitution,
const map<string, string> &pac_eq_name);
void substitutePacExpectation(const map<string, expr_t>& pac_expectation_substitution,
const map<string, string>& pac_eq_name);
//! Substitutes the pac_target_nonstationary operator of a given pac_model
void substitutePacTargetNonstationary(const string &pac_model_name, expr_t substexpr);
void substitutePacTargetNonstationary(const string& pac_model_name, expr_t substexpr);
//! Table to undiff LHS variables for pac vector z
vector<int> getUndiffLHSForPac(const string &aux_model_name,
const ExprNode::subst_table_t &diff_subst_table) const;
vector<int> getUndiffLHSForPac(const string& aux_model_name,
const ExprNode::subst_table_t& diff_subst_table) const;
//! Transforms the model by replacing trend variables with a 1
void removeTrendVariableFromEquations();
......@@ -631,10 +696,10 @@ public:
//! Transforms the model by creating aux vars for the diff of forward vars
/*! If subset is empty, does the transformation for all fwrd vars; otherwise
restrict it to the vars in subset */
void differentiateForwardVars(const vector<string> &subset);
void differentiateForwardVars(const vector<string>& subset);
//! Fills eval context with values of model local variables and auxiliary variables
void fillEvalContext(eval_context_t &eval_context) const;
void fillEvalContext(eval_context_t& eval_context) const;
/*! Checks that all pac_expectation operators have been substituted, error
out otherwise */
......@@ -647,20 +712,22 @@ public:
auto
getStaticOnlyEquationsInfo() const
{
return tuple{static_only_equations, static_only_equations_lineno, static_only_equations_equation_tags};
};
return tuple {static_only_equations, static_only_equations_lineno,
static_only_complementarity_conditions, static_only_equations_equation_tags};
}
//! Returns true if a parameter was used in the model block with a lead or lag
bool ParamUsedWithLeadLag() const;
bool isChecksumMatching(const string &basename) const;
bool isChecksumMatching(const string& basename) const;
//! Simplify model equations: if a variable is equal to a constant, replace that variable elsewhere in the model
//! Simplify model equations: if a variable is equal to a constant, replace that variable
//! elsewhere in the model
/*! Equations with MCP tags are excluded, see dynare#1697 */
void simplifyEquations();
// Converts a set of equation tags into the corresponding set of equation numbers
set<int> getEquationNumbersFromTags(const set<string> &eqtags) const;
set<int> getEquationNumbersFromTags(const set<string>& eqtags) const;
// Returns the set of equations (as numbers) which have a pac_expectation operator
set<int> findPacExpectationEquationNumbers() const;
......@@ -682,21 +749,25 @@ public:
{
static_mfs = static_mfs_arg;
}
// Checks that all alternatives are declared for all Occbin regimes in all equations
void checkOccbinRegimes() const;
};
template<bool julia>
void
DynamicModel::writeParamsDerivativesFile(const string &basename) const
DynamicModel::writeParamsDerivativesFile(const string& basename) const
{
if (!params_derivatives.size())
return;
constexpr ExprNodeOutputType output_type { julia ? ExprNodeOutputType::juliaDynamicModel : ExprNodeOutputType::matlabDynamicModel };
constexpr ExprNodeOutputType output_type {julia ? ExprNodeOutputType::juliaDynamicModel
: ExprNodeOutputType::matlabDynamicModel};
auto [tt_output, rp_output, gp_output, rpp_output, gpp_output, hp_output, g3p_output]
{ writeParamsDerivativesFileHelper<output_type>() };
auto [tt_output, rp_output, gp_output, rpp_output, gpp_output, hp_output,
g3p_output] {writeParamsDerivativesFileHelper<output_type>()};
if constexpr(!julia)
if constexpr (!julia)
{
filesystem::path filename {packageDir(basename) / "dynamic_params_derivs.m"};
ofstream paramsDerivsFile {filename, ios::out | ios::binary};
......@@ -705,80 +776,160 @@ DynamicModel::writeParamsDerivativesFile(const string &basename) const
cerr << "ERROR: Can't open file " << filename.string() << " for writing" << endl;
exit(EXIT_FAILURE);
}
paramsDerivsFile << "function [rp, gp, rpp, gpp, hp, g3p] = dynamic_params_derivs(y, x, params, steady_state, it_, ss_param_deriv, ss_param_2nd_deriv)" << endl
<< "%" << endl
<< "% Compute the derivatives of the dynamic model with respect to the parameters" << endl
<< "% Inputs :" << endl
<< "% y [#dynamic variables by 1] double vector of endogenous variables in the order stored" << endl
<< "% in M_.lead_lag_incidence; see the Manual" << endl
<< "% x [nperiods by M_.exo_nbr] double matrix of exogenous variables (in declaration order)" << endl
<< "% for all simulation periods" << endl
<< "% params [M_.param_nbr by 1] double vector of parameter values in declaration order" << endl
<< "% steady_state [M_.endo_nbr by 1] double vector of steady state values" << endl
<< "% it_ scalar double time period for exogenous variables for which to evaluate the model" << endl
<< "% ss_param_deriv [M_.eq_nbr by #params] Jacobian matrix of the steady states values with respect to the parameters" << endl
<< "% ss_param_2nd_deriv [M_.eq_nbr by #params by #params] Hessian matrix of the steady states values with respect to the parameters" << endl
<< "%" << endl
<< "% Outputs:" << endl
<< "% rp [M_.eq_nbr by #params] double Jacobian matrix of dynamic model equations with respect to parameters " << endl
<< "% Dynare may prepend or append auxiliary equations, see M_.aux_vars" << endl
<< "% gp [M_.endo_nbr by #dynamic variables by #params] double Derivative of the Jacobian matrix of the dynamic model equations with respect to the parameters" << endl
<< "% rows: equations in order of declaration" << endl
<< "% columns: variables in order stored in M_.lead_lag_incidence" << endl
<< "% rpp [#second_order_residual_terms by 4] double Hessian matrix of second derivatives of residuals with respect to parameters;" << endl
<< "% rows: respective derivative term" << endl
<< "% 1st column: equation number of the term appearing" << endl
<< "% 2nd column: number of the first parameter in derivative" << endl
<< "% 3rd column: number of the second parameter in derivative" << endl
<< "% 4th column: value of the Hessian term" << endl
<< "% gpp [#second_order_Jacobian_terms by 5] double Hessian matrix of second derivatives of the Jacobian with respect to the parameters;" << endl
<< "% rows: respective derivative term" << endl
<< "% 1st column: equation number of the term appearing" << endl
<< "% 2nd column: column number of variable in Jacobian of the dynamic model" << endl
<< "% 3rd column: number of the first parameter in derivative" << endl
<< "% 4th column: number of the second parameter in derivative" << endl
<< "% 5th column: value of the Hessian term" << endl
<< "% hp [#first_order_Hessian_terms by 5] double Jacobian matrix of derivatives of the dynamic Hessian with respect to the parameters;" << endl
<< "% rows: respective derivative term" << endl
<< "% 1st column: equation number of the term appearing" << endl
<< "% 2nd column: column number of first variable in Hessian of the dynamic model" << endl
<< "% 3rd column: column number of second variable in Hessian of the dynamic model" << endl
<< "% 4th column: number of the parameter in derivative" << endl
<< "% 5th column: value of the Hessian term" << endl
<< "% g3p [#first_order_g3_terms by 6] double Jacobian matrix of derivatives of g3 (dynamic 3rd derivs) with respect to the parameters;" << endl
<< "% rows: respective derivative term" << endl
<< "% 1st column: equation number of the term appearing" << endl
<< "% 2nd column: column number of first variable in g3 of the dynamic model" << endl
<< "% 3rd column: column number of second variable in g3 of the dynamic model" << endl
<< "% 4th column: column number of third variable in g3 of the dynamic model" << endl
<< "% 5th column: number of the parameter in derivative" << endl
<< "% 6th column: value of the Hessian term" << endl
<< "%" << endl
<< "%" << endl
<< "% Warning : this file is generated automatically by Dynare" << endl
<< "% from model file (.mod)" << endl << endl
<< "T = NaN(" << params_derivs_temporary_terms_idxs.size() << ",1);" << endl
<< tt_output.str()
<< "rp = zeros(" << equations.size() << ", "
<< symbol_table.param_nbr() << ");" << endl
<< rp_output.str()
<< "gp = zeros(" << equations.size() << ", " << getJacobianColsNbr(false) << ", " << symbol_table.param_nbr() << ");" << endl
<< gp_output.str()
<< "if nargout >= 3" << endl
<< "rpp = zeros(" << params_derivatives.at({ 0, 2 }).size() << ",4);" << endl
<< rpp_output.str()
<< "gpp = zeros(" << params_derivatives.at({ 1, 2 }).size() << ",5);" << endl
<< gpp_output.str()
<< "end" << endl
<< "if nargout >= 5" << endl
<< "hp = zeros(" << params_derivatives.at({ 2, 1 }).size() << ",5);" << endl
<< hp_output.str()
<< "end" << endl
<< "if nargout >= 6" << endl
<< "g3p = zeros(" << params_derivatives.at({ 3, 1 }).size() << ",6);" << endl
<< g3p_output.str()
<< "end" << endl
<< "end" << endl;
paramsDerivsFile
<< "function [rp, gp, rpp, gpp, hp, g3p] = dynamic_params_derivs(y, x, params, "
"steady_state, it_, ss_param_deriv, ss_param_2nd_deriv)"
<< endl
<< "%" << endl
<< "% Compute the derivatives of the dynamic model with respect to the parameters" << endl
<< "% Inputs :" << endl
<< "% y [#dynamic variables by 1] double vector of endogenous variables in "
"the order stored"
<< endl
<< "% in M_.lead_lag_incidence; see the "
"Manual"
<< endl
<< "% x [nperiods by M_.exo_nbr] double matrix of exogenous variables (in "
"declaration order)"
<< endl
<< "% for all simulation periods" << endl
<< "% params [M_.param_nbr by 1] double vector of parameter values in "
"declaration order"
<< endl
<< "% steady_state [M_.endo_nbr by 1] double vector of steady state values"
<< endl
<< "% it_ scalar double time period for exogenous "
"variables for which to evaluate the model"
<< endl
<< "% ss_param_deriv [M_.eq_nbr by #params] Jacobian matrix of the steady "
"states values with respect to the parameters"
<< endl
<< "% ss_param_2nd_deriv [M_.eq_nbr by #params by #params] Hessian matrix of the "
"steady states values with respect to the parameters"
<< endl
<< "%" << endl
<< "% Outputs:" << endl
<< "% rp [M_.eq_nbr by #params] double Jacobian matrix of dynamic model "
"equations with respect to parameters "
<< endl
<< "% Dynare may prepend or append "
"auxiliary equations, see M_.aux_vars"
<< endl
<< "% gp [M_.endo_nbr by #dynamic variables by #params] double Derivative of "
"the Jacobian matrix of the dynamic model equations with respect to the parameters"
<< endl
<< "% rows: equations in order "
"of declaration"
<< endl
<< "% columns: variables in "
"order stored in M_.lead_lag_incidence"
<< endl
<< "% rpp [#second_order_residual_terms by 4] double Hessian matrix of second "
"derivatives of residuals with respect to parameters;"
<< endl
<< "% rows: respective "
"derivative term"
<< endl
<< "% 1st column: equation "
"number of the term appearing"
<< endl
<< "% 2nd column: number of "
"the first parameter in derivative"
<< endl
<< "% 3rd column: number of "
"the second parameter in derivative"
<< endl
<< "% 4th column: value of "
"the Hessian term"
<< endl
<< "% gpp [#second_order_Jacobian_terms by 5] double Hessian matrix of second "
"derivatives of the Jacobian with respect to the parameters;"
<< endl
<< "% rows: respective "
"derivative term"
<< endl
<< "% 1st column: equation "
"number of the term appearing"
<< endl
<< "% 2nd column: column "
"number of variable in Jacobian of the dynamic model"
<< endl
<< "% 3rd column: number of "
"the first parameter in derivative"
<< endl
<< "% 4th column: number of "
"the second parameter in derivative"
<< endl
<< "% 5th column: value of "
"the Hessian term"
<< endl
<< "% hp [#first_order_Hessian_terms by 5] double Jacobian matrix of "
"derivatives of the dynamic Hessian with respect to the parameters;"
<< endl
<< "% rows: respective "
"derivative term"
<< endl
<< "% 1st column: equation "
"number of the term appearing"
<< endl
<< "% 2nd column: column "
"number of first variable in Hessian of the dynamic model"
<< endl
<< "% 3rd column: column "
"number of second variable in Hessian of the dynamic model"
<< endl
<< "% 4th column: number of "
"the parameter in derivative"
<< endl
<< "% 5th column: value of "
"the Hessian term"
<< endl
<< "% g3p [#first_order_g3_terms by 6] double Jacobian matrix of derivatives of "
"g3 (dynamic 3rd derivs) with respect to the parameters;"
<< endl
<< "% rows: respective "
"derivative term"
<< endl
<< "% 1st column: equation "
"number of the term appearing"
<< endl
<< "% 2nd column: column "
"number of first variable in g3 of the dynamic model"
<< endl
<< "% 3rd column: column "
"number of second variable in g3 of the dynamic model"
<< endl
<< "% 4th column: column "
"number of third variable in g3 of the dynamic model"
<< endl
<< "% 5th column: number of "
"the parameter in derivative"
<< endl
<< "% 6th column: value of "
"the Hessian term"
<< endl
<< "%" << endl
<< "%" << endl
<< "% Warning : this file is generated automatically by Dynare" << endl
<< "% from model file (.mod)" << endl
<< endl
<< "T = NaN(" << params_derivs_temporary_terms_idxs.size() << ",1);" << endl
<< tt_output.str() << "rp = zeros(" << equations.size() << ", "
<< symbol_table.param_nbr() << ");" << endl
<< rp_output.str() << "gp = zeros(" << equations.size() << ", "
<< getJacobianColsNbr(false) << ", " << symbol_table.param_nbr() << ");" << endl
<< gp_output.str() << "if nargout >= 3" << endl
<< "rpp = zeros(" << params_derivatives.at({0, 2}).size() << ",4);" << endl
<< rpp_output.str() << "gpp = zeros(" << params_derivatives.at({1, 2}).size() << ",5);"
<< endl
<< gpp_output.str() << "end" << endl
<< "if nargout >= 5" << endl
<< "hp = zeros(" << params_derivatives.at({2, 1}).size() << ",5);" << endl
<< hp_output.str() << "end" << endl
<< "if nargout >= 6" << endl
<< "g3p = zeros(" << params_derivatives.at({3, 1}).size() << ",6);" << endl
<< g3p_output.str() << "end" << endl
<< "end" << endl;
paramsDerivsFile.close();
}
else
......@@ -790,25 +941,24 @@ DynamicModel::writeParamsDerivativesFile(const string &basename) const
<< "function dynamic_params_derivs(y, x, params, steady_state, it_,"
<< "ss_param_deriv, ss_param_2nd_deriv)" << endl
<< "@inbounds begin" << endl
<< tt_output.str()
<< "rp = zeros(" << equations.size() << ", "
<< tt_output.str() << "rp = zeros(" << equations.size() << ", "
<< symbol_table.param_nbr() << ");" << endl
<< rp_output.str()
<< "gp = zeros(" << equations.size() << ", " << getJacobianColsNbr(false) << ", " << symbol_table.param_nbr() << ");" << endl
<< gp_output.str()
<< "rpp = zeros(" << params_derivatives.at({ 0, 2 }).size() << ",4);" << endl
<< rpp_output.str()
<< "gpp = zeros(" << params_derivatives.at({ 1, 2 }).size() << ",5);" << endl
<< gpp_output.str()
<< "hp = zeros(" << params_derivatives.at({ 2, 1 }).size() << ",5);" << endl
<< hp_output.str()
<< "g3p = zeros(" << params_derivatives.at({ 3, 1 }).size() << ",6);" << endl
<< g3p_output.str()
<< "end" << endl
<< rp_output.str() << "gp = zeros(" << equations.size() << ", "
<< getJacobianColsNbr(false) << ", " << symbol_table.param_nbr() << ");" << endl
<< gp_output.str() << "rpp = zeros(" << params_derivatives.at({0, 2}).size() << ",4);"
<< endl
<< rpp_output.str() << "gpp = zeros(" << params_derivatives.at({1, 2}).size() << ",5);"
<< endl
<< gpp_output.str() << "hp = zeros(" << params_derivatives.at({2, 1}).size() << ",5);"
<< endl
<< hp_output.str() << "g3p = zeros(" << params_derivatives.at({3, 1}).size() << ",6);"
<< endl
<< g3p_output.str() << "end" << endl
<< "return (rp, gp, rpp, gpp, hp, g3p)" << endl
<< "end" << endl;
writeToFileIfModified(output, filesystem::path{basename} / "model" / "julia" / "DynamicParamsDerivs.jl");
writeToFileIfModified(output, filesystem::path {basename} / "model" / "julia"
/ "DynamicParamsDerivs.jl");
}
}
......