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
  • 4.6
  • 5.x
  • 6.x
  • aux_vars_fix
  • julia
  • llvm-15
  • master
  • python-codegen
  • rework_pac
  • uop
  • julia-6.2.0
  • julia-6.3.0
  • julia-6.4.0
  • julia-7.0.0
14 results

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
  • master
  • created_preprocessor_repo
2 results
Show changes
Showing
with 11247 additions and 6406 deletions
/* /*
* Copyright (C) 2003-2018 Dynare Team * Copyright © 2003-2023 Dynare Team
* *
* This file is part of Dynare. * This file is part of Dynare.
* *
...@@ -14,59 +14,89 @@ ...@@ -14,59 +14,89 @@
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Dynare. If not, see <http://www.gnu.org/licenses/>. * along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include <algorithm>
#include <filesystem>
#include <fstream>
#include <iostream> #include <iostream>
#include <regex>
#include <sstream> #include <sstream>
#include <fstream> #include <string>
#include <thread>
#include <vector>
#include <cstdlib> #include <cstdlib>
#include <cstring>
#ifndef PACKAGE_VERSION
# define PACKAGE_VERSION 4.
#endif
#include <unistd.h> #include <unistd.h>
#include "ParsingDriver.hh"
#include "Configuration.hh"
#include "ExtendedPreprocessorTypes.hh" #include "ExtendedPreprocessorTypes.hh"
#include "ConfigFile.hh" #include "ModFile.hh"
#include "ParsingDriver.hh"
/* Prototype for second part of main function /* Prototype for the function that handles the macro-expansion of the .mod file
Splitting main() in two parts was necessary because ParsingDriver.h and MacroDriver.h can't be Splitting this out was necessary because ParsingDriver.hh and macro/Driver.hh can't be
included simultaneously (because of Bison limitations). included simultaneously (because of Bison limitations).
Function can be found in: MacroExpandModFile.cc
*/ */
void main2(stringstream &in, string &basename, bool debug, bool clear_all, bool clear_global, stringstream macroExpandModFile(const filesystem::path& filename, const istream& modfile,
bool no_tmp_terms, bool no_log, bool no_warn, bool warn_uninit, bool console, bool debug, bool save_macro, filesystem::path save_macro_file,
bool nograph, bool nointeractive, bool parallel, ConfigFile &config_file, bool line_macro, const vector<pair<string, string>>& defines,
WarningConsolidation &warnings_arg, bool nostrict, bool stochastic, bool check_model_changes, vector<filesystem::path> paths);
bool minimal_workspace, bool compute_xrefs, FileOutputType output_mode,
LanguageOutputType lang, int params_derivs_order
#if defined(_WIN32) || defined(__CYGWIN32__) || defined(__MINGW32__)
, bool cygwin, bool msvc, bool mingw
#endif
, JsonOutputPointType json, JsonFileOutputType json_output_mode, bool onlyjson, bool jsonderivsimple
, bool nopreprocessoroutput
);
void main1(string &modfile, string &basename, string &modfiletxt, bool debug, bool save_macro, string &save_macro_file,
bool no_line_macro, bool no_empty_line_macro, map<string, string> &defines, vector<string> &path, stringstream &macro_output);
void void
usage() usage()
{ {
cerr << "Dynare usage: dynare mod_file [debug] [noclearall] [onlyclearglobals] [savemacro[=macro_file]] [onlymacro] [nolinemacro] [noemptylinemacro] [notmpterms] [nolog] [warn_uninit]" cerr << "Dynare usage: dynare mod_file [debug] [noclearall] [onlyclearglobals] "
<< " [console] [nograph] [nointeractive] [parallel[=cluster_name]] [conffile=parallel_config_path_and_filename] [parallel_slave_open_mode] [parallel_test]" "[savemacro[=macro_file]] [onlymacro] [linemacro] [notmpterms] [nolog] [warn_uninit]"
<< " [-D<variable>[=<value>]] [-I/path] [nostrict] [stochastic] [fast] [minimal_workspace] [compute_xrefs] [output=dynamic|first|second|third] [language=C|C++|julia]" << " [console] [nograph] [nointeractive] [parallel[=cluster_name]] "
<< " [params_derivs_order=0|1|2]" "[conffile=path_to_config_file] [parallel_follower_open_mode] "
#if defined(_WIN32) || defined(__CYGWIN32__) || defined(__MINGW32__) "[parallel_test] [parallel_use_psexec=true|false]"
<< " [cygwin] [msvc] [mingw]" << " [-D<variable>[=<value>]] [-I/path] [nostrict] [stochastic] [fast] [minimal_workspace] "
#endif "[compute_xrefs] [output=first|second|third] [language=matlab|julia]"
<< " [json=parse|check|transform|compute] [jsonstdout] [onlyjson] [jsonderivsimple] [nopathchange] [nopreprocessoroutput]" << " [params_derivs_order=0|1|2] [transform_unary_ops] "
"[exclude_eqs=<equation_tag_list_or_file>] [include_eqs=<equation_tag_list_or_file>]"
<< " [json=parse|check|transform|compute] [jsonstdout] [onlyjson] [jsonderivsimple] "
"[nopathchange] [nopreprocessoroutput]"
<< " [mexext=<extension>] [matlabroot=<path>] [onlymodel] [notime] [use_dll] "
"[nocommutativity]"
<< endl; << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
/* Looks for an options list in the first non-empty line of the .mod file (but rewind
the input stream afterwards).
This function should be kept in sync with the one with the same name in matlab/dynare.m */
vector<string>
parse_options_line(istream& modfile)
{
vector<string> options;
string first_nonempty_line;
regex pat {R"(^\s*//\s*--\+\s*options:([^\+]*)\+--)"};
smatch matches;
while (getline(modfile, first_nonempty_line))
if (!first_nonempty_line.empty())
{
if (regex_search(first_nonempty_line, matches, pat) && matches.size() > 1
&& matches[1].matched)
{
regex pat2 {R"([^,\s]+)"};
string s {matches[1]};
for (sregex_iterator p(s.begin(), s.end(), pat2); p != sregex_iterator {}; ++p)
options.push_back(p->str());
}
break;
}
modfile.seekg(0);
return options;
}
int int
main(int argc, char** argv) main(int argc, char** argv)
{ {
...@@ -83,331 +113,444 @@ main(int argc, char **argv) ...@@ -83,331 +113,444 @@ main(int argc, char **argv)
usage(); usage();
} }
const filesystem::path filename {argv[1]};
ifstream modfile(filename, ios::binary);
if (modfile.fail())
{
cerr << "ERROR: Could not open file: " << filename.string() << endl;
exit(EXIT_FAILURE);
}
// Create options list, using first line of mod-file and command line
vector<string> options = parse_options_line(modfile);
for (int arg = 2; arg < argc; arg++)
options.emplace_back(argv[arg]);
// Parse options
bool notime = false;
bool clear_all = true; bool clear_all = true;
bool clear_global = false; bool clear_global = false;
bool save_macro = false; bool save_macro = false;
string save_macro_file; filesystem::path save_macro_file;
bool debug = false; bool debug = false;
bool no_tmp_terms = false; bool no_tmp_terms = false;
bool only_macro = false; bool only_macro = false;
bool no_line_macro = false; bool line_macro = false;
bool no_empty_line_macro = false;
bool no_log = false;
bool no_warn = false; bool no_warn = false;
int params_derivs_order = 2; int params_derivs_order = 2;
bool warn_uninit = false; bool warn_uninit = false;
bool console = false; bool console = false;
bool nograph = false; bool nograph = false;
bool nointeractive = false; bool nointeractive = false;
#if defined(_WIN32) || defined(__CYGWIN32__) || defined(__MINGW32__) filesystem::path conffile;
bool cygwin = false;
bool msvc = false;
bool mingw = false;
#endif
string parallel_config_file;
bool parallel = false; bool parallel = false;
string cluster_name; string cluster_name;
bool parallel_slave_open_mode = false; bool parallel_follower_open_mode
= false; // Must be the same default as in matlab/default_option_values.m
bool parallel_test = false; bool parallel_test = false;
bool parallel_use_psexec = true; // Must be the same default as in matlab/default_option_values.m
bool nostrict = false; bool nostrict = false;
bool stochastic = false; bool stochastic = false;
bool check_model_changes = false; bool check_model_changes = false;
bool minimal_workspace = false; bool minimal_workspace = false;
bool compute_xrefs = false; bool compute_xrefs = false;
map<string, string> defines; bool transform_unary_ops = false;
vector<string> path; bool gui = false;
FileOutputType output_mode = none; string exclude_eqs, include_eqs;
JsonOutputPointType json = nojson; vector<pair<string, string>> defines;
JsonFileOutputType json_output_mode = file; vector<filesystem::path> paths;
OutputType output_mode {OutputType::standard};
JsonOutputPointType json {JsonOutputPointType::nojson};
JsonFileOutputType json_output_mode {JsonFileOutputType::file};
bool onlyjson = false; bool onlyjson = false;
bool jsonderivsimple = false; bool jsonderivsimple = false;
LanguageOutputType language = matlab; LanguageOutputType language {LanguageOutputType::matlab};
bool nopreprocessoroutput = false; string mexext;
filesystem::path matlabroot;
bool onlymodel = false;
bool use_dll = false;
// Parse options for (auto s : options)
for (int arg = 2; arg < argc; arg++)
{ {
if (!strcmp(argv[arg], "debug")) if (s == "debug")
debug = true; debug = true;
else if (!strcmp(argv[arg], "noclearall")) else if (s == "notime")
notime = true;
else if (s == "noclearall")
clear_all = false; clear_all = false;
else if (strlen(argv[arg]) >= 19 && !strncmp(argv[arg], "params_derivs_order", 19)) else if (s.substr(0, 19) == "params_derivs_order")
{ {
if (strlen(argv[arg]) >= 22 || argv[arg][19] != '=' if (s.length() > 21 || s.at(19) != '='
|| !(argv[arg][20] == '0' || argv[arg][20] == '1' || argv[arg][20] == '2')) || !(s.at(20) == '0' || s.at(20) == '1' || s.at(20) == '2'))
{ {
cerr << "Incorrect syntax for params_derivs_order option" << endl; cerr << "Incorrect syntax for params_derivs_order option" << endl;
usage(); usage();
} }
params_derivs_order = atoi(argv[arg] + 20); params_derivs_order = stoi(s.substr(20));
} }
else if (!strcmp(argv[arg], "onlyclearglobals")) else if (s == "onlyclearglobals")
{ {
clear_all = false; clear_all = false;
clear_global = true; clear_global = true;
} }
else if (!strcmp(argv[arg], "onlymacro")) else if (s == "onlymacro")
only_macro = true; only_macro = true;
else if (strlen(argv[arg]) >= 9 && !strncmp(argv[arg], "savemacro", 9)) else if (s.substr(0, 9) == "savemacro")
{ {
save_macro = true; save_macro = true;
if (strlen(argv[arg]) > 9) if (s.length() > 9)
{ {
if (strlen(argv[arg]) == 10 || argv[arg][9] != '=') if (s.length() == 10 || s.at(9) != '=')
{ {
cerr << "Incorrect syntax for savemacro option" << endl; cerr << "Incorrect syntax for savemacro option" << endl;
usage(); usage();
} }
save_macro_file = string(argv[arg] + 10); save_macro_file = s.substr(10);
} }
} }
else if (!strcmp(argv[arg], "nolinemacro")) else if (s == "linemacro")
no_line_macro = true; line_macro = true;
else if (!strcmp(argv[arg], "noemptylinemacro")) else if (s == "notmpterms")
no_empty_line_macro = true;
else if (!strcmp(argv[arg], "notmpterms"))
no_tmp_terms = true; no_tmp_terms = true;
else if (!strcmp(argv[arg], "nolog")) else if (s == "nolog")
no_log = true; {
else if (!strcmp(argv[arg], "nowarn")) // Do nothing, the option is implemented at the dynare.m level.
// We nevertheless accept it, to avoid an “unknown option” error.
}
else if (s == "nowarn")
no_warn = true; no_warn = true;
else if (!strcmp(argv[arg], "warn_uninit")) else if (s == "warn_uninit")
warn_uninit = true; warn_uninit = true;
else if (!strcmp(argv[arg], "console")) else if (s == "console")
console = true; console = true;
else if (!strcmp(argv[arg], "nograph")) else if (s == "nograph")
nograph = true; nograph = true;
else if (!strcmp(argv[arg], "nointeractive")) else if (s == "nointeractive")
nointeractive = true; nointeractive = true;
#if defined(_WIN32) || defined(__CYGWIN32__) || defined(__MINGW32__) else if (s.substr(0, 8) == "conffile")
else if (!strcmp(argv[arg], "cygwin")) {
cygwin = true; if (s.length() <= 9 || s.at(8) != '=')
else if (!strcmp(argv[arg], "msvc"))
msvc = true;
else if (!strcmp(argv[arg], "mingw"))
mingw = true;
#endif
else if (strlen(argv[arg]) >= 8 && !strncmp(argv[arg], "conffile", 8))
{
if (strlen(argv[arg]) <= 9 || argv[arg][8] != '=')
{ {
cerr << "Incorrect syntax for conffile option" << endl; cerr << "Incorrect syntax for conffile option" << endl;
usage(); usage();
} }
parallel_config_file = string(argv[arg] + 9); conffile = s.substr(9);
} }
else if (!strcmp(argv[arg], "parallel_slave_open_mode")) else if (s == "parallel_follower_open_mode"
parallel_slave_open_mode = true; || s == "parallel_slave_open_mode") // Kept for backward compatibility, see #86
else if (!strcmp(argv[arg], "parallel_test")) parallel_follower_open_mode = true;
else if (s == "parallel_test")
parallel_test = true; parallel_test = true;
else if (!strcmp(argv[arg], "nostrict")) else if (s.substr(0, 19) == "parallel_use_psexec")
{
if (s.length() <= 20 || s.at(19) != '=')
{
cerr << "Incorrect syntax for parallel_use_psexec option" << endl;
usage();
}
s.erase(0, 20);
if (s == "true")
parallel_use_psexec = true;
else if (s == "false")
parallel_use_psexec = false;
else
{
cerr << "Incorrect syntax for parallel_use_psexec option" << endl;
usage();
}
}
else if (s == "nostrict")
nostrict = true; nostrict = true;
else if (!strcmp(argv[arg], "stochastic")) else if (s == "stochastic")
stochastic = true; stochastic = true;
else if (!strcmp(argv[arg], "fast")) else if (s == "fast")
check_model_changes = true; check_model_changes = true;
else if (!strcmp(argv[arg], "minimal_workspace")) else if (s == "minimal_workspace")
minimal_workspace = true; minimal_workspace = true;
else if (!strcmp(argv[arg], "compute_xrefs")) else if (s == "compute_xrefs")
compute_xrefs = true; compute_xrefs = true;
else if (strlen(argv[arg]) >= 8 && !strncmp(argv[arg], "parallel", 8)) else if (s == "transform_unary_ops")
transform_unary_ops = true;
else if (s.substr(0, 8) == "parallel")
{ {
parallel = true; parallel = true;
if (strlen(argv[arg]) > 8) if (s.length() > 8)
{ {
if (strlen(argv[arg]) == 9 || argv[arg][8] != '=') if (s.length() == 9 || s.at(8) != '=')
{ {
cerr << "Incorrect syntax for parallel option" << endl; cerr << "Incorrect syntax for parallel option" << endl;
usage(); usage();
} }
cluster_name = string(argv[arg] + 9); cluster_name = s.substr(9);
} }
} }
else if (strlen(argv[arg]) >= 2 && !strncmp(argv[arg], "-D", 2)) else if (s.substr(0, 2) == "-D")
{ {
if (strlen(argv[arg]) == 2) if (s.length() == 2)
{ {
cerr << "Incorrect syntax for command line define: the defined variable " cerr << "Incorrect syntax for command line define: the defined variable "
<< "must not be separated from -D by whitespace." << endl; << "must not be separated from -D by whitespace." << endl;
usage(); usage();
} }
size_t equal_index = string(argv[arg]).find('='); if (auto equal_index = s.find('='); equal_index != string::npos)
if (equal_index != string::npos) defines.emplace_back(s.substr(2, equal_index - 2), s.substr(equal_index + 1));
{
string key = string(argv[arg]).erase(equal_index).erase(0, 2);
defines[key] = string(argv[arg]).erase(0, equal_index+1);
}
else else
{ defines.emplace_back(s.substr(2), "true");
string key = string(argv[arg]).erase(0, 2);
defines[key] = "1";
}
} }
else if (strlen(argv[arg]) >= 2 && !strncmp(argv[arg], "-I", 2)) else if (s.substr(0, 2) == "-I")
{ {
if (strlen(argv[arg]) == 2) if (s.length() == 2)
{ {
cerr << "Incorrect syntax for command line define: the defined variable " cerr << "Incorrect syntax for command line define: the defined variable "
<< "must not be separated from -I by whitespace." << endl; << "must not be separated from -I by whitespace." << endl;
usage(); usage();
} }
path.push_back(string(argv[arg]).erase(0, 2)); paths.emplace_back(s.substr(2));
} }
else if (strlen(argv[arg]) >= 6 && !strncmp(argv[arg], "output", 6)) else if (s.substr(0, 6) == "output")
{ {
if (strlen(argv[arg]) <= 7 || argv[arg][6] != '=') if (s.length() <= 7 || s.at(6) != '=')
{ {
cerr << "Incorrect syntax for output option" << endl; cerr << "Incorrect syntax for output option" << endl;
usage(); usage();
} }
if (strlen(argv[arg]) == 14 && !strncmp(argv[arg] + 7, "dynamic", 7))
output_mode = dynamic; s.erase(0, 7);
else if (strlen(argv[arg]) == 12 && !strncmp(argv[arg] + 7, "first", 5))
output_mode = first; if (s == "first")
else if (strlen(argv[arg]) == 13 && !strncmp(argv[arg] + 7, "second", 6)) output_mode = OutputType::first;
output_mode = second; else if (s == "second")
else if (strlen(argv[arg]) == 12 && !strncmp(argv[arg] + 7, "third", 5)) output_mode = OutputType::second;
output_mode = third; else if (s == "third")
output_mode = OutputType::third;
else else
{ {
cerr << "Incorrect syntax for output option" << endl; cerr << "Incorrect syntax for output option" << endl;
usage(); usage();
} }
} }
else if (strlen(argv[arg]) >= 8 && !strncmp(argv[arg], "language", 8)) else if (s.substr(0, 8) == "language")
{ {
if (strlen(argv[arg]) <= 9 || argv[arg][8] != '=') if (s.length() <= 9 || s.at(8) != '=')
{ {
cerr << "Incorrect syntax for language option" << endl; cerr << "Incorrect syntax for language option" << endl;
usage(); usage();
} }
if (strlen(argv[arg]) == 14 && !strncmp(argv[arg] + 9, "julia", 5)) s.erase(0, 9);
language = julia;
else if (s == "matlab")
{ language = LanguageOutputType::matlab;
// we don't want temp terms in external functions (except Julia) else if (s == "julia")
no_tmp_terms = true; language = LanguageOutputType::julia;
if (strlen(argv[arg]) == 10 && !strncmp(argv[arg] + 9, "C", 1))
language = c;
else if (strlen(argv[arg]) == 12 && !strncmp(argv[arg] + 9, "C++", 3))
language = cpp;
else if (strlen(argv[arg]) == 13 && !strncmp(argv[arg] + 9, "cuda", 4))
language = cuda;
else if (strlen(argv[arg]) == 15 && !strncmp(argv[arg] + 9, "python", 6))
language = python;
else else
{ {
cerr << "Incorrect syntax for language option" << endl; cerr << "Incorrect syntax for language option" << endl;
usage(); usage();
} }
} }
} else if (s == "jsonstdout")
else if (!strcmp(argv[arg], "jsonstdout")) json_output_mode = JsonFileOutputType::standardout;
json_output_mode = standardout; else if (s == "onlyjson")
else if (!strcmp(argv[arg], "onlyjson"))
onlyjson = true; onlyjson = true;
else if (!strcmp(argv[arg], "nopreprocessoroutput")) else if (s == "nopreprocessoroutput")
nopreprocessoroutput = true; cout.rdbuf(nullptr);
else if (!strcmp(argv[arg], "jsonderivsimple")) else if (s == "jsonderivsimple")
jsonderivsimple = true; jsonderivsimple = true;
else if (strlen(argv[arg]) >= 4 && !strncmp(argv[arg], "json", 4)) else if (s.substr(0, 4) == "json")
{ {
if (strlen(argv[arg]) <= 5 || argv[arg][4] != '=') if (s.length() <= 5 || s.at(4) != '=')
{ {
cerr << "Incorrect syntax for json option" << endl; cerr << "Incorrect syntax for json option" << endl;
usage(); usage();
} }
if (strlen(argv[arg]) == 10 && !strncmp(argv[arg] + 5, "parse", 5))
json = parsing; s.erase(0, 5);
else if (strlen(argv[arg]) == 10 && !strncmp(argv[arg] + 5, "check", 5))
json = checkpass; if (s == "parse")
else if (strlen(argv[arg]) == 14 && !strncmp(argv[arg] + 5, "transform", 9)) json = JsonOutputPointType::parsing;
json = transformpass; else if (s == "check")
else if (strlen(argv[arg]) == 12 && !strncmp(argv[arg] + 5, "compute", 7)) json = JsonOutputPointType::checkpass;
json = computingpass; else if (s == "transform")
json = JsonOutputPointType::transformpass;
else if (s == "compute")
json = JsonOutputPointType::computingpass;
else else
{ {
cerr << "Incorrect syntax for json option" << endl; cerr << "Incorrect syntax for json option" << endl;
usage(); usage();
} }
} }
else else if (s.substr(0, 6) == "mexext")
{
if (s.length() <= 7 || s.at(6) != '=')
{ {
cerr << "Unknown option: " << argv[arg] << endl; cerr << "Incorrect syntax for mexext option" << endl;
usage(); usage();
} }
mexext = s.substr(7);
} }
else if (s.substr(0, 11) == "exclude_eqs")
if (!nopreprocessoroutput) {
cout << "Dynare Preprocessor (version " << PACKAGE_VERSION << ")." << endl if (s.length() <= 12 || s.at(11) != '=')
<< "Starting preprocessing of the model file ..." << endl; {
cerr << "Incorrect syntax for exclude_eqs option" << endl;
// Construct basename (i.e. remove file extension if there is one) usage();
string basename = argv[1]; }
string modfile, modfiletxt; exclude_eqs = s.substr(12);
size_t fsc = basename.find_first_of(';'); }
if (fsc != string::npos) else if (s.substr(0, 11) == "include_eqs")
{
if (s.length() <= 12 || s.at(11) != '=')
{
cerr << "Incorrect syntax for include_eqs option" << endl;
usage();
}
include_eqs = s.substr(12);
}
else if (s.substr(0, 10) == "matlabroot")
{ {
// If a semicolon is found in argv[1], treat it as the text of the modfile if (s.length() <= 11 || s.at(10) != '=')
modfile = "mod_file_passed_as_string.mod"; {
basename = "mod_file_passed_as_string"; cerr << "Incorrect syntax for matlabroot option" << endl;
modfiletxt = argv[1]; usage();
} }
matlabroot = filesystem::path {s.substr(11)};
}
else if (s == "onlymodel")
onlymodel = true;
else if (s == "gui")
gui = true;
else if (s == "use_dll")
use_dll = true;
else if (s == "nocommutativity")
DataTree::setNoCommutativity();
else else
{ {
// If a semicolon is NOT found in argv[1], treat it as the name of the modfile cerr << "Unknown option: " << s << endl;
modfile = argv[1]; usage();
size_t pos = basename.find_last_of('.'); }
if (pos != string::npos) }
basename.erase(pos);
ifstream modfile(argv[1], ios::binary); cout << "Starting preprocessing of the model file ..." << endl;
if (modfile.fail())
// Determine root of Dynare installation
const filesystem::path argv0 {argv[0]};
// Normal case: binary is in preprocessor/dynare-preprocessor(.exe)?
filesystem::path dynareroot = argv0.parent_path().parent_path();
if (argv0.filename().stem() == "dynare_m")
// Special case: backward compatibility location in matlab/preprocessor64/dynare_m(.exe)?
dynareroot = dynareroot.parent_path();
// Construct basename (i.e. remove file extension if there is one)
/* Calling string() method on filename.stem(): not necessary on GNU/Linux and macOS because there
is an implicit conversion from filesystem:path to string (i.e. basic_string<char>), but needed
on Windows because the implicit conversion is only to wstring (i.e. basic_string<wchar_t>). */
const string basename {filename.stem().string()};
// Forbid some basenames, since they will cause trouble (see preprocessor#62)
set<string> forbidden_basenames = {"T", "y", "x", "params", "steady_state", "it_", "true"};
if (forbidden_basenames.contains(basename))
{ {
cerr << "ERROR: Could not open file: " << argv[1] << endl; cerr << "ERROR: Please use another name for your .mod file. The one you have chosen ("
<< argv[1] << ") conflicts with internal Dynare names." << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
stringstream buffer;
buffer << modfile.rdbuf();
modfiletxt = buffer.str();
}
WarningConsolidation warnings(no_warn); WarningConsolidation warnings(no_warn);
// Process config file // Process config file
ConfigFile config_file(parallel, parallel_test, parallel_slave_open_mode, cluster_name); Configuration config {parallel, parallel_test, parallel_follower_open_mode, parallel_use_psexec,
config_file.getConfigFileInfo(parallel_config_file); cluster_name};
config_file.checkPass(warnings); config.getConfigFileInfo(conffile, warnings);
config_file.transformPass(); config.checkPass(warnings);
config.transformPass();
// If Include option was passed to the [paths] block of the config file, add // If Include option was passed to the [paths] block of the config file, add
// it to paths before macroprocessing // it to paths before macroprocessing
vector<string> config_include_paths = config_file.getIncludePaths(); for (const auto& it : config.getIncludePaths())
for (vector<string>::const_iterator it = config_include_paths.begin(); paths.emplace_back(it);
it != config_include_paths.end(); it++)
path.push_back(*it);
// Do macro processing /*
stringstream macro_output; * Macro-expand MOD file
main1(modfile, basename, modfiletxt, debug, save_macro, save_macro_file, no_line_macro, no_empty_line_macro, */
defines, path, macro_output); stringstream macro_output
= macroExpandModFile(filename, modfile, debug, save_macro, move(save_macro_file), line_macro,
defines, move(paths));
if (only_macro) if (only_macro)
return EXIT_SUCCESS; return EXIT_SUCCESS;
// Do the rest if (!exclude_eqs.empty() && !include_eqs.empty())
main2(macro_output, basename, debug, clear_all, clear_global, {
no_tmp_terms, no_log, no_warn, warn_uninit, console, nograph, nointeractive, cerr << "You may only pass one of `include_eqs` and `exclude_eqs`" << endl;
parallel, config_file, warnings, nostrict, stochastic, check_model_changes, minimal_workspace, exit(EXIT_FAILURE);
compute_xrefs, output_mode, language, params_derivs_order }
#if defined(_WIN32) || defined(__CYGWIN32__) || defined(__MINGW32__)
, cygwin, msvc, mingw /*
#endif * Process Macro-expanded MOD file
, json, json_output_mode, onlyjson, jsonderivsimple, nopreprocessoroutput */
); ParsingDriver p(warnings, nostrict);
filesystem::remove_all(basename + "/model/json");
// Do parsing and construct internal representation of mod file
unique_ptr<ModFile> mod_file = p.parse(macro_output, debug);
// Handle use_dll option specified on the command line
if (use_dll)
mod_file->use_dll = true;
if (mod_file->use_dll && language == LanguageOutputType::julia)
{
cerr << "ERROR: `use_dll` option is not compatible with Julia" << endl;
exit(EXIT_FAILURE);
}
if (mod_file->use_dll)
ModelTree::initializeMEXCompilationWorkers(max(jthread::hardware_concurrency(), 1U), dynareroot,
mexext);
if (json == JsonOutputPointType::parsing)
mod_file->writeJsonOutput(basename, json, json_output_mode, onlyjson);
// Run checking pass
mod_file->checkPass(nostrict, stochastic);
if (json == JsonOutputPointType::checkpass)
mod_file->writeJsonOutput(basename, json, json_output_mode, onlyjson);
// Perform transformations on the model (creation of auxiliary vars and equations)
mod_file->transformPass(nostrict, stochastic,
compute_xrefs || json == JsonOutputPointType::transformpass,
transform_unary_ops, exclude_eqs, include_eqs);
if (json == JsonOutputPointType::transformpass)
mod_file->writeJsonOutput(basename, json, json_output_mode, onlyjson);
// Evaluate parameters initialization, initval, endval and pounds
mod_file->evalAllExpressions(warn_uninit);
// Do computations
mod_file->computingPass(no_tmp_terms, output_mode, params_derivs_order);
if (json == JsonOutputPointType::computingpass)
mod_file->writeJsonOutput(basename, json, json_output_mode, onlyjson, jsonderivsimple);
// Write output files
if (language == LanguageOutputType::julia)
mod_file->writeJuliaOutput(basename);
else
mod_file->writeMOutput(basename, clear_all, clear_global, no_warn, console, nograph,
nointeractive, config, check_model_changes, minimal_workspace,
compute_xrefs, mexext, matlabroot, onlymodel, gui, notime);
/* Ensures that workers are not destroyed before they finish compiling.
Also ensures that the preprocessor final message is printed after the end of
compilation (and is not printed in case of compilation failure). */
if (mod_file->use_dll)
ModelTree::waitForMEXCompilationWorkers();
cout << "Preprocessing completed." << endl;
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
/*
* Copyright (C) 2015-2017 Dynare Team
*
* This file is part of Dynare.
*
* Dynare is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Dynare is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Dynare. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sstream>
#include <fstream>
#include "macro/MacroDriver.hh"
bool compareNewline (int i, int j) {
return i == '\n' && j == '\n';
}
void
main1(string &modfile, string &basename, string &modfiletxt, bool debug, bool save_macro, string &save_macro_file,
bool no_line_macro, bool no_empty_line_macro, map<string, string> &defines, vector<string> &path, stringstream &macro_output)
{
// Do macro processing
MacroDriver m;
m.parse(modfile, basename, modfiletxt, macro_output, debug, no_line_macro, defines, path);
if (save_macro)
{
if (save_macro_file.empty())
save_macro_file = basename + "-macroexp.mod";
ofstream macro_output_file(save_macro_file.c_str());
if (macro_output_file.fail())
{
cerr << "Cannot open " << save_macro_file << " for macro output" << endl;
exit(EXIT_FAILURE);
}
string str (macro_output.str());
if (no_empty_line_macro)
str.erase(unique(str.begin(), str.end(), compareNewline), str.end());
macro_output_file << str;
macro_output_file.close();
}
}
/*
* Copyright (C) 2008-2017 Dynare Team
*
* This file is part of Dynare.
*
* Dynare is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Dynare is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Dynare. If not, see <http://www.gnu.org/licenses/>.
*/
#include <iostream>
#include "ParsingDriver.hh"
#include "ModFile.hh"
#include "ConfigFile.hh"
#include "ExtendedPreprocessorTypes.hh"
void
main2(stringstream &in, string &basename, bool debug, bool clear_all, bool clear_global,
bool no_tmp_terms, bool no_log, bool no_warn, bool warn_uninit, bool console,
bool nograph, bool nointeractive, bool parallel, ConfigFile &config_file,
WarningConsolidation &warnings, bool nostrict, bool stochastic, bool check_model_changes,
bool minimal_workspace, bool compute_xrefs, FileOutputType output_mode,
LanguageOutputType language, int params_derivs_order
#if defined(_WIN32) || defined(__CYGWIN32__) || defined(__MINGW32__)
, bool cygwin, bool msvc, bool mingw
#endif
, JsonOutputPointType json, JsonFileOutputType json_output_mode, bool onlyjson, bool jsonderivsimple
, bool nopreprocessoroutput
)
{
ParsingDriver p(warnings, nostrict);
// Do parsing and construct internal representation of mod file
ModFile *mod_file = p.parse(in, debug);
if (json == parsing)
mod_file->writeJsonOutput(basename, json, json_output_mode, onlyjson, nopreprocessoroutput);
// Run checking pass
mod_file->checkPass(nostrict, stochastic);
if (json == checkpass)
mod_file->writeJsonOutput(basename, json, json_output_mode, onlyjson, nopreprocessoroutput);
// Perform transformations on the model (creation of auxiliary vars and equations)
mod_file->transformPass(nostrict, stochastic, compute_xrefs || json == transformpass, nopreprocessoroutput);
if (json == transformpass)
mod_file->writeJsonOutput(basename, json, json_output_mode, onlyjson, nopreprocessoroutput);
// Evaluate parameters initialization, initval, endval and pounds
mod_file->evalAllExpressions(warn_uninit, nopreprocessoroutput);
// Do computations
mod_file->computingPass(no_tmp_terms, output_mode, params_derivs_order, nopreprocessoroutput);
if (json == computingpass)
mod_file->writeJsonOutput(basename, json, json_output_mode, onlyjson, nopreprocessoroutput, jsonderivsimple);
// Write outputs
if (output_mode != none)
mod_file->writeExternalFiles(basename, output_mode, language, nopreprocessoroutput);
else
mod_file->writeOutputFiles(basename, clear_all, clear_global, no_log, no_warn, console, nograph,
nointeractive, config_file, check_model_changes, minimal_workspace, compute_xrefs
#if defined(_WIN32) || defined(__CYGWIN32__) || defined(__MINGW32__)
, cygwin, msvc, mingw
#endif
, nopreprocessoroutput
);
delete mod_file;
if (!nopreprocessoroutput)
cout << "Preprocessing completed." << endl;
}
/*
* Copyright © 2020-2025 Dynare Team
*
* This file is part of Dynare.
*
* Dynare is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Dynare is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/
#include "EquationTags.hh"
#include <ostream>
#include <regex>
#include <utility>
set<int>
EquationTags::getEqnsByKey(const string& key) const
{
set<int> retval;
for (const auto& [eqn, tags] : eqn_tags)
if (tags.contains(key))
retval.insert(eqn);
return retval;
}
set<int>
EquationTags::getEqnsByTag(const string& key, const string& value) const
{
set<int> retval;
for (const auto& [eqn, tags] : eqn_tags)
if (auto tmp = tags.find(key); tmp != tags.end() && tmp->second == value)
retval.insert(eqn);
return retval;
}
optional<int>
EquationTags::getEqnByTag(const string& key, const string& value) const
{
for (const auto& [eqn, tags] : eqn_tags)
if (auto tmp = tags.find(key); tmp != tags.end() && tmp->second == value)
return eqn;
return nullopt;
}
set<int>
EquationTags::getEqnsByTags(const map<string, string>& tags_selected) const
{
set<int> retval;
for (const auto& [eqn, tags] : eqn_tags)
{
for (const auto& [key, value] : tags_selected)
if (auto tmp = tags.find(key); tmp == tags.end() || tmp->second != value)
goto next_eq;
retval.insert(eqn);
next_eq:;
}
return retval;
}
void
EquationTags::erase(const set<int>& eqns, const map<int, int>& old_eqn_num_2_new)
{
for (int eqn : eqns)
eqn_tags.erase(eqn);
for (const auto& [oldeqn, neweqn] : old_eqn_num_2_new)
if (eqn_tags.contains(oldeqn))
{
auto tmp = eqn_tags.extract(oldeqn);
tmp.key() = neweqn;
eqn_tags.insert(move(tmp));
}
}
void
EquationTags::writeCheckSumInfo(ostream& output) const
{
for (const auto& [eqn, tags] : eqn_tags)
for (const auto& [key, value] : tags)
output << " " << eqn + 1 << key << " " << value << endl;
}
void
EquationTags::writeOutput(ostream& output) const
{
output << "M_.equations_tags = {" << endl;
for (const auto& [eqn, tags] : eqn_tags)
for (const auto& [key, value] : tags)
output << " " << eqn + 1 << " , '" << key << "' , '" << value << "' ;" << endl;
output << "};" << endl;
}
void
EquationTags::writeLatexOutput(ostream& output, int eqn) const
{
if (!eqn_tags.contains(eqn))
return;
auto escape_special_latex_symbols = [](string str) {
const regex special_latex_chars(R"([&%$#_{}])");
const regex backslash(R"(\\)");
const regex tilde(R"(~)");
const regex carrot(R"(\^)");
const regex textbackslash(R"(\\textbackslash)");
str = regex_replace(str, backslash, R"(\textbackslash)");
str = regex_replace(str, special_latex_chars, R"(\$&)");
str = regex_replace(str, carrot, R"(\^{})");
str = regex_replace(str, tilde, R"(\textasciitilde{})");
return regex_replace(str, textbackslash, R"(\textbackslash{})");
};
output << R"(\noindent[)";
for (bool wrote_eq_tag {false}; const auto& [key, value] : eqn_tags.at(eqn))
{
if (exchange(wrote_eq_tag, true))
output << ", ";
output << escape_special_latex_symbols(key);
if (!value.empty())
output << "= `" << escape_special_latex_symbols(value) << "'";
}
output << "]" << endl;
}
void
EquationTags::writeJsonAST(ostream& output, int eqn) const
{
if (!eqn_tags.contains(eqn))
return;
output << R"(, "tags": {)";
for (bool wroteFirst {false}; const auto& [key, value] : eqn_tags.at(eqn))
{
if (exchange(wroteFirst, true))
output << ", ";
output << R"(")" << key << R"(": ")" << value << R"(")";
}
output << "}";
}
/*
* Copyright © 2020-2023 Dynare Team
*
* This file is part of Dynare.
*
* Dynare is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Dynare is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef EQUATION_TAGS_HH
#define EQUATION_TAGS_HH
#include <map>
#include <optional>
#include <set>
#include <string>
using namespace std;
class EquationTags
{
private:
map<int, map<string, string>> eqn_tags;
public:
// Add multiple equation tags for the given equation
void
add(int eqn, map<string, string> tags)
{
if (eqn_tags.contains(eqn))
eqn_tags[eqn].insert(move_iterator {tags.begin()}, move_iterator {tags.end()});
else
eqn_tags[eqn] = move(tags);
}
//! Add a single equation tag for the given equation
void
add(int eqn, string key, string value)
{
eqn_tags[eqn][move(key)] = move(value);
}
//! Clear all equation tag information
void
clear()
{
eqn_tags.clear();
}
//! Erase tags for given equations, using old_eqn_num_2_new as the mapping
//! to use for the remaining equation numbers
void erase(const set<int>& eqns, const map<int, int>& old_eqn_num_2_new);
//! Various functions to get info from equation tags
//! Get equation tags for a given equation
[[nodiscard]] map<string, string>
getTagsByEqn(int eqn) const
{
if (auto it = eqn_tags.find(eqn); it != eqn_tags.end())
return it->second;
return {};
}
//! Get equations that have the given key
[[nodiscard]] set<int> getEqnsByKey(const string& key) const;
//! Get equations that have the given key and value
[[nodiscard]] set<int> getEqnsByTag(const string& key, const string& value) const;
//! Get the first equation that has the given key and value
[[nodiscard]] optional<int> getEqnByTag(const string& key, const string& value) const;
// Get equations that have all the given keys and values (seen as a conjunction)
[[nodiscard]] set<int> getEqnsByTags(const map<string, string>& tags_selected) const;
//! Get the tag value given the equation number and key
[[nodiscard]] optional<string>
getTagValueByEqnAndKey(int eqn, const string& key) const
{
if (auto it = eqn_tags.find(eqn); it != eqn_tags.end())
if (auto it2 = it->second.find(key); it2 != it->second.end())
return it2->second;
return nullopt;
}
//! Get the equations marked dynamic
[[nodiscard]] set<int>
getDynamicEqns() const
{
return getEqnsByTag("dynamic", "");
}
//! Returns true if equation tag with key and value exists
[[nodiscard]] bool
exists(const string& key, const string& value) const
{
return getEqnByTag(key, value).has_value();
}
//! Returns true if equation tag with key exists for a given equation
[[nodiscard]] bool
exists(int eqn, const string& key) const
{
auto it = eqn_tags.find(eqn);
return it != eqn_tags.end() && it->second.contains(key);
}
//! Various functions to write equation tags
void writeCheckSumInfo(ostream& output) const;
void writeOutput(ostream& output) const;
void writeLatexOutput(ostream& output, int eqn) const;
void writeJsonAST(ostream& output, int eq) const;
};
#endif
Source diff could not be displayed: it is too large. Options to address this: view the blob.
/* /*
* Copyright (C) 2007-2017 Dynare Team * Copyright © 2007-2025 Dynare Team
* *
* This file is part of Dynare. * This file is part of Dynare.
* *
...@@ -14,112 +14,222 @@ ...@@ -14,112 +14,222 @@
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Dynare. If not, see <http://www.gnu.org/licenses/>. * along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/ */
#ifndef _EXPR_NODE_HH #ifndef EXPR_NODE_HH
#define _EXPR_NODE_HH #define EXPR_NODE_HH
#include <set> #include <concepts>
#include <functional>
#include <map> #include <map>
#include <vector> #include <optional>
#include <ostream> #include <ostream>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
using namespace std; using namespace std;
#include "SymbolTable.hh" #include "Bytecode.hh"
#include "CodeInterpreter.hh" #include "CommonEnums.hh"
#include "ExternalFunctionsTable.hh" #include "ExternalFunctionsTable.hh"
class DataTree; class DataTree;
class NumConstNode;
class VariableNode; class VariableNode;
class UnaryOpNode;
class BinaryOpNode; class BinaryOpNode;
class PacExpectationNode;
typedef class ExprNode *expr_t; using expr_t = class ExprNode*;
struct ExprNodeLess; struct ExprNodeLess;
//! Type for set of temporary terms //! Type for set of temporary terms
/*! They are ordered by index number thanks to ExprNodeLess */ /*! The ExprNodeLess ordering is important for the temporary terms algorithm,
typedef set<expr_t, ExprNodeLess> temporary_terms_t; see the definition of ExprNodeLess */
using temporary_terms_t = set<expr_t, ExprNodeLess>;
//! set of temporary terms used in a block /*! Keeps track of array indices of temporary_terms for writing */
typedef set<int> temporary_terms_inuse_t; using temporary_terms_idxs_t = unordered_map<expr_t, int>;
typedef map<int, int> map_idx_t;
//! Type for evaluation contexts //! Type for evaluation contexts
/*! The key is a symbol id. Lags are assumed to be null */ /*! The key is a symbol id. Lags are assumed to be null */
typedef map<int, double> eval_context_t; using eval_context_t = map<int, double>;
//! Type for tracking first/second derivative functions that have already been written as temporary terms //! Type for tracking first/second derivative functions that have already been written as temporary
typedef map<pair<int, vector<expr_t> >, int> deriv_node_temp_terms_t; //! terms
using deriv_node_temp_terms_t = map<pair<int, vector<expr_t>>, int>;
//! Type for the substitution map used for creating aux. vars for diff and unary_ops
/*! Let ≅ be the equivalence relationship such that two expressions e₁ and e₂
are equivalent iff e₁ can be obtained from e₂ by shifting all leads/lags by
the same number of periods (e.g. x₋₁+y₂≅x₁+y₄).
For each equivalence class, we select a representative element, which is
the class member which has no lead and a variable appearing at current
period (in the previous example, it would be x₋₃+y). (Obviously, if there
is no variable in the expression, then there is only one element in the
class, and that one is the representative)
Each member of an equivalence class is represented by an integer,
corresponding to its distance to the representative element (e.g. x₋₁+y₂
has index 2 and x₁+y₄ has index 4). The representative element has index 0
by definition.
The keys in the std::map are the representative elements of the various
equivalence classes. The values are themselves std::map that describe the
equivalence class: they associate indices of class members to the
expressions with which they should be substituted. */
using lag_equivalence_table_t = map<expr_t, map<int, expr_t>>;
//! Possible types of output when writing ExprNode(s) (not used for bytecode)
enum class ExprNodeOutputType
{
matlabStaticModel, //!< Matlab code, static model, legacy representation
matlabDynamicModel, //!< Matlab code, dynamic model, legacy representation
matlabSparseStaticModel, //!< Matlab code, static model, sparse representation
matlabSparseDynamicModel, //!< Matlab code, dynamic model, sparse representation
CDynamicModel, //!< C code, dynamic model, legacy representation
CStaticModel, //!< C code, static model, legacy representation
CSparseDynamicModel, //!< C code, dynamic model, sparse representation
CSparseStaticModel, //!< C code, static model, sparse representation
juliaStaticModel, //!< Julia code, static model, legacy representation
juliaDynamicModel, //!< Julia code, dynamic model, legacy representation
juliaSparseStaticModel, //!< Julia code, static model, sparse representation
juliaSparseDynamicModel, //!< Julia code, dynamic model, sparse representation
matlabOutsideModel, //!< Matlab code, outside model block (for example in initval)
latexStaticModel, //!< LaTeX code, static model
latexDynamicModel, //!< LaTeX code, dynamic model
latexDynamicSteadyStateOperator, //!< LaTeX code, dynamic model, inside a steady state operator
matlabDynamicSteadyStateOperator, //!< Matlab code, dynamic model, inside a steady state operator
CDynamicSteadyStateOperator, //!< C code, dynamic model, inside a steady state operator
juliaDynamicSteadyStateOperator, //!< Julia code, dynamic model, inside a steady state operator
steadyStateFile, //!< Matlab code, in the generated steady state file
juliaSteadyStateFile, //!< Julia code, in the generated steady state file
matlabDseries, //!< Matlab code for dseries
juliaTimeDataFrame, //!< Julia code for TimeDataFrame objects
epilogueFile, //!< Matlab code, in the generated epilogue file
occbinDifferenceFile //!< MATLAB, in the generated occbin_difference file
};
//! Possible types of output when writing ExprNode(s) // Possible types of output when writing ExprNode(s) in bytecode
enum ExprNodeOutputType enum class ExprNodeBytecodeOutputType
{ {
oMatlabStaticModel, //!< Matlab code, static model dynamicModel,
oMatlabDynamicModel, //!< Matlab code, dynamic model staticModel,
oMatlabStaticModelSparse, //!< Matlab code, static block decomposed model dynamicSteadyStateOperator, // Inside a steady_state operator
oMatlabDynamicModelSparse, //!< Matlab code, dynamic block decomposed model dynamicAssignmentLHS, // Assignment of a dynamic variable on the LHS of a (recursive) equation
oCDynamicModel, //!< C code, dynamic model staticAssignmentLHS // Assignment of a static variable on the LHS of a (recursive) equation
oCDynamic2Model, //!< C code, dynamic model, alternative numbering of endogenous variables
oCStaticModel, //!< C code, static model
oJuliaStaticModel, //!< Julia code, static model
oJuliaDynamicModel, //!< Julia code, dynamic model
oMatlabOutsideModel, //!< Matlab code, outside model block (for example in initval)
oLatexStaticModel, //!< LaTeX code, static model
oLatexDynamicModel, //!< LaTeX code, dynamic model
oLatexDynamicSteadyStateOperator, //!< LaTeX code, dynamic model, inside a steady state operator
oMatlabDynamicSteadyStateOperator, //!< Matlab code, dynamic model, inside a steady state operator
oMatlabDynamicSparseSteadyStateOperator, //!< Matlab code, dynamic block decomposed model, inside a steady state operator
oCDynamicSteadyStateOperator, //!< C code, dynamic model, inside a steady state operator
oJuliaDynamicSteadyStateOperator, //!< Julia code, dynamic model, inside a steady state operator
oSteadyStateFile, //!< Matlab code, in the generated steady state file
oCSteadyStateFile, //!< C code, in the generated steady state file
oJuliaSteadyStateFile //!< Julia code, in the generated steady state file
}; };
#define IS_MATLAB(output_type) ((output_type) == oMatlabStaticModel \ constexpr bool
|| (output_type) == oMatlabDynamicModel \ isMatlabOutput(ExprNodeOutputType output_type)
|| (output_type) == oMatlabOutsideModel \ {
|| (output_type) == oMatlabStaticModelSparse \ return output_type == ExprNodeOutputType::matlabStaticModel
|| (output_type) == oMatlabDynamicModelSparse \ || output_type == ExprNodeOutputType::matlabDynamicModel
|| (output_type) == oMatlabDynamicSteadyStateOperator \ || output_type == ExprNodeOutputType::matlabSparseStaticModel
|| (output_type) == oMatlabDynamicSparseSteadyStateOperator \ || output_type == ExprNodeOutputType::matlabSparseDynamicModel
|| (output_type) == oSteadyStateFile) || output_type == ExprNodeOutputType::matlabOutsideModel
|| output_type == ExprNodeOutputType::matlabDynamicSteadyStateOperator
#define IS_JULIA(output_type) ((output_type) == oJuliaStaticModel \ || output_type == ExprNodeOutputType::steadyStateFile
|| (output_type) == oJuliaDynamicModel \ || output_type == ExprNodeOutputType::matlabDseries
|| (output_type) == oJuliaDynamicSteadyStateOperator \ || output_type == ExprNodeOutputType::epilogueFile
|| (output_type) == oJuliaSteadyStateFile) || output_type == ExprNodeOutputType::occbinDifferenceFile;
}
#define IS_C(output_type) ((output_type) == oCDynamicModel \
|| (output_type) == oCDynamic2Model \ constexpr bool
|| (output_type) == oCStaticModel \ isJuliaOutput(ExprNodeOutputType output_type)
|| (output_type) == oCDynamicSteadyStateOperator \ {
|| (output_type) == oCSteadyStateFile) return output_type == ExprNodeOutputType::juliaStaticModel
|| output_type == ExprNodeOutputType::juliaDynamicModel
#define IS_LATEX(output_type) ((output_type) == oLatexStaticModel \ || output_type == ExprNodeOutputType::juliaSparseStaticModel
|| (output_type) == oLatexDynamicModel \ || output_type == ExprNodeOutputType::juliaSparseDynamicModel
|| (output_type) == oLatexDynamicSteadyStateOperator) || output_type == ExprNodeOutputType::juliaDynamicSteadyStateOperator
|| output_type == ExprNodeOutputType::juliaSteadyStateFile
|| output_type == ExprNodeOutputType::juliaTimeDataFrame;
}
constexpr bool
isCOutput(ExprNodeOutputType output_type)
{
return output_type == ExprNodeOutputType::CDynamicModel
|| output_type == ExprNodeOutputType::CStaticModel
|| output_type == ExprNodeOutputType::CSparseDynamicModel
|| output_type == ExprNodeOutputType::CSparseStaticModel
|| output_type == ExprNodeOutputType::CDynamicSteadyStateOperator;
}
constexpr bool
isLatexOutput(ExprNodeOutputType output_type)
{
return output_type == ExprNodeOutputType::latexStaticModel
|| output_type == ExprNodeOutputType::latexDynamicModel
|| output_type == ExprNodeOutputType::latexDynamicSteadyStateOperator;
}
constexpr bool
isSteadyStateOperatorOutput(ExprNodeOutputType output_type)
{
return output_type == ExprNodeOutputType::latexDynamicSteadyStateOperator
|| output_type == ExprNodeOutputType::matlabDynamicSteadyStateOperator
|| output_type == ExprNodeOutputType::CDynamicSteadyStateOperator
|| output_type == ExprNodeOutputType::juliaDynamicSteadyStateOperator;
}
constexpr bool
isSparseModelOutput(ExprNodeOutputType output_type)
{
return output_type == ExprNodeOutputType::matlabSparseStaticModel
|| output_type == ExprNodeOutputType::matlabSparseDynamicModel
|| output_type == ExprNodeOutputType::juliaSparseStaticModel
|| output_type == ExprNodeOutputType::juliaSparseDynamicModel
|| output_type == ExprNodeOutputType::CSparseDynamicModel
|| output_type == ExprNodeOutputType::CSparseStaticModel;
}
constexpr bool
isAssignmentLHSBytecodeOutput(ExprNodeBytecodeOutputType output_type)
{
return output_type == ExprNodeBytecodeOutputType::staticAssignmentLHS
|| output_type == ExprNodeBytecodeOutputType::dynamicAssignmentLHS;
}
/* Equal to 1 for Matlab langage or Julia, or to 0 for C language. Not defined for LaTeX. /* Equal to 1 for Matlab langage or Julia, or to 0 for C language. Not defined for LaTeX.
In Matlab and Julia, array indexes begin at 1, while they begin at 0 in C */ In Matlab and Julia, array indexes begin at 1, while they begin at 0 in C */
#define ARRAY_SUBSCRIPT_OFFSET(output_type) ((int) (IS_MATLAB(output_type) || IS_JULIA(output_type))) constexpr int
ARRAY_SUBSCRIPT_OFFSET(ExprNodeOutputType output_type)
{
return static_cast<int>(isMatlabOutput(output_type) || isJuliaOutput(output_type));
}
// Left and right array subscript delimiters: '(' and ')' for Matlab, '[' and ']' for C // Left and right array subscript delimiters: '(' and ')' for Matlab, '[' and ']' for C
#define LEFT_ARRAY_SUBSCRIPT(output_type) (IS_MATLAB(output_type) ? '(' : '[') constexpr char
#define RIGHT_ARRAY_SUBSCRIPT(output_type) (IS_MATLAB(output_type) ? ')' : ']') LEFT_ARRAY_SUBSCRIPT(ExprNodeOutputType output_type)
{
return isMatlabOutput(output_type) ? '(' : '[';
}
constexpr char
RIGHT_ARRAY_SUBSCRIPT(ExprNodeOutputType output_type)
{
return isMatlabOutput(output_type) ? ')' : ']';
}
// Left and right parentheses // Left and right parentheses
#define LEFT_PAR(output_type) (IS_LATEX(output_type) ? "\\left(" : "(") inline string
#define RIGHT_PAR(output_type) (IS_LATEX(output_type) ? "\\right)" : ")") LEFT_PAR(ExprNodeOutputType output_type)
{
return isLatexOutput(output_type) ? "\\left(" : "(";
}
// Computing cost above which a node can be declared a temporary term inline string
#define MIN_COST_MATLAB (40*90) RIGHT_PAR(ExprNodeOutputType output_type)
#define MIN_COST_C (40*4) {
#define MIN_COST(is_matlab) ((is_matlab) ? MIN_COST_MATLAB : MIN_COST_C) return isLatexOutput(output_type) ? "\\right)" : ")";
}
//! Base class for expression nodes //! Base class for expression nodes
class ExprNode class ExprNode
...@@ -135,20 +245,31 @@ enum ExprNodeOutputType ...@@ -135,20 +245,31 @@ enum ExprNodeOutputType
friend class BinaryOpNode; friend class BinaryOpNode;
friend class TrinaryOpNode; friend class TrinaryOpNode;
friend class AbstractExternalFunctionNode; friend class AbstractExternalFunctionNode;
friend class VarExpectationNode;
friend class PacExpectationNode;
private: private:
//! Computes derivative w.r. to a derivation ID (but doesn't store it in derivatives map) //! Computes derivative w.r. to a derivation ID (but doesn't store it in derivatives map)
/*! You shoud use getDerivative() to get the benefit of symbolic a priori and of caching */ /*! You shoud use getDerivative() to get the benefit of symbolic a priori and of caching */
virtual expr_t computeDerivative(int deriv_id) = 0; virtual expr_t computeDerivative(int deriv_id) = 0;
/* Internal helper for getChainRuleDerivative(), that does the computation
but assumes that the caching of this is handled elsewhere */
virtual expr_t
computeChainRuleDerivative(int deriv_id, const map<int, BinaryOpNode*>& recursive_variables,
unordered_map<expr_t, set<int>>& non_null_chain_rule_derivatives,
unordered_map<expr_t, map<int, expr_t>>& cache)
= 0;
protected: protected:
//! Reference to the enclosing DataTree //! Reference to the enclosing DataTree
DataTree& datatree; DataTree& datatree;
//! Index number //! Index number
int idx; const int idx;
//! Is the data member non_null_derivatives initialized ? //! Is the data member non_null_derivatives initialized ?
bool preparedForDerivation; bool preparedForDerivation {false};
//! Set of derivation IDs with respect to which the derivative is potentially non-null //! Set of derivation IDs with respect to which the derivative is potentially non-null
set<int> non_null_derivatives; set<int> non_null_derivatives;
...@@ -156,62 +277,177 @@ enum ExprNodeOutputType ...@@ -156,62 +277,177 @@ enum ExprNodeOutputType
//! Used for caching of first order derivatives (when non-null) //! Used for caching of first order derivatives (when non-null)
map<int, expr_t> derivatives; map<int, expr_t> derivatives;
constexpr static int min_cost_matlab {40 * 90};
constexpr static int min_cost_c {40 * 4};
constexpr static int
min_cost(bool is_matlab)
{
return is_matlab ? min_cost_matlab : min_cost_c;
}
//! Initializes data member non_null_derivatives
virtual void prepareForDerivation() = 0;
/* Computes the derivatives which are potentially non-null, using symbolic a
priori, similarly to prepareForDerivation(), but in a chain rule
derivation context. See getChainRuleDerivation() for the meaning of
“recursive_variables”. Note that all non-endogenous variables are
automatically considered to have a zero derivative (since they’re never
used in a chain rule context) */
virtual void prepareForChainRuleDerivation(
const map<int, BinaryOpNode*>& recursive_variables,
unordered_map<expr_t, set<int>>& non_null_chain_rule_derivatives) const
= 0;
//! Cost of computing current node //! Cost of computing current node
/*! Nodes included in temporary_terms are considered having a null cost */ /*! Nodes included in temporary_terms are considered having a null cost */
virtual int cost(int cost, bool is_matlab) const; [[nodiscard]] virtual int cost(int cost, bool is_matlab) const;
virtual int cost(const temporary_terms_t &temporary_terms, bool is_matlab) const; [[nodiscard]] virtual int
virtual int cost(const map<NodeTreeReference, temporary_terms_t> &temp_terms_map, bool is_matlab) const; cost(const vector<vector<unordered_set<expr_t>>>& blocks_temporary_terms, bool is_matlab) const;
[[nodiscard]] virtual int cost(const map<pair<int, int>, unordered_set<expr_t>>& temp_terms_map,
bool is_matlab) const;
//! For creating equation cross references //! For creating equation cross references
struct EquationInfo struct EquationInfo
{ {
set<pair<int, int> > param; set<pair<int, int>> param, endo, exo, exo_det;
set<pair<int, int> > endo;
set<pair<int, int> > exo;
set<pair<int, int> > exo_det;
}; };
//! If this node is a temporary term, writes its temporary term representation
/*! Returns true if node is a temporary term and has therefore been
written to output*/
bool checkIfTemporaryTermThenWrite(ostream& output, ExprNodeOutputType output_type,
const temporary_terms_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs) const;
// Same as above, for the bytecode case
bool
checkIfTemporaryTermThenWriteBytecode(Bytecode::Writer& code_file,
ExprNodeBytecodeOutputType output_type,
const temporary_terms_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs) const;
// Internal helper for matchVariableTimesConstantTimesParam()
virtual void matchVTCTPHelper(optional<int>& var_id, int& lag, optional<int>& param_id,
double& constant, bool at_denominator) const;
/* Computes the representative element and the index under the
lag-equivalence relationship. See the comment above
lag_equivalence_table_t for an explanation of these concepts. */
[[nodiscard]] pair<expr_t, int> getLagEquivalenceClass() const;
/* Computes the set of all sub-expressions that contain the variable
(symb_id, lag).
Note that if a diff operator is encountered:
- diff(expr) will be added to the output set if either expr or expr(-1)
contains the variable;
- the method will be called recursively on expr-expr(-1) */
virtual void computeSubExprContainingVariable(int symb_id, int lag,
set<expr_t>& contain_var) const
= 0;
public: public:
ExprNode(DataTree &datatree_arg); ExprNode(DataTree& datatree_arg, int idx_arg);
virtual virtual ~ExprNode() = default;
~ExprNode();
//! Initializes data member non_null_derivatives ExprNode(const ExprNode&) = delete;
virtual void prepareForDerivation() = 0; ExprNode& operator=(const ExprNode&) = delete;
//! Returns derivative w.r. to derivation ID //! Returns derivative w.r. to derivation ID
/*! Uses a symbolic a priori to pre-detect null derivatives, and caches the result for other derivatives (to avoid computing it several times) /*! Uses a symbolic a priori to pre-detect null derivatives, and caches the result for other
For an equal node, returns the derivative of lhs minus rhs */ derivatives (to avoid computing it several times) For an equal node, returns the derivative of
lhs minus rhs */
expr_t getDerivative(int deriv_id); expr_t getDerivative(int deriv_id);
//! Computes derivatives by applying the chain rule for some variables /* Computes derivatives by applying the chain rule for some variables.
/*! — “recursive_variables” contains the derivation ID for which chain rules
\param deriv_id The derivation ID with respect to which we are derivating must be applied. Keys are derivation IDs, values are equations of the
\param recursive_variables Contains the derivation ID for which chain rules must be applied. Keys are derivation IDs, values are equations of the form x=f(y) where x is the key variable and x doesn't appear in y form x=f(y) where x is the key variable and x doesn't appear in y
*/ — “non_null_chain_rule_derivatives” is used to store the indices of
virtual expr_t getChainRuleDerivative(int deriv_id, const map<int, expr_t> &recursive_variables) = 0; variables that are potentially non-null (using symbolic a priori),
similarly to ExprNode::non_null_derivatives.
— “cache” is used to store already-computed derivatives (in a map
<expression, deriv_id> → derivative)
NB: always returns zero when “deriv_id” corresponds to a non-endogenous
variable (since such variables are never used in a chain rule context).
NB 2: “non_null_chain_rule_derivatives” and “cache” are specific to a given
value of “recursive_variables”, and thus should not be reused accross
calls that use different values of “recursive_variables”.
NB 3: the use of std::unordered_map instead of std::map for caching
purposes improves performance on very very large models (tens of thousands
of equations) */
expr_t getChainRuleDerivative(int deriv_id, const map<int, BinaryOpNode*>& recursive_variables,
unordered_map<expr_t, set<int>>& non_null_chain_rule_derivatives,
unordered_map<expr_t, map<int, expr_t>>& cache);
//! Returns precedence of node //! Returns precedence of node
/*! Equals 100 for constants, variables, unary ops, and temporary terms */ /*! Equals 100 for constants, variables, unary ops, and temporary terms */
virtual int precedence(ExprNodeOutputType output_t, const temporary_terms_t &temporary_terms) const; [[nodiscard]] virtual int precedence(ExprNodeOutputType output_t,
const temporary_terms_t& temporary_terms) const;
//! Fills temporary_terms set, using reference counts //! Compute temporary terms in this expression
/*! A node will be marked as a temporary term if it is referenced at least two times (i.e. has at least two parents), and has a computing cost (multiplied by reference count) greater to datatree.min_cost */ /*!
virtual void computeTemporaryTerms(map<expr_t, pair<int, NodeTreeReference> > &reference_count, \param[in] derivOrder the derivation order (first w.r.t. endo/exo,
map<NodeTreeReference, temporary_terms_t> &temp_terms_map, second w.r.t. params)
bool is_matlab, NodeTreeReference tr) const; \param[out] temp_terms_map the computed temporary terms, associated
with their derivation order
\param[out] reference_count a temporary structure, used to count
references to each node (integer in outer pair is the
reference count, the inner pair is the derivation order)
\param[in] is_matlab whether we are in a MATLAB context, since that
affects the cost of each operator
A node will be marked as a temporary term if it is referenced at least
two times (i.e. has at least two parents), and has a computing cost
(multiplied by reference count) greater to datatree.min_cost
NB: the use of std::unordered_map instead of std::map for caching
purposes improves performance on very large models (⩾5000 equations)
*/
virtual void computeTemporaryTerms(
const pair<int, int>& derivOrder, map<pair<int, int>, unordered_set<expr_t>>& temp_terms_map,
unordered_map<expr_t, pair<int, pair<int, int>>>& reference_count, bool is_matlab) const;
//! Writes output of node, using a Txxx notation for nodes in temporary_terms, and specifiying the set of already written external functions //! Compute temporary terms in this expression for block decomposed model
/*!
\param[in] blk the block number
\param[in] eq the equation number (within the block)
\param[out] blocks_temporary_terms the computed temporary terms, per block
and per equation in the block
\param[out] reference_count a temporary structure, used to count
references to each node (first integer is the
reference count, second integer is the number of the block in which the
expression first appears, third integer is the equation number within the block)
Same rules as computeTemporaryTerms() for computing cost.
NB: the use of std::unordered_{set,map} instead of std::{set,map} for caching
and output improves performance on very large models (⩾5000 equations)
*/
virtual void
computeBlockTemporaryTerms(int blk, int eq,
vector<vector<unordered_set<expr_t>>>& blocks_temporary_terms,
unordered_map<expr_t, tuple<int, int, int>>& reference_count) const;
//! Writes output of node, using a Txxx notation for nodes in temporary_terms, and specifiying the
//! set of already written external functions
/*! /*!
\param[in] output the output stream \param[in] output the output stream
\param[in] output_type the type of output (MATLAB, C, LaTeX...) \param[in] output_type the type of output (MATLAB, C, LaTeX...)
\param[in] temporary_terms the nodes that are marked as temporary terms \param[in] temporary_terms the nodes that are marked as temporary terms
\param[in,out] tef_terms the set of already written external function nodes \param[in] a map from temporary_terms to integers indexes (in the
MATLAB, C or Julia vector of temporary terms); can be empty
when writing MATLAB with block decomposition)
\param[in] tef_terms the set of already written external function nodes
*/ */
virtual void writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, deriv_node_temp_terms_t &tef_terms) const = 0; virtual void writeOutput(ostream& output, ExprNodeOutputType output_type,
const temporary_terms_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs,
const deriv_node_temp_terms_t& tef_terms) const
= 0;
//! returns true if the expr node contains an external function //! returns true if the expr node contains an external function
virtual bool containsExternalFunction() const = 0; [[nodiscard]] virtual bool containsExternalFunction() const = 0;
//! Writes output of node (with no temporary terms and with "outside model" output type) //! Writes output of node (with no temporary terms and with "outside model" output type)
void writeOutput(ostream& output) const; void writeOutput(ostream& output) const;
...@@ -220,16 +456,29 @@ enum ExprNodeOutputType ...@@ -220,16 +456,29 @@ enum ExprNodeOutputType
void writeOutput(ostream& output, ExprNodeOutputType output_type) const; void writeOutput(ostream& output, ExprNodeOutputType output_type) const;
//! Writes output of node, using a Txxx notation for nodes in temporary_terms //! Writes output of node, using a Txxx notation for nodes in temporary_terms
void writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms) const; void writeOutput(ostream& output, ExprNodeOutputType output_type,
const temporary_terms_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs) const;
//! Writes output of node in JSON syntax //! Writes output of node in JSON syntax
virtual void writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, deriv_node_temp_terms_t &tef_terms, const bool isdynamic = true) const = 0; virtual void writeJsonOutput(ostream& output, const temporary_terms_t& temporary_terms,
const deriv_node_temp_terms_t& tef_terms,
bool isdynamic = true) const
= 0;
// Returns a string representation of the expression, used by the GDB pretty printer
[[nodiscard]] string toString() const;
//! Writes the Abstract Syntax Tree in JSON
virtual void writeJsonAST(ostream& output) const = 0;
virtual int precedenceJson(const temporary_terms_t &temporary_terms) const; [[nodiscard]] virtual int precedenceJson(const temporary_terms_t& temporary_terms) const;
//! Writes the output for an external function, ensuring that the external function is called as few times as possible using temporary terms //! Writes the output for an external function, ensuring that the external function is called as
//! few times as possible using temporary terms
virtual void writeExternalFunctionOutput(ostream& output, ExprNodeOutputType output_type, virtual void writeExternalFunctionOutput(ostream& output, ExprNodeOutputType output_type,
const temporary_terms_t& temporary_terms, const temporary_terms_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs,
deriv_node_temp_terms_t& tef_terms) const; deriv_node_temp_terms_t& tef_terms) const;
//! Write the JSON output of an external function in a string vector //! Write the JSON output of an external function in a string vector
...@@ -237,14 +486,15 @@ enum ExprNodeOutputType ...@@ -237,14 +486,15 @@ enum ExprNodeOutputType
virtual void writeJsonExternalFunctionOutput(vector<string>& efout, virtual void writeJsonExternalFunctionOutput(vector<string>& efout,
const temporary_terms_t& temporary_terms, const temporary_terms_t& temporary_terms,
deriv_node_temp_terms_t& tef_terms, deriv_node_temp_terms_t& tef_terms,
const bool isdynamic = true) const; bool isdynamic = true) const;
virtual void compileExternalFunctionOutput(ostream &CompileCode, unsigned int &instruction_number, virtual void writeBytecodeExternalFunctionOutput(
bool lhs_rhs, const temporary_terms_t &temporary_terms, Bytecode::Writer& code_file, ExprNodeBytecodeOutputType output_type,
const map_idx_t &map_idx, bool dynamic, bool steady_dynamic, const temporary_terms_t& temporary_terms, const temporary_terms_idxs_t& temporary_terms_idxs,
deriv_node_temp_terms_t& tef_terms) const; deriv_node_temp_terms_t& tef_terms) const;
//! Computes the set of all variables of a given symbol type in the expression (with information on lags) //! Computes the set of all variables of a given symbol type in the expression (with information
//! on lags)
/*! /*!
Variables are stored as integer pairs of the form (symb_id, lag). Variables are stored as integer pairs of the form (symb_id, lag).
They are added to the set given in argument. They are added to the set given in argument.
...@@ -253,7 +503,14 @@ enum ExprNodeOutputType ...@@ -253,7 +503,14 @@ enum ExprNodeOutputType
*/ */
virtual void collectDynamicVariables(SymbolType type_arg, set<pair<int, int>>& result) const = 0; virtual void collectDynamicVariables(SymbolType type_arg, set<pair<int, int>>& result) const = 0;
//! Computes the set of all variables of a given symbol type in the expression (without information on lags) //! Find the maximum lag in a VAR: handles case where LHS is diff
[[nodiscard]] virtual int VarMaxLag(const set<expr_t>& lhs_lag_equiv) const = 0;
//! Finds LHS variable in a VAR equation
virtual void collectVARLHSVariable(set<expr_t>& result) const = 0;
//! Computes the set of all variables of a given symbol type in the expression (without
//! information on lags)
/*! /*!
Variables are stored as symb_id. Variables are stored as symb_id.
They are added to the set given in argument. They are added to the set given in argument.
...@@ -268,179 +525,232 @@ enum ExprNodeOutputType ...@@ -268,179 +525,232 @@ enum ExprNodeOutputType
They are added to the set given in argument. They are added to the set given in argument.
Note that model local variables are substituted by their expression in the computation. Note that model local variables are substituted by their expression in the computation.
*/ */
virtual void collectEndogenous(set<pair<int, int> > &result) const; void collectEndogenous(set<pair<int, int>>& result) const;
//! Computes the set of exogenous variables in the expression
/*!
Exogenous are stored as integer pairs of the form (type_specific_id, lag).
They are added to the set given in argument.
Note that model local variables are substituted by their expression in the computation.
*/
virtual void collectExogenous(set<pair<int, int> > &result) const;
virtual void collectTemporary_terms(const temporary_terms_t &temporary_terms, temporary_terms_inuse_t &temporary_terms_inuse, int Curr_Block) const = 0;
virtual void computeTemporaryTerms(map<expr_t, int> &reference_count,
temporary_terms_t &temporary_terms,
map<expr_t, pair<int, int> > &first_occurence,
int Curr_block,
vector< vector<temporary_terms_t> > &v_temporary_terms,
int equation) const;
class EvalException class EvalException
{ {
}; };
class EvalExternalFunctionException : public EvalException class EvalExternalFunctionException : public EvalException
{ {
}; };
virtual double eval(const eval_context_t &eval_context) const throw (EvalException, EvalExternalFunctionException) = 0; [[nodiscard]] virtual double eval(const eval_context_t& eval_context) const noexcept(false) = 0;
virtual void compile(ostream &CompileCode, unsigned int &instruction_number, bool lhs_rhs, const temporary_terms_t &temporary_terms, const map_idx_t &map_idx, bool dynamic, bool steady_dynamic, deriv_node_temp_terms_t &tef_terms) const = 0;
void compile(ostream &CompileCode, unsigned int &instruction_number, bool lhs_rhs, const temporary_terms_t &temporary_terms, const map_idx_t &map_idx, bool dynamic, bool steady_dynamic) const; // Write output to bytecode file
virtual void writeBytecodeOutput(Bytecode::Writer& code_file,
ExprNodeBytecodeOutputType output_type,
const temporary_terms_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs,
const deriv_node_temp_terms_t& tef_terms) const
= 0;
//! Creates a static version of this node //! Creates a static version of this node
/*! /*!
This method duplicates the current node by creating a similar node from which all leads/lags have been stripped, This method duplicates the current node by creating a similar node from which all leads/lags
adds the result in the static_datatree argument (and not in the original datatree), and returns it. have been stripped, adds the result in the static_datatree argument (and not in the original
datatree), and returns it.
*/ */
virtual expr_t toStatic(DataTree& static_datatree) const = 0; virtual expr_t toStatic(DataTree& static_datatree) const = 0;
/*! /*!
Compute cross references for equations Compute cross references for equations
*/ */
// virtual void computeXrefs(set<int> &param, set<int> &endo, set<int> &exo, set<int> &exo_det) const = 0; // virtual void computeXrefs(set<int> &param, set<int> &endo, set<int> &exo, set<int> &exo_det)
// const = 0;
virtual void computeXrefs(EquationInfo& ei) const = 0; virtual void computeXrefs(EquationInfo& ei) const = 0;
//! Try to normalize an equation linear in its endogenous variable
virtual pair<int, expr_t> normalizeEquation(int symb_id_endo, vector<pair<int, pair<expr_t, expr_t> > > &List_of_Op_RHS) const = 0;
//! Returns the maximum lead of endogenous in this expression //! Helper for normalization of equations
/*! Normalize the equation this = rhs.
Must be called on a node containing the desired LHS variable.
Returns an equal node of the form: LHS variable = new RHS.
Must be given the set of all subexpressions that contain the desired LHS variable.
Throws a NormallizationFailed() exception if normalization is not possible. */
virtual BinaryOpNode* normalizeEquationHelper(const set<expr_t>& contain_var, expr_t rhs) const
= 0;
class NormalizationFailed
{
};
//! Returns the maximum lead of endogenous in this expression (not incl. heterogeneous endo)
/*! Always returns a non-negative value */ /*! Always returns a non-negative value */
virtual int maxEndoLead() const = 0; [[nodiscard]] virtual int maxEndoLead() const = 0;
//! Returns the maximum lead of exogenous in this expression //! Returns the maximum lead of exogenous in this expression (not incl. heterogeneous exo)
/*! Always returns a non-negative value */ /*! Always returns a non-negative value */
virtual int maxExoLead() const = 0; [[nodiscard]] virtual int maxExoLead() const = 0;
//! Returns the maximum lag of endogenous in this expression //! Returns the maximum lag of endogenous in this expression (not incl. heterogeneous endo)
/*! Always returns a non-negative value */ /*! Always returns a non-negative value */
virtual int maxEndoLag() const = 0; [[nodiscard]] virtual int maxEndoLag() const = 0;
//! Returns the maximum lag of exogenous in this expression //! Returns the maximum lag of exogenous in this expression (not incl. heterogeneous exo)
/*! Always returns a non-negative value */ /*! Always returns a non-negative value */
virtual int maxExoLag() const = 0; [[nodiscard]] virtual int maxExoLag() const = 0;
/* Returns the maximum lead of endo/exo/exodet in this expression (including heterogeneous
endo/exo). A negative value means that the expression contains only lagged variables. A value
of numeric_limits<int>::min() means that there is no variable. */
[[nodiscard]] virtual int maxLead() const = 0;
/* Returns the maximum lag of endo/exo/exodet in this expression (including heterogeneous
endo/exo). A negative value means that the expression contains only leaded variables. A value
of numeric_limits<int>::min() means that there is no variable. */
[[nodiscard]] virtual int maxLag() const = 0;
//! Returns the relative period of the most forward term in this expression /* Returns the maximum lag of endo/exo/exodet (including heterogeneous endo/exo), as if diffs were
/*! A negative value means that the expression contains only lagged variables */ expanded. This function behaves as maxLag(), except that it treats diff() differently. For
virtual int maxLead() const = 0; e.g., on diff(diff(x(-1))), maxLag() returns 1 while maxLagWithDiffsExpanded() returns 3. */
[[nodiscard]] virtual int maxLagWithDiffsExpanded() const = 0;
//! Returns a new expression where all the leads/lags have been shifted backwards by the same amount [[nodiscard]] virtual expr_t undiff() const = 0;
//! Returns a new expression where all the leads/lags have been shifted backwards by the same
//! amount
/*! /*!
Only acts on endogenous, exogenous, exogenous det Only acts on endogenous, exogenous, exogenous det
\param[in] n The number of lags by which to shift \param[in] n The number of lags by which to shift
\return The same expression except that leads/lags have been shifted backwards \return The same expression except that leads/lags have been shifted backwards
*/ */
virtual expr_t decreaseLeadsLags(int n) const = 0; [[nodiscard]] virtual expr_t decreaseLeadsLags(int n) const = 0;
//! Type for the substitution map used in the process of creating auxiliary vars
using subst_table_t = map<const ExprNode*, const VariableNode*>;
//! Type for the substitution map used in the process of creating auxiliary vars for leads >= 2 //! Type for the substitution map used in the process of substituting adl expressions
typedef map<const ExprNode *, const VariableNode *> subst_table_t; using subst_table_adl_t = map<const ExprNode*, const expr_t>;
//! Creates auxiliary endo lead variables corresponding to this expression //! Creates auxiliary endo lead variables corresponding to this expression
/*! /*!
If maximum endogenous lead >= 3, this method will also create intermediary auxiliary var, and will add the equations of the form aux1 = aux2(+1) to the substitution table. If maximum endogenous lead >= 3, this method will also create intermediary auxiliary var, and
\pre This expression is assumed to have maximum endogenous lead >= 2 will add the equations of the form aux1 = aux2(+1) to the substitution table. \pre This
\param[in,out] subst_table The table to which new auxiliary variables and their correspondance will be added expression is assumed to have maximum endogenous lead >= 2 \param[in,out] subst_table The table
\param[out] neweqs Equations to be added to the model to match the creation of auxiliary variables. to which new auxiliary variables and their correspondance will be added \param[out] neweqs
\return The new variable node corresponding to the current expression Equations to be added to the model to match the creation of auxiliary variables. \return The new
variable node corresponding to the current expression
*/ */
VariableNode *createEndoLeadAuxiliaryVarForMyself(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const; VariableNode* createEndoLeadAuxiliaryVarForMyself(subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs) const;
//! Creates auxiliary exo lead variables corresponding to this expression //! Creates auxiliary exo lead variables corresponding to this expression
/*! /*!
If maximum exogenous lead >= 2, this method will also create intermediary auxiliary var, and will add the equations of the form aux1 = aux2(+1) to the substitution table. If maximum exogenous lead >= 2, this method will also create intermediary auxiliary var, and
\pre This expression is assumed to have maximum exogenous lead >= 1 will add the equations of the form aux1 = aux2(+1) to the substitution table. \pre This
\param[in,out] subst_table The table to which new auxiliary variables and their correspondance will be added expression is assumed to have maximum exogenous lead >= 1 \param[in,out] subst_table The table
\param[out] neweqs Equations to be added to the model to match the creation of auxiliary variables. to which new auxiliary variables and their correspondance will be added \param[out] neweqs
\return The new variable node corresponding to the current expression Equations to be added to the model to match the creation of auxiliary variables. \return The new
variable node corresponding to the current expression
*/ */
VariableNode *createExoLeadAuxiliaryVarForMyself(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const; VariableNode* createExoLeadAuxiliaryVarForMyself(subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs) const;
//! Constructs a new expression where sub-expressions with max endo lead >= 2 have been replaced by auxiliary variables //! Constructs a new expression where sub-expressions with max endo lead >= 2 have been replaced
//! by auxiliary variables
/*! /*!
\param[in,out] subst_table Map used to store expressions that have already be substituted and their corresponding variable, in order to avoid creating two auxiliary variables for the same sub-expr. \param[in,out] subst_table Map used to store expressions that have already be substituted and
\param[out] neweqs Equations to be added to the model to match the creation of auxiliary variables. their corresponding variable, in order to avoid creating two auxiliary variables for the same
sub-expr. \param[out] neweqs Equations to be added to the model to match the creation of
auxiliary variables.
If the method detects a sub-expr which needs to be substituted, two cases are possible: If the method detects a sub-expr which needs to be substituted, two cases are possible:
- if this expr is in the table, then it will use the corresponding variable and return the substituted expression - if this expr is in the table, then it will use the corresponding variable and return the
- if this expr is not in the table, then it will create an auxiliary endogenous variable, add the substitution in the table and return the substituted expression substituted expression
- if this expr is not in the table, then it will create an auxiliary endogenous variable, add
the substitution in the table and return the substituted expression
\return A new equivalent expression where sub-expressions with max endo lead >= 2 have been replaced by auxiliary variables \return A new equivalent expression where sub-expressions with max endo lead >= 2 have been
replaced by auxiliary variables
*/ */
virtual expr_t substituteEndoLeadGreaterThanTwo(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool deterministic_model) const = 0; virtual expr_t substituteEndoLeadGreaterThanTwo(subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs,
bool deterministic_model) const
= 0;
//! Constructs a new expression where endo variables with max endo lag >= 2 have been replaced by auxiliary variables //! Constructs a new expression where endo variables with max endo lag >= 2 have been replaced by
//! auxiliary variables
/*! /*!
\param[in,out] subst_table Map used to store expressions that have already be substituted and their corresponding variable, in order to avoid creating two auxiliary variables for the same sub-expr. \param[in,out] subst_table Map used to store expressions that have already be substituted and
\param[out] neweqs Equations to be added to the model to match the creation of auxiliary variables. their corresponding variable, in order to avoid creating two auxiliary variables for the same
sub-expr. \param[out] neweqs Equations to be added to the model to match the creation of
auxiliary variables.
*/ */
virtual expr_t substituteEndoLagGreaterThanTwo(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const = 0; virtual expr_t substituteEndoLagGreaterThanTwo(subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs) const
= 0;
//! Constructs a new expression where exogenous variables with a lead have been replaced by auxiliary variables //! Constructs a new expression where exogenous variables with a lead have been replaced by
//! auxiliary variables
/*! /*!
\param[in,out] subst_table Map used to store expressions that have already be substituted and their corresponding variable, in order to avoid creating two auxiliary variables for the same sub-expr. \param[in,out] subst_table Map used to store expressions that have already be substituted and
\param[out] neweqs Equations to be added to the model to match the creation of auxiliary variables. their corresponding variable, in order to avoid creating two auxiliary variables for the same
sub-expr. \param[out] neweqs Equations to be added to the model to match the creation of
auxiliary variables.
*/ */
virtual expr_t substituteExoLead(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool deterministic_model) const = 0; virtual expr_t substituteExoLead(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
//! Constructs a new expression where exogenous variables with a lag have been replaced by auxiliary variables bool deterministic_model) const
= 0;
//! Constructs a new expression where exogenous variables with a lag have been replaced by
//! auxiliary variables
/*! /*!
\param[in,out] subst_table Map used to store expressions that have already be substituted and their corresponding variable, in order to avoid creating two auxiliary variables for the same sub-expr. \param[in,out] subst_table Map used to store expressions that have already be substituted and
\param[out] neweqs Equations to be added to the model to match the creation of auxiliary variables. their corresponding variable, in order to avoid creating two auxiliary variables for the same
sub-expr. \param[out] neweqs Equations to be added to the model to match the creation of
auxiliary variables.
*/ */
virtual expr_t substituteExoLag(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const = 0; virtual expr_t substituteExoLag(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs) const
= 0;
//! Constructs a new expression where the expectation operator has been replaced by auxiliary variables //! Constructs a new expression where the expectation operator has been replaced by auxiliary
//! variables
/*! /*!
\param[in,out] subst_table Map used to store expressions that have already be substituted and their corresponding variable, in order to avoid creating two auxiliary variables for the same sub-expr. \param[in,out] subst_table Map used to store expressions that have already be substituted and
\param[out] neweqs Equations to be added to the model to match the creation of auxiliary variables. their corresponding variable, in order to avoid creating two auxiliary variables for the same
\param[in] partial_information_model Are we substituting in a partial information model? sub-expr. \param[out] neweqs Equations to be added to the model to match the creation of
auxiliary variables. \param[in] partial_information_model Are we substituting in a partial
information model?
*/ */
virtual expr_t substituteExpectation(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool partial_information_model) const = 0; virtual expr_t substituteExpectation(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
bool partial_information_model) const
= 0;
virtual expr_t decreaseLeadsLagsPredeterminedVariables() const = 0; [[nodiscard]] virtual expr_t decreaseLeadsLagsPredeterminedVariables() const = 0;
//! Constructs a new expression where forward variables (supposed to be at most in t+1) have been replaced by themselves at t, plus a new aux var representing their (time) differentiate //! Constructs a new expression where forward variables (supposed to be at most in t+1) have been
//! replaced by themselves at t, plus a new aux var representing their (time) differentiate
/*! /*!
\param[in] subset variables to which to limit the transformation; transform \param[in] subset variables to which to limit the transformation; transform
all fwrd vars if empty all fwrd vars if empty
\param[in,out] subst_table Map used to store mapping between a given \param[in,out] subst_table Map used to store mapping between a given
forward variable and the aux var that contains its differentiate forward variable and the aux var that contains its differentiate
\param[out] neweqs Equations to be added to the model to match the creation of auxiliary variables. \param[out] neweqs Equations to be added to the model to match the creation of auxiliary
variables.
*/ */
virtual expr_t differentiateForwardVars(const vector<string> &subset, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const = 0; virtual expr_t differentiateForwardVars(const vector<string>& subset, subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs) const
= 0;
//! Return true if the nodeID is a numerical constant equal to value and false otherwise //! Return true if the nodeID is a numerical constant equal to value and false otherwise
/*! /*!
\param[in] value of the numerical constante \param[in] value of the numerical constante
\param[out] the boolean equal to true if NodeId is a constant equal to value \param[out] the boolean equal to true if NodeId is a constant equal to value
*/ */
virtual bool isNumConstNodeEqualTo(double value) const = 0; [[nodiscard]] virtual bool isNumConstNodeEqualTo(double value) const = 0;
//! Returns true if the expression contains one or several endogenous variable //! Returns the maximum number of nested diffs in the expression
virtual bool containsEndogenous(void) const = 0; [[nodiscard]] virtual int countDiffs() const = 0;
//! Returns true if the expression contains one or several exogenous variable //! Return true if the nodeID is a variable withe a type equal to type_arg, a specific variable id
virtual bool containsExogenous() const = 0; //! aqual to varfiable_id and a lag equal to lag_arg and false otherwise
//! Return true if the nodeID is a variable withe a type equal to type_arg, a specific variable id aqual to varfiable_id and a lag equal to lag_arg and false otherwise
/*! /*!
\param[in] the type (type_arg), specifique variable id (variable_id and the lag (lag_arg) \param[in] the type (type_arg), specifique variable id (variable_id and the lag (lag_arg)
\param[out] the boolean equal to true if NodeId is the variable \param[out] the boolean equal to true if NodeId is the variable
*/ */
virtual bool isVariableNodeEqualTo(SymbolType type_arg, int variable_id, int lag_arg) const = 0; [[nodiscard]] virtual bool isVariableNodeEqualTo(SymbolType type_arg, int variable_id,
int lag_arg) const
= 0;
//! Replaces the Trend var with datatree.One //! Replaces the Trend var with datatree.One
virtual expr_t replaceTrendVar() const = 0; [[nodiscard]] virtual expr_t replaceTrendVar() const = 0;
//! Constructs a new expression where the variable indicated by symb_id has been detrended //! Constructs a new expression where the variable indicated by symb_id has been detrended
/*! /*!
...@@ -451,20 +761,203 @@ enum ExprNodeOutputType ...@@ -451,20 +761,203 @@ enum ExprNodeOutputType
*/ */
virtual expr_t detrend(int symb_id, bool log_trend, expr_t trend) const = 0; virtual expr_t detrend(int symb_id, bool log_trend, expr_t trend) const = 0;
//! Substitute adl operator
[[nodiscard]] virtual expr_t substituteAdl() const = 0;
//! Substitute out model-local variables
[[nodiscard]] virtual expr_t substituteModelLocalVariables() const = 0;
//! Substitute VarExpectation nodes
[[nodiscard]] virtual expr_t
substituteVarExpectation(const map<string, expr_t>& subst_table) const
= 0;
//! Mark diff nodes to be substituted
/*! The various nodes that are equivalent up to a shift of leads/lags are
grouped together in the “nodes” table. See the comment above
lag_equivalence_table_t for more details. */
virtual void findDiffNodes(lag_equivalence_table_t& nodes) const = 0;
//! Substitute diff operator
virtual expr_t substituteDiff(const lag_equivalence_table_t& nodes, subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs) const
= 0;
//! Mark unary ops nodes to be substituted
/*! The various nodes that are equivalent up to a shift of leads/lags are
grouped together in the “nodes” table. See the comment above
lag_equivalence_table_t for more details. */
virtual void findUnaryOpNodesForAuxVarCreation(lag_equivalence_table_t& nodes) const = 0;
//! Substitute unary ops nodes
virtual expr_t substituteUnaryOpNodes(const lag_equivalence_table_t& nodes,
subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs) const
= 0;
//! Substitute pac_expectation operator
virtual expr_t substitutePacExpectation(const string& name, expr_t subexpr) = 0;
//! Substitute pac_target_nonstationary operator
virtual expr_t substitutePacTargetNonstationary(const string& name, expr_t subexpr) = 0;
[[nodiscard]] virtual optional<int> findTargetVariable(int lhs_symb_id) const = 0;
//! Add ExprNodes to the provided datatree //! Add ExprNodes to the provided datatree
virtual expr_t cloneDynamic(DataTree &dynamic_datatree) const = 0; virtual expr_t clone(DataTree& alt_datatree) const = 0;
//! Move a trend variable with lag/lead to time t by dividing/multiplying by its growth factor //! Move a trend variable with lag/lead to time t by dividing/multiplying by its growth factor
virtual expr_t removeTrendLeadLag(map<int, expr_t> trend_symbols_map) const = 0; [[nodiscard]] virtual expr_t removeTrendLeadLag(const map<int, expr_t>& trend_symbols_map) const
= 0;
//! Returns true if the expression is in static form (no lead, no lag, no expectation, no STEADY_STATE)
virtual bool isInStaticForm() const = 0; //! Returns true if the expression is in static form (no lead, no lag, no expectation, no
//! STEADY_STATE)
[[nodiscard]] virtual bool isInStaticForm() const = 0;
//! Matches a linear combination of variables (endo or exo), where scalars can be
//! constant*parameter
/*! Returns a list of (variable_id, lag, param_id, constant)
corresponding to the terms in the expression. When there is no
parameter in a term, param_id is nullopt.
Can throw a MatchFailureException.
*/
[[nodiscard]] vector<tuple<int, int, optional<int>, double>>
matchLinearCombinationOfVariables() const;
/* Matches a linear combination of variables (endo or exo), where scalars can
be constant*parameter. In addition, there may be one or more scalar terms
(i.e. without a variable).
Returns a list of (variable_id, lag, param_id, constant)
corresponding to the terms in the expression. When there is no
parameter in a term, param_id is nullopt. When the term is scalar (i.e.
no variable), then variable_id is nullopt.
Can throw a MatchFailureException.
*/
[[nodiscard]] vector<tuple<optional<int>, int, optional<int>, double>>
matchLinearCombinationOfVariablesPlusConstant() const;
/* Matches a parameter, times a linear combination of variables (endo or
exo), where scalars can be constant*parameters.
The first output argument is the symbol ID of the parameter.
The second output argument is the linear combination, in the same format
as the output of matchLinearCombinationOfVariables(). */
[[nodiscard]] pair<int, vector<tuple<int, int, optional<int>, double>>>
matchParamTimesLinearCombinationOfVariables() const;
/* Matches a linear combination of endogenous, where scalars can be any
constant expression (i.e. containing no endogenous, no exogenous and no
exogenous deterministic). The linear combination can contain constant
terms (intercept).
Returns a pair composed of:
– the terms of the form endogenous*scalar, as a list of (endo_id, constant expr);
– the sum of all constant (intercept) terms */
[[nodiscard]] pair<vector<pair<int, expr_t>>, expr_t>
matchLinearCombinationOfEndogenousWithConstant() const;
/* Matches an expression of the form parameter*(var1-endo2).
endo2 must correspond to symb_id. var1 must be an endogenous or an
exogenous; it must be of the form X(-1) or log(X(-1)) or log(X)(-1) (unary ops aux var),
where X itself is *not* an aux var.
Returns the symbol IDs of the parameter and of var1.
Throws a MatchFailureException otherwise */
[[nodiscard]] pair<int, int> matchParamTimesTargetMinusVariable(int symb_id) const;
//! Returns true if expression is of the form:
//! param * (endog op endog op ...) + param * (endog op endog op ...) + ...
[[nodiscard]] virtual bool isParamTimesEndogExpr() const = 0;
//! Fills the EC matrix structure
void fillErrorCorrectionRow(int eqn, const vector<int>& nontarget_lhs,
const vector<int>& target_lhs, map<tuple<int, int>, expr_t>& A0,
map<tuple<int, int>, expr_t>& A0star) const;
//! Replaces variables found in BinaryOpNode::findConstantEquations() with their constant values
virtual expr_t replaceVarsInEquation(map<VariableNode*, NumConstNode*>& table) const = 0;
//! Returns true if PacExpectationNode encountered
[[nodiscard]] virtual bool containsPacExpectation(const string& pac_model_name = "") const = 0;
//! Returns true if PacTargetNonstationaryNode encountered
[[nodiscard]] virtual bool containsPacTargetNonstationary(const string& pac_model_name = "") const
= 0;
//! Decompose an expression into its additive terms
/*! Returns a list of terms, with their sign (either 1 or -1, depending
on whether the terms appears with a plus or a minus).
The current_sign argument should normally be left to 1.
If current_sign == -1, then all signs are inverted */
virtual void decomposeAdditiveTerms(vector<pair<expr_t, int>>& terms, int current_sign = 1) const;
//! Decompose an expression into its multiplicative factors
/*! Returns a list of factors, with their exponents (either 1 or -1, depending
on whether the factors appear at the numerator or the denominator).
The current_exponent argument should normally be left to 1.
If current_exponent == -1, then all exponents are inverted */
virtual void decomposeMultiplicativeFactors(vector<pair<expr_t, int>>& factors,
int current_exponent = 1) const;
// Matches an expression of the form variable*constant*parameter
/* Returns a tuple (variable_id, lag, param_id, constant).
If `variable_obligatory` is true, then the expression must contain a variable.
If present, the variable must be an exogenous or an endogenous. If absent,
and `variable_obligatory` is false, then variable_id is nullopt.
The constant is optional (in which case 1 is returned); there can be
several multiplicative constants; constants can also appear at the
denominator (i.e. after a divide sign).
The parameter is optional (in which case param_id is nullopt).
If the expression is not of the expected form, throws a
MatchFailureException
*/
[[nodiscard]] tuple<optional<int>, int, optional<int>, double>
matchVariableTimesConstantTimesParam(bool variable_obligatory) const;
/* Matches an expression of the form endogenous*constant where constant is an
expression containing no endogenous, no exogenous and no exogenous deterministic.
Returns (endo_id, constant expr).
Note that it will also match a simple endogenous (in which case the
constant will of course be equal to one). */
[[nodiscard]] virtual pair<int, expr_t> matchEndogenousTimesConstant() const;
//! Exception thrown when matching fails
struct MatchFailureException
{
const string message;
};
//! Substitute auxiliary variables by their expression in static model /* Match an expression of the form ∏ x(l)ᵏ, where x are endogenous, as used
virtual expr_t substituteStaticAuxiliaryVariable() const = 0; in the match_moments block.
For each factor, adds an integer in the 3 vectors in argument (symb_id in
the first, lag in the second, exponent in the third).
Throws a MatchFailureException if not of the right form. */
virtual void matchMatchedMoment(vector<int>& symb_ids, vector<int>& lags,
vector<int>& powers) const;
/* Returns true if the expression contains no endogenous, no exogenous and no
exogenous deterministic */
[[nodiscard]] bool isConstant() const;
// Returns true if the expression contains an exogenous or an exogenous deterministic
[[nodiscard]] bool hasExogenous() const;
// Substitutes orig_symb_id(±l) with exp(aux_symb_id(±l)) (used for “var(log)”)
[[nodiscard]] virtual expr_t substituteLogTransform(int orig_symb_id, int aux_symb_id) const = 0;
/* Matches an expression that constitutes a complementarity condition.
If successful, returns a triplet (endo_symb_id, lower_bound, upper_bound).
Otherwise, throws a MatchFailureException. */
[[nodiscard]] virtual tuple<int, expr_t, expr_t>
matchComplementarityCondition(const optional<int>& heterogeneity_dimension = nullopt) const;
/* Replaces aggregation operators (e.g. SUM()) by new auxiliary variables.
Also declares those aggregation operators in the HeterogeneityTable, so as to
compute their index in the dedicated vector in argument of the dynamic/static files. */
[[nodiscard]] virtual expr_t substituteAggregationOperators(subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs) const
= 0;
}; };
//! Object used to compare two nodes (using their indexes) //! Object used to compare two nodes (using their indexes)
/*! Note that in this ordering, a subexpression is always less than the
expression from which it is extracted. This property is used extensively in
the temporary terms computations. */
struct ExprNodeLess struct ExprNodeLess
{ {
bool bool
...@@ -475,619 +968,1167 @@ struct ExprNodeLess ...@@ -475,619 +968,1167 @@ struct ExprNodeLess
}; };
//! Numerical constant node //! Numerical constant node
/*! The constant is necessarily non-negative (this is enforced at the NumericalConstants class level) */ /*! The constant is necessarily non-negative (this is enforced at the NumericalConstants class
* level) */
class NumConstNode : public ExprNode class NumConstNode : public ExprNode
{ {
private: public:
//! Id from numerical constants table //! Id from numerical constants table
const int id; const int id;
virtual expr_t computeDerivative(int deriv_id);
private:
expr_t computeDerivative(int deriv_id) override;
expr_t
computeChainRuleDerivative(int deriv_id, const map<int, BinaryOpNode*>& recursive_variables,
unordered_map<expr_t, set<int>>& non_null_chain_rule_derivatives,
unordered_map<expr_t, map<int, expr_t>>& cache) override;
protected:
void prepareForDerivation() override;
void prepareForChainRuleDerivation(
const map<int, BinaryOpNode*>& recursive_variables,
unordered_map<expr_t, set<int>>& non_null_chain_rule_derivatives) const override;
void matchVTCTPHelper(optional<int>& var_id, int& lag, optional<int>& param_id, double& constant,
bool at_denominator) const override;
void computeSubExprContainingVariable(int symb_id, int lag,
set<expr_t>& contain_var) const override;
public: public:
NumConstNode(DataTree &datatree_arg, int id_arg); NumConstNode(DataTree& datatree_arg, int idx_arg, int id_arg);
int void writeOutput(ostream& output, ExprNodeOutputType output_type,
get_id() const const temporary_terms_t& temporary_terms,
{ const temporary_terms_idxs_t& temporary_terms_idxs,
return id; const deriv_node_temp_terms_t& tef_terms) const override;
}; void writeJsonAST(ostream& output) const override;
virtual void prepareForDerivation(); void writeJsonOutput(ostream& output, const temporary_terms_t& temporary_terms,
virtual void writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, deriv_node_temp_terms_t &tef_terms) const; const deriv_node_temp_terms_t& tef_terms, bool isdynamic) const override;
virtual void writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, deriv_node_temp_terms_t &tef_terms, const bool isdynamic) const; [[nodiscard]] bool containsExternalFunction() const override;
virtual bool containsExternalFunction() const; void collectVARLHSVariable(set<expr_t>& result) const override;
virtual void collectDynamicVariables(SymbolType type_arg, set<pair<int, int> > &result) const; void collectDynamicVariables(SymbolType type_arg, set<pair<int, int>>& result) const override;
virtual void collectTemporary_terms(const temporary_terms_t &temporary_terms, temporary_terms_inuse_t &temporary_terms_inuse, int Curr_Block) const; [[nodiscard]] double eval(const eval_context_t& eval_context) const noexcept(false) override;
virtual double eval(const eval_context_t &eval_context) const throw (EvalException, EvalExternalFunctionException); void writeBytecodeOutput(Bytecode::Writer& code_file, ExprNodeBytecodeOutputType output_type,
virtual void compile(ostream &CompileCode, unsigned int &instruction_number, bool lhs_rhs, const temporary_terms_t &temporary_terms, const map_idx_t &map_idx, bool dynamic, bool steady_dynamic, deriv_node_temp_terms_t &tef_terms) const; const temporary_terms_t& temporary_terms,
virtual expr_t toStatic(DataTree &static_datatree) const; const temporary_terms_idxs_t& temporary_terms_idxs,
virtual void computeXrefs(EquationInfo &ei) const; const deriv_node_temp_terms_t& tef_terms) const override;
virtual pair<int, expr_t> normalizeEquation(int symb_id_endo, vector<pair<int, pair<expr_t, expr_t> > > &List_of_Op_RHS) const; expr_t toStatic(DataTree& static_datatree) const override;
virtual expr_t getChainRuleDerivative(int deriv_id, const map<int, expr_t> &recursive_variables); void computeXrefs(EquationInfo& ei) const override;
virtual int maxEndoLead() const; BinaryOpNode* normalizeEquationHelper(const set<expr_t>& contain_var, expr_t rhs) const override;
virtual int maxExoLead() const; [[nodiscard]] int maxEndoLead() const override;
virtual int maxEndoLag() const; [[nodiscard]] int maxExoLead() const override;
virtual int maxExoLag() const; [[nodiscard]] int maxEndoLag() const override;
virtual int maxLead() const; [[nodiscard]] int maxExoLag() const override;
virtual expr_t decreaseLeadsLags(int n) const; [[nodiscard]] int maxLead() const override;
virtual expr_t substituteEndoLeadGreaterThanTwo(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool deterministic_model) const; [[nodiscard]] int maxLag() const override;
virtual expr_t substituteEndoLagGreaterThanTwo(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const; [[nodiscard]] int maxLagWithDiffsExpanded() const override;
virtual expr_t substituteExoLead(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool deterministic_model) const; [[nodiscard]] int VarMaxLag(const set<expr_t>& lhs_lag_equiv) const override;
virtual expr_t substituteExoLag(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const; [[nodiscard]] expr_t undiff() const override;
virtual expr_t substituteExpectation(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool partial_information_model) const; [[nodiscard]] expr_t decreaseLeadsLags(int n) const override;
virtual expr_t decreaseLeadsLagsPredeterminedVariables() const; expr_t substituteEndoLeadGreaterThanTwo(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
virtual expr_t differentiateForwardVars(const vector<string> &subset, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const; bool deterministic_model) const override;
virtual bool isNumConstNodeEqualTo(double value) const; expr_t substituteEndoLagGreaterThanTwo(subst_table_t& subst_table,
virtual bool containsEndogenous(void) const; vector<BinaryOpNode*>& neweqs) const override;
virtual bool containsExogenous() const; expr_t substituteExoLead(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
virtual bool isVariableNodeEqualTo(SymbolType type_arg, int variable_id, int lag_arg) const; bool deterministic_model) const override;
virtual expr_t replaceTrendVar() const; expr_t substituteExoLag(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs) const override;
virtual expr_t detrend(int symb_id, bool log_trend, expr_t trend) const; expr_t substituteExpectation(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
virtual expr_t cloneDynamic(DataTree &dynamic_datatree) const; bool partial_information_model) const override;
virtual expr_t removeTrendLeadLag(map<int, expr_t> trend_symbols_map) const; [[nodiscard]] expr_t substituteAdl() const override;
virtual bool isInStaticForm() const; [[nodiscard]] expr_t substituteModelLocalVariables() const override;
virtual expr_t substituteStaticAuxiliaryVariable() const; [[nodiscard]] expr_t
substituteVarExpectation(const map<string, expr_t>& subst_table) const override;
void findDiffNodes(lag_equivalence_table_t& nodes) const override;
void findUnaryOpNodesForAuxVarCreation(lag_equivalence_table_t& nodes) const override;
[[nodiscard]] optional<int> findTargetVariable(int lhs_symb_id) const override;
expr_t substituteDiff(const lag_equivalence_table_t& nodes, subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs) const override;
expr_t substituteUnaryOpNodes(const lag_equivalence_table_t& nodes, subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs) const override;
expr_t substitutePacExpectation(const string& name, expr_t subexpr) override;
expr_t substitutePacTargetNonstationary(const string& name, expr_t subexpr) override;
[[nodiscard]] expr_t decreaseLeadsLagsPredeterminedVariables() const override;
expr_t differentiateForwardVars(const vector<string>& subset, subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs) const override;
[[nodiscard]] bool isNumConstNodeEqualTo(double value) const override;
[[nodiscard]] int countDiffs() const override;
[[nodiscard]] bool isVariableNodeEqualTo(SymbolType type_arg, int variable_id,
int lag_arg) const override;
[[nodiscard]] expr_t replaceTrendVar() const override;
expr_t detrend(int symb_id, bool log_trend, expr_t trend) const override;
expr_t clone(DataTree& alt_datatree) const override;
[[nodiscard]] expr_t removeTrendLeadLag(const map<int, expr_t>& trend_symbols_map) const override;
[[nodiscard]] bool isInStaticForm() const override;
expr_t replaceVarsInEquation(map<VariableNode*, NumConstNode*>& table) const override;
[[nodiscard]] bool containsPacExpectation(const string& pac_model_name = "") const override;
[[nodiscard]] bool containsPacTargetNonstationary(const string& pac_model_name
= "") const override;
[[nodiscard]] bool isParamTimesEndogExpr() const override;
[[nodiscard]] expr_t substituteLogTransform(int orig_symb_id, int aux_symb_id) const override;
[[nodiscard]] expr_t substituteAggregationOperators(subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs) const override;
}; };
//! Symbol or variable node //! Symbol or variable node
class VariableNode : public ExprNode class VariableNode : public ExprNode
{ {
friend class UnaryOpNode; friend class UnaryOpNode;
private:
public:
//! Id from the symbol table //! Id from the symbol table
const int symb_id; const int symb_id;
const SymbolType type;
//! A positive value is a lead, a negative is a lag //! A positive value is a lead, a negative is a lag
const int lag; const int lag;
virtual expr_t computeDerivative(int deriv_id);
private:
expr_t computeDerivative(int deriv_id) override;
expr_t
computeChainRuleDerivative(int deriv_id, const map<int, BinaryOpNode*>& recursive_variables,
unordered_map<expr_t, set<int>>& non_null_chain_rule_derivatives,
unordered_map<expr_t, map<int, expr_t>>& cache) override;
protected:
void prepareForDerivation() override;
void prepareForChainRuleDerivation(
const map<int, BinaryOpNode*>& recursive_variables,
unordered_map<expr_t, set<int>>& non_null_chain_rule_derivatives) const override;
void matchVTCTPHelper(optional<int>& var_id, int& lag, optional<int>& param_id, double& constant,
bool at_denominator) const override;
void computeSubExprContainingVariable(int symb_id, int lag,
set<expr_t>& contain_var) const override;
public: public:
VariableNode(DataTree &datatree_arg, int symb_id_arg, int lag_arg); VariableNode(DataTree& datatree_arg, int idx_arg, int symb_id_arg, int lag_arg);
virtual void prepareForDerivation(); [[nodiscard]] SymbolType get_type() const;
virtual void writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, deriv_node_temp_terms_t &tef_terms) const; [[nodiscard]] string getName() const;
virtual void writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, deriv_node_temp_terms_t &tef_terms, const bool isdynamic) const; [[nodiscard]] int getDerivID() const;
virtual bool containsExternalFunction() const; [[nodiscard]] int getTypeSpecificID() const;
virtual void collectDynamicVariables(SymbolType type_arg, set<pair<int, int> > &result) const; void writeOutput(ostream& output, ExprNodeOutputType output_type,
virtual void computeTemporaryTerms(map<expr_t, int > &reference_count, const temporary_terms_t& temporary_terms,
temporary_terms_t &temporary_terms, const temporary_terms_idxs_t& temporary_terms_idxs,
map<expr_t, pair<int, int> > &first_occurence, const deriv_node_temp_terms_t& tef_terms) const override;
int Curr_block, void writeJsonAST(ostream& output) const override;
vector< vector<temporary_terms_t> > &v_temporary_terms, void writeJsonOutput(ostream& output, const temporary_terms_t& temporary_terms,
int equation) const; const deriv_node_temp_terms_t& tef_terms, bool isdynamic) const override;
virtual void collectTemporary_terms(const temporary_terms_t &temporary_terms, temporary_terms_inuse_t &temporary_terms_inuse, int Curr_Block) const; [[nodiscard]] bool containsExternalFunction() const override;
virtual double eval(const eval_context_t &eval_context) const throw (EvalException, EvalExternalFunctionException); void collectVARLHSVariable(set<expr_t>& result) const override;
virtual void compile(ostream &CompileCode, unsigned int &instruction_number, bool lhs_rhs, const temporary_terms_t &temporary_terms, const map_idx_t &map_idx, bool dynamic, bool steady_dynamic, deriv_node_temp_terms_t &tef_terms) const; void collectDynamicVariables(SymbolType type_arg, set<pair<int, int>>& result) const override;
virtual expr_t toStatic(DataTree &static_datatree) const; [[nodiscard]] double eval(const eval_context_t& eval_context) const noexcept(false) override;
virtual void computeXrefs(EquationInfo &ei) const; void writeBytecodeOutput(Bytecode::Writer& code_file, ExprNodeBytecodeOutputType output_type,
SymbolType const temporary_terms_t& temporary_terms,
get_type() const const temporary_terms_idxs_t& temporary_terms_idxs,
{ const deriv_node_temp_terms_t& tef_terms) const override;
return type; expr_t toStatic(DataTree& static_datatree) const override;
}; void computeXrefs(EquationInfo& ei) const override;
int BinaryOpNode* normalizeEquationHelper(const set<expr_t>& contain_var, expr_t rhs) const override;
get_symb_id() const [[nodiscard]] int maxEndoLead() const override;
{ [[nodiscard]] int maxExoLead() const override;
return symb_id; [[nodiscard]] int maxEndoLag() const override;
}; [[nodiscard]] int maxExoLag() const override;
int [[nodiscard]] int maxLead() const override;
get_lag() const [[nodiscard]] int maxLag() const override;
{ [[nodiscard]] int maxLagWithDiffsExpanded() const override;
return lag; [[nodiscard]] int VarMaxLag(const set<expr_t>& lhs_lag_equiv) const override;
}; [[nodiscard]] expr_t undiff() const override;
virtual pair<int, expr_t> normalizeEquation(int symb_id_endo, vector<pair<int, pair<expr_t, expr_t> > > &List_of_Op_RHS) const; [[nodiscard]] expr_t decreaseLeadsLags(int n) const override;
virtual expr_t getChainRuleDerivative(int deriv_id, const map<int, expr_t> &recursive_variables); expr_t substituteEndoLeadGreaterThanTwo(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
virtual int maxEndoLead() const; bool deterministic_model) const override;
virtual int maxExoLead() const; expr_t substituteEndoLagGreaterThanTwo(subst_table_t& subst_table,
virtual int maxEndoLag() const; vector<BinaryOpNode*>& neweqs) const override;
virtual int maxExoLag() const; expr_t substituteExoLead(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
virtual int maxLead() const; bool deterministic_model) const override;
virtual expr_t decreaseLeadsLags(int n) const; expr_t substituteExoLag(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs) const override;
virtual expr_t substituteEndoLeadGreaterThanTwo(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool deterministic_model) const; expr_t substituteExpectation(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
virtual expr_t substituteEndoLagGreaterThanTwo(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const; bool partial_information_model) const override;
virtual expr_t substituteExoLead(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool deterministic_model) const; [[nodiscard]] expr_t substituteAdl() const override;
virtual expr_t substituteExoLag(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const; [[nodiscard]] expr_t substituteModelLocalVariables() const override;
virtual expr_t substituteExpectation(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool partial_information_model) const; [[nodiscard]] expr_t
virtual expr_t decreaseLeadsLagsPredeterminedVariables() const; substituteVarExpectation(const map<string, expr_t>& subst_table) const override;
virtual expr_t differentiateForwardVars(const vector<string> &subset, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const; void findDiffNodes(lag_equivalence_table_t& nodes) const override;
virtual bool isNumConstNodeEqualTo(double value) const; void findUnaryOpNodesForAuxVarCreation(lag_equivalence_table_t& nodes) const override;
virtual bool containsEndogenous(void) const; [[nodiscard]] optional<int> findTargetVariable(int lhs_symb_id) const override;
virtual bool containsExogenous() const; expr_t substituteDiff(const lag_equivalence_table_t& nodes, subst_table_t& subst_table,
virtual bool isVariableNodeEqualTo(SymbolType type_arg, int variable_id, int lag_arg) const; vector<BinaryOpNode*>& neweqs) const override;
virtual expr_t replaceTrendVar() const; expr_t substituteUnaryOpNodes(const lag_equivalence_table_t& nodes, subst_table_t& subst_table,
virtual expr_t detrend(int symb_id, bool log_trend, expr_t trend) const; vector<BinaryOpNode*>& neweqs) const override;
virtual expr_t cloneDynamic(DataTree &dynamic_datatree) const; expr_t substitutePacExpectation(const string& name, expr_t subexpr) override;
virtual expr_t removeTrendLeadLag(map<int, expr_t> trend_symbols_map) const; expr_t substitutePacTargetNonstationary(const string& name, expr_t subexpr) override;
virtual bool isInStaticForm() const; [[nodiscard]] expr_t decreaseLeadsLagsPredeterminedVariables() const override;
//! Substitute auxiliary variables by their expression in static model expr_t differentiateForwardVars(const vector<string>& subset, subst_table_t& subst_table,
virtual expr_t substituteStaticAuxiliaryVariable() const; vector<BinaryOpNode*>& neweqs) const override;
[[nodiscard]] bool isNumConstNodeEqualTo(double value) const override;
[[nodiscard]] int countDiffs() const override;
[[nodiscard]] bool isVariableNodeEqualTo(SymbolType type_arg, int variable_id,
int lag_arg) const override;
[[nodiscard]] expr_t replaceTrendVar() const override;
expr_t detrend(int symb_id, bool log_trend, expr_t trend) const override;
expr_t clone(DataTree& alt_datatree) const override;
[[nodiscard]] expr_t removeTrendLeadLag(const map<int, expr_t>& trend_symbols_map) const override;
[[nodiscard]] bool isInStaticForm() const override;
expr_t replaceVarsInEquation(map<VariableNode*, NumConstNode*>& table) const override;
[[nodiscard]] bool containsPacExpectation(const string& pac_model_name = "") const override;
[[nodiscard]] bool containsPacTargetNonstationary(const string& pac_model_name
= "") const override;
[[nodiscard]] bool isParamTimesEndogExpr() const override;
void matchMatchedMoment(vector<int>& symb_ids, vector<int>& lags,
vector<int>& powers) const override;
[[nodiscard]] pair<int, expr_t> matchEndogenousTimesConstant() const override;
[[nodiscard]] expr_t substituteLogTransform(int orig_symb_id, int aux_symb_id) const override;
[[nodiscard]] expr_t substituteAggregationOperators(subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs) const override;
}; };
//! Unary operator node //! Unary operator node
class UnaryOpNode : public ExprNode class UnaryOpNode : public ExprNode
{ {
private: protected:
void prepareForDerivation() override;
void prepareForChainRuleDerivation(
const map<int, BinaryOpNode*>& recursive_variables,
unordered_map<expr_t, set<int>>& non_null_chain_rule_derivatives) const override;
void matchVTCTPHelper(optional<int>& var_id, int& lag, optional<int>& param_id, double& constant,
bool at_denominator) const override;
void computeSubExprContainingVariable(int symb_id, int lag,
set<expr_t>& contain_var) const override;
// Returns the node obtained by applying a transformation recursively on the argument (in same
// datatree)
template<typename Callable, typename... Args>
requires invocable<Callable, expr_t, Args...>
expr_t
recurseTransform(Callable&& op, Args&&... args) const
{
expr_t substarg {invoke(forward<Callable>(op), arg, forward<Args>(args)...)};
return buildSimilarUnaryOpNode(substarg, datatree);
}
public:
const expr_t arg; const expr_t arg;
//! Stores the information set. Only used for expectation operator //! Stores the information set. Only used for expectation operator
const int expectation_information_set; const int expectation_information_set;
//! Only used for oSteadyStateParamDeriv and oSteadyStateParam2ndDeriv //! Only used for UnaryOpcode::steadyStateParamDeriv and UnaryOpcode::steadyStateParam2ndDeriv
const int param1_symb_id, param2_symb_id; const int param1_symb_id, param2_symb_id;
const UnaryOpcode op_code; const UnaryOpcode op_code;
virtual expr_t computeDerivative(int deriv_id); const string adl_param_name;
virtual int cost(int cost, bool is_matlab) const; const vector<int> adl_lags;
virtual int cost(const temporary_terms_t &temporary_terms, bool is_matlab) const;
virtual int cost(const map<NodeTreeReference, temporary_terms_t> &temp_terms_map, bool is_matlab) const; private:
expr_t computeDerivative(int deriv_id) override;
expr_t
computeChainRuleDerivative(int deriv_id, const map<int, BinaryOpNode*>& recursive_variables,
unordered_map<expr_t, set<int>>& non_null_chain_rule_derivatives,
unordered_map<expr_t, map<int, expr_t>>& cache) override;
[[nodiscard]] int cost(int cost, bool is_matlab) const override;
[[nodiscard]] int cost(const vector<vector<unordered_set<expr_t>>>& blocks_temporary_terms,
bool is_matlab) const override;
[[nodiscard]] int cost(const map<pair<int, int>, unordered_set<expr_t>>& temp_terms_map,
bool is_matlab) const override;
//! Returns the derivative of this node if darg is the derivative of the argument //! Returns the derivative of this node if darg is the derivative of the argument
expr_t composeDerivatives(expr_t darg, int deriv_id); expr_t composeDerivatives(expr_t darg, int deriv_id);
public: public:
UnaryOpNode(DataTree &datatree_arg, UnaryOpcode op_code_arg, const expr_t arg_arg, int expectation_information_set_arg, int param1_symb_id_arg, int param2_symb_id_arg); UnaryOpNode(DataTree& datatree_arg, int idx_arg, UnaryOpcode op_code_arg, const expr_t arg_arg,
virtual void prepareForDerivation(); int expectation_information_set_arg, int param1_symb_id_arg, int param2_symb_id_arg,
virtual void computeTemporaryTerms(map<expr_t, pair<int, NodeTreeReference> > &reference_count, string adl_param_name_arg, vector<int> adl_lags_arg);
map<NodeTreeReference, temporary_terms_t> &temp_terms_map, void computeTemporaryTerms(const pair<int, int>& derivOrder,
bool is_matlab, NodeTreeReference tr) const; map<pair<int, int>, unordered_set<expr_t>>& temp_terms_map,
virtual void writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, deriv_node_temp_terms_t &tef_terms) const; unordered_map<expr_t, pair<int, pair<int, int>>>& reference_count,
virtual void writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, deriv_node_temp_terms_t &tef_terms, const bool isdynamic) const; bool is_matlab) const override;
virtual bool containsExternalFunction() const; void computeBlockTemporaryTerms(
virtual void writeExternalFunctionOutput(ostream &output, ExprNodeOutputType output_type, int blk, int eq, vector<vector<unordered_set<expr_t>>>& blocks_temporary_terms,
unordered_map<expr_t, tuple<int, int, int>>& reference_count) const override;
void writeOutput(ostream& output, ExprNodeOutputType output_type,
const temporary_terms_t& temporary_terms, const temporary_terms_t& temporary_terms,
deriv_node_temp_terms_t &tef_terms) const; const temporary_terms_idxs_t& temporary_terms_idxs,
virtual void writeJsonExternalFunctionOutput(vector<string> &efout, const deriv_node_temp_terms_t& tef_terms) const override;
void writeJsonAST(ostream& output) const override;
void writeJsonOutput(ostream& output, const temporary_terms_t& temporary_terms,
const deriv_node_temp_terms_t& tef_terms, bool isdynamic) const override;
[[nodiscard]] bool containsExternalFunction() const override;
void writeExternalFunctionOutput(ostream& output, ExprNodeOutputType output_type,
const temporary_terms_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs,
deriv_node_temp_terms_t& tef_terms) const override;
void writeJsonExternalFunctionOutput(vector<string>& efout,
const temporary_terms_t& temporary_terms, const temporary_terms_t& temporary_terms,
deriv_node_temp_terms_t& tef_terms, deriv_node_temp_terms_t& tef_terms,
const bool isdynamic) const; bool isdynamic) const override;
virtual void compileExternalFunctionOutput(ostream &CompileCode, unsigned int &instruction_number, void writeBytecodeExternalFunctionOutput(Bytecode::Writer& code_file,
bool lhs_rhs, const temporary_terms_t &temporary_terms, ExprNodeBytecodeOutputType output_type,
const map_idx_t &map_idx, bool dynamic, bool steady_dynamic, const temporary_terms_t& temporary_terms,
deriv_node_temp_terms_t &tef_terms) const; const temporary_terms_idxs_t& temporary_terms_idxs,
virtual void computeTemporaryTerms(map<expr_t, int> &reference_count, deriv_node_temp_terms_t& tef_terms) const override;
temporary_terms_t &temporary_terms, void collectVARLHSVariable(set<expr_t>& result) const override;
map<expr_t, pair<int, int> > &first_occurence, void collectDynamicVariables(SymbolType type_arg, set<pair<int, int>>& result) const override;
int Curr_block, static double eval_opcode(UnaryOpcode op_code, double v) noexcept(false);
vector< vector<temporary_terms_t> > &v_temporary_terms, [[nodiscard]] double eval(const eval_context_t& eval_context) const noexcept(false) override;
int equation) const; void writeBytecodeOutput(Bytecode::Writer& code_file, ExprNodeBytecodeOutputType output_type,
virtual void collectDynamicVariables(SymbolType type_arg, set<pair<int, int> > &result) const; const temporary_terms_t& temporary_terms,
virtual void collectTemporary_terms(const temporary_terms_t &temporary_terms, temporary_terms_inuse_t &temporary_terms_inuse, int Curr_Block) const; const temporary_terms_idxs_t& temporary_terms_idxs,
static double eval_opcode(UnaryOpcode op_code, double v) throw (EvalException, EvalExternalFunctionException); const deriv_node_temp_terms_t& tef_terms) const override;
virtual double eval(const eval_context_t &eval_context) const throw (EvalException, EvalExternalFunctionException); expr_t toStatic(DataTree& static_datatree) const override;
virtual void compile(ostream &CompileCode, unsigned int &instruction_number, bool lhs_rhs, const temporary_terms_t &temporary_terms, const map_idx_t &map_idx, bool dynamic, bool steady_dynamic, deriv_node_temp_terms_t &tef_terms) const; void computeXrefs(EquationInfo& ei) const override;
//! Returns operand BinaryOpNode* normalizeEquationHelper(const set<expr_t>& contain_var, expr_t rhs) const override;
expr_t [[nodiscard]] int maxEndoLead() const override;
get_arg() const [[nodiscard]] int maxExoLead() const override;
{ [[nodiscard]] int maxEndoLag() const override;
return (arg); [[nodiscard]] int maxExoLag() const override;
}; [[nodiscard]] int maxLead() const override;
//! Returns op code [[nodiscard]] int maxLag() const override;
UnaryOpcode [[nodiscard]] int maxLagWithDiffsExpanded() const override;
get_op_code() const [[nodiscard]] int VarMaxLag(const set<expr_t>& lhs_lag_equiv) const override;
{ [[nodiscard]] expr_t undiff() const override;
return (op_code); [[nodiscard]] expr_t decreaseLeadsLags(int n) const override;
}; expr_t substituteEndoLeadGreaterThanTwo(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
virtual expr_t toStatic(DataTree &static_datatree) const; bool deterministic_model) const override;
virtual void computeXrefs(EquationInfo &ei) const; //! Creates another UnaryOpNode with the same opcode, but with a possibly different datatree and
virtual pair<int, expr_t> normalizeEquation(int symb_id_endo, vector<pair<int, pair<expr_t, expr_t> > > &List_of_Op_RHS) const; //! argument
virtual expr_t getChainRuleDerivative(int deriv_id, const map<int, expr_t> &recursive_variables);
virtual int maxEndoLead() const;
virtual int maxExoLead() const;
virtual int maxEndoLag() const;
virtual int maxExoLag() const;
virtual int maxLead() const;
virtual expr_t decreaseLeadsLags(int n) const;
virtual expr_t substituteEndoLeadGreaterThanTwo(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool deterministic_model) const;
//! Creates another UnaryOpNode with the same opcode, but with a possibly different datatree and argument
expr_t buildSimilarUnaryOpNode(expr_t alt_arg, DataTree& alt_datatree) const; expr_t buildSimilarUnaryOpNode(expr_t alt_arg, DataTree& alt_datatree) const;
virtual expr_t substituteEndoLagGreaterThanTwo(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const; expr_t substituteEndoLagGreaterThanTwo(subst_table_t& subst_table,
virtual expr_t substituteExoLead(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool deterministic_model) const; vector<BinaryOpNode*>& neweqs) const override;
virtual expr_t substituteExoLag(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const; expr_t substituteExoLead(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
virtual expr_t substituteExpectation(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool partial_information_model) const; bool deterministic_model) const override;
virtual expr_t decreaseLeadsLagsPredeterminedVariables() const; expr_t substituteExoLag(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs) const override;
virtual expr_t differentiateForwardVars(const vector<string> &subset, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const; expr_t substituteExpectation(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
virtual bool isNumConstNodeEqualTo(double value) const; bool partial_information_model) const override;
virtual bool containsEndogenous(void) const; [[nodiscard]] expr_t substituteAdl() const override;
virtual bool containsExogenous() const; [[nodiscard]] expr_t substituteModelLocalVariables() const override;
virtual bool isVariableNodeEqualTo(SymbolType type_arg, int variable_id, int lag_arg) const; [[nodiscard]] expr_t
virtual expr_t replaceTrendVar() const; substituteVarExpectation(const map<string, expr_t>& subst_table) const override;
virtual expr_t detrend(int symb_id, bool log_trend, expr_t trend) const; void findDiffNodes(lag_equivalence_table_t& nodes) const override;
virtual expr_t cloneDynamic(DataTree &dynamic_datatree) const; [[nodiscard]] bool createAuxVarForUnaryOpNode() const;
virtual expr_t removeTrendLeadLag(map<int, expr_t> trend_symbols_map) const; void findUnaryOpNodesForAuxVarCreation(lag_equivalence_table_t& nodes) const override;
virtual bool isInStaticForm() const; [[nodiscard]] optional<int> findTargetVariable(int lhs_symb_id) const override;
//! Substitute auxiliary variables by their expression in static model expr_t substituteDiff(const lag_equivalence_table_t& nodes, subst_table_t& subst_table,
virtual expr_t substituteStaticAuxiliaryVariable() const; vector<BinaryOpNode*>& neweqs) const override;
expr_t substituteUnaryOpNodes(const lag_equivalence_table_t& nodes, subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs) const override;
expr_t substitutePacExpectation(const string& name, expr_t subexpr) override;
expr_t substitutePacTargetNonstationary(const string& name, expr_t subexpr) override;
[[nodiscard]] expr_t decreaseLeadsLagsPredeterminedVariables() const override;
expr_t differentiateForwardVars(const vector<string>& subset, subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs) const override;
[[nodiscard]] bool isNumConstNodeEqualTo(double value) const override;
[[nodiscard]] int countDiffs() const override;
[[nodiscard]] bool isVariableNodeEqualTo(SymbolType type_arg, int variable_id,
int lag_arg) const override;
[[nodiscard]] expr_t replaceTrendVar() const override;
expr_t detrend(int symb_id, bool log_trend, expr_t trend) const override;
expr_t clone(DataTree& alt_datatree) const override;
[[nodiscard]] expr_t removeTrendLeadLag(const map<int, expr_t>& trend_symbols_map) const override;
[[nodiscard]] bool isInStaticForm() const override;
expr_t replaceVarsInEquation(map<VariableNode*, NumConstNode*>& table) const override;
[[nodiscard]] bool containsPacExpectation(const string& pac_model_name = "") const override;
[[nodiscard]] bool containsPacTargetNonstationary(const string& pac_model_name
= "") const override;
[[nodiscard]] bool isParamTimesEndogExpr() const override;
void decomposeAdditiveTerms(vector<pair<expr_t, int>>& terms, int current_sign) const override;
[[nodiscard]] expr_t substituteLogTransform(int orig_symb_id, int aux_symb_id) const override;
[[nodiscard]] expr_t substituteAggregationOperators(subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs) const override;
}; };
//! Binary operator node //! Binary operator node
class BinaryOpNode : public ExprNode class BinaryOpNode : public ExprNode
{ {
private: protected:
void prepareForDerivation() override;
void prepareForChainRuleDerivation(
const map<int, BinaryOpNode*>& recursive_variables,
unordered_map<expr_t, set<int>>& non_null_chain_rule_derivatives) const override;
void matchVTCTPHelper(optional<int>& var_id, int& lag, optional<int>& param_id, double& constant,
bool at_denominator) const override;
void computeSubExprContainingVariable(int symb_id, int lag,
set<expr_t>& contain_var) const override;
public:
const expr_t arg1, arg2; const expr_t arg1, arg2;
const BinaryOpcode op_code; const BinaryOpcode op_code;
virtual expr_t computeDerivative(int deriv_id); const int powerDerivOrder;
virtual int cost(int cost, bool is_matlab) const; const string adlparam;
virtual int cost(const temporary_terms_t &temporary_terms, bool is_matlab) const;
virtual int cost(const map<NodeTreeReference, temporary_terms_t> &temp_terms_map, bool is_matlab) const; private:
expr_t computeDerivative(int deriv_id) override;
expr_t
computeChainRuleDerivative(int deriv_id, const map<int, BinaryOpNode*>& recursive_variables,
unordered_map<expr_t, set<int>>& non_null_chain_rule_derivatives,
unordered_map<expr_t, map<int, expr_t>>& cache) override;
[[nodiscard]] int cost(int cost, bool is_matlab) const override;
[[nodiscard]] int cost(const vector<vector<unordered_set<expr_t>>>& blocks_temporary_terms,
bool is_matlab) const override;
[[nodiscard]] int cost(const map<pair<int, int>, unordered_set<expr_t>>& temp_terms_map,
bool is_matlab) const override;
//! Returns the derivative of this node if darg1 and darg2 are the derivatives of the arguments //! Returns the derivative of this node if darg1 and darg2 are the derivatives of the arguments
expr_t composeDerivatives(expr_t darg1, expr_t darg2); expr_t composeDerivatives(expr_t darg1, expr_t darg2);
const int powerDerivOrder; // Returns the node obtained by applying a transformation recursively on the arguments (in same
// datatree)
template<typename Callable, typename... Args>
requires invocable<Callable, expr_t, Args...>
expr_t
recurseTransform(Callable&& op, Args&&... args) const
{
expr_t substarg1 {invoke(forward<Callable>(op), arg1, forward<Args>(args)...)};
expr_t substarg2 {invoke(forward<Callable>(op), arg2, forward<Args>(args)...)};
return buildSimilarBinaryOpNode(substarg1, substarg2, datatree);
}
public: public:
BinaryOpNode(DataTree &datatree_arg, const expr_t arg1_arg, BinaryOpNode(DataTree& datatree_arg, int idx_arg, const expr_t arg1_arg, BinaryOpcode op_code_arg,
BinaryOpcode op_code_arg, const expr_t arg2_arg); const expr_t arg2_arg, int powerDerivOrder);
BinaryOpNode(DataTree &datatree_arg, const expr_t arg1_arg, [[nodiscard]] int precedenceJson(const temporary_terms_t& temporary_terms) const override;
BinaryOpcode op_code_arg, const expr_t arg2_arg, int powerDerivOrder); [[nodiscard]] int precedence(ExprNodeOutputType output_type,
virtual void prepareForDerivation(); const temporary_terms_t& temporary_terms) const override;
virtual int precedenceJson(const temporary_terms_t &temporary_terms) const; void computeTemporaryTerms(const pair<int, int>& derivOrder,
virtual int precedence(ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms) const; map<pair<int, int>, unordered_set<expr_t>>& temp_terms_map,
virtual void computeTemporaryTerms(map<expr_t, pair<int, NodeTreeReference> > &reference_count, unordered_map<expr_t, pair<int, pair<int, int>>>& reference_count,
map<NodeTreeReference, temporary_terms_t> &temp_terms_map, bool is_matlab) const override;
bool is_matlab, NodeTreeReference tr) const; void computeBlockTemporaryTerms(
virtual void writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, deriv_node_temp_terms_t &tef_terms) const; int blk, int eq, vector<vector<unordered_set<expr_t>>>& blocks_temporary_terms,
virtual void writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, deriv_node_temp_terms_t &tef_terms, const bool isdynamic) const; unordered_map<expr_t, tuple<int, int, int>>& reference_count) const override;
virtual bool containsExternalFunction() const; void writeOutput(ostream& output, ExprNodeOutputType output_type,
virtual void writeExternalFunctionOutput(ostream &output, ExprNodeOutputType output_type,
const temporary_terms_t& temporary_terms, const temporary_terms_t& temporary_terms,
deriv_node_temp_terms_t &tef_terms) const; const temporary_terms_idxs_t& temporary_terms_idxs,
virtual void writeJsonExternalFunctionOutput(vector<string> &efout, const deriv_node_temp_terms_t& tef_terms) const override;
void writeJsonAST(ostream& output) const override;
void writeJsonOutput(ostream& output, const temporary_terms_t& temporary_terms,
const deriv_node_temp_terms_t& tef_terms, bool isdynamic) const override;
[[nodiscard]] bool containsExternalFunction() const override;
void writeExternalFunctionOutput(ostream& output, ExprNodeOutputType output_type,
const temporary_terms_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs,
deriv_node_temp_terms_t& tef_terms) const override;
void writeJsonExternalFunctionOutput(vector<string>& efout,
const temporary_terms_t& temporary_terms, const temporary_terms_t& temporary_terms,
deriv_node_temp_terms_t& tef_terms, deriv_node_temp_terms_t& tef_terms,
const bool isdynamic) const; bool isdynamic) const override;
virtual void compileExternalFunctionOutput(ostream &CompileCode, unsigned int &instruction_number, void writeBytecodeExternalFunctionOutput(Bytecode::Writer& code_file,
bool lhs_rhs, const temporary_terms_t &temporary_terms, ExprNodeBytecodeOutputType output_type,
const map_idx_t &map_idx, bool dynamic, bool steady_dynamic, const temporary_terms_t& temporary_terms,
deriv_node_temp_terms_t &tef_terms) const; const temporary_terms_idxs_t& temporary_terms_idxs,
virtual void computeTemporaryTerms(map<expr_t, int> &reference_count, deriv_node_temp_terms_t& tef_terms) const override;
temporary_terms_t &temporary_terms, void collectVARLHSVariable(set<expr_t>& result) const override;
map<expr_t, pair<int, int> > &first_occurence, void collectDynamicVariables(SymbolType type_arg, set<pair<int, int>>& result) const override;
int Curr_block, static double eval_opcode(double v1, BinaryOpcode op_code, double v2,
vector< vector<temporary_terms_t> > &v_temporary_terms, int derivOrder) noexcept(false);
int equation) const; [[nodiscard]] double eval(const eval_context_t& eval_context) const noexcept(false) override;
virtual void collectDynamicVariables(SymbolType type_arg, set<pair<int, int> > &result) const; void writeBytecodeOutput(Bytecode::Writer& code_file, ExprNodeBytecodeOutputType output_type,
virtual void collectTemporary_terms(const temporary_terms_t &temporary_terms, temporary_terms_inuse_t &temporary_terms_inuse, int Curr_Block) const; const temporary_terms_t& temporary_terms,
static double eval_opcode(double v1, BinaryOpcode op_code, double v2, int derivOrder) throw (EvalException, EvalExternalFunctionException); const temporary_terms_idxs_t& temporary_terms_idxs,
virtual double eval(const eval_context_t &eval_context) const throw (EvalException, EvalExternalFunctionException); const deriv_node_temp_terms_t& tef_terms) const override;
virtual void compile(ostream &CompileCode, unsigned int &instruction_number, bool lhs_rhs, const temporary_terms_t &temporary_terms, const map_idx_t &map_idx, bool dynamic, bool steady_dynamic, deriv_node_temp_terms_t &tef_terms) const; expr_t Compute_RHS(expr_t arg1, expr_t arg2, int op, int op_type) const;
virtual expr_t Compute_RHS(expr_t arg1, expr_t arg2, int op, int op_type) const; expr_t toStatic(DataTree& static_datatree) const override;
//! Returns first operand void computeXrefs(EquationInfo& ei) const override;
expr_t BinaryOpNode* normalizeEquationHelper(const set<expr_t>& contain_var, expr_t rhs) const override;
get_arg1() const //! Try to normalize an equation with respect to a given dynamic variable.
{ /*! Should only be called on Equal nodes. The variable must appear in the equation. */
return (arg1); [[nodiscard]] BinaryOpNode* normalizeEquation(int symb_id, int lag) const;
}; [[nodiscard]] int maxEndoLead() const override;
//! Returns second operand [[nodiscard]] int maxExoLead() const override;
expr_t [[nodiscard]] int maxEndoLag() const override;
get_arg2() const [[nodiscard]] int maxExoLag() const override;
{ [[nodiscard]] int maxLead() const override;
return (arg2); [[nodiscard]] int maxLag() const override;
}; [[nodiscard]] int maxLagWithDiffsExpanded() const override;
//! Returns op code [[nodiscard]] int VarMaxLag(const set<expr_t>& lhs_lag_equiv) const override;
BinaryOpcode [[nodiscard]] expr_t undiff() const override;
get_op_code() const [[nodiscard]] expr_t decreaseLeadsLags(int n) const override;
{ expr_t substituteEndoLeadGreaterThanTwo(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
return (op_code); bool deterministic_model) const override;
}; //! Creates another BinaryOpNode with the same opcode, but with a possibly different datatree and
int //! arguments
get_power_deriv_order() const
{
return powerDerivOrder;
}
virtual expr_t toStatic(DataTree &static_datatree) const;
virtual void computeXrefs(EquationInfo &ei) const;
virtual pair<int, expr_t> normalizeEquation(int symb_id_endo, vector<pair<int, pair<expr_t, expr_t> > > &List_of_Op_RHS) const;
virtual expr_t getChainRuleDerivative(int deriv_id, const map<int, expr_t> &recursive_variables);
virtual int maxEndoLead() const;
virtual int maxExoLead() const;
virtual int maxEndoLag() const;
virtual int maxExoLag() const;
virtual int maxLead() const;
virtual expr_t decreaseLeadsLags(int n) const;
virtual expr_t substituteEndoLeadGreaterThanTwo(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool deterministic_model) const;
//! Creates another BinaryOpNode with the same opcode, but with a possibly different datatree and arguments
expr_t buildSimilarBinaryOpNode(expr_t alt_arg1, expr_t alt_arg2, DataTree& alt_datatree) const; expr_t buildSimilarBinaryOpNode(expr_t alt_arg1, expr_t alt_arg2, DataTree& alt_datatree) const;
virtual expr_t substituteEndoLagGreaterThanTwo(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const; expr_t substituteEndoLagGreaterThanTwo(subst_table_t& subst_table,
virtual expr_t substituteExoLead(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool deterministic_model) const; vector<BinaryOpNode*>& neweqs) const override;
virtual expr_t substituteExoLag(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const; expr_t substituteExoLead(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
virtual expr_t substituteExpectation(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool partial_information_model) const; bool deterministic_model) const override;
virtual expr_t decreaseLeadsLagsPredeterminedVariables() const; expr_t substituteExoLag(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs) const override;
virtual expr_t differentiateForwardVars(const vector<string> &subset, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const; expr_t substituteExpectation(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
virtual bool isNumConstNodeEqualTo(double value) const; bool partial_information_model) const override;
virtual bool containsEndogenous(void) const; [[nodiscard]] expr_t substituteAdl() const override;
virtual bool containsExogenous() const; [[nodiscard]] expr_t substituteModelLocalVariables() const override;
virtual bool isVariableNodeEqualTo(SymbolType type_arg, int variable_id, int lag_arg) const; [[nodiscard]] expr_t
virtual expr_t replaceTrendVar() const; substituteVarExpectation(const map<string, expr_t>& subst_table) const override;
virtual expr_t detrend(int symb_id, bool log_trend, expr_t trend) const; void findDiffNodes(lag_equivalence_table_t& nodes) const override;
virtual expr_t cloneDynamic(DataTree &dynamic_datatree) const; void findUnaryOpNodesForAuxVarCreation(lag_equivalence_table_t& nodes) const override;
virtual expr_t removeTrendLeadLag(map<int, expr_t> trend_symbols_map) const; [[nodiscard]] bool findTargetVariableHelper1(int lhs_symb_id, int rhs_symb_id) const;
//! Function to write out the oPowerNode in expr_t terms as opposed to writing out the function itself optional<int> findTargetVariableHelper(const expr_t arg1, const expr_t arg2,
expr_t unpackPowerDeriv() const; int lhs_symb_id) const;
[[nodiscard]] optional<int> findTargetVariable(int lhs_symb_id) const override;
expr_t substituteDiff(const lag_equivalence_table_t& nodes, subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs) const override;
expr_t substituteUnaryOpNodes(const lag_equivalence_table_t& nodes, subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs) const override;
expr_t substitutePacExpectation(const string& name, expr_t subexpr) override;
expr_t substitutePacTargetNonstationary(const string& name, expr_t subexpr) override;
[[nodiscard]] expr_t decreaseLeadsLagsPredeterminedVariables() const override;
expr_t differentiateForwardVars(const vector<string>& subset, subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs) const override;
[[nodiscard]] bool isNumConstNodeEqualTo(double value) const override;
[[nodiscard]] int countDiffs() const override;
[[nodiscard]] bool isVariableNodeEqualTo(SymbolType type_arg, int variable_id,
int lag_arg) const override;
[[nodiscard]] expr_t replaceTrendVar() const override;
expr_t detrend(int symb_id, bool log_trend, expr_t trend) const override;
expr_t clone(DataTree& alt_datatree) const override;
[[nodiscard]] expr_t removeTrendLeadLag(const map<int, expr_t>& trend_symbols_map) const override;
//! Function to write out the oPowerNode in expr_t terms as opposed to writing out the function
//! itself
[[nodiscard]] expr_t unpackPowerDeriv() const;
//! Returns MULT_i*(lhs-rhs) = 0, creating multiplier MULT_i //! Returns MULT_i*(lhs-rhs) = 0, creating multiplier MULT_i
expr_t addMultipliersToConstraints(int i); expr_t addMultipliersToConstraints(int i);
//! Returns the non-zero hand-side of an equation (that must have a hand side equal to zero) //! Returns the non-zero hand side of an equation (that must have a hand side equal to zero)
expr_t getNonZeroPartofEquation() const; [[nodiscard]] expr_t getNonZeroPartofEquation() const;
virtual bool isInStaticForm() const; [[nodiscard]] bool isInStaticForm() const override;
//! Substitute auxiliary variables by their expression in static model void fillAutoregressiveRow(int eqn, const vector<int>& lhs,
virtual expr_t substituteStaticAuxiliaryVariable() const; map<tuple<int, int, int>, expr_t>& AR) const;
//! Substitute auxiliary variables by their expression in static model auxiliary variable definition //! Finds equations where a variable is equal to a constant
virtual expr_t substituteStaticAuxiliaryDefinition() const; void findConstantEquations(map<VariableNode*, NumConstNode*>& table) const;
expr_t replaceVarsInEquation(map<VariableNode*, NumConstNode*>& table) const override;
[[nodiscard]] bool containsPacExpectation(const string& pac_model_name = "") const override;
[[nodiscard]] bool containsPacTargetNonstationary(const string& pac_model_name
= "") const override;
/*
ec_params_and_vars:
- 1st element = feedback force parameter
- 2nd element = list of terms in the cointegration relationship (symb_id,
is target ?, multiplicative scalar); this form theoretically allows for a
linear combination in the cointegration, though for the time being we allow
less than that
ar_params_and_vars: elements are indexed according to lag (index 0 is lag
1); each tuple is (parameter_id, variable_id, variable_lag) where
variable_lag is *not* the lag order in the AR
(because variable is an AUX_DIFF_LAG)
*/
void getPacAREC(
int lhs_symb_id, int 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>>& additive_vars_params_and_constants) const;
//! Finds the share of optimizing agents in the PAC equation,
//! the expr node associated with it,
//! and the expr node associated with the non-optimizing part
[[nodiscard]] tuple<optional<int>, expr_t, expr_t, expr_t>
getPacOptimizingShareAndExprNodes(int lhs_orig_symb_id) const;
[[nodiscard]] pair<optional<int>, expr_t>
getPacOptimizingShareAndExprNodesHelper(int lhs_orig_symb_id) const;
[[nodiscard]] expr_t getPacNonOptimizingPart(int optim_share_symb_id) const;
[[nodiscard]] bool isParamTimesEndogExpr() const override;
void decomposeAdditiveTerms(vector<pair<expr_t, int>>& terms, int current_sign) const override;
void decomposeMultiplicativeFactors(vector<pair<expr_t, int>>& factors,
int current_exponent = 1) const override;
void matchMatchedMoment(vector<int>& symb_ids, vector<int>& lags,
vector<int>& powers) const override;
[[nodiscard]] pair<int, expr_t> matchEndogenousTimesConstant() const override;
[[nodiscard]] expr_t substituteLogTransform(int orig_symb_id, int aux_symb_id) const override;
[[nodiscard]] expr_t substituteAggregationOperators(subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs) const override;
[[nodiscard]] tuple<int, expr_t, expr_t>
matchComplementarityCondition(const optional<int>& heterogeneity_dimension
= nullopt) const override;
}; };
//! Trinary operator node //! Trinary operator node
class TrinaryOpNode : public ExprNode class TrinaryOpNode : public ExprNode
{ {
friend class ModelTree; friend class ModelTree;
private:
public:
const expr_t arg1, arg2, arg3; const expr_t arg1, arg2, arg3;
const TrinaryOpcode op_code; const TrinaryOpcode op_code;
virtual expr_t computeDerivative(int deriv_id);
virtual int cost(int cost, bool is_matlab) const; protected:
virtual int cost(const temporary_terms_t &temporary_terms, bool is_matlab) const; void prepareForDerivation() override;
virtual int cost(const map<NodeTreeReference, temporary_terms_t> &temp_terms_map, bool is_matlab) const; void prepareForChainRuleDerivation(
//! Returns the derivative of this node if darg1, darg2 and darg3 are the derivatives of the arguments const map<int, BinaryOpNode*>& recursive_variables,
unordered_map<expr_t, set<int>>& non_null_chain_rule_derivatives) const override;
void computeSubExprContainingVariable(int symb_id, int lag,
set<expr_t>& contain_var) const override;
private:
expr_t computeDerivative(int deriv_id) override;
expr_t
computeChainRuleDerivative(int deriv_id, const map<int, BinaryOpNode*>& recursive_variables,
unordered_map<expr_t, set<int>>& non_null_chain_rule_derivatives,
unordered_map<expr_t, map<int, expr_t>>& cache) override;
[[nodiscard]] int cost(int cost, bool is_matlab) const override;
[[nodiscard]] int cost(const vector<vector<unordered_set<expr_t>>>& blocks_temporary_terms,
bool is_matlab) const override;
[[nodiscard]] int cost(const map<pair<int, int>, unordered_set<expr_t>>& temp_terms_map,
bool is_matlab) const override;
//! Returns the derivative of this node if darg1, darg2 and darg3 are the derivatives of the
//! arguments
expr_t composeDerivatives(expr_t darg1, expr_t darg2, expr_t darg3); expr_t composeDerivatives(expr_t darg1, expr_t darg2, expr_t darg3);
// Returns the node obtained by applying a transformation recursively on the arguments (in same
// datatree)
template<typename Callable, typename... Args>
requires invocable<Callable, expr_t, Args...>
expr_t
recurseTransform(Callable&& op, Args&&... args) const
{
expr_t substarg1 {invoke(forward<Callable>(op), arg1, forward<Args>(args)...)};
expr_t substarg2 {invoke(forward<Callable>(op), arg2, forward<Args>(args)...)};
expr_t substarg3 {invoke(forward<Callable>(op), arg3, forward<Args>(args)...)};
return buildSimilarTrinaryOpNode(substarg1, substarg2, substarg3, datatree);
}
public: public:
TrinaryOpNode(DataTree &datatree_arg, const expr_t arg1_arg, TrinaryOpNode(DataTree& datatree_arg, int idx_arg, const expr_t arg1_arg,
TrinaryOpcode op_code_arg, const expr_t arg2_arg, const expr_t arg3_arg); TrinaryOpcode op_code_arg, const expr_t arg2_arg, const expr_t arg3_arg);
virtual void prepareForDerivation(); [[nodiscard]] int precedence(ExprNodeOutputType output_type,
virtual int precedence(ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms) const; const temporary_terms_t& temporary_terms) const override;
virtual void computeTemporaryTerms(map<expr_t, pair<int, NodeTreeReference> > &reference_count, void computeTemporaryTerms(const pair<int, int>& derivOrder,
map<NodeTreeReference, temporary_terms_t> &temp_terms_map, map<pair<int, int>, unordered_set<expr_t>>& temp_terms_map,
bool is_matlab, NodeTreeReference tr) const; unordered_map<expr_t, pair<int, pair<int, int>>>& reference_count,
virtual void writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, deriv_node_temp_terms_t &tef_terms) const; bool is_matlab) const override;
virtual void writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, deriv_node_temp_terms_t &tef_terms, const bool isdynamic) const; void computeBlockTemporaryTerms(
virtual bool containsExternalFunction() const; int blk, int eq, vector<vector<unordered_set<expr_t>>>& blocks_temporary_terms,
virtual void writeExternalFunctionOutput(ostream &output, ExprNodeOutputType output_type, unordered_map<expr_t, tuple<int, int, int>>& reference_count) const override;
void writeOutput(ostream& output, ExprNodeOutputType output_type,
const temporary_terms_t& temporary_terms, const temporary_terms_t& temporary_terms,
deriv_node_temp_terms_t &tef_terms) const; const temporary_terms_idxs_t& temporary_terms_idxs,
virtual void writeJsonExternalFunctionOutput(vector<string> &efout, const deriv_node_temp_terms_t& tef_terms) const override;
void writeJsonAST(ostream& output) const override;
void writeJsonOutput(ostream& output, const temporary_terms_t& temporary_terms,
const deriv_node_temp_terms_t& tef_terms, bool isdynamic) const override;
[[nodiscard]] bool containsExternalFunction() const override;
void writeExternalFunctionOutput(ostream& output, ExprNodeOutputType output_type,
const temporary_terms_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs,
deriv_node_temp_terms_t& tef_terms) const override;
void writeJsonExternalFunctionOutput(vector<string>& efout,
const temporary_terms_t& temporary_terms, const temporary_terms_t& temporary_terms,
deriv_node_temp_terms_t& tef_terms, deriv_node_temp_terms_t& tef_terms,
const bool isdynamic) const; bool isdynamic) const override;
virtual void compileExternalFunctionOutput(ostream &CompileCode, unsigned int &instruction_number, void writeBytecodeExternalFunctionOutput(Bytecode::Writer& code_file,
bool lhs_rhs, const temporary_terms_t &temporary_terms, ExprNodeBytecodeOutputType output_type,
const map_idx_t &map_idx, bool dynamic, bool steady_dynamic, const temporary_terms_t& temporary_terms,
deriv_node_temp_terms_t &tef_terms) const; const temporary_terms_idxs_t& temporary_terms_idxs,
virtual void computeTemporaryTerms(map<expr_t, int> &reference_count, deriv_node_temp_terms_t& tef_terms) const override;
temporary_terms_t &temporary_terms, void collectVARLHSVariable(set<expr_t>& result) const override;
map<expr_t, pair<int, int> > &first_occurence, void collectDynamicVariables(SymbolType type_arg, set<pair<int, int>>& result) const override;
int Curr_block, static double eval_opcode(double v1, TrinaryOpcode op_code, double v2, double v3) noexcept(false);
vector< vector<temporary_terms_t> > &v_temporary_terms, [[nodiscard]] double eval(const eval_context_t& eval_context) const noexcept(false) override;
int equation) const; void writeBytecodeOutput(Bytecode::Writer& code_file, ExprNodeBytecodeOutputType output_type,
virtual void collectDynamicVariables(SymbolType type_arg, set<pair<int, int> > &result) const; const temporary_terms_t& temporary_terms,
virtual void collectTemporary_terms(const temporary_terms_t &temporary_terms, temporary_terms_inuse_t &temporary_terms_inuse, int Curr_Block) const; const temporary_terms_idxs_t& temporary_terms_idxs,
static double eval_opcode(double v1, TrinaryOpcode op_code, double v2, double v3) throw (EvalException, EvalExternalFunctionException); const deriv_node_temp_terms_t& tef_terms) const override;
virtual double eval(const eval_context_t &eval_context) const throw (EvalException, EvalExternalFunctionException); expr_t toStatic(DataTree& static_datatree) const override;
virtual void compile(ostream &CompileCode, unsigned int &instruction_number, bool lhs_rhs, const temporary_terms_t &temporary_terms, const map_idx_t &map_idx, bool dynamic, bool steady_dynamic, deriv_node_temp_terms_t &tef_terms) const; void computeXrefs(EquationInfo& ei) const override;
virtual expr_t toStatic(DataTree &static_datatree) const; BinaryOpNode* normalizeEquationHelper(const set<expr_t>& contain_var, expr_t rhs) const override;
virtual void computeXrefs(EquationInfo &ei) const; [[nodiscard]] int maxEndoLead() const override;
virtual pair<int, expr_t> normalizeEquation(int symb_id_endo, vector<pair<int, pair<expr_t, expr_t> > > &List_of_Op_RHS) const; [[nodiscard]] int maxExoLead() const override;
virtual expr_t getChainRuleDerivative(int deriv_id, const map<int, expr_t> &recursive_variables); [[nodiscard]] int maxEndoLag() const override;
virtual int maxEndoLead() const; [[nodiscard]] int maxExoLag() const override;
virtual int maxExoLead() const; [[nodiscard]] int maxLead() const override;
virtual int maxEndoLag() const; [[nodiscard]] int maxLag() const override;
virtual int maxExoLag() const; [[nodiscard]] int maxLagWithDiffsExpanded() const override;
virtual int maxLead() const; [[nodiscard]] int VarMaxLag(const set<expr_t>& lhs_lag_equiv) const override;
virtual expr_t decreaseLeadsLags(int n) const; [[nodiscard]] expr_t undiff() const override;
virtual expr_t substituteEndoLeadGreaterThanTwo(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool deterministic_model) const; [[nodiscard]] expr_t decreaseLeadsLags(int n) const override;
//! Creates another TrinaryOpNode with the same opcode, but with a possibly different datatree and arguments expr_t substituteEndoLeadGreaterThanTwo(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
expr_t buildSimilarTrinaryOpNode(expr_t alt_arg1, expr_t alt_arg2, expr_t alt_arg3, DataTree &alt_datatree) const; bool deterministic_model) const override;
virtual expr_t substituteEndoLagGreaterThanTwo(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const; //! Creates another TrinaryOpNode with the same opcode, but with a possibly different datatree and
virtual expr_t substituteExoLead(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool deterministic_model) const; //! arguments
virtual expr_t substituteExoLag(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const; expr_t buildSimilarTrinaryOpNode(expr_t alt_arg1, expr_t alt_arg2, expr_t alt_arg3,
virtual expr_t substituteExpectation(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool partial_information_model) const; DataTree& alt_datatree) const;
virtual expr_t decreaseLeadsLagsPredeterminedVariables() const; expr_t substituteEndoLagGreaterThanTwo(subst_table_t& subst_table,
virtual expr_t differentiateForwardVars(const vector<string> &subset, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const; vector<BinaryOpNode*>& neweqs) const override;
virtual bool isNumConstNodeEqualTo(double value) const; expr_t substituteExoLead(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
virtual bool containsEndogenous(void) const; bool deterministic_model) const override;
virtual bool containsExogenous() const; expr_t substituteExoLag(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs) const override;
virtual bool isVariableNodeEqualTo(SymbolType type_arg, int variable_id, int lag_arg) const; expr_t substituteExpectation(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
virtual expr_t replaceTrendVar() const; bool partial_information_model) const override;
virtual expr_t detrend(int symb_id, bool log_trend, expr_t trend) const; [[nodiscard]] expr_t substituteAdl() const override;
virtual expr_t cloneDynamic(DataTree &dynamic_datatree) const; [[nodiscard]] expr_t substituteModelLocalVariables() const override;
virtual expr_t removeTrendLeadLag(map<int, expr_t> trend_symbols_map) const; [[nodiscard]] expr_t
virtual bool isInStaticForm() const; substituteVarExpectation(const map<string, expr_t>& subst_table) const override;
//! Substitute auxiliary variables by their expression in static model void findDiffNodes(lag_equivalence_table_t& nodes) const override;
virtual expr_t substituteStaticAuxiliaryVariable() const; void findUnaryOpNodesForAuxVarCreation(lag_equivalence_table_t& nodes) const override;
[[nodiscard]] optional<int> findTargetVariable(int lhs_symb_id) const override;
expr_t substituteDiff(const lag_equivalence_table_t& nodes, subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs) const override;
expr_t substituteUnaryOpNodes(const lag_equivalence_table_t& nodes, subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs) const override;
expr_t substitutePacExpectation(const string& name, expr_t subexpr) override;
expr_t substitutePacTargetNonstationary(const string& name, expr_t subexpr) override;
[[nodiscard]] expr_t decreaseLeadsLagsPredeterminedVariables() const override;
expr_t differentiateForwardVars(const vector<string>& subset, subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs) const override;
[[nodiscard]] bool isNumConstNodeEqualTo(double value) const override;
[[nodiscard]] int countDiffs() const override;
[[nodiscard]] bool isVariableNodeEqualTo(SymbolType type_arg, int variable_id,
int lag_arg) const override;
[[nodiscard]] expr_t replaceTrendVar() const override;
expr_t detrend(int symb_id, bool log_trend, expr_t trend) const override;
expr_t clone(DataTree& alt_datatree) const override;
[[nodiscard]] expr_t removeTrendLeadLag(const map<int, expr_t>& trend_symbols_map) const override;
[[nodiscard]] bool isInStaticForm() const override;
expr_t replaceVarsInEquation(map<VariableNode*, NumConstNode*>& table) const override;
[[nodiscard]] bool containsPacExpectation(const string& pac_model_name = "") const override;
[[nodiscard]] bool containsPacTargetNonstationary(const string& pac_model_name
= "") const override;
[[nodiscard]] bool isParamTimesEndogExpr() const override;
[[nodiscard]] expr_t substituteAggregationOperators(subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs) const override;
[[nodiscard]] expr_t substituteLogTransform(int orig_symb_id, int aux_symb_id) const override;
}; };
//! External function node //! External function node
class AbstractExternalFunctionNode : public ExprNode class AbstractExternalFunctionNode : public ExprNode
{ {
public:
const int symb_id;
const vector<expr_t> arguments;
private: private:
virtual expr_t computeDerivative(int deriv_id); expr_t computeDerivative(int deriv_id) override;
expr_t
computeChainRuleDerivative(int deriv_id, const map<int, BinaryOpNode*>& recursive_variables,
unordered_map<expr_t, set<int>>& non_null_chain_rule_derivatives,
unordered_map<expr_t, map<int, expr_t>>& cache) override;
virtual expr_t composeDerivatives(const vector<expr_t>& dargs) = 0; virtual expr_t composeDerivatives(const vector<expr_t>& dargs) = 0;
// Computes the maximum of f applied to all arguments (result will always be non-negative)
int maxHelper(const function<int(expr_t)>& f) const;
// Returns the node obtained by applying a transformation recursively on the arguments (in same
// datatree)
template<typename Callable, typename... Args>
requires invocable<Callable, expr_t, Args...>
expr_t
recurseTransform(Callable&& op, Args&&... args) const
{
vector<expr_t> arguments_subst;
for (auto argument : arguments)
arguments_subst.push_back(invoke(forward<Callable>(op), argument, forward<Args>(args)...));
return buildSimilarExternalFunctionNode(arguments_subst, datatree);
}
protected: protected:
//! Thrown when trying to access an unknown entry in external_function_node_map //! Thrown when trying to access an unknown entry in external_function_node_map
class UnknownFunctionNameAndArgs class UnknownFunctionNameAndArgs
{ {
}; };
const int symb_id; void prepareForDerivation() override;
const vector<expr_t> arguments; void prepareForChainRuleDerivation(
const map<int, BinaryOpNode*>& recursive_variables,
unordered_map<expr_t, set<int>>& non_null_chain_rule_derivatives) const override;
//! Returns true if the given external function has been written as a temporary term //! Returns true if the given external function has been written as a temporary term
bool alreadyWrittenAsTefTerm(int the_symb_id, deriv_node_temp_terms_t &tef_terms) const; [[nodiscard]] bool alreadyWrittenAsTefTerm(int the_symb_id,
const deriv_node_temp_terms_t& tef_terms) const;
//! Returns the index in the tef_terms map of this external function //! Returns the index in the tef_terms map of this external function
int getIndxInTefTerms(int the_symb_id, deriv_node_temp_terms_t &tef_terms) const throw (UnknownFunctionNameAndArgs); [[nodiscard]] int getIndxInTefTerms(int the_symb_id,
const deriv_node_temp_terms_t& tef_terms) const
noexcept(false);
//! Helper function to write output arguments of any given external function //! Helper function to write output arguments of any given external function
void writeExternalFunctionArguments(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, deriv_node_temp_terms_t &tef_terms) const; void writeExternalFunctionArguments(ostream& output, ExprNodeOutputType output_type,
void writeJsonExternalFunctionArguments(ostream &output, const temporary_terms_t &temporary_terms, deriv_node_temp_terms_t &tef_terms, const bool isdynamic) const; const temporary_terms_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs,
const deriv_node_temp_terms_t& tef_terms) const;
void writeJsonASTExternalFunctionArguments(ostream& output) const;
void writeJsonExternalFunctionArguments(ostream& output, const temporary_terms_t& temporary_terms,
const deriv_node_temp_terms_t& tef_terms,
bool isdynamic) const;
void writeBytecodeExternalFunctionArguments(Bytecode::Writer& code_file,
ExprNodeBytecodeOutputType output_type,
const temporary_terms_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs,
const deriv_node_temp_terms_t& tef_terms) const;
/*! Returns a predicate that tests whether an other ExprNode is an external
function which is computed by the same external function call (i.e. it has
the same so-called "Tef" index) */
[[nodiscard]] virtual function<bool(expr_t)> sameTefTermPredicate() const = 0;
void computeSubExprContainingVariable(int symb_id, int lag,
set<expr_t>& contain_var) const override;
public: public:
AbstractExternalFunctionNode(DataTree &datatree_arg, int symb_id_arg, AbstractExternalFunctionNode(DataTree& datatree_arg, int idx_arg, int symb_id_arg,
const vector<expr_t> &arguments_arg); vector<expr_t> arguments_arg);
virtual void prepareForDerivation(); [[nodiscard]] string getName() const;
virtual void computeTemporaryTerms(map<expr_t, pair<int, NodeTreeReference> > &reference_count, void computeTemporaryTerms(const pair<int, int>& derivOrder,
map<NodeTreeReference, temporary_terms_t> &temp_terms_map, map<pair<int, int>, unordered_set<expr_t>>& temp_terms_map,
bool is_matlab, NodeTreeReference tr) const = 0; unordered_map<expr_t, pair<int, pair<int, int>>>& reference_count,
virtual void writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, deriv_node_temp_terms_t &tef_terms) const = 0; bool is_matlab) const override;
virtual void writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, deriv_node_temp_terms_t &tef_terms, const bool isdynamic = true) const = 0; void computeBlockTemporaryTerms(
virtual bool containsExternalFunction() const; int blk, int eq, vector<vector<unordered_set<expr_t>>>& blocks_temporary_terms,
virtual void writeExternalFunctionOutput(ostream &output, ExprNodeOutputType output_type, unordered_map<expr_t, tuple<int, int, int>>& reference_count) const override;
void writeOutput(ostream& output, ExprNodeOutputType output_type,
const temporary_terms_t& temporary_terms, const temporary_terms_t& temporary_terms,
deriv_node_temp_terms_t &tef_terms) const = 0; const temporary_terms_idxs_t& temporary_terms_idxs,
virtual void writeJsonExternalFunctionOutput(vector<string> &efout, const deriv_node_temp_terms_t& tef_terms) const override
= 0;
void writeJsonAST(ostream& output) const override = 0;
void writeJsonOutput(ostream& output, const temporary_terms_t& temporary_terms,
const deriv_node_temp_terms_t& tef_terms,
bool isdynamic = true) const override
= 0;
[[nodiscard]] bool containsExternalFunction() const override;
void writeExternalFunctionOutput(ostream& output, ExprNodeOutputType output_type,
const temporary_terms_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs,
deriv_node_temp_terms_t& tef_terms) const override
= 0;
void writeJsonExternalFunctionOutput(vector<string>& efout,
const temporary_terms_t& temporary_terms, const temporary_terms_t& temporary_terms,
deriv_node_temp_terms_t& tef_terms, deriv_node_temp_terms_t& tef_terms,
const bool isdynamic = true) const = 0; bool isdynamic = true) const override
virtual void compileExternalFunctionOutput(ostream &CompileCode, unsigned int &instruction_number, = 0;
bool lhs_rhs, const temporary_terms_t &temporary_terms, void writeBytecodeExternalFunctionOutput(Bytecode::Writer& code_file,
const map_idx_t &map_idx, bool dynamic, bool steady_dynamic, ExprNodeBytecodeOutputType output_type,
deriv_node_temp_terms_t &tef_terms) const = 0; const temporary_terms_t& temporary_terms,
virtual void computeTemporaryTerms(map<expr_t, int> &reference_count, const temporary_terms_idxs_t& temporary_terms_idxs,
temporary_terms_t &temporary_terms, deriv_node_temp_terms_t& tef_terms) const override
map<expr_t, pair<int, int> > &first_occurence, = 0;
int Curr_block, void collectVARLHSVariable(set<expr_t>& result) const override;
vector< vector<temporary_terms_t> > &v_temporary_terms, void collectDynamicVariables(SymbolType type_arg, set<pair<int, int>>& result) const override;
int equation) const = 0; [[nodiscard]] double eval(const eval_context_t& eval_context) const noexcept(false) override;
virtual void collectDynamicVariables(SymbolType type_arg, set<pair<int, int> > &result) const; void writeBytecodeOutput(Bytecode::Writer& code_file, ExprNodeBytecodeOutputType output_type,
virtual void collectTemporary_terms(const temporary_terms_t &temporary_terms, temporary_terms_inuse_t &temporary_terms_inuse, int Curr_Block) const; const temporary_terms_t& temporary_terms,
virtual double eval(const eval_context_t &eval_context) const throw (EvalException, EvalExternalFunctionException); const temporary_terms_idxs_t& temporary_terms_idxs,
unsigned int compileExternalFunctionArguments(ostream &CompileCode, unsigned int &instruction_number, const deriv_node_temp_terms_t& tef_terms) const override
bool lhs_rhs, const temporary_terms_t &temporary_terms, = 0;
const map_idx_t &map_idx, bool dynamic, bool steady_dynamic, expr_t toStatic(DataTree& static_datatree) const override;
deriv_node_temp_terms_t &tef_terms) const; void computeXrefs(EquationInfo& ei) const override = 0;
BinaryOpNode* normalizeEquationHelper(const set<expr_t>& contain_var, expr_t rhs) const override;
virtual void compile(ostream &CompileCode, unsigned int &instruction_number, bool lhs_rhs, const temporary_terms_t &temporary_terms, const map_idx_t &map_idx, bool dynamic, bool steady_dynamic, deriv_node_temp_terms_t &tef_terms) const = 0; [[nodiscard]] int maxEndoLead() const override;
virtual expr_t toStatic(DataTree &static_datatree) const = 0; [[nodiscard]] int maxExoLead() const override;
virtual void computeXrefs(EquationInfo &ei) const = 0; [[nodiscard]] int maxEndoLag() const override;
virtual pair<int, expr_t> normalizeEquation(int symb_id_endo, vector<pair<int, pair<expr_t, expr_t> > > &List_of_Op_RHS) const; [[nodiscard]] int maxExoLag() const override;
virtual expr_t getChainRuleDerivative(int deriv_id, const map<int, expr_t> &recursive_variables); [[nodiscard]] int maxLead() const override;
virtual int maxEndoLead() const; [[nodiscard]] int maxLag() const override;
virtual int maxExoLead() const; [[nodiscard]] int maxLagWithDiffsExpanded() const override;
virtual int maxEndoLag() const; [[nodiscard]] int VarMaxLag(const set<expr_t>& lhs_lag_equiv) const override;
virtual int maxExoLag() const; [[nodiscard]] expr_t undiff() const override;
virtual int maxLead() const; [[nodiscard]] expr_t decreaseLeadsLags(int n) const override;
virtual expr_t decreaseLeadsLags(int n) const; expr_t substituteEndoLeadGreaterThanTwo(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
virtual expr_t substituteEndoLeadGreaterThanTwo(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool deterministic_model) const; bool deterministic_model) const override;
virtual expr_t substituteEndoLagGreaterThanTwo(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const; expr_t substituteEndoLagGreaterThanTwo(subst_table_t& subst_table,
virtual expr_t substituteExoLead(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool deterministic_model) const; vector<BinaryOpNode*>& neweqs) const override;
virtual expr_t substituteExoLag(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const; expr_t substituteExoLead(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
virtual expr_t substituteExpectation(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool partial_information_model) const; bool deterministic_model) const override;
virtual expr_t buildSimilarExternalFunctionNode(vector<expr_t> &alt_args, DataTree &alt_datatree) const = 0; expr_t substituteExoLag(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs) const override;
virtual expr_t decreaseLeadsLagsPredeterminedVariables() const; expr_t substituteExpectation(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
virtual expr_t differentiateForwardVars(const vector<string> &subset, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const; bool partial_information_model) const override;
virtual bool isNumConstNodeEqualTo(double value) const; [[nodiscard]] expr_t substituteAdl() const override;
virtual bool containsEndogenous(void) const; [[nodiscard]] expr_t substituteModelLocalVariables() const override;
virtual bool containsExogenous() const; [[nodiscard]] expr_t
virtual bool isVariableNodeEqualTo(SymbolType type_arg, int variable_id, int lag_arg) const; substituteVarExpectation(const map<string, expr_t>& subst_table) const override;
virtual void writePrhs(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, deriv_node_temp_terms_t &tef_terms, const string &ending) const; void findDiffNodes(lag_equivalence_table_t& nodes) const override;
virtual expr_t replaceTrendVar() const; void findUnaryOpNodesForAuxVarCreation(lag_equivalence_table_t& nodes) const override;
virtual expr_t detrend(int symb_id, bool log_trend, expr_t trend) const; [[nodiscard]] optional<int> findTargetVariable(int lhs_symb_id) const override;
virtual expr_t cloneDynamic(DataTree &dynamic_datatree) const = 0; expr_t substituteDiff(const lag_equivalence_table_t& nodes, subst_table_t& subst_table,
virtual expr_t removeTrendLeadLag(map<int, expr_t> trend_symbols_map) const; vector<BinaryOpNode*>& neweqs) const override;
virtual bool isInStaticForm() const; expr_t substituteUnaryOpNodes(const lag_equivalence_table_t& nodes, subst_table_t& subst_table,
//! Substitute auxiliary variables by their expression in static model vector<BinaryOpNode*>& neweqs) const override;
virtual expr_t substituteStaticAuxiliaryVariable() const; expr_t substitutePacExpectation(const string& name, expr_t subexpr) override;
expr_t substitutePacTargetNonstationary(const string& name, expr_t subexpr) override;
virtual expr_t buildSimilarExternalFunctionNode(vector<expr_t>& alt_args,
DataTree& alt_datatree) const
= 0;
[[nodiscard]] expr_t decreaseLeadsLagsPredeterminedVariables() const override;
expr_t differentiateForwardVars(const vector<string>& subset, subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs) const override;
[[nodiscard]] bool isNumConstNodeEqualTo(double value) const override;
[[nodiscard]] int countDiffs() const override;
[[nodiscard]] bool isVariableNodeEqualTo(SymbolType type_arg, int variable_id,
int lag_arg) const override;
void writePrhs(ostream& output, ExprNodeOutputType output_type,
const temporary_terms_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs,
const deriv_node_temp_terms_t& tef_terms) const;
[[nodiscard]] expr_t replaceTrendVar() const override;
expr_t detrend(int symb_id, bool log_trend, expr_t trend) const override;
expr_t clone(DataTree& alt_datatree) const override;
[[nodiscard]] expr_t removeTrendLeadLag(const map<int, expr_t>& trend_symbols_map) const override;
[[nodiscard]] bool isInStaticForm() const override;
expr_t replaceVarsInEquation(map<VariableNode*, NumConstNode*>& table) const override;
[[nodiscard]] bool containsPacExpectation(const string& pac_model_name = "") const override;
[[nodiscard]] bool containsPacTargetNonstationary(const string& pac_model_name
= "") const override;
[[nodiscard]] bool isParamTimesEndogExpr() const override;
[[nodiscard]] expr_t substituteLogTransform(int orig_symb_id, int aux_symb_id) const override;
[[nodiscard]] expr_t substituteAggregationOperators(subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs) const override;
}; };
class ExternalFunctionNode : public AbstractExternalFunctionNode class ExternalFunctionNode : public AbstractExternalFunctionNode
{ {
friend class FirstDerivExternalFunctionNode;
friend class SecondDerivExternalFunctionNode;
private: private:
virtual expr_t composeDerivatives(const vector<expr_t> &dargs); expr_t composeDerivatives(const vector<expr_t>& dargs) override;
protected:
[[nodiscard]] function<bool(expr_t)> sameTefTermPredicate() const override;
public: public:
ExternalFunctionNode(DataTree &datatree_arg, int symb_id_arg, ExternalFunctionNode(DataTree& datatree_arg, int idx_arg, int symb_id_arg,
const vector<expr_t>& arguments_arg); const vector<expr_t>& arguments_arg);
virtual void computeTemporaryTerms(map<expr_t, pair<int, NodeTreeReference> > &reference_count, void writeOutput(ostream& output, ExprNodeOutputType output_type,
map<NodeTreeReference, temporary_terms_t> &temp_terms_map,
bool is_matlab, NodeTreeReference tr) const;
virtual void writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, deriv_node_temp_terms_t &tef_terms) const;
virtual void writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, deriv_node_temp_terms_t &tef_terms, const bool isdynamic) const;
virtual void writeExternalFunctionOutput(ostream &output, ExprNodeOutputType output_type,
const temporary_terms_t& temporary_terms, const temporary_terms_t& temporary_terms,
deriv_node_temp_terms_t &tef_terms) const; const temporary_terms_idxs_t& temporary_terms_idxs,
virtual void writeJsonExternalFunctionOutput(vector<string> &efout, const deriv_node_temp_terms_t& tef_terms) const override;
void writeJsonAST(ostream& output) const override;
void writeJsonOutput(ostream& output, const temporary_terms_t& temporary_terms,
const deriv_node_temp_terms_t& tef_terms, bool isdynamic) const override;
void writeExternalFunctionOutput(ostream& output, ExprNodeOutputType output_type,
const temporary_terms_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs,
deriv_node_temp_terms_t& tef_terms) const override;
void writeJsonExternalFunctionOutput(vector<string>& efout,
const temporary_terms_t& temporary_terms, const temporary_terms_t& temporary_terms,
deriv_node_temp_terms_t& tef_terms, deriv_node_temp_terms_t& tef_terms,
const bool isdynamic) const; bool isdynamic) const override;
virtual void compileExternalFunctionOutput(ostream &CompileCode, unsigned int &instruction_number, void writeBytecodeExternalFunctionOutput(Bytecode::Writer& code_file,
bool lhs_rhs, const temporary_terms_t &temporary_terms, ExprNodeBytecodeOutputType output_type,
const map_idx_t &map_idx, bool dynamic, bool steady_dynamic, const temporary_terms_t& temporary_terms,
deriv_node_temp_terms_t &tef_terms) const; const temporary_terms_idxs_t& temporary_terms_idxs,
virtual void computeTemporaryTerms(map<expr_t, int> &reference_count, deriv_node_temp_terms_t& tef_terms) const override;
temporary_terms_t &temporary_terms, void writeBytecodeOutput(Bytecode::Writer& code_file, ExprNodeBytecodeOutputType output_type,
map<expr_t, pair<int, int> > &first_occurence, const temporary_terms_t& temporary_terms,
int Curr_block, const temporary_terms_idxs_t& temporary_terms_idxs,
vector< vector<temporary_terms_t> > &v_temporary_terms, const deriv_node_temp_terms_t& tef_terms) const override;
int equation) const; void computeXrefs(EquationInfo& ei) const override;
virtual void compile(ostream &CompileCode, unsigned int &instruction_number, bool lhs_rhs, const temporary_terms_t &temporary_terms, const map_idx_t &map_idx, bool dynamic, bool steady_dynamic, deriv_node_temp_terms_t &tef_terms) const; expr_t buildSimilarExternalFunctionNode(vector<expr_t>& alt_args,
virtual expr_t toStatic(DataTree &static_datatree) const; DataTree& alt_datatree) const override;
virtual void computeXrefs(EquationInfo &ei) const;
virtual expr_t buildSimilarExternalFunctionNode(vector<expr_t> &alt_args, DataTree &alt_datatree) const;
virtual expr_t cloneDynamic(DataTree &dynamic_datatree) const;
}; };
class FirstDerivExternalFunctionNode : public AbstractExternalFunctionNode class FirstDerivExternalFunctionNode : public AbstractExternalFunctionNode
{ {
private: public:
const int inputIndex; const int inputIndex;
virtual expr_t composeDerivatives(const vector<expr_t> &dargs);
private:
expr_t composeDerivatives(const vector<expr_t>& dargs) override;
protected:
[[nodiscard]] function<bool(expr_t)> sameTefTermPredicate() const override;
public: public:
FirstDerivExternalFunctionNode(DataTree &datatree_arg, FirstDerivExternalFunctionNode(DataTree& datatree_arg, int idx_arg, int top_level_symb_id_arg,
int top_level_symb_id_arg, const vector<expr_t>& arguments_arg, int inputIndex_arg);
const vector<expr_t> &arguments_arg, void writeOutput(ostream& output, ExprNodeOutputType output_type,
int inputIndex_arg);
virtual void computeTemporaryTerms(map<expr_t, pair<int, NodeTreeReference> > &reference_count,
map<NodeTreeReference, temporary_terms_t> &temp_terms_map,
bool is_matlab, NodeTreeReference tr) const;
virtual void computeTemporaryTerms(map<expr_t, int> &reference_count,
temporary_terms_t &temporary_terms,
map<expr_t, pair<int, int> > &first_occurence,
int Curr_block,
vector< vector<temporary_terms_t> > &v_temporary_terms,
int equation) const;
virtual void writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, deriv_node_temp_terms_t &tef_terms) const;
virtual void writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, deriv_node_temp_terms_t &tef_terms, const bool isdynamic) const;
virtual void compile(ostream &CompileCode, unsigned int &instruction_number,
bool lhs_rhs, const temporary_terms_t &temporary_terms,
const map_idx_t &map_idx, bool dynamic, bool steady_dynamic,
deriv_node_temp_terms_t &tef_terms) const;
virtual void writeExternalFunctionOutput(ostream &output, ExprNodeOutputType output_type,
const temporary_terms_t& temporary_terms, const temporary_terms_t& temporary_terms,
deriv_node_temp_terms_t &tef_terms) const; const temporary_terms_idxs_t& temporary_terms_idxs,
virtual void writeJsonExternalFunctionOutput(vector<string> &efout, const deriv_node_temp_terms_t& tef_terms) const override;
void writeJsonAST(ostream& output) const override;
void writeJsonOutput(ostream& output, const temporary_terms_t& temporary_terms,
const deriv_node_temp_terms_t& tef_terms, bool isdynamic) const override;
void writeBytecodeOutput(Bytecode::Writer& code_file, ExprNodeBytecodeOutputType output_type,
const temporary_terms_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs,
const deriv_node_temp_terms_t& tef_terms) const override;
void writeExternalFunctionOutput(ostream& output, ExprNodeOutputType output_type,
const temporary_terms_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs,
deriv_node_temp_terms_t& tef_terms) const override;
void writeJsonExternalFunctionOutput(vector<string>& efout,
const temporary_terms_t& temporary_terms, const temporary_terms_t& temporary_terms,
deriv_node_temp_terms_t& tef_terms, deriv_node_temp_terms_t& tef_terms,
const bool isdynamic) const; bool isdynamic) const override;
virtual void compileExternalFunctionOutput(ostream &CompileCode, unsigned int &instruction_number, void writeBytecodeExternalFunctionOutput(Bytecode::Writer& code_file,
bool lhs_rhs, const temporary_terms_t &temporary_terms, ExprNodeBytecodeOutputType output_type,
const map_idx_t &map_idx, bool dynamic, bool steady_dynamic, const temporary_terms_t& temporary_terms,
deriv_node_temp_terms_t &tef_terms) const; const temporary_terms_idxs_t& temporary_terms_idxs,
virtual expr_t toStatic(DataTree &static_datatree) const; deriv_node_temp_terms_t& tef_terms) const override;
virtual void computeXrefs(EquationInfo &ei) const; void computeXrefs(EquationInfo& ei) const override;
virtual expr_t buildSimilarExternalFunctionNode(vector<expr_t> &alt_args, DataTree &alt_datatree) const; expr_t buildSimilarExternalFunctionNode(vector<expr_t>& alt_args,
virtual expr_t cloneDynamic(DataTree &dynamic_datatree) const; DataTree& alt_datatree) const override;
}; };
class SecondDerivExternalFunctionNode : public AbstractExternalFunctionNode class SecondDerivExternalFunctionNode : public AbstractExternalFunctionNode
{ {
private: public:
const int inputIndex1; const int inputIndex1;
const int inputIndex2; const int inputIndex2;
virtual expr_t composeDerivatives(const vector<expr_t> &dargs);
private:
expr_t composeDerivatives(const vector<expr_t>& dargs) override;
protected:
[[nodiscard]] function<bool(expr_t)> sameTefTermPredicate() const override;
public: public:
SecondDerivExternalFunctionNode(DataTree &datatree_arg, SecondDerivExternalFunctionNode(DataTree& datatree_arg, int idx_arg, int top_level_symb_id_arg,
int top_level_symb_id_arg, const vector<expr_t>& arguments_arg, int inputIndex1_arg,
const vector<expr_t> &arguments_arg,
int inputIndex1_arg,
int inputIndex2_arg); int inputIndex2_arg);
virtual void computeTemporaryTerms(map<expr_t, pair<int, NodeTreeReference> > &reference_count, void writeOutput(ostream& output, ExprNodeOutputType output_type,
map<NodeTreeReference, temporary_terms_t> &temp_terms_map,
bool is_matlab, NodeTreeReference tr) const;
virtual void computeTemporaryTerms(map<expr_t, int> &reference_count,
temporary_terms_t &temporary_terms,
map<expr_t, pair<int, int> > &first_occurence,
int Curr_block,
vector< vector<temporary_terms_t> > &v_temporary_terms,
int equation) const;
virtual void writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, deriv_node_temp_terms_t &tef_terms) const;
virtual void writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, deriv_node_temp_terms_t &tef_terms, const bool isdynamic) const;
virtual void compile(ostream &CompileCode, unsigned int &instruction_number,
bool lhs_rhs, const temporary_terms_t &temporary_terms,
const map_idx_t &map_idx, bool dynamic, bool steady_dynamic,
deriv_node_temp_terms_t &tef_terms) const;
virtual void writeExternalFunctionOutput(ostream &output, ExprNodeOutputType output_type,
const temporary_terms_t& temporary_terms, const temporary_terms_t& temporary_terms,
deriv_node_temp_terms_t &tef_terms) const; const temporary_terms_idxs_t& temporary_terms_idxs,
virtual void writeJsonExternalFunctionOutput(vector<string> &efout, const deriv_node_temp_terms_t& tef_terms) const override;
void writeJsonAST(ostream& output) const override;
void writeJsonOutput(ostream& output, const temporary_terms_t& temporary_terms,
const deriv_node_temp_terms_t& tef_terms, bool isdynamic) const override;
void writeBytecodeOutput(Bytecode::Writer& code_file, ExprNodeBytecodeOutputType output_type,
const temporary_terms_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs,
const deriv_node_temp_terms_t& tef_terms) const override;
void writeExternalFunctionOutput(ostream& output, ExprNodeOutputType output_type,
const temporary_terms_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs,
deriv_node_temp_terms_t& tef_terms) const override;
void writeJsonExternalFunctionOutput(vector<string>& efout,
const temporary_terms_t& temporary_terms, const temporary_terms_t& temporary_terms,
deriv_node_temp_terms_t& tef_terms, deriv_node_temp_terms_t& tef_terms,
const bool isdynamic) const; bool isdynamic) const override;
virtual void compileExternalFunctionOutput(ostream &CompileCode, unsigned int &instruction_number, void writeBytecodeExternalFunctionOutput(Bytecode::Writer& code_file,
bool lhs_rhs, const temporary_terms_t &temporary_terms, ExprNodeBytecodeOutputType output_type,
const map_idx_t &map_idx, bool dynamic, bool steady_dynamic, const temporary_terms_t& temporary_terms,
deriv_node_temp_terms_t &tef_terms) const; const temporary_terms_idxs_t& temporary_terms_idxs,
virtual expr_t toStatic(DataTree &static_datatree) const; deriv_node_temp_terms_t& tef_terms) const override;
virtual void computeXrefs(EquationInfo &ei) const; void computeXrefs(EquationInfo& ei) const override;
virtual expr_t buildSimilarExternalFunctionNode(vector<expr_t> &alt_args, DataTree &alt_datatree) const; expr_t buildSimilarExternalFunctionNode(vector<expr_t>& alt_args,
virtual expr_t cloneDynamic(DataTree &dynamic_datatree) const; DataTree& alt_datatree) const override;
};
/* Common superclass for nodes that have the following two characteristics:
– they take a submodel name as an argument
– they will be substituted out in the middle of the transform pass
*/
class SubModelNode : public ExprNode
{
public:
const string model_name;
SubModelNode(DataTree& datatree_arg, int idx_arg, string model_name_arg);
void computeTemporaryTerms(const pair<int, int>& derivOrder,
map<pair<int, int>, unordered_set<expr_t>>& temp_terms_map,
unordered_map<expr_t, pair<int, pair<int, int>>>& reference_count,
bool is_matlab) const override;
void computeBlockTemporaryTerms(
int blk, int eq, vector<vector<unordered_set<expr_t>>>& blocks_temporary_terms,
unordered_map<expr_t, tuple<int, int, int>>& reference_count) const override;
expr_t toStatic(DataTree& static_datatree) const override;
expr_t computeDerivative(int deriv_id) override;
[[nodiscard]] int maxEndoLead() const override;
[[nodiscard]] int maxExoLead() const override;
[[nodiscard]] int maxEndoLag() const override;
[[nodiscard]] int maxExoLag() const override;
[[nodiscard]] int maxLead() const override;
[[nodiscard]] int maxLag() const override;
[[nodiscard]] int VarMaxLag(const set<expr_t>& lhs_lag_equiv) const override;
[[nodiscard]] expr_t undiff() const override;
[[nodiscard]] expr_t decreaseLeadsLags(int n) const override;
[[nodiscard]] int countDiffs() const override;
expr_t substituteEndoLeadGreaterThanTwo(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
bool deterministic_model) const override;
expr_t substituteEndoLagGreaterThanTwo(subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs) const override;
expr_t substituteExoLead(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
bool deterministic_model) const override;
expr_t substituteExoLag(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs) const override;
[[nodiscard]] bool containsExternalFunction() const override;
[[nodiscard]] double eval(const eval_context_t& eval_context) const noexcept(false) override;
void computeXrefs(EquationInfo& ei) const override;
expr_t substituteExpectation(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
bool partial_information_model) const override;
[[nodiscard]] expr_t substituteAdl() const override;
[[nodiscard]] expr_t substituteModelLocalVariables() const override;
void findDiffNodes(lag_equivalence_table_t& nodes) const override;
void findUnaryOpNodesForAuxVarCreation(lag_equivalence_table_t& nodes) const override;
[[nodiscard]] optional<int> findTargetVariable(int lhs_symb_id) const override;
expr_t substituteDiff(const lag_equivalence_table_t& nodes, subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs) const override;
expr_t substituteUnaryOpNodes(const lag_equivalence_table_t& nodes, subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs) const override;
BinaryOpNode* normalizeEquationHelper(const set<expr_t>& contain_var, expr_t rhs) const override;
void writeBytecodeOutput(Bytecode::Writer& code_file, ExprNodeBytecodeOutputType output_type,
const temporary_terms_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs,
const deriv_node_temp_terms_t& tef_terms) const override;
void collectVARLHSVariable(set<expr_t>& result) const override;
void collectDynamicVariables(SymbolType type_arg, set<pair<int, int>>& result) const override;
[[nodiscard]] bool isNumConstNodeEqualTo(double value) const override;
[[nodiscard]] bool isVariableNodeEqualTo(SymbolType type_arg, int variable_id,
int lag_arg) const override;
[[nodiscard]] bool isInStaticForm() const override;
expr_t replaceVarsInEquation(map<VariableNode*, NumConstNode*>& table) const override;
[[nodiscard]] bool isParamTimesEndogExpr() const override;
expr_t differentiateForwardVars(const vector<string>& subset, subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs) const override;
[[nodiscard]] expr_t decreaseLeadsLagsPredeterminedVariables() const override;
[[nodiscard]] expr_t replaceTrendVar() const override;
expr_t detrend(int symb_id, bool log_trend, expr_t trend) const override;
[[nodiscard]] expr_t removeTrendLeadLag(const map<int, expr_t>& trend_symbols_map) const override;
[[nodiscard]] expr_t substituteLogTransform(int orig_symb_id, int aux_symb_id) const override;
[[nodiscard]] expr_t substituteAggregationOperators(subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs) const override;
protected:
void prepareForDerivation() override;
void prepareForChainRuleDerivation(
const map<int, BinaryOpNode*>& recursive_variables,
unordered_map<expr_t, set<int>>& non_null_chain_rule_derivatives) const override;
void computeSubExprContainingVariable(int symb_id, int lag,
set<expr_t>& contain_var) const override;
private:
expr_t
computeChainRuleDerivative(int deriv_id, const map<int, BinaryOpNode*>& recursive_variables,
unordered_map<expr_t, set<int>>& non_null_chain_rule_derivatives,
unordered_map<expr_t, map<int, expr_t>>& cache) override;
};
class VarExpectationNode : public SubModelNode
{
public:
VarExpectationNode(DataTree& datatree_arg, int idx_arg, string model_name_arg);
void writeOutput(ostream& output, ExprNodeOutputType output_type,
const temporary_terms_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs,
const deriv_node_temp_terms_t& tef_terms) const override;
expr_t clone(DataTree& alt_datatree) const override;
[[nodiscard]] int maxLagWithDiffsExpanded() const override;
[[nodiscard]] expr_t
substituteVarExpectation(const map<string, expr_t>& subst_table) const override;
expr_t substitutePacExpectation(const string& name, expr_t subexpr) override;
expr_t substitutePacTargetNonstationary(const string& name, expr_t subexpr) override;
[[nodiscard]] bool containsPacExpectation(const string& pac_model_name = "") const override;
[[nodiscard]] bool containsPacTargetNonstationary(const string& pac_model_name
= "") const override;
void writeJsonAST(ostream& output) const override;
void writeJsonOutput(ostream& output, const temporary_terms_t& temporary_terms,
const deriv_node_temp_terms_t& tef_terms, bool isdynamic) const override;
};
class PacExpectationNode : public SubModelNode
{
public:
PacExpectationNode(DataTree& datatree_arg, int idx_arg, string model_name);
void writeOutput(ostream& output, ExprNodeOutputType output_type,
const temporary_terms_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs,
const deriv_node_temp_terms_t& tef_terms) const override;
expr_t clone(DataTree& alt_datatree) const override;
[[nodiscard]] int maxLagWithDiffsExpanded() const override;
[[nodiscard]] expr_t
substituteVarExpectation(const map<string, expr_t>& subst_table) const override;
expr_t substitutePacExpectation(const string& name, expr_t subexpr) override;
expr_t substitutePacTargetNonstationary(const string& name, expr_t subexpr) override;
[[nodiscard]] bool containsPacExpectation(const string& pac_model_name = "") const override;
[[nodiscard]] bool containsPacTargetNonstationary(const string& pac_model_name
= "") const override;
void writeJsonAST(ostream& output) const override;
void writeJsonOutput(ostream& output, const temporary_terms_t& temporary_terms,
const deriv_node_temp_terms_t& tef_terms, bool isdynamic) const override;
};
class PacTargetNonstationaryNode : public SubModelNode
{
public:
PacTargetNonstationaryNode(DataTree& datatree_arg, int idx_arg, string model_name);
void writeOutput(ostream& output, ExprNodeOutputType output_type,
const temporary_terms_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs,
const deriv_node_temp_terms_t& tef_terms) const override;
expr_t clone(DataTree& alt_datatree) const override;
[[nodiscard]] int maxLagWithDiffsExpanded() const override;
[[nodiscard]] expr_t
substituteVarExpectation(const map<string, expr_t>& subst_table) const override;
expr_t substitutePacExpectation(const string& name, expr_t subexpr) override;
expr_t substitutePacTargetNonstationary(const string& name, expr_t subexpr) override;
[[nodiscard]] bool containsPacExpectation(const string& pac_model_name = "") const override;
[[nodiscard]] bool containsPacTargetNonstationary(const string& pac_model_name
= "") const override;
void writeJsonAST(ostream& output) const override;
void writeJsonOutput(ostream& output, const temporary_terms_t& temporary_terms,
const deriv_node_temp_terms_t& tef_terms, bool isdynamic) const override;
}; };
#endif #endif
/* /*
* Copyright (C) 2014-2017 Dynare Team * Copyright © 2014-2023 Dynare Team
* *
* This file is part of Dynare. * This file is part of Dynare.
* *
...@@ -14,38 +14,36 @@ ...@@ -14,38 +14,36 @@
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Dynare. If not, see <http://www.gnu.org/licenses/>. * along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/ */
#ifndef _EXTENDED_PREPROCESSOR_TYPES_HH #ifndef EXTENDED_PREPROCESSOR_TYPES_HH
#define _EXTENDED_PREPROCESSOR_TYPES_HH #define EXTENDED_PREPROCESSOR_TYPES_HH
enum FileOutputType // Values for the “output” option
enum class OutputType
{ {
none, // outputs files for Matlab/Octave processing standard, // Default value, infer the derivation order from .mod file only
dynamic, // outputs <fname>_dynamic.* and related files first, // Output only 1st dynamic derivatives with no other computations
first, // outputs <fname>_first_derivatives.* and related files second, // Output at least 2nd dynamic derivatives
second, // outputs <fname>_first_derivatives.*, <fname>_second_derivatives.* and related files third, // Output at least 3rd dynamic derivatives
third, // outputs <fname>_first_derivatives.*, <fname>_second_derivatives.*, <fname>_third_derivatives.* and related files
}; };
enum LanguageOutputType // Values for the “language” option
enum class LanguageOutputType
{ {
matlab, // outputs files for Matlab/Octave processing matlab, // outputs files for MATLAB/Octave processing
c, // outputs files for C
cpp, // outputs files for C++
cuda, // outputs files for CUDA (not yet implemented)
julia, // outputs files for Julia julia, // outputs files for Julia
python, // outputs files for Python (not yet implemented) (not yet implemented)
}; };
enum JsonFileOutputType enum class JsonFileOutputType
{ {
file, // output JSON files to file file, // output JSON files to file
standardout, // output JSON files to stdout standardout, // output JSON files to stdout
}; };
enum JsonOutputPointType // Values for the “json” option
enum class JsonOutputPointType
{ {
nojson, // don't output JSON nojson, // don't output JSON
parsing, // output JSON after the parsing step parsing, // output JSON after the parsing step
...@@ -53,4 +51,5 @@ enum JsonOutputPointType ...@@ -53,4 +51,5 @@ enum JsonOutputPointType
transformpass, // output JSON after the transform pass transformpass, // output JSON after the transform pass
computingpass // output JSON after the computing pass computingpass // output JSON after the computing pass
}; };
#endif #endif
/* /*
* Copyright (C) 2010-2015 Dynare Team * Copyright © 2010-2015 Dynare Team
* *
* This file is part of Dynare. * This file is part of Dynare.
* *
...@@ -14,38 +14,34 @@ ...@@ -14,38 +14,34 @@
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Dynare. If not, see <http://www.gnu.org/licenses/>. * along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include <cstdlib>
#include <cassert> #include <cassert>
#include <cerrno>
#include <cmath> #include <cmath>
#include <cstdlib>
#include <iostream> #include <iostream>
#include "ExternalFunctionsTable.hh" #include "ExternalFunctionsTable.hh"
#include "SymbolTable.hh" #include "SymbolTable.hh"
ExternalFunctionsTable::ExternalFunctionsTable()
{
}
void void
ExternalFunctionsTable::addExternalFunction(int symb_id, const external_function_options &external_function_options_arg, bool track_nargs) ExternalFunctionsTable::addExternalFunction(
int symb_id, const external_function_options& external_function_options_arg, bool track_nargs)
{ {
assert(symb_id >= 0); assert(symb_id >= 0);
assert(external_function_options_arg.nargs > 0); assert(external_function_options_arg.nargs > 0);
// Change options to be saved so the table is consistent // Change options to be saved so the table is consistent
external_function_options external_function_options_chng = external_function_options_arg; external_function_options external_function_options_chng = external_function_options_arg;
if (external_function_options_arg.firstDerivSymbID == eExtFunSetButNoNameProvided) if (external_function_options_arg.firstDerivSymbID == IDSetButNoNameProvided)
external_function_options_chng.firstDerivSymbID = symb_id; external_function_options_chng.firstDerivSymbID = symb_id;
if (external_function_options_arg.secondDerivSymbID == eExtFunSetButNoNameProvided) if (external_function_options_arg.secondDerivSymbID == IDSetButNoNameProvided)
external_function_options_chng.secondDerivSymbID = symb_id; external_function_options_chng.secondDerivSymbID = symb_id;
if (!track_nargs) if (!track_nargs)
external_function_options_chng.nargs = eExtFunNotSet; external_function_options_chng.nargs = IDNotSet;
// Ensure 1st & 2nd deriv option consistency // Ensure 1st & 2nd deriv option consistency
if (external_function_options_chng.secondDerivSymbID == symb_id if (external_function_options_chng.secondDerivSymbID == symb_id
...@@ -58,23 +54,26 @@ ExternalFunctionsTable::addExternalFunction(int symb_id, const external_function ...@@ -58,23 +54,26 @@ ExternalFunctionsTable::addExternalFunction(int symb_id, const external_function
if ((external_function_options_chng.secondDerivSymbID != symb_id if ((external_function_options_chng.secondDerivSymbID != symb_id
&& external_function_options_chng.firstDerivSymbID == symb_id) && external_function_options_chng.firstDerivSymbID == symb_id)
&& external_function_options_chng.secondDerivSymbID != eExtFunNotSet) && external_function_options_chng.secondDerivSymbID != IDNotSet)
{ {
cerr << "ERROR: If the first derivative is provided by the top-level function, the " cerr << "ERROR: If the first derivative is provided by the top-level function, the "
<< "second derivative cannot be provided by any other external function." << endl; << "second derivative cannot be provided by any other external function." << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (external_function_options_chng.secondDerivSymbID != eExtFunNotSet if (external_function_options_chng.secondDerivSymbID != IDNotSet
&& external_function_options_chng.firstDerivSymbID == eExtFunNotSet) && external_function_options_chng.firstDerivSymbID == IDNotSet)
{ {
cerr << "ERROR: If the second derivative is provided, the first derivative must also be provided." << endl; cerr << "ERROR: If the second derivative is provided, the first derivative must also be "
"provided."
<< endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (external_function_options_chng.secondDerivSymbID == external_function_options_chng.firstDerivSymbID if (external_function_options_chng.secondDerivSymbID
== external_function_options_chng.firstDerivSymbID
&& external_function_options_chng.firstDerivSymbID != symb_id && external_function_options_chng.firstDerivSymbID != symb_id
&& external_function_options_chng.firstDerivSymbID != eExtFunNotSet) && external_function_options_chng.firstDerivSymbID != IDNotSet)
{ {
cerr << "ERROR: If the Jacobian and Hessian are provided by the same function, that " cerr << "ERROR: If the Jacobian and Hessian are provided by the same function, that "
<< "function must be the top-level function." << endl; << "function must be the top-level function." << endl;
...@@ -85,29 +84,40 @@ ExternalFunctionsTable::addExternalFunction(int symb_id, const external_function ...@@ -85,29 +84,40 @@ ExternalFunctionsTable::addExternalFunction(int symb_id, const external_function
if (exists(symb_id)) if (exists(symb_id))
{ {
bool ok_to_overwrite = false; bool ok_to_overwrite = false;
if (getNargs(symb_id) == eExtFunNotSet) // implies that the information stored about this function is not important if (getNargs(symb_id)
== IDNotSet) // implies that the information stored about this function is not important
ok_to_overwrite = true; ok_to_overwrite = true;
if (!ok_to_overwrite) // prevents multiple non-compatible calls to external_function(name=funcname) if (!ok_to_overwrite) // prevents multiple non-compatible calls to
// external_function(name=funcname)
{ // e.g. e_f(name=a,nargs=1,fd,sd) and e_f(name=a,nargs=2,fd=b,sd=c) should cause an error { // e.g. e_f(name=a,nargs=1,fd,sd) and e_f(name=a,nargs=2,fd=b,sd=c) should cause an error
if (external_function_options_chng.nargs != getNargs(symb_id)) if (external_function_options_chng.nargs != getNargs(symb_id))
{ {
cerr << "ERROR: The number of arguments passed to the external_function() statement do not " cerr << "ERROR: The number of arguments passed to the external_function() statement "
<< "match the number of arguments passed to a previous call or declaration of the top-level function."<< endl; "do not "
<< "match the number of arguments passed to a previous call or declaration of "
"the top-level function."
<< endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (external_function_options_chng.firstDerivSymbID != getFirstDerivSymbID(symb_id)) if (external_function_options_chng.firstDerivSymbID != getFirstDerivSymbID(symb_id))
{ {
cerr << "ERROR: The first derivative function passed to the external_function() statement does not " cerr << "ERROR: The first derivative function passed to the external_function() "
<< "match the first derivative function passed to a previous call or declaration of the top-level function."<< endl; "statement does not "
<< "match the first derivative function passed to a previous call or "
"declaration of the top-level function."
<< endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (external_function_options_chng.secondDerivSymbID != getSecondDerivSymbID(symb_id)) if (external_function_options_chng.secondDerivSymbID != getSecondDerivSymbID(symb_id))
{ {
cerr << "ERROR: The second derivative function passed to the external_function() statement does not " cerr << "ERROR: The second derivative function passed to the external_function() "
<< "match the second derivative function passed to a previous call or declaration of the top-level function."<< endl; "statement does not "
<< "match the second derivative function passed to a previous call or "
"declaration of the top-level function."
<< endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
......
/* /*
* Copyright (C) 2010-2015 Dynare Team * Copyright © 2010-2023 Dynare Team
* *
* This file is part of Dynare. * This file is part of Dynare.
* *
...@@ -14,39 +14,29 @@ ...@@ -14,39 +14,29 @@
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Dynare. If not, see <http://www.gnu.org/licenses/>. * along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/ */
#ifndef _EXTERNALFUNCTIONSTABLE_HH #ifndef EXTERNAL_FUNCTIONS_TABLE_HH
#define _EXTERNALFUNCTIONSTABLE_HH #define EXTERNAL_FUNCTIONS_TABLE_HH
using namespace std;
#include <algorithm>
#include <iostream> #include <iostream>
#include <map>
#include <string> #include <string>
#include <vector> #include <vector>
#include <map>
enum ExternalFunctionSetOrNot using namespace std;
{
eExtFunSetButNoNameProvided = -2, //! Signifies that the derivative is obtained from the top-level function
eExtFunNotSet = -1, //! Signifies that no external function exists that calculates the derivative
eExtFunSetDefaultNargs = 1 //! This is the default number of arguments when nargs is not specified
};
//! Handles external functions //! Handles external functions
class ExternalFunctionsTable class ExternalFunctionsTable
{ {
public: public:
//! Thrown when trying to access an unknown symbol (by id) //! Thrown when trying to access an unknown symbol (by id)
class UnknownExternalFunctionSymbolIDException struct UnknownExternalFunctionSymbolIDException
{ {
public:
//! Symbol ID //! Symbol ID
int id; int id;
UnknownExternalFunctionSymbolIDException(int id_arg) : id(id_arg)
{
}
}; };
/* For all arguments, -2 means not set /* For all arguments, -2 means not set
...@@ -58,70 +48,64 @@ public: ...@@ -58,70 +48,64 @@ public:
{ {
int nargs, firstDerivSymbID, secondDerivSymbID; int nargs, firstDerivSymbID, secondDerivSymbID;
}; };
typedef map<int, external_function_options> external_function_table_type; using external_function_table_type = map<int, external_function_options>;
//! Symbol ID used when no external function exists that calculates the derivative
constexpr static int IDNotSet = -1;
//! Symbol ID used when the derivative is obtained from the top-level function
constexpr static int IDSetButNoNameProvided = -2;
//! Default number of arguments when nargs is not specified
constexpr static int defaultNargs = 1;
private: private:
//! Map containing options provided to external_functions() //! Map containing options provided to external_functions()
external_function_table_type externalFunctionTable; external_function_table_type externalFunctionTable;
public: public:
ExternalFunctionsTable();
//! Adds an external function to the table as well as its derivative functions //! Adds an external function to the table as well as its derivative functions
void addExternalFunction(int symb_id, const external_function_options &external_function_options_arg, bool track_nargs); void addExternalFunction(int symb_id,
const external_function_options& external_function_options_arg,
bool track_nargs);
//! See if the function exists in the External Functions Table //! See if the function exists in the External Functions Table
inline bool exists(int symb_id) const; [[nodiscard]] inline bool exists(int symb_id) const;
//! Get the number of arguments for a given external function //! Get the number of arguments for a given external function
inline int getNargs(int symb_id) const throw (UnknownExternalFunctionSymbolIDException); [[nodiscard]] inline int getNargs(int symb_id) const noexcept(false);
//! Get the symbol_id of the first derivative function //! Get the symbol_id of the first derivative function
inline int getFirstDerivSymbID(int symb_id) const throw (UnknownExternalFunctionSymbolIDException); [[nodiscard]] inline int getFirstDerivSymbID(int symb_id) const noexcept(false);
//! Get the symbol_id of the second derivative function //! Get the symbol_id of the second derivative function
inline int getSecondDerivSymbID(int symb_id) const throw (UnknownExternalFunctionSymbolIDException); [[nodiscard]] inline int getSecondDerivSymbID(int symb_id) const noexcept(false);
//! Returns the total number of unique external functions declared or used in the .mod file
inline int get_total_number_of_unique_model_block_external_functions() const;
}; };
inline bool inline bool
ExternalFunctionsTable::exists(int symb_id) const ExternalFunctionsTable::exists(int symb_id) const
{ {
external_function_table_type::const_iterator iter = externalFunctionTable.find(symb_id); return externalFunctionTable.contains(symb_id);
return (iter != externalFunctionTable.end());
} }
inline int inline int
ExternalFunctionsTable::getNargs(int symb_id) const throw (UnknownExternalFunctionSymbolIDException) ExternalFunctionsTable::getNargs(int symb_id) const noexcept(false)
{ {
if (exists(symb_id)) if (auto it = externalFunctionTable.find(symb_id); it != externalFunctionTable.end())
return externalFunctionTable.find(symb_id)->second.nargs; return it->second.nargs;
else else
throw UnknownExternalFunctionSymbolIDException(symb_id); throw UnknownExternalFunctionSymbolIDException {symb_id};
} }
inline int inline int
ExternalFunctionsTable::getFirstDerivSymbID(int symb_id) const throw (UnknownExternalFunctionSymbolIDException) ExternalFunctionsTable::getFirstDerivSymbID(int symb_id) const noexcept(false)
{ {
if (exists(symb_id)) if (auto it = externalFunctionTable.find(symb_id); it != externalFunctionTable.end())
return externalFunctionTable.find(symb_id)->second.firstDerivSymbID; return it->second.firstDerivSymbID;
else else
throw UnknownExternalFunctionSymbolIDException(symb_id); throw UnknownExternalFunctionSymbolIDException {symb_id};
} }
inline int inline int
ExternalFunctionsTable::getSecondDerivSymbID(int symb_id) const throw (UnknownExternalFunctionSymbolIDException) ExternalFunctionsTable::getSecondDerivSymbID(int symb_id) const noexcept(false)
{ {
if (exists(symb_id)) if (auto it = externalFunctionTable.find(symb_id); it != externalFunctionTable.end())
return externalFunctionTable.find(symb_id)->second.secondDerivSymbID; return it->second.secondDerivSymbID;
else else
throw UnknownExternalFunctionSymbolIDException(symb_id); throw UnknownExternalFunctionSymbolIDException {symb_id};
}
inline int
ExternalFunctionsTable::get_total_number_of_unique_model_block_external_functions() const
{
int number_of_unique_model_block_external_functions = 0;
for (external_function_table_type::const_iterator it = externalFunctionTable.begin();
it != externalFunctionTable.end(); it++)
if (it->second.nargs > 0)
number_of_unique_model_block_external_functions++;
return number_of_unique_model_block_external_functions;
} }
#endif #endif
/*
* Copyright © 2024 Dynare Team
*
* This file is part of Dynare.
*
* Dynare is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Dynare is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/
#include <cassert>
#include <utility>
#include "HeterogeneityTable.hh"
#include "SymbolTable.hh"
void
HeterogeneityTable::setSymbolTable(SymbolTable* symbol_table_arg)
{
symbol_table = symbol_table_arg;
}
int
HeterogeneityTable::addDimension(string name)
{
if (name_to_id.contains(name))
throw AlreadyDeclaredDimensionException {move(name)};
int id {static_cast<int>(id_to_name.size())};
name_to_id.emplace(name, id);
id_to_name.push_back(move(name));
return id;
}
bool
HeterogeneityTable::exists(const string& name) const
{
return name_to_id.contains(name);
}
int
HeterogeneityTable::getID(const string& name) const
{
if (auto it = name_to_id.find(name); it != name_to_id.end())
return it->second;
else
throw UnknownDimensionNameException {name};
}
string
HeterogeneityTable::getName(int id) const
{
if (id < 0 || id >= static_cast<int>(id_to_name.size()))
throw UnknownDimensionIDException {id};
else
return id_to_name[id];
}
bool
HeterogeneityTable::empty() const
{
return name_to_id.empty();
}
vector<string>
HeterogeneityTable::getDimensions() const
{
return id_to_name;
}
int
HeterogeneityTable::size() const
{
return static_cast<int>(name_to_id.size());
}
void
HeterogeneityTable::addSummedHeterogeneousEndogenous(int symb_id)
{
assert(symbol_table->getType(symb_id) == SymbolType::heterogeneousEndogenous);
if (summed_het_endo_to_index.contains(symb_id))
throw AlreadyDeclaredSummedHeterogeneousEndogenousException {symb_id};
int index {static_cast<int>(index_to_summed_het_endo.size())};
summed_het_endo_to_index.emplace(symb_id, index);
index_to_summed_het_endo.push_back(symb_id);
}
int
HeterogeneityTable::getSummedHeterogenousEndogenousIndex(int symb_id) const
{
if (auto it = summed_het_endo_to_index.find(symb_id); it != summed_het_endo_to_index.end())
return it->second;
else
throw UnknownSummedHeterogeneousEndogenousException {symb_id};
}
int
HeterogeneityTable::aggregateEndoSize() const
{
return index_to_summed_het_endo.size();
}
void
HeterogeneityTable::writeOutput(ostream& output) const
{
for (size_t id {0}; id < id_to_name.size(); id++)
output << "M_.heterogeneity(" << id + 1 << ").dimension_name = '" << id_to_name[id] << "';"
<< endl;
output << "M_.heterogeneity_aggregates = {" << endl;
for (int symb_id : index_to_summed_het_endo)
output << "'sum', " << symbol_table->getHeterogeneityDimension(symb_id) + 1 << ", "
<< symbol_table->getTypeSpecificID(symb_id) + 1 << ";" << endl;
output << "};" << endl;
}
void
HeterogeneityTable::writeJsonOutput(ostream& output) const
{
assert(!empty());
output << R"("heterogeneity_dimension": [)";
for (bool first_written {false}; const auto& dim : id_to_name)
{
if (exchange(first_written, true))
output << ", ";
output << '"' << dim << '"';
}
output << "]" << endl;
}
/*
* Copyright © 2024 Dynare Team
*
* This file is part of Dynare.
*
* Dynare is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Dynare is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef HETEROGENEITY_TABLE_HH
#define HETEROGENEITY_TABLE_HH
#include <map>
#include <ostream>
#include <string>
#include <vector>
using namespace std;
class SymbolTable; // Forward declaration, to avoid circularity
/*
There is a guarantee that heterogeneity IDs are increasing, i.e. if dimension A is added after
dimension B, then the ID of A is greater than the ID of B.
Moreover, the IDs form a contiguous interval starting at 0.
*/
class HeterogeneityTable
{
private:
// Maps dimension names to IDs
map<string, int> name_to_id;
// Maps dimension IDs to names
vector<string> id_to_name;
SymbolTable* symbol_table {nullptr}; // Cannot be a reference, because of circularity
/* Keeps track of the SUM() operator instances.
Maps a symbol ID that appears inside a SUM() operator into an index in
M_.heterogeneity_aggregates */
map<int, int> summed_het_endo_to_index;
// Maps an index in M_.heterogeneity_aggregates into a symbol ID
vector<int> index_to_summed_het_endo;
public:
void setSymbolTable(SymbolTable* symbol_table_arg);
struct AlreadyDeclaredDimensionException
{
// Dimension name
const string name;
};
struct UnknownDimensionNameException
{
// Dimension name
const string name;
};
struct UnknownDimensionIDException
{
// Dimension ID
const int id;
};
// Returns the dimension ID
int addDimension(string name);
[[nodiscard]] bool exists(const string& name) const;
[[nodiscard]] int getID(const string& name) const;
[[nodiscard]] string getName(int id) const;
[[nodiscard]] bool empty() const;
[[nodiscard]] vector<string> getDimensions() const;
[[nodiscard]] int size() const;
struct AlreadyDeclaredSummedHeterogeneousEndogenousException
{
const int symb_id;
};
struct UnknownSummedHeterogeneousEndogenousException
{
const int symb_id;
};
void addSummedHeterogeneousEndogenous(int symb_id);
[[nodiscard]] int getSummedHeterogenousEndogenousIndex(int symb_id) const;
[[nodiscard]] int aggregateEndoSize() const;
void writeOutput(ostream& output) const;
void writeJsonOutput(ostream& output) const;
};
#endif
/*
* Copyright © 2024-2025 Dynare Team
*
* This file is part of Dynare.
*
* Dynare is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Dynare is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/
#include <cstdlib>
#include <iostream>
#include "HeterogeneousModel.hh"
HeterogeneousModel::HeterogeneousModel(SymbolTable& symbol_table_arg,
NumericalConstants& num_constants_arg,
ExternalFunctionsTable& external_functions_table_arg,
HeterogeneityTable& heterogeneity_table_arg,
int heterogeneity_dimension_arg) :
ModelTree {symbol_table_arg, num_constants_arg, external_functions_table_arg,
heterogeneity_table_arg, true},
heterogeneity_dimension {heterogeneity_dimension_arg}
{
}
HeterogeneousModel&
HeterogeneousModel::operator=(const HeterogeneousModel& m)
{
ModelTree::operator=(m);
assert(heterogeneity_dimension == m.heterogeneity_dimension);
deriv_id_table = m.deriv_id_table;
inv_deriv_id_table = m.inv_deriv_id_table;
return *this;
}
void
HeterogeneousModel::computeChainRuleJacobian()
{
cerr << "Heterogeneous::computeChainRuleJacobian(): unimplemented" << endl;
exit(EXIT_FAILURE);
}
int
HeterogeneousModel::getBlockJacobianEndoCol([[maybe_unused]] int blk, [[maybe_unused]] int var,
[[maybe_unused]] int lead_lag) const
{
cerr << "Heterogeneous::getBlockJacobianEndoCol(): unimplemented" << endl;
exit(EXIT_FAILURE);
}
int
HeterogeneousModel::getMFS() const
{
cerr << "Heterogeneous::getMFS(): unimplemented" << endl;
exit(EXIT_FAILURE);
}
void
HeterogeneousModel::computeDerivIDs()
{
set<pair<int, int>> dynvars;
for (auto& equation : equations)
{
equation->collectDynamicVariables(SymbolType::heterogeneousEndogenous, dynvars);
equation->collectDynamicVariables(SymbolType::heterogeneousExogenous, dynvars);
equation->collectDynamicVariables(SymbolType::endogenous, dynvars);
equation->collectDynamicVariables(SymbolType::exogenous, dynvars);
equation->collectDynamicVariables(SymbolType::parameter, dynvars);
}
for (const auto& [symb_id, lead_lag] : dynvars)
{
auto type {symbol_table.getType(symb_id)};
if (isHeterogeneous(type))
assert(symbol_table.getHeterogeneityDimension(symb_id) == heterogeneity_dimension);
if (type == SymbolType::heterogeneousEndogenous || type == SymbolType::endogenous)
assert(abs(lead_lag) <= 1);
if (type == SymbolType::heterogeneousExogenous || type == SymbolType::exogenous)
assert(lead_lag == 0);
int deriv_id {static_cast<int>(deriv_id_table.size())};
deriv_id_table.emplace(pair {symb_id, lead_lag}, deriv_id);
inv_deriv_id_table.emplace_back(symb_id, lead_lag);
}
}
/*
* Unfold complementarity conditions: (i) declare the multipliers associated
* with each bound constraint μ_l and μ_u ; (ii) add or substract the
* multiplier into the associated condition; (iii) add the the complementarity
* slackness conditions into the set of equations. For example,
* households choose {cₜ, aₜ₊₁} to maximize expected lifetime utility:
* max 𝐸ₜ [∑ₛ₌₀^∞ βˢ · u(cₜ₊ₛ)]
*
* Subject to:
* 1. Budget constraint: cₜ + aₜ₊₁ = yₜ + (1 + rₜ) · aₜ
* 2. Borrowing constraint: aₜ₊₁ ≥ aₘᵢₙ
*
* Let u'(cₜ) denote the marginal utility of consumption.
* Let μₜ ≥ 0 be the Lagrange multiplier on the borrowing constraint.
*
* Then, the Euler equation becomes:
* u′(cₜ) = β · (1 + rₜ₊₁) · u′(cₜ₊₁) − μₜ
*
* Together with:
* aₜ₊₁ ≥ aₘᵢₙ [primal feasibility]
* μₜ ≥ 0 [dual feasibility]
* μₜ · (aₜ₊₁ − aₘᵢₙ) = 0 [complementarity slackness]
* Note that the primal feasibility and dual feasibility constraints are not
* introduced here, but Bhandari et al. (2023) show in Appendix B.1 that they
* are redundant.
*/
void
HeterogeneousModel::transformPass()
{
for (int i = 0; i < static_cast<int>(equations.size()); ++i)
{
if (!complementarity_conditions[i])
continue;
/*
* `const auto& [symb_id, lb, ub] = *complementarity_conditions[i];` was not used here because
* the call to `addEquation` may eventually lead to a resize of the
* `complementarity_conditions` vector, which may invalidate the reference to its element. We
* take a copy instead for safety.
*/
auto [symb_id, lb, ub] = *complementarity_conditions[i];
VariableNode* var = getVariable(symb_id);
if (lb)
{
int mu_id = symbol_table.addHeterogeneousMultiplierAuxiliaryVar(
heterogeneity_dimension, i, "MULT_L_" + symbol_table.getName(symb_id));
expr_t mu_L = AddVariable(mu_id);
auto substeq = AddEqual(AddPlus(equations[i]->arg1, mu_L), equations[i]->arg2);
assert(substeq);
equations[i] = substeq;
addEquation(AddEqual(AddTimes(mu_L, AddMinus(var, lb)), Zero), nullopt);
}
if (ub)
{
int mu_id = symbol_table.addHeterogeneousMultiplierAuxiliaryVar(
heterogeneity_dimension, i, "MULT_U_" + symbol_table.getName(symb_id));
auto mu_U = AddVariable(mu_id);
auto substeq = AddEqual(AddMinus(equations[i]->arg1, mu_U), equations[i]->arg2);
assert(substeq);
equations[i] = substeq;
addEquation(AddEqual(AddTimes(mu_U, AddMinus(ub, var)), Zero), nullopt);
}
}
}
void
HeterogeneousModel::computingPass(int derivsOrder, bool no_tmp_terms, bool use_dll)
{
assert(!use_dll); // Not yet implemented
computeDerivIDs();
set<int> vars;
for (auto& [symb_lag, deriv_id] : deriv_id_table)
if (symbol_table.getType(symb_lag.first) != SymbolType::parameter)
vars.insert(deriv_id);
cout << "Computing " << modelClassName() << " derivatives (order " << derivsOrder << ")." << endl;
computeDerivatives(derivsOrder, vars);
computeTemporaryTerms(!use_dll, no_tmp_terms);
computeMCPEquationsReordering(heterogeneity_dimension);
}
void
HeterogeneousModel::writeModelFiles(const string& basename, bool julia) const
{
assert(!julia); // Not yet implemented
writeSparseModelMFiles<true>(basename, heterogeneity_dimension);
writeComplementarityConditionsFile<true>(basename, heterogeneity_dimension);
}
int
HeterogeneousModel::getJacobianCol(int deriv_id, bool sparse) const
{
assert(sparse);
SymbolType type {getTypeByDerivID(deriv_id)};
int tsid {getTypeSpecificIDByDerivID(deriv_id)};
int lag {getLagByDerivID(deriv_id)};
if (type == SymbolType::heterogeneousEndogenous)
return tsid + (lag + 1) * symbol_table.het_endo_nbr(heterogeneity_dimension);
int shift {3 * symbol_table.het_endo_nbr(heterogeneity_dimension)};
if (type == SymbolType::heterogeneousExogenous)
return shift + tsid;
shift += symbol_table.het_exo_nbr(heterogeneity_dimension);
if (type == SymbolType::endogenous)
return shift + tsid + (lag + 1) * symbol_table.endo_nbr();
shift += symbol_table.endo_nbr();
if (type == SymbolType::exogenous)
return shift + tsid;
throw UnknownDerivIDException();
}
int
HeterogeneousModel::getJacobianColsNbr(bool sparse) const
{
assert(sparse);
return 3 * (symbol_table.het_endo_nbr(heterogeneity_dimension) + symbol_table.endo_nbr())
+ symbol_table.het_exo_nbr(heterogeneity_dimension) + symbol_table.exo_nbr();
}
SymbolType
HeterogeneousModel::getTypeByDerivID(int deriv_id) const noexcept(false)
{
return symbol_table.getType(getSymbIDByDerivID(deriv_id));
}
int
HeterogeneousModel::getLagByDerivID(int deriv_id) const noexcept(false)
{
if (deriv_id < 0 || deriv_id >= static_cast<int>(inv_deriv_id_table.size()))
throw UnknownDerivIDException();
return inv_deriv_id_table[deriv_id].second;
}
int
HeterogeneousModel::getSymbIDByDerivID(int deriv_id) const noexcept(false)
{
if (deriv_id < 0 || deriv_id >= static_cast<int>(inv_deriv_id_table.size()))
throw UnknownDerivIDException();
return inv_deriv_id_table[deriv_id].first;
}
int
HeterogeneousModel::getTypeSpecificIDByDerivID(int deriv_id) const
{
return symbol_table.getTypeSpecificID(getSymbIDByDerivID(deriv_id));
}
int
HeterogeneousModel::getDerivID(int symb_id, int lead_lag) const noexcept(false)
{
if (auto it = deriv_id_table.find({symb_id, lead_lag}); it == deriv_id_table.end())
throw UnknownDerivIDException();
else
return it->second;
}
void
HeterogeneousModel::writeDriverOutput(ostream& output) const
{
std::vector<int> state_var;
for (int endoID = 0; endoID < symbol_table.het_endo_nbr(heterogeneity_dimension); endoID++)
try
{
getDerivID(symbol_table.getID(SymbolType::heterogeneousEndogenous, endoID,
heterogeneity_dimension),
-1);
if (ranges::find(state_var, endoID) == state_var.end())
state_var.push_back(endoID);
}
catch (UnknownDerivIDException& e)
{
}
output << "M_.heterogeneity(" << heterogeneity_dimension + 1 << ").state_var = [";
for (int it : state_var)
output << it + 1 << " ";
output << "];" << endl;
output << "M_.heterogeneity(" << heterogeneity_dimension + 1 << ").dynamic_tmp_nbr = [";
for (const auto& it : temporary_terms_derivatives)
output << it.size() << "; ";
output << "];" << endl;
writeDriverSparseIndicesHelper(
"heterogeneity("s + to_string(heterogeneity_dimension + 1) + ").dynamic", output);
output << "M_.heterogeneity(" << heterogeneity_dimension + 1
<< ").dynamic_mcp_equations_reordering = [";
for (auto i : mcp_equations_reordering)
output << i + 1 << "; ";
output << "];" << endl;
}
/*
* Copyright © 2024 Dynare Team
*
* This file is part of Dynare.
*
* Dynare is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Dynare is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef HETEROGENEOUS_MODEL_HH
#define HETEROGENEOUS_MODEL_HH
#include <string>
#include "ModelTree.hh"
using namespace std;
class HeterogeneousModel : public ModelTree
{
public:
const int heterogeneity_dimension;
HeterogeneousModel(SymbolTable& symbol_table_arg, NumericalConstants& num_constants_arg,
ExternalFunctionsTable& external_functions_table_arg,
HeterogeneityTable& heterogeneity_table_arg, int heterogeneity_dimension_arg);
HeterogeneousModel(const HeterogeneousModel& m) = default;
HeterogeneousModel& operator=(const HeterogeneousModel& m);
void transformPass();
void computingPass(int derivsOrder, bool no_tmp_terms, bool use_dll);
void writeModelFiles(const string& basename, bool julia) const;
void writeDriverOutput(ostream& output) const;
[[nodiscard]] int getJacobianCol(int deriv_id, bool sparse) const override;
[[nodiscard]] int getJacobianColsNbr(bool sparse) const override;
#if 0
void substituteEndoLeadGreaterThanTwo();
//! Transforms the model by removing all lags greater or equal than 2 on endos
void substituteEndoLagGreaterThanTwo();
//! Transforms the model by removing all leads on exos
/*! Note that this can create new lags on endos and exos */
void substituteExoLead();
//! Transforms the model by removing all lags on exos
void substituteExoLag();
//! 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
void transformPredeterminedVariables();
//! Substitutes out all model-local variables
void substituteModelLocalVariables();
#endif
// FIXME: the following 5 functions are identical to those in DynamicModel. Factorization?
[[nodiscard]] int getDerivID(int symb_id, int lead_lag) const noexcept(false) override;
[[nodiscard]] SymbolType getTypeByDerivID(int deriv_id) const noexcept(false) override;
[[nodiscard]] int getLagByDerivID(int deriv_id) const noexcept(false) override;
[[nodiscard]] int getSymbIDByDerivID(int deriv_id) const noexcept(false) override;
[[nodiscard]] int getTypeSpecificIDByDerivID(int deriv_id) const override;
protected:
void computeChainRuleJacobian() override;
int getBlockJacobianEndoCol(int blk, int var, int lead_lag) const override;
string
modelClassName() const override
{
return "dynamic model for heterogeneity dimension '"
+ heterogeneity_table.getName(heterogeneity_dimension) + "'";
}
int getMFS() const override;
private:
// Maps a pair (symbol ID, lead/lag) to a deriv ID
map<pair<int, int>, int> deriv_id_table;
// Maps a deriv ID to a pair (symbol ID, lead/lag)
vector<pair<int, int>> inv_deriv_id_table;
// Allocates the derivation IDs for all endogenous variables for this heterogeneity dimension
void computeDerivIDs();
};
#endif
/*
* Copyright © 2015-2023 Dynare Team
*
* This file is part of Dynare.
*
* Dynare is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Dynare is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <filesystem>
#include <fstream>
#include <regex>
#include <sstream>
#include "macro/Driver.hh"
stringstream
macroExpandModFile(const filesystem::path& filename, const istream& modfile, bool debug,
bool save_macro, filesystem::path save_macro_file, bool line_macro,
const vector<pair<string, string>>& defines, vector<filesystem::path> paths)
{
// Do macro processing
stringstream macro_output;
macro::Environment env = macro::Environment();
macro::Driver m;
/* Calling string() method on filename: not necessary on GNU/Linux and macOS because there is an
implicit conversion from filesystem:path to string (i.e. basic_string<char>), but needed on
Windows because the implicit conversion is only to wstring (i.e. basic_string<wchar_t>). */
m.parse(filename.string(), modfile, debug, defines, env, paths, macro_output);
if (save_macro)
{
if (save_macro_file.empty())
save_macro_file = filename.stem().string() + "_macroexp.mod";
ofstream macro_output_file {save_macro_file};
if (macro_output_file.fail())
{
cerr << "Cannot open " << save_macro_file.string() << " for macro output" << endl;
exit(EXIT_FAILURE);
}
string str(macro_output.str());
if (!line_macro)
{
/* Remove the @#line directives.
Unfortunately GCC 11 does not yet support std::regex::multiline
(despite it being in the C++17 standard), so we are forced to use
a trick to emulate the “usual” behaviour of the caret ^;
here, the latter only matches the beginning of file.
This also means that we are forced to remove the EOL before the
@#line, and not the one after it (matching the EOL before and the
EOL after in the same regexp does not work). */
str = regex_replace(str, regex(R"((^|\r?\n)@#line.*)"), "");
/* Remove the EOLs at the beginning of the output, the first one
being a remnant of the first @#line directive. */
str = regex_replace(str, regex(R"(^(\r?\n)+)"), "");
/* Replace sequences of several newlines by a single newline (in
both LF and CR+LF conventions). */
str = regex_replace(str, regex(R"(\n{2,})"), "\n");
str = regex_replace(str, regex(R"((\r\n){2,})"), "\r\n");
}
macro_output_file << str;
macro_output_file.close();
}
return macro_output;
}
SUBDIRS = macro
BUILT_SOURCES = DynareBison.hh stack.hh position.hh location.hh DynareBison.cc DynareFlex.cc FlexLexer.h
bin_PROGRAMS = dynare_m
# We don't put BUILT_SOURCES in dynare_m_SOURCES, otherwise DynareBison.o and DynareFlex.o will be linked two times (Automake translates DynareFlex.ll and DynareBison.yy into their respective .o); so BUILT_SOURCES is in EXTRA_DIST
dynare_m_SOURCES = \
DynareFlex.ll \
DynareBison.yy \
ComputingTasks.cc \
ComputingTasks.hh \
ModelTree.cc \
ModelTree.hh \
StaticModel.cc \
StaticModel.hh \
DynamicModel.cc \
DynamicModel.hh \
NumericalConstants.cc \
NumericalConstants.hh \
NumericalInitialization.cc \
NumericalInitialization.hh \
Shocks.cc \
Shocks.hh \
SigmaeInitialization.cc \
SigmaeInitialization.hh \
SymbolTable.cc \
SymbolTable.hh \
SymbolList.cc \
SymbolList.hh \
ParsingDriver.cc \
ParsingDriver.hh \
DataTree.cc \
DataTree.hh \
ModFile.cc \
ModFile.hh \
ConfigFile.cc \
ConfigFile.hh \
Statement.cc \
Statement.hh \
ExprNode.cc \
ExprNode.hh \
MinimumFeedbackSet.cc \
MinimumFeedbackSet.hh \
DynareMain.cc \
DynareMain1.cc \
DynareMain2.cc \
CodeInterpreter.hh \
ExternalFunctionsTable.cc \
ExternalFunctionsTable.hh \
SteadyStateModel.hh \
SteadyStateModel.cc \
WarningConsolidation.hh \
WarningConsolidation.cc \
ExtendedPreprocessorTypes.hh
ACLOCAL_AMFLAGS = -I m4
EXTRA_DIST = \
Doxyfile \
$(BUILT_SOURCES)
# The -I. is for <FlexLexer.h>
dynare_m_CPPFLAGS = $(BOOST_CPPFLAGS) -I.
dynare_m_LDFLAGS = $(BOOST_LDFLAGS)
dynare_m_LDADD = macro/libmacro.a
DynareFlex.cc FlexLexer.h: DynareFlex.ll
$(LEX) -o DynareFlex.cc DynareFlex.ll
cp $(LEXINC)/FlexLexer.h . || test -f ./FlexLexer.h
DynareBison.cc DynareBison.hh location.hh stack.hh position.hh: DynareBison.yy
$(YACC) -o DynareBison.cc DynareBison.yy
all-local: $(PROGRAMS)
if HAVE_DOXYGEN
html-local:
$(DOXYGEN)
endif
clean-local:
rm -rf doc/html/
/*
* Copyright (C) 2009-2017 Dynare Team
*
* This file is part of Dynare.
*
* Dynare is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Dynare is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Dynare. If not, see <http://www.gnu.org/licenses/>.
*/
#include <iostream>
#include "MinimumFeedbackSet.hh"
namespace MFS
{
void
Suppress(AdjacencyList_t::vertex_descriptor vertex_to_eliminate, AdjacencyList_t &G)
{
clear_vertex(vertex_to_eliminate, G);
remove_vertex(vertex_to_eliminate, G);
}
void
Suppress(int vertex_num, AdjacencyList_t &G)
{
Suppress(vertex(vertex_num, G), G);
}
void
Eliminate(AdjacencyList_t::vertex_descriptor vertex_to_eliminate, AdjacencyList_t &G)
{
if (in_degree(vertex_to_eliminate, G) > 0 && out_degree(vertex_to_eliminate, G) > 0)
{
AdjacencyList_t::in_edge_iterator it_in, in_end;
AdjacencyList_t::out_edge_iterator it_out, out_end;
for (tie(it_in, in_end) = in_edges(vertex_to_eliminate, G); it_in != in_end; ++it_in)
for (tie(it_out, out_end) = out_edges(vertex_to_eliminate, G); it_out != out_end; ++it_out)
{
AdjacencyList_t::edge_descriptor ed;
bool exist;
tie(ed, exist) = edge(source(*it_in, G), target(*it_out, G), G);
if (!exist)
add_edge(source(*it_in, G), target(*it_out, G), G);
}
}
Suppress(vertex_to_eliminate, G);
}
bool
has_cycle_dfs(AdjacencyList_t &g, AdjacencyList_t::vertex_descriptor u, color_t &color, vector<int> &circuit_stack)
{
property_map<AdjacencyList_t, vertex_index_t>::type v_index = get(vertex_index, g);
color[u] = gray_color;
graph_traits<AdjacencyList_t>::out_edge_iterator vi, vi_end;
for (tie(vi, vi_end) = out_edges(u, g); vi != vi_end; ++vi)
if (color[target(*vi, g)] == white_color && has_cycle_dfs(g, target(*vi, g), color, circuit_stack))
{
// cycle detected, return immediately
circuit_stack.push_back(v_index[target(*vi, g)]);
return true;
}
else if (color[target(*vi, g)] == gray_color)
{
// *vi is an ancestor!
circuit_stack.push_back(v_index[target(*vi, g)]);
return true;
}
color[u] = black_color;
return false;
}
bool
has_cycle(vector<int> &circuit_stack, AdjacencyList_t &g)
{
// Initialize color map to white
color_t color;
graph_traits<AdjacencyList_t>::vertex_iterator vi, vi_end;
for (tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi)
color[*vi] = white_color;
// Perform depth-first search
for (tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi)
if (color[*vi] == white_color && has_cycle_dfs(g, *vi, color, circuit_stack))
return true;
return false;
}
void
Print(AdjacencyList_t &G)
{
AdjacencyList_t::vertex_iterator it, it_end;
property_map<AdjacencyList_t, vertex_index_t>::type v_index = get(vertex_index, G);
cout << "Graph\n";
cout << "-----\n";
for (tie(it, it_end) = vertices(G); it != it_end; ++it)
{
cout << "vertex[" << v_index[*it] + 1 << "] <-";
AdjacencyList_t::in_edge_iterator it_in, in_end;
for (tie(it_in, in_end) = in_edges(*it, G); it_in != in_end; ++it_in)
cout << v_index[source(*it_in, G)] + 1 << " ";
cout << "\n ->";
AdjacencyList_t::out_edge_iterator it_out, out_end;
for (tie(it_out, out_end) = out_edges(*it, G); it_out != out_end; ++it_out)
cout << v_index[target(*it_out, G)] + 1 << " ";
cout << "\n";
}
}
AdjacencyList_t
AM_2_AdjacencyList(bool *AM, unsigned int n)
{
AdjacencyList_t G(n);
property_map<AdjacencyList_t, vertex_index_t>::type v_index = get(vertex_index, G);
property_map<AdjacencyList_t, vertex_index1_t>::type v_index1 = get(vertex_index1, G);
for (unsigned int i = 0; i < n; i++)
{
put(v_index, vertex(i, G), i);
put(v_index1, vertex(i, G), i);
}
for (unsigned int i = 0; i < n; i++)
for (unsigned int j = 0; j < n; j++)
if (AM[i*n+j])
add_edge(vertex(j, G), vertex(i, G), G);
return G;
}
AdjacencyList_t
extract_subgraph(AdjacencyList_t &G1, set<int> select_index)
{
unsigned int n = select_index.size();
AdjacencyList_t G(n);
property_map<AdjacencyList_t, vertex_index_t>::type v_index = get(vertex_index, G);
property_map<AdjacencyList_t, vertex_index1_t>::type v_index1 = get(vertex_index1, G);
property_map<AdjacencyList_t, vertex_index_t>::type v1_index = get(vertex_index, G1);
map<int, int> reverse_index;
set<int>::iterator it;
unsigned int i;
for (it = select_index.begin(), i = 0; i < n; i++, ++it)
{
reverse_index[get(v1_index, vertex(*it, G1))] = i;
put(v_index, vertex(i, G), get(v1_index, vertex(*it, G1)));
put(v_index1, vertex(i, G), i);
}
for (it = select_index.begin(), i = 0; i < n; i++, ++it)
{
AdjacencyList_t::out_edge_iterator it_out, out_end;
AdjacencyList_t::vertex_descriptor vi = vertex(*it, G1);
for (tie(it_out, out_end) = out_edges(vi, G1); it_out != out_end; ++it_out)
{
int ii = v1_index[target(*it_out, G1)];
if (select_index.find(ii) != select_index.end())
add_edge(vertex(reverse_index[get(v1_index, source(*it_out, G1))], G), vertex(reverse_index[get(v1_index, target(*it_out, G1))], G), G);
}
}
return G;
}
vector_vertex_descriptor_t
Collect_Doublet(AdjacencyList_t::vertex_descriptor vertex, AdjacencyList_t &G)
{
AdjacencyList_t::in_edge_iterator it_in, in_end;
AdjacencyList_t::out_edge_iterator it_out, out_end;
vector<AdjacencyList_t::vertex_descriptor> Doublet;
if (in_degree(vertex, G) > 0 && out_degree(vertex, G) > 0)
for (tie(it_in, in_end) = in_edges(vertex, G); it_in != in_end; ++it_in)
for (tie(it_out, out_end) = out_edges(vertex, G); it_out != out_end; ++it_out)
if (source(*it_in, G) == target(*it_out, G) && source(*it_in, G) != target(*it_in, G)) // not a loop
Doublet.push_back(source(*it_in, G));
return Doublet;
}
bool
Vertex_Belong_to_a_Clique(AdjacencyList_t::vertex_descriptor vertex, AdjacencyList_t &G)
{
vector<AdjacencyList_t::vertex_descriptor> liste;
bool agree = true;
AdjacencyList_t::in_edge_iterator it_in, in_end;
AdjacencyList_t::out_edge_iterator it_out, out_end;
tie(it_in, in_end) = in_edges(vertex, G);
tie(it_out, out_end) = out_edges(vertex, G);
while (it_in != in_end && it_out != out_end && agree)
{
agree = (source(*it_in, G) == target(*it_out, G) && source(*it_in, G) != target(*it_in, G)); //not a loop
liste.push_back(source(*it_in, G));
++it_in;
++it_out;
}
if (agree)
{
if (it_in != in_end || it_out != out_end)
agree = false;
unsigned int i = 1;
while (i < liste.size() && agree)
{
unsigned int j = i + 1;
while (j < liste.size() && agree)
{
AdjacencyList_t::edge_descriptor ed;
bool exist1, exist2;
tie(ed, exist1) = edge(liste[i], liste[j], G);
tie(ed, exist2) = edge(liste[j], liste[i], G);
agree = (exist1 && exist2);
j++;
}
i++;
}
}
return agree;
}
bool
Elimination_of_Vertex_With_One_or_Less_Indegree_or_Outdegree_Step(AdjacencyList_t &G)
{
bool something_has_been_done = false;
bool not_a_loop;
int i;
AdjacencyList_t::vertex_iterator it, it1, ita, it_end;
for (tie(it, it_end) = vertices(G), i = 0; it != it_end; ++it, i++)
{
int in_degree_n = in_degree(*it, G);
int out_degree_n = out_degree(*it, G);
if (in_degree_n <= 1 || out_degree_n <= 1)
{
not_a_loop = true;
if (in_degree_n >= 1 && out_degree_n >= 1) // Do not eliminate a vertex if it loops on itself!
{
AdjacencyList_t::in_edge_iterator it_in, in_end;
for (tie(it_in, in_end) = in_edges(*it, G); it_in != in_end; ++it_in)
if (source(*it_in, G) == target(*it_in, G))
{
#ifdef verbose
cout << v_index[source(*it_in, G)] << " == " << v_index[target(*it_in, G)] << "\n";
#endif
not_a_loop = false;
}
}
if (not_a_loop)
{
#ifdef verbose
property_map<AdjacencyList_t, vertex_index_t>::type v_index = get(vertex_index, G);
cout << "->eliminate vertex[" << v_index[*it] + 1 << "]\n";
#endif
Eliminate(*it, G);
#ifdef verbose
Print(G);
#endif
something_has_been_done = true;
if (i > 0)
it = ita;
else
{
tie(it, it_end) = vertices(G);
i--;
}
}
}
ita = it;
}
return something_has_been_done;
}
bool
Elimination_of_Vertex_belonging_to_a_clique_Step(AdjacencyList_t &G)
{
AdjacencyList_t::vertex_iterator it, it1, ita, it_end;
bool something_has_been_done = false;
int i;
for (tie(it, it_end) = vertices(G), i = 0; it != it_end; ++it, i++)
{
if (Vertex_Belong_to_a_Clique(*it, G))
{
#ifdef verbose
property_map<AdjacencyList_t, vertex_index_t>::type v_index = get(vertex_index, G);
cout << "eliminate vertex[" << v_index[*it] + 1 << "]\n";
#endif
Eliminate(*it, G);
something_has_been_done = true;
if (i > 0)
it = ita;
else
{
tie(it, it_end) = vertices(G);
i--;
}
}
ita = it;
}
return something_has_been_done;
}
bool
Suppression_of_Vertex_X_if_it_loops_store_in_set_of_feedback_vertex_Step(set<int> &feed_back_vertices, AdjacencyList_t &G)
{
bool something_has_been_done = false;
AdjacencyList_t::vertex_iterator it, it_end, ita;
int i = 0;
for (tie(it, it_end) = vertices(G); it != it_end; ++it, i++)
{
AdjacencyList_t::edge_descriptor ed;
bool exist;
tie(ed, exist) = edge(*it, *it, G);
if (exist)
{
#ifdef verbose
property_map<AdjacencyList_t, vertex_index_t>::type v_index = get(vertex_index, G);
cout << "store v[*it] = " << v_index[*it]+1 << "\n";
#endif
property_map<AdjacencyList_t, vertex_index1_t>::type v_index1 = get(vertex_index1, G);
feed_back_vertices.insert(v_index1[*it]);
/*property_map<AdjacencyList_t, vertex_index_t>::type v_index = get(vertex_index, G);
feed_back_vertices.insert(v_index[*it] );*/
Suppress(*it, G);
something_has_been_done = true;
if (i > 0)
it = ita;
else
{
tie(it, it_end) = vertices(G);
i--;
}
}
ita = it;
}
return something_has_been_done;
}
AdjacencyList_t
Minimal_set_of_feedback_vertex(set<int> &feed_back_vertices, const AdjacencyList_t &G1)
{
bool something_has_been_done = true;
int cut_ = 0;
feed_back_vertices.clear();
AdjacencyList_t G(G1);
while (num_vertices(G) > 0)
{
while (something_has_been_done && num_vertices(G) > 0)
{
//Rule 1
something_has_been_done = (Elimination_of_Vertex_With_One_or_Less_Indegree_or_Outdegree_Step(G) /*or something_has_been_done*/);
#ifdef verbose
cout << "1 something_has_been_done=" << something_has_been_done << "\n";
#endif
//Rule 2
something_has_been_done = (Elimination_of_Vertex_belonging_to_a_clique_Step(G) || something_has_been_done);
#ifdef verbose
cout << "2 something_has_been_done=" << something_has_been_done << "\n";
#endif
//Rule 3
something_has_been_done = (Suppression_of_Vertex_X_if_it_loops_store_in_set_of_feedback_vertex_Step(feed_back_vertices, G) || something_has_been_done);
#ifdef verbose
cout << "3 something_has_been_done=" << something_has_been_done << "\n";
#endif
}
vector<int> circuit;
if (!has_cycle(circuit, G))
{
#ifdef verbose
cout << "has_cycle=false\n";
#endif
//sort(feed_back_vertices.begin(), feed_back_vertices.end());
return G;
}
if (num_vertices(G) > 0)
{
/*if nothing has been done in the five previous rule then cut the vertex with the maximum in_degree+out_degree*/
unsigned int max_degree = 0, num = 0;
AdjacencyList_t::vertex_iterator it, it_end, max_degree_index;
for (tie(it, it_end) = vertices(G); it != it_end; ++it, num++)
{
if (in_degree(*it, G) + out_degree(*it, G) > max_degree)
{
max_degree = in_degree(*it, G) + out_degree(*it, G);
max_degree_index = it;
}
}
property_map<AdjacencyList_t, vertex_index1_t>::type v_index1 = get(vertex_index1, G);
feed_back_vertices.insert(v_index1[*max_degree_index]);
/*property_map<AdjacencyList_t, vertex_index_t>::type v_index = get(vertex_index, G);
feed_back_vertices.insert(v_index[*max_degree_index]);*/
//cout << "v_index1[*max_degree_index] = " << v_index1[*max_degree_index] << "\n";
cut_++;
#ifdef verbose
property_map<AdjacencyList_t, vertex_index_t>::type v_index = get(vertex_index, G);
cout << "--> cut vertex " << v_index[*max_degree_index] + 1 << "\n";
#endif
Suppress(*max_degree_index, G);
something_has_been_done = true;
}
}
#ifdef verbose
cout << "cut_=" << cut_ << "\n";
#endif
//sort(feed_back_vertices.begin(), feed_back_vertices.end());
return G;
}
struct rev
{
bool
operator()(const int a, const int b) const
{
return (a > b);
}
};
void
Reorder_the_recursive_variables(const AdjacencyList_t &G1, set<int> &feedback_vertices, vector< int> &Reordered_Vertices)
{
AdjacencyList_t G(G1);
property_map<AdjacencyList_t, vertex_index_t>::type v_index = get(vertex_index, G);
set<int>::iterator its, ita;
set<int, rev> fv;
for (its = feedback_vertices.begin(); its != feedback_vertices.end(); its++)
fv.insert(*its);
int i = 0;
for (its = fv.begin(); its != fv.end(); ++its, i++)
Suppress(*its, G);
bool something_has_been_done = true;
while (something_has_been_done)
{
something_has_been_done = false;
AdjacencyList_t::vertex_iterator it, it_end, ita;
for (tie(it, it_end) = vertices(G), i = 0; it != it_end; ++it, i++)
{
if (in_degree(*it, G) == 0)
{
Reordered_Vertices.push_back(v_index[*it]);
Suppress(*it, G);
something_has_been_done = true;
if (i > 0)
it = ita;
else
{
tie(it, it_end) = vertices(G);
i--;
}
}
ita = it;
}
}
if (num_vertices(G))
cout << "Error in the computation of feedback vertex set\n";
}
}
/*
* Copyright (C) 2009-2010 Dynare Team
*
* This file is part of Dynare.
*
* Dynare is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Dynare is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Dynare. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _MINIMUMFEEDBACKSET_HH
#define _MINIMUMFEEDBACKSET_HH
#include <map>
#include <vector>
#include <boost/graph/adjacency_list.hpp>
using namespace std;
using namespace boost;
namespace MFS
{
typedef property<vertex_index_t, int,
property<vertex_index1_t, int,
property<vertex_degree_t, int,
property<vertex_in_degree_t, int,
property<vertex_out_degree_t, int > > > > > VertexProperty_t;
typedef adjacency_list<listS, listS, bidirectionalS, VertexProperty_t> AdjacencyList_t;
typedef map<graph_traits<AdjacencyList_t>::vertex_descriptor, default_color_type> color_t;
typedef vector<AdjacencyList_t::vertex_descriptor> vector_vertex_descriptor_t;
//! Eliminate a vertex i
/*! For a vertex i replace all edges e_k_i and e_i_j by a shorcut e_k_j and then Suppress the vertex i*/
void Eliminate(AdjacencyList_t::vertex_descriptor vertex_to_eliminate, AdjacencyList_t &G);
//! Collect all doublets (edges e_i_k such that there is an edge e_k_i with k!=i in the graph)
/*! Returns the vector of doublets */
vector_vertex_descriptor_t Collect_Doublet(AdjacencyList_t::vertex_descriptor vertex, AdjacencyList_t &G);
//! Detect all the clique (all vertex in a clique are related to each other) in the graph
bool Vertex_Belong_to_a_Clique(AdjacencyList_t::vertex_descriptor vertex, AdjacencyList_t &G);
//! Graph reduction: eliminating purely intermediate variables or variables outside of any circuit
bool Elimination_of_Vertex_With_One_or_Less_Indegree_or_Outdegree_Step(AdjacencyList_t &G);
//! Graph reduction: elimination of a vertex inside a clique
bool Elimination_of_Vertex_belonging_to_a_clique_Step(AdjacencyList_t &G);
//! A vertex belong to the feedback vertex set if the vertex loops on itself.
/*! We have to suppress this vertex and store it into the feedback set.*/
bool Suppression_of_Vertex_X_if_it_loops_store_in_set_of_feedback_vertex_Step(set<int> &feed_back_vertices, AdjacencyList_t &G1);
//! Print the Graph
void Print(AdjacencyList_t &G);
//! Create an adjacency graph from a Adjacency Matrix (an incidence Matrix without the diagonal terms)
AdjacencyList_t AM_2_AdjacencyList(bool *AMp, unsigned int n);
//! Extracts a subgraph
/*!
\param[in] G1 The original graph
\param[in] select_index The vertex indices to select
\return The subgraph
The property vertex_index of the subgraph contains indices of the original
graph, the property vertex_index1 contains new contiguous indices specific
to the subgraph.
*/
AdjacencyList_t extract_subgraph(AdjacencyList_t &G1, set<int> select_index);
//! Check if the graph contains any cycle (true if the model contains at least one cycle, false otherwise)
bool has_cycle(vector<int> &circuit_stack, AdjacencyList_t &g);
bool has_cycle_dfs(AdjacencyList_t &g, AdjacencyList_t::vertex_descriptor u, color_t &color, vector<int> &circuit_stack);
//! Return the feedback set
AdjacencyList_t Minimal_set_of_feedback_vertex(set<int> &feed_back_vertices, const AdjacencyList_t &G);
//! Clear all in and out edges of vertex_to_eliminate and remove vertex_to_eliminate from the graph
void Suppress(AdjacencyList_t::vertex_descriptor vertex_to_eliminate, AdjacencyList_t &G);
void Suppress(int vertex_num, AdjacencyList_t &G);
//! Reorder the recursive variables
/*! They appear first in a quasi triangular form and they are followed by the feedback variables */
void Reorder_the_recursive_variables(const AdjacencyList_t &G1, set<int> &feedback_vertices, vector< int> &Reordered_Vertices);
};
#endif // _MINIMUMFEEDBACKSET_HH
/* /*
* Copyright (C) 2006-2018 Dynare Team * Copyright © 2006-2025 Dynare Team
* *
* This file is part of Dynare. * This file is part of Dynare.
* *
...@@ -14,114 +14,135 @@ ...@@ -14,114 +14,135 @@
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Dynare. If not, see <http://www.gnu.org/licenses/>. * along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include <cassert>
#include <cstdlib> #include <cstdlib>
#include <iostream>
#include <fstream> #include <fstream>
#include <iostream>
#include <random>
#include <typeinfo> #include <typeinfo>
#include <cassert>
#ifndef _WIN32
# include <unistd.h>
#endif
#include "ModFile.hh" #include <filesystem>
#include "ConfigFile.hh"
#include "ComputingTasks.hh"
ModFile::ModFile(WarningConsolidation &warnings_arg) #include "ComputingTasks.hh"
: expressions_tree(symbol_table, num_constants, external_functions_table), #include "ModFile.hh"
original_model(symbol_table, num_constants, external_functions_table), #include "Shocks.hh"
dynamic_model(symbol_table, num_constants, external_functions_table),
trend_dynamic_model(symbol_table, num_constants, external_functions_table), ModFile::ModFile(WarningConsolidation& warnings_arg) :
ramsey_FOC_equations_dynamic_model(symbol_table, num_constants, external_functions_table), symbol_table {heterogeneity_table},
orig_ramsey_dynamic_model(symbol_table, num_constants, external_functions_table), var_model_table {symbol_table},
static_model(symbol_table, num_constants, external_functions_table), trend_component_model_table {symbol_table},
steady_state_model(symbol_table, num_constants, external_functions_table, static_model), var_expectation_model_table {symbol_table},
linear(false), block(false), byte_code(false), use_dll(false), no_static(false), pac_model_table {symbol_table},
differentiate_forward_vars(false), nonstationary_variables(false), expressions_tree {symbol_table, num_constants, external_functions_table, heterogeneity_table},
param_used_with_lead_lag(false), warnings(warnings_arg) original_model {symbol_table,
{ num_constants,
} external_functions_table,
heterogeneity_table,
ModFile::~ModFile() trend_component_model_table,
{ var_model_table},
for (vector<Statement *>::iterator it = statements.begin(); dynamic_model {symbol_table,
it != statements.end(); it++) num_constants,
delete (*it); external_functions_table,
heterogeneity_table,
trend_component_model_table,
var_model_table},
trend_dynamic_model {symbol_table,
num_constants,
external_functions_table,
heterogeneity_table,
trend_component_model_table,
var_model_table},
orig_ramsey_dynamic_model {symbol_table,
num_constants,
external_functions_table,
heterogeneity_table,
trend_component_model_table,
var_model_table},
epilogue {symbol_table,
num_constants,
external_functions_table,
heterogeneity_table,
trend_component_model_table,
var_model_table},
static_model {symbol_table, num_constants, external_functions_table, heterogeneity_table},
steady_state_model {symbol_table, num_constants, external_functions_table, heterogeneity_table,
static_model},
warnings {warnings_arg}
{
heterogeneity_table.setSymbolTable(&symbol_table);
} }
void void
ModFile::evalAllExpressions(bool warn_uninit, const bool nopreprocessoroutput) ModFile::evalAllExpressions(bool warn_uninit)
{ {
if (!nopreprocessoroutput) cout << "Evaluating expressions..." << endl;
cout << "Evaluating expressions...";
// Loop over all statements, and fill global eval context if relevant // Loop over all statements, and fill global eval context if relevant
for (vector<Statement *>::const_iterator it = statements.begin(); it != statements.end(); it++) for (auto& st : statements)
{ {
InitParamStatement *ips = dynamic_cast<InitParamStatement *>(*it); if (auto ips = dynamic_cast<InitParamStatement*>(st.get()); ips)
if (ips)
ips->fillEvalContext(global_eval_context); ips->fillEvalContext(global_eval_context);
InitOrEndValStatement *ies = dynamic_cast<InitOrEndValStatement *>(*it); if (auto ies = dynamic_cast<InitOrEndValStatement*>(st.get()); ies)
if (ies)
ies->fillEvalContext(global_eval_context); ies->fillEvalContext(global_eval_context);
LoadParamsAndSteadyStateStatement *lpass = dynamic_cast<LoadParamsAndSteadyStateStatement *>(*it); if (auto lpass = dynamic_cast<LoadParamsAndSteadyStateStatement*>(st.get()); lpass)
if (lpass)
lpass->fillEvalContext(global_eval_context); lpass->fillEvalContext(global_eval_context);
} }
// Evaluate model local variables // Evaluate model local variables
dynamic_model.fillEvalContext(global_eval_context); dynamic_model.fillEvalContext(global_eval_context);
if (!nopreprocessoroutput)
cout << "done" << endl;
// Check if some symbols are not initialized, and give them a zero value then // Check if some symbols are not initialized, and give them a zero value then
for (int id = 0; id <= symbol_table.maxID(); id++) for (int id = 0; id <= symbol_table.maxID(); id++)
{ if (auto type = symbol_table.getType(id);
SymbolType type = symbol_table.getType(id); (type == SymbolType::endogenous || type == SymbolType::exogenous
if ((type == eEndogenous || type == eExogenous || type == eExogenousDet || type == SymbolType::exogenousDet || type == SymbolType::parameter
|| type == eParameter || type == eModelLocalVariable) || type == SymbolType::modelLocalVariable)
&& global_eval_context.find(id) == global_eval_context.end()) && !global_eval_context.contains(id))
{ {
if (warn_uninit) if (warn_uninit)
warnings << "WARNING: Can't find a numeric initial value for " warnings << "WARNING: Can't find a numeric initial value for " << symbol_table.getName(id)
<< symbol_table.getName(id) << ", using zero" << endl; << ", using zero" << endl;
global_eval_context[id] = 0; global_eval_context[id] = 0;
} }
} }
}
void void
ModFile::addStatement(Statement *st) ModFile::addStatement(unique_ptr<Statement> st)
{ {
statements.push_back(st); statements.push_back(move(st));
} }
void void
ModFile::addStatementAtFront(Statement *st) ModFile::addStatementAtFront(unique_ptr<Statement> st)
{ {
statements.insert(statements.begin(), st); statements.insert(statements.begin(), move(st));
} }
void void
ModFile::checkPass(bool nostrict, bool stochastic) ModFile::checkPass(bool nostrict, bool stochastic)
{ {
for (vector<Statement *>::iterator it = statements.begin(); for (auto& statement : statements)
it != statements.end(); it++) statement->checkPass(mod_file_struct, warnings);
(*it)->checkPass(mod_file_struct, warnings);
// Check the steady state block // Check the steady state block
steady_state_model.checkPass(mod_file_struct, warnings); steady_state_model.checkPass(mod_file_struct, warnings);
if (mod_file_struct.write_latex_steady_state_model_present && // Check epilogue block
!mod_file_struct.steady_state_model_present) epilogue.checkPass(mod_file_struct);
pac_model_table.checkPass(mod_file_struct);
if (mod_file_struct.write_latex_steady_state_model_present
&& !mod_file_struct.steady_state_model_present)
{ {
cerr << "ERROR: You cannot have a write_latex_steady_state_model statement without a steady_state_model block." << endl; cerr << "ERROR: You cannot have a write_latex_steady_state_model statement without a "
"steady_state_model block."
<< endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
...@@ -133,28 +154,28 @@ ModFile::checkPass(bool nostrict, bool stochastic) ...@@ -133,28 +154,28 @@ ModFile::checkPass(bool nostrict, bool stochastic)
if (param_used_with_lead_lag) if (param_used_with_lead_lag)
warnings << "WARNING: A parameter was used with a lead or a lag in the model block" << endl; warnings << "WARNING: A parameter was used with a lead or a lag in the model block" << endl;
bool stochastic_statement_present = mod_file_struct.stoch_simul_present bool stochastic_statement_present
|| mod_file_struct.estimation_present = mod_file_struct.stoch_simul_present || mod_file_struct.estimation_present
|| mod_file_struct.osr_present || mod_file_struct.osr_present || mod_file_struct.discretionary_policy_present
|| mod_file_struct.ramsey_policy_present || mod_file_struct.calib_smoother_present || mod_file_struct.identification_present
|| mod_file_struct.discretionary_policy_present || mod_file_struct.mom_estimation_present || mod_file_struct.sensitivity_present
|| mod_file_struct.calib_smoother_present
|| stochastic; || stochastic;
// Allow empty model only when doing a standalone BVAR estimation // Allow empty model only when doing a standalone BVAR estimation
if (dynamic_model.equation_number() == 0 if (dynamic_model.equation_number() == 0
&& (mod_file_struct.check_present && (mod_file_struct.check_present || mod_file_struct.perfect_foresight_solver_present
|| mod_file_struct.perfect_foresight_solver_present || mod_file_struct.perfect_foresight_with_expectation_errors_solver_present
|| stochastic_statement_present)) || stochastic_statement_present))
{ {
cerr << "ERROR: At least one model equation must be declared!" << endl; cerr << "ERROR: At least one model equation must be declared!" << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if ((mod_file_struct.ramsey_model_present || mod_file_struct.ramsey_policy_present) if (mod_file_struct.ramsey_model_present && mod_file_struct.discretionary_policy_present)
&& mod_file_struct.discretionary_policy_present)
{ {
cerr << "ERROR: You cannot use the discretionary_policy command when you use either ramsey_model or ramsey_policy and vice versa" << endl; cerr << "ERROR: You cannot use the discretionary_policy command when you use either "
"ramsey_model or ramsey_policy and vice versa"
<< endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
...@@ -163,60 +184,70 @@ ModFile::checkPass(bool nostrict, bool stochastic) ...@@ -163,60 +184,70 @@ ModFile::checkPass(bool nostrict, bool stochastic)
|| (!(mod_file_struct.ramsey_model_present || mod_file_struct.discretionary_policy_present) || (!(mod_file_struct.ramsey_model_present || mod_file_struct.discretionary_policy_present)
&& mod_file_struct.planner_objective_present)) && mod_file_struct.planner_objective_present))
{ {
cerr << "ERROR: A planner_objective statement must be used with a ramsey_model, a ramsey_policy or a discretionary_policy statement and vice versa." << endl; cerr << "ERROR: A planner_objective statement must be used with a ramsey_model, a "
exit(EXIT_FAILURE); "ramsey_policy or a discretionary_policy statement and vice versa."
} << endl;
if ((mod_file_struct.osr_present && (!mod_file_struct.osr_params_present || !mod_file_struct.optim_weights_present))
|| ((!mod_file_struct.osr_present || !mod_file_struct.osr_params_present) && mod_file_struct.optim_weights_present)
|| ((!mod_file_struct.osr_present || !mod_file_struct.optim_weights_present) && mod_file_struct.osr_params_present))
{
cerr << "ERROR: The osr statement must be used with osr_params and optim_weights." << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (mod_file_struct.perfect_foresight_solver_present && stochastic_statement_present) if (!ramsey_constraints.empty() && !mod_file_struct.ramsey_model_present)
{ {
cerr << "ERROR: A .mod file cannot contain both one of {perfect_foresight_solver,simul} and one of {stoch_simul, estimation, osr, ramsey_policy, discretionary_policy}. This is not possible: one cannot mix perfect foresight context with stochastic context in the same file." << endl; cerr << "ERROR: A ramsey_constraints block requires the presence of a ramsey_model or "
"ramsey_policy statement"
<< endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (mod_file_struct.k_order_solver && byte_code) if ((mod_file_struct.osr_present
&& (!mod_file_struct.osr_params_present || !mod_file_struct.optim_weights_present))
|| ((!mod_file_struct.osr_present || !mod_file_struct.osr_params_present)
&& mod_file_struct.optim_weights_present)
|| ((!mod_file_struct.osr_present || !mod_file_struct.optim_weights_present)
&& mod_file_struct.osr_params_present))
{ {
cerr << "ERROR: 'k_order_solver' (which is implicit if order >= 3), is not yet compatible with 'bytecode'." << endl; cerr << "ERROR: The osr statement must be used with osr_params and optim_weights." << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (use_dll && (block || byte_code)) if ((mod_file_struct.perfect_foresight_solver_present
|| mod_file_struct.perfect_foresight_with_expectation_errors_solver_present)
&& stochastic_statement_present)
{ {
cerr << "ERROR: In 'model' block, 'use_dll' option is not compatible with 'block' or 'bytecode'" << endl; cerr << "ERROR: A .mod file cannot contain both one of {perfect_foresight_solver, simul, "
"perfect_foresight_with_expectation_errors_solver} and one of {stoch_simul, "
"estimation, osr, ramsey_policy, discretionary_policy}. This is not possible: one "
"cannot mix perfect foresight context with stochastic context in the same file."
<< endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (block || byte_code) if (use_dll && bytecode)
if (dynamic_model.isModelLocalVariableUsed())
{ {
cerr << "ERROR: In 'model' block, 'block' or 'bytecode' options are not yet compatible with pound expressions" << endl; cerr << "ERROR: In 'model' block, 'use_dll' option is not compatible with 'bytecode'" << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if ((stochastic_statement_present || mod_file_struct.check_present || mod_file_struct.steady_present) && no_static) if ((stochastic_statement_present || mod_file_struct.check_present
|| mod_file_struct.steady_present)
&& no_static)
{ {
cerr << "ERROR: no_static option is incompatible with stoch_simul, estimation, osr, ramsey_policy, discretionary_policy, steady and check commands" << endl; cerr << "ERROR: no_static option is incompatible with stoch_simul, estimation, osr, "
"ramsey_policy, discretionary_policy, steady and check commands"
<< endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (mod_file_struct.dsge_var_estimated) if (mod_file_struct.dsge_var_estimated && !mod_file_struct.dsge_prior_weight_in_estimated_params)
if (!mod_file_struct.dsge_prior_weight_in_estimated_params)
{ {
cerr << "ERROR: When estimating a DSGE-VAR model and estimating the weight of the prior, dsge_prior_weight must " cerr << "ERROR: When estimating a DSGE-VAR model and estimating the weight of the prior, "
"dsge_prior_weight must "
<< "be referenced in the estimated_params block." << endl; << "be referenced in the estimated_params block." << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (symbol_table.exists("dsge_prior_weight")) if (symbol_table.exists("dsge_prior_weight"))
{ {
if (symbol_table.getType("dsge_prior_weight") != eParameter) if (symbol_table.getType("dsge_prior_weight") != SymbolType::parameter)
{ {
cerr << "ERROR: dsge_prior_weight may only be used as a parameter." << endl; cerr << "ERROR: dsge_prior_weight may only be used as a parameter." << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
...@@ -228,19 +259,23 @@ ModFile::checkPass(bool nostrict, bool stochastic) ...@@ -228,19 +259,23 @@ ModFile::checkPass(bool nostrict, bool stochastic)
if (mod_file_struct.dsge_var_estimated || !mod_file_struct.dsge_var_calibrated.empty()) if (mod_file_struct.dsge_var_estimated || !mod_file_struct.dsge_var_calibrated.empty())
{ {
cerr << "ERROR: dsge_prior_weight can either be declared as a parameter (deprecated) or via the dsge_var option " cerr << "ERROR: dsge_prior_weight can either be declared as a parameter (deprecated) or "
"via the dsge_var option "
<< "to the estimation statement (preferred), but not both." << endl; << "to the estimation statement (preferred), but not both." << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (!mod_file_struct.dsge_prior_weight_initialized && !mod_file_struct.dsge_prior_weight_in_estimated_params) if (!mod_file_struct.dsge_prior_weight_initialized
&& !mod_file_struct.dsge_prior_weight_in_estimated_params)
{ {
cerr << "ERROR: If dsge_prior_weight is declared as a parameter, it must either be initialized or placed in the " cerr << "ERROR: If dsge_prior_weight is declared as a parameter, it must either be "
"initialized or placed in the "
<< "estimated_params block." << endl; << "estimated_params block." << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (mod_file_struct.dsge_prior_weight_initialized && mod_file_struct.dsge_prior_weight_in_estimated_params) if (mod_file_struct.dsge_prior_weight_initialized
&& mod_file_struct.dsge_prior_weight_in_estimated_params)
{ {
cerr << "ERROR: dsge_prior_weight cannot be both initialized and estimated." << endl; cerr << "ERROR: dsge_prior_weight cannot be both initialized and estimated." << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
...@@ -248,119 +283,349 @@ ModFile::checkPass(bool nostrict, bool stochastic) ...@@ -248,119 +283,349 @@ ModFile::checkPass(bool nostrict, bool stochastic)
} }
if (mod_file_struct.dsge_prior_weight_in_estimated_params) if (mod_file_struct.dsge_prior_weight_in_estimated_params)
{
if (!mod_file_struct.dsge_var_estimated && !mod_file_struct.dsge_var_calibrated.empty()) if (!mod_file_struct.dsge_var_estimated && !mod_file_struct.dsge_var_calibrated.empty())
{ {
cerr << "ERROR: If dsge_prior_weight is in the estimated_params block, the prior weight cannot be calibrated " cerr << "ERROR: If dsge_prior_weight is in the estimated_params block, the prior weight "
"cannot be calibrated "
<< "via the dsge_var option in the estimation statement." << endl; << "via the dsge_var option in the estimation statement." << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
else if (!mod_file_struct.dsge_var_estimated && !symbol_table.exists("dsge_prior_weight")) else if (!mod_file_struct.dsge_var_estimated && !symbol_table.exists("dsge_prior_weight"))
{ {
cerr << "ERROR: If dsge_prior_weight is in the estimated_params block, it must either be declared as a parameter " cerr << "ERROR: If dsge_prior_weight is in the estimated_params block, it must either be "
<< "(deprecated) or the dsge_var option must be passed to the estimation statement (preferred)." << endl; "declared as a parameter "
<< "(deprecated) or the dsge_var option must be passed to the estimation statement "
"(preferred)."
<< endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
}
/* This check must come before checking that there are as many static-only as dynamic-only
equations, see #103 */
dynamic_model.checkOccbinRegimes();
if (dynamic_model.staticOnlyEquationsNbr() != dynamic_model.dynamicOnlyEquationsNbr()) if (dynamic_model.staticOnlyEquationsNbr() != dynamic_model.dynamicOnlyEquationsNbr())
{ {
cerr << "ERROR: the number of equations marked [static] must be equal to the number of equations marked [dynamic]" << endl; cerr << "ERROR: the number of equations marked [static] must be equal to the number of "
"equations marked [dynamic]"
<< endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (dynamic_model.staticOnlyEquationsNbr() > 0 if (dynamic_model.staticOnlyEquationsNbr() > 0
&& (mod_file_struct.ramsey_model_present || mod_file_struct.discretionary_policy_present)) && (mod_file_struct.ramsey_model_present || mod_file_struct.discretionary_policy_present))
{ {
cerr << "ERROR: marking equations as [static] or [dynamic] is not possible with ramsey_model, ramsey_policy or discretionary_policy" << endl; cerr << "ERROR: marking equations as [static] or [dynamic] is not possible with "
"ramsey_model, ramsey_policy or discretionary_policy"
<< endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (stochastic_statement_present if (stochastic_statement_present
&& (dynamic_model.isUnaryOpUsed(oSign) && (dynamic_model.isUnaryOpUsed(UnaryOpcode::sign)
|| dynamic_model.isUnaryOpUsed(oAbs) || dynamic_model.isUnaryOpUsed(UnaryOpcode::abs)
|| dynamic_model.isBinaryOpUsed(oMax) || dynamic_model.isBinaryOpUsed(BinaryOpcode::max)
|| dynamic_model.isBinaryOpUsed(oMin) || dynamic_model.isBinaryOpUsed(BinaryOpcode::min)
|| dynamic_model.isBinaryOpUsed(oGreater) || dynamic_model.isBinaryOpUsed(BinaryOpcode::greater)
|| dynamic_model.isBinaryOpUsed(oLess) || dynamic_model.isBinaryOpUsed(BinaryOpcode::less)
|| dynamic_model.isBinaryOpUsed(oGreaterEqual) || dynamic_model.isBinaryOpUsed(BinaryOpcode::greaterEqual)
|| dynamic_model.isBinaryOpUsed(oLessEqual) || dynamic_model.isBinaryOpUsed(BinaryOpcode::lessEqual)
|| dynamic_model.isBinaryOpUsed(oEqualEqual) || dynamic_model.isBinaryOpUsed(BinaryOpcode::equalEqual)
|| dynamic_model.isBinaryOpUsed(oDifferent))) || dynamic_model.isBinaryOpUsed(BinaryOpcode::different)))
warnings << "WARNING: you are using a function (max, min, abs, sign) or an operator (<, >, <=, >=, ==, !=) which is unsuitable for a stochastic context; see the reference manual, section about \"Expressions\", for more details." << endl; warnings
<< R"(WARNING: you are using a function (max, min, abs, sign) or an operator (<, >, <=, >=, ==, !=) which is unsuitable for a stochastic context; see the reference manual, section about "Expressions", for more details.)"
<< endl;
if (linear if (linear
&& (dynamic_model.isUnaryOpUsed(oSign) && (dynamic_model.isUnaryOpUsedOnType(SymbolType::endogenous, UnaryOpcode::sign)
|| dynamic_model.isUnaryOpUsed(oAbs) || dynamic_model.isUnaryOpUsedOnType(SymbolType::endogenous, UnaryOpcode::abs)
|| dynamic_model.isBinaryOpUsed(oMax) || dynamic_model.isBinaryOpUsedOnType(SymbolType::endogenous, BinaryOpcode::max)
|| dynamic_model.isBinaryOpUsed(oMin) || dynamic_model.isBinaryOpUsedOnType(SymbolType::endogenous, BinaryOpcode::min)
|| dynamic_model.isBinaryOpUsed(oGreater) || dynamic_model.isBinaryOpUsedOnType(SymbolType::endogenous, BinaryOpcode::greater)
|| dynamic_model.isBinaryOpUsed(oLess) || dynamic_model.isBinaryOpUsedOnType(SymbolType::endogenous, BinaryOpcode::less)
|| dynamic_model.isBinaryOpUsed(oGreaterEqual) || dynamic_model.isBinaryOpUsedOnType(SymbolType::endogenous, BinaryOpcode::greaterEqual)
|| dynamic_model.isBinaryOpUsed(oLessEqual) || dynamic_model.isBinaryOpUsedOnType(SymbolType::endogenous, BinaryOpcode::lessEqual)
|| dynamic_model.isBinaryOpUsed(oEqualEqual) || dynamic_model.isBinaryOpUsedOnType(SymbolType::endogenous, BinaryOpcode::equalEqual)
|| dynamic_model.isBinaryOpUsed(oDifferent))) || dynamic_model.isBinaryOpUsedOnType(SymbolType::endogenous, BinaryOpcode::different)))
warnings << "WARNING: you have declared your model 'linear' but you are using a function (max, min, abs, sign) or an operator (<, >, <=, >=, ==, !=) which potentially makes it non-linear." << endl; {
cerr << "ERROR: you have declared your model 'linear' but you are using a function "
<< "(max, min, abs, sign) or an operator (<, >, <=, >=, ==, !=) on an "
<< "endogenous variable." << endl;
exit(EXIT_FAILURE);
}
if (linear && !mod_file_struct.perfect_foresight_solver_present
&& !mod_file_struct.perfect_foresight_with_expectation_errors_solver_present
&& (dynamic_model.isUnaryOpUsedOnType(SymbolType::exogenous, UnaryOpcode::sign)
|| dynamic_model.isUnaryOpUsedOnType(SymbolType::exogenous, UnaryOpcode::abs)
|| dynamic_model.isBinaryOpUsedOnType(SymbolType::exogenous, BinaryOpcode::max)
|| dynamic_model.isBinaryOpUsedOnType(SymbolType::exogenous, BinaryOpcode::min)
|| dynamic_model.isBinaryOpUsedOnType(SymbolType::exogenous, BinaryOpcode::greater)
|| dynamic_model.isBinaryOpUsedOnType(SymbolType::exogenous, BinaryOpcode::less)
|| dynamic_model.isBinaryOpUsedOnType(SymbolType::exogenous, BinaryOpcode::greaterEqual)
|| dynamic_model.isBinaryOpUsedOnType(SymbolType::exogenous, BinaryOpcode::lessEqual)
|| dynamic_model.isBinaryOpUsedOnType(SymbolType::exogenous, BinaryOpcode::equalEqual)
|| dynamic_model.isBinaryOpUsedOnType(SymbolType::exogenous, BinaryOpcode::different)))
{
cerr << "ERROR: you have declared your model 'linear' but you are using a function "
<< "(max, min, abs, sign) or an operator (<, >, <=, >=, ==, !=) on an "
<< "exogenous variable in a non-perfect-foresight context." << endl;
exit(EXIT_FAILURE);
}
// Test if some estimated parameters are used within the values of shocks // Test if some estimated parameters are used within the values of shocks
// statements (see issue #469) // statements (see issue #469)
set<int> parameters_intersect; set<int> parameters_intersect;
set_intersection(mod_file_struct.parameters_within_shocks_values.begin(), ranges::set_intersection(mod_file_struct.parameters_within_shocks_values,
mod_file_struct.parameters_within_shocks_values.end(), mod_file_struct.estimated_parameters,
mod_file_struct.estimated_parameters.begin(),
mod_file_struct.estimated_parameters.end(),
inserter(parameters_intersect, parameters_intersect.begin())); inserter(parameters_intersect, parameters_intersect.begin()));
if (parameters_intersect.size() > 0) if (parameters_intersect.size() > 0)
{ {
cerr << "ERROR: some estimated parameters ("; cerr << "ERROR: some estimated parameters (";
for (set<int>::const_iterator it = parameters_intersect.begin(); for (bool printed_something {false}; int symb_id : parameters_intersect)
it != parameters_intersect.end();)
{ {
cerr << symbol_table.getName(*it); if (exchange(printed_something, true))
if (++it != parameters_intersect.end())
cerr << ", "; cerr << ", ";
cerr << symbol_table.getName(symb_id);
} }
cerr << ") also appear in the expressions defining the variance/covariance matrix of shocks; this is not allowed." << endl; cerr << ") also appear in the expressions defining the variance/covariance matrix of shocks; "
"this is not allowed."
<< endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
// Check if some exogenous is not used in the model block, Issue #841 // Check if some exogenous is not used in the model block, Issue #841
set<int> unusedExo = dynamic_model.findUnusedExogenous(); set<int> unusedExo0 = dynamic_model.findUnusedExogenous();
set<int> unusedExo;
ranges::set_difference(unusedExo0, mod_file_struct.pac_params,
inserter(unusedExo, unusedExo.begin()));
if (unusedExo.size() > 0) if (unusedExo.size() > 0)
{ {
ostringstream unused_exos; ostringstream unused_exos;
for (set<int>::iterator it = unusedExo.begin(); it != unusedExo.end(); it++) for (int it : unusedExo)
unused_exos << symbol_table.getName(*it) << " "; unused_exos << symbol_table.getName(it) << " ";
if (nostrict) if (nostrict)
warnings << "WARNING: " << unused_exos.str() warnings << "WARNING: " << unused_exos.str()
<< "not used in model block, removed by nostrict command-line option" << endl; << "not used in model block, removed by nostrict command-line option" << endl;
else else
{ {
cerr << "ERROR: " << unused_exos.str() << "not used in model block. To bypass this error, use the `nostrict` option. This may lead to crashes or unexpected behavior." << endl; cerr << "ERROR: " << unused_exos.str()
<< "not used in model block. To bypass this error, use the `nostrict` option. This "
"may lead to crashes or unexpected behavior."
<< endl;
exit(EXIT_FAILURE);
}
}
if (!heterogeneity_table.empty())
{
if (block)
{
cerr << "ERROR: the 'block' option of the 'model' block is not supported for "
"heterogeneous models"
<< endl;
exit(EXIT_FAILURE);
}
if (mod_file_struct.check_present)
{
cerr << "ERROR: The 'check' command is not supported for heterogeneous models" << endl;
exit(EXIT_FAILURE);
}
if (mod_file_struct.steady_present)
{
cerr << "ERROR: The 'steady' command is not supported for heterogeneous models" << endl;
exit(EXIT_FAILURE);
}
if (mod_file_struct.perfect_foresight_solver_present)
{
cerr << "ERROR: The 'perfect_foresight_solver' command is not supported for "
"heterogeneous models"
<< endl;
exit(EXIT_FAILURE);
}
if (mod_file_struct.perfect_foresight_with_expectation_errors_solver_present)
{
cerr << "ERROR: The 'perfect_foresight_with_expectation_errors_solver' command is not "
"supported for heterogeneous models"
<< endl;
exit(EXIT_FAILURE);
}
if (mod_file_struct.stoch_simul_present)
{
cerr << "ERROR: The 'stoch_simul' command is not supported for heterogeneous models"
<< endl;
exit(EXIT_FAILURE);
}
if (mod_file_struct.estimation_present)
{
cerr << "ERROR: The 'estimation' command is not supported for heterogeneous models"
<< endl;
exit(EXIT_FAILURE);
}
if (mod_file_struct.osr_present)
{
cerr << "ERROR: The 'osr' command is not supported for heterogeneous models" << endl;
exit(EXIT_FAILURE);
}
if (mod_file_struct.osr_params_present)
{
cerr << "ERROR: The 'osr_params' command is not supported for heterogeneous models"
<< endl;
exit(EXIT_FAILURE);
}
if (mod_file_struct.optim_weights_present)
{
cerr << "ERROR: The 'optim_weights' block is not supported for heterogeneous models"
<< endl;
exit(EXIT_FAILURE);
}
if (mod_file_struct.ramsey_model_present)
{
cerr << "ERROR: The 'ramsey_model' command is not supported for heterogeneous models"
<< endl;
exit(EXIT_FAILURE);
}
if (mod_file_struct.discretionary_policy_present)
{
cerr << "ERROR: The 'discretionary_policy' command is not supported for heterogeneous "
"models"
<< endl;
exit(EXIT_FAILURE);
}
if (mod_file_struct.planner_objective_present)
{
cerr << "ERROR: The 'planner_objective' command is not supported for heterogeneous models"
<< endl;
exit(EXIT_FAILURE);
}
if (mod_file_struct.extended_path_present)
{
cerr << "ERROR: The 'extended_path' command is not supported for heterogeneous models"
<< endl;
exit(EXIT_FAILURE);
}
if (mod_file_struct.identification_present)
{
cerr << "ERROR: The 'identification' command is not supported for heterogeneous models"
<< endl;
exit(EXIT_FAILURE);
}
if (mod_file_struct.sensitivity_present)
{
cerr << "ERROR: The 'sensitivity' command is not supported for heterogeneous models"
<< endl;
exit(EXIT_FAILURE);
}
if (mod_file_struct.mom_estimation_present)
{
cerr
<< "ERROR: The 'methods_of_moments' command is not supported for heterogeneous models"
<< endl;
exit(EXIT_FAILURE);
}
if (mod_file_struct.occbin_constraints_present)
{
cerr << "ERROR: The 'occbin_constraints' block is not supported for heterogeneous models"
<< endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
} }
void void
ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, const bool nopreprocessoroutput) ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, bool transform_unary_ops,
{ const string& exclude_eqs, const string& include_eqs)
// Save the original model (must be done before any model transformations by preprocessor) {
/* Save the original model (must be done before any model transformations by preprocessor)
— except predetermined variables (which must be handled before the call to
setLeadsLagsOrig(), see #47, and also before equation simplification,
since the latter uses leads/lags, see #83)
— except substituting out variables which we know are constant (they
appear in an equation of the form: X = constant)
— except adl operators which we always want expanded
— except diff operators with a lead which have been expanded by
DataTree:AddDiff()
*/
dynamic_model.includeExcludeEquations(exclude_eqs, true);
dynamic_model.includeExcludeEquations(include_eqs, false);
if (symbol_table.predeterminedNbr() > 0)
dynamic_model.transformPredeterminedVariables();
dynamic_model.simplifyEquations();
dynamic_model.substituteAdl();
dynamic_model.setLeadsLagsOrig(); dynamic_model.setLeadsLagsOrig();
dynamic_model.cloneDynamic(original_model); original_model = dynamic_model;
dynamic_model.expandEqTags();
if (nostrict) // Replace all model-local variables by their expression
{ dynamic_model.substituteModelLocalVariables();
// Check that all declared endogenous are used in equations
set<int> unusedEndogs = dynamic_model.findUnusedEndogenous(); set<int> unusedEndogs = dynamic_model.findUnusedEndogenous();
for (set<int>::iterator it = unusedEndogs.begin(); it != unusedEndogs.end(); it++) bool unusedEndogsIsErr = !nostrict && !mod_file_struct.bvar_present && unusedEndogs.size();
for (int unusedEndog : unusedEndogs)
if (nostrict)
{ {
symbol_table.changeType(*it, eUnusedEndogenous); symbol_table.changeType(unusedEndog, SymbolType::unusedEndogenous);
warnings << "WARNING: '" << symbol_table.getName(*it) warnings << "WARNING: '" << symbol_table.getName(unusedEndog)
<< "' not used in model block, removed by nostrict command-line option" << endl; << "' not used in model block, removed by nostrict command-line option" << endl;
} }
} else if (unusedEndogsIsErr)
cerr << "Error: " << symbol_table.getName(unusedEndog) << " not used in the model block"
<< endl;
if (symbol_table.predeterminedNbr() > 0) if (unusedEndogsIsErr)
dynamic_model.transformPredeterminedVariables(); exit(EXIT_FAILURE);
/* Get the list of equations in which to scan for and substitute unary ops:
– equations which are part of VARs and Trend Component Models
– PAC equations (those with a pac_expectation operator) */
set<string> var_tcm_eqtags;
for (const auto& [name, tags] : trend_component_model_table.getEqTags())
for (auto& tag : tags)
var_tcm_eqtags.insert(tag);
for (const auto& [name, tags] : var_model_table.getEqTags())
for (auto& tag : tags)
var_tcm_eqtags.insert(tag);
set<int> unary_ops_eqs = dynamic_model.getEquationNumbersFromTags(var_tcm_eqtags);
unary_ops_eqs.merge(dynamic_model.findPacExpectationEquationNumbers());
// Check that no variable in VAR/TCM/PAC equations was declared with “var(log)”
dynamic_model.checkNoWithLogTransform(unary_ops_eqs);
// Create auxiliary variables and equations for unary ops
lag_equivalence_table_t unary_ops_nodes;
ExprNode::subst_table_t unary_ops_subst_table;
if (transform_unary_ops)
tie(unary_ops_nodes, unary_ops_subst_table)
= dynamic_model.substituteUnaryOps(var_expectation_model_table, pac_model_table);
else
// substitute only those unary ops that appear in VAR, TCM and PAC model equations
tie(unary_ops_nodes, unary_ops_subst_table) = dynamic_model.substituteUnaryOps(
unary_ops_eqs, var_expectation_model_table, pac_model_table);
// Create auxiliary variable and equations for Diff operators
auto [diff_nodes, diff_subst_table]
= dynamic_model.substituteDiff(var_expectation_model_table, pac_model_table);
// Fill trend component and VAR model tables
dynamic_model.fillTrendComponentModelTable();
original_model.fillTrendComponentModelTableFromOrigModel();
dynamic_model.fillTrendComponentModelTableAREC(diff_subst_table);
dynamic_model.fillVarModelTable();
original_model.fillVarModelTableFromOrigModel();
// VAR expectation models
var_expectation_model_table.transformPass(diff_subst_table, dynamic_model, var_model_table,
trend_component_model_table);
// PAC model
pac_model_table.transformPass(unary_ops_nodes, unary_ops_subst_table, diff_nodes,
diff_subst_table, dynamic_model, var_model_table,
trend_component_model_table);
// Create auxiliary vars for Expectation operator // Create auxiliary vars for Expectation operator
dynamic_model.substituteExpectation(mod_file_struct.partial_information); dynamic_model.substituteExpectation(mod_file_struct.partial_information);
...@@ -368,71 +633,82 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, const ...@@ -368,71 +633,82 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, const
if (nonstationary_variables) if (nonstationary_variables)
{ {
dynamic_model.detrendEquations(); dynamic_model.detrendEquations();
dynamic_model.cloneDynamic(trend_dynamic_model); trend_dynamic_model = dynamic_model;
dynamic_model.removeTrendVariableFromEquations(); dynamic_model.removeTrendVariableFromEquations();
const auto& trend_symbols = dynamic_model.getTrendSymbolsMap();
const auto& nonstationary_symbols = dynamic_model.getNonstationarySymbolsMap();
epilogue.detrend(trend_symbols, nonstationary_symbols);
} }
mod_file_struct.orig_eq_nbr = dynamic_model.equation_number(); epilogue.toStatic();
if (mod_file_struct.ramsey_model_present) if (mod_file_struct.ramsey_model_present)
{ {
StaticModel *planner_objective = NULL; mod_file_struct.ramsey_orig_eq_nbr = dynamic_model.equation_number();
for (vector<Statement *>::iterator it = statements.begin(); it != statements.end(); it++) PlannerObjectiveStatement* pos = nullptr;
for (auto& statement : statements)
if (auto pos2 = dynamic_cast<PlannerObjectiveStatement*>(statement.get()); pos2)
{
if (pos)
{ {
PlannerObjectiveStatement *pos = dynamic_cast<PlannerObjectiveStatement *>(*it); cerr << "ERROR: there can only be one planner_objective statement" << endl;
if (pos != NULL) exit(EXIT_FAILURE);
planner_objective = pos->getPlannerObjective(); }
else
pos = pos2;
} }
assert(planner_objective != NULL); assert(pos);
const PlannerObjective& planner_objective = pos->getPlannerObjective();
/* /*
clone the model then clone the new equations back to the original because clone the model then clone the new equations back to the original because
we have to call computeDerivIDs (in computeRamseyPolicyFOCs and computingPass) we have to call computeDerivIDs (in computeRamseyPolicyFOCs and computingPass)
*/ */
if (linear) if (linear)
dynamic_model.cloneDynamic(orig_ramsey_dynamic_model); orig_ramsey_dynamic_model = dynamic_model;
dynamic_model.cloneDynamic(ramsey_FOC_equations_dynamic_model); DynamicModel ramsey_FOC_equations_dynamic_model {symbol_table,
ramsey_FOC_equations_dynamic_model.computeRamseyPolicyFOCs(*planner_objective, nopreprocessoroutput); num_constants,
external_functions_table,
heterogeneity_table,
trend_component_model_table,
var_model_table};
ramsey_FOC_equations_dynamic_model = dynamic_model;
auto clone_if_not_null
= [&](expr_t e) { return e ? e->clone(ramsey_FOC_equations_dynamic_model) : nullptr; };
map<int, pair<expr_t, expr_t>> cloned_ramsey_constraints;
for (const auto& [symb_id, bounds] : ramsey_constraints)
cloned_ramsey_constraints.try_emplace(symb_id, clone_if_not_null(bounds.first),
clone_if_not_null(bounds.second));
mod_file_struct.ramsey_orig_endo_nbr
= ramsey_FOC_equations_dynamic_model.computeRamseyPolicyFOCs(planner_objective,
cloned_ramsey_constraints);
ramsey_FOC_equations_dynamic_model.replaceMyEquations(dynamic_model); ramsey_FOC_equations_dynamic_model.replaceMyEquations(dynamic_model);
mod_file_struct.ramsey_eq_nbr = dynamic_model.equation_number() - mod_file_struct.orig_eq_nbr;
} }
// Workaround for #1193 dynamic_model.createVariableMapping();
if (!mod_file_struct.hist_vals_wrong_lag.empty())
{
bool err = false;
for (map<int, int>::const_iterator it = mod_file_struct.hist_vals_wrong_lag.begin();
it != mod_file_struct.hist_vals_wrong_lag.end(); it++)
if (dynamic_model.minLagForSymbol(it->first) > it->second - 1)
{
cerr << "ERROR: histval: variable " << symbol_table.getName(it->first)
<< " does not appear in the model with the lag " << it->second - 1
<< " (see the reference manual for the timing convention in 'histval')" << endl;
err = true;
}
if (err)
exit(EXIT_FAILURE);
}
if (mod_file_struct.stoch_simul_present // Must come after detrending of variables and Ramsey policy transformation
|| mod_file_struct.estimation_present dynamic_model.substituteLogTransform();
|| mod_file_struct.osr_present
|| mod_file_struct.ramsey_policy_present if (!heterogeneity_table.empty())
|| mod_file_struct.discretionary_policy_present dynamic_model.substituteAggregationOperators();
|| mod_file_struct.calib_smoother_present
|| stochastic ) /* Create auxiliary vars for leads and lags greater than 2, on both endos and
{ exos. The transformation is not exactly the same on stochastic and
// In stochastic models, create auxiliary vars for leads and lags greater than 2, on both endos and exos deterministic models, because there is no need to take into account the
dynamic_model.substituteEndoLeadGreaterThanTwo(false); Jensen inequality on the latter. */
dynamic_model.substituteExoLead(false); bool deterministic_model
dynamic_model.substituteEndoLagGreaterThanTwo(false); = !(mod_file_struct.stoch_simul_present || mod_file_struct.estimation_present
dynamic_model.substituteExoLag(false); || mod_file_struct.osr_present || mod_file_struct.discretionary_policy_present
} || mod_file_struct.calib_smoother_present || mod_file_struct.identification_present
else || mod_file_struct.mom_estimation_present || mod_file_struct.sensitivity_present
{ || stochastic);
// In deterministic models, create auxiliary vars for leads and lags endogenous greater than 2, only on endos (useless on exos) dynamic_model.substituteEndoLeadGreaterThanTwo(deterministic_model);
dynamic_model.substituteEndoLeadGreaterThanTwo(true); dynamic_model.substituteExoLead(deterministic_model);
dynamic_model.substituteEndoLagGreaterThanTwo(true); dynamic_model.substituteEndoLagGreaterThanTwo(deterministic_model);
} dynamic_model.substituteExoLag(deterministic_model);
dynamic_model.updateVarAndTrendModel();
if (differentiate_forward_vars) if (differentiate_forward_vars)
dynamic_model.differentiateForwardVars(differentiate_forward_vars_subset); dynamic_model.differentiateForwardVars(differentiate_forward_vars_subset);
...@@ -440,10 +716,10 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, const ...@@ -440,10 +716,10 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, const
if (mod_file_struct.dsge_var_estimated || !mod_file_struct.dsge_var_calibrated.empty()) if (mod_file_struct.dsge_var_estimated || !mod_file_struct.dsge_var_calibrated.empty())
try try
{ {
int sid = symbol_table.addSymbol("dsge_prior_weight", eParameter); int sid = symbol_table.addSymbol("dsge_prior_weight", SymbolType::parameter);
if (!mod_file_struct.dsge_var_calibrated.empty()) if (!mod_file_struct.dsge_var_calibrated.empty())
addStatementAtFront(new InitParamStatement(sid, addStatementAtFront(make_unique<InitParamStatement>(
expressions_tree.AddNonNegativeConstant(mod_file_struct.dsge_var_calibrated), sid, expressions_tree.AddNonNegativeConstant(mod_file_struct.dsge_var_calibrated),
symbol_table)); symbol_table));
} }
catch (SymbolTable::AlreadyDeclaredException& e) catch (SymbolTable::AlreadyDeclaredException& e)
...@@ -453,6 +729,12 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, const ...@@ -453,6 +729,12 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, const
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
dynamic_model.reorderAuxiliaryEquations();
symbol_table.resizeHetAuxVars();
for (auto& hm : heterogeneous_models)
hm.transformPass();
// Freeze the symbol table // Freeze the symbol table
symbol_table.freeze(); symbol_table.freeze();
...@@ -467,60 +749,100 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, const ...@@ -467,60 +749,100 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, const
*/ */
if (!(mod_file_struct.ramsey_model_present || mod_file_struct.discretionary_policy_present) if (!(mod_file_struct.ramsey_model_present || mod_file_struct.discretionary_policy_present)
&& !(mod_file_struct.bvar_present && dynamic_model.equation_number() == 0) && !(mod_file_struct.bvar_present && dynamic_model.equation_number() == 0)
&& !(mod_file_struct.occbin_option)
&& (dynamic_model.equation_number() != symbol_table.endo_nbr())) && (dynamic_model.equation_number() != symbol_table.endo_nbr()))
{ {
cerr << "ERROR: There are " << dynamic_model.equation_number() << " equations but " << symbol_table.endo_nbr() << " endogenous variables!" << endl; cerr << "ERROR: There are " << dynamic_model.equation_number() << " equations but "
<< symbol_table.endo_nbr() << " endogenous variables!" << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (symbol_table.exo_det_nbr() > 0 && mod_file_struct.perfect_foresight_solver_present) if (symbol_table.exo_det_nbr() > 0
&& (mod_file_struct.perfect_foresight_solver_present
|| mod_file_struct.perfect_foresight_with_expectation_errors_solver_present))
{ {
cerr << "ERROR: A .mod file cannot contain both one of {perfect_foresight_solver, simul} and varexo_det declaration (all exogenous variables are deterministic in this case)" << endl; cerr << "ERROR: A .mod file cannot contain both one of {perfect_foresight_solver, simul, "
"perfect_foresight_with_expectation_errors_solver} and varexo_det declaration (all "
"exogenous variables are deterministic in this case)"
<< endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (mod_file_struct.ramsey_policy_present && symbol_table.exo_det_nbr() > 0) if (mod_file_struct.ramsey_model_present && symbol_table.exo_det_nbr() > 0)
{ {
cerr << "ERROR: ramsey_policy is incompatible with deterministic exogenous variables" << endl; cerr << "ERROR: ramsey_model and ramsey_policy are incompatible with deterministic exogenous "
"variables"
<< endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (mod_file_struct.ramsey_policy_present) if (mod_file_struct.identification_present && symbol_table.exo_det_nbr() > 0)
for (vector<Statement *>::iterator it = statements.begin(); it != statements.end(); it++)
{ {
RamseyPolicyStatement *rps = dynamic_cast<RamseyPolicyStatement *>(*it); cerr << "ERROR: identification is incompatible with deterministic exogenous variables"
if (rps != NULL) << endl;
rps->checkRamseyPolicyList(); exit(EXIT_FAILURE);
} }
if (mod_file_struct.identification_present && symbol_table.exo_det_nbr() > 0) if (mod_file_struct.occbin_constraints_present
&& (mod_file_struct.osr_present || mod_file_struct.mom_estimation_present
|| mod_file_struct.ramsey_model_present || mod_file_struct.discretionary_policy_present
|| mod_file_struct.extended_path_present || mod_file_struct.identification_present
|| mod_file_struct.sensitivity_present))
{ {
cerr << "ERROR: identification is incompatible with deterministic exogenous variables" << endl; cerr << "ERROR: the 'occbin_constraints' block is not compatible with commands other than "
"'estimation', 'stoch_simul', and 'calib_smoother'."
<< endl;
exit(EXIT_FAILURE);
}
if (mod_file_struct.shocks_surprise_present && !mod_file_struct.occbin_constraints_present)
{
cerr << "ERROR: the 'shocks(surprise)' block can only be used in conjunction with the "
"'occbin_constraints' block."
<< endl;
exit(EXIT_FAILURE);
}
if (mod_file_struct.shocks_learnt_in_present
&& !mod_file_struct.perfect_foresight_with_expectation_errors_solver_present)
{
cerr << "ERROR: the 'shocks(learnt_in=…)' block can only be used in conjunction with the "
"'perfect_foresight_with_expectation_errors_solver' command."
<< endl;
exit(EXIT_FAILURE);
}
if (mod_file_struct.endval_learnt_in_present
&& !mod_file_struct.perfect_foresight_with_expectation_errors_solver_present)
{
cerr << "ERROR: the 'endval(learnt_in=…)' block can only be used in conjunction with the "
"'perfect_foresight_with_expectation_errors_solver' command."
<< endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (!nopreprocessoroutput)
if (!mod_file_struct.ramsey_model_present) if (!mod_file_struct.ramsey_model_present)
cout << "Found " << dynamic_model.equation_number() << " equation(s)." << endl; cout << "Found " << dynamic_model.equation_number() << " equation(s)." << endl;
else else
{ {
cout << "Found " << mod_file_struct.orig_eq_nbr << " equation(s)." << endl; cout << "Found " << mod_file_struct.ramsey_orig_eq_nbr << " equation(s)." << endl;
cout << "Found " << dynamic_model.equation_number() << " FOC equation(s) for Ramsey Problem." << endl; cout << "Found " << dynamic_model.equation_number() << " FOC equation(s) for Ramsey Problem."
<< endl;
} }
if (symbol_table.exists("dsge_prior_weight")) if (symbol_table.exists("dsge_prior_weight"))
{
if (mod_file_struct.bayesian_irf_present) if (mod_file_struct.bayesian_irf_present)
{ {
if (symbol_table.exo_nbr() != symbol_table.observedVariablesNbr()) if (symbol_table.exo_nbr() != symbol_table.observedVariablesNbr())
{ {
cerr << "ERROR: When estimating a DSGE-Var and the bayesian_irf option is passed to the estimation " cerr << "ERROR: When estimating a DSGE-Var and the bayesian_irf option is passed to "
<< "statement, the number of shocks must equal the number of observed variables." << endl; "the estimation "
<< "statement, the number of shocks must equal the number of observed variables."
<< endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
else else if (symbol_table.exo_nbr() < symbol_table.observedVariablesNbr())
if (symbol_table.exo_nbr() < symbol_table.observedVariablesNbr())
{ {
cerr << "ERROR: When estimating a DSGE-Var, the number of shocks must be " cerr << "ERROR: When estimating a DSGE-Var, the number of shocks must be "
<< "greater than or equal to the number of observed variables." << endl; << "greater than or equal to the number of observed variables." << endl;
...@@ -528,8 +850,19 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, const ...@@ -528,8 +850,19 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, const
} }
} }
for (int dim {0}; dim < heterogeneity_table.size(); dim++)
if (heterogeneous_models.at(dim).equation_number() != symbol_table.het_endo_nbr(dim))
{
cerr << "ERROR: There are " << heterogeneous_models.at(dim).equation_number()
<< " equations but " << symbol_table.het_endo_nbr(dim)
<< " endogenous variables in the model for heterogeneity dimension '"
<< heterogeneity_table.getName(dim) << "'!" << endl;
exit(EXIT_FAILURE);
}
}
void void
ModFile::computingPass(bool no_tmp_terms, FileOutputType output, int params_derivs_order, const bool nopreprocessoroutput) ModFile::computingPass(bool no_tmp_terms, OutputType output, int params_derivs_order)
{ {
// Mod file may have no equation (for example in a standalone BVAR estimation) // Mod file may have no equation (for example in a standalone BVAR estimation)
if (dynamic_model.equation_number() > 0) if (dynamic_model.equation_number() > 0)
...@@ -538,116 +871,201 @@ ModFile::computingPass(bool no_tmp_terms, FileOutputType output, int params_deri ...@@ -538,116 +871,201 @@ ModFile::computingPass(bool no_tmp_terms, FileOutputType output, int params_deri
trend_dynamic_model.runTrendTest(global_eval_context); trend_dynamic_model.runTrendTest(global_eval_context);
// Compute static model and its derivatives // Compute static model and its derivatives
dynamic_model.toStatic(static_model); static_model = static_cast<StaticModel>(dynamic_model);
if (!no_static) if (!no_static)
{ {
if (mod_file_struct.stoch_simul_present if (mod_file_struct.stoch_simul_present || mod_file_struct.estimation_present
|| mod_file_struct.estimation_present || mod_file_struct.osr_present || mod_file_struct.osr_present || mod_file_struct.ramsey_model_present
|| mod_file_struct.ramsey_model_present || mod_file_struct.identification_present || mod_file_struct.identification_present || mod_file_struct.calib_smoother_present
|| mod_file_struct.calib_smoother_present) || mod_file_struct.mom_estimation_present)
static_model.set_cutoff_to_zero(); static_model.set_cutoff_to_zero();
const bool static_hessian = mod_file_struct.identification_present int derivsOrder = 1;
|| mod_file_struct.estimation_analytic_derivation;
int paramsDerivsOrder = 0; int paramsDerivsOrder = 0;
if (mod_file_struct.identification_present || mod_file_struct.estimation_analytic_derivation) if (mod_file_struct.identification_present
|| mod_file_struct.estimation_analytic_derivation)
derivsOrder = 2;
if (mod_file_struct.identification_present
|| mod_file_struct.estimation_analytic_derivation
|| mod_file_struct.osr_analytic_derivation
|| (mod_file_struct.GMM_present
&& (mod_file_struct.analytic_standard_errors_present
|| mod_file_struct.analytic_jacobian_present)))
paramsDerivsOrder = params_derivs_order; paramsDerivsOrder = params_derivs_order;
static_model.computingPass(global_eval_context, no_tmp_terms, static_hessian,
false, paramsDerivsOrder, block, byte_code, nopreprocessoroutput); static_model.computingPass(derivsOrder, paramsDerivsOrder, global_eval_context,
no_tmp_terms, block, use_dll);
if (mod_file_struct.ramsey_model_present)
static_model.computeRamseyMultipliersDerivatives(mod_file_struct.ramsey_orig_endo_nbr,
!use_dll, no_tmp_terms);
} }
// Set things to compute for dynamic model // Set things to compute for dynamic model
if (mod_file_struct.perfect_foresight_solver_present || mod_file_struct.check_present if (mod_file_struct.perfect_foresight_solver_present
|| mod_file_struct.stoch_simul_present || mod_file_struct.perfect_foresight_with_expectation_errors_solver_present
|| mod_file_struct.check_present || mod_file_struct.stoch_simul_present
|| mod_file_struct.estimation_present || mod_file_struct.osr_present || mod_file_struct.estimation_present || mod_file_struct.osr_present
|| mod_file_struct.ramsey_model_present || mod_file_struct.identification_present || mod_file_struct.ramsey_model_present || mod_file_struct.identification_present
|| mod_file_struct.calib_smoother_present) || mod_file_struct.calib_smoother_present || mod_file_struct.mom_estimation_present)
{ {
if (mod_file_struct.perfect_foresight_solver_present) if (mod_file_struct.perfect_foresight_solver_present
dynamic_model.computingPass(true, false, false, none, global_eval_context, no_tmp_terms, block, use_dll, byte_code, nopreprocessoroutput); || mod_file_struct.perfect_foresight_with_expectation_errors_solver_present)
{
int derivsOrder = 1;
if (output == OutputType::second)
derivsOrder = 2;
else if (output == OutputType::third)
derivsOrder = 3;
dynamic_model.computingPass(derivsOrder, 0, global_eval_context, no_tmp_terms, block,
use_dll);
}
else else
{ {
if (mod_file_struct.stoch_simul_present if (mod_file_struct.stoch_simul_present || mod_file_struct.estimation_present
|| mod_file_struct.estimation_present || mod_file_struct.osr_present || mod_file_struct.osr_present || mod_file_struct.ramsey_model_present
|| mod_file_struct.ramsey_model_present || mod_file_struct.identification_present || mod_file_struct.identification_present
|| mod_file_struct.calib_smoother_present) || mod_file_struct.calib_smoother_present
|| mod_file_struct.mom_estimation_present)
dynamic_model.set_cutoff_to_zero(); dynamic_model.set_cutoff_to_zero();
if (mod_file_struct.order_option < 1 || mod_file_struct.order_option > 3) if (mod_file_struct.order_option < 1)
{ {
cerr << "ERROR: Incorrect order option..." << endl; cerr << "ERROR: Incorrect order option..." << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
bool hessian = mod_file_struct.order_option >= 2 int derivsOrder
|| mod_file_struct.identification_present = max(mod_file_struct.order_option,
|| mod_file_struct.estimation_analytic_derivation mod_file_struct.identification_order + 1); // See preprocessor#40
|| linear if (mod_file_struct.GMM_present
|| output == second && (mod_file_struct.analytic_standard_errors_present
|| output == third; || mod_file_struct.analytic_jacobian_present)) // analytic_standard_errors or
bool thirdDerivatives = mod_file_struct.order_option == 3 // analytic_jacobian require
|| mod_file_struct.estimation_analytic_derivation // one order more
|| output == third; derivsOrder
= max(mod_file_struct.order_option,
max(mod_file_struct.identification_order, mod_file_struct.mom_order)
+ 1); // See preprocessor#40
if (mod_file_struct.sensitivity_present || linear || output == OutputType::second)
derivsOrder = max(derivsOrder, 2);
if (mod_file_struct.estimation_analytic_derivation || output == OutputType::third)
derivsOrder = max(derivsOrder, 3);
int paramsDerivsOrder = 0; int paramsDerivsOrder = 0;
if (mod_file_struct.identification_present || mod_file_struct.estimation_analytic_derivation) if (mod_file_struct.identification_present
|| mod_file_struct.estimation_analytic_derivation
|| mod_file_struct.osr_analytic_derivation
|| (mod_file_struct.GMM_present
&& (mod_file_struct.analytic_standard_errors_present
|| mod_file_struct.analytic_jacobian_present)))
paramsDerivsOrder = params_derivs_order; paramsDerivsOrder = params_derivs_order;
dynamic_model.computingPass(true, hessian, thirdDerivatives, paramsDerivsOrder, global_eval_context, no_tmp_terms, block, use_dll, byte_code, nopreprocessoroutput); dynamic_model.computingPass(derivsOrder, paramsDerivsOrder, global_eval_context,
no_tmp_terms, block, use_dll);
if (linear && mod_file_struct.ramsey_model_present) if (linear && mod_file_struct.ramsey_model_present)
orig_ramsey_dynamic_model.computingPass(true, true, false, paramsDerivsOrder, global_eval_context, no_tmp_terms, block, use_dll, byte_code, nopreprocessoroutput); orig_ramsey_dynamic_model.computingPass(2, paramsDerivsOrder, global_eval_context,
no_tmp_terms, block, use_dll);
} }
} }
else // No computing task requested, compute derivatives up to 2nd order by default else // No computing task requested, compute derivatives up to 2nd order by default unless
dynamic_model.computingPass(true, true, false, none, global_eval_context, no_tmp_terms, block, use_dll, byte_code, nopreprocessoroutput); // output=first (preprocessor#100) or third (preprocessor#121) is requested
map<int, string> eqs;
if (mod_file_struct.ramsey_model_present)
orig_ramsey_dynamic_model.setNonZeroHessianEquations(eqs);
else
dynamic_model.setNonZeroHessianEquations(eqs);
if (linear && !eqs.empty())
{ {
cerr << "ERROR: If the model is declared linear the second derivatives must be equal to zero." << endl switch (output)
<< " The following equations had non-zero second derivatives:" << endl;
for (map<int, string >::const_iterator it = eqs.begin(); it != eqs.end(); it++)
{ {
cerr << " * Eq # " << it->first+1; case OutputType::first:
if (!it->second.empty()) dynamic_model.computingPass(1, 0, global_eval_context, no_tmp_terms, block, use_dll);
cerr << " [" << it->second << "]"; break;
cerr << endl; case OutputType::third:
dynamic_model.computingPass(3, 0, global_eval_context, no_tmp_terms, block, use_dll);
break;
default:
dynamic_model.computingPass(2, 0, global_eval_context, no_tmp_terms, block, use_dll);
break;
} }
exit(EXIT_FAILURE); }
if (linear)
{
if (mod_file_struct.ramsey_model_present)
orig_ramsey_dynamic_model.checkIsLinear();
else
dynamic_model.checkIsLinear();
} }
} }
for (vector<Statement *>::iterator it = statements.begin(); // Those matrices can only be filled here, because we use derivatives
it != statements.end(); it++) dynamic_model.fillVarModelTableMatrices();
(*it)->computingPass();
for (auto& hm : heterogeneous_models)
hm.computingPass(mod_file_struct.order_option, no_tmp_terms, use_dll);
for (auto& statement : statements)
statement->computingPass(mod_file_struct);
// Compute epilogue derivatives (but silence standard output)
streambuf* oldcout = cout.rdbuf();
cout.rdbuf(nullptr);
epilogue.computingPass(2, 0, global_eval_context, true, false, false);
cout.rdbuf(oldcout);
} }
void void
ModFile::writeOutputFiles(const string &basename, bool clear_all, bool clear_global, bool no_log, bool no_warn, ModFile::remove_directory_with_matlab_lock(const filesystem::path& dir)
bool console, bool nograph, bool nointeractive, const ConfigFile &config_file,
bool check_model_changes, bool minimal_workspace, bool compute_xrefs
#if defined(_WIN32) || defined(__CYGWIN32__)
, bool cygwin, bool msvc, bool mingw
#endif
, const bool nopreprocessoroutput
) const
{ {
ofstream mOutputFile; auto dirStatus {status(dir)};
if (!exists(dirStatus))
return;
if (basename.size()) if (is_directory(dirStatus))
for (const auto& e : filesystem::directory_iterator {dir})
if (e.is_directory())
remove_directory_with_matlab_lock(e);
auto tmp {unique_path()};
rename(dir, tmp);
remove_all(tmp);
}
void
ModFile::writeMOutput(const string& basename, bool clear_all, bool clear_global, bool no_warn,
bool console, bool nograph, bool nointeractive, const Configuration& config,
bool check_model_changes, bool minimal_workspace, bool compute_xrefs,
const string& mexext, const filesystem::path& matlabroot, bool onlymodel,
bool gui, bool notime) const
{ {
string fname(basename); if (basename.empty())
fname += ".m";
mOutputFile.open(fname.c_str(), ios::out | ios::binary);
if (!mOutputFile.is_open())
{ {
cerr << "ERROR: Can't open file " << fname << " for writing" << endl; cerr << "ERROR: Missing file name" << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
auto plusfolder {DataTree::packageDir(basename)};
if (check_model_changes && !heterogeneity_table.empty())
{
cerr << "ERROR: the 'fast' option is not supported for heterogeneous models" << endl;
exit(EXIT_FAILURE);
} }
else bool hasModelChanged = !dynamic_model.isChecksumMatching(basename) || !check_model_changes;
if (hasModelChanged)
{ {
cerr << "ERROR: Missing file name" << endl; // Erase possible remnants of previous runs
/* Under MATLAB+Windows (but not under Octave nor under GNU/Linux or
macOS), if we directly remove the "+" subdirectory, then the
preprocessor is not able to recreate it afterwards (presumably because
MATLAB maintains some sort of lock on it). So we use a hack. */
remove_directory_with_matlab_lock(plusfolder);
filesystem::remove_all(basename + "/model/src");
filesystem::remove_all(basename + "/model/bytecode");
// Do not remove basename/model/julia/, otherwise it would break calls to
// writeToFileIfModified()
}
create_directory(plusfolder);
filesystem::path fname {plusfolder / "driver.m"};
ofstream mOutputFile {fname, ios::out | ios::binary};
if (!mOutputFile.is_open())
{
cerr << "ERROR: Can't open file " << fname.string() << " for writing" << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
...@@ -655,24 +1073,27 @@ ModFile::writeOutputFiles(const string &basename, bool clear_all, bool clear_glo ...@@ -655,24 +1073,27 @@ ModFile::writeOutputFiles(const string &basename, bool clear_all, bool clear_glo
<< "% Status : main Dynare file" << endl << "% Status : main Dynare file" << endl
<< "%" << endl << "%" << endl
<< "% Warning : this file is generated automatically by Dynare" << endl << "% Warning : this file is generated automatically by Dynare" << endl
<< "% from model file (.mod)" << endl << endl; << "% from model file (.mod)" << endl
<< endl;
if (no_warn) if (no_warn)
mOutputFile << "warning off" << endl; // This will be executed *after* function warning_config() mOutputFile << "warning off" << endl; // This will be executed *after* function warning_config()
if (clear_all) if (clear_all)
mOutputFile << "if isoctave || matlab_ver_less_than('8.6')" << endl mOutputFile << "clearvars -global" << endl
<< " clear all" << endl << "clear_persistent_variables(fileparts(which('dynare')), false)" << endl;
<< "else" << endl
<< " clearvars -global" << endl
<< " clear_persistent_variables(fileparts(which('dynare')), false)" << endl
<< "end" << endl;
else if (clear_global) else if (clear_global)
mOutputFile << "clear M_ options_ oo_ estim_params_ bayestopt_ dataset_ dataset_info estimation_info ys0_ ex0_;" << endl; mOutputFile << "clearvars -global M_ options_ oo_ estim_params_ bayestopt_ dataset_ "
"dataset_info estimation_info;"
<< endl;
mOutputFile << "tic0 = tic;" << endl if (!notime)
mOutputFile << "tic0 = tic;" << endl;
mOutputFile
<< "% Define global variables." << endl << "% Define global variables." << endl
<< "global M_ options_ oo_ estim_params_ bayestopt_ dataset_ dataset_info estimation_info ys0_ ex0_" << endl << "global M_ options_ oo_ estim_params_ bayestopt_ dataset_ dataset_info estimation_info"
<< endl
<< "options_ = [];" << endl << "options_ = [];" << endl
<< "M_.fname = '" << basename << "';" << endl << "M_.fname = '" << basename << "';" << endl
<< "M_.dynare_version = '" << PACKAGE_VERSION << "';" << endl << "M_.dynare_version = '" << PACKAGE_VERSION << "';" << endl
...@@ -681,37 +1102,38 @@ ModFile::writeOutputFiles(const string &basename, bool clear_all, bool clear_glo ...@@ -681,37 +1102,38 @@ ModFile::writeOutputFiles(const string &basename, bool clear_all, bool clear_glo
<< "%" << endl << "%" << endl
<< "% Some global variables initialization" << endl << "% Some global variables initialization" << endl
<< "%" << endl; << "%" << endl;
config_file.writeHooks(mOutputFile); if (!onlymodel)
mOutputFile << "global_initialization;" << endl config.writeHooks(mOutputFile);
<< "diary off;" << endl; mOutputFile << "global_initialization;" << endl;
if (!no_log)
mOutputFile << "diary('" << basename << ".log');" << endl;
if (minimal_workspace)
mOutputFile << "options_.minimal_workspace = 1;" << endl;
if (console) if (console)
mOutputFile << "options_.console_mode = 1;" << endl mOutputFile << "options_.console_mode = true;" << endl << "options_.nodisplay = true;" << endl;
<< "options_.nodisplay = 1;" << endl;
if (nograph) if (nograph)
mOutputFile << "options_.nograph = 1;" << endl; mOutputFile << "options_.nograph = true;" << endl;
if (nointeractive) if (nointeractive)
mOutputFile << "options_.nointeractive = 1;" << endl; mOutputFile << "options_.nointeractive = true;" << endl;
if (param_used_with_lead_lag) if (param_used_with_lead_lag)
mOutputFile << "M_.parameter_used_with_lead_lag = true;" << endl; mOutputFile << "M_.parameter_used_with_lead_lag = true;" << endl;
if (!nopreprocessoroutput)
cout << "Processing outputs ..." << endl;
symbol_table.writeOutput(mOutputFile); symbol_table.writeOutput(mOutputFile);
heterogeneity_table.writeOutput(mOutputFile);
var_model_table.writeOutput(basename, mOutputFile);
trend_component_model_table.writeOutput(basename, mOutputFile);
var_expectation_model_table.writeOutput(mOutputFile);
pac_model_table.writeOutput(mOutputFile);
// Initialize M_.Sigma_e, M_.Correlation_matrix, M_.H, and M_.Correlation_matrix_ME // Initialize M_.Sigma_e, M_.Correlation_matrix, M_.H, and M_.Correlation_matrix_ME
mOutputFile << "M_.Sigma_e = zeros(" << symbol_table.exo_nbr() << ", " mOutputFile << "M_.Sigma_e = zeros(" << symbol_table.exo_nbr() << ", " << symbol_table.exo_nbr()
<< symbol_table.exo_nbr() << ");" << endl << ");" << endl
<< "M_.Correlation_matrix = eye(" << symbol_table.exo_nbr() << ", " << "M_.Correlation_matrix = eye(" << symbol_table.exo_nbr() << ", "
<< symbol_table.exo_nbr() << ");" << endl; << symbol_table.exo_nbr() << ");" << endl;
for (int hd {0}; hd < heterogeneity_table.size(); hd++)
mOutputFile << "M_.heterogeneity(" << hd + 1 << ").Sigma_e = zeros("
<< symbol_table.het_exo_nbr(hd) << ", " << symbol_table.het_exo_nbr(hd) << ");"
<< endl;
if (mod_file_struct.calibrated_measurement_errors) if (mod_file_struct.calibrated_measurement_errors)
mOutputFile << "M_.H = zeros(" << symbol_table.observedVariablesNbr() << ", " mOutputFile << "M_.H = zeros(" << symbol_table.observedVariablesNbr() << ", "
...@@ -719,204 +1141,204 @@ ModFile::writeOutputFiles(const string &basename, bool clear_all, bool clear_glo ...@@ -719,204 +1141,204 @@ ModFile::writeOutputFiles(const string &basename, bool clear_all, bool clear_glo
<< "M_.Correlation_matrix_ME = eye(" << symbol_table.observedVariablesNbr() << ", " << "M_.Correlation_matrix_ME = eye(" << symbol_table.observedVariablesNbr() << ", "
<< symbol_table.observedVariablesNbr() << ");" << endl; << symbol_table.observedVariablesNbr() << ");" << endl;
else else
mOutputFile << "M_.H = 0;" << endl mOutputFile << "M_.H = 0;" << endl << "M_.Correlation_matrix_ME = 1;" << endl;
<< "M_.Correlation_matrix_ME = 1;" << endl;
// May be later modified by a shocks block // May be later modified by a shocks block
mOutputFile << "M_.sigma_e_is_diagonal = 1;" << endl; mOutputFile << "M_.sigma_e_is_diagonal = true;" << endl;
// Initialize M_.det_shocks /* Initialize the structures created for several blocks, as part of the implementation of the
mOutputFile << "M_.det_shocks = [];" << endl; “overwrite” option */
mOutputFile << "M_.det_shocks = [];" << endl
if (linear == 1) << "M_.surprise_shocks = [];" << endl
mOutputFile << "options_.linear = 1;" << endl; << "M_.learnt_shocks = [];" << endl
<< "M_.learnt_endval = [];" << endl
mOutputFile << "options_.block=" << block << ";" << endl << "M_.heteroskedastic_shocks.Qvalue_orig = [];" << endl
<< "options_.bytecode=" << byte_code << ";" << endl << "M_.heteroskedastic_shocks.Qscale_orig = [];" << endl
<< "options_.use_dll=" << use_dll << ";" << endl; << "M_.matched_irfs = {};" << endl
<< "M_.matched_irfs_weights = {};" << endl
<< "M_.perfect_foresight_controlled_paths = [];" << endl;
// NB: options_.{ramsey,discretionary}_policy should rather be fields of M_
mOutputFile << boolalpha << "options_.linear = " << linear << ";" << endl
<< "options_.block = " << block << ";" << endl
<< "options_.bytecode = " << bytecode << ";" << endl
<< "options_.use_dll = " << use_dll << ";" << endl
<< "options_.ramsey_policy = " << mod_file_struct.ramsey_model_present << ";" << endl
<< "options_.discretionary_policy = " << mod_file_struct.discretionary_policy_present
<< ";" << endl;
if (mod_file_struct.discretionary_policy_present)
mOutputFile << "M_.discretionary_orig_eq_nbr = " << original_model.equation_number() << ";"
<< endl;
if (parallel_local_files.size() > 0) if (parallel_local_files.size() > 0)
{ {
mOutputFile << "options_.parallel_info.local_files = {" << endl; mOutputFile << "options_.parallel_info.local_files = {" << endl;
for (size_t i = 0; i < parallel_local_files.size(); i++) for (const auto& parallel_local_file : parallel_local_files)
{ {
size_t j = parallel_local_files[i].find_last_of("/\\"); size_t j = parallel_local_file.find_last_of(R"(/\)");
if (j == string::npos) if (j == string::npos)
mOutputFile << "'', '" << parallel_local_files[i] << "';" << endl; mOutputFile << "'', '" << parallel_local_file << "';" << endl;
else else
mOutputFile << "'" << parallel_local_files[i].substr(0, j+1) << "', '" mOutputFile << "'" << parallel_local_file.substr(0, j + 1) << "', '"
<< parallel_local_files[i].substr(j+1, string::npos) << "';" << endl; << parallel_local_file.substr(j + 1) << "';" << endl;
} }
mOutputFile << "};" << endl; mOutputFile << "};" << endl;
} }
if (dynamic_model.isHessianComputed())
{
mOutputFile << "M_.nonzero_hessian_eqs = "; mOutputFile << "M_.nonzero_hessian_eqs = ";
if (mod_file_struct.ramsey_model_present)
orig_ramsey_dynamic_model.printNonZeroHessianEquations(mOutputFile);
else
dynamic_model.printNonZeroHessianEquations(mOutputFile); dynamic_model.printNonZeroHessianEquations(mOutputFile);
mOutputFile << ";" << endl mOutputFile << ";" << endl << "M_.hessian_eq_zero = isempty(M_.nonzero_hessian_eqs);" << endl;
<< "M_.hessian_eq_zero = isempty(M_.nonzero_hessian_eqs);" << endl; }
config_file.writeCluster(mOutputFile); if (!onlymodel)
config.writeCluster(mOutputFile);
if (byte_code) if (bytecode)
mOutputFile << "if exist('bytecode') ~= 3" << endl mOutputFile << "if exist('bytecode') ~= 3" << endl
<< " error('DYNARE: Can''t find bytecode DLL. Please compile it or remove the ''bytecode'' option.')" << endl << " error('DYNARE: Can''t find bytecode DLL. Please compile it or remove the "
"''bytecode'' option.')"
<< endl
<< "end" << endl; << "end" << endl;
bool hasModelChanged = !dynamic_model.isChecksumMatching(basename); mOutputFile << "M_.eq_nbr = " << dynamic_model.equation_number() << ";" << endl
if (!check_model_changes) << "M_.ramsey_orig_eq_nbr = " << mod_file_struct.ramsey_orig_eq_nbr << ";" << endl
hasModelChanged = true; << "M_.ramsey_orig_endo_nbr = " << mod_file_struct.ramsey_orig_endo_nbr << ";" << endl
<< "M_.set_auxiliary_variables = exist(['./+' M_.fname "
if (hasModelChanged) "'/set_auxiliary_variables.m'], 'file') == 2;"
{ << endl;
// Erase possible remnants of previous runs
unlink((basename + "_dynamic.m").c_str());
unlink((basename + "_dynamic.cod").c_str());
unlink((basename + "_dynamic.bin").c_str());
unlink((basename + "_static.m").c_str());
unlink((basename + "_static.cod").c_str());
unlink((basename + "_static.bin").c_str());
unlink((basename + "_steadystate2.m").c_str()); epilogue.writeOutput(mOutputFile);
unlink((basename + "_set_auxiliary_variables.m").c_str());
}
if (!use_dll)
{
mOutputFile << "erase_compiled_function('" + basename + "_static');" << endl;
mOutputFile << "erase_compiled_function('" + basename + "_dynamic');" << endl;
}
#if defined(_WIN32) || defined(__CYGWIN32__) if (dynamic_model.equation_number() > 0)
# if (defined(_MSC_VER) && _MSC_VER < 1700)
// If using USE_DLL with MSVC 10.0 or earlier, check that the user didn't use a function not supported by the compiler (because MSVC <= 10.0 doesn't comply with C99 standard)
if (use_dll && msvc)
{ {
if (dynamic_model.isUnaryOpUsed(oAcosh)) dynamic_model.writeDriverOutput(mOutputFile, compute_xrefs);
if (!no_static)
{ {
cerr << "ERROR: acosh() function is not supported with USE_DLL option and older MSVC compilers; use Cygwin, MinGW or upgrade your MSVC compiler to 11.0 (2012) or later." << endl; static_model.writeDriverOutput(mOutputFile);
exit(EXIT_FAILURE); if (mod_file_struct.ramsey_model_present)
static_model.writeDriverRamseyMultipliersDerivativesSparseIndices(mOutputFile);
} }
if (dynamic_model.isUnaryOpUsed(oAsinh))
{
cerr << "ERROR: asinh() function is not supported with USE_DLL option and older MSVC compilers; use Cygwin, MinGW or upgrade your MSVC compiler to 11.0 (2012) or later." << endl;
exit(EXIT_FAILURE);
} }
if (dynamic_model.isUnaryOpUsed(oAtanh))
for (const auto& hm : heterogeneous_models)
hm.writeDriverOutput(mOutputFile);
if (onlymodel || gui)
for (const auto& statement : statements)
{ {
cerr << "ERROR: atanh() function is not supported with USE_DLL option and older MSVC compilers; use Cygwin, MinGW or upgrade your MSVC compiler to 11.0 (2012) or later." << endl; /* Special treatment for initval block: insert initial values for the
exit(EXIT_FAILURE); auxiliary variables and initialize exo det */
} if (auto ivs = dynamic_cast<InitValStatement*>(statement.get()); ivs)
{
ivs->writeOutput(mOutputFile, basename, minimal_workspace);
static_model.writeAuxVarInitval(mOutputFile, ExprNodeOutputType::matlabOutsideModel);
ivs->writeOutputPostInit(mOutputFile);
} }
# endif
#endif
// Compile the dynamic MEX file for use_dll option // Special treatment for endval block: insert initial values for the auxiliary variables
// When check_model_changes is true, don't force compile if MEX is fresher than source if (auto evs = dynamic_cast<EndValStatement*>(statement.get()); evs)
if (use_dll)
{ {
#if defined(_WIN32) || defined(__CYGWIN32__) || defined(__MINGW32__) evs->writeOutput(mOutputFile, basename, minimal_workspace);
if (msvc) static_model.writeAuxVarInitval(mOutputFile, ExprNodeOutputType::matlabOutsideModel);
// MATLAB/Windows + Microsoft Visual C++
mOutputFile << "dyn_mex('msvc', '" << basename << "', " << !check_model_changes << ")" << endl;
else if (cygwin)
// MATLAB/Windows + Cygwin g++
mOutputFile << "dyn_mex('cygwin', '" << basename << "', " << !check_model_changes << ")" << endl;
else if (mingw)
// MATLAB/Windows + MinGW g++
mOutputFile << "dyn_mex('mingw', '" << basename << "', " << !check_model_changes << ")" << endl;
else
mOutputFile << "if isoctave" << endl
<< " dyn_mex('', '" << basename << "', " << !check_model_changes << ")" << endl
<< "else" << endl
<< " error('When using the USE_DLL option on Matlab, you must give the ''cygwin'', ''msvc'', or ''mingw'' option to the ''dynare'' command')" << endl
<< "end" << endl;
#else
// other configurations
mOutputFile << "dyn_mex('', '" << basename << "', " << !check_model_changes << ")" << endl;
#endif
} }
// Add path for block option with M-files if (auto ips = dynamic_cast<InitParamStatement*>(statement.get()); ips)
if (block && !byte_code) ips->writeOutput(mOutputFile, basename, minimal_workspace);
mOutputFile << "addpath " << basename << ";" << endl;
mOutputFile << "M_.orig_eq_nbr = " << mod_file_struct.orig_eq_nbr << ";" << endl if (auto ss = dynamic_cast<ShocksStatement*>(statement.get()); ss)
<< "M_.eq_nbr = " << dynamic_model.equation_number() << ";" << endl ss->writeOutput(mOutputFile, basename, minimal_workspace);
<< "M_.ramsey_eq_nbr = " << mod_file_struct.ramsey_eq_nbr << ";" << endl
<< "M_.set_auxiliary_variables = exist(['./' M_.fname '_set_auxiliary_variables.m'], 'file') == 2;" << endl;
if (dynamic_model.equation_number() > 0) if (auto eps = dynamic_cast<EstimatedParamsStatement*>(statement.get()); eps)
{ eps->writeOutput(mOutputFile, basename, minimal_workspace);
dynamic_model.writeOutput(mOutputFile, basename, block, byte_code, use_dll, mod_file_struct.order_option, mod_file_struct.estimation_present, compute_xrefs, false);
if (!no_static) if (auto sgs = dynamic_cast<ShockGroupsStatement*>(statement.get()); sgs)
static_model.writeOutput(mOutputFile, block); sgs->writeOutput(mOutputFile, basename, minimal_workspace);
}
// Print statements if (gui)
for (vector<Statement *>::const_iterator it = statements.begin(); if (auto it = dynamic_cast<NativeStatement*>(statement.get()); it)
it != statements.end(); it++) it->writeOutput(mOutputFile, basename, minimal_workspace);
}
else
{ {
(*it)->writeOutput(mOutputFile, basename, minimal_workspace); for (const auto& statement : statements)
{
statement->writeOutput(mOutputFile, basename, minimal_workspace);
/* Special treatment for initval block: insert initial values for the /* Special treatment for initval block: insert initial values for the
auxiliary variables and initialize exo det */ auxiliary variables and initialize exo det */
InitValStatement *ivs = dynamic_cast<InitValStatement *>(*it); if (auto ivs = dynamic_cast<InitValStatement*>(statement.get()); ivs)
if (ivs != NULL)
{ {
static_model.writeAuxVarInitval(mOutputFile, oMatlabOutsideModel); static_model.writeAuxVarInitval(mOutputFile, ExprNodeOutputType::matlabOutsideModel);
ivs->writeOutputPostInit(mOutputFile); ivs->writeOutputPostInit(mOutputFile);
} }
// Special treatment for endval block: insert initial values for the auxiliary variables // Special treatment for endval block: insert initial values for the auxiliary variables
EndValStatement *evs = dynamic_cast<EndValStatement *>(*it); if (auto evs = dynamic_cast<EndValStatement*>(statement.get()); evs)
if (evs != NULL) static_model.writeAuxVarInitval(mOutputFile, ExprNodeOutputType::matlabOutsideModel);
static_model.writeAuxVarInitval(mOutputFile, oMatlabOutsideModel);
// Special treatment for load params and steady state statement: insert initial values for the auxiliary variables // Special treatment for load params and steady state statement: insert initial values for
LoadParamsAndSteadyStateStatement *lpass = dynamic_cast<LoadParamsAndSteadyStateStatement *>(*it); // the auxiliary variables
if (lpass && !no_static) if (auto lpass = dynamic_cast<LoadParamsAndSteadyStateStatement*>(statement.get());
static_model.writeAuxVarInitval(mOutputFile, oMatlabOutsideModel); lpass && !no_static)
static_model.writeAuxVarInitval(mOutputFile, ExprNodeOutputType::matlabOutsideModel);
} }
// Remove path for block option with M-files if (!notime)
if (block && !byte_code) mOutputFile << endl
mOutputFile << "rmpath " << basename << ";" << endl; << endl
<< "oo_.time = toc(tic0);" << endl
<< "disp(['Total computing time : ' dynsec2hms(oo_.time) ]);" << endl;
mOutputFile << "save('" << basename << "_results.mat', 'oo_', 'M_', 'options_');" << endl mOutputFile << "if ~exist([M_.dname filesep 'Output'],'dir')" << endl
<< " mkdir(M_.dname,'Output');" << endl
<< "end" << endl
<< "save([M_.dname filesep 'Output' filesep '" << basename
<< "_results.mat'], 'oo_', 'M_', 'options_');" << endl
<< "if exist('estim_params_', 'var') == 1" << endl << "if exist('estim_params_', 'var') == 1" << endl
<< " save('" << basename << "_results.mat', 'estim_params_', '-append');" << endl << "end" << endl << " save([M_.dname filesep 'Output' filesep '" << basename
<< "_results.mat'], 'estim_params_', '-append');" << endl
<< "end" << endl
<< "if exist('bayestopt_', 'var') == 1" << endl << "if exist('bayestopt_', 'var') == 1" << endl
<< " save('" << basename << "_results.mat', 'bayestopt_', '-append');" << endl << "end" << endl << " save([M_.dname filesep 'Output' filesep '" << basename
<< "_results.mat'], 'bayestopt_', '-append');" << endl
<< "end" << endl
<< "if exist('dataset_', 'var') == 1" << endl << "if exist('dataset_', 'var') == 1" << endl
<< " save('" << basename << "_results.mat', 'dataset_', '-append');" << endl << "end" << endl << " save([M_.dname filesep 'Output' filesep '" << basename
<< "_results.mat'], 'dataset_', '-append');" << endl
<< "end" << endl
<< "if exist('estimation_info', 'var') == 1" << endl << "if exist('estimation_info', 'var') == 1" << endl
<< " save('" << basename << "_results.mat', 'estimation_info', '-append');" << endl << "end" << endl << " save([M_.dname filesep 'Output' filesep '" << basename
<< "_results.mat'], 'estimation_info', '-append');" << endl
<< "end" << endl
<< "if exist('dataset_info', 'var') == 1" << endl << "if exist('dataset_info', 'var') == 1" << endl
<< " save('" << basename << "_results.mat', 'dataset_info', '-append');" << endl << "end" << endl << " save([M_.dname filesep 'Output' filesep '" << basename
<< "_results.mat'], 'dataset_info', '-append');" << endl
<< "end" << endl
<< "if exist('oo_recursive_', 'var') == 1" << endl << "if exist('oo_recursive_', 'var') == 1" << endl
<< " save('" << basename << "_results.mat', 'oo_recursive_', '-append');" << endl << "end" << endl; << " save([M_.dname filesep 'Output' filesep '" << basename
<< "_results.mat'], 'oo_recursive_', '-append');" << endl
config_file.writeEndParallel(mOutputFile); << "end" << endl
<< "if exist('options_mom_', 'var') == 1" << endl
<< " save([M_.dname filesep 'Output' filesep '" << basename
<< "_results.mat'], 'options_mom_', '-append');" << endl
<< "end" << endl;
mOutputFile << endl << endl config.writeEndParallel(mOutputFile);
<< "disp(['Total computing time : ' dynsec2hms(toc(tic0)) ]);" << endl;
if (!no_warn) if (!no_warn)
{ {
if (warnings.countWarnings() > 0) if (int num_warnings {warnings.numWarnings()}; num_warnings > 0)
mOutputFile << "disp('Note: " << warnings.countWarnings() << " warning(s) encountered in the preprocessor')" << endl; mOutputFile << "disp('Note: " << num_warnings
<< " warning(s) encountered in the preprocessor')" << endl;
mOutputFile << "if ~isempty(lastwarn)" << endl mOutputFile << "if ~isempty(lastwarn)" << endl
<< " disp('Note: warning(s) encountered in MATLAB/Octave code')" << endl << " disp('Note: warning(s) encountered in MATLAB/Octave code')" << endl
<< "end" << endl; << "end" << endl;
} }
}
if (!no_log)
mOutputFile << "diary off" << endl;
mOutputFile.close(); mOutputFile.close();
...@@ -927,419 +1349,94 @@ ModFile::writeOutputFiles(const string &basename, bool clear_all, bool clear_glo ...@@ -927,419 +1349,94 @@ ModFile::writeOutputFiles(const string &basename, bool clear_all, bool clear_glo
{ {
if (!no_static) if (!no_static)
{ {
static_model.writeStaticFile(basename, block, byte_code, use_dll, false); static_model.writeStaticFile(basename, use_dll, mexext, matlabroot, false);
static_model.writeParamsDerivativesFile(basename, false); static_model.writeParamsDerivativesFile<false>(basename);
} if (mod_file_struct.ramsey_model_present)
dynamic_model.writeDynamicFile(basename, block, byte_code, use_dll, mod_file_struct.order_option, false);
dynamic_model.writeParamsDerivativesFile(basename, false);
}
// Create steady state file
steady_state_model.writeSteadyStateFile(basename, mod_file_struct.ramsey_model_present, false);
}
if (!nopreprocessoroutput)
cout << "done" << endl;
}
void
ModFile::writeExternalFiles(const string &basename, FileOutputType output, LanguageOutputType language, const bool nopreprocessoroutput) const
{
switch (language)
{
case c:
writeExternalFilesC(basename, output);
break;
case cpp:
writeExternalFilesCC(basename, output);
break;
case julia:
writeExternalFilesJulia(basename, output, nopreprocessoroutput);
break;
default:
cerr << "This case shouldn't happen. Contact the authors of Dynare" << endl;
exit(EXIT_FAILURE);
}
}
void
ModFile::writeExternalFilesC(const string &basename, FileOutputType output) const
{
writeModelC(basename);
steady_state_model.writeSteadyStateFileC(basename, mod_file_struct.ramsey_model_present);
dynamic_model.writeDynamicFile(basename, block, byte_code, use_dll, mod_file_struct.order_option, false);
if (!no_static)
static_model.writeStaticFile(basename, false, false, true, false);
// static_model.writeStaticCFile(basename, block, byte_code, use_dll);
// static_model.writeParamsDerivativesFileC(basename, cuda);
// static_model.writeAuxVarInitvalC(mOutputFile, oMatlabOutsideModel, cuda);
dynamic_model.writeResidualsC(basename, cuda);
// dynamic_model.writeParamsDerivativesFileC(basename, cuda);
dynamic_model.writeFirstDerivativesC(basename, cuda);
if (output == second)
dynamic_model.writeSecondDerivativesC_csr(basename, cuda);
else if (output == third)
{
dynamic_model.writeSecondDerivativesC_csr(basename, cuda);
dynamic_model.writeThirdDerivativesC_csr(basename, cuda);
}
}
void
ModFile::writeModelC(const string &basename) const
{
string filename = basename + ".c";
ofstream mDriverCFile;
mDriverCFile.open(filename.c_str(), ios::out | ios::binary);
if (!mDriverCFile.is_open())
{
cerr << "Error: Can't open file " << filename << " for writing" << endl;
exit(EXIT_FAILURE);
}
mDriverCFile << "/*" << endl
<< " * " << filename << " : Driver file for Dynare C code" << endl
<< " *" << endl
<< " * Warning : this file is generated automatically by Dynare" << endl
<< " * from model file (.mod)" << endl
<< " */" << endl
<< endl
<< "#include \"dynare_driver.h\"" << endl
<< endl
<< "struct" << endl
<< "{" << endl;
// Write basic info
symbol_table.writeCOutput(mDriverCFile);
mDriverCFile << endl << "params.resize(param_nbr);" << endl;
if (dynamic_model.equation_number() > 0)
{
dynamic_model.writeCOutput(mDriverCFile, basename, block, byte_code, use_dll, mod_file_struct.order_option, mod_file_struct.estimation_present);
// if (!no_static)
// static_model.writeCOutput(mOutputFile, block);
}
// Print statements
for (vector<Statement *>::const_iterator it = statements.begin();
it != statements.end(); it++)
(*it)->writeCOutput(mDriverCFile, basename);
mDriverCFile << "} DynareInfo;" << endl;
mDriverCFile.close();
// Write informational m file
ofstream mOutputFile;
if (basename.size())
{
string fname(basename);
fname += ".m";
mOutputFile.open(fname.c_str(), ios::out | ios::binary);
if (!mOutputFile.is_open())
{ {
cerr << "ERROR: Can't open file " << fname if (use_dll)
<< " for writing" << endl; static_model.writeRamseyMultipliersDerivativesCFile(
exit(EXIT_FAILURE); basename, mexext, matlabroot, mod_file_struct.ramsey_orig_endo_nbr);
}
}
else else
{ static_model.writeRamseyMultipliersDerivativesMFile(
cerr << "ERROR: Missing file name" << endl; basename, mod_file_struct.ramsey_orig_endo_nbr);
exit(EXIT_FAILURE);
} }
mOutputFile << "%" << endl
<< "% Status : informational m file" << endl
<< "%" << endl
<< "% Warning : this file is generated automatically by Dynare" << endl
<< "% from model file (.mod)" << endl << endl
<< "disp('The following C file was successfully created:');" << endl
<< "ls preprocessorOutput.c" << endl << endl;
mOutputFile.close();
} }
void dynamic_model.writeDynamicFile(basename, use_dll, mexext, matlabroot, false);
ModFile::writeExternalFilesCC(const string &basename, FileOutputType output) const
{
writeModelCC(basename);
steady_state_model.writeSteadyStateFileC(basename, mod_file_struct.ramsey_model_present);
dynamic_model.writeDynamicFile(basename, block, byte_code, use_dll, mod_file_struct.order_option, false);
if (!no_static)
static_model.writeStaticFile(basename, false, false, true, false);
// static_model.writeStaticCFile(basename, block, byte_code, use_dll); dynamic_model.writeParamsDerivativesFile<false>(basename);
// static_model.writeParamsDerivativesFileC(basename, cuda);
// static_model.writeAuxVarInitvalC(mOutputFile, oMatlabOutsideModel, cuda);
// dynamic_model.writeResidualsC(basename, cuda);
// dynamic_model.writeParamsDerivativesFileC(basename, cuda);
dynamic_model.writeResidualsC(basename, cuda);
dynamic_model.writeFirstDerivativesC_csr(basename, cuda);
if (output == second)
dynamic_model.writeSecondDerivativesC_csr(basename, cuda);
else if (output == third)
{
dynamic_model.writeSecondDerivativesC_csr(basename, cuda);
dynamic_model.writeThirdDerivativesC_csr(basename, cuda);
}
}
void
ModFile::writeModelCC(const string &basename) const
{
string filename = basename + ".cc";
ofstream mDriverCFile;
mDriverCFile.open(filename.c_str(), ios::out | ios::binary);
if (!mDriverCFile.is_open())
{
cerr << "Error: Can't open file " << filename << " for writing" << endl;
exit(EXIT_FAILURE);
} }
mDriverCFile << "/*" << endl // Create steady state file
<< " * " << filename << " : Driver file for Dynare C++ code" << endl steady_state_model.writeSteadyStateFile(basename, false);
<< " *" << endl
<< " * Warning : this file is generated automatically by Dynare" << endl
<< " * from model file (.mod)" << endl
<< " */" << endl
<< endl
<< "#include \"dynare_cpp_driver.hh\"" << endl
<< endl
<< "DynareInfo::DynareInfo(void)" << endl
<< "{" << endl;
// Write basic info
symbol_table.writeCCOutput(mDriverCFile);
mDriverCFile << endl << "params.resize(param_nbr);" << endl;
if (dynamic_model.equation_number() > 0)
{
dynamic_model.writeCCOutput(mDriverCFile, basename, block, byte_code, use_dll, mod_file_struct.order_option, mod_file_struct.estimation_present);
// if (!no_static)
// static_model.writeCOutput(mOutputFile, block);
}
// Print statements
for (vector<Statement *>::const_iterator it = statements.begin();
it != statements.end(); it++)
(*it)->writeCOutput(mDriverCFile, basename);
mDriverCFile << "};" << endl; // Create epilogue file
mDriverCFile.close(); epilogue.writeEpilogueFile(basename);
// Write informational m file pac_model_table.writeTargetCoefficientsFile(basename);
ofstream mOutputFile;
if (basename.size()) for (const auto& hm : heterogeneous_models)
{ hm.writeModelFiles(basename, false);
string fname(basename);
fname += ".m";
mOutputFile.open(fname.c_str(), ios::out | ios::binary);
if (!mOutputFile.is_open())
{
cerr << "ERROR: Can't open file " << fname
<< " for writing" << endl;
exit(EXIT_FAILURE);
} }
} }
else
{
cerr << "ERROR: Missing file name" << endl;
exit(EXIT_FAILURE);
}
mOutputFile << "%" << endl
<< "% Status : informational m file" << endl
<< "%" << endl
<< "% Warning : this file is generated automatically by Dynare" << endl
<< "% from model file (.mod)" << endl << endl
<< "disp('The following C++ file was successfully created:');" << endl
<< "ls preprocessorOutput.cc" << endl << endl;
mOutputFile.close();
}
void void
ModFile::writeExternalFilesJulia(const string &basename, FileOutputType output, const bool nopreprocessoroutput) const ModFile::writeJuliaOutput(const string& basename) const
{
ofstream jlOutputFile;
if (basename.size())
{ {
string fname(basename);
fname += ".jl";
jlOutputFile.open(fname.c_str(), ios::out | ios::binary);
if (!jlOutputFile.is_open())
{
cerr << "ERROR: Can't open file " << fname
<< " for writing" << endl;
exit(EXIT_FAILURE);
}
}
else
{
cerr << "ERROR: Missing file name" << endl;
exit(EXIT_FAILURE);
}
jlOutputFile << "module " << basename << endl
<< "#" << endl
<< "# NB: this file was automatically generated by Dynare" << endl
<< "# from " << basename << ".mod" << endl
<< "#" << endl << endl
<< "using DynareModel" << endl
<< "using DynareOptions" << endl
<< "using DynareOutput" << endl << endl
<< "using Utils" << endl
<< "using SteadyState" << endl << endl
<< "using " << basename << "Static" << endl
<< "using " << basename << "Dynamic" << endl
<< "if isfile(\"" << basename << "SteadyState.jl" "\")" << endl
<< " using " << basename << "SteadyState" << endl
<< "end" << endl
<< "if isfile(\"" << basename << "SteadyState2.jl" "\")" << endl
<< " using " << basename << "SteadyState2" << endl
<< "end" << endl << endl
<< "export model_, options_, oo_" << endl;
// Write Output
jlOutputFile << endl
<< "oo_ = dynare_output()" << endl
<< "oo_.dynare_version = \"" << PACKAGE_VERSION << "\"" << endl;
// Write Options
jlOutputFile << endl
<< "options_ = dynare_options()" << endl
<< "options_.dynare_version = \"" << PACKAGE_VERSION << "\"" << endl;
if (linear == 1)
jlOutputFile << "options_.linear = true" << endl;
// Write Model
jlOutputFile << endl
<< "model_ = dynare_model()" << endl
<< "model_.fname = \"" << basename << "\"" << endl
<< "model_.dynare_version = \"" << PACKAGE_VERSION << "\"" << endl
<< "model_.sigma_e = zeros(Float64, " << symbol_table.exo_nbr() << ", "
<< symbol_table.exo_nbr() << ")" << endl
<< "model_.correlation_matrix = ones(Float64, " << symbol_table.exo_nbr() << ", "
<< symbol_table.exo_nbr() << ")" << endl
<< "model_.orig_eq_nbr = " << mod_file_struct.orig_eq_nbr << endl
<< "model_.eq_nbr = " << dynamic_model.equation_number() << endl
<< "model_.ramsey_eq_nbr = " << mod_file_struct.ramsey_eq_nbr << endl;
if (mod_file_struct.calibrated_measurement_errors)
jlOutputFile << "model_.h = zeros(Float64,"
<< symbol_table.observedVariablesNbr() << ", "
<< symbol_table.observedVariablesNbr() << ");" << endl
<< "model_.correlation_matrix_me = ones(Float64, "
<< symbol_table.observedVariablesNbr() << ", "
<< symbol_table.observedVariablesNbr() << ");" << endl;
else
jlOutputFile << "model_.h = zeros(Float64, 1, 1)" << endl
<< "model_.correlation_matrix_me = ones(Float64, 1, 1)" << endl;
if (!nopreprocessoroutput)
cout << "Processing outputs ..." << endl;
symbol_table.writeJuliaOutput(jlOutputFile);
if (dynamic_model.equation_number() > 0) if (dynamic_model.equation_number() > 0)
{ {
dynamic_model.writeOutput(jlOutputFile, basename, false, false, false,
mod_file_struct.order_option,
mod_file_struct.estimation_present, false, true);
if (!no_static) if (!no_static)
{ {
static_model.writeStaticFile(basename, false, false, false, true); static_model.writeStaticFile(basename, false, "", {}, true);
static_model.writeParamsDerivativesFile(basename, true); static_model.writeParamsDerivativesFile<true>(basename);
} }
dynamic_model.writeDynamicFile(basename, block, byte_code, use_dll, dynamic_model.writeDynamicFile(basename, use_dll, "", {}, true);
mod_file_struct.order_option, true); dynamic_model.writeParamsDerivativesFile<true>(basename);
dynamic_model.writeParamsDerivativesFile(basename, true);
} }
steady_state_model.writeSteadyStateFile(basename, mod_file_struct.ramsey_model_present, true); steady_state_model.writeSteadyStateFile(basename, true);
// Print statements (includes parameter values)
for (vector<Statement *>::const_iterator it = statements.begin();
it != statements.end(); it++)
(*it)->writeJuliaOutput(jlOutputFile, basename);
jlOutputFile << "model_.static = " << basename << "Static.static!" << endl
<< "model_.dynamic = " << basename << "Dynamic.dynamic!" << endl
<< "if isfile(\"" << basename << "SteadyState.jl" "\")" << endl
<< " model_.user_written_analytical_steady_state = true" << endl
<< " model_.steady_state = " << basename << "SteadyState.steady_state!" << endl
<< "end" << endl
<< "if isfile(\"" << basename << "SteadyState2.jl" "\")" << endl
<< " model_.analytical_steady_state = true" << endl
<< " model_.steady_state = " << basename << "SteadyState2.steady_state!" << endl
<< "end" << endl
<< "if isfile(\"" << basename << "StaticParamsDerivs.jl" "\")" << endl
<< " using " << basename << "StaticParamsDerivs" << endl
<< " model_.static_params_derivs = " << basename << "StaticParamsDerivs.params_derivs" << endl
<< "end" << endl
<< "if isfile(\"" << basename << "DynamicParamsDerivs.jl" "\")" << endl
<< " using " << basename << "DynamicParamsDerivs" << endl
<< " model_.dynamic_params_derivs = " << basename << "DynamicParamsDerivs.params_derivs" << endl
<< "end" << endl
<< "end" << endl;
jlOutputFile.close();
if (!nopreprocessoroutput)
cout << "done" << endl;
} }
void void
ModFile::writeJsonOutput(const string &basename, JsonOutputPointType json, JsonFileOutputType json_output_mode, bool onlyjson, const bool nopreprocessoroutput, bool jsonderivsimple) ModFile::writeJsonOutput(const string& basename, JsonOutputPointType json,
JsonFileOutputType json_output_mode, bool onlyjson, bool jsonderivsimple)
{ {
if (json == nojson) if (json == JsonOutputPointType::nojson)
return; return;
if (json == parsing || json == checkpass) if (json == JsonOutputPointType::parsing || json == JsonOutputPointType::checkpass)
symbol_table.freeze(); symbol_table.freeze();
if (json_output_mode == standardout) if (json_output_mode == JsonFileOutputType::standardout)
cout << "//-- BEGIN JSON --// " << endl cout << "//-- BEGIN JSON --// " << endl << "{" << endl;
<< "{" << endl;
writeJsonOutputParsingCheck(basename, json_output_mode, json == transformpass, json == computingpass); writeJsonOutputParsingCheck(basename, json_output_mode,
json == JsonOutputPointType::transformpass,
json == JsonOutputPointType::computingpass);
if (json == parsing || json == checkpass) if (json == JsonOutputPointType::parsing || json == JsonOutputPointType::checkpass)
symbol_table.unfreeze(); symbol_table.unfreeze();
if (json == computingpass) if (json == JsonOutputPointType::computingpass)
writeJsonComputingPassOutput(basename, json_output_mode, jsonderivsimple); writeJsonComputingPassOutput(basename, json_output_mode, jsonderivsimple);
if (json_output_mode == standardout) if (json_output_mode == JsonFileOutputType::standardout)
cout << "}" << endl cout << "}" << endl << "//-- END JSON --// " << endl;
<< "//-- END JSON --// " << endl;
if (!nopreprocessoroutput)
switch (json) switch (json)
{ {
case parsing: case JsonOutputPointType::parsing:
cout << "JSON written after Parsing step." << endl; cout << "JSON written after Parsing step." << endl;
break; break;
case checkpass: case JsonOutputPointType::checkpass:
cout << "JSON written after Check step." << endl; cout << "JSON written after Check step." << endl;
break; break;
case transformpass: case JsonOutputPointType::transformpass:
cout << "JSON written after Transform step." << endl; cout << "JSON written after Transform step." << endl;
break; break;
case computingpass: case JsonOutputPointType::computingpass:
cout << "JSON written after Computing step." << endl; cout << "JSON written after Computing step." << endl;
break; break;
case nojson: case JsonOutputPointType::nojson:
cerr << "ModFile::writeJsonOutput: should not arrive here." << endl; cerr << "ModFile::writeJsonOutput: should not arrive here." << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
...@@ -1349,24 +1446,55 @@ ModFile::writeJsonOutput(const string &basename, JsonOutputPointType json, JsonF ...@@ -1349,24 +1446,55 @@ ModFile::writeJsonOutput(const string &basename, JsonOutputPointType json, JsonF
} }
void void
ModFile::writeJsonOutputParsingCheck(const string &basename, JsonFileOutputType json_output_mode, bool transformpass, bool computingpass) const ModFile::writeJsonOutputParsingCheck(const string& basename, JsonFileOutputType json_output_mode,
bool transformpass, bool computingpass) const
{ {
ostringstream output; ostringstream output;
output << "{" << endl; output << "{" << endl;
symbol_table.writeJsonOutput(output); symbol_table.writeJsonOutput(output);
output << ", "; output << ", ";
if (!heterogeneity_table.empty())
{
heterogeneity_table.writeJsonOutput(output);
output << ", ";
}
dynamic_model.writeJsonOutput(output); dynamic_model.writeJsonOutput(output);
output << ", ";
static_model.writeJsonOutput(output);
if (!statements.empty() || !var_model_table.empty() || !trend_component_model_table.empty())
{
output << R"(, "statements": [)";
if (!var_model_table.empty())
{
var_model_table.writeJsonOutput(output);
output << ", ";
}
if (!trend_component_model_table.empty())
{
trend_component_model_table.writeJsonOutput(output);
output << ", ";
}
if (!var_expectation_model_table.empty())
{
var_expectation_model_table.writeJsonOutput(output);
output << ", ";
}
if (!statements.empty()) if (!pac_model_table.empty())
{ {
output << ", \"statements\": ["; pac_model_table.writeJsonOutput(output);
for (vector<Statement *>::const_iterator it = statements.begin(); output << ", ";
it != statements.end(); it++) }
for (bool printed_something {false}; auto& it : statements)
{ {
if (it != statements.begin()) if (exchange(printed_something, true))
output << ", " << endl; output << ", " << endl;
(*it)->writeJsonOutput(output); it->writeJsonOutput(output);
} }
output << "]" << endl; output << "]" << endl;
} }
...@@ -1376,6 +1504,9 @@ ModFile::writeJsonOutputParsingCheck(const string &basename, JsonFileOutputType ...@@ -1376,6 +1504,9 @@ ModFile::writeJsonOutputParsingCheck(const string &basename, JsonFileOutputType
output << ","; output << ",";
dynamic_model.writeJsonDynamicModelInfo(output); dynamic_model.writeJsonDynamicModelInfo(output);
} }
output << R"(, "steady_state_model": )" << mod_file_struct.steady_state_model_present << endl;
output << "}" << endl; output << "}" << endl;
ostringstream original_model_output; ostringstream original_model_output;
...@@ -1384,6 +1515,31 @@ ModFile::writeJsonOutputParsingCheck(const string &basename, JsonFileOutputType ...@@ -1384,6 +1515,31 @@ ModFile::writeJsonOutputParsingCheck(const string &basename, JsonFileOutputType
{ {
original_model_output << "{"; original_model_output << "{";
original_model.writeJsonOriginalModelOutput(original_model_output); original_model.writeJsonOriginalModelOutput(original_model_output);
if (!statements.empty() || !var_model_table.empty() || !trend_component_model_table.empty())
{
original_model_output << endl << R"(, "statements": [)";
if (!var_model_table.empty())
{
var_model_table.writeJsonOutput(original_model_output);
original_model_output << ", ";
}
if (!trend_component_model_table.empty())
{
trend_component_model_table.writeJsonOutput(original_model_output);
original_model_output << ", ";
}
if (!pac_model_table.empty())
{
pac_model_table.writeJsonOutput(original_model_output);
original_model_output << ", ";
}
for (bool printed_something {false}; const auto& it : statements)
{
original_model_output << (exchange(printed_something, true) ? "," : "") << endl;
it->writeJsonOutput(original_model_output);
}
original_model_output << "]" << endl;
}
original_model_output << "}" << endl; original_model_output << "}" << endl;
} }
...@@ -1393,36 +1549,32 @@ ModFile::writeJsonOutputParsingCheck(const string &basename, JsonFileOutputType ...@@ -1393,36 +1549,32 @@ ModFile::writeJsonOutputParsingCheck(const string &basename, JsonFileOutputType
steady_state_model.writeJsonSteadyStateFile(steady_state_model_output, steady_state_model.writeJsonSteadyStateFile(steady_state_model_output,
transformpass || computingpass); transformpass || computingpass);
if (json_output_mode == standardout) if (json_output_mode == JsonFileOutputType::standardout)
{ {
if (transformpass || computingpass) if (transformpass || computingpass)
cout << "\"transformed_modfile\": "; cout << R"("transformed_modfile": )";
else else
cout << "\"modfile\": "; cout << R"("modfile": )";
cout << output.str(); cout << output.str();
if (!original_model_output.str().empty()) if (!original_model_output.str().empty())
cout << ", \"original_model\": " << original_model_output.str(); cout << R"(, "original_model": )" << original_model_output.str();
if (!steady_state_model_output.str().empty()) if (!steady_state_model_output.str().empty())
cout << ", \"steady_state_model\": " << steady_state_model_output.str(); cout << R"(, "steady_state_model": )" << steady_state_model_output.str();
} }
else else
{ {
ofstream jsonOutputFile; if (!basename.size())
if (basename.size())
{ {
string fname(basename); cerr << "ERROR: Missing file name" << endl;
fname += ".json";
jsonOutputFile.open(fname.c_str(), ios::out | ios::binary);
if (!jsonOutputFile.is_open())
{
cerr << "ERROR: Can't open file " << fname << " for writing" << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
}
else filesystem::create_directories(basename + "/model/json");
const filesystem::path fname {basename + "/model/json/modfile.json"};
ofstream jsonOutputFile {fname, ios::out | ios::binary};
if (!jsonOutputFile.is_open())
{ {
cerr << "ERROR: Missing file name" << endl; cerr << "ERROR: Can't open file " << fname.string() << " for writing" << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
...@@ -1433,12 +1585,11 @@ ModFile::writeJsonOutputParsingCheck(const string &basename, JsonFileOutputType ...@@ -1433,12 +1585,11 @@ ModFile::writeJsonOutputParsingCheck(const string &basename, JsonFileOutputType
{ {
if (basename.size()) if (basename.size())
{ {
string fname(basename); const filesystem::path fname {basename + "/model/json/modfile-original.json"};
fname += "_original.json"; jsonOutputFile.open(fname, ios::out | ios::binary);
jsonOutputFile.open(fname.c_str(), ios::out | ios::binary);
if (!jsonOutputFile.is_open()) if (!jsonOutputFile.is_open())
{ {
cerr << "ERROR: Can't open file " << fname << " for writing" << endl; cerr << "ERROR: Can't open file " << fname.string() << " for writing" << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
...@@ -1455,12 +1606,11 @@ ModFile::writeJsonOutputParsingCheck(const string &basename, JsonFileOutputType ...@@ -1455,12 +1606,11 @@ ModFile::writeJsonOutputParsingCheck(const string &basename, JsonFileOutputType
{ {
if (basename.size()) if (basename.size())
{ {
string fname(basename); const filesystem::path fname {basename + "/model/json/steady_state_model.json"};
fname += "_steady_state_model.json"; jsonOutputFile.open(fname, ios::out | ios::binary);
jsonOutputFile.open(fname.c_str(), ios::out | ios::binary);
if (!jsonOutputFile.is_open()) if (!jsonOutputFile.is_open())
{ {
cerr << "ERROR: Can't open file " << fname << " for writing" << endl; cerr << "ERROR: Can't open file " << fname.string() << " for writing" << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
...@@ -1477,15 +1627,17 @@ ModFile::writeJsonOutputParsingCheck(const string &basename, JsonFileOutputType ...@@ -1477,15 +1627,17 @@ ModFile::writeJsonOutputParsingCheck(const string &basename, JsonFileOutputType
} }
void void
ModFile::writeJsonComputingPassOutput(const string &basename, JsonFileOutputType json_output_mode, bool jsonderivsimple) const ModFile::writeJsonComputingPassOutput(const string& basename, JsonFileOutputType json_output_mode,
bool jsonderivsimple) const
{ {
if (basename.empty() && json_output_mode != standardout) if (basename.empty() && json_output_mode != JsonFileOutputType::standardout)
{ {
cerr << "ERROR: Missing file name" << endl; cerr << "ERROR: Missing file name" << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
ostringstream tmp_out, static_output, dynamic_output, static_paramsd_output, dynamic_paramsd_output; ostringstream tmp_out, static_output, dynamic_output, static_paramsd_output,
dynamic_paramsd_output;
static_output << "{"; static_output << "{";
static_model.writeJsonComputingPassOutput(static_output, !jsonderivsimple); static_model.writeJsonComputingPassOutput(static_output, !jsonderivsimple);
...@@ -1495,64 +1647,72 @@ ModFile::writeJsonComputingPassOutput(const string &basename, JsonFileOutputType ...@@ -1495,64 +1647,72 @@ ModFile::writeJsonComputingPassOutput(const string &basename, JsonFileOutputType
dynamic_model.writeJsonComputingPassOutput(dynamic_output, !jsonderivsimple); dynamic_model.writeJsonComputingPassOutput(dynamic_output, !jsonderivsimple);
dynamic_output << "}"; dynamic_output << "}";
tmp_out << ""; static_model.writeJsonParamsDerivatives(tmp_out, !jsonderivsimple);
static_paramsd_output << "";
static_model.writeJsonParamsDerivativesFile(tmp_out, !jsonderivsimple);
if (!tmp_out.str().empty()) if (!tmp_out.str().empty())
static_paramsd_output << "{" << tmp_out.str() << "}" << endl; static_paramsd_output << "{" << tmp_out.str() << "}" << endl;
tmp_out.str(""); tmp_out.str("");
dynamic_paramsd_output << ""; dynamic_model.writeJsonParamsDerivatives(tmp_out, !jsonderivsimple);
dynamic_model.writeJsonParamsDerivativesFile(tmp_out, !jsonderivsimple);
if (!tmp_out.str().empty()) if (!tmp_out.str().empty())
dynamic_paramsd_output << "{" << tmp_out.str() << "}" << endl; dynamic_paramsd_output << "{" << tmp_out.str() << "}" << endl;
if (json_output_mode == standardout) if (json_output_mode == JsonFileOutputType::standardout)
{ {
cout << ", \"static_model\": " << static_output.str() << endl cout << R"(, "static_model": )" << static_output.str() << endl
<< ", \"dynamic_model\": " << dynamic_output.str() << endl; << R"(, "dynamic_model": )" << dynamic_output.str() << endl;
if (!static_paramsd_output.str().empty()) if (!static_paramsd_output.str().empty())
cout << ", \"static_params_deriv\": " << static_paramsd_output.str() << endl; cout << R"(, "static_params_deriv": )" << static_paramsd_output.str() << endl;
if (!dynamic_paramsd_output.str().empty()) if (!dynamic_paramsd_output.str().empty())
cout << ", \"dynamic_params_deriv\": " << dynamic_paramsd_output.str() << endl; cout << R"(, "dynamic_params_deriv": )" << dynamic_paramsd_output.str() << endl;
} }
else else
{ {
string fname_original, fname_static, fname_dynamic; filesystem::create_directories(basename + "/model/json");
fname_static = basename + "_static.json";
fname_dynamic = basename + "_dynamic.json";
writeJsonFileHelper(fname_static, static_output); writeJsonFileHelper(basename + "/model/json/static.json", static_output);
writeJsonFileHelper(fname_dynamic, dynamic_output); writeJsonFileHelper(basename + "/model/json/dynamic.json", dynamic_output);
if (!static_paramsd_output.str().empty()) if (!static_paramsd_output.str().empty())
{ writeJsonFileHelper(basename + "/model/json/static_params_derivs.json",
string fname_static_params; static_paramsd_output);
fname_static_params = basename + "_static_params_derivs.json";
writeJsonFileHelper(fname_static_params, static_paramsd_output);
}
if (!dynamic_paramsd_output.str().empty()) if (!dynamic_paramsd_output.str().empty())
{ writeJsonFileHelper(basename + "/model/json/params_derivs.json", dynamic_paramsd_output);
string fname_dynamic_params;
fname_dynamic_params = basename + "_params_derivs.json";
writeJsonFileHelper(fname_dynamic_params, dynamic_paramsd_output);
}
} }
} }
void void
ModFile::writeJsonFileHelper(string &fname, ostringstream &output) const ModFile::writeJsonFileHelper(const filesystem::path& fname, ostringstream& output) const
{ {
ofstream jsonOutput; ofstream jsonOutput {fname, ios::out | ios::binary};
jsonOutput.open(fname.c_str(), ios::out | ios::binary);
if (!jsonOutput.is_open()) if (!jsonOutput.is_open())
{ {
cerr << "ERROR: Can't open file " << fname << " for writing" << endl; cerr << "ERROR: Can't open file " << fname.string() << " for writing" << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
jsonOutput << output.str(); jsonOutput << output.str();
jsonOutput.close(); jsonOutput.close();
} }
filesystem::path
ModFile::unique_path()
{
filesystem::path path;
string possible_characters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
random_device rd;
mt19937 generator(rd());
uniform_int_distribution distribution {0, static_cast<int>(possible_characters.size()) - 1};
do
{
constexpr int rand_length = 10;
string rand_str(rand_length, '\0');
for (auto& dis : rand_str)
dis = possible_characters[distribution(generator)];
path = rand_str;
}
while (exists(path));
return path;
}
/* /*
* Copyright (C) 2006-2017 Dynare Team * Copyright © 2006-2024 Dynare Team
* *
* This file is part of Dynare. * This file is part of Dynare.
* *
...@@ -14,48 +14,56 @@ ...@@ -14,48 +14,56 @@
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Dynare. If not, see <http://www.gnu.org/licenses/>. * along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/ */
#ifndef _MOD_FILE_HH #ifndef MOD_FILE_HH
#define _MOD_FILE_HH #define MOD_FILE_HH
using namespace std;
#include <ostream>
#include <ctime> #include <ctime>
#include <filesystem>
#include <iostream> #include <iostream>
#include <ostream>
#include <sstream> #include <sstream>
#include "SymbolTable.hh" #include "Configuration.hh"
#include "DynamicModel.hh"
#include "ExtendedPreprocessorTypes.hh"
#include "ExternalFunctionsTable.hh"
#include "HeterogeneityTable.hh"
#include "HeterogeneousModel.hh"
#include "ModelEquationBlock.hh"
#include "NumericalConstants.hh" #include "NumericalConstants.hh"
#include "NumericalInitialization.hh" #include "NumericalInitialization.hh"
#include "StaticModel.hh"
#include "DynamicModel.hh"
#include "SteadyStateModel.hh"
#include "Statement.hh" #include "Statement.hh"
#include "ExternalFunctionsTable.hh" #include "StaticModel.hh"
#include "ConfigFile.hh" #include "SubModel.hh"
#include "SymbolTable.hh"
#include "WarningConsolidation.hh" #include "WarningConsolidation.hh"
#include "ExtendedPreprocessorTypes.hh"
// for checksum computation using namespace std;
#ifndef PRIVATE_BUFFER_SIZE
# define PRIVATE_BUFFER_SIZE 1024
#endif
//! The abstract representation of a "mod" file //! The abstract representation of a "mod" file
class ModFile class ModFile
{ {
public: public:
ModFile(WarningConsolidation &warnings_arg); explicit ModFile(WarningConsolidation& warnings_arg);
~ModFile(); // For heterogeneity dimensions
HeterogeneityTable heterogeneity_table;
//! Symbol table //! Symbol table
SymbolTable symbol_table; SymbolTable symbol_table;
//! External Functions table //! External Functions table
ExternalFunctionsTable external_functions_table; ExternalFunctionsTable external_functions_table;
//! Numerical constants table //! Numerical constants table
NumericalConstants num_constants; NumericalConstants num_constants;
//! Var Model Table used for storing info about VAR models
VarModelTable var_model_table;
//! Trend Component Model Table used for storing info about trend component models
TrendComponentModelTable trend_component_model_table;
//! Table for storing the models declared with var_expectation_model
VarExpectationModelTable var_expectation_model_table;
//! PAC Model Table used for storing info about pac models
PacModelTable pac_model_table;
//! Expressions outside model block //! Expressions outside model block
DataTree expressions_tree; DataTree expressions_tree;
//! Original model, as declared in the "model" block, that won't be modified by the preprocessor //! Original model, as declared in the "model" block, that won't be modified by the preprocessor
...@@ -64,31 +72,36 @@ public: ...@@ -64,31 +72,36 @@ public:
DynamicModel dynamic_model; DynamicModel dynamic_model;
//! A copy of Dynamic model, for testing trends declared by user //! A copy of Dynamic model, for testing trends declared by user
DynamicModel trend_dynamic_model; DynamicModel trend_dynamic_model;
//! A model in which to create the FOC for the ramsey problem
DynamicModel ramsey_FOC_equations_dynamic_model;
//! A copy of the original model, used to test model linearity under ramsey problem //! A copy of the original model, used to test model linearity under ramsey problem
DynamicModel orig_ramsey_dynamic_model; OrigRamseyDynamicModel orig_ramsey_dynamic_model;
//! Epilogue model, as declared in the "epilogue" block
Epilogue epilogue;
//! Static model, as derived from the "model" block when leads and lags have been removed //! Static model, as derived from the "model" block when leads and lags have been removed
StaticModel static_model; StaticModel static_model;
//! Static model, as declared in the "steady_state_model" block if present //! Static model, as declared in the "steady_state_model" block if present
SteadyStateModel steady_state_model; SteadyStateModel steady_state_model;
// Heterogeneous model blocks, ordered per heterogeneity dimension ID
vector<HeterogeneousModel> heterogeneous_models;
//! Option linear //! Option linear
bool linear; bool linear {false};
//! Is the model block decomposed? //! Was the “block” option passed to the “model” block?
bool block; bool block {false};
//! Is the model stored in bytecode format (byte_code=true) or in a M-file (byte_code=false) //! Is the model stored in bytecode format (bytecode=true) or in a M-file (bytecode=false)
bool byte_code; bool bytecode {false};
//! Is the model stored in a MEX file ? (option "use_dll" of "model") /*! Is the model stored in a MEX file ? (option “use_dll”, either in the
bool use_dll; “model” block or on the preprocessor command line) */
bool use_dll {false};
//! Is the static model have to computed (no_static=false) or not (no_static=true). Option of 'model' //! Is the static model have to computed (no_static=false) or not (no_static=true). Option of
bool no_static; //! 'model'
bool no_static {false};
//! Is the 'differentiate_forward_vars' option used? //! Is the 'differentiate_forward_vars' option used?
bool differentiate_forward_vars; bool differentiate_forward_vars {false};
/*! If the 'differentiate_forward_vars' option is used, contains the set of /*! If the 'differentiate_forward_vars' option is used, contains the set of
endogenous with respect to which to do the transformation; endogenous with respect to which to do the transformation;
...@@ -97,52 +110,74 @@ public: ...@@ -97,52 +110,74 @@ public:
vector<string> differentiate_forward_vars_subset; vector<string> differentiate_forward_vars_subset;
//! Are nonstationary variables present ? //! Are nonstationary variables present ?
bool nonstationary_variables; bool nonstationary_variables {false};
//! Global evaluation context //! Global evaluation context
/*! Filled using initval blocks and parameters initializations */ /*! Filled using initval blocks and parameters initializations */
eval_context_t global_eval_context; eval_context_t global_eval_context;
//! Parameter used with lead/lag //! Parameter used with lead/lag
bool param_used_with_lead_lag; bool param_used_with_lead_lag {false};
//! Stores the list of extra files to be transefered during a parallel run //! Stores the list of extra files to be transefered during a parallel run
/*! (i.e. option parallel_local_files of model block) */ /*! (i.e. option parallel_local_files of model block) */
vector<string> parallel_local_files; vector<string> parallel_local_files;
/* Contents of the ramsey_constraints block.
Maps symb_id → (lower_bound, upper_bound).
NB: The two expr_t live in dynamic_model */
map<int, pair<expr_t, expr_t>> ramsey_constraints;
private: private:
//! List of statements //! List of statements
vector<Statement *> statements; vector<unique_ptr<Statement>> statements;
//! Structure of the mod file //! Structure of the mod file
ModFileStructure mod_file_struct; ModFileStructure mod_file_struct;
//! Warnings Encountered //! Warnings Encountered
WarningConsolidation& warnings; WarningConsolidation& warnings;
//! Functions used in writing of JSON outut. See writeJsonOutput //! Functions used in writing of JSON outut. See writeJsonOutput
void writeJsonOutputParsingCheck(const string &basename, JsonFileOutputType json_output_mode, bool transformpass, bool computingpass) const; void writeJsonOutputParsingCheck(const string& basename, JsonFileOutputType json_output_mode,
void writeJsonComputingPassOutput(const string &basename, JsonFileOutputType json_output_mode, bool jsonderivsimple) const; bool transformpass, bool computingpass) const;
void writeJsonFileHelper(string &fname, ostringstream &output) const; void writeJsonComputingPassOutput(const string& basename, JsonFileOutputType json_output_mode,
bool jsonderivsimple) const;
void writeJsonFileHelper(const filesystem::path& fname, ostringstream& output) const;
/* Generate a random temporary path, in the current directory. Equivalent to
boost::filesystem::unique_path(). Both are insecure, but currently there
is no better portable solution. Maybe in a later C++ standard? */
static filesystem::path unique_path();
/* Hack for removing a directory that is locked by MATLAB/Windows. The
directory is renamed before being deleted. The renaming must occur in the
same directory, otherwise it may fail if the destination is not on the
same filesystem. This technique is applied recursively to subdirectories.
Works even if the argument does not exist or is not a directory. */
static void remove_directory_with_matlab_lock(const filesystem::path& dir);
public: public:
//! Add a statement //! Add a statement
void addStatement(Statement *st); void addStatement(unique_ptr<Statement> st);
//! Add a statement at the front of the statements vector //! Add a statement at the front of the statements vector
void addStatementAtFront(Statement *st); void addStatementAtFront(unique_ptr<Statement> st);
//! Evaluate all the statements //! Evaluate all the statements
/*! \param warn_uninit Should a warning be displayed for uninitialized endogenous/exogenous/parameters ? */ /*! \param warn_uninit Should a warning be displayed for uninitialized
void evalAllExpressions(bool warn_uninit, const bool nopreprocessoroutput); * endogenous/exogenous/parameters ? */
void evalAllExpressions(bool warn_uninit);
//! Do some checking and fills mod_file_struct //! Do some checking and fills mod_file_struct
/*! \todo add check for number of equations and endogenous if ramsey_policy is present */ /*! \todo add check for number of equations and endogenous if ramsey_policy is present */
void checkPass(bool nostrict, bool stochastic); void checkPass(bool nostrict, bool stochastic);
//! Perform some transformations on the model (creation of auxiliary vars and equations) //! Perform some transformations on the model (creation of auxiliary vars and equations)
/*! \param compute_xrefs if true, equation cross references will be computed */ /*! \param compute_xrefs if true, equation cross references will be computed */
void transformPass(bool nostrict, bool stochastic, bool compute_xrefs, const bool nopreprocessoroutput); void transformPass(bool nostrict, bool stochastic, bool compute_xrefs, bool transform_unary_ops,
const string& exclude_eqs, const string& include_eqs);
//! Execute computations //! Execute computations
/*! \param no_tmp_terms if true, no temporary terms will be computed in the static and dynamic files */ /*! \param no_tmp_terms if true, no temporary terms will be computed in the static and dynamic
* files */
/*! \param params_derivs_order compute this order of derivs wrt parameters */ /*! \param params_derivs_order compute this order of derivs wrt parameters */
void computingPass(bool no_tmp_terms, FileOutputType output, int params_derivs_order, const bool nopreprocessoroutput); void computingPass(bool no_tmp_terms, OutputType output, int params_derivs_order);
//! Writes Matlab/Octave output files //! Writes Matlab/Octave output files
/*! /*!
\param basename The base name used for writing output files. Should be the name of the mod file without its extension \param basename The base name used for writing output files. Should be the name of the mod file
\param clear_all Should a "clear all" instruction be written to output ? without its extension \param clear_all Should a "clear all" instruction be written to output ?
\param console Are we in console mode ? \param console Are we in console mode ?
\param nograph Should we build the figures? \param nograph Should we build the figures?
\param nointeractive Should Dynare request user input? \param nointeractive Should Dynare request user input?
...@@ -151,31 +186,23 @@ public: ...@@ -151,31 +186,23 @@ public:
\param mingw Should the MEX command of use_dll be adapted for MinGW? \param mingw Should the MEX command of use_dll be adapted for MinGW?
\param compute_xrefs if true, equation cross references will be computed \param compute_xrefs if true, equation cross references will be computed
*/ */
void writeOutputFiles(const string &basename, bool clear_all, bool clear_global, bool no_log, bool no_warn, void writeMOutput(const string& basename, bool clear_all, bool clear_global, bool no_warn,
bool console, bool nograph, bool nointeractive, const ConfigFile &config_file, bool console, bool nograph, bool nointeractive, const Configuration& config,
bool check_model_changes, bool minimal_workspace, bool compute_xrefs bool check_model_changes, bool minimal_workspace, bool compute_xrefs,
#if defined(_WIN32) || defined(__CYGWIN32__) || defined(__MINGW32__) const string& mexext, const filesystem::path& matlabroot, bool onlymodel,
, bool cygwin, bool msvc, bool mingw bool gui, bool notime) const;
#endif
, const bool nopreprocessoroutput void writeJuliaOutput(const string& basename) const;
) const;
void writeExternalFiles(const string &basename, FileOutputType output, LanguageOutputType language, const bool nopreprocessoroutput) const;
void writeExternalFilesC(const string &basename, FileOutputType output) const;
void writeExternalFilesCC(const string &basename, FileOutputType output) const;
void writeExternalFilesJulia(const string &basename, FileOutputType output, const bool nopreprocessoroutput) const;
//! Writes C output files only => No further Matlab processing
void writeCOutputFiles(const string &basename) const;
void writeModelC(const string &basename) const;
//! Writes Cpp output files only => No further Matlab processing
void writeCCOutputFiles(const string &basename) const;
void writeModelCC(const string &basename) const;
void computeChecksum(); void computeChecksum();
//! Write JSON representation of ModFile object //! Write JSON representation of ModFile object
//! Initially created to enable Julia to work with .mod files //! Initially created to enable Julia to work with .mod files
//! Potentially outputs ModFile after the various parts of processing (parsing, checkPass, transformPass, computingPass) //! Potentially outputs ModFile after the various parts of processing (parsing, checkPass,
//! Allows user of other host language platforms (python, fortran, etc) to provide support for dynare .mod files //! transformPass, computingPass) Allows user of other host language platforms (python, fortran,
void writeJsonOutput(const string &basename, JsonOutputPointType json, JsonFileOutputType json_output_mode, bool onlyjson, const bool nopreprocessoroutput, bool jsonderivsimple = false); //! etc) to provide support for dynare .mod files
void writeJsonOutput(const string& basename, JsonOutputPointType json,
JsonFileOutputType json_output_mode, bool onlyjson,
bool jsonderivsimple = false);
}; };
#endif // ! MOD_FILE_HH #endif