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
Select Git revision
Loading items

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
Select Git revision
Loading items
Show changes
Commits on Source (254)
Showing
with 3925 additions and 2927 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: variables:
TERM: linux TERM: linux
MINGW32_BOOST_VERSION: 1.81.0-7 MINGW64_BOOST_VERSION: 1.88.0-2
MINGW64_BOOST_VERSION: 1.81.0-7
WGET_OPTIONS: '--no-verbose --no-use-server-timestamps --retry-connrefused --retry-on-host-error' 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: build_linux_x86_64:
stage: build stage: build
...@@ -16,7 +17,7 @@ build_linux_x86_64: ...@@ -16,7 +17,7 @@ build_linux_x86_64:
build_linux_arm64: build_linux_arm64:
stage: build stage: build
script: script:
- meson setup -D buildtype=release --cross-file scripts/arm64-cross.ini build - meson setup -D buildtype=release --cross-file scripts/linux-arm64-cross.ini build
- meson compile -C build -v - meson compile -C build -v
artifacts: artifacts:
paths: paths:
...@@ -46,8 +47,40 @@ build_macos_x86_64: ...@@ -46,8 +47,40 @@ build_macos_x86_64:
tags: tags:
- macOS - macOS
script: script:
- arch -x86_64 meson setup -D buildtype=release --native-file scripts/homebrew-native.ini build - 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 - arch -x86_64 meson compile -C build -v
artifacts: artifacts:
paths: paths:
- build/src/dynare-preprocessor - build/src/dynare-preprocessor
build_macos_arm64:
stage: build
tags:
- macOS
script:
- export PATH="/opt/homebrew/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 @@ ...@@ -15,7 +15,7 @@
\pgfdeclareimage[height=0.8cm]{logo}{dlogo} \pgfdeclareimage[height=0.8cm]{logo}{dlogo}
\institute[Dynare Team]{\pgfuseimage{logo}} \institute[Dynare Team]{\pgfuseimage{logo}}
\date{23 May 2023} \date{31 May 2024}
\AtBeginSection[] \AtBeginSection[]
{ {
...@@ -156,9 +156,9 @@ ...@@ -156,9 +156,9 @@
\item comparison operators: \texttt{< > <= >= == !=} \item comparison operators: \texttt{< > <= >= == !=}
\item logical operators: \verb+&& || !+ \item logical operators: \verb+&& || !+
\item range with unit increment: \texttt{1:4} is equivalent to \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]]}) 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{itemize}
\end{block} \end{block}
...@@ -314,7 +314,7 @@ Then \texttt{distance(3, 4)} will be equivalent to \texttt{5}. ...@@ -314,7 +314,7 @@ Then \texttt{distance(3, 4)} will be equivalent to \texttt{5}.
\end{frame} \end{frame}
\begin{frame}[fragile=singleslide] \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+ The value of a macro-variable can be defined with the \verb+@#define+
directive. directive.
...@@ -335,9 +335,31 @@ Then \texttt{distance(3, 4)} will be equivalent to \texttt{5}. ...@@ -335,9 +335,31 @@ Then \texttt{distance(3, 4)} will be equivalent to \texttt{5}.
@#define t = ("US" in w) // Equals true @#define t = ("US" in w) // Equals true
\end{verbatim} \end{verbatim}
\end{block} \end{block}
NB: You can define macro variables on the Dynare command line by using the \texttt{-D} option
\end{frame} \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] \begin{frame}[fragile=singleslide]
\frametitle{Expression substitution} \frametitle{Expression substitution}
\framesubtitle{Dummy example} \framesubtitle{Dummy example}
...@@ -611,7 +633,7 @@ end; ...@@ -611,7 +633,7 @@ end;
\begin{frame} \begin{frame}
\frametitle{Macro-related command line options} \frametitle{Macro-related command line options}
\begin{itemize} \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 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{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. \item \texttt{onlymacro}: Stops processing after the macro processing step.
...@@ -866,7 +888,7 @@ rhos = [ 0.8, 0.9, 1]; ...@@ -866,7 +888,7 @@ rhos = [ 0.8, 0.9, 1];
\ccbysa \ccbysa
\column{0.71\textwidth} \column{0.71\textwidth}
\tiny \tiny
Copyright © 2008-2023 Dynare Team \\ Copyright © 2008-2024 Dynare Team \\
License: \href{http://creativecommons.org/licenses/by-sa/4.0/}{Creative License: \href{http://creativecommons.org/licenses/by-sa/4.0/}{Creative
Commons Attribution-ShareAlike 4.0} Commons Attribution-ShareAlike 4.0}
\end{columns} \end{columns}
......
This diff is collapsed.
...@@ -2,7 +2,8 @@ ...@@ -2,7 +2,8 @@
# It is not used when building Dynare as a whole. # It is not used when building Dynare as a whole.
project('dynare-preprocessor', 'cpp', 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' ], default_options : [ 'cpp_std=gnu++20', 'warning_level=2' ],
meson_version : '>=0.64.0') meson_version : '>=0.64.0')
......
# Meson native file for compiling under Homebrew / arm64
[binaries]
cpp = '/opt/homebrew/bin/g++-15'
flex = '/opt/homebrew/opt/flex/bin/flex'
bison = '/opt/homebrew/opt/bison/bin/bison'
[built-in options]
# XCode 15 (on Ventura and Sonoma) has a linker issue, see https://github.com/mesonbuild/meson/issues/12282, workaround is to use ld_classic
cpp_link_args = [ '-Wl,-ld_classic' ]
\ No newline at end of file
# Meson native file for compiling under Homebrew / x86-64
[binaries]
cpp = '/usr/local/bin/g++-15'
flex = '/usr/local/opt/flex/bin/flex'
bison = '/usr/local/opt/bison/bin/bison'
[built-in options]
# XCode 15 (on Ventura and Sonoma) has a linker issue, see https://github.com/mesonbuild/meson/issues/12282, workaround is to use ld_classic
cpp_link_args = [ '-Wl,-ld_classic' ]
\ No newline at end of file
# Meson native file for compiling under Homebrew
[binaries]
cpp = 'g++-13'
flex = '/usr/local/opt/flex/bin/flex'
bison = '/usr/local/opt/bison/bin/bison'
File moved
# 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. * This file is part of Dynare.
* *
...@@ -17,14 +17,17 @@ ...@@ -17,14 +17,17 @@
* along with Dynare. If not, see <https://www.gnu.org/licenses/>. * along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include <iostream>
#include <ios>
#include <cstdlib>
#include <algorithm> #include <algorithm>
#include <cstdlib>
#include <ios>
#include <iostream>
#include "Bytecode.hh" #include "Bytecode.hh"
BytecodeWriter::BytecodeWriter(const filesystem::path &filename) namespace Bytecode
{
Writer::Writer(const filesystem::path& filename)
{ {
open(filename, ios::out | ios::binary); open(filename, ios::out | ios::binary);
if (!is_open()) if (!is_open())
...@@ -35,17 +38,16 @@ BytecodeWriter::BytecodeWriter(const filesystem::path &filename) ...@@ -35,17 +38,16 @@ BytecodeWriter::BytecodeWriter(const filesystem::path &filename)
} }
template<> template<>
BytecodeWriter & Writer&
operator<<(BytecodeWriter &code_file, const FCALL_ &instr) operator<<(Writer& code_file, const FCALL& instr)
{ {
code_file.instructions_positions.push_back(code_file.tellp()); code_file.instructions_positions.push_back(code_file.tellp());
auto write_member = [&code_file](const auto &member) auto write_member = [&code_file](const auto& member) {
{
code_file.write(reinterpret_cast<const char*>(&member), sizeof 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_output_arguments);
write_member(instr.nb_input_arguments); write_member(instr.nb_input_arguments);
write_member(instr.indx); write_member(instr.indx);
...@@ -66,23 +68,22 @@ operator<<(BytecodeWriter &code_file, const FCALL_ &instr) ...@@ -66,23 +68,22 @@ operator<<(BytecodeWriter &code_file, const FCALL_ &instr)
} }
template<> template<>
BytecodeWriter & Writer&
operator<<(BytecodeWriter &code_file, const FBEGINBLOCK_ &instr) operator<<(Writer& code_file, const FBEGINBLOCK& instr)
{ {
code_file.instructions_positions.push_back(code_file.tellp()); code_file.instructions_positions.push_back(code_file.tellp());
auto write_member = [&code_file](const auto &member) auto write_member = [&code_file](const auto& member) {
{
code_file.write(reinterpret_cast<const char*>(&member), sizeof 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.size);
write_member(instr.type); write_member(instr.type);
for (int i = 0; i < instr.size; i++) for (int i = 0; i < instr.size; i++)
{ {
write_member(instr.variable[i]); write_member(instr.variables[i]);
write_member(instr.equation[i]); write_member(instr.equations[i]);
} }
if (instr.type == BlockSimulationType::solveTwoBoundariesSimple if (instr.type == BlockSimulationType::solveTwoBoundariesSimple
|| instr.type == BlockSimulationType::solveTwoBoundariesComplete || instr.type == BlockSimulationType::solveTwoBoundariesComplete
...@@ -96,8 +97,10 @@ operator<<(BytecodeWriter &code_file, const FBEGINBLOCK_ &instr) ...@@ -96,8 +97,10 @@ operator<<(BytecodeWriter &code_file, const FBEGINBLOCK_ &instr)
write_member(instr.det_exo_size); write_member(instr.det_exo_size);
write_member(instr.exo_size); write_member(instr.exo_size);
for_each_n(instr.det_exogenous.begin(), instr.det_exo_size, write_member); ranges::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.exogenous.begin(), instr.exo_size, write_member);
return code_file; return code_file;
} }
}
This diff is collapsed.
/* /*
* Copyright © 2007-2022 Dynare Team * Copyright © 2007-2024 Dynare Team
* *
* This file is part of Dynare. * This file is part of Dynare.
* *
...@@ -17,24 +17,30 @@ ...@@ -17,24 +17,30 @@
* along with Dynare. If not, see <https://www.gnu.org/licenses/>. * along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/ */
#ifndef _COMMON_ENUMS_HH #ifndef COMMON_ENUMS_HH
#define _COMMON_ENUMS_HH #define COMMON_ENUMS_HH
//! Enumeration of possible symbol types //! 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 enum class SymbolType
{ {
endogenous = 0, //!< Endogenous endogenous = 0, // Endogenous (non-heterogeneous)
exogenous = 1, //!< Exogenous exogenous = 1, // Exogenous (non-heterogeneous)
exogenousDet = 2, //!< Exogenous deterministic exogenousDet = 2, // Exogenous deterministic (non-heterogeneous)
parameter = 4, //!< Parameter parameter = 4, // Parameter (non-heterogeneous)
modelLocalVariable = 10, //!< Local variable whose scope is model (pound expression) heterogeneousEndogenous = 5, // Endogenous that is heterogeneous across some dimension
modFileLocalVariable = 11, //!< Local variable whose scope is mod file (model excluded) heterogeneousExogenous = 6, // Exogenous that is heterogeneous across some dimension
externalFunction = 12, //!< External (user-defined) function heterogeneousParameter = 7, // Parameter that is heterogeneous across some dimension
trend = 13, //!< Trend variable modelLocalVariable = 10, // Local variable whose scope is model (pound expression)
statementDeclaredVariable = 14, //!< Local variable assigned within a Statement (see subsample statement for example) 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 logTrend = 15, //!< Log-trend variable
unusedEndogenous = 16, //!< Type to mark unused endogenous variables when `nostrict` option is passed 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)
...@@ -42,6 +48,13 @@ enum class SymbolType ...@@ -42,6 +48,13 @@ enum class SymbolType
excludedVariable = 19 //!< Variable excluded via model_remove/var_remove/include_eqs/exclude_eqs 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 enum class UnaryOpcode
{ {
uminus, uminus,
...@@ -66,12 +79,14 @@ enum class UnaryOpcode ...@@ -66,12 +79,14 @@ enum class UnaryOpcode
sign, sign,
steadyState, steadyState,
steadyStateParamDeriv, // for the derivative of the STEADY_STATE operator w.r.t. to a parameter 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 steadyStateParam2ndDeriv, // for the 2nd derivative of the STEADY_STATE operator w.r.t. to a
// parameter
expectation, expectation,
erf, erf,
erfc, erfc,
diff, diff,
adl adl,
sum
}; };
enum class BinaryOpcode enum class BinaryOpcode
...@@ -119,23 +134,24 @@ enum class PriorDistributions ...@@ -119,23 +134,24 @@ enum class PriorDistributions
enum class EquationType enum class EquationType
{ {
unknown, //!< Unknown equation type evaluate, //!< Simple evaluation, normalized variable on left-hand side (written as such by the
evaluate, //!< Simple evaluation, normalized variable on left-hand side (written as such by the user) //!< user)
evaluateRenormalized, //!< Simple evaluation, normalized variable on left-hand side (normalization computed by the preprocessor) 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 solve //!< No simple evaluation of the equation, it has to be solved
}; };
enum class BlockSimulationType enum class BlockSimulationType
{ {
unknown, //!< Unknown simulation type evaluateForward = 1, //!< Simple evaluation, normalized variable on left-hand side, forward
evaluateForward, //!< Simple evaluation, normalized variable on left-hand side, forward
evaluateBackward, //!< Simple evaluation, normalized variable on left-hand side, backward evaluateBackward, //!< Simple evaluation, normalized variable on left-hand side, backward
solveForwardSimple, //!< Block of one equation, newton solver needed, forward solveForwardSimple, //!< Block of one equation, newton solver needed, forward
solveBackwardSimple, //!< Block of one equation, newton solver needed, backward solveBackwardSimple, //!< Block of one equation, newton solver needed, backward
solveTwoBoundariesSimple, //!< Block of one equation, Newton solver needed, forward and backward solveTwoBoundariesSimple, //!< Block of one equation, Newton solver needed, forward and backward
solveForwardComplete, //!< Block of several equations, Newton solver needed, forward solveForwardComplete, //!< Block of several equations, Newton solver needed, forward
solveBackwardComplete, //!< Block of several equations, Newton solver needed, backward solveBackwardComplete, //!< Block of several equations, Newton solver needed, backward
solveTwoBoundariesComplete //!< Block of several equations, Newton solver needed, forward and backwar solveTwoBoundariesComplete //!< Block of several equations, Newton solver needed, forward and
//!< backwar
}; };
enum class PacTargetKind enum class PacTargetKind
...@@ -146,4 +162,4 @@ enum class PacTargetKind ...@@ -146,4 +162,4 @@ enum class PacTargetKind
dd dd
}; };
#endif // _COMMON_ENUMS_HH #endif
This diff is collapsed.
This diff is collapsed.
...@@ -17,57 +17,49 @@ ...@@ -17,57 +17,49 @@
* along with Dynare. If not, see <https://www.gnu.org/licenses/>. * along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/ */
#ifndef _CONFIG_FILE_HH #ifndef CONFIGURATION_HH
#define _CONFIG_FILE_HH #define CONFIGURATION_HH
#include <filesystem>
#include <map> #include <map>
#include <vector> #include <vector>
#include <filesystem>
#include "WarningConsolidation.hh" #include "WarningConsolidation.hh"
using namespace std; using namespace std;
using member_nodes_t = map<string, double>; /* The abstract representation of the configuration.
Merges information from the command-line and from the configuration file. */
class Hook class Configuration
{ {
public: 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: private:
map<string, string> hooks; using member_nodes_t = map<string, double>;
public:
map<string, string>
get_hooks() const
{
return hooks;
};
};
class Path class Path
{ {
public: public:
explicit Path(vector<string> includepath_arg); explicit Path(vector<string> includepath_arg);
private: [[nodiscard]] map<string, vector<string>>
map<string, vector<string>> paths;
public:
map<string, vector<string>>
get_paths() const get_paths() const
{ {
return paths; return paths;
}; }
private:
map<string, vector<string>> paths;
}; };
class FollowerNode struct FollowerNode
{ {
friend class ConfigFile; FollowerNode(string computerName_arg, string port_arg, int minCpuNbr_arg, int maxCpuNbr_arg,
public: string userName_arg, string password_arg, string remoteDrive_arg,
FollowerNode(string computerName_arg, string port_arg, int minCpuNbr_arg, int maxCpuNbr_arg, string userName_arg, string remoteDirectory_arg, string programPath_arg, string programConfig_arg,
string password_arg, string remoteDrive_arg, string remoteDirectory_arg, string matlabOctavePath_arg, bool singleCompThread_arg,
string programPath_arg, string programConfig_arg, string matlabOctavePath_arg, bool singleCompThread_arg,
int numberOfThreadsPerJob_arg, string operatingSystem_arg); int numberOfThreadsPerJob_arg, string operatingSystem_arg);
protected:
const string computerName, port; const string computerName, port;
int minCpuNbr, maxCpuNbr; int minCpuNbr, maxCpuNbr;
const string userName, password; const string userName, password;
...@@ -78,55 +70,51 @@ protected: ...@@ -78,55 +70,51 @@ protected:
const string operatingSystem; const string operatingSystem;
}; };
class Cluster struct Cluster
{ {
friend class ConfigFile;
public:
explicit Cluster(member_nodes_t member_nodes_arg); explicit Cluster(member_nodes_t member_nodes_arg);
protected:
member_nodes_t member_nodes; 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);
private:
const bool parallel, parallel_test, parallel_follower_open_mode, parallel_use_psexec; const bool parallel, parallel_test, parallel_follower_open_mode, parallel_use_psexec;
const string cluster_name; const string cluster_name;
string firstClusterName; string firstClusterName;
//! Hooks //! Hooks
vector<Hook> hooks; string global_init_file;
//! Paths //! Paths
vector<Path> paths; vector<Path> paths;
//! Cluster Table //! Cluster Table
map<string, Cluster> clusters; map<string, Cluster> clusters;
//! Node Map //! Node Map
map<string, FollowerNode> follower_nodes; map<string, FollowerNode> follower_nodes;
//! Add Hooks
void addHooksConfFileElement(string global_init_file);
//! Add Paths //! Add Paths
void addPathsConfFileElement(vector<string> includepath); void addPathsConfFileElement(vector<string> includepath);
//! Add a FollowerNode or a Cluster object //! Add a FollowerNode or a Cluster object
void addParallelConfFileElement(bool inNode, bool inCluster, const member_nodes_t &member_nodes, const string &name, void addParallelConfFileElement(bool inNode, bool inCluster, const member_nodes_t& member_nodes,
const string &computerName, const string &port, int minCpuNbr, int maxCpuNbr, const string& name, const string& computerName,
const string &userName, const string &password, const string &remoteDrive, const string& port, int minCpuNbr, int maxCpuNbr,
const string &remoteDirectory, const string &programPath, const string &programConfig, const string& userName, const string& password,
const string &matlabOctavePath, bool singleCompThread, int numberOfThreadsPerJob, const string& remoteDrive, const string& remoteDirectory,
const string &operatingSystem); 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: public:
//! Parse config file //! Parse config file
void getConfigFileInfo(const filesystem::path &parallel_config_file); void getConfigFileInfo(const filesystem::path& conffile_option, WarningConsolidation& warnings);
//! Check Pass //! Check Pass
void checkPass(WarningConsolidation& warnings) const; void checkPass(WarningConsolidation& warnings) const;
//! Check Pass //! Check Pass
void transformPass(); void transformPass();
//! Get Path Info //! Get Path Info
vector<filesystem::path> getIncludePaths() const; [[nodiscard]] vector<filesystem::path> getIncludePaths() const;
//! Write any hooks //! Write any hooks
void writeHooks(ostream& output) const; void writeHooks(ostream& output) const;
//! Create options_.parallel structure, write options //! Create options_.parallel structure, write options
...@@ -135,4 +123,4 @@ public: ...@@ -135,4 +123,4 @@ public:
void writeEndParallel(ostream& output) const; void writeEndParallel(ostream& output) const;
}; };
#endif // ! CONFIG_FILE_HH #endif
/* /*
* Copyright © 2003-2023 Dynare Team * Copyright © 2003-2025 Dynare Team
* *
* This file is part of Dynare. * This file is part of Dynare.
* *
...@@ -17,13 +17,14 @@ ...@@ -17,13 +17,14 @@
* along with Dynare. If not, see <https://www.gnu.org/licenses/>. * along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include <cstdlib>
#include <cassert>
#include <iostream>
#include <algorithm> #include <algorithm>
#include <iterator> #include <cassert>
#include <cstdlib>
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>
#include <iostream>
#include <iterator>
#include <ranges>
#include "DataTree.hh" #include "DataTree.hh"
...@@ -46,13 +47,13 @@ DataTree::initConstants() ...@@ -46,13 +47,13 @@ DataTree::initConstants()
Pi = AddNonNegativeConstant("3.141592653589793"); Pi = AddNonNegativeConstant("3.141592653589793");
} }
DataTree::DataTree(SymbolTable &symbol_table_arg, DataTree::DataTree(SymbolTable& symbol_table_arg, NumericalConstants& num_constants_arg,
NumericalConstants &num_constants_arg,
ExternalFunctionsTable& external_functions_table_arg, ExternalFunctionsTable& external_functions_table_arg,
bool is_dynamic_arg) : HeterogeneityTable& heterogeneity_table_arg, bool is_dynamic_arg) :
symbol_table {symbol_table_arg}, symbol_table {symbol_table_arg},
num_constants {num_constants_arg}, num_constants {num_constants_arg},
external_functions_table {external_functions_table_arg}, external_functions_table {external_functions_table_arg},
heterogeneity_table {heterogeneity_table_arg},
is_dynamic {is_dynamic_arg} is_dynamic {is_dynamic_arg}
{ {
initConstants(); initConstants();
...@@ -62,19 +63,21 @@ DataTree::DataTree(const DataTree &d) : ...@@ -62,19 +63,21 @@ DataTree::DataTree(const DataTree &d) :
symbol_table {d.symbol_table}, symbol_table {d.symbol_table},
num_constants {d.num_constants}, num_constants {d.num_constants},
external_functions_table {d.external_functions_table}, external_functions_table {d.external_functions_table},
heterogeneity_table {d.heterogeneity_table},
is_dynamic {d.is_dynamic}, is_dynamic {d.is_dynamic},
local_variables_vector {d.local_variables_vector} local_variables_vector {d.local_variables_vector}
{ {
// Constants must be initialized first because they are used in some Add* methods // Constants must be initialized first because they are used in some Add* methods
initConstants(); initConstants();
// 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) for (const auto& it : d.node_list)
it->clone(*this); it->clone(*this);
assert(node_list.size() == d.node_list.size()); 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&
...@@ -83,6 +86,7 @@ DataTree::operator=(const DataTree &d) ...@@ -83,6 +86,7 @@ DataTree::operator=(const DataTree &d)
assert(&symbol_table == &d.symbol_table); assert(&symbol_table == &d.symbol_table);
assert(&num_constants == &d.num_constants); assert(&num_constants == &d.num_constants);
assert(&external_functions_table == &d.external_functions_table); assert(&external_functions_table == &d.external_functions_table);
assert(&heterogeneity_table == &d.heterogeneity_table);
assert(is_dynamic == d.is_dynamic); assert(is_dynamic == d.is_dynamic);
num_const_node_map.clear(); num_const_node_map.clear();
...@@ -125,8 +129,7 @@ DataTree::AddNonNegativeConstant(const string &value) ...@@ -125,8 +129,7 @@ DataTree::AddNonNegativeConstant(const string &value)
{ {
int id = num_constants.AddNonNegativeConstant(value); int id = num_constants.AddNonNegativeConstant(value);
if (auto it = num_const_node_map.find(id); if (auto it = num_const_node_map.find(id); it != num_const_node_map.end())
it != num_const_node_map.end())
return it->second; return it->second;
auto sp = make_unique<NumConstNode>(*this, node_list.size(), id); auto sp = make_unique<NumConstNode>(*this, node_list.size(), id);
...@@ -145,8 +148,7 @@ DataTree::AddVariable(int symb_id, int lag) ...@@ -145,8 +148,7 @@ DataTree::AddVariable(int symb_id, int lag)
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (auto it = variable_node_map.find({ symb_id, lag }); if (auto it = variable_node_map.find({symb_id, lag}); it != variable_node_map.end())
it != variable_node_map.end())
return it->second; return it->second;
auto sp = make_unique<VariableNode>(*this, node_list.size(), symb_id, lag); auto sp = make_unique<VariableNode>(*this, node_list.size(), symb_id, lag);
...@@ -162,7 +164,8 @@ DataTree::getVariable(int symb_id, int lag) const ...@@ -162,7 +164,8 @@ 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()) 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); exit(EXIT_FAILURE);
} }
return it->second; return it->second;
...@@ -208,7 +211,7 @@ DataTree::AddPlus(expr_t iArg1, expr_t iArg2) ...@@ -208,7 +211,7 @@ DataTree::AddPlus(expr_t iArg1, expr_t iArg2)
// To treat commutativity of "+" // To treat commutativity of "+"
// Nodes iArg1 and iArg2 are sorted by index // 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); swap(iArg1, iArg2);
return AddBinaryOp(iArg1, BinaryOpcode::plus, iArg2); return AddBinaryOp(iArg1, BinaryOpcode::plus, iArg2);
} }
...@@ -250,8 +253,7 @@ DataTree::AddUMinus(expr_t iArg1) ...@@ -250,8 +253,7 @@ DataTree::AddUMinus(expr_t iArg1)
return Zero; return Zero;
// Simplify -(-x) in x // Simplify -(-x) in x
if (auto uarg = dynamic_cast<UnaryOpNode *>(iArg1); if (auto uarg = dynamic_cast<UnaryOpNode*>(iArg1); uarg && uarg->op_code == UnaryOpcode::uminus)
uarg && uarg->op_code == UnaryOpcode::uminus)
return uarg->arg; return uarg->arg;
return AddUnaryOp(UnaryOpcode::uminus, iArg1); return AddUnaryOp(UnaryOpcode::uminus, iArg1);
...@@ -287,7 +289,7 @@ DataTree::AddTimes(expr_t iArg1, expr_t iArg2) ...@@ -287,7 +289,7 @@ DataTree::AddTimes(expr_t iArg1, expr_t iArg2)
// To treat commutativity of "*" // To treat commutativity of "*"
// Nodes iArg1 and iArg2 are sorted by index // 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); swap(iArg1, iArg2);
return AddBinaryOp(iArg1, BinaryOpcode::times, iArg2); return AddBinaryOp(iArg1, BinaryOpcode::times, iArg2);
} }
...@@ -668,7 +670,8 @@ DataTree::AddSteadyStateParamDeriv(expr_t iArg1, int param_symb_id) ...@@ -668,7 +670,8 @@ DataTree::AddSteadyStateParamDeriv(expr_t iArg1, int param_symb_id)
expr_t expr_t
DataTree::AddSteadyStateParam2ndDeriv(expr_t iArg1, int param1_symb_id, int param2_symb_id) 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 expr_t
...@@ -680,8 +683,7 @@ DataTree::AddExpectation(int iArg1, expr_t iArg2) ...@@ -680,8 +683,7 @@ DataTree::AddExpectation(int iArg1, expr_t iArg2)
expr_t expr_t
DataTree::AddVarExpectation(const string& model_name) DataTree::AddVarExpectation(const string& model_name)
{ {
if (auto it = var_expectation_node_map.find(model_name); if (auto it = var_expectation_node_map.find(model_name); it != var_expectation_node_map.end())
it != var_expectation_node_map.end())
return it->second; return it->second;
auto sp = make_unique<VarExpectationNode>(*this, node_list.size(), model_name); auto sp = make_unique<VarExpectationNode>(*this, node_list.size(), model_name);
...@@ -694,8 +696,7 @@ DataTree::AddVarExpectation(const string &model_name) ...@@ -694,8 +696,7 @@ DataTree::AddVarExpectation(const string &model_name)
expr_t expr_t
DataTree::AddPacExpectation(const string& model_name) DataTree::AddPacExpectation(const string& model_name)
{ {
if (auto it = pac_expectation_node_map.find(model_name); if (auto it = pac_expectation_node_map.find(model_name); it != pac_expectation_node_map.end())
it != pac_expectation_node_map.end())
return it->second; return it->second;
auto sp = make_unique<PacExpectationNode>(*this, node_list.size(), model_name); auto sp = make_unique<PacExpectationNode>(*this, node_list.size(), model_name);
...@@ -757,43 +758,56 @@ DataTree::AddExternalFunction(int symb_id, const vector<expr_t> &arguments) ...@@ -757,43 +758,56 @@ DataTree::AddExternalFunction(int symb_id, const vector<expr_t> &arguments)
} }
expr_t 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); 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()) it != first_deriv_external_function_node_map.end())
return it->second; 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(); auto p = sp.get();
node_list.push_back(move(sp)); 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; return p;
} }
expr_t 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); 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, if (auto it = second_deriv_external_function_node_map.find(
top_level_symb_id }); {arguments, input_index1, input_index2, top_level_symb_id});
it != second_deriv_external_function_node_map.end()) it != second_deriv_external_function_node_map.end())
return it->second; 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(); auto p = sp.get();
node_list.push_back(move(sp)); 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; return p;
} }
expr_t
DataTree::AddSum(expr_t arg)
{
return AddUnaryOp(UnaryOpcode::sum, arg);
}
bool bool
DataTree::isSymbolUsed(int symb_id) const DataTree::isSymbolUsed(int symb_id) const
{ {
for (const auto &[symb_lag, expr] : variable_node_map) if (ranges::any_of(views::keys(variable_node_map),
if (symb_lag.first == symb_id) [=](const auto& symb_lag) { return symb_lag.first == symb_id; }))
return true; return true;
if (local_variables_table.contains(symb_id)) if (local_variables_table.contains(symb_id))
...@@ -840,18 +854,18 @@ DataTree::addAllParamDerivId([[maybe_unused]] set<int> &deriv_id_set) ...@@ -840,18 +854,18 @@ DataTree::addAllParamDerivId([[maybe_unused]] set<int> &deriv_id_set)
bool bool
DataTree::isUnaryOpUsed(UnaryOpcode opcode) const DataTree::isUnaryOpUsed(UnaryOpcode opcode) const
{ {
return any_of(unary_op_node_map.begin(), unary_op_node_map.end(), return ranges::any_of(views::keys(unary_op_node_map),
[=](const auto &it) { return get<1>(it.first) == opcode; }); [=](const auto& key) { return get<1>(key) == opcode; });
} }
bool bool
DataTree::isUnaryOpUsedOnType(SymbolType type, UnaryOpcode opcode) const DataTree::isUnaryOpUsedOnType(SymbolType type, UnaryOpcode opcode) const
{ {
set<int> var; set<int> var;
for (const auto &it : unary_op_node_map) for (const auto& [key, value] : unary_op_node_map)
if (get<1>(it.first) == opcode) if (get<1>(key) == opcode)
{ {
it.second->collectVariables(type, var); value->collectVariables(type, var);
if (!var.empty()) if (!var.empty())
return true; return true;
} }
...@@ -861,18 +875,18 @@ DataTree::isUnaryOpUsedOnType(SymbolType type, UnaryOpcode opcode) const ...@@ -861,18 +875,18 @@ DataTree::isUnaryOpUsedOnType(SymbolType type, UnaryOpcode opcode) const
bool bool
DataTree::isBinaryOpUsed(BinaryOpcode opcode) const DataTree::isBinaryOpUsed(BinaryOpcode opcode) const
{ {
return any_of(binary_op_node_map.begin(), binary_op_node_map.end(), return ranges::any_of(views::keys(binary_op_node_map),
[=](const auto &it) { return get<2>(it.first) == opcode; }); [=](const auto& key) { return get<2>(key) == opcode; });
} }
bool bool
DataTree::isBinaryOpUsedOnType(SymbolType type, BinaryOpcode opcode) const DataTree::isBinaryOpUsedOnType(SymbolType type, BinaryOpcode opcode) const
{ {
set<int> var; set<int> var;
for (const auto &it : binary_op_node_map) for (const auto& [key, value] : binary_op_node_map)
if (get<2>(it.first) == opcode) if (get<2>(key) == opcode)
{ {
it.second->collectVariables(type, var); value->collectVariables(type, var);
if (!var.empty()) if (!var.empty())
return true; return true;
} }
...@@ -897,7 +911,9 @@ DataTree::writeCHelpersDefinition(ostream &output) const ...@@ -897,7 +911,9 @@ DataTree::writeCHelpersDefinition(ostream &output) const
<< "inline double" << endl << "inline double" << endl
<< "getPowerDeriv(double x, double p, int k)" << endl << "getPowerDeriv(double x, double p, int k)" << endl
<< "{" << 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 << " return 0.0;" << endl
<< " else" << endl << " else" << endl
<< " {" << endl << " {" << endl
...@@ -932,8 +948,7 @@ DataTree::strsplit(string_view str, char delim) ...@@ -932,8 +948,7 @@ DataTree::strsplit(string_view str, char delim)
while (true) while (true)
{ {
size_t idx {str.find(delim)}; size_t idx {str.find(delim)};
if (auto sub {str.substr(0, idx)}; if (auto sub {str.substr(0, idx)}; !sub.empty())
!sub.empty())
result.emplace_back(sub); result.emplace_back(sub);
if (idx == string_view::npos) if (idx == string_view::npos)
break; break;
...@@ -943,10 +958,10 @@ DataTree::strsplit(string_view str, char delim) ...@@ -943,10 +958,10 @@ DataTree::strsplit(string_view str, char delim)
} }
filesystem::path filesystem::path
DataTree::packageDir(string_view package) DataTree::packageDir(const string_view& package)
{ {
filesystem::path d; filesystem::path d;
for (const auto &it : strsplit(move(package), '.')) for (const auto& it : strsplit(package, '.'))
d /= "+" + it; d /= "+" + it;
return d; return d;
} }
...@@ -956,7 +971,7 @@ DataTree::writeToFileIfModified(stringstream &new_contents, const filesystem::pa ...@@ -956,7 +971,7 @@ DataTree::writeToFileIfModified(stringstream &new_contents, const filesystem::pa
{ {
ifstream old_file {filename, ios::in | ios::binary}; ifstream old_file {filename, ios::in | ios::binary};
if (old_file.is_open() if (old_file.is_open()
&& equal(istreambuf_iterator<char>{old_file}, istreambuf_iterator<char>{}, && ranges::equal(istreambuf_iterator<char> {old_file}, istreambuf_iterator<char> {},
istreambuf_iterator<char> {new_contents}, istreambuf_iterator<char> {})) istreambuf_iterator<char> {new_contents}, istreambuf_iterator<char> {}))
return; return;
old_file.close(); old_file.close();
...@@ -969,7 +984,7 @@ DataTree::writeToFileIfModified(stringstream &new_contents, const filesystem::pa ...@@ -969,7 +984,7 @@ DataTree::writeToFileIfModified(stringstream &new_contents, const filesystem::pa
cerr << "ERROR: Can't open file " << filename.string() << " for writing" << endl; cerr << "ERROR: Can't open file " << filename.string() << " for writing" << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
copy(istreambuf_iterator<char>{new_contents}, istreambuf_iterator<char>{}, ranges::copy(istreambuf_iterator<char> {new_contents}, istreambuf_iterator<char> {},
ostreambuf_iterator<char> {new_file}); ostreambuf_iterator<char> {new_file});
new_file.close(); new_file.close();
} }
/* /*
* Copyright © 2003-2023 Dynare Team * Copyright © 2003-2024 Dynare Team
* *
* This file is part of Dynare. * This file is part of Dynare.
* *
...@@ -17,25 +17,26 @@ ...@@ -17,25 +17,26 @@
* along with Dynare. If not, see <https://www.gnu.org/licenses/>. * along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/ */
#ifndef _DATATREE_HH #ifndef DATA_TREE_HH
#define _DATATREE_HH #define DATA_TREE_HH
#include <string>
#include <map>
#include <vector>
#include <sstream>
#include <iomanip>
#include <cmath> #include <cmath>
#include <utility>
#include <memory>
#include <filesystem> #include <filesystem>
#include <iomanip>
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <string_view> #include <string_view>
#include <utility>
#include <vector>
#include "SymbolTable.hh"
#include "NumericalConstants.hh"
#include "ExternalFunctionsTable.hh"
#include "ExprNode.hh" #include "ExprNode.hh"
#include "ExternalFunctionsTable.hh"
#include "HeterogeneityTable.hh"
#include "NumericalConstants.hh"
#include "SubModel.hh" #include "SubModel.hh"
#include "SymbolTable.hh"
using namespace std; using namespace std;
...@@ -48,7 +49,11 @@ public: ...@@ -48,7 +49,11 @@ public:
NumericalConstants& num_constants; NumericalConstants& num_constants;
//! A reference to the external functions table //! 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? //! 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; const bool is_dynamic;
private: private:
...@@ -60,8 +65,10 @@ private: ...@@ -60,8 +65,10 @@ private:
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; 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 //! (arg, op_code, arg_exp_info_set, param1_symb_id, param2_symb_id, adl_param_name, adl_lags) ->
using unary_op_node_map_t = map<tuple<expr_t, UnaryOpcode, int, int, int, string, vector<int>>, UnaryOpNode *>; //! 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; unary_op_node_map_t unary_op_node_map;
//! ( arg1, arg2, opCode, order of Power Derivative) -> BinaryOpNode //! ( arg1, arg2, opCode, order of Power Derivative) -> BinaryOpNode
...@@ -89,11 +96,13 @@ private: ...@@ -89,11 +96,13 @@ private:
pac_target_nonstationary_node_map_t pac_target_nonstationary_node_map; pac_target_nonstationary_node_map_t pac_target_nonstationary_node_map;
// (arguments, deriv_idx, symb_id) -> FirstDerivExternalFunctionNode // (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; first_deriv_external_function_node_map_t first_deriv_external_function_node_map;
// (arguments, deriv_idx1, deriv_idx2, symb_id) -> SecondDerivExternalFunctionNode // (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; second_deriv_external_function_node_map_t second_deriv_external_function_node_map;
// Flag to disable simplifications related to commutativity of addition and multiplication // Flag to disable simplifications related to commutativity of addition and multiplication
...@@ -102,11 +111,12 @@ private: ...@@ -102,11 +111,12 @@ private:
protected: protected:
//! Stores local variables value (maps symbol ID to corresponding node) //! Stores local variables value (maps symbol ID to corresponding node)
map<int, expr_t> local_variables_table; 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; vector<int> local_variables_vector;
//! Internal implementation of ParamUsedWithLeadLag() //! Internal implementation of ParamUsedWithLeadLag()
bool ParamUsedWithLeadLagInternal() const; [[nodiscard]] bool ParamUsedWithLeadLagInternal() const;
/* Writes the contents of “new_contents” to the file “filename”. However, if /* 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 the file already exists and would not be modified by this operation, then do
...@@ -119,18 +129,21 @@ private: ...@@ -119,18 +129,21 @@ private:
//! The list of nodes //! The list of nodes
vector<unique_ptr<ExprNode>> node_list; 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 AddUnaryOp(UnaryOpcode op_code, expr_t arg, int arg_exp_info_set = 0,
inline expr_t AddBinaryOp(expr_t arg1, BinaryOpcode op_code, expr_t arg2, int powerDerivOrder = 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); 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 //! Initializes the predefined constants, used only from the constructors
void initConstants(); void initConstants();
public: public:
DataTree(SymbolTable &symbol_table_arg, DataTree(SymbolTable& symbol_table_arg, NumericalConstants& num_constants_arg,
NumericalConstants &num_constants_arg,
ExternalFunctionsTable& external_functions_table_arg, ExternalFunctionsTable& external_functions_table_arg,
bool is_static_args = false); HeterogeneityTable& heterogeneity_table_arg, bool is_dynamic_arg = false);
virtual ~DataTree() = default; virtual ~DataTree() = default;
...@@ -159,7 +172,7 @@ public: ...@@ -159,7 +172,7 @@ public:
//! Gets a variable //! Gets a variable
/*! Same as AddVariable, except that it fails if the variable node has not /*! Same as AddVariable, except that it fails if the variable node has not
already been created */ 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 //! Adds "arg1+arg2" to model tree
expr_t AddPlus(expr_t iArg1, expr_t iArg2); expr_t AddPlus(expr_t iArg1, expr_t iArg2);
//! Adds "arg1-arg2" to model tree //! Adds "arg1-arg2" to model tree
...@@ -261,22 +274,28 @@ public: ...@@ -261,22 +274,28 @@ public:
//! Adds an external function node //! 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 //! 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 //! 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 //! 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 //! 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 //! 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 //! 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 //! Checks if a given binary op is used somewhere in the data tree on an endogenous variable
bool isBinaryOpUsedOnType(SymbolType type, BinaryOpcode opcode) const; [[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 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 */ /*! 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 /* Writes definitions of C function helpers (getPowerDeriv(), sign()) as
inline functions */ inline functions */
void writeCHelpersDefinition(ostream& output) const; void writeCHelpersDefinition(ostream& output) const;
...@@ -298,17 +317,17 @@ public: ...@@ -298,17 +317,17 @@ public:
}; };
// Returns the derivation ID, or throws an exception if the derivation ID does not exist // 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 // 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 // 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 // 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 // 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 // Get the symbol name corresponding to a derivation ID
string [[nodiscard]] string
getNameByDerivID(int deriv_id) const getNameByDerivID(int deriv_id) const
{ {
return symbol_table.getName(getSymbIDByDerivID(deriv_id)); return symbol_table.getName(getSymbIDByDerivID(deriv_id));
...@@ -317,7 +336,7 @@ public: ...@@ -317,7 +336,7 @@ public:
/* Returns the column of the Jacobian associated to a derivation ID. /* Returns the column of the Jacobian associated to a derivation ID.
The “sparse” argument selects between the legacy representation and the The “sparse” argument selects between the legacy representation and the
sparse representation. */ sparse representation. */
virtual int [[nodiscard]] virtual int
getJacobianCol([[maybe_unused]] int deriv_id, [[maybe_unused]] bool sparse) const getJacobianCol([[maybe_unused]] int deriv_id, [[maybe_unused]] bool sparse) const
{ {
throw UnknownDerivIDException(); throw UnknownDerivIDException();
...@@ -326,7 +345,7 @@ public: ...@@ -326,7 +345,7 @@ public:
/* Returns the number of columns of the Jacobian /* Returns the number of columns of the Jacobian
The “sparse” argument selects between the legacy representation and the The “sparse” argument selects between the legacy representation and the
sparse representation. */ sparse representation. */
virtual int [[nodiscard]] virtual int
getJacobianColsNbr([[maybe_unused]] bool sparse) const getJacobianColsNbr([[maybe_unused]] bool sparse) const
{ {
throw UnknownDerivIDException(); throw UnknownDerivIDException();
...@@ -335,27 +354,26 @@ public: ...@@ -335,27 +354,26 @@ public:
//! Adds to the set all the deriv IDs corresponding to parameters //! Adds to the set all the deriv IDs corresponding to parameters
virtual void addAllParamDerivId(set<int>& deriv_id_set); 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;
};
struct UnknownLocalVariableException struct UnknownLocalVariableException
{ {
//! Symbol ID //! Symbol ID
int id; int id;
}; };
expr_t [[nodiscard]] expr_t
getLocalVariable(int symb_id) const getLocalVariable(int symb_id, int lead_lag) const
{ {
auto it = local_variables_table.find(symb_id); auto it = local_variables_table.find(symb_id);
if (it == local_variables_table.end()) if (it == local_variables_table.end())
throw UnknownLocalVariableException {symb_id}; 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; return it->second;
else
return it->second->decreaseLeadsLags(-lead_lag);
} }
static void static void
...@@ -373,7 +391,7 @@ public: ...@@ -373,7 +391,7 @@ public:
and returns the path to the corresponding filesystem directory. and returns the path to the corresponding filesystem directory.
In practice the package nesting is used for the planner_objective (stored In practice the package nesting is used for the planner_objective (stored
inside +objective subdir). */ inside +objective subdir). */
static filesystem::path packageDir(string_view package); static filesystem::path packageDir(const string_view& package);
}; };
inline expr_t inline expr_t
...@@ -384,7 +402,7 @@ DataTree::AddPossiblyNegativeConstant(double v) ...@@ -384,7 +402,7 @@ DataTree::AddPossiblyNegativeConstant(double v)
if (isnan(v)) if (isnan(v))
return NaN; return NaN;
if (isinf(v)) if (isinf(v))
return (v < 0 ? MinusInfinity : Infinity); return v < 0 ? MinusInfinity : Infinity;
bool neg = false; bool neg = false;
if (v < 0) if (v < 0)
...@@ -404,21 +422,23 @@ DataTree::AddPossiblyNegativeConstant(double v) ...@@ -404,21 +422,23 @@ DataTree::AddPossiblyNegativeConstant(double v)
} }
inline expr_t 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 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()) it != unary_op_node_map.end())
return it->second; return it->second;
// Try to reduce to a constant // 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 // Case where arg is a constant and op_code == UnaryOpcode::uminus (i.e. we're adding a negative
if (auto carg = dynamic_cast<NumConstNode *>(arg); // constant) is skipped
op_code != UnaryOpcode::uminus || !carg) if (auto carg = dynamic_cast<NumConstNode*>(arg); op_code != UnaryOpcode::uminus || !carg)
{ {
try try
{ {
double argval = arg->eval({}); double argval = arg->eval({}); // NOLINT(clang-analyzer-core.CallAndMessage)
double val = UnaryOpNode::eval_opcode(op_code, argval); double val = UnaryOpNode::eval_opcode(op_code, argval);
return AddPossiblyNegativeConstant(val); return AddPossiblyNegativeConstant(val);
} }
...@@ -427,10 +447,13 @@ DataTree::AddUnaryOp(UnaryOpcode op_code, expr_t arg, int arg_exp_info_set, int ...@@ -427,10 +447,13 @@ DataTree::AddUnaryOp(UnaryOpcode op_code, expr_t arg, int arg_exp_info_set, int
} }
} }
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(); auto p = sp.get();
node_list.push_back(move(sp)); 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; return p;
} }
...@@ -444,8 +467,8 @@ DataTree::AddBinaryOp(expr_t arg1, BinaryOpcode op_code, expr_t arg2, int powerD ...@@ -444,8 +467,8 @@ DataTree::AddBinaryOp(expr_t arg1, BinaryOpcode op_code, expr_t arg2, int powerD
// Try to reduce to a constant // Try to reduce to a constant
try try
{ {
double argval1 = arg1->eval({}); double argval1 = arg1->eval({}); // NOLINT(clang-analyzer-core.CallAndMessage)
double argval2 = arg2->eval({}); double argval2 = arg2->eval({}); // NOLINT(clang-analyzer-core.CallAndMessage)
double val = BinaryOpNode::eval_opcode(argval1, op_code, argval2, powerDerivOrder); double val = BinaryOpNode::eval_opcode(argval1, op_code, argval2, powerDerivOrder);
return AddPossiblyNegativeConstant(val); return AddPossiblyNegativeConstant(val);
} }
...@@ -453,7 +476,8 @@ DataTree::AddBinaryOp(expr_t arg1, BinaryOpcode op_code, expr_t arg2, int powerD ...@@ -453,7 +476,8 @@ DataTree::AddBinaryOp(expr_t arg1, BinaryOpcode op_code, expr_t arg2, int powerD
{ {
} }
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(); auto p = sp.get();
node_list.push_back(move(sp)); 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);
......