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
  • 4.6
  • 5.x
  • aux_vars_fix
  • dynare_lite
  • julia
  • master
  • pylib
  • rework_pac
  • uop
  • wasm
  • created_preprocessor_repo
  • julia-6.2.0
12 results
Show changes
Showing
with 6915 additions and 4665 deletions
/* -*- C++ -*- */ /* -*- C++ -*- */
/* /*
* Copyright © 2003-2023 Dynare Team * Copyright © 2003-2025 Dynare Team
* *
* This file is part of Dynare. * This file is part of Dynare.
* *
...@@ -20,7 +20,6 @@ ...@@ -20,7 +20,6 @@
%{ %{
#include <cstring>
#include "ParsingDriver.hh" #include "ParsingDriver.hh"
using namespace std; using namespace std;
...@@ -60,7 +59,6 @@ string eofbuff; ...@@ -60,7 +59,6 @@ string eofbuff;
%x VERBATIM_BLOCK %x VERBATIM_BLOCK
%x NATIVE %x NATIVE
%x NATIVE_COMMENT %x NATIVE_COMMENT
%x DATES_STATEMENT
%x LINE1 %x LINE1
%x LINE2 %x LINE2
%x LINE3 %x LINE3
...@@ -70,7 +68,9 @@ string eofbuff; ...@@ -70,7 +68,9 @@ string eofbuff;
#define YY_USER_ACTION location_increment(yylloc, yytext); #define YY_USER_ACTION location_increment(yylloc, yytext);
%} %}
DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]) NAME [a-z_][a-z0-9_]*
FLOAT_NUMBER ((([0-9]*\.[0-9]+)|([0-9]+\.))([ed][-+]?[0-9]+)?)|([0-9]+[ed][-+]?[0-9]+)
DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]|[sh][12])
%% %%
/* Code put at the beginning of yylex() */ /* Code put at the beginning of yylex() */
...@@ -92,12 +92,12 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]) ...@@ -92,12 +92,12 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4])
} }
/* spaces, tabs and carriage returns are ignored */ /* spaces, tabs and carriage returns are ignored */
<INITIAL,DYNARE_STATEMENT,DYNARE_BLOCK,COMMENT,DATES_STATEMENT,LINE1,LINE2,LINE3>[[:space:]]+ { yylloc->step(); } <INITIAL,DYNARE_STATEMENT,DYNARE_BLOCK,COMMENT,LINE1,LINE2,LINE3>[[:space:]]+ { yylloc->step(); }
/* Comments */ /* Comments */
<INITIAL,DYNARE_STATEMENT,DYNARE_BLOCK,DATES_STATEMENT>%.* <INITIAL,DYNARE_STATEMENT,DYNARE_BLOCK>%.*
<INITIAL,DYNARE_STATEMENT,DYNARE_BLOCK,DATES_STATEMENT>"//".* <INITIAL,DYNARE_STATEMENT,DYNARE_BLOCK>"//".*
<INITIAL,DYNARE_STATEMENT,DYNARE_BLOCK,DATES_STATEMENT>"/*" {comment_caller = YY_START; BEGIN COMMENT;} <INITIAL,DYNARE_STATEMENT,DYNARE_BLOCK>"/*" {comment_caller = YY_START; BEGIN COMMENT;}
<COMMENT>"*/" {BEGIN comment_caller;} <COMMENT>"*/" {BEGIN comment_caller;}
<COMMENT>. <COMMENT>.
...@@ -111,6 +111,7 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]) ...@@ -111,6 +111,7 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4])
<INITIAL>predetermined_variables {BEGIN DYNARE_STATEMENT; return token::PREDETERMINED_VARIABLES;} <INITIAL>predetermined_variables {BEGIN DYNARE_STATEMENT; return token::PREDETERMINED_VARIABLES;}
<INITIAL>parameters {BEGIN DYNARE_STATEMENT; return token::PARAMETERS;} <INITIAL>parameters {BEGIN DYNARE_STATEMENT; return token::PARAMETERS;}
<INITIAL>model_local_variable {BEGIN DYNARE_STATEMENT; return token::MODEL_LOCAL_VARIABLE;} <INITIAL>model_local_variable {BEGIN DYNARE_STATEMENT; return token::MODEL_LOCAL_VARIABLE;}
<INITIAL>heterogeneity_dimension {BEGIN DYNARE_STATEMENT; return token::HETEROGENEITY_DIMENSION;}
<INITIAL>model_info {BEGIN DYNARE_STATEMENT; return token::MODEL_INFO;} <INITIAL>model_info {BEGIN DYNARE_STATEMENT; return token::MODEL_INFO;}
<INITIAL>estimation {BEGIN DYNARE_STATEMENT; return token::ESTIMATION;} <INITIAL>estimation {BEGIN DYNARE_STATEMENT; return token::ESTIMATION;}
<INITIAL>set_time {BEGIN DYNARE_STATEMENT; return token::SET_TIME;} <INITIAL>set_time {BEGIN DYNARE_STATEMENT; return token::SET_TIME;}
...@@ -155,7 +156,8 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]) ...@@ -155,7 +156,8 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4])
<INITIAL>bvar_density {BEGIN DYNARE_STATEMENT; return token::BVAR_DENSITY; } <INITIAL>bvar_density {BEGIN DYNARE_STATEMENT; return token::BVAR_DENSITY; }
<INITIAL>bvar_forecast {BEGIN DYNARE_STATEMENT; return token::BVAR_FORECAST; } <INITIAL>bvar_forecast {BEGIN DYNARE_STATEMENT; return token::BVAR_FORECAST; }
<INITIAL>bvar_irf {BEGIN DYNARE_STATEMENT; return token::BVAR_IRF; } <INITIAL>bvar_irf {BEGIN DYNARE_STATEMENT; return token::BVAR_IRF; }
<INITIAL>dynare_sensitivity {BEGIN DYNARE_STATEMENT; return token::DYNARE_SENSITIVITY;} <INITIAL>sensitivity {BEGIN DYNARE_STATEMENT; return token::SENSITIVITY;}
<INITIAL>dynare_sensitivity {BEGIN DYNARE_STATEMENT; return token::DYNARE_SENSITIVITY;} // Deprecated
<INITIAL>initval_file {BEGIN DYNARE_STATEMENT; return token::INITVAL_FILE;} <INITIAL>initval_file {BEGIN DYNARE_STATEMENT; return token::INITVAL_FILE;}
<INITIAL>histval_file {BEGIN DYNARE_STATEMENT; return token::HISTVAL_FILE;} <INITIAL>histval_file {BEGIN DYNARE_STATEMENT; return token::HISTVAL_FILE;}
<INITIAL>forecast {BEGIN DYNARE_STATEMENT; return token::FORECAST;} <INITIAL>forecast {BEGIN DYNARE_STATEMENT; return token::FORECAST;}
...@@ -232,6 +234,9 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]) ...@@ -232,6 +234,9 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4])
<INITIAL>occbin_constraints {BEGIN DYNARE_BLOCK; return token::OCCBIN_CONSTRAINTS;} <INITIAL>occbin_constraints {BEGIN DYNARE_BLOCK; return token::OCCBIN_CONSTRAINTS;}
<INITIAL>model_replace {BEGIN DYNARE_BLOCK; return token::MODEL_REPLACE;} <INITIAL>model_replace {BEGIN DYNARE_BLOCK; return token::MODEL_REPLACE;}
<INITIAL>pac_target_info {BEGIN DYNARE_BLOCK; return token::PAC_TARGET_INFO;} <INITIAL>pac_target_info {BEGIN DYNARE_BLOCK; return token::PAC_TARGET_INFO;}
<INITIAL>matched_irfs {BEGIN DYNARE_BLOCK; return token::MATCHED_IRFS;}
<INITIAL>matched_irfs_weights {BEGIN DYNARE_BLOCK; return token::MATCHED_IRFS_WEIGHTS;}
<INITIAL>perfect_foresight_controlled_paths {BEGIN DYNARE_BLOCK; return token::PERFECT_FORESIGHT_CONTROLLED_PATHS;}
/* For the semicolon after an "end" keyword */ /* For the semicolon after an "end" keyword */
<INITIAL>; {return Dynare::parser::token_type (yytext[0]);} <INITIAL>; {return Dynare::parser::token_type (yytext[0]);}
...@@ -242,7 +247,7 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]) ...@@ -242,7 +247,7 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4])
<DYNARE_STATEMENT>subsamples {return token::SUBSAMPLES;} <DYNARE_STATEMENT>subsamples {return token::SUBSAMPLES;}
<DYNARE_STATEMENT>options {return token::OPTIONS;} <DYNARE_STATEMENT>options {return token::OPTIONS;}
<DYNARE_STATEMENT>prior { <DYNARE_STATEMENT>prior {
yylval->build<string>(yytext); yylval->emplace<string>(yytext);
return token::PRIOR; return token::PRIOR;
} }
<INITIAL>std {BEGIN DYNARE_STATEMENT; return token::STD;} <INITIAL>std {BEGIN DYNARE_STATEMENT; return token::STD;}
...@@ -252,31 +257,12 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]) ...@@ -252,31 +257,12 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4])
<INITIAL>prior_function {BEGIN DYNARE_STATEMENT; return token::PRIOR_FUNCTION;} <INITIAL>prior_function {BEGIN DYNARE_STATEMENT; return token::PRIOR_FUNCTION;}
<INITIAL>posterior_function {BEGIN DYNARE_STATEMENT; return token::POSTERIOR_FUNCTION;} <INITIAL>posterior_function {BEGIN DYNARE_STATEMENT; return token::POSTERIOR_FUNCTION;}
/* Inside of a Dynare statement */ <DYNARE_STATEMENT,DYNARE_BLOCK>{DATE} {
<DYNARE_STATEMENT>{DATE} { yylval->emplace<string>(yytext);
char *yycopy = strdup(yytext); return token::DATE;
char *uput = yycopy + yyleng;
unput(')');
unput('\'');
while (uput > yycopy)
unput(*--uput);
unput('\'');
unput('(');
unput('s');
unput('e');
unput('t');
unput('a');
unput('d');
free( yycopy );
} }
<DYNARE_STATEMENT>${DATE} { yylloc->step();
#if (YY_FLEX_MAJOR_VERSION > 2) || (YY_FLEX_MAJOR_VERSION == 2 && YY_FLEX_MINOR_VERSION >= 6) /* Inside a Dynare statement */
yyout << yytext + 1;
#else
*yyout << yytext + 1;
#endif
}
<DYNARE_STATEMENT>dates {dates_parens_nb=0; BEGIN DATES_STATEMENT; yylval->build<string>("dates");}
<DYNARE_STATEMENT>file {return token::FILE;} <DYNARE_STATEMENT>file {return token::FILE;}
<DYNARE_STATEMENT>datafile {return token::DATAFILE;} <DYNARE_STATEMENT>datafile {return token::DATAFILE;}
<DYNARE_STATEMENT>dirname {return token::DIRNAME;} <DYNARE_STATEMENT>dirname {return token::DIRNAME;}
...@@ -309,10 +295,10 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]) ...@@ -309,10 +295,10 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4])
<DYNARE_STATEMENT>posterior_nograph {return token::POSTERIOR_NOGRAPH;} <DYNARE_STATEMENT>posterior_nograph {return token::POSTERIOR_NOGRAPH;}
<DYNARE_STATEMENT>nodisplay {return token::NODISPLAY;} <DYNARE_STATEMENT>nodisplay {return token::NODISPLAY;}
<DYNARE_STATEMENT>graph_format {return token::GRAPH_FORMAT;} <DYNARE_STATEMENT>graph_format {return token::GRAPH_FORMAT;}
<DYNARE_STATEMENT>eps {yylval->build<string>(yytext); return token::EPS;} <DYNARE_STATEMENT>eps {yylval->emplace<string>(yytext); return token::EPS;}
<DYNARE_STATEMENT>pdf {yylval->build<string>(yytext); return token::PDF;} <DYNARE_STATEMENT>pdf {yylval->emplace<string>(yytext); return token::PDF;}
<DYNARE_STATEMENT>fig {yylval->build<string>(yytext); return token::FIG;} <DYNARE_STATEMENT>fig {yylval->emplace<string>(yytext); return token::FIG;}
<DYNARE_STATEMENT>none {yylval->build<string>(yytext); return token::NONE;} <DYNARE_STATEMENT>none {yylval->emplace<string>(yytext); return token::NONE;}
<DYNARE_STATEMENT>print {return token::PRINT;} <DYNARE_STATEMENT>print {return token::PRINT;}
<DYNARE_STATEMENT>noprint {return token::NOPRINT;} <DYNARE_STATEMENT>noprint {return token::NOPRINT;}
<DYNARE_STATEMENT>conf_sig {return token::CONF_SIG;} <DYNARE_STATEMENT>conf_sig {return token::CONF_SIG;}
...@@ -420,6 +406,7 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]) ...@@ -420,6 +406,7 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4])
<DYNARE_STATEMENT>logarithmic_reduction {return token::LOGARITHMIC_REDUCTION;} <DYNARE_STATEMENT>logarithmic_reduction {return token::LOGARITHMIC_REDUCTION;}
<DYNARE_STATEMENT>use_univariate_filters_if_singularity_is_detected {return token::USE_UNIVARIATE_FILTERS_IF_SINGULARITY_IS_DETECTED;} <DYNARE_STATEMENT>use_univariate_filters_if_singularity_is_detected {return token::USE_UNIVARIATE_FILTERS_IF_SINGULARITY_IS_DETECTED;}
<DYNARE_STATEMENT>hybrid {return token::HYBRID;} <DYNARE_STATEMENT>hybrid {return token::HYBRID;}
<DYNARE_STATEMENT>use_first_order_solution {return token::USE_FIRST_ORDER_SOLUTION;}
<DYNARE_STATEMENT>default {return token::DEFAULT;} <DYNARE_STATEMENT>default {return token::DEFAULT;}
<DYNARE_STATEMENT>init2shocks {return token::INIT2SHOCKS;} <DYNARE_STATEMENT>init2shocks {return token::INIT2SHOCKS;}
...@@ -432,6 +419,11 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]) ...@@ -432,6 +419,11 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4])
<DYNARE_STATEMENT>kitagawa {return token::KITAGAWA;} <DYNARE_STATEMENT>kitagawa {return token::KITAGAWA;}
<DYNARE_STATEMENT>smooth {return token::SMOOTH;} <DYNARE_STATEMENT>smooth {return token::SMOOTH;}
<DYNARE_STATEMENT>stratified {return token::STRATIFIED;} <DYNARE_STATEMENT>stratified {return token::STRATIFIED;}
<DYNARE_STATEMENT>residual {
yylval->emplace<string>(yytext);
return token::RESIDUAL;
}
<DYNARE_STATEMENT>multinomial {return token::MULTINOMIAL;}
<DYNARE_STATEMENT>cpf_weights {return token::CPF_WEIGHTS;} <DYNARE_STATEMENT>cpf_weights {return token::CPF_WEIGHTS;}
<DYNARE_STATEMENT>amisanotristani {return token::AMISANOTRISTANI;} <DYNARE_STATEMENT>amisanotristani {return token::AMISANOTRISTANI;}
<DYNARE_STATEMENT>murrayjonesparslow {return token::MURRAYJONESPARSLOW;} <DYNARE_STATEMENT>murrayjonesparslow {return token::MURRAYJONESPARSLOW;}
...@@ -453,43 +445,40 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]) ...@@ -453,43 +445,40 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4])
<DYNARE_STATEMENT>fsolve_options {return token::FSOLVE_OPTIONS;} <DYNARE_STATEMENT>fsolve_options {return token::FSOLVE_OPTIONS;}
<DYNARE_STATEMENT>alpha { <DYNARE_STATEMENT>alpha {
yylval->build<string>(yytext); yylval->emplace<string>(yytext);
return token::ALPHA; return token::ALPHA;
} }
<DYNARE_STATEMENT>beta { <DYNARE_STATEMENT>beta {
yylval->build<string>(yytext); yylval->emplace<string>(yytext);
return token::BETA; return token::BETA;
} }
<DYNARE_STATEMENT>gamma { <DYNARE_STATEMENT>gamma {
yylval->build<string>(yytext); yylval->emplace<string>(yytext);
return token::GAMMA; return token::GAMMA;
} }
<DYNARE_STATEMENT>inv_gamma { <DYNARE_STATEMENT>inv_gamma {
yylval->build<string>(yytext); yylval->emplace<string>(yytext);
return token::INV_GAMMA; return token::INV_GAMMA;
} }
<DYNARE_STATEMENT>inv_gamma1 { <DYNARE_STATEMENT>inv_gamma1 {
yylval->build<string>(yytext); yylval->emplace<string>(yytext);
return token::INV_GAMMA1; return token::INV_GAMMA1;
} }
<DYNARE_STATEMENT>inv_gamma2 { <DYNARE_STATEMENT>inv_gamma2 {
yylval->build<string>(yytext); yylval->emplace<string>(yytext);
return token::INV_GAMMA2; return token::INV_GAMMA2;
} }
<DYNARE_STATEMENT>dirichlet { <DYNARE_STATEMENT>dirichlet {
yylval->build<string>(yytext); yylval->emplace<string>(yytext);
return token::DIRICHLET; return token::DIRICHLET;
} }
<DYNARE_STATEMENT>weibull { <DYNARE_STATEMENT>weibull {return token::WEIBULL;}
yylval->build<string>(yytext);
return token::WEIBULL;
}
<DYNARE_STATEMENT>normal { <DYNARE_STATEMENT>normal {
yylval->build<string>(yytext); yylval->emplace<string>(yytext);
return token::NORMAL; return token::NORMAL;
} }
<DYNARE_STATEMENT>uniform { <DYNARE_STATEMENT>uniform {
yylval->build<string>(yytext); yylval->emplace<string>(yytext);
return token::UNIFORM; return token::UNIFORM;
} }
<DYNARE_STATEMENT>gsig2_lmdm {return token::GSIG2_LMDM;} <DYNARE_STATEMENT>gsig2_lmdm {return token::GSIG2_LMDM;}
...@@ -500,13 +489,13 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]) ...@@ -500,13 +489,13 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4])
<DYNARE_STATEMENT>ncsk {return token::NCSK;} <DYNARE_STATEMENT>ncsk {return token::NCSK;}
<DYNARE_STATEMENT>nstd {return token::NSTD;} <DYNARE_STATEMENT>nstd {return token::NSTD;}
<DYNARE_STATEMENT>ninv { <DYNARE_STATEMENT>ninv {
yylval->build<string>(yytext); yylval->emplace<string>(yytext);
return token::NINV; return token::NINV;
} }
<DYNARE_STATEMENT>indxparr {return token::INDXPARR;} <DYNARE_STATEMENT>indxparr {return token::INDXPARR;}
<DYNARE_STATEMENT>indxovr {return token::INDXOVR;} <DYNARE_STATEMENT>indxovr {return token::INDXOVR;}
<DYNARE_STATEMENT>aband { <DYNARE_STATEMENT>aband {
yylval->build<string>(yytext); yylval->emplace<string>(yytext);
return token::ABAND; return token::ABAND;
} }
<DYNARE_STATEMENT>write_equation_tags {return token::WRITE_EQUATION_TAGS;} <DYNARE_STATEMENT>write_equation_tags {return token::WRITE_EQUATION_TAGS;}
...@@ -523,18 +512,18 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]) ...@@ -523,18 +512,18 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4])
<DYNARE_STATEMENT>indxgdls {return token::INDXGDLS;} <DYNARE_STATEMENT>indxgdls {return token::INDXGDLS;}
<DYNARE_STATEMENT>eq_ms {return token::EQ_MS;} <DYNARE_STATEMENT>eq_ms {return token::EQ_MS;}
<DYNARE_STATEMENT>cms { <DYNARE_STATEMENT>cms {
yylval->build<string>(yytext); yylval->emplace<string>(yytext);
return token::CMS; return token::CMS;
} }
<DYNARE_STATEMENT>ncms { <DYNARE_STATEMENT>ncms {
yylval->build<string>(yytext); yylval->emplace<string>(yytext);
return token::NCMS; return token::NCMS;
} }
<DYNARE_STATEMENT>eq_cms {return token::EQ_CMS;} <DYNARE_STATEMENT>eq_cms {return token::EQ_CMS;}
<DYNARE_STATEMENT>tlindx {return token::TLINDX;} <DYNARE_STATEMENT>tlindx {return token::TLINDX;}
<DYNARE_STATEMENT>tlnumber {return token::TLNUMBER;} <DYNARE_STATEMENT>tlnumber {return token::TLNUMBER;}
<DYNARE_STATEMENT>cnum { <DYNARE_STATEMENT>cnum {
yylval->build<string>(yytext); yylval->emplace<string>(yytext);
return token::CNUM; return token::CNUM;
} }
<DYNARE_STATEMENT>nodecomposition {return token::NODECOMPOSITION;}; <DYNARE_STATEMENT>nodecomposition {return token::NODECOMPOSITION;};
...@@ -616,18 +605,9 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]) ...@@ -616,18 +605,9 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4])
<DYNARE_STATEMENT>substitute_libs {return token::SUBSTITUTE_LIBS;} <DYNARE_STATEMENT>substitute_libs {return token::SUBSTITUTE_LIBS;}
<DYNARE_STATEMENT>compiler {return token::COMPILER;} <DYNARE_STATEMENT>compiler {return token::COMPILER;}
<DYNARE_STATEMENT>instruments {return token::INSTRUMENTS;} <DYNARE_STATEMENT>instruments {return token::INSTRUMENTS;}
<DYNARE_STATEMENT>hessian { <DYNARE_STATEMENT>hessian {return token::HESSIAN;}
yylval->build<string>(yytext); <DYNARE_STATEMENT>prior_variance {return token::PRIOR_VARIANCE;}
return token::HESSIAN; <DYNARE_STATEMENT>identity_matrix {return token::IDENTITY_MATRIX;}
}
<DYNARE_STATEMENT>prior_variance {
yylval->build<string>(yytext);
return token::PRIOR_VARIANCE;
}
<DYNARE_STATEMENT>identity_matrix {
yylval->build<string>(yytext);
return token::IDENTITY_MATRIX;
}
<DYNARE_STATEMENT>mcmc_jumping_covariance {return token::MCMC_JUMPING_COVARIANCE;} <DYNARE_STATEMENT>mcmc_jumping_covariance {return token::MCMC_JUMPING_COVARIANCE;}
/* These four (var, varexo, varexo_det, parameters) are for change_type */ /* These four (var, varexo, varexo_det, parameters) are for change_type */
...@@ -655,6 +635,7 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]) ...@@ -655,6 +635,7 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4])
<DYNARE_STATEMENT>homotopy_step_size_increase_success_count {return token::HOMOTOPY_STEP_SIZE_INCREASE_SUCCESS_COUNT;} <DYNARE_STATEMENT>homotopy_step_size_increase_success_count {return token::HOMOTOPY_STEP_SIZE_INCREASE_SUCCESS_COUNT;}
<DYNARE_STATEMENT>homotopy_linearization_fallback {return token::HOMOTOPY_LINEARIZATION_FALLBACK;} <DYNARE_STATEMENT>homotopy_linearization_fallback {return token::HOMOTOPY_LINEARIZATION_FALLBACK;}
<DYNARE_STATEMENT>homotopy_marginal_linearization_fallback {return token::HOMOTOPY_MARGINAL_LINEARIZATION_FALLBACK;} <DYNARE_STATEMENT>homotopy_marginal_linearization_fallback {return token::HOMOTOPY_MARGINAL_LINEARIZATION_FALLBACK;}
<DYNARE_STATEMENT>homotopy_exclude_varexo {return token::HOMOTOPY_EXCLUDE_VAREXO;}
<DYNARE_STATEMENT>nocheck {return token::NOCHECK; } <DYNARE_STATEMENT>nocheck {return token::NOCHECK; }
<DYNARE_STATEMENT>steady_solve_algo {return token::STEADY_SOLVE_ALGO;} <DYNARE_STATEMENT>steady_solve_algo {return token::STEADY_SOLVE_ALGO;}
...@@ -726,14 +707,6 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]) ...@@ -726,14 +707,6 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4])
<DYNARE_STATEMENT>lmmcp {return token::LMMCP;} <DYNARE_STATEMENT>lmmcp {return token::LMMCP;}
<DYNARE_STATEMENT>additional_optimizer_steps {return token::ADDITIONAL_OPTIMIZER_STEPS;} <DYNARE_STATEMENT>additional_optimizer_steps {return token::ADDITIONAL_OPTIMIZER_STEPS;}
<DYNARE_STATEMENT>bartlett_kernel_lag {return token::BARTLETT_KERNEL_LAG; } <DYNARE_STATEMENT>bartlett_kernel_lag {return token::BARTLETT_KERNEL_LAG; }
<DYNARE_STATEMENT>optimal {
yylval->build<string>(yytext);
return token::OPTIMAL;
}
<DYNARE_STATEMENT>diagonal {
yylval->build<string>(yytext);
return token::DIAGONAL;
}
<DYNARE_STATEMENT>gmm {return token::GMM;} <DYNARE_STATEMENT>gmm {return token::GMM;}
<DYNARE_STATEMENT>smm {return token::SMM;} <DYNARE_STATEMENT>smm {return token::SMM;}
<DYNARE_STATEMENT>irf_matching {return token::IRF_MATCHING;} <DYNARE_STATEMENT>irf_matching {return token::IRF_MATCHING;}
...@@ -777,32 +750,48 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]) ...@@ -777,32 +750,48 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4])
<DYNARE_STATEMENT>with_epilogue {return token::WITH_EPILOGUE;} <DYNARE_STATEMENT>with_epilogue {return token::WITH_EPILOGUE;}
<DYNARE_STATEMENT>heteroskedastic_filter {return token::HETEROSKEDASTIC_FILTER;} <DYNARE_STATEMENT>heteroskedastic_filter {return token::HETEROSKEDASTIC_FILTER;}
<DYNARE_STATEMENT>non_zero {return token::NON_ZERO;} <DYNARE_STATEMENT>non_zero {return token::NON_ZERO;}
<DYNARE_STATEMENT>preconditioner {return token::PRECONDITIONER;}
<DYNARE_STATEMENT>umfiter {return token::UMFITER;}
<DYNARE_STATEMENT>iterstack {return token::ITERSTACK;}
<DYNARE_STATEMENT>ilu {return token::ILU;}
<DYNARE_STATEMENT>iter_tol {return token::ITER_TOL;}
<DYNARE_STATEMENT>iter_maxit {return token::ITER_MAXIT;}
<DYNARE_STATEMENT>gmres_restart {return token::GMRES_RESTART;}
<DYNARE_STATEMENT>iterstack_maxlu {return token::ITERSTACK_MAXLU;}
<DYNARE_STATEMENT>iterstack_nperiods {return token::ITERSTACK_NPERIODS;}
<DYNARE_STATEMENT>iterstack_nlu {return token::ITERSTACK_NLU;}
<DYNARE_STATEMENT>iterstack_relu {return token::ITERSTACK_RELU;}
<DYNARE_STATEMENT>check_jacobian_singularity {return token::CHECK_JACOBIAN_SINGULARITY;}
<DYNARE_STATEMENT>\$[^$]*\$ { <DYNARE_STATEMENT>\$[^$]*\$ {
strtok(yytext+1, "$"); yylval->emplace<string>(yytext + 1).pop_back();
yylval->build<string>(yytext + 1);
return token::TEX_NAME; return token::TEX_NAME;
} }
/* Inside a Dynare block */ /* Inside a Dynare block */
<DYNARE_BLOCK>var {return token::VAR;} <DYNARE_BLOCK>var {return token::VAR;}
<DYNARE_BLOCK>varexo {return token::VAREXO;}
<DYNARE_BLOCK>stderr {return token::STDERR;} <DYNARE_BLOCK>stderr {return token::STDERR;}
<DYNARE_BLOCK>values {return token::VALUES;} <DYNARE_BLOCK>values {return token::VALUES;}
<DYNARE_BLOCK>corr {return token::CORR;} <DYNARE_BLOCK>corr {return token::CORR;}
<DYNARE_BLOCK>periods {return token::PERIODS;} <DYNARE_BLOCK>periods {return token::PERIODS;}
<DYNARE_BLOCK>scales {return token::SCALES;} <DYNARE_BLOCK>scales {return token::SCALES;}
<DYNARE_BLOCK>add { <DYNARE_BLOCK>add {
yylval->build<string>(yytext); yylval->emplace<string>(yytext);
return token::ADD; return token::ADD;
} }
<DYNARE_BLOCK>multiply { <DYNARE_BLOCK>multiply {
yylval->build<string>(yytext); yylval->emplace<string>(yytext);
return token::MULTIPLY; return token::MULTIPLY;
} }
<DYNARE_STATEMENT,DYNARE_BLOCK>cutoff {return token::CUTOFF;} <DYNARE_STATEMENT,DYNARE_BLOCK>cutoff {return token::CUTOFF;}
<DYNARE_STATEMENT,DYNARE_BLOCK>mfs {return token::MFS;} <DYNARE_STATEMENT,DYNARE_BLOCK>mfs {
yylval->emplace<string>(yytext);
return token::MFS;
}
<DYNARE_STATEMENT,DYNARE_BLOCK>static_mfs {return token::STATIC_MFS;} <DYNARE_STATEMENT,DYNARE_BLOCK>static_mfs {return token::STATIC_MFS;}
<DYNARE_STATEMENT,DYNARE_BLOCK>balanced_growth_test_tol {return token::BALANCED_GROWTH_TEST_TOL;} <DYNARE_STATEMENT,DYNARE_BLOCK>balanced_growth_test_tol {return token::BALANCED_GROWTH_TEST_TOL;}
<DYNARE_STATEMENT,DYNARE_BLOCK>heterogeneity {return token::HETEROGENEITY;}
<DYNARE_BLOCK>gamma_pdf {return token::GAMMA_PDF;} <DYNARE_BLOCK>gamma_pdf {return token::GAMMA_PDF;}
<DYNARE_BLOCK>beta_pdf {return token::BETA_PDF;} <DYNARE_BLOCK>beta_pdf {return token::BETA_PDF;}
<DYNARE_BLOCK>normal_pdf {return token::NORMAL_PDF;} <DYNARE_BLOCK>normal_pdf {return token::NORMAL_PDF;}
...@@ -814,19 +803,19 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]) ...@@ -814,19 +803,19 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4])
<DYNARE_BLOCK>dsge_prior_weight {return token::DSGE_PRIOR_WEIGHT;} <DYNARE_BLOCK>dsge_prior_weight {return token::DSGE_PRIOR_WEIGHT;}
<DYNARE_BLOCK>surprise {return token::SURPRISE;} <DYNARE_BLOCK>surprise {return token::SURPRISE;}
<DYNARE_BLOCK>bind { <DYNARE_BLOCK>bind {
yylval->build<string>(yytext); yylval->emplace<string>(yytext);
return token::BIND; return token::BIND;
} }
<DYNARE_BLOCK>relax { <DYNARE_BLOCK>relax {
yylval->build<string>(yytext); yylval->emplace<string>(yytext);
return token::RELAX; return token::RELAX;
} }
<DYNARE_BLOCK>error_bind { <DYNARE_BLOCK>error_bind {
yylval->build<string>(yytext); yylval->emplace<string>(yytext);
return token::ERROR_BIND; return token::ERROR_BIND;
} }
<DYNARE_BLOCK>error_relax { <DYNARE_BLOCK>error_relax {
yylval->build<string>(yytext); yylval->emplace<string>(yytext);
return token::ERROR_RELAX; return token::ERROR_RELAX;
} }
<DYNARE_BLOCK>relative_to_initval {return token::RELATIVE_TO_INITVAL;} <DYNARE_BLOCK>relative_to_initval {return token::RELATIVE_TO_INITVAL;}
...@@ -840,22 +829,24 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]) ...@@ -840,22 +829,24 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4])
<DYNARE_BLOCK,DYNARE_STATEMENT>auxname {return token::AUXNAME;} <DYNARE_BLOCK,DYNARE_STATEMENT>auxname {return token::AUXNAME;}
<DYNARE_BLOCK>auxname_target_nonstationary {return token::AUXNAME_TARGET_NONSTATIONARY;} <DYNARE_BLOCK>auxname_target_nonstationary {return token::AUXNAME_TARGET_NONSTATIONARY;}
<DYNARE_BLOCK,DYNARE_STATEMENT>kind { <DYNARE_BLOCK,DYNARE_STATEMENT>kind {
yylval->build<string>(yytext); yylval->emplace<string>(yytext);
return token::KIND; return token::KIND;
} }
<DYNARE_BLOCK,DYNARE_STATEMENT>ll { <DYNARE_BLOCK,DYNARE_STATEMENT>ll {
yylval->build<string>(yytext); yylval->emplace<string>(yytext);
return token::LL; return token::LL;
} }
<DYNARE_BLOCK,DYNARE_STATEMENT>dl { <DYNARE_BLOCK,DYNARE_STATEMENT>dl {
yylval->build<string>(yytext); yylval->emplace<string>(yytext);
return token::DL; return token::DL;
} }
<DYNARE_BLOCK,DYNARE_STATEMENT>dd { <DYNARE_BLOCK,DYNARE_STATEMENT>dd {
yylval->build<string>(yytext); yylval->emplace<string>(yytext);
return token::DD; return token::DD;
} }
<DYNARE_BLOCK>weights {return token::WEIGHTS;}
<DYNARE_BLOCK>exogenize {return token::EXOGENIZE;}
<DYNARE_BLOCK>endogenize {return token::ENDOGENIZE;}
/* Inside Dynare statement */ /* Inside Dynare statement */
<DYNARE_STATEMENT>solve_algo {return token::SOLVE_ALGO;} <DYNARE_STATEMENT>solve_algo {return token::SOLVE_ALGO;}
...@@ -865,17 +856,16 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]) ...@@ -865,17 +856,16 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4])
<DYNARE_STATEMENT>robust_lin_solve {return token::ROBUST_LIN_SOLVE;} <DYNARE_STATEMENT>robust_lin_solve {return token::ROBUST_LIN_SOLVE;}
<DYNARE_STATEMENT>drop {return token::DROP;} <DYNARE_STATEMENT>drop {return token::DROP;}
<DYNARE_STATEMENT>order {return token::ORDER;} <DYNARE_STATEMENT>order {return token::ORDER;}
<DYNARE_STATEMENT>sylvester {return token::SYLVESTER;}
<DYNARE_STATEMENT>lyapunov {return token::LYAPUNOV;} <DYNARE_STATEMENT>lyapunov {return token::LYAPUNOV;}
<DYNARE_STATEMENT>dr { <DYNARE_STATEMENT>dr {
yylval->build<string>(yytext); yylval->emplace<string>(yytext);
return token::DR; return token::DR;
} }
<DYNARE_STATEMENT>sylvester_fixed_point_tol {return token::SYLVESTER_FIXED_POINT_TOL;}
<DYNARE_STATEMENT>lyapunov_complex_threshold {return token::LYAPUNOV_COMPLEX_THRESHOLD;} <DYNARE_STATEMENT>lyapunov_complex_threshold {return token::LYAPUNOV_COMPLEX_THRESHOLD;}
<DYNARE_STATEMENT>lyapunov_fixed_point_tol {return token::LYAPUNOV_FIXED_POINT_TOL;} <DYNARE_STATEMENT>lyapunov_fixed_point_tol {return token::LYAPUNOV_FIXED_POINT_TOL;}
<DYNARE_STATEMENT>lyapunov_doubling_tol {return token::LYAPUNOV_DOUBLING_TOL;} <DYNARE_STATEMENT>lyapunov_doubling_tol {return token::LYAPUNOV_DOUBLING_TOL;}
<DYNARE_STATEMENT>dr_cycle_reduction_tol {return token::DR_CYCLE_REDUCTION_TOL;} <DYNARE_STATEMENT>dr_cycle_reduction_tol {return token::DR_CYCLE_REDUCTION_TOL;}
<DYNARE_STATEMENT>dr_cycle_reduction_maxiter {return token::DR_CYCLE_REDUCTION_MAXITER;}
<DYNARE_STATEMENT>dr_logarithmic_reduction_tol {return token::DR_LOGARITHMIC_REDUCTION_TOL;} <DYNARE_STATEMENT>dr_logarithmic_reduction_tol {return token::DR_LOGARITHMIC_REDUCTION_TOL;}
<DYNARE_STATEMENT>dr_logarithmic_reduction_maxiter {return token::DR_LOGARITHMIC_REDUCTION_MAXITER;} <DYNARE_STATEMENT>dr_logarithmic_reduction_maxiter {return token::DR_LOGARITHMIC_REDUCTION_MAXITER;}
<DYNARE_STATEMENT>replic {return token::REPLIC;} <DYNARE_STATEMENT>replic {return token::REPLIC;}
...@@ -932,11 +922,11 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]) ...@@ -932,11 +922,11 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4])
<DYNARE_STATEMENT>time_shift {return token::TIME_SHIFT;} <DYNARE_STATEMENT>time_shift {return token::TIME_SHIFT;}
<DYNARE_STATEMENT>structural {return token::STRUCTURAL;} <DYNARE_STATEMENT>structural {return token::STRUCTURAL;}
<DYNARE_STATEMENT>true { <DYNARE_STATEMENT>true {
yylval->build<string>(yytext); yylval->emplace<string>(yytext);
return token::TRUE; return token::TRUE;
} }
<DYNARE_STATEMENT>false { <DYNARE_STATEMENT>false {
yylval->build<string>(yytext); yylval->emplace<string>(yytext);
return token::FALSE; return token::FALSE;
} }
...@@ -1004,12 +994,15 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]) ...@@ -1004,12 +994,15 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4])
<DYNARE_BLOCK>var_expectation {return token::VAR_EXPECTATION;} <DYNARE_BLOCK>var_expectation {return token::VAR_EXPECTATION;}
<DYNARE_BLOCK>pac_expectation {return token::PAC_EXPECTATION;} <DYNARE_BLOCK>pac_expectation {return token::PAC_EXPECTATION;}
<DYNARE_BLOCK>pac_target_nonstationary {return token::PAC_TARGET_NONSTATIONARY;} <DYNARE_BLOCK>pac_target_nonstationary {return token::PAC_TARGET_NONSTATIONARY;}
<DYNARE_BLOCK>sum {return token::SUM;}
<DYNARE_STATEMENT>discount {return token::DISCOUNT;} <DYNARE_STATEMENT>discount {return token::DISCOUNT;}
<DYNARE_STATEMENT,DYNARE_BLOCK>varobs {return token::VAROBS;} <DYNARE_STATEMENT,DYNARE_BLOCK>varobs {return token::VAROBS;}
<DYNARE_STATEMENT,DYNARE_BLOCK>varexobs {return token::VAREXOBS;} <DYNARE_STATEMENT,DYNARE_BLOCK>varexobs {return token::VAREXOBS;}
<DYNARE_STATEMENT,DYNARE_BLOCK>nan {return token::NAN_CONSTANT;} <DYNARE_STATEMENT,DYNARE_BLOCK>nan {return token::NAN_CONSTANT;}
<DYNARE_STATEMENT,DYNARE_BLOCK>inf {return token::INF_CONSTANT;} <DYNARE_STATEMENT,DYNARE_BLOCK>inf {return token::INF_CONSTANT;}
<DYNARE_STATEMENT,DYNARE_BLOCK>constants {return token::CONSTANTS;} <DYNARE_STATEMENT,DYNARE_BLOCK>constants {return token::CONSTANTS;}
<DYNARE_BLOCK>⟂ {return token::PERPENDICULAR;}
<DYNARE_BLOCK>_\|_ {return token::PERPENDICULAR;}
/* options for GSA module by Marco Ratto */ /* options for GSA module by Marco Ratto */
<DYNARE_STATEMENT>identification {return token::IDENTIFICATION;} <DYNARE_STATEMENT>identification {return token::IDENTIFICATION;}
...@@ -1057,38 +1050,27 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]) ...@@ -1057,38 +1050,27 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4])
<DYNARE_STATEMENT>use_shock_groups {return token::USE_SHOCK_GROUPS;} <DYNARE_STATEMENT>use_shock_groups {return token::USE_SHOCK_GROUPS;}
<DYNARE_STATEMENT>colormap {return token::COLORMAP;} <DYNARE_STATEMENT>colormap {return token::COLORMAP;}
<DYNARE_STATEMENT,DYNARE_BLOCK>[a-z_][a-z0-9_]* { <DYNARE_STATEMENT,DYNARE_BLOCK>{NAME} {
yylval->build<string>(yytext); yylval->emplace<string>(yytext);
return token::NAME; return token::NAME;
} }
<DYNARE_STATEMENT,DYNARE_BLOCK>((([0-9]*\.[0-9]+)|([0-9]+\.))([ed][-+]?[0-9]+)?)|([0-9]+[ed][-+]?[0-9]+) { <DYNARE_STATEMENT,DYNARE_BLOCK>{FLOAT_NUMBER} {
yylval->build<string>(yytext); yylval->emplace<string>(yytext);
return token::FLOAT_NUMBER; return token::FLOAT_NUMBER;
} }
<DYNARE_STATEMENT,DYNARE_BLOCK>[0-9]+ { <DYNARE_STATEMENT,DYNARE_BLOCK>[0-9]+ {
yylval->build<string>(yytext); yylval->emplace<string>(yytext);
return token::INT_NUMBER; return token::INT_NUMBER;
} }
<DATES_STATEMENT>\( { yylval->as<string>().append(yytext); dates_parens_nb++; }
<DATES_STATEMENT>\) {
yylval->as<string>().append(yytext);
if (--dates_parens_nb == 0)
{
BEGIN DYNARE_STATEMENT;
return token::DATES;
}
}
<DATES_STATEMENT>. { yylval->as<string>().append(yytext); }
<DYNARE_BLOCK>\|e { return token::PIPE_E; } <DYNARE_BLOCK>\|e { return token::PIPE_E; }
<DYNARE_BLOCK>\|x { return token::PIPE_X; } <DYNARE_BLOCK>\|x { return token::PIPE_X; }
<DYNARE_BLOCK>\|p { return token::PIPE_P; } <DYNARE_BLOCK>\|p { return token::PIPE_P; }
<DYNARE_STATEMENT,DYNARE_BLOCK>\'[^\']*\' { <DYNARE_STATEMENT,DYNARE_BLOCK>\'[^\']*\' {
yylval->build<string>(yytext + 1).pop_back(); yylval->emplace<string>(yytext + 1).pop_back();
return token::QUOTED_STRING; return token::QUOTED_STRING;
} }
...@@ -1120,11 +1102,11 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]) ...@@ -1120,11 +1102,11 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4])
element in initval (in which case Dynare recognizes the matrix name as an external element in initval (in which case Dynare recognizes the matrix name as an external
function symbol), and may want to modify the matrix later with Matlab statements. function symbol), and may want to modify the matrix later with Matlab statements.
*/ */
<INITIAL>[a-z_][a-z0-9_]* { <INITIAL>{NAME} {
if (driver.symbol_exists_and_is_not_modfile_local_or_external_function(yytext)) if (driver.symbol_exists_and_is_not_modfile_local_or_external_function(yytext))
{ {
BEGIN DYNARE_STATEMENT; BEGIN DYNARE_STATEMENT;
yylval->build<string>(yytext); yylval->emplace<string>(yytext);
return token::NAME; return token::NAME;
} }
else else
...@@ -1145,7 +1127,7 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]) ...@@ -1145,7 +1127,7 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4])
be able to back out of the statement if we realize it's a native statement be able to back out of the statement if we realize it's a native statement
and move to the NATIVE context and move to the NATIVE context
*/ */
<INITIAL>\[([[:space:]]*[a-z_][a-z0-9_]*[[:space:]]*,{1}[[:space:]]*)*([[:space:]]*[a-z_][a-z0-9_]*[[:space:]]*){1}\] { <INITIAL>\[([[:space:]]*{NAME}[[:space:]]*,{1}[[:space:]]*)*([[:space:]]*{NAME}[[:space:]]*){1}\] {
string yytextcpy{yytext}; string yytextcpy{yytext};
yytextcpy.erase(remove(yytextcpy.begin(), yytextcpy.end(), '['), yytextcpy.end()); yytextcpy.erase(remove(yytextcpy.begin(), yytextcpy.end(), '['), yytextcpy.end());
yytextcpy.erase(remove(yytextcpy.begin(), yytextcpy.end(), ']'), yytextcpy.end()); yytextcpy.erase(remove(yytextcpy.begin(), yytextcpy.end(), ']'), yytextcpy.end());
...@@ -1169,7 +1151,7 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]) ...@@ -1169,7 +1151,7 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4])
if (dynare_statement) if (dynare_statement)
{ {
BEGIN DYNARE_STATEMENT; BEGIN DYNARE_STATEMENT;
yylval->build<vector<string>>(val); yylval->emplace<vector<string>>(val);
return token::SYMBOL_VEC; return token::SYMBOL_VEC;
} }
} }
...@@ -1214,7 +1196,7 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]) ...@@ -1214,7 +1196,7 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4])
<NATIVE_COMMENT>"*/"[[:space:]]*\n { BEGIN NATIVE; } <NATIVE_COMMENT>"*/"[[:space:]]*\n { BEGIN NATIVE; }
<NATIVE_COMMENT>. <NATIVE_COMMENT>.
<INITIAL,DYNARE_STATEMENT,DYNARE_BLOCK,COMMENT,DATES_STATEMENT,LINE1,LINE2,LINE3,NATIVE_COMMENT><<EOF>> { yyterminate(); } <INITIAL,DYNARE_STATEMENT,DYNARE_BLOCK,COMMENT,LINE1,LINE2,LINE3,NATIVE_COMMENT><<EOF>> { yyterminate(); }
<*>. { driver.error(*yylloc, "character unrecognized by lexer"); } <*>. { driver.error(*yylloc, "character unrecognized by lexer"); }
%% %%
......
...@@ -17,24 +17,24 @@ ...@@ -17,24 +17,24 @@
* along with Dynare. If not, see <https://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 <vector>
#include <string> #include <string>
#include <regex>
#include <thread> #include <thread>
#include <algorithm> #include <vector>
#include <filesystem>
#include <cstdlib> #include <cstdlib>
#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 "ModFile.hh"
#include "ParsingDriver.hh"
/* Prototype for the function that handles the macro-expansion of the .mod file /* Prototype for the function that handles the macro-expansion of the .mod file
Splitting this out was necessary because ParsingDriver.hh and macro/Driver.hh can't be Splitting this out was necessary because ParsingDriver.hh and macro/Driver.hh can't be
...@@ -42,21 +42,27 @@ ...@@ -42,21 +42,27 @@
Function can be found in: MacroExpandModFile.cc Function can be found in: MacroExpandModFile.cc
*/ */
stringstream stringstream macroExpandModFile(const filesystem::path& filename, const istream& modfile,
macroExpandModFile(const filesystem::path &filename, const istream &modfile, bool debug, bool save_macro, filesystem::path save_macro_file,
bool debug, bool save_macro, filesystem::path save_macro_file, bool line_macro, bool line_macro, const vector<pair<string, string>>& defines,
const vector<pair<string, string>> &defines,
vector<filesystem::path> paths); vector<filesystem::path> paths);
void void
usage() usage()
{ {
cerr << "Dynare usage: dynare mod_file [debug] [noclearall] [onlyclearglobals] [savemacro[=macro_file]] [onlymacro] [linemacro] [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_follower_open_mode] [parallel_test] [parallel_use_psexec=true|false]" "[savemacro[=macro_file]] [onlymacro] [linemacro] [notmpterms] [nolog] [warn_uninit]"
<< " [-D<variable>[=<value>]] [-I/path] [nostrict] [stochastic] [fast] [minimal_workspace] [compute_xrefs] [output=second|third] [language=matlab|julia]" << " [console] [nograph] [nointeractive] [parallel[=cluster_name]] "
<< " [params_derivs_order=0|1|2] [transform_unary_ops] [exclude_eqs=<equation_tag_list_or_file>] [include_eqs=<equation_tag_list_or_file>]" "[conffile=path_to_config_file] [parallel_follower_open_mode] "
<< " [json=parse|check|transform|compute] [jsonstdout] [onlyjson] [jsonderivsimple] [nopathchange] [nopreprocessoroutput]" "[parallel_test] [parallel_use_psexec=true|false]"
<< " [mexext=<extension>] [matlabroot=<path>] [onlymodel] [notime] [use_dll] [nocommutativity]" << " [-D<variable>[=<value>]] [-I/path] [nostrict] [stochastic] [fast] [minimal_workspace] "
"[compute_xrefs] [output=first|second|third] [language=matlab|julia]"
<< " [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);
} }
...@@ -75,13 +81,12 @@ parse_options_line(istream &modfile) ...@@ -75,13 +81,12 @@ parse_options_line(istream &modfile)
while (getline(modfile, first_nonempty_line)) while (getline(modfile, first_nonempty_line))
if (!first_nonempty_line.empty()) if (!first_nonempty_line.empty())
{ {
if (regex_search(first_nonempty_line, matches, pat) if (regex_search(first_nonempty_line, matches, pat) && matches.size() > 1
&& matches.size() > 1 && matches[1].matched) && matches[1].matched)
{ {
regex pat2 {R"([^,\s]+)"}; regex pat2 {R"([^,\s]+)"};
string s {matches[1]}; string s {matches[1]};
for (sregex_iterator p(s.begin(), s.end(), pat2); for (sregex_iterator p(s.begin(), s.end(), pat2); p != sregex_iterator {}; ++p)
p != sregex_iterator{}; ++p)
options.push_back(p->str()); options.push_back(p->str());
} }
break; break;
...@@ -137,10 +142,11 @@ main(int argc, char **argv) ...@@ -137,10 +142,11 @@ main(int argc, char **argv)
bool console = false; bool console = false;
bool nograph = false; bool nograph = false;
bool nointeractive = false; bool nointeractive = false;
filesystem::path parallel_config_file; filesystem::path conffile;
bool parallel = false; bool parallel = false;
string cluster_name; string cluster_name;
bool parallel_follower_open_mode = false; // Must be the same default as in matlab/default_option_values.m 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 parallel_use_psexec = true; // Must be the same default as in matlab/default_option_values.m
bool nostrict = false; bool nostrict = false;
...@@ -228,7 +234,7 @@ main(int argc, char **argv) ...@@ -228,7 +234,7 @@ main(int argc, char **argv)
cerr << "Incorrect syntax for conffile option" << endl; cerr << "Incorrect syntax for conffile option" << endl;
usage(); usage();
} }
parallel_config_file = s.substr(9); conffile = s.substr(9);
} }
else if (s == "parallel_follower_open_mode" else if (s == "parallel_follower_open_mode"
|| s == "parallel_slave_open_mode") // Kept for backward compatibility, see #86 || s == "parallel_slave_open_mode") // Kept for backward compatibility, see #86
...@@ -289,8 +295,7 @@ main(int argc, char **argv) ...@@ -289,8 +295,7 @@ main(int argc, char **argv)
usage(); usage();
} }
if (auto equal_index = s.find('='); if (auto equal_index = s.find('='); equal_index != string::npos)
equal_index != string::npos)
defines.emplace_back(s.substr(2, equal_index - 2), s.substr(equal_index + 1)); defines.emplace_back(s.substr(2, equal_index - 2), s.substr(equal_index + 1));
else else
defines.emplace_back(s.substr(2), "true"); defines.emplace_back(s.substr(2), "true");
...@@ -315,7 +320,9 @@ main(int argc, char **argv) ...@@ -315,7 +320,9 @@ main(int argc, char **argv)
s.erase(0, 7); s.erase(0, 7);
if (s == "second") if (s == "first")
output_mode = OutputType::first;
else if (s == "second")
output_mode = OutputType::second; output_mode = OutputType::second;
else if (s == "third") else if (s == "third")
output_mode = OutputType::third; output_mode = OutputType::third;
...@@ -439,9 +446,9 @@ main(int argc, char **argv) ...@@ -439,9 +446,9 @@ main(int argc, char **argv)
dynareroot = dynareroot.parent_path(); dynareroot = dynareroot.parent_path();
// Construct basename (i.e. remove file extension if there is one) // Construct basename (i.e. remove file extension if there is one)
/* Calling `string()` method on filename because of bug in GCC/MinGW 10.2 /* Calling string() method on filename.stem(): not necessary on GNU/Linux and macOS because there
(shipped in Debian “Bullseye” 11), that fails to accept implicit is an implicit conversion from filesystem:path to string (i.e. basic_string<char>), but needed
conversion to string from filename::path. */ on Windows because the implicit conversion is only to wstring (i.e. basic_string<wchar_t>). */
const string basename {filename.stem().string()}; const string basename {filename.stem().string()};
// Forbid some basenames, since they will cause trouble (see preprocessor#62) // Forbid some basenames, since they will cause trouble (see preprocessor#62)
...@@ -456,21 +463,22 @@ main(int argc, char **argv) ...@@ -456,21 +463,22 @@ main(int argc, char **argv)
WarningConsolidation warnings(no_warn); WarningConsolidation warnings(no_warn);
// Process config file // Process config file
ConfigFile config_file(parallel, parallel_test, parallel_follower_open_mode, parallel_use_psexec, 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
for (const auto &it : config_file.getIncludePaths()) for (const auto& it : config.getIncludePaths())
paths.emplace_back(it); paths.emplace_back(it);
/* /*
* Macro-expand MOD file * Macro-expand MOD file
*/ */
stringstream macro_output = stringstream macro_output
macroExpandModFile(filename, modfile, debug, save_macro, move(save_macro_file), line_macro, = macroExpandModFile(filename, modfile, debug, save_macro, move(save_macro_file), line_macro,
defines, move(paths)); defines, move(paths));
if (only_macro) if (only_macro)
...@@ -503,8 +511,8 @@ main(int argc, char **argv) ...@@ -503,8 +511,8 @@ main(int argc, char **argv)
} }
if (mod_file->use_dll) if (mod_file->use_dll)
ModelTree::initializeMEXCompilationWorkers(max(jthread::hardware_concurrency(), 1U), ModelTree::initializeMEXCompilationWorkers(max(jthread::hardware_concurrency(), 1U), dynareroot,
dynareroot, mexext); mexext);
if (json == JsonOutputPointType::parsing) if (json == JsonOutputPointType::parsing)
mod_file->writeJsonOutput(basename, json, json_output_mode, onlyjson); mod_file->writeJsonOutput(basename, json, json_output_mode, onlyjson);
...@@ -515,7 +523,8 @@ main(int argc, char **argv) ...@@ -515,7 +523,8 @@ main(int argc, char **argv)
mod_file->writeJsonOutput(basename, json, json_output_mode, onlyjson); mod_file->writeJsonOutput(basename, json, json_output_mode, onlyjson);
// Perform transformations on the model (creation of auxiliary vars and equations) // Perform transformations on the model (creation of auxiliary vars and equations)
mod_file->transformPass(nostrict, stochastic, compute_xrefs || json == JsonOutputPointType::transformpass, mod_file->transformPass(nostrict, stochastic,
compute_xrefs || json == JsonOutputPointType::transformpass,
transform_unary_ops, exclude_eqs, include_eqs); transform_unary_ops, exclude_eqs, include_eqs);
if (json == JsonOutputPointType::transformpass) if (json == JsonOutputPointType::transformpass)
mod_file->writeJsonOutput(basename, json, json_output_mode, onlyjson); mod_file->writeJsonOutput(basename, json, json_output_mode, onlyjson);
...@@ -533,8 +542,8 @@ main(int argc, char **argv) ...@@ -533,8 +542,8 @@ main(int argc, char **argv)
mod_file->writeJuliaOutput(basename); mod_file->writeJuliaOutput(basename);
else else
mod_file->writeMOutput(basename, clear_all, clear_global, no_warn, console, nograph, mod_file->writeMOutput(basename, clear_all, clear_global, no_warn, console, nograph,
nointeractive, config_file, check_model_changes, minimal_workspace, compute_xrefs, nointeractive, config, check_model_changes, minimal_workspace,
mexext, matlabroot, onlymodel, gui, notime); compute_xrefs, mexext, matlabroot, onlymodel, gui, notime);
/* Ensures that workers are not destroyed before they finish compiling. /* Ensures that workers are not destroyed before they finish compiling.
Also ensures that the preprocessor final message is printed after the end of Also ensures that the preprocessor final message is printed after the end of
......
/* /*
* Copyright © 2020-2023 Dynare Team * Copyright © 2020-2025 Dynare Team
* *
* This file is part of Dynare. * This file is part of Dynare.
* *
...@@ -19,8 +19,8 @@ ...@@ -19,8 +19,8 @@
#include "EquationTags.hh" #include "EquationTags.hh"
#include <regex>
#include <ostream> #include <ostream>
#include <regex>
#include <utility> #include <utility>
set<int> set<int>
...@@ -62,8 +62,7 @@ EquationTags::getEqnsByTags(const map<string, string> &tags_selected) const ...@@ -62,8 +62,7 @@ EquationTags::getEqnsByTags(const map<string, string> &tags_selected) const
if (auto tmp = tags.find(key); tmp == tags.end() || tmp->second != value) if (auto tmp = tags.find(key); tmp == tags.end() || tmp->second != value)
goto next_eq; goto next_eq;
retval.insert(eqn); retval.insert(eqn);
next_eq: next_eq:;
;
} }
return retval; return retval;
} }
...@@ -75,10 +74,9 @@ EquationTags::erase(const set<int> &eqns, const map<int, int> &old_eqn_num_2_new ...@@ -75,10 +74,9 @@ EquationTags::erase(const set<int> &eqns, const map<int, int> &old_eqn_num_2_new
eqn_tags.erase(eqn); eqn_tags.erase(eqn);
for (const auto& [oldeqn, neweqn] : old_eqn_num_2_new) for (const auto& [oldeqn, neweqn] : old_eqn_num_2_new)
for (auto & [eqn, tags] : eqn_tags) if (eqn_tags.contains(oldeqn))
if (eqn == oldeqn)
{ {
auto tmp = eqn_tags.extract(eqn); auto tmp = eqn_tags.extract(oldeqn);
tmp.key() = neweqn; tmp.key() = neweqn;
eqn_tags.insert(move(tmp)); eqn_tags.insert(move(tmp));
} }
...@@ -89,8 +87,7 @@ EquationTags::writeCheckSumInfo(ostream &output) const ...@@ -89,8 +87,7 @@ EquationTags::writeCheckSumInfo(ostream &output) const
{ {
for (const auto& [eqn, tags] : eqn_tags) for (const auto& [eqn, tags] : eqn_tags)
for (const auto& [key, value] : tags) for (const auto& [key, value] : tags)
output << " " << eqn + 1 output << " " << eqn + 1 << key << " " << value << endl;
<< key << " " << value << endl;
} }
void void
...@@ -99,8 +96,7 @@ EquationTags::writeOutput(ostream &output) const ...@@ -99,8 +96,7 @@ EquationTags::writeOutput(ostream &output) const
output << "M_.equations_tags = {" << endl; output << "M_.equations_tags = {" << endl;
for (const auto& [eqn, tags] : eqn_tags) for (const auto& [eqn, tags] : eqn_tags)
for (const auto& [key, value] : tags) for (const auto& [key, value] : tags)
output << " " << eqn + 1 << " , '" output << " " << eqn + 1 << " , '" << key << "' , '" << value << "' ;" << endl;
<< key << "' , '" << value << "' ;" << endl;
output << "};" << endl; output << "};" << endl;
} }
...@@ -110,8 +106,7 @@ EquationTags::writeLatexOutput(ostream &output, int eqn) const ...@@ -110,8 +106,7 @@ EquationTags::writeLatexOutput(ostream &output, int eqn) const
if (!eqn_tags.contains(eqn)) if (!eqn_tags.contains(eqn))
return; return;
auto escape_special_latex_symbols = [](string str) auto escape_special_latex_symbols = [](string str) {
{
const regex special_latex_chars(R"([&%$#_{}])"); const regex special_latex_chars(R"([&%$#_{}])");
const regex backslash(R"(\\)"); const regex backslash(R"(\\)");
const regex tilde(R"(~)"); const regex tilde(R"(~)");
...@@ -125,8 +120,7 @@ EquationTags::writeLatexOutput(ostream &output, int eqn) const ...@@ -125,8 +120,7 @@ EquationTags::writeLatexOutput(ostream &output, int eqn) const
}; };
output << R"(\noindent[)"; output << R"(\noindent[)";
for (bool wrote_eq_tag {false}; for (bool wrote_eq_tag {false}; const auto& [key, value] : eqn_tags.at(eqn))
const auto & [key, value] : eqn_tags.at(eqn))
{ {
if (exchange(wrote_eq_tag, true)) if (exchange(wrote_eq_tag, true))
output << ", "; output << ", ";
...@@ -145,8 +139,7 @@ EquationTags::writeJsonAST(ostream &output, int eqn) const ...@@ -145,8 +139,7 @@ EquationTags::writeJsonAST(ostream &output, int eqn) const
return; return;
output << R"(, "tags": {)"; output << R"(, "tags": {)";
for (bool wroteFirst {false}; for (bool wroteFirst {false}; const auto& [key, value] : eqn_tags.at(eqn))
const auto &[key, value] : eqn_tags.at(eqn))
{ {
if (exchange(wroteFirst, true)) if (exchange(wroteFirst, true))
output << ", "; output << ", ";
......
...@@ -17,13 +17,13 @@ ...@@ -17,13 +17,13 @@
* along with Dynare. If not, see <https://www.gnu.org/licenses/>. * along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/ */
#ifndef _EQUATION_TAGS_HH #ifndef EQUATION_TAGS_HH
#define _EQUATION_TAGS_HH #define EQUATION_TAGS_HH
#include <map> #include <map>
#include <optional>
#include <set> #include <set>
#include <string> #include <string>
#include <optional>
using namespace std; using namespace std;
...@@ -31,6 +31,7 @@ class EquationTags ...@@ -31,6 +31,7 @@ class EquationTags
{ {
private: private:
map<int, map<string, string>> eqn_tags; map<int, map<string, string>> eqn_tags;
public: public:
// Add multiple equation tags for the given equation // Add multiple equation tags for the given equation
void void
...@@ -62,7 +63,7 @@ public: ...@@ -62,7 +63,7 @@ public:
//! Various functions to get info from equation tags //! Various functions to get info from equation tags
//! Get equation tags for a given equation //! Get equation tags for a given equation
map<string, string> [[nodiscard]] map<string, string>
getTagsByEqn(int eqn) const getTagsByEqn(int eqn) const
{ {
if (auto it = eqn_tags.find(eqn); it != eqn_tags.end()) if (auto it = eqn_tags.find(eqn); it != eqn_tags.end())
...@@ -71,19 +72,19 @@ public: ...@@ -71,19 +72,19 @@ public:
} }
//! Get equations that have the given key //! Get equations that have the given key
set<int> getEqnsByKey(const string &key) const; [[nodiscard]] set<int> getEqnsByKey(const string& key) const;
//! Get equations that have the given key and value //! Get equations that have the given key and value
set<int> getEqnsByTag(const string &key, const string &value) const; [[nodiscard]] set<int> getEqnsByTag(const string& key, const string& value) const;
//! Get the first equation that has the given key and value //! Get the first equation that has the given key and value
optional<int> getEqnByTag(const string &key, const string &value) const; [[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) // Get equations that have all the given keys and values (seen as a conjunction)
set<int> getEqnsByTags(const map<string, string> &tags_selected) const; [[nodiscard]] set<int> getEqnsByTags(const map<string, string>& tags_selected) const;
//! Get the tag value given the equation number and key //! Get the tag value given the equation number and key
optional<string> [[nodiscard]] optional<string>
getTagValueByEqnAndKey(int eqn, const string& key) const getTagValueByEqnAndKey(int eqn, const string& key) const
{ {
if (auto it = eqn_tags.find(eqn); it != eqn_tags.end()) if (auto it = eqn_tags.find(eqn); it != eqn_tags.end())
...@@ -93,21 +94,21 @@ public: ...@@ -93,21 +94,21 @@ public:
} }
//! Get the equations marked dynamic //! Get the equations marked dynamic
set<int> [[nodiscard]] set<int>
getDynamicEqns() const getDynamicEqns() const
{ {
return getEqnsByTag("dynamic", ""); return getEqnsByTag("dynamic", "");
} }
//! Returns true if equation tag with key and value exists //! Returns true if equation tag with key and value exists
bool [[nodiscard]] bool
exists(const string& key, const string& value) const exists(const string& key, const string& value) const
{ {
return getEqnByTag(key, value).has_value(); return getEqnByTag(key, value).has_value();
} }
//! Returns true if equation tag with key exists for a given equation //! Returns true if equation tag with key exists for a given equation
bool [[nodiscard]] bool
exists(int eqn, const string& key) const exists(int eqn, const string& key) const
{ {
auto it = eqn_tags.find(eqn); auto it = eqn_tags.find(eqn);
......
Source diff could not be displayed: it is too large. Options to address this: view the blob.
/* /*
* Copyright © 2007-2023 Dynare Team * Copyright © 2007-2025 Dynare Team
* *
* This file is part of Dynare. * This file is part of Dynare.
* *
...@@ -17,24 +17,25 @@ ...@@ -17,24 +17,25 @@
* along with Dynare. If not, see <https://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 <map>
#include <vector>
#include <ostream>
#include <functional> #include <functional>
#include <map>
#include <optional> #include <optional>
#include <utility> #include <ostream>
#include <set>
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include <utility>
#include <vector>
using namespace std; using namespace std;
#include "Bytecode.hh"
#include "CommonEnums.hh" #include "CommonEnums.hh"
#include "ExternalFunctionsTable.hh" #include "ExternalFunctionsTable.hh"
#include "Bytecode.hh"
class DataTree; class DataTree;
class NumConstNode; class NumConstNode;
...@@ -58,7 +59,8 @@ using temporary_terms_idxs_t = unordered_map<expr_t, int>; ...@@ -58,7 +59,8 @@ using temporary_terms_idxs_t = unordered_map<expr_t, int>;
/*! The key is a symbol id. Lags are assumed to be null */ /*! The key is a symbol id. Lags are assumed to be null */
using eval_context_t = map<int, double>; 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
//! terms
using deriv_node_temp_terms_t = map<pair<int, vector<expr_t>>, int>; 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 //! Type for the substitution map used for creating aux. vars for diff and unary_ops
...@@ -217,13 +219,13 @@ RIGHT_ARRAY_SUBSCRIPT(ExprNodeOutputType output_type) ...@@ -217,13 +219,13 @@ RIGHT_ARRAY_SUBSCRIPT(ExprNodeOutputType output_type)
} }
// Left and right parentheses // Left and right parentheses
inline string constexpr string
LEFT_PAR(ExprNodeOutputType output_type) LEFT_PAR(ExprNodeOutputType output_type)
{ {
return isLatexOutput(output_type) ? "\\left(" : "("; return isLatexOutput(output_type) ? "\\left(" : "(";
} }
inline string constexpr string
RIGHT_PAR(ExprNodeOutputType output_type) RIGHT_PAR(ExprNodeOutputType output_type)
{ {
return isLatexOutput(output_type) ? "\\right)" : ")"; return isLatexOutput(output_type) ? "\\right)" : ")";
...@@ -245,6 +247,7 @@ class ExprNode ...@@ -245,6 +247,7 @@ class ExprNode
friend class AbstractExternalFunctionNode; friend class AbstractExternalFunctionNode;
friend class VarExpectationNode; friend class VarExpectationNode;
friend class PacExpectationNode; 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 */
...@@ -252,7 +255,11 @@ private: ...@@ -252,7 +255,11 @@ private:
/* Internal helper for getChainRuleDerivative(), that does the computation /* Internal helper for getChainRuleDerivative(), that does the computation
but assumes that the caching of this is handled elsewhere */ 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; 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
...@@ -276,7 +283,7 @@ protected: ...@@ -276,7 +283,7 @@ protected:
min_cost(bool is_matlab) min_cost(bool is_matlab)
{ {
return is_matlab ? min_cost_matlab : min_cost_c; return is_matlab ? min_cost_matlab : min_cost_c;
}; }
//! Initializes data member non_null_derivatives //! Initializes data member non_null_derivatives
virtual void prepareForDerivation() = 0; virtual void prepareForDerivation() = 0;
...@@ -287,13 +294,18 @@ protected: ...@@ -287,13 +294,18 @@ protected:
“recursive_variables”. Note that all non-endogenous variables are “recursive_variables”. Note that all non-endogenous variables are
automatically considered to have a zero derivative (since they’re never automatically considered to have a zero derivative (since they’re never
used in a chain rule context) */ 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; 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 vector<vector<unordered_set<expr_t>>> &blocks_temporary_terms, bool is_matlab) const; [[nodiscard]] virtual int
virtual int cost(const map<pair<int, int>, unordered_set<expr_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
...@@ -309,18 +321,20 @@ protected: ...@@ -309,18 +321,20 @@ protected:
const temporary_terms_idxs_t& temporary_terms_idxs) const; const temporary_terms_idxs_t& temporary_terms_idxs) const;
// Same as above, for the bytecode case // Same as above, for the bytecode case
bool checkIfTemporaryTermThenWriteBytecode(BytecodeWriter &code_file, bool
checkIfTemporaryTermThenWriteBytecode(Bytecode::Writer& code_file,
ExprNodeBytecodeOutputType output_type, ExprNodeBytecodeOutputType output_type,
const temporary_terms_t& temporary_terms, const temporary_terms_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs) const; const temporary_terms_idxs_t& temporary_terms_idxs) const;
// Internal helper for matchVariableTimesConstantTimesParam() // Internal helper for matchVariableTimesConstantTimesParam()
virtual void matchVTCTPHelper(optional<int> &var_id, int &lag, optional<int> &param_id, double &constant, bool at_denominator) const; 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 /* Computes the representative element and the index under the
lag-equivalence relationship. See the comment above lag-equivalence relationship. See the comment above
lag_equivalence_table_t for an explanation of these concepts. */ lag_equivalence_table_t for an explanation of these concepts. */
pair<expr_t, int> getLagEquivalenceClass() const; [[nodiscard]] pair<expr_t, int> getLagEquivalenceClass() const;
/* Computes the set of all sub-expressions that contain the variable /* Computes the set of all sub-expressions that contain the variable
(symb_id, lag). (symb_id, lag).
...@@ -328,7 +342,9 @@ protected: ...@@ -328,7 +342,9 @@ protected:
- diff(expr) will be added to the output set if either expr or expr(-1) - diff(expr) will be added to the output set if either expr or expr(-1)
contains the variable; contains the variable;
- the method will be called recursively on expr-expr(-1) */ - 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; virtual void computeSubExprContainingVariable(int symb_id, int lag,
set<expr_t>& contain_var) const
= 0;
public: public:
ExprNode(DataTree& datatree_arg, int idx_arg); ExprNode(DataTree& datatree_arg, int idx_arg);
...@@ -338,8 +354,9 @@ public: ...@@ -338,8 +354,9 @@ public:
ExprNode& operator=(const ExprNode&) = delete; 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.
...@@ -359,11 +376,14 @@ public: ...@@ -359,11 +376,14 @@ public:
NB 3: the use of std::unordered_map instead of std::map for caching 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 purposes improves performance on very very large models (tens of thousands
of equations) */ 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); 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;
//! Compute temporary terms in this expression //! Compute temporary terms in this expression
/*! /*!
...@@ -384,10 +404,9 @@ public: ...@@ -384,10 +404,9 @@ public:
NB: the use of std::unordered_map instead of std::map for caching NB: the use of std::unordered_map instead of std::map for caching
purposes improves performance on very large models (⩾5000 equations) purposes improves performance on very large models (⩾5000 equations)
*/ */
virtual void computeTemporaryTerms(const pair<int, int> &derivOrder, virtual void computeTemporaryTerms(
map<pair<int, int>, unordered_set<expr_t>> &temp_terms_map, 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, unordered_map<expr_t, pair<int, pair<int, int>>>& reference_count, bool is_matlab) const;
bool is_matlab) const;
//! Compute temporary terms in this expression for block decomposed model //! Compute temporary terms in this expression for block decomposed model
/*! /*!
...@@ -405,11 +424,13 @@ public: ...@@ -405,11 +424,13 @@ public:
NB: the use of std::unordered_{set,map} instead of std::{set,map} for caching 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) and output improves performance on very large models (⩾5000 equations)
*/ */
virtual void computeBlockTemporaryTerms(int blk, int eq, virtual void
computeBlockTemporaryTerms(int blk, int eq,
vector<vector<unordered_set<expr_t>>>& blocks_temporary_terms, vector<vector<unordered_set<expr_t>>>& blocks_temporary_terms,
unordered_map<expr_t, tuple<int, int, int>>& reference_count) const; 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 //! 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...)
...@@ -419,10 +440,14 @@ public: ...@@ -419,10 +440,14 @@ public:
when writing MATLAB with block decomposition) when writing MATLAB with block decomposition)
\param[in] tef_terms the set of already written external function nodes \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, const temporary_terms_idxs_t &temporary_terms_idxs, const 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;
...@@ -431,20 +456,26 @@ public: ...@@ -431,20 +456,26 @@ public:
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 temporary_terms_idxs_t &temporary_terms_idxs) 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, const deriv_node_temp_terms_t &tef_terms, 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 // Returns a string representation of the expression, used by the GDB pretty printer
string toString() const; [[nodiscard]] string toString() const;
//! Writes the Abstract Syntax Tree in JSON //! Writes the Abstract Syntax Tree in JSON
virtual void writeJsonAST(ostream& output) const = 0; 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, const temporary_terms_idxs_t& temporary_terms_idxs,
...@@ -457,13 +488,13 @@ public: ...@@ -457,13 +488,13 @@ public:
deriv_node_temp_terms_t& tef_terms, deriv_node_temp_terms_t& tef_terms,
bool isdynamic = true) const; bool isdynamic = true) const;
virtual void writeBytecodeExternalFunctionOutput(BytecodeWriter &code_file, virtual void writeBytecodeExternalFunctionOutput(
ExprNodeBytecodeOutputType output_type, Bytecode::Writer& code_file, ExprNodeBytecodeOutputType output_type,
const temporary_terms_t &temporary_terms, const temporary_terms_t& temporary_terms, const temporary_terms_idxs_t& temporary_terms_idxs,
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.
...@@ -473,12 +504,13 @@ public: ...@@ -473,12 +504,13 @@ public:
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;
//! Find the maximum lag in a VAR: handles case where LHS is diff //! Find the maximum lag in a VAR: handles case where LHS is diff
virtual int VarMaxLag(const set<expr_t> &lhs_lag_equiv) const = 0; [[nodiscard]] virtual int VarMaxLag(const set<expr_t>& lhs_lag_equiv) const = 0;
//! Finds LHS variable in a VAR equation //! Finds LHS variable in a VAR equation
virtual void collectVARLHSVariable(set<expr_t>& result) const = 0; 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) //! 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.
...@@ -503,22 +535,29 @@ public: ...@@ -503,22 +535,29 @@ public:
{ {
}; };
virtual double eval(const eval_context_t &eval_context) const noexcept(false) = 0; [[nodiscard]] virtual double eval(const eval_context_t& eval_context) const noexcept(false) = 0;
// Write output to bytecode file // Write output to bytecode file
virtual void writeBytecodeOutput(BytecodeWriter &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; 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;
//! Helper for normalization of equations //! Helper for normalization of equations
...@@ -527,52 +566,53 @@ public: ...@@ -527,52 +566,53 @@ public:
Returns an equal node of the form: LHS variable = new RHS. 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. Must be given the set of all subexpressions that contain the desired LHS variable.
Throws a NormallizationFailed() exception if normalization is not possible. */ Throws a NormallizationFailed() exception if normalization is not possible. */
virtual BinaryOpNode *normalizeEquationHelper(const set<expr_t> &contain_var, expr_t rhs) const = 0; virtual BinaryOpNode* normalizeEquationHelper(const set<expr_t>& contain_var, expr_t rhs) const
class NormalizationFailed {}; = 0;
class NormalizationFailed
{
};
//! Returns the maximum lead of endogenous in this expression //! 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 /* Returns the maximum lead of endo/exo/exodet in this expression (including heterogeneous
/*! A negative value means that the expression contains only lagged endo/exo). A negative value means that the expression contains only lagged variables. A value
variables. A value of numeric_limits<int>::min() means that there is of numeric_limits<int>::min() means that there is no variable. */
no variable. */ [[nodiscard]] virtual int maxLead() const = 0;
virtual int maxLead() const = 0;
//! Returns the maximum lag of endo/exo/exodet in this expression /* Returns the maximum lag of endo/exo/exodet in this expression (including heterogeneous
/*! A negative value means that the expression contains only leaded endo/exo). A negative value means that the expression contains only leaded variables. A value
variables. A value of numeric_limits<int>::min() means that there is of numeric_limits<int>::min() means that there is no variable. */
no variable. */ [[nodiscard]] virtual int maxLag() const = 0;
virtual int maxLag() const = 0;
//! Returns the maximum lag of endo/exo/exodet, as if diffs were expanded /* Returns the maximum lag of endo/exo/exodet (including heterogeneous endo/exo), as if diffs were
/*! This function behaves as maxLag(), except that it treats diff() expanded. This function behaves as maxLag(), except that it treats diff() differently. For
differently. For e.g., on diff(diff(x(-1))), maxLag() returns 1 while e.g., on diff(diff(x(-1))), maxLag() returns 1 while maxLagWithDiffsExpanded() returns 3. */
maxLagWithDiffsExpanded() returns 3. */ [[nodiscard]] virtual int maxLagWithDiffsExpanded() const = 0;
virtual int maxLagWithDiffsExpanded() const = 0;
virtual expr_t undiff() const = 0; [[nodiscard]] virtual expr_t undiff() const = 0;
//! Returns a new expression where all the leads/lags have been shifted backwards by the same amount //! 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 //! Type for the substitution map used in the process of creating auxiliary vars
using subst_table_t = map<const ExprNode*, const VariableNode*>; using subst_table_t = map<const ExprNode*, const VariableNode*>;
...@@ -582,96 +622,135 @@ public: ...@@ -582,96 +622,135 @@ public:
//! 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 the maximum number of nested diffs in the expression //! Returns the maximum number of nested diffs in the expression
virtual int countDiffs() const = 0; [[nodiscard]] virtual int countDiffs() const = 0;
//! 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 //! 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
/*! /*!
...@@ -683,13 +762,15 @@ public: ...@@ -683,13 +762,15 @@ public:
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 //! Substitute adl operator
virtual expr_t substituteAdl() const = 0; [[nodiscard]] virtual expr_t substituteAdl() const = 0;
//! Substitute out model-local variables //! Substitute out model-local variables
virtual expr_t substituteModelLocalVariables() const = 0; [[nodiscard]] virtual expr_t substituteModelLocalVariables() const = 0;
//! Substitute VarExpectation nodes //! Substitute VarExpectation nodes
virtual expr_t substituteVarExpectation(const map<string, expr_t> &subst_table) const = 0; [[nodiscard]] virtual expr_t
substituteVarExpectation(const map<string, expr_t>& subst_table) const
= 0;
//! Mark diff nodes to be substituted //! Mark diff nodes to be substituted
/*! The various nodes that are equivalent up to a shift of leads/lags are /*! The various nodes that are equivalent up to a shift of leads/lags are
...@@ -697,7 +778,9 @@ public: ...@@ -697,7 +778,9 @@ public:
lag_equivalence_table_t for more details. */ lag_equivalence_table_t for more details. */
virtual void findDiffNodes(lag_equivalence_table_t& nodes) const = 0; virtual void findDiffNodes(lag_equivalence_table_t& nodes) const = 0;
//! Substitute diff operator //! Substitute diff operator
virtual expr_t substituteDiff(const lag_equivalence_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const = 0; 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 //! Mark unary ops nodes to be substituted
/*! The various nodes that are equivalent up to a shift of leads/lags are /*! The various nodes that are equivalent up to a shift of leads/lags are
...@@ -705,7 +788,10 @@ public: ...@@ -705,7 +788,10 @@ public:
lag_equivalence_table_t for more details. */ lag_equivalence_table_t for more details. */
virtual void findUnaryOpNodesForAuxVarCreation(lag_equivalence_table_t& nodes) const = 0; virtual void findUnaryOpNodesForAuxVarCreation(lag_equivalence_table_t& nodes) const = 0;
//! Substitute unary ops nodes //! Substitute unary ops nodes
virtual expr_t substituteUnaryOpNodes(const lag_equivalence_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const = 0; virtual expr_t substituteUnaryOpNodes(const lag_equivalence_table_t& nodes,
subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs) const
= 0;
//! Substitute pac_expectation operator //! Substitute pac_expectation operator
virtual expr_t substitutePacExpectation(const string& name, expr_t subexpr) = 0; virtual expr_t substitutePacExpectation(const string& name, expr_t subexpr) = 0;
...@@ -713,24 +799,28 @@ public: ...@@ -713,24 +799,28 @@ public:
//! Substitute pac_target_nonstationary operator //! Substitute pac_target_nonstationary operator
virtual expr_t substitutePacTargetNonstationary(const string& name, expr_t subexpr) = 0; virtual expr_t substitutePacTargetNonstationary(const string& name, expr_t subexpr) = 0;
virtual optional<int> findTargetVariable(int lhs_symb_id) const = 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 clone(DataTree& alt_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(const 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) //! Returns true if the expression is in static form (no lead, no lag, no expectation, no
virtual bool isInStaticForm() const = 0; //! STEADY_STATE)
[[nodiscard]] virtual bool isInStaticForm() const = 0;
//! Matches a linear combination of variables (endo or exo), where scalars can be constant*parameter //! 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) /*! Returns a list of (variable_id, lag, param_id, constant)
corresponding to the terms in the expression. When there is no corresponding to the terms in the expression. When there is no
parameter in a term, param_id is nullopt. parameter in a term, param_id is nullopt.
Can throw a MatchFailureException. Can throw a MatchFailureException.
*/ */
vector<tuple<int, int, optional<int>, double>> matchLinearCombinationOfVariables() const; [[nodiscard]] vector<tuple<int, int, optional<int>, double>>
matchLinearCombinationOfVariables() const;
/* Matches a linear combination of variables (endo or exo), where scalars can /* 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 be constant*parameter. In addition, there may be one or more scalar terms
...@@ -741,14 +831,16 @@ public: ...@@ -741,14 +831,16 @@ public:
no variable), then variable_id is nullopt. no variable), then variable_id is nullopt.
Can throw a MatchFailureException. Can throw a MatchFailureException.
*/ */
vector<tuple<optional<int>, int, optional<int>, double>> matchLinearCombinationOfVariablesPlusConstant() const; [[nodiscard]] vector<tuple<optional<int>, int, optional<int>, double>>
matchLinearCombinationOfVariablesPlusConstant() const;
/* Matches a parameter, times a linear combination of variables (endo or /* Matches a parameter, times a linear combination of variables (endo or
exo), where scalars can be constant*parameters. exo), where scalars can be constant*parameters.
The first output argument is the symbol ID of the parameter. The first output argument is the symbol ID of the parameter.
The second output argument is the linear combination, in the same format The second output argument is the linear combination, in the same format
as the output of matchLinearCombinationOfVariables(). */ as the output of matchLinearCombinationOfVariables(). */
pair<int, vector<tuple<int, int, optional<int>, double>>> matchParamTimesLinearCombinationOfVariables() const; [[nodiscard]] pair<int, vector<tuple<int, int, optional<int>, double>>>
matchParamTimesLinearCombinationOfVariables() const;
/* Matches a linear combination of endogenous, where scalars can be any /* Matches a linear combination of endogenous, where scalars can be any
constant expression (i.e. containing no endogenous, no exogenous and no constant expression (i.e. containing no endogenous, no exogenous and no
...@@ -757,7 +849,8 @@ public: ...@@ -757,7 +849,8 @@ public:
Returns a pair composed of: Returns a pair composed of:
– the terms of the form endogenous*scalar, as a list of (endo_id, constant expr); – the terms of the form endogenous*scalar, as a list of (endo_id, constant expr);
– the sum of all constant (intercept) terms */ – the sum of all constant (intercept) terms */
pair<vector<pair<int, expr_t>>, expr_t> matchLinearCombinationOfEndogenousWithConstant() const; [[nodiscard]] pair<vector<pair<int, expr_t>>, expr_t>
matchLinearCombinationOfEndogenousWithConstant() const;
/* Matches an expression of the form parameter*(var1-endo2). /* Matches an expression of the form parameter*(var1-endo2).
endo2 must correspond to symb_id. var1 must be an endogenous or an endo2 must correspond to symb_id. var1 must be an endogenous or an
...@@ -765,25 +858,26 @@ public: ...@@ -765,25 +858,26 @@ public:
where X itself is *not* an aux var. where X itself is *not* an aux var.
Returns the symbol IDs of the parameter and of var1. Returns the symbol IDs of the parameter and of var1.
Throws a MatchFailureException otherwise */ Throws a MatchFailureException otherwise */
pair<int, int> matchParamTimesTargetMinusVariable(int symb_id) const; [[nodiscard]] pair<int, int> matchParamTimesTargetMinusVariable(int symb_id) const;
//! Returns true if expression is of the form: //! Returns true if expression is of the form:
//! param * (endog op endog op ...) + param * (endog op endog op ...) + ... //! param * (endog op endog op ...) + param * (endog op endog op ...) + ...
virtual bool isParamTimesEndogExpr() const = 0; [[nodiscard]] virtual bool isParamTimesEndogExpr() const = 0;
//! Fills the EC matrix structure //! Fills the EC matrix structure
void fillErrorCorrectionRow(int eqn, const vector<int> &nontarget_lhs, const vector<int> &target_lhs, void fillErrorCorrectionRow(int eqn, const vector<int>& nontarget_lhs,
map<tuple<int, int>, expr_t> &A0, const vector<int>& target_lhs, map<tuple<int, int>, expr_t>& A0,
map<tuple<int, int>, expr_t>& A0star) const; map<tuple<int, int>, expr_t>& A0star) const;
//! Replaces variables found in BinaryOpNode::findConstantEquations() with their constant values //! Replaces variables found in BinaryOpNode::findConstantEquations() with their constant values
virtual expr_t replaceVarsInEquation(map<VariableNode*, NumConstNode*>& table) const = 0; virtual expr_t replaceVarsInEquation(map<VariableNode*, NumConstNode*>& table) const = 0;
//! Returns true if PacExpectationNode encountered //! Returns true if PacExpectationNode encountered
virtual bool containsPacExpectation(const string &pac_model_name = "") const = 0; [[nodiscard]] virtual bool containsPacExpectation(const string& pac_model_name = "") const = 0;
//! Returns true if PacTargetNonstationaryNode encountered //! Returns true if PacTargetNonstationaryNode encountered
virtual bool containsPacTargetNonstationary(const string &pac_model_name = "") const = 0; [[nodiscard]] virtual bool containsPacTargetNonstationary(const string& pac_model_name = "") const
= 0;
//! Decompose an expression into its additive terms //! Decompose an expression into its additive terms
/*! Returns a list of terms, with their sign (either 1 or -1, depending /*! Returns a list of terms, with their sign (either 1 or -1, depending
...@@ -797,7 +891,8 @@ public: ...@@ -797,7 +891,8 @@ public:
on whether the factors appear at the numerator or the denominator). on whether the factors appear at the numerator or the denominator).
The current_exponent argument should normally be left to 1. The current_exponent argument should normally be left to 1.
If current_exponent == -1, then all exponents are inverted */ If current_exponent == -1, then all exponents are inverted */
virtual void decomposeMultiplicativeFactors(vector<pair<expr_t, int>> &factors, int current_exponent = 1) const; virtual void decomposeMultiplicativeFactors(vector<pair<expr_t, int>>& factors,
int current_exponent = 1) const;
// Matches an expression of the form variable*constant*parameter // Matches an expression of the form variable*constant*parameter
/* Returns a tuple (variable_id, lag, param_id, constant). /* Returns a tuple (variable_id, lag, param_id, constant).
...@@ -811,14 +906,15 @@ public: ...@@ -811,14 +906,15 @@ public:
If the expression is not of the expected form, throws a If the expression is not of the expected form, throws a
MatchFailureException MatchFailureException
*/ */
tuple<optional<int>, int, optional<int>, double> matchVariableTimesConstantTimesParam(bool variable_obligatory) const; [[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 /* Matches an expression of the form endogenous*constant where constant is an
expression containing no endogenous, no exogenous and no exogenous deterministic. expression containing no endogenous, no exogenous and no exogenous deterministic.
Returns (endo_id, constant expr). Returns (endo_id, constant expr).
Note that it will also match a simple endogenous (in which case the Note that it will also match a simple endogenous (in which case the
constant will of course be equal to one). */ constant will of course be equal to one). */
virtual pair<int, expr_t> matchEndogenousTimesConstant() const; [[nodiscard]] virtual pair<int, expr_t> matchEndogenousTimesConstant() const;
//! Exception thrown when matching fails //! Exception thrown when matching fails
struct MatchFailureException struct MatchFailureException
...@@ -831,17 +927,31 @@ public: ...@@ -831,17 +927,31 @@ public:
For each factor, adds an integer in the 3 vectors in argument (symb_id in 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). the first, lag in the second, exponent in the third).
Throws a MatchFailureException if not of the right form. */ Throws a MatchFailureException if not of the right form. */
virtual void matchMatchedMoment(vector<int> &symb_ids, vector<int> &lags, vector<int> &powers) const; 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 /* Returns true if the expression contains no endogenous, no exogenous and no
exogenous deterministic */ exogenous deterministic */
bool isConstant() const; [[nodiscard]] bool isConstant() const;
// Returns true if the expression contains an exogenous or an exogenous deterministic // Returns true if the expression contains an exogenous or an exogenous deterministic
bool hasExogenous() const; [[nodiscard]] bool hasExogenous() const;
// Substitutes orig_symb_id(±l) with exp(aux_symb_id(±l)) (used for “var(log)”) // Substitutes orig_symb_id(±l) with exp(aux_symb_id(±l)) (used for “var(log)”)
virtual expr_t substituteLogTransform(int orig_symb_id, int aux_symb_id) const = 0; [[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)
...@@ -858,148 +968,212 @@ struct ExprNodeLess ...@@ -858,148 +968,212 @@ 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
{ {
public: public:
//! Id from numerical constants table //! Id from numerical constants table
const int id; const int id;
private: private:
expr_t computeDerivative(int deriv_id) override; 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; 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: protected:
void prepareForDerivation() override; 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 prepareForChainRuleDerivation(
void matchVTCTPHelper(optional<int> &var_id, int &lag, optional<int> &param_id, double &constant, bool at_denominator) const override; const map<int, BinaryOpNode*>& recursive_variables,
void computeSubExprContainingVariable(int symb_id, int lag, set<expr_t> &contain_var) const override; 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 idx_arg, int id_arg); NumConstNode(DataTree& datatree_arg, int idx_arg, int id_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; 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;
void writeJsonAST(ostream& output) 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 writeJsonOutput(ostream& output, const temporary_terms_t& temporary_terms,
bool containsExternalFunction() const override; const deriv_node_temp_terms_t& tef_terms, bool isdynamic) const override;
[[nodiscard]] bool containsExternalFunction() const override;
void collectVARLHSVariable(set<expr_t>& result) const override; void collectVARLHSVariable(set<expr_t>& result) const override;
void collectDynamicVariables(SymbolType type_arg, set<pair<int, int>>& result) const override; void collectDynamicVariables(SymbolType type_arg, set<pair<int, int>>& result) const override;
double eval(const eval_context_t &eval_context) const noexcept(false) override; [[nodiscard]] double eval(const eval_context_t& eval_context) const noexcept(false) override;
void writeBytecodeOutput(BytecodeWriter &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 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;
expr_t toStatic(DataTree& static_datatree) const override; expr_t toStatic(DataTree& static_datatree) const override;
void computeXrefs(EquationInfo& ei) const override; void computeXrefs(EquationInfo& ei) const override;
BinaryOpNode* normalizeEquationHelper(const set<expr_t>& contain_var, expr_t rhs) const override; BinaryOpNode* normalizeEquationHelper(const set<expr_t>& contain_var, expr_t rhs) const override;
int maxEndoLead() const override; [[nodiscard]] int maxEndoLead() const override;
int maxExoLead() const override; [[nodiscard]] int maxExoLead() const override;
int maxEndoLag() const override; [[nodiscard]] int maxEndoLag() const override;
int maxExoLag() const override; [[nodiscard]] int maxExoLag() const override;
int maxLead() const override; [[nodiscard]] int maxLead() const override;
int maxLag() const override; [[nodiscard]] int maxLag() const override;
int maxLagWithDiffsExpanded() const override; [[nodiscard]] int maxLagWithDiffsExpanded() const override;
int VarMaxLag(const set<expr_t> &lhs_lag_equiv) const override; [[nodiscard]] int VarMaxLag(const set<expr_t>& lhs_lag_equiv) const override;
expr_t undiff() const override; [[nodiscard]] expr_t undiff() const override;
expr_t decreaseLeadsLags(int n) const override; [[nodiscard]] expr_t decreaseLeadsLags(int n) const override;
expr_t substituteEndoLeadGreaterThanTwo(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool deterministic_model) const override; expr_t substituteEndoLeadGreaterThanTwo(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
expr_t substituteEndoLagGreaterThanTwo(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override; bool deterministic_model) const override;
expr_t substituteExoLead(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; expr_t substituteExoLag(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs) const override;
expr_t substituteExpectation(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool partial_information_model) const override; expr_t substituteExpectation(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
expr_t substituteAdl() const override; bool partial_information_model) const override;
expr_t substituteModelLocalVariables() const override; [[nodiscard]] expr_t substituteAdl() const override;
expr_t substituteVarExpectation(const map<string, expr_t> &subst_table) const override; [[nodiscard]] expr_t substituteModelLocalVariables() const override;
[[nodiscard]] expr_t
substituteVarExpectation(const map<string, expr_t>& subst_table) const override;
void findDiffNodes(lag_equivalence_table_t& nodes) const override; void findDiffNodes(lag_equivalence_table_t& nodes) const override;
void findUnaryOpNodesForAuxVarCreation(lag_equivalence_table_t& nodes) const override; void findUnaryOpNodesForAuxVarCreation(lag_equivalence_table_t& nodes) const override;
optional<int> findTargetVariable(int lhs_symb_id) 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 substituteDiff(const lag_equivalence_table_t& nodes, subst_table_t& subst_table,
expr_t substituteUnaryOpNodes(const lag_equivalence_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override; 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 substitutePacExpectation(const string& name, expr_t subexpr) override;
expr_t substitutePacTargetNonstationary(const string& name, expr_t subexpr) override; expr_t substitutePacTargetNonstationary(const string& name, expr_t subexpr) override;
expr_t decreaseLeadsLagsPredeterminedVariables() const override; [[nodiscard]] expr_t decreaseLeadsLagsPredeterminedVariables() const override;
expr_t differentiateForwardVars(const vector<string> &subset, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override; expr_t differentiateForwardVars(const vector<string>& subset, subst_table_t& subst_table,
bool isNumConstNodeEqualTo(double value) const override; vector<BinaryOpNode*>& neweqs) const override;
int countDiffs() const override; [[nodiscard]] bool isNumConstNodeEqualTo(double value) const override;
bool isVariableNodeEqualTo(SymbolType type_arg, int variable_id, int lag_arg) const override; [[nodiscard]] int countDiffs() const override;
expr_t replaceTrendVar() 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 detrend(int symb_id, bool log_trend, expr_t trend) const override;
expr_t clone(DataTree& alt_datatree) const override; expr_t clone(DataTree& alt_datatree) const override;
expr_t removeTrendLeadLag(const map<int, expr_t> &trend_symbols_map) const override; [[nodiscard]] expr_t removeTrendLeadLag(const map<int, expr_t>& trend_symbols_map) const override;
bool isInStaticForm() const override; [[nodiscard]] bool isInStaticForm() const override;
expr_t replaceVarsInEquation(map<VariableNode*, NumConstNode*>& table) const override; expr_t replaceVarsInEquation(map<VariableNode*, NumConstNode*>& table) const override;
bool containsPacExpectation(const string &pac_model_name = "") const override; [[nodiscard]] bool containsPacExpectation(const string& pac_model_name = "") const override;
bool containsPacTargetNonstationary(const string &pac_model_name = "") const override; [[nodiscard]] bool containsPacTargetNonstationary(const string& pac_model_name
bool isParamTimesEndogExpr() const override; = "") const override;
expr_t substituteLogTransform(int orig_symb_id, int aux_symb_id) 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;
public: public:
//! Id from the symbol table //! Id from the symbol table
const int symb_id; const int symb_id;
//! 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;
private: private:
expr_t computeDerivative(int deriv_id) override; 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; 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: protected:
void prepareForDerivation() override; 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 prepareForChainRuleDerivation(
void matchVTCTPHelper(optional<int> &var_id, int &lag, optional<int> &param_id, double &constant, bool at_denominator) const override; const map<int, BinaryOpNode*>& recursive_variables,
void computeSubExprContainingVariable(int symb_id, int lag, set<expr_t> &contain_var) const override; 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 idx_arg, int symb_id_arg, int lag_arg); VariableNode(DataTree& datatree_arg, int idx_arg, int symb_id_arg, int lag_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; [[nodiscard]] SymbolType get_type() const;
[[nodiscard]] string getName() const;
[[nodiscard]] int getDerivID() const;
[[nodiscard]] int getTypeSpecificID() const;
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;
void writeJsonAST(ostream& output) 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 writeJsonOutput(ostream& output, const temporary_terms_t& temporary_terms,
bool containsExternalFunction() const override; const deriv_node_temp_terms_t& tef_terms, bool isdynamic) const override;
[[nodiscard]] bool containsExternalFunction() const override;
void collectVARLHSVariable(set<expr_t>& result) const override; void collectVARLHSVariable(set<expr_t>& result) const override;
void collectDynamicVariables(SymbolType type_arg, set<pair<int, int>>& result) const override; void collectDynamicVariables(SymbolType type_arg, set<pair<int, int>>& result) const override;
double eval(const eval_context_t &eval_context) const noexcept(false) override; [[nodiscard]] double eval(const eval_context_t& eval_context) const noexcept(false) override;
void writeBytecodeOutput(BytecodeWriter &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 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;
expr_t toStatic(DataTree& static_datatree) const override; expr_t toStatic(DataTree& static_datatree) const override;
void computeXrefs(EquationInfo& ei) const override; void computeXrefs(EquationInfo& ei) const override;
SymbolType get_type() const;
BinaryOpNode* normalizeEquationHelper(const set<expr_t>& contain_var, expr_t rhs) const override; BinaryOpNode* normalizeEquationHelper(const set<expr_t>& contain_var, expr_t rhs) const override;
int maxEndoLead() const override; [[nodiscard]] int maxEndoLead() const override;
int maxExoLead() const override; [[nodiscard]] int maxExoLead() const override;
int maxEndoLag() const override; [[nodiscard]] int maxEndoLag() const override;
int maxExoLag() const override; [[nodiscard]] int maxExoLag() const override;
int maxLead() const override; [[nodiscard]] int maxLead() const override;
int maxLag() const override; [[nodiscard]] int maxLag() const override;
int maxLagWithDiffsExpanded() const override; [[nodiscard]] int maxLagWithDiffsExpanded() const override;
int VarMaxLag(const set<expr_t> &lhs_lag_equiv) const override; [[nodiscard]] int VarMaxLag(const set<expr_t>& lhs_lag_equiv) const override;
expr_t undiff() const override; [[nodiscard]] expr_t undiff() const override;
expr_t decreaseLeadsLags(int n) const override; [[nodiscard]] expr_t decreaseLeadsLags(int n) const override;
expr_t substituteEndoLeadGreaterThanTwo(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool deterministic_model) const override; expr_t substituteEndoLeadGreaterThanTwo(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
expr_t substituteEndoLagGreaterThanTwo(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override; bool deterministic_model) const override;
expr_t substituteExoLead(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; expr_t substituteExoLag(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs) const override;
expr_t substituteExpectation(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool partial_information_model) const override; expr_t substituteExpectation(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
expr_t substituteAdl() const override; bool partial_information_model) const override;
expr_t substituteModelLocalVariables() const override; [[nodiscard]] expr_t substituteAdl() const override;
expr_t substituteVarExpectation(const map<string, expr_t> &subst_table) const override; [[nodiscard]] expr_t substituteModelLocalVariables() const override;
[[nodiscard]] expr_t
substituteVarExpectation(const map<string, expr_t>& subst_table) const override;
void findDiffNodes(lag_equivalence_table_t& nodes) const override; void findDiffNodes(lag_equivalence_table_t& nodes) const override;
void findUnaryOpNodesForAuxVarCreation(lag_equivalence_table_t& nodes) const override; void findUnaryOpNodesForAuxVarCreation(lag_equivalence_table_t& nodes) const override;
optional<int> findTargetVariable(int lhs_symb_id) 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 substituteDiff(const lag_equivalence_table_t& nodes, subst_table_t& subst_table,
expr_t substituteUnaryOpNodes(const lag_equivalence_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override; 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 substitutePacExpectation(const string& name, expr_t subexpr) override;
expr_t substitutePacTargetNonstationary(const string& name, expr_t subexpr) override; expr_t substitutePacTargetNonstationary(const string& name, expr_t subexpr) override;
expr_t decreaseLeadsLagsPredeterminedVariables() const override; [[nodiscard]] expr_t decreaseLeadsLagsPredeterminedVariables() const override;
expr_t differentiateForwardVars(const vector<string> &subset, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override; expr_t differentiateForwardVars(const vector<string>& subset, subst_table_t& subst_table,
bool isNumConstNodeEqualTo(double value) const override; vector<BinaryOpNode*>& neweqs) const override;
int countDiffs() const override; [[nodiscard]] bool isNumConstNodeEqualTo(double value) const override;
bool isVariableNodeEqualTo(SymbolType type_arg, int variable_id, int lag_arg) const override; [[nodiscard]] int countDiffs() const override;
expr_t replaceTrendVar() 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 detrend(int symb_id, bool log_trend, expr_t trend) const override;
expr_t clone(DataTree& alt_datatree) const override; expr_t clone(DataTree& alt_datatree) const override;
expr_t removeTrendLeadLag(const map<int, expr_t> &trend_symbols_map) const override; [[nodiscard]] expr_t removeTrendLeadLag(const map<int, expr_t>& trend_symbols_map) const override;
bool isInStaticForm() const override; [[nodiscard]] bool isInStaticForm() const override;
expr_t replaceVarsInEquation(map<VariableNode*, NumConstNode*>& table) const override; expr_t replaceVarsInEquation(map<VariableNode*, NumConstNode*>& table) const override;
bool containsPacExpectation(const string &pac_model_name = "") const override; [[nodiscard]] bool containsPacExpectation(const string& pac_model_name = "") const override;
bool containsPacTargetNonstationary(const string &pac_model_name = "") const override; [[nodiscard]] bool containsPacTargetNonstationary(const string& pac_model_name
bool isParamTimesEndogExpr() const override; = "") const override;
void matchMatchedMoment(vector<int> &symb_ids, vector<int> &lags, vector<int> &powers) const override; [[nodiscard]] bool isParamTimesEndogExpr() const override;
pair<int, expr_t> matchEndogenousTimesConstant() const override; void matchMatchedMoment(vector<int>& symb_ids, vector<int>& lags,
expr_t substituteLogTransform(int orig_symb_id, int aux_symb_id) const override; 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
...@@ -1007,17 +1181,24 @@ class UnaryOpNode : public ExprNode ...@@ -1007,17 +1181,24 @@ class UnaryOpNode : public ExprNode
{ {
protected: protected:
void prepareForDerivation() override; 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 prepareForChainRuleDerivation(
void matchVTCTPHelper(optional<int> &var_id, int &lag, optional<int> &param_id, double &constant, bool at_denominator) const override; const map<int, BinaryOpNode*>& recursive_variables,
void computeSubExprContainingVariable(int symb_id, int lag, set<expr_t> &contain_var) const override; unordered_map<expr_t, set<int>>& non_null_chain_rule_derivatives) const override;
// Returns the node obtained by applying a transformation recursively on the argument (in same datatree) 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> template<typename Callable, typename... Args>
requires invocable<Callable, expr_t, Args...>
expr_t expr_t
recurseTransform(Callable&& op, Args&&... args) const recurseTransform(Callable&& op, Args&&... args) const
{ {
expr_t substarg {invoke(forward<Callable>(op), arg, forward<Args>(args)...)}; expr_t substarg {invoke(forward<Callable>(op), arg, forward<Args>(args)...)};
return buildSimilarUnaryOpNode(substarg, datatree); return buildSimilarUnaryOpNode(substarg, datatree);
} }
public: 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
...@@ -1027,26 +1208,40 @@ public: ...@@ -1027,26 +1208,40 @@ public:
const UnaryOpcode op_code; const UnaryOpcode op_code;
const string adl_param_name; const string adl_param_name;
const vector<int> adl_lags; const vector<int> adl_lags;
private: private:
expr_t computeDerivative(int deriv_id) override; 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; expr_t
int cost(int cost, bool is_matlab) const override; computeChainRuleDerivative(int deriv_id, const map<int, BinaryOpNode*>& recursive_variables,
int cost(const vector<vector<unordered_set<expr_t>>> &blocks_temporary_terms, bool is_matlab) const override; unordered_map<expr_t, set<int>>& non_null_chain_rule_derivatives,
int cost(const map<pair<int, int>, unordered_set<expr_t>> &temp_terms_map, bool is_matlab) const override; 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, int idx_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, string adl_param_name_arg, vector<int> adl_lags_arg); UnaryOpNode(DataTree& datatree_arg, int idx_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,
string adl_param_name_arg, vector<int> adl_lags_arg);
void computeTemporaryTerms(const pair<int, int>& derivOrder, void computeTemporaryTerms(const pair<int, int>& derivOrder,
map<pair<int, int>, unordered_set<expr_t>>& temp_terms_map, map<pair<int, int>, unordered_set<expr_t>>& temp_terms_map,
unordered_map<expr_t, pair<int, pair<int, int>>>& reference_count, unordered_map<expr_t, pair<int, pair<int, int>>>& reference_count,
bool is_matlab) const override; bool is_matlab) const override;
void computeBlockTemporaryTerms(int blk, int eq, vector<vector<unordered_set<expr_t>>> &blocks_temporary_terms, 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; 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_idxs_t &temporary_terms_idxs, const deriv_node_temp_terms_t &tef_terms) const override; 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;
void writeJsonAST(ostream& output) 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 writeJsonOutput(ostream& output, const temporary_terms_t& temporary_terms,
bool containsExternalFunction() const override; const deriv_node_temp_terms_t& tef_terms, bool isdynamic) const override;
[[nodiscard]] bool containsExternalFunction() const override;
void writeExternalFunctionOutput(ostream& output, ExprNodeOutputType output_type, 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, const temporary_terms_idxs_t& temporary_terms_idxs,
...@@ -1055,7 +1250,7 @@ public: ...@@ -1055,7 +1250,7 @@ public:
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,
bool isdynamic) const override; bool isdynamic) const override;
void writeBytecodeExternalFunctionOutput(BytecodeWriter &code_file, void writeBytecodeExternalFunctionOutput(Bytecode::Writer& code_file,
ExprNodeBytecodeOutputType output_type, ExprNodeBytecodeOutputType output_type,
const temporary_terms_t& temporary_terms, const temporary_terms_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs, const temporary_terms_idxs_t& temporary_terms_idxs,
...@@ -1063,55 +1258,71 @@ public: ...@@ -1063,55 +1258,71 @@ public:
void collectVARLHSVariable(set<expr_t>& result) const override; void collectVARLHSVariable(set<expr_t>& result) const override;
void collectDynamicVariables(SymbolType type_arg, set<pair<int, int>>& result) const override; void collectDynamicVariables(SymbolType type_arg, set<pair<int, int>>& result) const override;
static double eval_opcode(UnaryOpcode op_code, double v) noexcept(false); static double eval_opcode(UnaryOpcode op_code, double v) noexcept(false);
double eval(const eval_context_t &eval_context) const noexcept(false) override; [[nodiscard]] double eval(const eval_context_t& eval_context) const noexcept(false) override;
void writeBytecodeOutput(BytecodeWriter &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 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;
expr_t toStatic(DataTree& static_datatree) const override; expr_t toStatic(DataTree& static_datatree) const override;
void computeXrefs(EquationInfo& ei) const override; void computeXrefs(EquationInfo& ei) const override;
BinaryOpNode* normalizeEquationHelper(const set<expr_t>& contain_var, expr_t rhs) const override; BinaryOpNode* normalizeEquationHelper(const set<expr_t>& contain_var, expr_t rhs) const override;
int maxEndoLead() const override; [[nodiscard]] int maxEndoLead() const override;
int maxExoLead() const override; [[nodiscard]] int maxExoLead() const override;
int maxEndoLag() const override; [[nodiscard]] int maxEndoLag() const override;
int maxExoLag() const override; [[nodiscard]] int maxExoLag() const override;
int maxLead() const override; [[nodiscard]] int maxLead() const override;
int maxLag() const override; [[nodiscard]] int maxLag() const override;
int maxLagWithDiffsExpanded() const override; [[nodiscard]] int maxLagWithDiffsExpanded() const override;
int VarMaxLag(const set<expr_t> &lhs_lag_equiv) const override; [[nodiscard]] int VarMaxLag(const set<expr_t>& lhs_lag_equiv) const override;
expr_t undiff() const override; [[nodiscard]] expr_t undiff() const override;
expr_t decreaseLeadsLags(int n) const override; [[nodiscard]] expr_t decreaseLeadsLags(int n) const override;
expr_t substituteEndoLeadGreaterThanTwo(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool deterministic_model) const override; expr_t substituteEndoLeadGreaterThanTwo(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
//! Creates another UnaryOpNode with the same opcode, but with a possibly different datatree and argument bool deterministic_model) const override;
//! 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;
expr_t substituteEndoLagGreaterThanTwo(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override; expr_t substituteEndoLagGreaterThanTwo(subst_table_t& subst_table,
expr_t substituteExoLead(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool deterministic_model) const override; 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; expr_t substituteExoLag(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs) const override;
expr_t substituteExpectation(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool partial_information_model) const override; expr_t substituteExpectation(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
expr_t substituteAdl() const override; bool partial_information_model) const override;
expr_t substituteModelLocalVariables() const override; [[nodiscard]] expr_t substituteAdl() const override;
expr_t substituteVarExpectation(const map<string, expr_t> &subst_table) const override; [[nodiscard]] expr_t substituteModelLocalVariables() const override;
[[nodiscard]] expr_t
substituteVarExpectation(const map<string, expr_t>& subst_table) const override;
void findDiffNodes(lag_equivalence_table_t& nodes) const override; void findDiffNodes(lag_equivalence_table_t& nodes) const override;
bool createAuxVarForUnaryOpNode() const; [[nodiscard]] bool createAuxVarForUnaryOpNode() const;
void findUnaryOpNodesForAuxVarCreation(lag_equivalence_table_t& nodes) const override; void findUnaryOpNodesForAuxVarCreation(lag_equivalence_table_t& nodes) const override;
optional<int> findTargetVariable(int lhs_symb_id) 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 substituteDiff(const lag_equivalence_table_t& nodes, subst_table_t& subst_table,
expr_t substituteUnaryOpNodes(const lag_equivalence_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override; 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 substitutePacExpectation(const string& name, expr_t subexpr) override;
expr_t substitutePacTargetNonstationary(const string& name, expr_t subexpr) override; expr_t substitutePacTargetNonstationary(const string& name, expr_t subexpr) override;
expr_t decreaseLeadsLagsPredeterminedVariables() const override; [[nodiscard]] expr_t decreaseLeadsLagsPredeterminedVariables() const override;
expr_t differentiateForwardVars(const vector<string> &subset, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override; expr_t differentiateForwardVars(const vector<string>& subset, subst_table_t& subst_table,
bool isNumConstNodeEqualTo(double value) const override; vector<BinaryOpNode*>& neweqs) const override;
int countDiffs() const override; [[nodiscard]] bool isNumConstNodeEqualTo(double value) const override;
bool isVariableNodeEqualTo(SymbolType type_arg, int variable_id, int lag_arg) const override; [[nodiscard]] int countDiffs() const override;
expr_t replaceTrendVar() 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 detrend(int symb_id, bool log_trend, expr_t trend) const override;
expr_t clone(DataTree& alt_datatree) const override; expr_t clone(DataTree& alt_datatree) const override;
expr_t removeTrendLeadLag(const map<int, expr_t> &trend_symbols_map) const override; [[nodiscard]] expr_t removeTrendLeadLag(const map<int, expr_t>& trend_symbols_map) const override;
bool isInStaticForm() const override; [[nodiscard]] bool isInStaticForm() const override;
expr_t replaceVarsInEquation(map<VariableNode*, NumConstNode*>& table) const override; expr_t replaceVarsInEquation(map<VariableNode*, NumConstNode*>& table) const override;
bool containsPacExpectation(const string &pac_model_name = "") const override; [[nodiscard]] bool containsPacExpectation(const string& pac_model_name = "") const override;
bool containsPacTargetNonstationary(const string &pac_model_name = "") const override; [[nodiscard]] bool containsPacTargetNonstationary(const string& pac_model_name
bool isParamTimesEndogExpr() const override; = "") const override;
[[nodiscard]] bool isParamTimesEndogExpr() const override;
void decomposeAdditiveTerms(vector<pair<expr_t, int>>& terms, int current_sign) const override; void decomposeAdditiveTerms(vector<pair<expr_t, int>>& terms, int current_sign) const override;
expr_t substituteLogTransform(int orig_symb_id, int aux_symb_id) 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
...@@ -1119,24 +1330,37 @@ class BinaryOpNode : public ExprNode ...@@ -1119,24 +1330,37 @@ class BinaryOpNode : public ExprNode
{ {
protected: protected:
void prepareForDerivation() override; 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 prepareForChainRuleDerivation(
void matchVTCTPHelper(optional<int> &var_id, int &lag, optional<int> &param_id, double &constant, bool at_denominator) const override; const map<int, BinaryOpNode*>& recursive_variables,
void computeSubExprContainingVariable(int symb_id, int lag, set<expr_t> &contain_var) const override; 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:
const expr_t arg1, arg2; const expr_t arg1, arg2;
const BinaryOpcode op_code; const BinaryOpcode op_code;
const int powerDerivOrder; const int powerDerivOrder;
const string adlparam; const string adlparam;
private: private:
expr_t computeDerivative(int deriv_id) override; 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; expr_t
int cost(int cost, bool is_matlab) const override; computeChainRuleDerivative(int deriv_id, const map<int, BinaryOpNode*>& recursive_variables,
int cost(const vector<vector<unordered_set<expr_t>>> &blocks_temporary_terms, bool is_matlab) const override; unordered_map<expr_t, set<int>>& non_null_chain_rule_derivatives,
int cost(const map<pair<int, int>, unordered_set<expr_t>> &temp_terms_map, bool is_matlab) const override; 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);
// Returns the node obtained by applying a transformation recursively on the arguments (in same datatree) // Returns the node obtained by applying a transformation recursively on the arguments (in same
// datatree)
template<typename Callable, typename... Args> template<typename Callable, typename... Args>
requires invocable<Callable, expr_t, Args...>
expr_t expr_t
recurseTransform(Callable&& op, Args&&... args) const recurseTransform(Callable&& op, Args&&... args) const
{ {
...@@ -1144,21 +1368,28 @@ private: ...@@ -1144,21 +1368,28 @@ private:
expr_t substarg2 {invoke(forward<Callable>(op), arg2, forward<Args>(args)...)}; expr_t substarg2 {invoke(forward<Callable>(op), arg2, forward<Args>(args)...)};
return buildSimilarBinaryOpNode(substarg1, substarg2, datatree); return buildSimilarBinaryOpNode(substarg1, substarg2, datatree);
} }
public: public:
BinaryOpNode(DataTree &datatree_arg, int idx_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, int powerDerivOrder); const expr_t arg2_arg, int powerDerivOrder);
int precedenceJson(const temporary_terms_t &temporary_terms) const override; [[nodiscard]] int precedenceJson(const temporary_terms_t& temporary_terms) const override;
int precedence(ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms) const override; [[nodiscard]] int precedence(ExprNodeOutputType output_type,
const temporary_terms_t& temporary_terms) const override;
void computeTemporaryTerms(const pair<int, int>& derivOrder, void computeTemporaryTerms(const pair<int, int>& derivOrder,
map<pair<int, int>, unordered_set<expr_t>>& temp_terms_map, map<pair<int, int>, unordered_set<expr_t>>& temp_terms_map,
unordered_map<expr_t, pair<int, pair<int, int>>>& reference_count, unordered_map<expr_t, pair<int, pair<int, int>>>& reference_count,
bool is_matlab) const override; bool is_matlab) const override;
void computeBlockTemporaryTerms(int blk, int eq, vector<vector<unordered_set<expr_t>>> &blocks_temporary_terms, 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; 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_idxs_t &temporary_terms_idxs, const deriv_node_temp_terms_t &tef_terms) const override; 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;
void writeJsonAST(ostream& output) 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 writeJsonOutput(ostream& output, const temporary_terms_t& temporary_terms,
bool containsExternalFunction() const override; const deriv_node_temp_terms_t& tef_terms, bool isdynamic) const override;
[[nodiscard]] bool containsExternalFunction() const override;
void writeExternalFunctionOutput(ostream& output, ExprNodeOutputType output_type, 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, const temporary_terms_idxs_t& temporary_terms_idxs,
...@@ -1167,74 +1398,92 @@ public: ...@@ -1167,74 +1398,92 @@ public:
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,
bool isdynamic) const override; bool isdynamic) const override;
void writeBytecodeExternalFunctionOutput(BytecodeWriter &code_file, void writeBytecodeExternalFunctionOutput(Bytecode::Writer& code_file,
ExprNodeBytecodeOutputType output_type, ExprNodeBytecodeOutputType output_type,
const temporary_terms_t& temporary_terms, const temporary_terms_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs, const temporary_terms_idxs_t& temporary_terms_idxs,
deriv_node_temp_terms_t& tef_terms) const override; deriv_node_temp_terms_t& tef_terms) const override;
void collectVARLHSVariable(set<expr_t>& result) const override; void collectVARLHSVariable(set<expr_t>& result) const override;
void collectDynamicVariables(SymbolType type_arg, set<pair<int, int>>& result) const override; void collectDynamicVariables(SymbolType type_arg, set<pair<int, int>>& result) const override;
static double eval_opcode(double v1, BinaryOpcode op_code, double v2, int derivOrder) noexcept(false); static double eval_opcode(double v1, BinaryOpcode op_code, double v2,
double eval(const eval_context_t &eval_context) const noexcept(false) override; int derivOrder) noexcept(false);
void writeBytecodeOutput(BytecodeWriter &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; [[nodiscard]] double eval(const eval_context_t& eval_context) const noexcept(false) 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;
expr_t Compute_RHS(expr_t arg1, expr_t arg2, int op, int op_type) const; expr_t Compute_RHS(expr_t arg1, expr_t arg2, int op, int op_type) const;
expr_t toStatic(DataTree& static_datatree) const override; expr_t toStatic(DataTree& static_datatree) const override;
void computeXrefs(EquationInfo& ei) const override; void computeXrefs(EquationInfo& ei) const override;
BinaryOpNode* normalizeEquationHelper(const set<expr_t>& contain_var, expr_t rhs) const override; BinaryOpNode* normalizeEquationHelper(const set<expr_t>& contain_var, expr_t rhs) const override;
//! Try to normalize an equation with respect to a given dynamic variable. //! 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. */ /*! Should only be called on Equal nodes. The variable must appear in the equation. */
BinaryOpNode *normalizeEquation(int symb_id, int lag) const; [[nodiscard]] BinaryOpNode* normalizeEquation(int symb_id, int lag) const;
int maxEndoLead() const override; [[nodiscard]] int maxEndoLead() const override;
int maxExoLead() const override; [[nodiscard]] int maxExoLead() const override;
int maxEndoLag() const override; [[nodiscard]] int maxEndoLag() const override;
int maxExoLag() const override; [[nodiscard]] int maxExoLag() const override;
int maxLead() const override; [[nodiscard]] int maxLead() const override;
int maxLag() const override; [[nodiscard]] int maxLag() const override;
int maxLagWithDiffsExpanded() const override; [[nodiscard]] int maxLagWithDiffsExpanded() const override;
int VarMaxLag(const set<expr_t> &lhs_lag_equiv) const override; [[nodiscard]] int VarMaxLag(const set<expr_t>& lhs_lag_equiv) const override;
expr_t undiff() const override; [[nodiscard]] expr_t undiff() const override;
expr_t decreaseLeadsLags(int n) const override; [[nodiscard]] expr_t decreaseLeadsLags(int n) const override;
expr_t substituteEndoLeadGreaterThanTwo(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool deterministic_model) const override; expr_t substituteEndoLeadGreaterThanTwo(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
//! Creates another BinaryOpNode with the same opcode, but with a possibly different datatree and arguments bool deterministic_model) const override;
//! 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;
expr_t substituteEndoLagGreaterThanTwo(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override; expr_t substituteEndoLagGreaterThanTwo(subst_table_t& subst_table,
expr_t substituteExoLead(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool deterministic_model) const override; 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; expr_t substituteExoLag(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs) const override;
expr_t substituteExpectation(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool partial_information_model) const override; expr_t substituteExpectation(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
expr_t substituteAdl() const override; bool partial_information_model) const override;
expr_t substituteModelLocalVariables() const override; [[nodiscard]] expr_t substituteAdl() const override;
expr_t substituteVarExpectation(const map<string, expr_t> &subst_table) const override; [[nodiscard]] expr_t substituteModelLocalVariables() const override;
[[nodiscard]] expr_t
substituteVarExpectation(const map<string, expr_t>& subst_table) const override;
void findDiffNodes(lag_equivalence_table_t& nodes) const override; void findDiffNodes(lag_equivalence_table_t& nodes) const override;
void findUnaryOpNodesForAuxVarCreation(lag_equivalence_table_t& nodes) const override; void findUnaryOpNodesForAuxVarCreation(lag_equivalence_table_t& nodes) const override;
bool findTargetVariableHelper1(int lhs_symb_id, int rhs_symb_id) const; [[nodiscard]] bool findTargetVariableHelper1(int lhs_symb_id, int rhs_symb_id) const;
optional<int> findTargetVariableHelper(const expr_t arg1, const expr_t arg2, int lhs_symb_id) const; optional<int> findTargetVariableHelper(const expr_t arg1, const expr_t arg2,
optional<int> findTargetVariable(int lhs_symb_id) const override; int lhs_symb_id) const;
expr_t substituteDiff(const lag_equivalence_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override; [[nodiscard]] optional<int> findTargetVariable(int lhs_symb_id) const override;
expr_t substituteUnaryOpNodes(const lag_equivalence_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) 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 substitutePacExpectation(const string& name, expr_t subexpr) override;
expr_t substitutePacTargetNonstationary(const string& name, expr_t subexpr) override; expr_t substitutePacTargetNonstationary(const string& name, expr_t subexpr) override;
expr_t decreaseLeadsLagsPredeterminedVariables() const override; [[nodiscard]] expr_t decreaseLeadsLagsPredeterminedVariables() const override;
expr_t differentiateForwardVars(const vector<string> &subset, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override; expr_t differentiateForwardVars(const vector<string>& subset, subst_table_t& subst_table,
bool isNumConstNodeEqualTo(double value) const override; vector<BinaryOpNode*>& neweqs) const override;
int countDiffs() const override; [[nodiscard]] bool isNumConstNodeEqualTo(double value) const override;
bool isVariableNodeEqualTo(SymbolType type_arg, int variable_id, int lag_arg) const override; [[nodiscard]] int countDiffs() const override;
expr_t replaceTrendVar() 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 detrend(int symb_id, bool log_trend, expr_t trend) const override;
expr_t clone(DataTree& alt_datatree) const override; expr_t clone(DataTree& alt_datatree) const override;
expr_t removeTrendLeadLag(const map<int, expr_t> &trend_symbols_map) 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 //! Function to write out the oPowerNode in expr_t terms as opposed to writing out the function
expr_t unpackPowerDeriv() const; //! 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;
bool isInStaticForm() const override; [[nodiscard]] bool isInStaticForm() const override;
void fillAutoregressiveRow(int eqn, const vector<int> &lhs, map<tuple<int, int, int>, expr_t> &AR) const; void fillAutoregressiveRow(int eqn, const vector<int>& lhs,
map<tuple<int, int, int>, expr_t>& AR) const;
//! Finds equations where a variable is equal to a constant //! Finds equations where a variable is equal to a constant
void findConstantEquations(map<VariableNode*, NumConstNode*>& table) const; void findConstantEquations(map<VariableNode*, NumConstNode*>& table) const;
expr_t replaceVarsInEquation(map<VariableNode*, NumConstNode*>& table) const override; expr_t replaceVarsInEquation(map<VariableNode*, NumConstNode*>& table) const override;
bool containsPacExpectation(const string &pac_model_name = "") const override; [[nodiscard]] bool containsPacExpectation(const string& pac_model_name = "") const override;
bool containsPacTargetNonstationary(const string &pac_model_name = "") const override; [[nodiscard]] bool containsPacTargetNonstationary(const string& pac_model_name
= "") const override;
/* /*
ec_params_and_vars: ec_params_and_vars:
- 1st element = feedback force parameter - 1st element = feedback force parameter
...@@ -1247,7 +1496,8 @@ public: ...@@ -1247,7 +1496,8 @@ public:
variable_lag is *not* the lag order in the AR variable_lag is *not* the lag order in the AR
(because variable is an AUX_DIFF_LAG) (because variable is an AUX_DIFF_LAG)
*/ */
void getPacAREC(int lhs_symb_id, int lhs_orig_symb_id, void getPacAREC(
int lhs_symb_id, int lhs_orig_symb_id,
pair<int, vector<tuple<int, bool, int>>>& ec_params_and_vars, pair<int, vector<tuple<int, bool, int>>>& ec_params_and_vars,
vector<tuple<optional<int>, optional<int>, int>>& ar_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; vector<tuple<int, int, optional<int>, double>>& additive_vars_params_and_constants) const;
...@@ -1255,38 +1505,61 @@ public: ...@@ -1255,38 +1505,61 @@ public:
//! Finds the share of optimizing agents in the PAC equation, //! Finds the share of optimizing agents in the PAC equation,
//! the expr node associated with it, //! the expr node associated with it,
//! and the expr node associated with the non-optimizing part //! and the expr node associated with the non-optimizing part
tuple<optional<int>, expr_t, expr_t, expr_t> getPacOptimizingShareAndExprNodes(int lhs_orig_symb_id) const; [[nodiscard]] tuple<optional<int>, expr_t, expr_t, expr_t>
pair<optional<int>, expr_t> getPacOptimizingShareAndExprNodesHelper(int lhs_orig_symb_id) const; getPacOptimizingShareAndExprNodes(int lhs_orig_symb_id) const;
expr_t getPacNonOptimizingPart(int optim_share_symb_id) const; [[nodiscard]] pair<optional<int>, expr_t>
bool isParamTimesEndogExpr() const override; 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 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 decomposeMultiplicativeFactors(vector<pair<expr_t, int>>& factors,
void matchMatchedMoment(vector<int> &symb_ids, vector<int> &lags, vector<int> &powers) const override; int current_exponent = 1) const override;
pair<int, expr_t> matchEndogenousTimesConstant() const override; void matchMatchedMoment(vector<int>& symb_ids, vector<int>& lags,
expr_t substituteLogTransform(int orig_symb_id, int aux_symb_id) const override; 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;
public: public:
const expr_t arg1, arg2, arg3; const expr_t arg1, arg2, arg3;
const TrinaryOpcode op_code; const TrinaryOpcode op_code;
protected: protected:
void prepareForDerivation() override; 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 prepareForChainRuleDerivation(
void computeSubExprContainingVariable(int symb_id, int lag, set<expr_t> &contain_var) const override; 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: private:
expr_t computeDerivative(int deriv_id) override; 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; expr_t
int cost(int cost, bool is_matlab) const override; computeChainRuleDerivative(int deriv_id, const map<int, BinaryOpNode*>& recursive_variables,
int cost(const vector<vector<unordered_set<expr_t>>> &blocks_temporary_terms, bool is_matlab) const override; unordered_map<expr_t, set<int>>& non_null_chain_rule_derivatives,
int cost(const map<pair<int, int>, unordered_set<expr_t>> &temp_terms_map, bool is_matlab) const override; unordered_map<expr_t, map<int, expr_t>>& cache) override;
//! Returns the derivative of this node if darg1, darg2 and darg3 are the derivatives of the arguments [[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) // Returns the node obtained by applying a transformation recursively on the arguments (in same
// datatree)
template<typename Callable, typename... Args> template<typename Callable, typename... Args>
requires invocable<Callable, expr_t, Args...>
expr_t expr_t
recurseTransform(Callable&& op, Args&&... args) const recurseTransform(Callable&& op, Args&&... args) const
{ {
...@@ -1295,20 +1568,27 @@ private: ...@@ -1295,20 +1568,27 @@ private:
expr_t substarg3 {invoke(forward<Callable>(op), arg3, forward<Args>(args)...)}; expr_t substarg3 {invoke(forward<Callable>(op), arg3, forward<Args>(args)...)};
return buildSimilarTrinaryOpNode(substarg1, substarg2, substarg3, datatree); return buildSimilarTrinaryOpNode(substarg1, substarg2, substarg3, datatree);
} }
public: public:
TrinaryOpNode(DataTree& datatree_arg, int idx_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);
int precedence(ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms) const override; [[nodiscard]] int precedence(ExprNodeOutputType output_type,
const temporary_terms_t& temporary_terms) const override;
void computeTemporaryTerms(const pair<int, int>& derivOrder, void computeTemporaryTerms(const pair<int, int>& derivOrder,
map<pair<int, int>, unordered_set<expr_t>>& temp_terms_map, map<pair<int, int>, unordered_set<expr_t>>& temp_terms_map,
unordered_map<expr_t, pair<int, pair<int, int>>>& reference_count, unordered_map<expr_t, pair<int, pair<int, int>>>& reference_count,
bool is_matlab) const override; bool is_matlab) const override;
void computeBlockTemporaryTerms(int blk, int eq, vector<vector<unordered_set<expr_t>>> &blocks_temporary_terms, 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; 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_idxs_t &temporary_terms_idxs, const deriv_node_temp_terms_t &tef_terms) const override; 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;
void writeJsonAST(ostream& output) 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 writeJsonOutput(ostream& output, const temporary_terms_t& temporary_terms,
bool containsExternalFunction() const override; const deriv_node_temp_terms_t& tef_terms, bool isdynamic) const override;
[[nodiscard]] bool containsExternalFunction() const override;
void writeExternalFunctionOutput(ostream& output, ExprNodeOutputType output_type, 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, const temporary_terms_idxs_t& temporary_terms_idxs,
...@@ -1317,7 +1597,7 @@ public: ...@@ -1317,7 +1597,7 @@ public:
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,
bool isdynamic) const override; bool isdynamic) const override;
void writeBytecodeExternalFunctionOutput(BytecodeWriter &code_file, void writeBytecodeExternalFunctionOutput(Bytecode::Writer& code_file,
ExprNodeBytecodeOutputType output_type, ExprNodeBytecodeOutputType output_type,
const temporary_terms_t& temporary_terms, const temporary_terms_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs, const temporary_terms_idxs_t& temporary_terms_idxs,
...@@ -1325,53 +1605,70 @@ public: ...@@ -1325,53 +1605,70 @@ public:
void collectVARLHSVariable(set<expr_t>& result) const override; void collectVARLHSVariable(set<expr_t>& result) const override;
void collectDynamicVariables(SymbolType type_arg, set<pair<int, int>>& result) const override; void collectDynamicVariables(SymbolType type_arg, set<pair<int, int>>& result) const override;
static double eval_opcode(double v1, TrinaryOpcode op_code, double v2, double v3) noexcept(false); static double eval_opcode(double v1, TrinaryOpcode op_code, double v2, double v3) noexcept(false);
double eval(const eval_context_t &eval_context) const noexcept(false) override; [[nodiscard]] double eval(const eval_context_t& eval_context) const noexcept(false) override;
void writeBytecodeOutput(BytecodeWriter &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 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;
expr_t toStatic(DataTree& static_datatree) const override; expr_t toStatic(DataTree& static_datatree) const override;
void computeXrefs(EquationInfo& ei) const override; void computeXrefs(EquationInfo& ei) const override;
BinaryOpNode* normalizeEquationHelper(const set<expr_t>& contain_var, expr_t rhs) const override; BinaryOpNode* normalizeEquationHelper(const set<expr_t>& contain_var, expr_t rhs) const override;
int maxEndoLead() const override; [[nodiscard]] int maxEndoLead() const override;
int maxExoLead() const override; [[nodiscard]] int maxExoLead() const override;
int maxEndoLag() const override; [[nodiscard]] int maxEndoLag() const override;
int maxExoLag() const override; [[nodiscard]] int maxExoLag() const override;
int maxLead() const override; [[nodiscard]] int maxLead() const override;
int maxLag() const override; [[nodiscard]] int maxLag() const override;
int maxLagWithDiffsExpanded() const override; [[nodiscard]] int maxLagWithDiffsExpanded() const override;
int VarMaxLag(const set<expr_t> &lhs_lag_equiv) const override; [[nodiscard]] int VarMaxLag(const set<expr_t>& lhs_lag_equiv) const override;
expr_t undiff() const override; [[nodiscard]] expr_t undiff() const override;
expr_t decreaseLeadsLags(int n) const override; [[nodiscard]] expr_t decreaseLeadsLags(int n) const override;
expr_t substituteEndoLeadGreaterThanTwo(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool deterministic_model) const override; expr_t substituteEndoLeadGreaterThanTwo(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
//! Creates another TrinaryOpNode with the same opcode, but with a possibly different datatree and arguments bool deterministic_model) const override;
expr_t buildSimilarTrinaryOpNode(expr_t alt_arg1, expr_t alt_arg2, expr_t alt_arg3, DataTree &alt_datatree) const; //! Creates another TrinaryOpNode with the same opcode, but with a possibly different datatree and
expr_t substituteEndoLagGreaterThanTwo(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override; //! arguments
expr_t substituteExoLead(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool deterministic_model) const override; expr_t buildSimilarTrinaryOpNode(expr_t alt_arg1, expr_t alt_arg2, expr_t alt_arg3,
DataTree& alt_datatree) const;
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; expr_t substituteExoLag(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs) const override;
expr_t substituteExpectation(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool partial_information_model) const override; expr_t substituteExpectation(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
expr_t substituteAdl() const override; bool partial_information_model) const override;
expr_t substituteModelLocalVariables() const override; [[nodiscard]] expr_t substituteAdl() const override;
expr_t substituteVarExpectation(const map<string, expr_t> &subst_table) const override; [[nodiscard]] expr_t substituteModelLocalVariables() const override;
[[nodiscard]] expr_t
substituteVarExpectation(const map<string, expr_t>& subst_table) const override;
void findDiffNodes(lag_equivalence_table_t& nodes) const override; void findDiffNodes(lag_equivalence_table_t& nodes) const override;
void findUnaryOpNodesForAuxVarCreation(lag_equivalence_table_t& nodes) const override; void findUnaryOpNodesForAuxVarCreation(lag_equivalence_table_t& nodes) const override;
optional<int> findTargetVariable(int lhs_symb_id) 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 substituteDiff(const lag_equivalence_table_t& nodes, subst_table_t& subst_table,
expr_t substituteUnaryOpNodes(const lag_equivalence_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override; 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 substitutePacExpectation(const string& name, expr_t subexpr) override;
expr_t substitutePacTargetNonstationary(const string& name, expr_t subexpr) override; expr_t substitutePacTargetNonstationary(const string& name, expr_t subexpr) override;
expr_t decreaseLeadsLagsPredeterminedVariables() const override; [[nodiscard]] expr_t decreaseLeadsLagsPredeterminedVariables() const override;
expr_t differentiateForwardVars(const vector<string> &subset, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override; expr_t differentiateForwardVars(const vector<string>& subset, subst_table_t& subst_table,
bool isNumConstNodeEqualTo(double value) const override; vector<BinaryOpNode*>& neweqs) const override;
int countDiffs() const override; [[nodiscard]] bool isNumConstNodeEqualTo(double value) const override;
bool isVariableNodeEqualTo(SymbolType type_arg, int variable_id, int lag_arg) const override; [[nodiscard]] int countDiffs() const override;
expr_t replaceTrendVar() 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 detrend(int symb_id, bool log_trend, expr_t trend) const override;
expr_t clone(DataTree& alt_datatree) const override; expr_t clone(DataTree& alt_datatree) const override;
expr_t removeTrendLeadLag(const map<int, expr_t> &trend_symbols_map) const override; [[nodiscard]] expr_t removeTrendLeadLag(const map<int, expr_t>& trend_symbols_map) const override;
bool isInStaticForm() const override; [[nodiscard]] bool isInStaticForm() const override;
expr_t replaceVarsInEquation(map<VariableNode*, NumConstNode*>& table) const override; expr_t replaceVarsInEquation(map<VariableNode*, NumConstNode*>& table) const override;
bool containsPacExpectation(const string &pac_model_name = "") const override; [[nodiscard]] bool containsPacExpectation(const string& pac_model_name = "") const override;
bool containsPacTargetNonstationary(const string &pac_model_name = "") const override; [[nodiscard]] bool containsPacTargetNonstationary(const string& pac_model_name
bool isParamTimesEndogExpr() const override; = "") const override;
expr_t substituteLogTransform(int orig_symb_id, int aux_symb_id) 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
...@@ -1380,14 +1677,20 @@ class AbstractExternalFunctionNode : public ExprNode ...@@ -1380,14 +1677,20 @@ class AbstractExternalFunctionNode : public ExprNode
public: public:
const int symb_id; const int symb_id;
const vector<expr_t> arguments; const vector<expr_t> arguments;
private: private:
expr_t computeDerivative(int deriv_id) override; 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; 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) // Computes the maximum of f applied to all arguments (result will always be non-negative)
int maxHelper(const function<int(expr_t)>& f) const; int maxHelper(const function<int(expr_t)>& f) const;
// Returns the node obtained by applying a transformation recursively on the arguments (in same datatree) // Returns the node obtained by applying a transformation recursively on the arguments (in same
// datatree)
template<typename Callable, typename... Args> template<typename Callable, typename... Args>
requires invocable<Callable, expr_t, Args...>
expr_t expr_t
recurseTransform(Callable&& op, Args&&... args) const recurseTransform(Callable&& op, Args&&... args) const
{ {
...@@ -1396,22 +1699,33 @@ private: ...@@ -1396,22 +1699,33 @@ private:
arguments_subst.push_back(invoke(forward<Callable>(op), argument, forward<Args>(args)...)); arguments_subst.push_back(invoke(forward<Callable>(op), argument, forward<Args>(args)...));
return buildSimilarExternalFunctionNode(arguments_subst, datatree); 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
{ {
}; };
void prepareForDerivation() override; 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 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, const 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, const deriv_node_temp_terms_t &tef_terms) const noexcept(false); [[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, const temporary_terms_idxs_t &temporary_terms_idxs, const deriv_node_temp_terms_t &tef_terms) const; void writeExternalFunctionArguments(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;
void writeJsonASTExternalFunctionArguments(ostream& output) 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 writeJsonExternalFunctionArguments(ostream& output, const temporary_terms_t& temporary_terms,
void writeBytecodeExternalFunctionArguments(BytecodeWriter &code_file, const deriv_node_temp_terms_t& tef_terms,
bool isdynamic) const;
void writeBytecodeExternalFunctionArguments(Bytecode::Writer& code_file,
ExprNodeBytecodeOutputType output_type, ExprNodeBytecodeOutputType output_type,
const temporary_terms_t& temporary_terms, const temporary_terms_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs, const temporary_terms_idxs_t& temporary_terms_idxs,
...@@ -1419,99 +1733,141 @@ protected: ...@@ -1419,99 +1733,141 @@ protected:
/*! Returns a predicate that tests whether an other ExprNode is an external /*! 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 function which is computed by the same external function call (i.e. it has
the same so-called "Tef" index) */ the same so-called "Tef" index) */
virtual function<bool (expr_t)> sameTefTermPredicate() const = 0; [[nodiscard]] virtual function<bool(expr_t)> sameTefTermPredicate() const = 0;
void computeSubExprContainingVariable(int symb_id, int lag, set<expr_t> &contain_var) const override; void computeSubExprContainingVariable(int symb_id, int lag,
set<expr_t>& contain_var) const override;
public: public:
AbstractExternalFunctionNode(DataTree& datatree_arg, int idx_arg, int symb_id_arg, AbstractExternalFunctionNode(DataTree& datatree_arg, int idx_arg, int symb_id_arg,
vector<expr_t> arguments_arg); vector<expr_t> arguments_arg);
[[nodiscard]] string getName() const;
void computeTemporaryTerms(const pair<int, int>& derivOrder, void computeTemporaryTerms(const pair<int, int>& derivOrder,
map<pair<int, int>, unordered_set<expr_t>>& temp_terms_map, map<pair<int, int>, unordered_set<expr_t>>& temp_terms_map,
unordered_map<expr_t, pair<int, pair<int, int>>>& reference_count, unordered_map<expr_t, pair<int, pair<int, int>>>& reference_count,
bool is_matlab) const override; bool is_matlab) const override;
void computeBlockTemporaryTerms(int blk, int eq, vector<vector<unordered_set<expr_t>>> &blocks_temporary_terms, 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; 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_idxs_t &temporary_terms_idxs, const deriv_node_temp_terms_t &tef_terms) const override = 0; 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
= 0;
void writeJsonAST(ostream& output) 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; void writeJsonOutput(ostream& output, const temporary_terms_t& temporary_terms,
bool containsExternalFunction() const override; 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, 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, const temporary_terms_idxs_t& temporary_terms_idxs,
deriv_node_temp_terms_t &tef_terms) const override = 0; deriv_node_temp_terms_t& tef_terms) const override
= 0;
void writeJsonExternalFunctionOutput(vector<string>& efout, 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,
bool isdynamic = true) const override = 0; bool isdynamic = true) const override
void writeBytecodeExternalFunctionOutput(BytecodeWriter &code_file, = 0;
void writeBytecodeExternalFunctionOutput(Bytecode::Writer& code_file,
ExprNodeBytecodeOutputType output_type, ExprNodeBytecodeOutputType output_type,
const temporary_terms_t& temporary_terms, const temporary_terms_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs, const temporary_terms_idxs_t& temporary_terms_idxs,
deriv_node_temp_terms_t &tef_terms) const override = 0; deriv_node_temp_terms_t& tef_terms) const override
= 0;
void collectVARLHSVariable(set<expr_t>& result) const override; void collectVARLHSVariable(set<expr_t>& result) const override;
void collectDynamicVariables(SymbolType type_arg, set<pair<int, int>>& result) const override; void collectDynamicVariables(SymbolType type_arg, set<pair<int, int>>& result) const override;
double eval(const eval_context_t &eval_context) const noexcept(false) override; [[nodiscard]] double eval(const eval_context_t& eval_context) const noexcept(false) override;
void writeBytecodeOutput(BytecodeWriter &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 = 0; 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
= 0;
expr_t toStatic(DataTree& static_datatree) const override; expr_t toStatic(DataTree& static_datatree) const override;
void computeXrefs(EquationInfo& ei) const override = 0; void computeXrefs(EquationInfo& ei) const override = 0;
BinaryOpNode* normalizeEquationHelper(const set<expr_t>& contain_var, expr_t rhs) const override; BinaryOpNode* normalizeEquationHelper(const set<expr_t>& contain_var, expr_t rhs) const override;
int maxEndoLead() const override; [[nodiscard]] int maxEndoLead() const override;
int maxExoLead() const override; [[nodiscard]] int maxExoLead() const override;
int maxEndoLag() const override; [[nodiscard]] int maxEndoLag() const override;
int maxExoLag() const override; [[nodiscard]] int maxExoLag() const override;
int maxLead() const override; [[nodiscard]] int maxLead() const override;
int maxLag() const override; [[nodiscard]] int maxLag() const override;
int maxLagWithDiffsExpanded() const override; [[nodiscard]] int maxLagWithDiffsExpanded() const override;
int VarMaxLag(const set<expr_t> &lhs_lag_equiv) const override; [[nodiscard]] int VarMaxLag(const set<expr_t>& lhs_lag_equiv) const override;
expr_t undiff() const override; [[nodiscard]] expr_t undiff() const override;
expr_t decreaseLeadsLags(int n) const override; [[nodiscard]] expr_t decreaseLeadsLags(int n) const override;
expr_t substituteEndoLeadGreaterThanTwo(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool deterministic_model) const override; expr_t substituteEndoLeadGreaterThanTwo(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
expr_t substituteEndoLagGreaterThanTwo(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override; bool deterministic_model) const override;
expr_t substituteExoLead(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; expr_t substituteExoLag(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs) const override;
expr_t substituteExpectation(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool partial_information_model) const override; expr_t substituteExpectation(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
expr_t substituteAdl() const override; bool partial_information_model) const override;
expr_t substituteModelLocalVariables() const override; [[nodiscard]] expr_t substituteAdl() const override;
expr_t substituteVarExpectation(const map<string, expr_t> &subst_table) const override; [[nodiscard]] expr_t substituteModelLocalVariables() const override;
[[nodiscard]] expr_t
substituteVarExpectation(const map<string, expr_t>& subst_table) const override;
void findDiffNodes(lag_equivalence_table_t& nodes) const override; void findDiffNodes(lag_equivalence_table_t& nodes) const override;
void findUnaryOpNodesForAuxVarCreation(lag_equivalence_table_t& nodes) const override; void findUnaryOpNodesForAuxVarCreation(lag_equivalence_table_t& nodes) const override;
optional<int> findTargetVariable(int lhs_symb_id) 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 substituteDiff(const lag_equivalence_table_t& nodes, subst_table_t& subst_table,
expr_t substituteUnaryOpNodes(const lag_equivalence_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override; 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 substitutePacExpectation(const string& name, expr_t subexpr) override;
expr_t substitutePacTargetNonstationary(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; virtual expr_t buildSimilarExternalFunctionNode(vector<expr_t>& alt_args,
expr_t decreaseLeadsLagsPredeterminedVariables() const override; DataTree& alt_datatree) const
expr_t differentiateForwardVars(const vector<string> &subset, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override; = 0;
bool isNumConstNodeEqualTo(double value) const override; [[nodiscard]] expr_t decreaseLeadsLagsPredeterminedVariables() const override;
int countDiffs() const override; expr_t differentiateForwardVars(const vector<string>& subset, subst_table_t& subst_table,
bool isVariableNodeEqualTo(SymbolType type_arg, int variable_id, int lag_arg) const override; vector<BinaryOpNode*>& neweqs) 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]] bool isNumConstNodeEqualTo(double value) const override;
expr_t replaceTrendVar() 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 detrend(int symb_id, bool log_trend, expr_t trend) const override;
expr_t clone(DataTree& alt_datatree) const override; expr_t clone(DataTree& alt_datatree) const override;
expr_t removeTrendLeadLag(const map<int, expr_t> &trend_symbols_map) const override; [[nodiscard]] expr_t removeTrendLeadLag(const map<int, expr_t>& trend_symbols_map) const override;
bool isInStaticForm() const override; [[nodiscard]] bool isInStaticForm() const override;
expr_t replaceVarsInEquation(map<VariableNode*, NumConstNode*>& table) const override; expr_t replaceVarsInEquation(map<VariableNode*, NumConstNode*>& table) const override;
bool containsPacExpectation(const string &pac_model_name = "") const override; [[nodiscard]] bool containsPacExpectation(const string& pac_model_name = "") const override;
bool containsPacTargetNonstationary(const string &pac_model_name = "") const override; [[nodiscard]] bool containsPacTargetNonstationary(const string& pac_model_name
bool isParamTimesEndogExpr() const override; = "") const override;
expr_t substituteLogTransform(int orig_symb_id, int aux_symb_id) 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 FirstDerivExternalFunctionNode;
friend class SecondDerivExternalFunctionNode; friend class SecondDerivExternalFunctionNode;
private: private:
expr_t composeDerivatives(const vector<expr_t>& dargs) override; expr_t composeDerivatives(const vector<expr_t>& dargs) override;
protected: protected:
function<bool (expr_t)> sameTefTermPredicate() const override; [[nodiscard]] function<bool(expr_t)> sameTefTermPredicate() const override;
public: public:
ExternalFunctionNode(DataTree& datatree_arg, int idx_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);
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; 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;
void writeJsonAST(ostream& output) 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 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, 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, const temporary_terms_idxs_t& temporary_terms_idxs,
...@@ -1520,33 +1876,42 @@ public: ...@@ -1520,33 +1876,42 @@ public:
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,
bool isdynamic) const override; bool isdynamic) const override;
void writeBytecodeExternalFunctionOutput(BytecodeWriter &code_file, void writeBytecodeExternalFunctionOutput(Bytecode::Writer& code_file,
ExprNodeBytecodeOutputType output_type, ExprNodeBytecodeOutputType output_type,
const temporary_terms_t& temporary_terms, const temporary_terms_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs, const temporary_terms_idxs_t& temporary_terms_idxs,
deriv_node_temp_terms_t& tef_terms) const override; deriv_node_temp_terms_t& tef_terms) const override;
void writeBytecodeOutput(BytecodeWriter &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 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 computeXrefs(EquationInfo& ei) const override; void computeXrefs(EquationInfo& ei) const override;
expr_t buildSimilarExternalFunctionNode(vector<expr_t> &alt_args, DataTree &alt_datatree) const override; expr_t buildSimilarExternalFunctionNode(vector<expr_t>& alt_args,
DataTree& alt_datatree) const override;
}; };
class FirstDerivExternalFunctionNode : public AbstractExternalFunctionNode class FirstDerivExternalFunctionNode : public AbstractExternalFunctionNode
{ {
public: public:
const int inputIndex; const int inputIndex;
private: private:
expr_t composeDerivatives(const vector<expr_t>& dargs) override; expr_t composeDerivatives(const vector<expr_t>& dargs) override;
protected: protected:
function<bool (expr_t)> sameTefTermPredicate() const override; [[nodiscard]] function<bool(expr_t)> sameTefTermPredicate() const override;
public: public:
FirstDerivExternalFunctionNode(DataTree &datatree_arg, int idx_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); const temporary_terms_t& temporary_terms,
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; const temporary_terms_idxs_t& temporary_terms_idxs,
const deriv_node_temp_terms_t& tef_terms) const override;
void writeJsonAST(ostream& output) 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 writeJsonOutput(ostream& output, const temporary_terms_t& temporary_terms,
void writeBytecodeOutput(BytecodeWriter &code_file, ExprNodeBytecodeOutputType output_type, 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_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs, const temporary_terms_idxs_t& temporary_terms_idxs,
const deriv_node_temp_terms_t& tef_terms) const override; const deriv_node_temp_terms_t& tef_terms) const override;
...@@ -1558,13 +1923,14 @@ public: ...@@ -1558,13 +1923,14 @@ public:
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,
bool isdynamic) const override; bool isdynamic) const override;
void writeBytecodeExternalFunctionOutput(BytecodeWriter &code_file, void writeBytecodeExternalFunctionOutput(Bytecode::Writer& code_file,
ExprNodeBytecodeOutputType output_type, ExprNodeBytecodeOutputType output_type,
const temporary_terms_t& temporary_terms, const temporary_terms_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs, const temporary_terms_idxs_t& temporary_terms_idxs,
deriv_node_temp_terms_t& tef_terms) const override; deriv_node_temp_terms_t& tef_terms) const override;
void computeXrefs(EquationInfo& ei) const override; void computeXrefs(EquationInfo& ei) const override;
expr_t buildSimilarExternalFunctionNode(vector<expr_t> &alt_args, DataTree &alt_datatree) const override; expr_t buildSimilarExternalFunctionNode(vector<expr_t>& alt_args,
DataTree& alt_datatree) const override;
}; };
class SecondDerivExternalFunctionNode : public AbstractExternalFunctionNode class SecondDerivExternalFunctionNode : public AbstractExternalFunctionNode
...@@ -1572,20 +1938,25 @@ class SecondDerivExternalFunctionNode : public AbstractExternalFunctionNode ...@@ -1572,20 +1938,25 @@ class SecondDerivExternalFunctionNode : public AbstractExternalFunctionNode
public: public:
const int inputIndex1; const int inputIndex1;
const int inputIndex2; const int inputIndex2;
private: private:
expr_t composeDerivatives(const vector<expr_t>& dargs) override; expr_t composeDerivatives(const vector<expr_t>& dargs) override;
protected: protected:
function<bool (expr_t)> sameTefTermPredicate() const override; [[nodiscard]] function<bool(expr_t)> sameTefTermPredicate() const override;
public: public:
SecondDerivExternalFunctionNode(DataTree &datatree_arg, int idx_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);
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; 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;
void writeJsonAST(ostream& output) 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 writeJsonOutput(ostream& output, const temporary_terms_t& temporary_terms,
void writeBytecodeOutput(BytecodeWriter &code_file, ExprNodeBytecodeOutputType output_type, 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_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs, const temporary_terms_idxs_t& temporary_terms_idxs,
const deriv_node_temp_terms_t& tef_terms) const override; const deriv_node_temp_terms_t& tef_terms) const override;
...@@ -1597,13 +1968,14 @@ public: ...@@ -1597,13 +1968,14 @@ public:
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,
bool isdynamic) const override; bool isdynamic) const override;
void writeBytecodeExternalFunctionOutput(BytecodeWriter &code_file, void writeBytecodeExternalFunctionOutput(Bytecode::Writer& code_file,
ExprNodeBytecodeOutputType output_type, ExprNodeBytecodeOutputType output_type,
const temporary_terms_t& temporary_terms, const temporary_terms_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs, const temporary_terms_idxs_t& temporary_terms_idxs,
deriv_node_temp_terms_t& tef_terms) const override; deriv_node_temp_terms_t& tef_terms) const override;
void computeXrefs(EquationInfo& ei) const override; void computeXrefs(EquationInfo& ei) const override;
expr_t buildSimilarExternalFunctionNode(vector<expr_t> &alt_args, DataTree &alt_datatree) const override; expr_t buildSimilarExternalFunctionNode(vector<expr_t>& alt_args,
DataTree& alt_datatree) const override;
}; };
/* Common superclass for nodes that have the following two characteristics: /* Common superclass for nodes that have the following two characteristics:
...@@ -1619,107 +1991,144 @@ public: ...@@ -1619,107 +1991,144 @@ public:
map<pair<int, int>, unordered_set<expr_t>>& temp_terms_map, map<pair<int, int>, unordered_set<expr_t>>& temp_terms_map,
unordered_map<expr_t, pair<int, pair<int, int>>>& reference_count, unordered_map<expr_t, pair<int, pair<int, int>>>& reference_count,
bool is_matlab) const override; bool is_matlab) const override;
void computeBlockTemporaryTerms(int blk, int eq, vector<vector<unordered_set<expr_t>>> &blocks_temporary_terms, 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; unordered_map<expr_t, tuple<int, int, int>>& reference_count) const override;
expr_t toStatic(DataTree& static_datatree) const override; expr_t toStatic(DataTree& static_datatree) const override;
expr_t computeDerivative(int deriv_id) override; expr_t computeDerivative(int deriv_id) override;
int maxEndoLead() const override; [[nodiscard]] int maxEndoLead() const override;
int maxExoLead() const override; [[nodiscard]] int maxExoLead() const override;
int maxEndoLag() const override; [[nodiscard]] int maxEndoLag() const override;
int maxExoLag() const override; [[nodiscard]] int maxExoLag() const override;
int maxLead() const override; [[nodiscard]] int maxLead() const override;
int maxLag() const override; [[nodiscard]] int maxLag() const override;
int VarMaxLag(const set<expr_t> &lhs_lag_equiv) const override; [[nodiscard]] int VarMaxLag(const set<expr_t>& lhs_lag_equiv) const override;
expr_t undiff() const override; [[nodiscard]] expr_t undiff() const override;
expr_t decreaseLeadsLags(int n) const override; [[nodiscard]] expr_t decreaseLeadsLags(int n) const override;
int countDiffs() 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 substituteEndoLeadGreaterThanTwo(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
expr_t substituteEndoLagGreaterThanTwo(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override; bool deterministic_model) const override;
expr_t substituteExoLead(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; expr_t substituteExoLag(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs) const override;
bool containsExternalFunction() const override; [[nodiscard]] bool containsExternalFunction() const override;
double eval(const eval_context_t &eval_context) const noexcept(false) override; [[nodiscard]] double eval(const eval_context_t& eval_context) const noexcept(false) override;
void computeXrefs(EquationInfo& ei) const override; void computeXrefs(EquationInfo& ei) const override;
expr_t substituteExpectation(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool partial_information_model) const override; expr_t substituteExpectation(subst_table_t& subst_table, vector<BinaryOpNode*>& neweqs,
expr_t substituteAdl() const override; bool partial_information_model) const override;
expr_t substituteModelLocalVariables() const override; [[nodiscard]] expr_t substituteAdl() const override;
[[nodiscard]] expr_t substituteModelLocalVariables() const override;
void findDiffNodes(lag_equivalence_table_t& nodes) const override; void findDiffNodes(lag_equivalence_table_t& nodes) const override;
void findUnaryOpNodesForAuxVarCreation(lag_equivalence_table_t& nodes) const override; void findUnaryOpNodesForAuxVarCreation(lag_equivalence_table_t& nodes) const override;
optional<int> findTargetVariable(int lhs_symb_id) 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 substituteDiff(const lag_equivalence_table_t& nodes, subst_table_t& subst_table,
expr_t substituteUnaryOpNodes(const lag_equivalence_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override; 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; BinaryOpNode* normalizeEquationHelper(const set<expr_t>& contain_var, expr_t rhs) const override;
void writeBytecodeOutput(BytecodeWriter &code_file, ExprNodeBytecodeOutputType output_type, void writeBytecodeOutput(Bytecode::Writer& code_file, ExprNodeBytecodeOutputType output_type,
const temporary_terms_t& temporary_terms, const temporary_terms_t& temporary_terms,
const temporary_terms_idxs_t& temporary_terms_idxs, const temporary_terms_idxs_t& temporary_terms_idxs,
const deriv_node_temp_terms_t& tef_terms) const override; const deriv_node_temp_terms_t& tef_terms) const override;
void collectVARLHSVariable(set<expr_t>& result) const override; void collectVARLHSVariable(set<expr_t>& result) const override;
void collectDynamicVariables(SymbolType type_arg, set<pair<int, int>>& result) const override; void collectDynamicVariables(SymbolType type_arg, set<pair<int, int>>& result) const override;
bool isNumConstNodeEqualTo(double value) const override; [[nodiscard]] bool isNumConstNodeEqualTo(double value) const override;
bool isVariableNodeEqualTo(SymbolType type_arg, int variable_id, int lag_arg) const override; [[nodiscard]] bool isVariableNodeEqualTo(SymbolType type_arg, int variable_id,
bool isInStaticForm() const override; int lag_arg) const override;
[[nodiscard]] bool isInStaticForm() const override;
expr_t replaceVarsInEquation(map<VariableNode*, NumConstNode*>& table) const override; expr_t replaceVarsInEquation(map<VariableNode*, NumConstNode*>& table) const override;
bool isParamTimesEndogExpr() const override; [[nodiscard]] bool isParamTimesEndogExpr() const override;
expr_t differentiateForwardVars(const vector<string> &subset, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override; expr_t differentiateForwardVars(const vector<string>& subset, subst_table_t& subst_table,
expr_t decreaseLeadsLagsPredeterminedVariables() const override; vector<BinaryOpNode*>& neweqs) const override;
expr_t replaceTrendVar() 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; expr_t detrend(int symb_id, bool log_trend, expr_t trend) const override;
expr_t removeTrendLeadLag(const map<int, expr_t> &trend_symbols_map) const override; [[nodiscard]] expr_t removeTrendLeadLag(const map<int, expr_t>& trend_symbols_map) const override;
expr_t substituteLogTransform(int orig_symb_id, int aux_symb_id) 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: protected:
void prepareForDerivation() override; 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 prepareForChainRuleDerivation(
void computeSubExprContainingVariable(int symb_id, int lag, set<expr_t> &contain_var) const override; 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: 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; 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 class VarExpectationNode : public SubModelNode
{ {
public: public:
VarExpectationNode(DataTree& datatree_arg, int idx_arg, string model_name_arg); 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; 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; expr_t clone(DataTree& alt_datatree) const override;
int maxLagWithDiffsExpanded() const override; [[nodiscard]] int maxLagWithDiffsExpanded() const override;
expr_t substituteVarExpectation(const map<string, expr_t> &subst_table) 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 substitutePacExpectation(const string& name, expr_t subexpr) override;
expr_t substitutePacTargetNonstationary(const string& name, expr_t subexpr) override; expr_t substitutePacTargetNonstationary(const string& name, expr_t subexpr) override;
bool containsPacExpectation(const string &pac_model_name = "") const override; [[nodiscard]] bool containsPacExpectation(const string& pac_model_name = "") const override;
bool containsPacTargetNonstationary(const string &pac_model_name = "") const override; [[nodiscard]] bool containsPacTargetNonstationary(const string& pac_model_name
= "") const override;
void writeJsonAST(ostream& output) 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 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 class PacExpectationNode : public SubModelNode
{ {
public: public:
PacExpectationNode(DataTree& datatree_arg, int idx_arg, string model_name); 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; 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; expr_t clone(DataTree& alt_datatree) const override;
int maxLagWithDiffsExpanded() const override; [[nodiscard]] int maxLagWithDiffsExpanded() const override;
expr_t substituteVarExpectation(const map<string, expr_t> &subst_table) 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 substitutePacExpectation(const string& name, expr_t subexpr) override;
expr_t substitutePacTargetNonstationary(const string& name, expr_t subexpr) override; expr_t substitutePacTargetNonstationary(const string& name, expr_t subexpr) override;
bool containsPacExpectation(const string &pac_model_name = "") const override; [[nodiscard]] bool containsPacExpectation(const string& pac_model_name = "") const override;
bool containsPacTargetNonstationary(const string &pac_model_name = "") const override; [[nodiscard]] bool containsPacTargetNonstationary(const string& pac_model_name
= "") const override;
void writeJsonAST(ostream& output) 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 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 class PacTargetNonstationaryNode : public SubModelNode
{ {
public: public:
PacTargetNonstationaryNode(DataTree& datatree_arg, int idx_arg, string model_name); 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; 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; expr_t clone(DataTree& alt_datatree) const override;
int maxLagWithDiffsExpanded() const override; [[nodiscard]] int maxLagWithDiffsExpanded() const override;
expr_t substituteVarExpectation(const map<string, expr_t> &subst_table) 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 substitutePacExpectation(const string& name, expr_t subexpr) override;
expr_t substitutePacTargetNonstationary(const string& name, expr_t subexpr) override; expr_t substitutePacTargetNonstationary(const string& name, expr_t subexpr) override;
bool containsPacExpectation(const string &pac_model_name = "") const override; [[nodiscard]] bool containsPacExpectation(const string& pac_model_name = "") const override;
bool containsPacTargetNonstationary(const string &pac_model_name = "") const override; [[nodiscard]] bool containsPacTargetNonstationary(const string& pac_model_name
= "") const override;
void writeJsonAST(ostream& output) 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 writeJsonOutput(ostream& output, const temporary_terms_t& temporary_terms,
const deriv_node_temp_terms_t& tef_terms, bool isdynamic) const override;
}; };
#endif #endif
/* /*
* Copyright © 2014-2021 Dynare Team * Copyright © 2014-2023 Dynare Team
* *
* This file is part of Dynare. * This file is part of Dynare.
* *
...@@ -17,13 +17,14 @@ ...@@ -17,13 +17,14 @@
* along with Dynare. If not, see <https://www.gnu.org/licenses/>. * along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/ */
#ifndef _EXTENDED_PREPROCESSOR_TYPES_HH #ifndef EXTENDED_PREPROCESSOR_TYPES_HH
#define _EXTENDED_PREPROCESSOR_TYPES_HH #define EXTENDED_PREPROCESSOR_TYPES_HH
// Values for the “output” option // Values for the “output” option
enum class OutputType enum class OutputType
{ {
standard, // Default value, infer the derivation order from .mod file only standard, // Default value, infer the derivation order from .mod file only
first, // Output only 1st dynamic derivatives with no other computations
second, // Output at least 2nd dynamic derivatives second, // Output at least 2nd dynamic derivatives
third, // Output at least 3rd dynamic derivatives third, // Output at least 3rd dynamic derivatives
}; };
......
...@@ -17,16 +17,17 @@ ...@@ -17,16 +17,17 @@
* along with Dynare. If not, see <https://www.gnu.org/licenses/>. * along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include <cstdlib>
#include <cassert> #include <cassert>
#include <cmath> #include <cmath>
#include <cstdlib>
#include <iostream> #include <iostream>
#include "ExternalFunctionsTable.hh" #include "ExternalFunctionsTable.hh"
#include "SymbolTable.hh" #include "SymbolTable.hh"
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);
...@@ -63,11 +64,14 @@ ExternalFunctionsTable::addExternalFunction(int symb_id, const external_function ...@@ -63,11 +64,14 @@ ExternalFunctionsTable::addExternalFunction(int symb_id, const external_function
if (external_function_options_chng.secondDerivSymbID != IDNotSet if (external_function_options_chng.secondDerivSymbID != IDNotSet
&& external_function_options_chng.firstDerivSymbID == IDNotSet) && 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 != IDNotSet) && external_function_options_chng.firstDerivSymbID != IDNotSet)
{ {
...@@ -80,29 +84,40 @@ ExternalFunctionsTable::addExternalFunction(int symb_id, const external_function ...@@ -80,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) == IDNotSet) // 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 © 2010-2022 Dynare Team * Copyright © 2010-2023 Dynare Team
* *
* This file is part of Dynare. * This file is part of Dynare.
* *
...@@ -17,14 +17,14 @@ ...@@ -17,14 +17,14 @@
* along with Dynare. If not, see <https://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
#include <algorithm>
#include <iostream> #include <iostream>
#include <map>
#include <string> #include <string>
#include <vector> #include <vector>
#include <map>
#include <algorithm>
using namespace std; using namespace std;
...@@ -55,20 +55,24 @@ public: ...@@ -55,20 +55,24 @@ public:
constexpr static int IDSetButNoNameProvided = -2; constexpr static int IDSetButNoNameProvided = -2;
//! Default number of arguments when nargs is not specified //! Default number of arguments when nargs is not specified
constexpr static int defaultNargs = 1; 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:
//! 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 noexcept(false); [[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 noexcept(false); [[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 noexcept(false); [[nodiscard]] inline int getSecondDerivSymbID(int symb_id) const noexcept(false);
}; };
inline bool inline bool
...@@ -80,8 +84,7 @@ ExternalFunctionsTable::exists(int symb_id) const ...@@ -80,8 +84,7 @@ ExternalFunctionsTable::exists(int symb_id) const
inline int inline int
ExternalFunctionsTable::getNargs(int symb_id) const noexcept(false) ExternalFunctionsTable::getNargs(int symb_id) const noexcept(false)
{ {
if (auto it = externalFunctionTable.find(symb_id); if (auto it = externalFunctionTable.find(symb_id); it != externalFunctionTable.end())
it != externalFunctionTable.end())
return it->second.nargs; return it->second.nargs;
else else
throw UnknownExternalFunctionSymbolIDException {symb_id}; throw UnknownExternalFunctionSymbolIDException {symb_id};
...@@ -90,8 +93,7 @@ ExternalFunctionsTable::getNargs(int symb_id) const noexcept(false) ...@@ -90,8 +93,7 @@ ExternalFunctionsTable::getNargs(int symb_id) const noexcept(false)
inline int inline int
ExternalFunctionsTable::getFirstDerivSymbID(int symb_id) const noexcept(false) ExternalFunctionsTable::getFirstDerivSymbID(int symb_id) const noexcept(false)
{ {
if (auto it = externalFunctionTable.find(symb_id); if (auto it = externalFunctionTable.find(symb_id); it != externalFunctionTable.end())
it != externalFunctionTable.end())
return it->second.firstDerivSymbID; return it->second.firstDerivSymbID;
else else
throw UnknownExternalFunctionSymbolIDException {symb_id}; throw UnknownExternalFunctionSymbolIDException {symb_id};
...@@ -100,8 +102,7 @@ ExternalFunctionsTable::getFirstDerivSymbID(int symb_id) const noexcept(false) ...@@ -100,8 +102,7 @@ ExternalFunctionsTable::getFirstDerivSymbID(int symb_id) const noexcept(false)
inline int inline int
ExternalFunctionsTable::getSecondDerivSymbID(int symb_id) const noexcept(false) ExternalFunctionsTable::getSecondDerivSymbID(int symb_id) const noexcept(false)
{ {
if (auto it = externalFunctionTable.find(symb_id); if (auto it = externalFunctionTable.find(symb_id); it != externalFunctionTable.end())
it != externalFunctionTable.end())
return it->second.secondDerivSymbID; return it->second.secondDerivSymbID;
else else
throw UnknownExternalFunctionSymbolIDException {symb_id}; throw UnknownExternalFunctionSymbolIDException {symb_id};
......
/*
* 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 * Copyright © 2015-2025 Dynare Team
* *
* This file is part of Dynare. * This file is part of Dynare.
* *
...@@ -17,32 +17,31 @@ ...@@ -17,32 +17,31 @@
* along with Dynare. If not, see <https://www.gnu.org/licenses/>. * along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include <algorithm>
#include <filesystem>
#include <fstream>
#include <regex> #include <regex>
#include <sstream> #include <sstream>
#include <fstream>
#include <filesystem>
#include <algorithm>
#include "macro/Driver.hh" #include "macro/Driver.hh"
stringstream stringstream
macroExpandModFile(const filesystem::path &filename, const istream &modfile, macroExpandModFile(const filesystem::path& filename, const istream& modfile, bool debug,
bool debug, bool save_macro, filesystem::path save_macro_file, bool line_macro, bool save_macro, filesystem::path save_macro_file, bool line_macro,
const vector<pair<string, string>> &defines, const vector<pair<string, string>>& defines, vector<filesystem::path> paths)
vector<filesystem::path> paths)
{ {
// Do macro processing // Do macro processing
stringstream macro_output; stringstream macro_output;
macro::Environment env = macro::Environment(); macro::Environment env = macro::Environment();
macro::Driver m; macro::Driver m;
/* Calling `string()` method on filename because of bug in GCC/MinGW 10.2 /* Calling string() method on filename: not necessary on GNU/Linux and macOS because there is an
(shipped in Debian “Bullseye” 11), that fails to accept implicit implicit conversion from filesystem:path to string (i.e. basic_string<char>), but needed on
conversion to string from filename::path. */ 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); m.parse(filename.string(), modfile, debug, defines, env, paths, macro_output);
if (save_macro) if (save_macro)
{ {
if (save_macro_file.empty()) if (save_macro_file.empty())
save_macro_file = filename.stem().string() + "-macroexp.mod"; save_macro_file = filename.stem().string() + "_macroexp.mod";
ofstream macro_output_file {save_macro_file}; ofstream macro_output_file {save_macro_file};
if (macro_output_file.fail()) if (macro_output_file.fail())
{ {
...@@ -53,15 +52,8 @@ macroExpandModFile(const filesystem::path &filename, const istream &modfile, ...@@ -53,15 +52,8 @@ macroExpandModFile(const filesystem::path &filename, const istream &modfile,
string str(macro_output.str()); string str(macro_output.str());
if (!line_macro) if (!line_macro)
{ {
/* Remove the @#line directives. // Remove the @#line directives.
Unfortunately GCC 11 does not yet support std::regex::multiline str = regex_replace(str, regex(R"(^@#line.*$)", 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 /* Remove the EOLs at the beginning of the output, the first one
being a remnant of the first @#line directive. */ being a remnant of the first @#line directive. */
str = regex_replace(str, regex(R"(^(\r?\n)+)"), ""); str = regex_replace(str, regex(R"(^(\r?\n)+)"), "");
......
/* /*
* Copyright © 2006-2023 Dynare Team * Copyright © 2006-2025 Dynare Team
* *
* This file is part of Dynare. * This file is part of Dynare.
* *
...@@ -17,40 +17,62 @@ ...@@ -17,40 +17,62 @@
* along with Dynare. If not, see <https://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 <typeinfo> #include <iostream>
#include <cassert>
#include <random> #include <random>
#include <typeinfo>
#include <filesystem> #include <filesystem>
#include "ModFile.hh"
#include "ConfigFile.hh"
#include "ComputingTasks.hh" #include "ComputingTasks.hh"
#include "ModFile.hh"
#include "Shocks.hh" #include "Shocks.hh"
ModFile::ModFile(WarningConsolidation &warnings_arg) ModFile::ModFile(WarningConsolidation& warnings_arg) :
: var_model_table{symbol_table}, symbol_table {heterogeneity_table},
var_model_table {symbol_table},
trend_component_model_table {symbol_table}, trend_component_model_table {symbol_table},
var_expectation_model_table {symbol_table}, var_expectation_model_table {symbol_table},
pac_model_table {symbol_table}, pac_model_table {symbol_table},
expressions_tree{symbol_table, num_constants, external_functions_table}, expressions_tree {symbol_table, num_constants, external_functions_table, heterogeneity_table},
original_model{symbol_table, num_constants, external_functions_table, original_model {symbol_table,
trend_component_model_table, var_model_table}, num_constants,
dynamic_model{symbol_table, num_constants, external_functions_table, external_functions_table,
trend_component_model_table, var_model_table}, heterogeneity_table,
trend_dynamic_model{symbol_table, num_constants, external_functions_table, trend_component_model_table,
trend_component_model_table, var_model_table}, var_model_table},
orig_ramsey_dynamic_model{symbol_table, num_constants, external_functions_table, dynamic_model {symbol_table,
trend_component_model_table, var_model_table}, num_constants,
epilogue{symbol_table, num_constants, external_functions_table, external_functions_table,
trend_component_model_table, var_model_table}, heterogeneity_table,
static_model{symbol_table, num_constants, external_functions_table}, trend_component_model_table,
steady_state_model{symbol_table, num_constants, external_functions_table, static_model}, 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} warnings {warnings_arg}
{ {
heterogeneity_table.setSymbolTable(&symbol_table);
} }
void void
...@@ -77,13 +99,14 @@ ModFile::evalAllExpressions(bool warn_uninit) ...@@ -77,13 +99,14 @@ ModFile::evalAllExpressions(bool warn_uninit)
// 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); if (auto type = symbol_table.getType(id);
(type == SymbolType::endogenous || type == SymbolType::exogenous || type == SymbolType::exogenousDet (type == SymbolType::endogenous || type == SymbolType::exogenous
|| type == SymbolType::parameter || type == SymbolType::modelLocalVariable) || type == SymbolType::exogenousDet || type == SymbolType::parameter
|| type == SymbolType::modelLocalVariable)
&& !global_eval_context.contains(id)) && !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;
} }
} }
...@@ -117,7 +140,9 @@ ModFile::checkPass(bool nostrict, bool stochastic) ...@@ -117,7 +140,9 @@ ModFile::checkPass(bool nostrict, bool stochastic)
if (mod_file_struct.write_latex_steady_state_model_present if (mod_file_struct.write_latex_steady_state_model_present
&& !mod_file_struct.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);
} }
...@@ -129,20 +154,16 @@ ModFile::checkPass(bool nostrict, bool stochastic) ...@@ -129,20 +154,16 @@ 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.discretionary_policy_present || mod_file_struct.calib_smoother_present || mod_file_struct.identification_present
|| mod_file_struct.calib_smoother_present || mod_file_struct.mom_estimation_present || mod_file_struct.sensitivity_present
|| mod_file_struct.identification_present
|| mod_file_struct.mom_estimation_present
|| mod_file_struct.sensitivity_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 || mod_file_struct.perfect_foresight_with_expectation_errors_solver_present
|| stochastic_statement_present)) || stochastic_statement_present))
{ {
...@@ -152,7 +173,9 @@ ModFile::checkPass(bool nostrict, bool stochastic) ...@@ -152,7 +173,9 @@ ModFile::checkPass(bool nostrict, bool stochastic)
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)
{ {
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);
} }
...@@ -161,28 +184,40 @@ ModFile::checkPass(bool nostrict, bool stochastic) ...@@ -161,28 +184,40 @@ 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 "
"ramsey_policy or a discretionary_policy statement and vice versa."
<< endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (mod_file_struct.ramsey_constraints_present && !mod_file_struct.ramsey_model_present) if (!ramsey_constraints.empty() && !mod_file_struct.ramsey_model_present)
{ {
cerr << "ERROR: A ramsey_constraints block requires the presence of a ramsey_model or ramsey_policy statement" << 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.osr_present && (!mod_file_struct.osr_params_present || !mod_file_struct.optim_weights_present)) if ((mod_file_struct.osr_present
|| ((!mod_file_struct.osr_present || !mod_file_struct.osr_params_present) && mod_file_struct.optim_weights_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)) || ((!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; 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 || mod_file_struct.perfect_foresight_with_expectation_errors_solver_present) if ((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: 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; 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);
} }
...@@ -192,15 +227,20 @@ ModFile::checkPass(bool nostrict, bool stochastic) ...@@ -192,15 +227,20 @@ ModFile::checkPass(bool nostrict, bool stochastic)
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 && !mod_file_struct.dsge_prior_weight_in_estimated_params) if (mod_file_struct.dsge_var_estimated && !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);
} }
...@@ -219,19 +259,23 @@ ModFile::checkPass(bool nostrict, bool stochastic) ...@@ -219,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);
...@@ -242,28 +286,40 @@ ModFile::checkPass(bool nostrict, bool stochastic) ...@@ -242,28 +286,40 @@ 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: 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);
} }
...@@ -278,7 +334,9 @@ ModFile::checkPass(bool nostrict, bool stochastic) ...@@ -278,7 +334,9 @@ ModFile::checkPass(bool nostrict, bool stochastic)
|| dynamic_model.isBinaryOpUsed(BinaryOpcode::lessEqual) || dynamic_model.isBinaryOpUsed(BinaryOpcode::lessEqual)
|| dynamic_model.isBinaryOpUsed(BinaryOpcode::equalEqual) || dynamic_model.isBinaryOpUsed(BinaryOpcode::equalEqual)
|| dynamic_model.isBinaryOpUsed(BinaryOpcode::different))) || dynamic_model.isBinaryOpUsed(BinaryOpcode::different)))
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; 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.isUnaryOpUsedOnType(SymbolType::endogenous, UnaryOpcode::sign) && (dynamic_model.isUnaryOpUsedOnType(SymbolType::endogenous, UnaryOpcode::sign)
...@@ -298,8 +356,7 @@ ModFile::checkPass(bool nostrict, bool stochastic) ...@@ -298,8 +356,7 @@ ModFile::checkPass(bool nostrict, bool stochastic)
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (linear if (linear && !mod_file_struct.perfect_foresight_solver_present
&& !mod_file_struct.perfect_foresight_solver_present
&& !mod_file_struct.perfect_foresight_with_expectation_errors_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::sign)
|| dynamic_model.isUnaryOpUsedOnType(SymbolType::exogenous, UnaryOpcode::abs) || dynamic_model.isUnaryOpUsedOnType(SymbolType::exogenous, UnaryOpcode::abs)
...@@ -321,30 +378,28 @@ ModFile::checkPass(bool nostrict, bool stochastic) ...@@ -321,30 +378,28 @@ ModFile::checkPass(bool nostrict, bool stochastic)
// 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 (bool printed_something{false}; for (bool printed_something {false}; int symb_id : parameters_intersect)
int symb_id : parameters_intersect)
{ {
if (exchange(printed_something, true)) if (exchange(printed_something, true))
cerr << ", "; cerr << ", ";
cerr << symbol_table.getName(symb_id); 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> unusedExo0 = dynamic_model.findUnusedExogenous(); set<int> unusedExo0 = dynamic_model.findUnusedExogenous();
set<int> unusedExo; set<int> unusedExo;
set_difference(unusedExo0.begin(), unusedExo0.end(), ranges::set_difference(unusedExo0, mod_file_struct.pac_params,
mod_file_struct.pac_params.begin(), mod_file_struct.pac_params.end(),
inserter(unusedExo, unusedExo.begin())); inserter(unusedExo, unusedExo.begin()));
if (unusedExo.size() > 0) if (unusedExo.size() > 0)
{ {
...@@ -357,7 +412,124 @@ ModFile::checkPass(bool nostrict, bool stochastic) ...@@ -357,7 +412,124 @@ ModFile::checkPass(bool nostrict, bool stochastic)
<< "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);
} }
} }
...@@ -401,7 +573,8 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, bool ...@@ -401,7 +573,8 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, bool
<< "' 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) else if (unusedEndogsIsErr)
cerr << "Error: " << symbol_table.getName(unusedEndog) << " not used in the model block"<< endl; cerr << "Error: " << symbol_table.getName(unusedEndog) << " not used in the model block"
<< endl;
if (unusedEndogsIsErr) if (unusedEndogsIsErr)
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
...@@ -427,13 +600,16 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, bool ...@@ -427,13 +600,16 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, bool
lag_equivalence_table_t unary_ops_nodes; lag_equivalence_table_t unary_ops_nodes;
ExprNode::subst_table_t unary_ops_subst_table; ExprNode::subst_table_t unary_ops_subst_table;
if (transform_unary_ops) if (transform_unary_ops)
tie(unary_ops_nodes, unary_ops_subst_table) = dynamic_model.substituteUnaryOps(var_expectation_model_table, pac_model_table); tie(unary_ops_nodes, unary_ops_subst_table)
= dynamic_model.substituteUnaryOps(var_expectation_model_table, pac_model_table);
else else
// substitute only those unary ops that appear in VAR, TCM and PAC model equations // 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); 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 // Create auxiliary variable and equations for Diff operators
auto [diff_nodes, diff_subst_table] = dynamic_model.substituteDiff(var_expectation_model_table, pac_model_table); auto [diff_nodes, diff_subst_table]
= dynamic_model.substituteDiff(var_expectation_model_table, pac_model_table);
// Fill trend component and VAR model tables // Fill trend component and VAR model tables
dynamic_model.fillTrendComponentModelTable(); dynamic_model.fillTrendComponentModelTable();
...@@ -447,9 +623,8 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, bool ...@@ -447,9 +623,8 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, bool
trend_component_model_table); trend_component_model_table);
// PAC model // PAC model
pac_model_table.transformPass(unary_ops_nodes, unary_ops_subst_table, pac_model_table.transformPass(unary_ops_nodes, unary_ops_subst_table, diff_nodes,
diff_nodes, diff_subst_table, diff_subst_table, dynamic_model, var_model_table,
dynamic_model, var_model_table,
trend_component_model_table); trend_component_model_table);
// Create auxiliary vars for Expectation operator // Create auxiliary vars for Expectation operator
...@@ -491,9 +666,22 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, bool ...@@ -491,9 +666,22 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, bool
*/ */
if (linear) if (linear)
orig_ramsey_dynamic_model = dynamic_model; orig_ramsey_dynamic_model = dynamic_model;
DynamicModel ramsey_FOC_equations_dynamic_model {symbol_table, num_constants, external_functions_table, trend_component_model_table, var_model_table}; DynamicModel ramsey_FOC_equations_dynamic_model {symbol_table,
num_constants,
external_functions_table,
heterogeneity_table,
trend_component_model_table,
var_model_table};
ramsey_FOC_equations_dynamic_model = dynamic_model; ramsey_FOC_equations_dynamic_model = dynamic_model;
mod_file_struct.ramsey_orig_endo_nbr = ramsey_FOC_equations_dynamic_model.computeRamseyPolicyFOCs(planner_objective); 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);
} }
...@@ -502,18 +690,18 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, bool ...@@ -502,18 +690,18 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, bool
// Must come after detrending of variables and Ramsey policy transformation // Must come after detrending of variables and Ramsey policy transformation
dynamic_model.substituteLogTransform(); dynamic_model.substituteLogTransform();
if (!heterogeneity_table.empty())
dynamic_model.substituteAggregationOperators();
/* Create auxiliary vars for leads and lags greater than 2, on both endos and /* 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 exos. The transformation is not exactly the same on stochastic and
deterministic models, because there is no need to take into account the deterministic models, because there is no need to take into account the
Jensen inequality on the latter. */ Jensen inequality on the latter. */
bool deterministic_model = !(mod_file_struct.stoch_simul_present bool deterministic_model
|| 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.discretionary_policy_present || mod_file_struct.calib_smoother_present || mod_file_struct.identification_present
|| mod_file_struct.calib_smoother_present || mod_file_struct.mom_estimation_present || mod_file_struct.sensitivity_present
|| mod_file_struct.identification_present
|| mod_file_struct.mom_estimation_present
|| mod_file_struct.sensitivity_present
|| stochastic); || stochastic);
dynamic_model.substituteEndoLeadGreaterThanTwo(deterministic_model); dynamic_model.substituteEndoLeadGreaterThanTwo(deterministic_model);
dynamic_model.substituteExoLead(deterministic_model); dynamic_model.substituteExoLead(deterministic_model);
...@@ -530,8 +718,8 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, bool ...@@ -530,8 +718,8 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, bool
{ {
int sid = symbol_table.addSymbol("dsge_prior_weight", SymbolType::parameter); 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(make_unique<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)
...@@ -543,6 +731,10 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, bool ...@@ -543,6 +731,10 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, bool
dynamic_model.reorderAuxiliaryEquations(); 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();
...@@ -559,54 +751,72 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, bool ...@@ -559,54 +751,72 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, bool
&& !(mod_file_struct.bvar_present && dynamic_model.equation_number() == 0) && !(mod_file_struct.bvar_present && dynamic_model.equation_number() == 0)
&& (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 if (symbol_table.exo_det_nbr() > 0
&& (mod_file_struct.perfect_foresight_solver_present || mod_file_struct.perfect_foresight_with_expectation_errors_solver_present)) && (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, perfect_foresight_with_expectation_errors_solver} 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_model_present && symbol_table.exo_det_nbr() > 0) if (mod_file_struct.ramsey_model_present && symbol_table.exo_det_nbr() > 0)
{ {
cerr << "ERROR: ramsey_model and ramsey_policy are 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.identification_present && symbol_table.exo_det_nbr() > 0) if (mod_file_struct.identification_present && symbol_table.exo_det_nbr() > 0)
{ {
cerr << "ERROR: identification is incompatible with deterministic exogenous variables" << endl; cerr << "ERROR: identification is incompatible with deterministic exogenous variables"
<< endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (mod_file_struct.occbin_constraints_present if (mod_file_struct.occbin_constraints_present
&& (mod_file_struct.osr_present || mod_file_struct.mom_estimation_present && (mod_file_struct.osr_present || mod_file_struct.mom_estimation_present
|| mod_file_struct.ramsey_model_present || mod_file_struct.ramsey_model_present || mod_file_struct.discretionary_policy_present
|| mod_file_struct.discretionary_policy_present || mod_file_struct.extended_path_present || mod_file_struct.extended_path_present || mod_file_struct.identification_present
|| mod_file_struct.identification_present || mod_file_struct.sensitivity_present)) || mod_file_struct.sensitivity_present))
{ {
cerr << "ERROR: the 'occbin_constraints' block is not compatible with commands other than 'estimation', 'stoch_simul', and 'calib_smoother'." << endl; cerr << "ERROR: the 'occbin_constraints' block is not compatible with commands other than "
"'estimation', 'stoch_simul', and 'calib_smoother'."
<< endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (mod_file_struct.shocks_surprise_present && !mod_file_struct.occbin_constraints_present) 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; cerr << "ERROR: the 'shocks(surprise)' block can only be used in conjunction with the "
"'occbin_constraints' block."
<< endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (mod_file_struct.shocks_learnt_in_present && !mod_file_struct.perfect_foresight_with_expectation_errors_solver_present) 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; 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); exit(EXIT_FAILURE);
} }
if (mod_file_struct.endval_learnt_in_present && !mod_file_struct.perfect_foresight_with_expectation_errors_solver_present) 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; 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);
} }
...@@ -615,7 +825,8 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, bool ...@@ -615,7 +825,8 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, bool
else else
{ {
cout << "Found " << mod_file_struct.ramsey_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"))
...@@ -624,8 +835,10 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, bool ...@@ -624,8 +835,10 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, bool
{ {
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);
} }
} }
...@@ -636,6 +849,16 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, bool ...@@ -636,6 +849,16 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, bool
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
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
...@@ -651,24 +874,28 @@ ModFile::computingPass(bool no_tmp_terms, OutputType output, int params_derivs_o ...@@ -651,24 +874,28 @@ ModFile::computingPass(bool no_tmp_terms, OutputType output, int params_derivs_o
static_model = static_cast<StaticModel>(dynamic_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) || mod_file_struct.mom_estimation_present)
static_model.set_cutoff_to_zero(); static_model.set_cutoff_to_zero();
int derivsOrder = 1; int derivsOrder = 1;
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; derivsOrder = 2;
if (mod_file_struct.identification_present if (mod_file_struct.identification_present
|| mod_file_struct.estimation_analytic_derivation || mod_file_struct.estimation_analytic_derivation
|| mod_file_struct.osr_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))) || (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(derivsOrder, paramsDerivsOrder, global_eval_context, no_tmp_terms, block, use_dll); static_model.computingPass(derivsOrder, paramsDerivsOrder, global_eval_context,
no_tmp_terms, block, use_dll);
if (mod_file_struct.ramsey_model_present) if (mod_file_struct.ramsey_model_present)
static_model.computeRamseyMultipliersDerivatives(mod_file_struct.ramsey_orig_endo_nbr, static_model.computeRamseyMultipliersDerivatives(mod_file_struct.ramsey_orig_endo_nbr,
!use_dll, no_tmp_terms); !use_dll, no_tmp_terms);
...@@ -676,8 +903,7 @@ ModFile::computingPass(bool no_tmp_terms, OutputType output, int params_derivs_o ...@@ -676,8 +903,7 @@ ModFile::computingPass(bool no_tmp_terms, OutputType output, int params_derivs_o
// Set things to compute for dynamic model // Set things to compute for dynamic model
if (mod_file_struct.perfect_foresight_solver_present if (mod_file_struct.perfect_foresight_solver_present
|| mod_file_struct.perfect_foresight_with_expectation_errors_solver_present || mod_file_struct.perfect_foresight_with_expectation_errors_solver_present
|| mod_file_struct.check_present || mod_file_struct.check_present || mod_file_struct.stoch_simul_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.mom_estimation_present) || mod_file_struct.calib_smoother_present || mod_file_struct.mom_estimation_present)
...@@ -690,25 +916,34 @@ ModFile::computingPass(bool no_tmp_terms, OutputType output, int params_derivs_o ...@@ -690,25 +916,34 @@ ModFile::computingPass(bool no_tmp_terms, OutputType output, int params_derivs_o
derivsOrder = 2; derivsOrder = 2;
else if (output == OutputType::third) else if (output == OutputType::third)
derivsOrder = 3; derivsOrder = 3;
dynamic_model.computingPass(derivsOrder, 0, global_eval_context, no_tmp_terms, block, use_dll); 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.mom_estimation_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) 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);
} }
int derivsOrder = max(mod_file_struct.order_option,mod_file_struct.identification_order + 1); // See preprocessor#40 int derivsOrder
= max(mod_file_struct.order_option,
mod_file_struct.identification_order + 1); // See preprocessor#40
if (mod_file_struct.GMM_present if (mod_file_struct.GMM_present
&& (mod_file_struct.analytic_standard_errors_present || mod_file_struct.analytic_jacobian_present)) //analytic_standard_errors or analytic_jacobian require one order more && (mod_file_struct.analytic_standard_errors_present
derivsOrder = max(mod_file_struct.order_option, || mod_file_struct.analytic_jacobian_present)) // analytic_standard_errors or
max(mod_file_struct.identification_order,mod_file_struct.mom_order) + 1); // See preprocessor#40 // analytic_jacobian require
// one order more
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) if (mod_file_struct.sensitivity_present || linear || output == OutputType::second)
derivsOrder = max(derivsOrder, 2); derivsOrder = max(derivsOrder, 2);
...@@ -718,15 +953,33 @@ ModFile::computingPass(bool no_tmp_terms, OutputType output, int params_derivs_o ...@@ -718,15 +953,33 @@ ModFile::computingPass(bool no_tmp_terms, OutputType output, int params_derivs_o
if (mod_file_struct.identification_present if (mod_file_struct.identification_present
|| mod_file_struct.estimation_analytic_derivation || mod_file_struct.estimation_analytic_derivation
|| mod_file_struct.osr_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))) || (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(derivsOrder, paramsDerivsOrder, global_eval_context, no_tmp_terms, block, use_dll); 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(2, paramsDerivsOrder, global_eval_context, no_tmp_terms, block, use_dll); 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
// output=first (preprocessor#100) or third (preprocessor#121) is requested
{
switch (output)
{
case OutputType::first:
dynamic_model.computingPass(1, 0, global_eval_context, no_tmp_terms, block, use_dll);
break;
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); dynamic_model.computingPass(2, 0, global_eval_context, no_tmp_terms, block, use_dll);
break;
}
}
if (linear) if (linear)
{ {
...@@ -740,6 +993,9 @@ ModFile::computingPass(bool no_tmp_terms, OutputType output, int params_derivs_o ...@@ -740,6 +993,9 @@ ModFile::computingPass(bool no_tmp_terms, OutputType output, int params_derivs_o
// Those matrices can only be filled here, because we use derivatives // Those matrices can only be filled here, because we use derivatives
dynamic_model.fillVarModelTableMatrices(); dynamic_model.fillVarModelTableMatrices();
for (auto& hm : heterogeneous_models)
hm.computingPass(mod_file_struct.order_option, no_tmp_terms, use_dll);
for (auto& statement : statements) for (auto& statement : statements)
statement->computingPass(mod_file_struct); statement->computingPass(mod_file_struct);
...@@ -769,10 +1025,10 @@ ModFile::remove_directory_with_matlab_lock(const filesystem::path &dir) ...@@ -769,10 +1025,10 @@ ModFile::remove_directory_with_matlab_lock(const filesystem::path &dir)
void void
ModFile::writeMOutput(const string& basename, bool clear_all, bool clear_global, bool no_warn, ModFile::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,
const string &mexext, const string& mexext, const filesystem::path& matlabroot, bool onlymodel,
const filesystem::path &matlabroot, bool onlymodel, bool gui, bool notime) const bool gui, bool notime) const
{ {
if (basename.empty()) if (basename.empty())
{ {
...@@ -782,6 +1038,11 @@ ModFile::writeMOutput(const string &basename, bool clear_all, bool clear_global, ...@@ -782,6 +1038,11 @@ ModFile::writeMOutput(const string &basename, bool clear_all, bool clear_global,
auto plusfolder {DataTree::packageDir(basename)}; 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);
}
bool hasModelChanged = !dynamic_model.isChecksumMatching(basename) || !check_model_changes; bool hasModelChanged = !dynamic_model.isChecksumMatching(basename) || !check_model_changes;
if (hasModelChanged) if (hasModelChanged)
{ {
...@@ -795,7 +1056,8 @@ ModFile::writeMOutput(const string &basename, bool clear_all, bool clear_global, ...@@ -795,7 +1056,8 @@ ModFile::writeMOutput(const string &basename, bool clear_all, bool clear_global,
filesystem::remove_all(basename + "/model/src"); filesystem::remove_all(basename + "/model/src");
filesystem::remove_all(basename + "/model/bytecode"); filesystem::remove_all(basename + "/model/bytecode");
// Do not remove basename/model/julia/, otherwise it would break calls to writeToFileIfModified() // Do not remove basename/model/julia/, otherwise it would break calls to
// writeToFileIfModified()
} }
create_directory(plusfolder); create_directory(plusfolder);
...@@ -811,7 +1073,8 @@ ModFile::writeMOutput(const string &basename, bool clear_all, bool clear_global, ...@@ -811,7 +1073,8 @@ ModFile::writeMOutput(const string &basename, bool clear_all, bool clear_global,
<< "% 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()
...@@ -820,13 +1083,17 @@ ModFile::writeMOutput(const string &basename, bool clear_all, bool clear_global, ...@@ -820,13 +1083,17 @@ ModFile::writeMOutput(const string &basename, bool clear_all, bool clear_global,
mOutputFile << "clearvars -global" << endl mOutputFile << "clearvars -global" << endl
<< "clear_persistent_variables(fileparts(which('dynare')), false)" << endl; << "clear_persistent_variables(fileparts(which('dynare')), false)" << endl;
else if (clear_global) else if (clear_global)
mOutputFile << "clear M_ options_ oo_ estim_params_ bayestopt_ dataset_ dataset_info estimation_info;" << endl; mOutputFile << "clearvars -global M_ options_ oo_ estim_params_ bayestopt_ dataset_ "
"dataset_info estimation_info;"
<< endl;
if (!notime) if (!notime)
mOutputFile << "tic0 = tic;" << endl; mOutputFile << "tic0 = tic;" << endl;
mOutputFile << "% Define global variables." << endl mOutputFile
<< "global M_ options_ oo_ estim_params_ bayestopt_ dataset_ dataset_info estimation_info" << endl << "% Define global variables." << 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
...@@ -836,15 +1103,11 @@ ModFile::writeMOutput(const string &basename, bool clear_all, bool clear_global, ...@@ -836,15 +1103,11 @@ ModFile::writeMOutput(const string &basename, bool clear_all, bool clear_global,
<< "% Some global variables initialization" << endl << "% Some global variables initialization" << endl
<< "%" << endl; << "%" << endl;
if (!onlymodel) if (!onlymodel)
config_file.writeHooks(mOutputFile); config.writeHooks(mOutputFile);
mOutputFile << "global_initialization;" << endl; mOutputFile << "global_initialization;" << endl;
if (minimal_workspace)
mOutputFile << "options_.minimal_workspace = true;" << endl;
if (console) if (console)
mOutputFile << "options_.console_mode = true;" << endl mOutputFile << "options_.console_mode = true;" << endl << "options_.nodisplay = true;" << endl;
<< "options_.nodisplay = true;" << endl;
if (nograph) if (nograph)
mOutputFile << "options_.nograph = true;" << endl; mOutputFile << "options_.nograph = true;" << endl;
...@@ -855,6 +1118,7 @@ ModFile::writeMOutput(const string &basename, bool clear_all, bool clear_global, ...@@ -855,6 +1118,7 @@ ModFile::writeMOutput(const string &basename, bool clear_all, bool clear_global,
mOutputFile << "M_.parameter_used_with_lead_lag = true;" << endl; mOutputFile << "M_.parameter_used_with_lead_lag = true;" << endl;
symbol_table.writeOutput(mOutputFile); symbol_table.writeOutput(mOutputFile);
heterogeneity_table.writeOutput(mOutputFile);
var_model_table.writeOutput(basename, mOutputFile); var_model_table.writeOutput(basename, mOutputFile);
trend_component_model_table.writeOutput(basename, mOutputFile); trend_component_model_table.writeOutput(basename, mOutputFile);
...@@ -862,10 +1126,14 @@ ModFile::writeMOutput(const string &basename, bool clear_all, bool clear_global, ...@@ -862,10 +1126,14 @@ ModFile::writeMOutput(const string &basename, bool clear_all, bool clear_global,
pac_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() << ", "
...@@ -873,31 +1141,35 @@ ModFile::writeMOutput(const string &basename, bool clear_all, bool clear_global, ...@@ -873,31 +1141,35 @@ ModFile::writeMOutput(const string &basename, bool clear_all, bool clear_global,
<< "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 = true;" << endl; mOutputFile << "M_.sigma_e_is_diagonal = true;" << endl;
// Initialize M_.det_shocks, M_.surprise_shocks, M_.learnt_shocks, M_.learnt_endval and M_.heteroskedastic_shocks /* Initialize the structures created for several blocks, as part of the implementation of the
“overwrite” option */
mOutputFile << "M_.det_shocks = [];" << endl mOutputFile << "M_.det_shocks = [];" << endl
<< "M_.surprise_shocks = [];" << endl << "M_.surprise_shocks = [];" << endl
<< "M_.learnt_shocks = [];" << endl << "M_.learnt_shocks = [];" << endl
<< "M_.learnt_endval = [];" << endl << "M_.learnt_endval = [];" << endl
<< "M_.heteroskedastic_shocks.Qvalue_orig = [];" << endl << "M_.heteroskedastic_shocks.Qvalue_orig = [];" << endl
<< "M_.heteroskedastic_shocks.Qscale_orig = [];" << endl; << "M_.heteroskedastic_shocks.Qscale_orig = [];" << 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_ // NB: options_.{ramsey,discretionary}_policy should rather be fields of M_
mOutputFile << boolalpha mOutputFile << boolalpha << "options_.linear = " << linear << ";" << endl
<< "options_.linear = " << linear << ";" << endl
<< "options_.block = " << block << ";" << endl << "options_.block = " << block << ";" << endl
<< "options_.bytecode = " << bytecode << ";" << endl << "options_.bytecode = " << bytecode << ";" << endl
<< "options_.use_dll = " << use_dll << ";" << endl << "options_.use_dll = " << use_dll << ";" << endl
<< "options_.ramsey_policy = " << mod_file_struct.ramsey_model_present << ";" << endl << "options_.ramsey_policy = " << mod_file_struct.ramsey_model_present << ";" << endl
<< "options_.discretionary_policy = " << mod_file_struct.discretionary_policy_present << ";" << endl; << "options_.discretionary_policy = " << mod_file_struct.discretionary_policy_present
<< ";" << endl;
if (mod_file_struct.discretionary_policy_present) if (mod_file_struct.discretionary_policy_present)
mOutputFile << "M_.discretionary_orig_eq_nbr = " << original_model.equation_number() << ";" << endl; mOutputFile << "M_.discretionary_orig_eq_nbr = " << original_model.equation_number() << ";"
<< endl;
if (parallel_local_files.size() > 0) if (parallel_local_files.size() > 0)
{ {
...@@ -918,22 +1190,25 @@ ModFile::writeMOutput(const string &basename, bool clear_all, bool clear_global, ...@@ -918,22 +1190,25 @@ ModFile::writeMOutput(const string &basename, bool clear_all, bool clear_global,
{ {
mOutputFile << "M_.nonzero_hessian_eqs = "; mOutputFile << "M_.nonzero_hessian_eqs = ";
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;
} }
if (!onlymodel) if (!onlymodel)
config_file.writeCluster(mOutputFile); config.writeCluster(mOutputFile);
if (bytecode) 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;
mOutputFile << "M_.eq_nbr = " << dynamic_model.equation_number() << ";" << endl mOutputFile << "M_.eq_nbr = " << dynamic_model.equation_number() << ";" << endl
<< "M_.ramsey_orig_eq_nbr = " << mod_file_struct.ramsey_orig_eq_nbr << ";" << endl << "M_.ramsey_orig_eq_nbr = " << mod_file_struct.ramsey_orig_eq_nbr << ";" << endl
<< "M_.ramsey_orig_endo_nbr = " << mod_file_struct.ramsey_orig_endo_nbr << ";" << endl << "M_.ramsey_orig_endo_nbr = " << mod_file_struct.ramsey_orig_endo_nbr << ";" << endl
<< "M_.set_auxiliary_variables = exist(['./+' M_.fname '/set_auxiliary_variables.m'], 'file') == 2;" << endl; << "M_.set_auxiliary_variables = exist(['./+' M_.fname "
"'/set_auxiliary_variables.m'], 'file') == 2;"
<< endl;
epilogue.writeOutput(mOutputFile); epilogue.writeOutput(mOutputFile);
...@@ -948,6 +1223,9 @@ ModFile::writeMOutput(const string &basename, bool clear_all, bool clear_global, ...@@ -948,6 +1223,9 @@ ModFile::writeMOutput(const string &basename, bool clear_all, bool clear_global,
} }
} }
for (const auto& hm : heterogeneous_models)
hm.writeDriverOutput(mOutputFile);
if (onlymodel || gui) if (onlymodel || gui)
for (const auto& statement : statements) for (const auto& statement : statements)
{ {
...@@ -1001,41 +1279,60 @@ ModFile::writeMOutput(const string &basename, bool clear_all, bool clear_global, ...@@ -1001,41 +1279,60 @@ ModFile::writeMOutput(const string &basename, bool clear_all, bool clear_global,
if (auto evs = dynamic_cast<EndValStatement*>(statement.get()); evs) if (auto evs = dynamic_cast<EndValStatement*>(statement.get()); evs)
static_model.writeAuxVarInitval(mOutputFile, ExprNodeOutputType::matlabOutsideModel); static_model.writeAuxVarInitval(mOutputFile, ExprNodeOutputType::matlabOutsideModel);
// 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
if (auto lpass = dynamic_cast<LoadParamsAndSteadyStateStatement *>(statement.get()); lpass && !no_static) // the auxiliary variables
if (auto lpass = dynamic_cast<LoadParamsAndSteadyStateStatement*>(statement.get());
lpass && !no_static)
static_model.writeAuxVarInitval(mOutputFile, ExprNodeOutputType::matlabOutsideModel); static_model.writeAuxVarInitval(mOutputFile, ExprNodeOutputType::matlabOutsideModel);
} }
if (!notime) if (!notime)
mOutputFile << endl << endl mOutputFile << endl
<< endl
<< "oo_.time = toc(tic0);" << endl << "oo_.time = toc(tic0);" << endl
<< "disp(['Total computing time : ' dynsec2hms(oo_.time) ]);" << endl; << "disp(['Total computing time : ' dynsec2hms(oo_.time) ]);" << endl;
mOutputFile << "if ~exist([M_.dname filesep 'Output'],'dir')" << endl mOutputFile << "if ~exist([M_.dname filesep 'Output'],'dir')" << endl
<< " mkdir(M_.dname,'Output');" << endl << " mkdir(M_.dname,'Output');" << endl
<< "end" << endl << "end" << endl
<< "save([M_.dname filesep 'Output' filesep '" << basename << "_results.mat'], 'oo_', 'M_', 'options_');" << 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([M_.dname filesep 'Output' filesep '" << 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([M_.dname filesep 'Output' filesep '" << 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([M_.dname filesep 'Output' filesep '" << 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([M_.dname filesep 'Output' filesep '" << 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([M_.dname filesep 'Output' filesep '" << 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([M_.dname filesep 'Output' filesep '" << basename << "_results.mat'], 'oo_recursive_', '-append');" << endl << "end" << endl << " save([M_.dname filesep 'Output' filesep '" << basename
<< "_results.mat'], 'oo_recursive_', '-append');" << endl
<< "end" << endl
<< "if exist('options_mom_', 'var') == 1" << endl << "if exist('options_mom_', 'var') == 1" << endl
<< " save([M_.dname filesep 'Output' filesep '" << basename << "_results.mat'], 'options_mom_', '-append');" << endl << "end" << endl; << " save([M_.dname filesep 'Output' filesep '" << basename
<< "_results.mat'], 'options_mom_', '-append');" << endl
<< "end" << endl;
config_file.writeEndParallel(mOutputFile); config.writeEndParallel(mOutputFile);
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
...@@ -1057,9 +1354,11 @@ ModFile::writeMOutput(const string &basename, bool clear_all, bool clear_global, ...@@ -1057,9 +1354,11 @@ ModFile::writeMOutput(const string &basename, bool clear_all, bool clear_global,
if (mod_file_struct.ramsey_model_present) if (mod_file_struct.ramsey_model_present)
{ {
if (use_dll) if (use_dll)
static_model.writeRamseyMultipliersDerivativesCFile(basename, mexext, matlabroot, mod_file_struct.ramsey_orig_endo_nbr); static_model.writeRamseyMultipliersDerivativesCFile(
basename, mexext, matlabroot, mod_file_struct.ramsey_orig_endo_nbr);
else else
static_model.writeRamseyMultipliersDerivativesMFile(basename, mod_file_struct.ramsey_orig_endo_nbr); static_model.writeRamseyMultipliersDerivativesMFile(
basename, mod_file_struct.ramsey_orig_endo_nbr);
} }
} }
...@@ -1075,6 +1374,9 @@ ModFile::writeMOutput(const string &basename, bool clear_all, bool clear_global, ...@@ -1075,6 +1374,9 @@ ModFile::writeMOutput(const string &basename, bool clear_all, bool clear_global,
epilogue.writeEpilogueFile(basename); epilogue.writeEpilogueFile(basename);
pac_model_table.writeTargetCoefficientsFile(basename); pac_model_table.writeTargetCoefficientsFile(basename);
for (const auto& hm : heterogeneous_models)
hm.writeModelFiles(basename, false);
} }
} }
...@@ -1095,7 +1397,8 @@ ModFile::writeJuliaOutput(const string &basename) const ...@@ -1095,7 +1397,8 @@ ModFile::writeJuliaOutput(const string &basename) const
} }
void void
ModFile::writeJsonOutput(const string &basename, JsonOutputPointType json, JsonFileOutputType json_output_mode, bool onlyjson, bool jsonderivsimple) ModFile::writeJsonOutput(const string& basename, JsonOutputPointType json,
JsonFileOutputType json_output_mode, bool onlyjson, bool jsonderivsimple)
{ {
if (json == JsonOutputPointType::nojson) if (json == JsonOutputPointType::nojson)
return; return;
...@@ -1104,10 +1407,11 @@ ModFile::writeJsonOutput(const string &basename, JsonOutputPointType json, JsonF ...@@ -1104,10 +1407,11 @@ ModFile::writeJsonOutput(const string &basename, JsonOutputPointType json, JsonF
symbol_table.freeze(); symbol_table.freeze();
if (json_output_mode == JsonFileOutputType::standardout) if (json_output_mode == JsonFileOutputType::standardout)
cout << "//-- BEGIN JSON --// " << endl cout << "//-- BEGIN JSON --// " << endl << "{" << endl;
<< "{" << endl;
writeJsonOutputParsingCheck(basename, json_output_mode, json == JsonOutputPointType::transformpass, json == JsonOutputPointType::computingpass); writeJsonOutputParsingCheck(basename, json_output_mode,
json == JsonOutputPointType::transformpass,
json == JsonOutputPointType::computingpass);
if (json == JsonOutputPointType::parsing || json == JsonOutputPointType::checkpass) if (json == JsonOutputPointType::parsing || json == JsonOutputPointType::checkpass)
symbol_table.unfreeze(); symbol_table.unfreeze();
...@@ -1116,8 +1420,7 @@ ModFile::writeJsonOutput(const string &basename, JsonOutputPointType json, JsonF ...@@ -1116,8 +1420,7 @@ ModFile::writeJsonOutput(const string &basename, JsonOutputPointType json, JsonF
writeJsonComputingPassOutput(basename, json_output_mode, jsonderivsimple); writeJsonComputingPassOutput(basename, json_output_mode, jsonderivsimple);
if (json_output_mode == JsonFileOutputType::standardout) if (json_output_mode == JsonFileOutputType::standardout)
cout << "}" << endl cout << "}" << endl << "//-- END JSON --// " << endl;
<< "//-- END JSON --// " << endl;
switch (json) switch (json)
{ {
...@@ -1143,20 +1446,24 @@ ModFile::writeJsonOutput(const string &basename, JsonOutputPointType json, JsonF ...@@ -1143,20 +1446,24 @@ 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 << ", "; output << ", ";
static_model.writeJsonOutput(output); static_model.writeJsonOutput(output);
if (!statements.empty() if (!statements.empty() || !var_model_table.empty() || !trend_component_model_table.empty())
|| !var_model_table.empty()
|| !trend_component_model_table.empty())
{ {
output << R"(, "statements": [)"; output << R"(, "statements": [)";
if (!var_model_table.empty()) if (!var_model_table.empty())
...@@ -1183,8 +1490,7 @@ ModFile::writeJsonOutputParsingCheck(const string &basename, JsonFileOutputType ...@@ -1183,8 +1490,7 @@ ModFile::writeJsonOutputParsingCheck(const string &basename, JsonFileOutputType
output << ", "; output << ", ";
} }
for (bool printed_something{false}; for (bool printed_something {false}; auto& it : statements)
auto &it : statements)
{ {
if (exchange(printed_something, true)) if (exchange(printed_something, true))
output << ", " << endl; output << ", " << endl;
...@@ -1198,6 +1504,9 @@ ModFile::writeJsonOutputParsingCheck(const string &basename, JsonFileOutputType ...@@ -1198,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;
...@@ -1224,8 +1533,7 @@ ModFile::writeJsonOutputParsingCheck(const string &basename, JsonFileOutputType ...@@ -1224,8 +1533,7 @@ ModFile::writeJsonOutputParsingCheck(const string &basename, JsonFileOutputType
pac_model_table.writeJsonOutput(original_model_output); pac_model_table.writeJsonOutput(original_model_output);
original_model_output << ", "; original_model_output << ", ";
} }
for (bool printed_something{false}; for (bool printed_something {false}; const auto& it : statements)
const auto &it : statements)
{ {
original_model_output << (exchange(printed_something, true) ? "," : "") << endl; original_model_output << (exchange(printed_something, true) ? "," : "") << endl;
it->writeJsonOutput(original_model_output); it->writeJsonOutput(original_model_output);
...@@ -1319,7 +1627,8 @@ ModFile::writeJsonOutputParsingCheck(const string &basename, JsonFileOutputType ...@@ -1319,7 +1627,8 @@ 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 != JsonFileOutputType::standardout) if (basename.empty() && json_output_mode != JsonFileOutputType::standardout)
{ {
...@@ -1327,7 +1636,8 @@ ModFile::writeJsonComputingPassOutput(const string &basename, JsonFileOutputType ...@@ -1327,7 +1636,8 @@ ModFile::writeJsonComputingPassOutput(const string &basename, JsonFileOutputType
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);
...@@ -1365,7 +1675,8 @@ ModFile::writeJsonComputingPassOutput(const string &basename, JsonFileOutputType ...@@ -1365,7 +1675,8 @@ ModFile::writeJsonComputingPassOutput(const string &basename, JsonFileOutputType
writeJsonFileHelper(basename + "/model/json/dynamic.json", 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", static_paramsd_output); writeJsonFileHelper(basename + "/model/json/static_params_derivs.json",
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); writeJsonFileHelper(basename + "/model/json/params_derivs.json", dynamic_paramsd_output);
......
/* /*
* Copyright © 2006-2023 Dynare Team * Copyright © 2006-2024 Dynare Team
* *
* This file is part of Dynare. * This file is part of Dynare.
* *
...@@ -17,27 +17,29 @@ ...@@ -17,27 +17,29 @@
* along with Dynare. If not, see <https://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
#include <ostream>
#include <ctime> #include <ctime>
#include <filesystem>
#include <iostream> #include <iostream>
#include <ostream>
#include <sstream> #include <sstream>
#include <filesystem>
#include "SymbolTable.hh" #include "Configuration.hh"
#include "NumericalConstants.hh"
#include "NumericalInitialization.hh"
#include "StaticModel.hh"
#include "DynamicModel.hh" #include "DynamicModel.hh"
#include "ExtendedPreprocessorTypes.hh"
#include "ExternalFunctionsTable.hh"
#include "HeterogeneityTable.hh"
#include "HeterogeneousModel.hh"
#include "ModelEquationBlock.hh" #include "ModelEquationBlock.hh"
#include "NumericalConstants.hh"
#include "NumericalInitialization.hh"
#include "Statement.hh" #include "Statement.hh"
#include "ExternalFunctionsTable.hh" #include "StaticModel.hh"
#include "ConfigFile.hh"
#include "WarningConsolidation.hh"
#include "ExtendedPreprocessorTypes.hh"
#include "SubModel.hh" #include "SubModel.hh"
#include "SymbolTable.hh"
#include "WarningConsolidation.hh"
using namespace std; using namespace std;
...@@ -46,6 +48,8 @@ class ModFile ...@@ -46,6 +48,8 @@ class ModFile
{ {
public: public:
explicit ModFile(WarningConsolidation& warnings_arg); explicit ModFile(WarningConsolidation& warnings_arg);
// For heterogeneity dimensions
HeterogeneityTable heterogeneity_table;
//! Symbol table //! Symbol table
SymbolTable symbol_table; SymbolTable symbol_table;
//! External Functions table //! External Functions table
...@@ -76,6 +80,8 @@ public: ...@@ -76,6 +80,8 @@ public:
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 {false}; bool linear {false};
...@@ -90,7 +96,8 @@ public: ...@@ -90,7 +96,8 @@ public:
“model” block or on the preprocessor command line) */ “model” block or on the preprocessor command line) */
bool use_dll {false}; 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
//! 'model'
bool no_static {false}; bool no_static {false};
//! Is the 'differentiate_forward_vars' option used? //! Is the 'differentiate_forward_vars' option used?
...@@ -116,6 +123,11 @@ public: ...@@ -116,6 +123,11 @@ public:
/*! (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<unique_ptr<Statement>> statements; vector<unique_ptr<Statement>> statements;
...@@ -124,8 +136,10 @@ private: ...@@ -124,8 +136,10 @@ private:
//! 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 writeJsonComputingPassOutput(const string& basename, JsonFileOutputType json_output_mode,
bool jsonderivsimple) const;
void writeJsonFileHelper(const filesystem::path& fname, ostringstream& output) const; void writeJsonFileHelper(const filesystem::path& fname, ostringstream& output) const;
/* Generate a random temporary path, in the current directory. Equivalent to /* Generate a random temporary path, in the current directory. Equivalent to
boost::filesystem::unique_path(). Both are insecure, but currently there boost::filesystem::unique_path(). Both are insecure, but currently there
...@@ -138,13 +152,15 @@ private: ...@@ -138,13 +152,15 @@ private:
same filesystem. This technique is applied recursively to subdirectories. same filesystem. This technique is applied recursively to subdirectories.
Works even if the argument does not exist or is not a directory. */ Works even if the argument does not exist or is not a directory. */
static void remove_directory_with_matlab_lock(const filesystem::path& dir); static void remove_directory_with_matlab_lock(const filesystem::path& dir);
public: public:
//! Add a statement //! Add a statement
void addStatement(unique_ptr<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(unique_ptr<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
* endogenous/exogenous/parameters ? */
void evalAllExpressions(bool warn_uninit); 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 */
...@@ -154,13 +170,14 @@ public: ...@@ -154,13 +170,14 @@ public:
void transformPass(bool nostrict, bool stochastic, bool compute_xrefs, bool transform_unary_ops, void transformPass(bool nostrict, bool stochastic, bool compute_xrefs, bool transform_unary_ops,
const string& exclude_eqs, const string& include_eqs); 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, OutputType output, int params_derivs_order); 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?
...@@ -170,19 +187,22 @@ public: ...@@ -170,19 +187,22 @@ public:
\param compute_xrefs if true, equation cross references will be computed \param compute_xrefs if true, equation cross references will be computed
*/ */
void writeMOutput(const string& basename, bool clear_all, bool clear_global, 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,
const string &mexext, const filesystem::path &matlabroot, const string& mexext, const filesystem::path& matlabroot, bool onlymodel,
bool onlymodel, bool gui, bool notime) const; bool gui, bool notime) const;
void writeJuliaOutput(const string& basename) const; void writeJuliaOutput(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, 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
/* /*
* Copyright © 2010-2023 Dynare Team * Copyright © 2010-2024 Dynare Team
* *
* This file is part of Dynare. * This file is part of Dynare.
* *
...@@ -17,17 +17,30 @@ ...@@ -17,17 +17,30 @@
* along with Dynare. If not, see <https://www.gnu.org/licenses/>. * along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include <cassert>
#include <algorithm> #include <algorithm>
#include <cassert>
#include <ranges>
#include <sstream> #include <sstream>
#include "ModelEquationBlock.hh" #include "ModelEquationBlock.hh"
PlannerObjective::PlannerObjective(SymbolTable& symbol_table_arg, PlannerObjective::PlannerObjective(SymbolTable& symbol_table_arg,
NumericalConstants& num_constants_arg, NumericalConstants& num_constants_arg,
ExternalFunctionsTable &external_functions_table_arg) : ExternalFunctionsTable& external_functions_table_arg,
StaticModel {symbol_table_arg, num_constants_arg, external_functions_table_arg} HeterogeneityTable& heterogeneity_table_arg) :
StaticModel {symbol_table_arg, num_constants_arg, external_functions_table_arg,
heterogeneity_table_arg}
{
}
void
PlannerObjective::writeDriverOutput(ostream& output) const
{ {
output << "M_.objective_tmp_nbr = [";
for (const auto& it : temporary_terms_derivatives)
output << it.size() << "; ";
output << "];" << endl;
writeDriverSparseIndicesHelper("objective", output);
} }
void void
...@@ -37,13 +50,17 @@ PlannerObjective::computingPassBlock([[maybe_unused]] const eval_context_t &eval ...@@ -37,13 +50,17 @@ PlannerObjective::computingPassBlock([[maybe_unused]] const eval_context_t &eval
// Disable block decomposition on planner objective // Disable block decomposition on planner objective
} }
OrigRamseyDynamicModel::OrigRamseyDynamicModel(SymbolTable &symbol_table_arg, OrigRamseyDynamicModel::OrigRamseyDynamicModel(
NumericalConstants &num_constants_arg, SymbolTable& symbol_table_arg, NumericalConstants& num_constants_arg,
ExternalFunctionsTable& external_functions_table_arg, ExternalFunctionsTable& external_functions_table_arg,
TrendComponentModelTable &trend_component_model_table_arg, HeterogeneityTable& heterogeneity_table_arg,
VarModelTable &var_model_table_arg) : TrendComponentModelTable& trend_component_model_table_arg, VarModelTable& var_model_table_arg) :
DynamicModel {symbol_table_arg, num_constants_arg, external_functions_table_arg, DynamicModel {symbol_table_arg,
trend_component_model_table_arg, var_model_table_arg} num_constants_arg,
external_functions_table_arg,
heterogeneity_table_arg,
trend_component_model_table_arg,
var_model_table_arg}
{ {
} }
...@@ -57,15 +74,16 @@ OrigRamseyDynamicModel::operator=(const DynamicModel &m) ...@@ -57,15 +74,16 @@ OrigRamseyDynamicModel::operator=(const DynamicModel &m)
SteadyStateModel::SteadyStateModel(SymbolTable& symbol_table_arg, SteadyStateModel::SteadyStateModel(SymbolTable& symbol_table_arg,
NumericalConstants& num_constants_arg, NumericalConstants& num_constants_arg,
ExternalFunctionsTable& external_functions_table_arg, ExternalFunctionsTable& external_functions_table_arg,
HeterogeneityTable& heterogeneity_table_arg,
const StaticModel& static_model_arg) : const StaticModel& static_model_arg) :
DataTree{symbol_table_arg, num_constants_arg, external_functions_table_arg}, DataTree {symbol_table_arg, num_constants_arg, external_functions_table_arg,
heterogeneity_table_arg},
static_model {static_model_arg} static_model {static_model_arg}
{ {
} }
SteadyStateModel::SteadyStateModel(const SteadyStateModel& m) : SteadyStateModel::SteadyStateModel(const SteadyStateModel& m) :
DataTree{m}, DataTree {m}, static_model {m.static_model}
static_model{m.static_model}
{ {
for (const auto& it : m.def_table) for (const auto& it : m.def_table)
def_table.emplace_back(it.first, it.second->clone(*this)); def_table.emplace_back(it.first, it.second->clone(*this));
...@@ -126,7 +144,8 @@ SteadyStateModel::checkPass(ModFileStructure &mod_file_struct, WarningConsolidat ...@@ -126,7 +144,8 @@ SteadyStateModel::checkPass(ModFileStructure &mod_file_struct, WarningConsolidat
// Check that symbols are not already defined // Check that symbols are not already defined
for (int symb_id : symb_ids) for (int symb_id : symb_ids)
if (so_far_defined.contains(symb_id)) if (so_far_defined.contains(symb_id))
warnings << "WARNING: in the 'steady_state_model' block, variable '" << symbol_table.getName(symb_id) << "' is declared twice" << endl; warnings << "WARNING: in the 'steady_state_model' block, variable '"
<< symbol_table.getName(symb_id) << "' is declared twice" << endl;
// Check that expression has no undefined symbol // Check that expression has no undefined symbol
if (!mod_file_struct.ramsey_model_present) if (!mod_file_struct.ramsey_model_present)
...@@ -137,8 +156,10 @@ SteadyStateModel::checkPass(ModFileStructure &mod_file_struct, WarningConsolidat ...@@ -137,8 +156,10 @@ SteadyStateModel::checkPass(ModFileStructure &mod_file_struct, WarningConsolidat
for (int used_symbol : used_symbols) for (int used_symbol : used_symbols)
if (!so_far_defined.contains(used_symbol)) if (!so_far_defined.contains(used_symbol))
{ {
cerr << "ERROR: in the 'steady_state_model' block, variable '" << symbol_table.getName(used_symbol) cerr << "ERROR: in the 'steady_state_model' block, variable '"
<< "' is undefined in the declaration of variable '" << symbol_table.getName(symb_ids[0]) << "'" << endl; << symbol_table.getName(used_symbol)
<< "' is undefined in the declaration of variable '"
<< symbol_table.getName(symb_ids[0]) << "'" << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
...@@ -155,7 +176,8 @@ SteadyStateModel::checkPass(ModFileStructure &mod_file_struct, WarningConsolidat ...@@ -155,7 +176,8 @@ SteadyStateModel::checkPass(ModFileStructure &mod_file_struct, WarningConsolidat
should_be_defined.erase(symbol_table.getID(s)); should_be_defined.erase(symbol_table.getID(s));
for (int v : should_be_defined) for (int v : should_be_defined)
if (!so_far_defined.contains(v)) if (!so_far_defined.contains(v))
warnings << "WARNING: in the 'steady_state_model' block, variable '" << symbol_table.getName(v) << "' is not assigned a value" << endl; warnings << "WARNING: in the 'steady_state_model' block, variable '"
<< symbol_table.getName(v) << "' is not assigned a value" << endl;
} }
void void
...@@ -191,16 +213,14 @@ SteadyStateModel::writeLatexSteadyStateFile(const string &basename) const ...@@ -191,16 +213,14 @@ SteadyStateModel::writeLatexSteadyStateFile(const string &basename) const
for (const auto& [ids, value] : def_table) for (const auto& [ids, value] : def_table)
for (int id : ids) for (int id : ids)
{ {
content_output << "\\begin{dmath}" << endl content_output << "\\begin{dmath}" << endl << symbol_table.getTeXName(id) << " = ";
<< symbol_table.getTeXName(id) << " = ";
value->writeOutput(content_output, ExprNodeOutputType::latexStaticModel); value->writeOutput(content_output, ExprNodeOutputType::latexStaticModel);
content_output << endl << "\\end{dmath}" << endl; content_output << endl << "\\end{dmath}" << endl;
} }
static_model.writeLatexAuxVarRecursiveDefinitions(content_output); static_model.writeLatexAuxVarRecursiveDefinitions(content_output);
output << "\\include{steady_state_content.tex}" << endl output << "\\include{steady_state_content.tex}" << endl << "\\end{document}" << endl;
<< "\\end{document}" << endl;
output.close(); output.close();
content_output.close(); content_output.close();
...@@ -212,7 +232,8 @@ SteadyStateModel::writeSteadyStateFile(const string &basename, bool julia) const ...@@ -212,7 +232,8 @@ SteadyStateModel::writeSteadyStateFile(const string &basename, bool julia) const
if (def_table.size() == 0) if (def_table.size() == 0)
return; return;
ExprNodeOutputType output_type = (julia ? ExprNodeOutputType::juliaSteadyStateFile : ExprNodeOutputType::steadyStateFile); ExprNodeOutputType output_type
= (julia ? ExprNodeOutputType::juliaSteadyStateFile : ExprNodeOutputType::steadyStateFile);
stringstream output; stringstream output;
if (!julia) if (!julia)
...@@ -257,7 +278,8 @@ SteadyStateModel::writeSteadyStateFile(const string &basename, bool julia) const ...@@ -257,7 +278,8 @@ SteadyStateModel::writeSteadyStateFile(const string &basename, bool julia) const
output << "end" << endl; output << "end" << endl;
if (julia) if (julia)
writeToFileIfModified(output, filesystem::path{basename} / "model" / "julia" / "SteadyState2.jl"); writeToFileIfModified(output,
filesystem::path {basename} / "model" / "julia" / "SteadyState2.jl");
else else
{ {
/* Calling writeToFileIfModified() is useless here since we write inside /* Calling writeToFileIfModified() is useless here since we write inside
...@@ -284,16 +306,14 @@ SteadyStateModel::writeJsonSteadyStateFile(ostream &output, bool transformComput ...@@ -284,16 +306,14 @@ SteadyStateModel::writeJsonSteadyStateFile(ostream &output, bool transformComput
output << "{\"steady_state_model\": ["; output << "{\"steady_state_model\": [";
for (bool printed_something{false}; for (bool printed_something {false}; const auto& [symb_ids, value] : def_table)
const auto &[symb_ids, value] : def_table)
{ {
if (exchange(printed_something, true)) if (exchange(printed_something, true))
output << ","; output << ",";
output << "{\"lhs\": "; output << "{\"lhs\": ";
if (symb_ids.size() > 1) if (symb_ids.size() > 1)
output << "["; output << "[";
for (bool printed_something2{false}; for (bool printed_something2 {false}; int symb_id : symb_ids)
int symb_id : symb_ids)
{ {
if (exchange(printed_something2, true)) if (exchange(printed_something2, true))
output << ","; output << ",";
...@@ -314,18 +334,21 @@ SteadyStateModel::writeJsonSteadyStateFile(ostream &output, bool transformComput ...@@ -314,18 +334,21 @@ SteadyStateModel::writeJsonSteadyStateFile(ostream &output, bool transformComput
output << "]}"; output << "]}";
} }
Epilogue::Epilogue(SymbolTable &symbol_table_arg, Epilogue::Epilogue(SymbolTable& symbol_table_arg, NumericalConstants& num_constants_arg,
NumericalConstants &num_constants_arg,
ExternalFunctionsTable& external_functions_table_arg, ExternalFunctionsTable& external_functions_table_arg,
HeterogeneityTable& heterogeneity_table_arg,
TrendComponentModelTable& trend_component_model_table_arg, TrendComponentModelTable& trend_component_model_table_arg,
VarModelTable& var_model_table_arg) : VarModelTable& var_model_table_arg) :
DynamicModel{symbol_table_arg, num_constants_arg, external_functions_table_arg, DynamicModel {symbol_table_arg,
trend_component_model_table_arg, var_model_table_arg} num_constants_arg,
external_functions_table_arg,
heterogeneity_table_arg,
trend_component_model_table_arg,
var_model_table_arg}
{ {
} }
Epilogue::Epilogue(const Epilogue &m) : Epilogue::Epilogue(const Epilogue& m) : DynamicModel {m}
DynamicModel{m}
{ {
for (const auto& it : m.dynamic_def_table) for (const auto& it : m.dynamic_def_table)
dynamic_def_table.emplace_back(it.first, it.second->clone(*this)); dynamic_def_table.emplace_back(it.first, it.second->clone(*this));
...@@ -356,7 +379,9 @@ Epilogue::checkPass(ModFileStructure &mod_file_struct) const ...@@ -356,7 +379,9 @@ Epilogue::checkPass(ModFileStructure &mod_file_struct) const
{ {
if (mod_file_struct.with_epilogue_option) if (mod_file_struct.with_epilogue_option)
{ {
cerr << "ERROR: the 'with_epilogue' option cannot be specified when there is no 'epilogue' block" << endl; cerr << "ERROR: the 'with_epilogue' option cannot be specified when there is no "
"'epilogue' block"
<< endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
return; return;
...@@ -366,7 +391,7 @@ Epilogue::checkPass(ModFileStructure &mod_file_struct) const ...@@ -366,7 +391,7 @@ Epilogue::checkPass(ModFileStructure &mod_file_struct) const
for (const auto& [symb_id, expr] : dynamic_def_table) for (const auto& [symb_id, expr] : dynamic_def_table)
if (so_far_defined.contains(symb_id)) if (so_far_defined.contains(symb_id))
{ {
cerr << "WARNING: in the 'epilogue' block, variable '" << symbol_table.getName(symb_id) cerr << "ERROR: in the 'epilogue' block, variable '" << symbol_table.getName(symb_id)
<< "' is declared twice" << endl; << "' is declared twice" << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
...@@ -385,11 +410,10 @@ void ...@@ -385,11 +410,10 @@ void
Epilogue::detrend(const map<int, expr_t>& trend_symbols_map, Epilogue::detrend(const map<int, expr_t>& trend_symbols_map,
const nonstationary_symbols_map_t& nonstationary_symbols_map) const nonstationary_symbols_map_t& nonstationary_symbols_map)
{ {
for (auto it = nonstationary_symbols_map.crbegin(); for (const auto& [symb_id, deflator] : ranges::reverse_view(nonstationary_symbols_map))
it != nonstationary_symbols_map.crend(); ++it)
for (auto& [symb_id, expr] : dynamic_def_table) for (auto& [symb_id, expr] : dynamic_def_table)
{ {
expr = expr->detrend(it->first, it->second.first, it->second.second); expr = expr->detrend(symb_id, deflator.first, deflator.second);
assert(expr); assert(expr);
} }
...@@ -433,21 +457,25 @@ Epilogue::writeStaticEpilogueFile(const string &basename) const ...@@ -433,21 +457,25 @@ Epilogue::writeStaticEpilogueFile(const string &basename) const
for (const auto& [symb_id, expr] : static_def_table) for (const auto& [symb_id, expr] : static_def_table)
{ {
// Rewrite external function TEF term for every equation as argument values could have been changed // Rewrite external function TEF term for every equation as argument values could have been
// in between two calls to the same function; // changed in between two calls to the same function;
deriv_node_temp_terms_t tef_terms; deriv_node_temp_terms_t tef_terms;
temporary_terms_t temporary_terms; temporary_terms_t temporary_terms;
temporary_terms_idxs_t temporary_terms_idxs; temporary_terms_idxs_t temporary_terms_idxs;
output << endl; output << endl;
if (expr->containsExternalFunction()) if (expr->containsExternalFunction())
expr->writeExternalFunctionOutput(output, ExprNodeOutputType::matlabDseries, temporary_terms, temporary_terms_idxs, tef_terms); expr->writeExternalFunctionOutput(output, ExprNodeOutputType::matlabDseries,
temporary_terms, temporary_terms_idxs, tef_terms);
output << "epilogue_static_tmp_term = "; output << "epilogue_static_tmp_term = ";
expr->writeOutput(output, ExprNodeOutputType::matlabDseries, temporary_terms, temporary_terms_idxs, tef_terms); expr->writeOutput(output, ExprNodeOutputType::matlabDseries, temporary_terms,
temporary_terms_idxs, tef_terms);
output << ";" << endl output << ";" << endl
<< "if isdseries(epilogue_static_tmp_term)" << endl << "if isdseries(epilogue_static_tmp_term)" << endl
<< " ds." << symbol_table.getName(symb_id) << " = epilogue_static_tmp_term;" << endl << " ds." << symbol_table.getName(symb_id) << " = epilogue_static_tmp_term;" << endl
<< "else" << endl << "else" << endl
<< " ds." << symbol_table.getName(symb_id) << " = dseries(ones(ds.nobs,1)*epilogue_static_tmp_term, ds.firstdate, '" << symbol_table.getName(symb_id) << "');" << endl << " ds." << symbol_table.getName(symb_id)
<< " = dseries(ones(ds.nobs,1)*epilogue_static_tmp_term, ds.firstdate, '"
<< symbol_table.getName(symb_id) << "');" << endl
<< "end" << endl; << "end" << endl;
} }
output << "end" << endl; output << "end" << endl;
...@@ -467,7 +495,8 @@ Epilogue::writeDynamicEpilogueFile(const string &basename) const ...@@ -467,7 +495,8 @@ Epilogue::writeDynamicEpilogueFile(const string &basename) const
output << "function ds = epilogue_dynamic(params, ds)" << endl output << "function ds = epilogue_dynamic(params, ds)" << endl
<< "% function ds = epilogue_dynamic(params, ds)" << endl << "% function ds = epilogue_dynamic(params, ds)" << endl
<< "% Epilogue file generated by Dynare preprocessor" << endl << endl << "% Epilogue file generated by Dynare preprocessor" << endl
<< endl
<< "simul_end_date = lastdate(ds);" << endl; << "simul_end_date = lastdate(ds);" << endl;
deriv_node_temp_terms_t tef_terms; deriv_node_temp_terms_t tef_terms;
...@@ -483,12 +512,12 @@ Epilogue::writeDynamicEpilogueFile(const string &basename) const ...@@ -483,12 +512,12 @@ Epilogue::writeDynamicEpilogueFile(const string &basename) const
output << endl output << endl
<< "if ~ds.exist('" << symbol_table.getName(symb_id) << "')" << endl << "if ~ds.exist('" << symbol_table.getName(symb_id) << "')" << endl
<< " ds = [ds dseries(NaN(ds.nobs,1), ds.firstdate, '" << symbol_table.getName(symb_id)<< "')];" << endl << " ds = [ds dseries(NaN(ds.nobs,1), ds.firstdate, '"
<< symbol_table.getName(symb_id) << "')];" << endl
<< "end" << endl << "end" << endl
<< "try" << endl << "try" << endl
<< " simul_begin_date = firstobservedperiod(ds{"; << " simul_begin_date = firstobservedperiod(ds{";
for (bool printed_something{false}; for (bool printed_something {false}; int symb_id : used_symbols)
int symb_id : used_symbols)
{ {
if (exchange(printed_something, true)) if (exchange(printed_something, true))
output << ", "; output << ", ";
...@@ -497,10 +526,9 @@ Epilogue::writeDynamicEpilogueFile(const string &basename) const ...@@ -497,10 +526,9 @@ Epilogue::writeDynamicEpilogueFile(const string &basename) const
output << "}) + " << max_lag << ";" << endl output << "}) + " << max_lag << ";" << endl
<< " from simul_begin_date to simul_end_date do " << " from simul_begin_date to simul_end_date do "
<< "ds." << symbol_table.getName(symb_id) << "(t) = "; << "ds." << symbol_table.getName(symb_id) << "(t) = ";
expr->writeOutput(output, ExprNodeOutputType::epilogueFile, temporary_terms, temporary_terms_idxs, tef_terms); expr->writeOutput(output, ExprNodeOutputType::epilogueFile, temporary_terms,
output << ";" << endl temporary_terms_idxs, tef_terms);
<< "catch" << endl output << ";" << endl << "catch" << endl << "end" << endl;
<< "end" << endl;
} }
output << "end" << endl; output << "end" << endl;
output.close(); output.close();
...@@ -511,22 +539,21 @@ Epilogue::writeOutput(ostream &output) const ...@@ -511,22 +539,21 @@ Epilogue::writeOutput(ostream &output) const
{ {
if (dynamic_def_table.empty()) if (dynamic_def_table.empty())
{ {
output << "M_.epilogue_names = {};" << endl output << "M_.epilogue_names = {};" << endl << "M_.epilogue_var_list_ = {};" << endl;
<< "M_.epilogue_var_list_ = {};" << endl;
return; return;
} }
output << "M_.epilogue_names = cell(" << dynamic_def_table.size() << ",1);" << endl; output << "M_.epilogue_names = cell(" << dynamic_def_table.size() << ",1);" << endl;
for (int idx{1}; for (int idx {1}; const auto& [symb_id, expr] : dynamic_def_table)
const auto &[symb_id, expr] : dynamic_def_table) output << "M_.epilogue_names{" << idx++ << "} = '" << symbol_table.getName(symb_id) << "';"
output << "M_.epilogue_names{" << idx++ << "} = '" << endl;
<< symbol_table.getName(symb_id) << "';" << endl;
set<int> endogs; set<int> endogs;
for (const auto& [symb_id, expr] : dynamic_def_table) for (const auto& [symb_id, expr] : dynamic_def_table)
expr->collectVariables(SymbolType::endogenous, endogs); expr->collectVariables(SymbolType::endogenous, endogs);
vector<string> symbol_list; vector<string> symbol_list;
symbol_list.reserve(endogs.size());
for (auto symb_id : endogs) for (auto symb_id : endogs)
symbol_list.push_back(symbol_table.getName(symb_id)); symbol_list.push_back(symbol_table.getName(symb_id));
SymbolList {move(symbol_list)}.writeOutput("M_.epilogue_var_list_", output); SymbolList {move(symbol_list)}.writeOutput("M_.epilogue_var_list_", output);
......
/* /*
* Copyright © 2010-2022 Dynare Team * Copyright © 2010-2025 Dynare Team
* *
* This file is part of Dynare. * This file is part of Dynare.
* *
...@@ -17,21 +17,24 @@ ...@@ -17,21 +17,24 @@
* along with Dynare. If not, see <https://www.gnu.org/licenses/>. * along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/ */
#ifndef _MODEL_EQUATION_BLOCK_HH #ifndef MODEL_EQUATION_BLOCK_HH
#define _MODEL_EQUATION_BLOCK_HH #define MODEL_EQUATION_BLOCK_HH
#include "DataTree.hh" #include "DataTree.hh"
#include "DynamicModel.hh"
#include "Statement.hh" #include "Statement.hh"
#include "StaticModel.hh" #include "StaticModel.hh"
#include "DynamicModel.hh"
#include "WarningConsolidation.hh" #include "WarningConsolidation.hh"
class PlannerObjective : public StaticModel class PlannerObjective : public StaticModel
{ {
public: public:
PlannerObjective(SymbolTable &symbol_table_arg, PlannerObjective(SymbolTable& symbol_table_arg, NumericalConstants& num_constants_arg,
NumericalConstants &num_constants_arg, ExternalFunctionsTable& external_functions_table_arg,
ExternalFunctionsTable &external_functions_table_arg); HeterogeneityTable& heterogeneity_table_arg);
// NB: masks the method with the same name in StaticModel (not in a virtual fashion)
void writeDriverOutput(ostream& output) const;
protected: protected:
string string
modelClassName() const override modelClassName() const override
...@@ -46,9 +49,9 @@ private: ...@@ -46,9 +49,9 @@ private:
class OrigRamseyDynamicModel : public DynamicModel class OrigRamseyDynamicModel : public DynamicModel
{ {
public: public:
OrigRamseyDynamicModel(SymbolTable &symbol_table_arg, OrigRamseyDynamicModel(SymbolTable& symbol_table_arg, NumericalConstants& num_constants_arg,
NumericalConstants &num_constants_arg,
ExternalFunctionsTable& external_functions_table_arg, ExternalFunctionsTable& external_functions_table_arg,
HeterogeneityTable& heterogeneity_table_arg,
TrendComponentModelTable& trend_component_model_table_arg, TrendComponentModelTable& trend_component_model_table_arg,
VarModelTable& var_model_table_arg); VarModelTable& var_model_table_arg);
OrigRamseyDynamicModel& operator=(const DynamicModel& m); OrigRamseyDynamicModel& operator=(const DynamicModel& m);
...@@ -59,21 +62,33 @@ protected: ...@@ -59,21 +62,33 @@ protected:
{ {
return "original Ramsey model"; return "original Ramsey model";
} }
int
getJacobianCol(int deriv_id, [[maybe_unused]] bool sparse) const override
{
/* Override the DynamicModel method by returning a dummy Jacobian column number.
The override is necessary because the method from DynamicModel fails with
endos with lag/lead greater than 1 or exos with a lag/lead, while substitutions
are by definition not done for an original model.
In particular, this fixes dynare#1960 (equation derivatives are computed for models declared
as linear, to check whether they are truly linear). */
return deriv_id;
}
}; };
class SteadyStateModel : public DataTree class SteadyStateModel : public DataTree
{ {
private: private:
//! Associates a set of symbol IDs (the variable(s) assigned in a given statement) to an expression (their assigned value) //! Associates a set of symbol IDs (the variable(s) assigned in a given statement) to an
//! expression (their assigned value)
vector<pair<vector<int>, expr_t>> def_table; vector<pair<vector<int>, expr_t>> def_table;
//! Reference to static model (for writing auxiliary equations) //! Reference to static model (for writing auxiliary equations)
const StaticModel& static_model; const StaticModel& static_model;
public: public:
SteadyStateModel(SymbolTable &symbol_table_arg, SteadyStateModel(SymbolTable& symbol_table_arg, NumericalConstants& num_constants_arg,
NumericalConstants &num_constants_arg,
ExternalFunctionsTable& external_functions_table_arg, ExternalFunctionsTable& external_functions_table_arg,
HeterogeneityTable& heterogeneity_table_arg,
const StaticModel& static_model_arg); const StaticModel& static_model_arg);
SteadyStateModel(const SteadyStateModel& m); SteadyStateModel(const SteadyStateModel& m);
...@@ -85,7 +100,8 @@ public: ...@@ -85,7 +100,8 @@ public:
void addMultipleDefinitions(const vector<int>& symb_ids, expr_t expr); void addMultipleDefinitions(const vector<int>& symb_ids, expr_t expr);
//! Checks that definitions are in a recursive order, and that no variable is declared twice //! Checks that definitions are in a recursive order, and that no variable is declared twice
/*! /*!
\param[in] ramsey_model Is there a Ramsey model in the MOD file? If yes, then disable the check on the recursivity of the declarations \param[in] ramsey_model Is there a Ramsey model in the MOD file? If yes, then disable the check
on the recursivity of the declarations
*/ */
void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) const; void checkPass(ModFileStructure& mod_file_struct, WarningConsolidation& warnings) const;
//! Write the steady state file //! Write the steady state file
...@@ -99,12 +115,14 @@ public: ...@@ -99,12 +115,14 @@ public:
class Epilogue : public DynamicModel class Epilogue : public DynamicModel
{ {
private: private:
//! Associates a symbol ID (the variable assigned in a given statement) to an expression (its assigned value) //! Associates a symbol ID (the variable assigned in a given statement) to an expression (its
//! assigned value)
vector<pair<int, expr_t>> dynamic_def_table, static_def_table; vector<pair<int, expr_t>> dynamic_def_table, static_def_table;
public: public:
Epilogue(SymbolTable &symbol_table_arg, Epilogue(SymbolTable& symbol_table_arg, NumericalConstants& num_constants_arg,
NumericalConstants &num_constants_arg,
ExternalFunctionsTable& external_functions_table_arg, ExternalFunctionsTable& external_functions_table_arg,
HeterogeneityTable& heterogeneity_table_arg,
TrendComponentModelTable& trend_component_model_table_arg, TrendComponentModelTable& trend_component_model_table_arg,
VarModelTable& var_model_table_arg); VarModelTable& var_model_table_arg);
......
/* /*
* Copyright © 2003-2023 Dynare Team * Copyright © 2003-2025 Dynare Team
* *
* This file is part of Dynare. * This file is part of Dynare.
* *
...@@ -33,9 +33,10 @@ ...@@ -33,9 +33,10 @@
# include <mach-o/dyld.h> # include <mach-o/dyld.h>
#endif #endif
#include <algorithm>
#include <numeric>
#include <regex> #include <regex>
#include <utility> #include <utility>
#include <algorithm>
/* NB: The workers must be listed *after* all the other static variables /* NB: The workers must be listed *after* all the other static variables
related to MEX compilation, so that when the preprocessor exits, the workers related to MEX compilation, so that when the preprocessor exits, the workers
...@@ -44,22 +45,30 @@ ...@@ -44,22 +45,30 @@
condition_variable_any ModelTree::mex_compilation_cv; condition_variable_any ModelTree::mex_compilation_cv;
mutex ModelTree::mex_compilation_mut; mutex ModelTree::mex_compilation_mut;
vector<tuple<filesystem::path, set<filesystem::path>, string>> ModelTree::mex_compilation_queue; vector<tuple<filesystem::path, set<filesystem::path>, string>> ModelTree::mex_compilation_queue;
set<filesystem::path> ModelTree::mex_compilation_ongoing, ModelTree::mex_compilation_done, ModelTree::mex_compilation_failed; set<filesystem::path> ModelTree::mex_compilation_ongoing, ModelTree::mex_compilation_done,
ModelTree::mex_compilation_failed;
vector<jthread> ModelTree::mex_compilation_workers; vector<jthread> ModelTree::mex_compilation_workers;
void void
ModelTree::copyHelper(const ModelTree& m) ModelTree::copyHelper(const ModelTree& m)
{ {
auto f = [this](expr_t e) { return e->clone(*this); }; auto f = [this](expr_t e) { return e ? e->clone(*this) : nullptr; };
// Equations // Equations
for (const auto& it : m.equations) for (const auto& it : m.equations)
equations.push_back(dynamic_cast<BinaryOpNode*>(f(it))); equations.push_back(dynamic_cast<BinaryOpNode*>(f(it)));
for (const auto& it : m.aux_equations) for (const auto& it : m.aux_equations)
aux_equations.push_back(dynamic_cast<BinaryOpNode*>(f(it))); aux_equations.push_back(dynamic_cast<BinaryOpNode*>(f(it)));
for (const auto& it : m.complementarity_conditions)
auto convert_deriv_map = [f](const map<vector<int>, expr_t> &dm) if (it)
{ {
const auto& [symb_id, lb, ub] = *it;
complementarity_conditions.emplace_back(in_place, symb_id, f(lb), f(ub));
}
else
complementarity_conditions.emplace_back(nullopt);
auto convert_deriv_map = [f](const map<vector<int>, expr_t>& dm) {
map<vector<int>, expr_t> dm2; map<vector<int>, expr_t> dm2;
for (const auto& it : dm) for (const auto& it : dm)
dm2.emplace(it.first, f(it.second)); dm2.emplace(it.first, f(it.second));
...@@ -74,8 +83,7 @@ ModelTree::copyHelper(const ModelTree &m) ...@@ -74,8 +83,7 @@ ModelTree::copyHelper(const ModelTree &m)
for (const auto& it : m.jacobian_sparse_column_major_order) for (const auto& it : m.jacobian_sparse_column_major_order)
jacobian_sparse_column_major_order.emplace(it.first, f(it.second)); jacobian_sparse_column_major_order.emplace(it.first, f(it.second));
auto convert_temporary_terms_t = [f](const temporary_terms_t &tt) auto convert_temporary_terms_t = [f](const temporary_terms_t& tt) {
{
temporary_terms_t tt2; temporary_terms_t tt2;
for (const auto& it : tt) for (const auto& it : tt)
tt2.insert(f(it)); tt2.insert(f(it));
...@@ -99,7 +107,8 @@ ModelTree::copyHelper(const ModelTree &m) ...@@ -99,7 +107,8 @@ ModelTree::copyHelper(const ModelTree &m)
nonstationary_symbols_map.emplace(it.first, pair {it.second.first, f(it.second.second)}); nonstationary_symbols_map.emplace(it.first, pair {it.second.first, f(it.second.second)});
for (const auto& it : m.equation_type_and_normalized_equation) for (const auto& it : m.equation_type_and_normalized_equation)
equation_type_and_normalized_equation.emplace_back(it.first, dynamic_cast<BinaryOpNode *>(f(it.second))); equation_type_and_normalized_equation.emplace_back(it.first,
dynamic_cast<BinaryOpNode*>(f(it.second)));
for (const auto& it : m.blocks_derivatives) for (const auto& it : m.blocks_derivatives)
{ {
...@@ -109,8 +118,7 @@ ModelTree::copyHelper(const ModelTree &m) ...@@ -109,8 +118,7 @@ ModelTree::copyHelper(const ModelTree &m)
blocks_derivatives.push_back(v); blocks_derivatives.push_back(v);
} }
auto convert_vector_tt = [f](vector<temporary_terms_t> vtt) auto convert_vector_tt = [f](const vector<temporary_terms_t>& vtt) {
{
vector<temporary_terms_t> vtt2; vector<temporary_terms_t> vtt2;
for (const auto& tt : vtt) for (const auto& tt : vtt)
{ {
...@@ -135,17 +143,18 @@ ModelTree::copyHelper(const ModelTree &m) ...@@ -135,17 +143,18 @@ ModelTree::copyHelper(const ModelTree &m)
} }
} }
ModelTree::ModelTree(SymbolTable &symbol_table_arg, ModelTree::ModelTree(SymbolTable& symbol_table_arg, NumericalConstants& num_constants_arg,
NumericalConstants &num_constants_arg,
ExternalFunctionsTable& external_functions_table_arg, ExternalFunctionsTable& external_functions_table_arg,
bool is_dynamic_arg) : HeterogeneityTable& heterogeneity_table_arg, bool is_dynamic_arg) :
DataTree{symbol_table_arg, num_constants_arg, external_functions_table_arg, is_dynamic_arg}, DataTree {symbol_table_arg, num_constants_arg, external_functions_table_arg,
heterogeneity_table_arg, is_dynamic_arg},
derivatives(4), derivatives(4),
NNZDerivatives(4, 0), NNZDerivatives(4, 0),
temporary_terms_derivatives(4) temporary_terms_derivatives(4)
{ {
// Ensure that elements accessed by writeParamsDerivativesFileHelper() exist // Ensure that elements accessed by writeParamsDerivativesFileHelper() exist
for (const auto &ord : {pair{0, 1}, pair{1, 1}, pair{0, 2}, pair{1, 2}, pair{2, 1}, pair{3, 1}}) for (const auto& ord :
{pair {0, 1}, pair {1, 1}, pair {0, 2}, pair {1, 2}, pair {2, 1}, pair {3, 1}})
params_derivatives.try_emplace(ord); params_derivatives.try_emplace(ord);
} }
...@@ -186,6 +195,8 @@ ModelTree::operator=(const ModelTree &m) ...@@ -186,6 +195,8 @@ ModelTree::operator=(const ModelTree &m)
equations_lineno = m.equations_lineno; equations_lineno = m.equations_lineno;
aux_equations.clear(); aux_equations.clear();
equation_tags = m.equation_tags; equation_tags = m.equation_tags;
complementarity_conditions.clear();
computed_derivs_order = m.computed_derivs_order; computed_derivs_order = m.computed_derivs_order;
NNZDerivatives = m.NNZDerivatives; NNZDerivatives = m.NNZDerivatives;
...@@ -257,13 +268,17 @@ ModelTree::computeNormalization(const jacob_map_t &contemporaneous_jacobian) ...@@ -257,13 +268,17 @@ ModelTree::computeNormalization(const jacob_map_t &contemporaneous_jacobian)
edmonds_maximum_cardinality_matching(g, &mate_map[0]); edmonds_maximum_cardinality_matching(g, &mate_map[0]);
// Check if all variables are normalized // Check if all variables are normalized
if (auto it = find(mate_map.begin(), mate_map.begin() + n, boost::graph_traits<BipartiteGraph>::null_vertex()); if (auto it = find(mate_map.begin(), mate_map.begin() + n,
boost::graph_traits<BipartiteGraph>::null_vertex());
it != mate_map.begin() + n) it != mate_map.begin() + n)
throw ModelNormalizationFailed {symbol_table.getName(symbol_table.getID(SymbolType::endogenous, it - mate_map.begin())) }; throw ModelNormalizationFailed {
symbol_table.getName(symbol_table.getID(SymbolType::endogenous, it - mate_map.begin()))};
// Create the resulting map, by copying the n first elements of mate_map, and substracting n to them // Create the resulting map, by copying the n first elements of mate_map, and substracting n to
// them
endo2eq.resize(equations.size()); endo2eq.resize(equations.size());
transform(mate_map.begin(), mate_map.begin() + n, endo2eq.begin(), [=](vertex_descriptor_t i) { return i-n; }); ranges::transform(mate_map.begin(), mate_map.begin() + n, endo2eq.begin(),
[=](vertex_descriptor_t i) { return i - n; });
} }
bool bool
...@@ -275,7 +290,9 @@ ModelTree::computeNonSingularNormalization(const eval_context_t &eval_context) ...@@ -275,7 +290,9 @@ ModelTree::computeNonSingularNormalization(const eval_context_t &eval_context)
not have as many equations as variables. */ not have as many equations as variables. */
if (n != symbol_table.endo_nbr()) if (n != symbol_table.endo_nbr())
{ {
cout << "The " << modelClassName() << " cannot be normalized, since it does not have as many equations as variables." << endl; cout << "The " << modelClassName()
<< " cannot be normalized, since it does not have as many equations as variables."
<< endl;
return false; return false;
} }
...@@ -296,7 +313,12 @@ ModelTree::computeNonSingularNormalization(const eval_context_t &eval_context) ...@@ -296,7 +313,12 @@ ModelTree::computeNonSingularNormalization(const eval_context_t &eval_context)
} }
catch (ModelNormalizationFailed& e) catch (ModelNormalizationFailed& e)
{ {
cerr << "WARNING: All equations are written so that a single contemporaneous endogenous variable appears on the left-hand side. This suggests a natural normalization of the model. However, variable " << e.unmatched_endo << " could not be matched with an equation. Check whether this is desired." << endl; cerr << "WARNING: All equations are written so that a single contemporaneous "
"endogenous variable appears on the left-hand side. This suggests a natural "
"normalization of the model. However, variable "
<< e.unmatched_endo
<< " could not be matched with an equation. Check whether this is desired."
<< endl;
} }
} }
...@@ -362,8 +384,8 @@ ModelTree::computeNonSingularNormalization(const eval_context_t &eval_context) ...@@ -362,8 +384,8 @@ ModelTree::computeNonSingularNormalization(const eval_context_t &eval_context)
} }
catch (ModelNormalizationFailed& e) catch (ModelNormalizationFailed& e)
{ {
cerr << "Could not normalize the " << modelClassName() << ". Variable " cerr << "Could not normalize the " << modelClassName() << ". Variable " << e.unmatched_endo
<< e.unmatched_endo << " is not in the maximum cardinality matching." << endl; << " is not in the maximum cardinality matching." << endl;
} }
return false; return false;
...@@ -381,11 +403,10 @@ ModelTree::evaluateAndReduceJacobian(const eval_context_t &eval_context) const ...@@ -381,11 +403,10 @@ ModelTree::evaluateAndReduceJacobian(const eval_context_t &eval_context) const
int eq = indices[0]; int eq = indices[0];
int var {getTypeSpecificIDByDerivID(deriv_id)}; int var {getTypeSpecificIDByDerivID(deriv_id)};
int lag = getLagByDerivID(deriv_id); int lag = getLagByDerivID(deriv_id);
double val { [&] double val {[&] {
{
try try
{ {
return d1->eval(eval_context); return d1->eval(eval_context); // NOLINT(clang-analyzer-core.NullDereference)
} }
catch (ExprNode::EvalExternalFunctionException& e) catch (ExprNode::EvalExternalFunctionException& e)
{ {
...@@ -509,7 +530,8 @@ ModelTree::computePrologueAndEpilogue() ...@@ -509,7 +530,8 @@ ModelTree::computePrologueAndEpilogue()
} }
void void
ModelTree::equationTypeDetermination(const map<tuple<int, int, int>, expr_t> &first_order_endo_derivatives) ModelTree::equationTypeDetermination(
const map<tuple<int, int, int>, expr_t>& first_order_endo_derivatives)
{ {
equation_type_and_normalized_equation.clear(); equation_type_and_normalized_equation.clear();
equation_type_and_normalized_equation.resize(equations.size()); equation_type_and_normalized_equation.resize(equations.size());
...@@ -536,7 +558,8 @@ ModelTree::equationTypeDetermination(const map<tuple<int, int, int>, expr_t> &fi ...@@ -536,7 +558,8 @@ ModelTree::equationTypeDetermination(const map<tuple<int, int, int>, expr_t> &fi
try try
{ {
normalized_eq = equations[eq]->normalizeEquation(symbol_table.getID(SymbolType::endogenous, var), 0); normalized_eq = equations[eq]->normalizeEquation(
symbol_table.getID(SymbolType::endogenous, var), 0);
if ((getMFS() == 2 && variable_not_in_derivative) || getMFS() == 3) if ((getMFS() == 2 && variable_not_in_derivative) || getMFS() == 3)
Equation_Simulation_Type = EquationType::evaluateRenormalized; Equation_Simulation_Type = EquationType::evaluateRenormalized;
} }
...@@ -567,7 +590,8 @@ ModelTree::computeDynamicStructureOfBlock(int blk) ...@@ -567,7 +590,8 @@ ModelTree::computeDynamicStructureOfBlock(int blk)
{ {
blocks[blk].max_endo_lag = max(blocks[blk].max_endo_lag, -lag); blocks[blk].max_endo_lag = max(blocks[blk].max_endo_lag, -lag);
blocks[blk].max_endo_lead = max(blocks[blk].max_endo_lead, lag); blocks[blk].max_endo_lead = max(blocks[blk].max_endo_lead, lag);
auto &[max_endo_lag, max_endo_lead] = max_endo_lag_lead[getBlockInitialVariableID(blk, endo)]; auto& [max_endo_lag, max_endo_lead]
= max_endo_lag_lead[getBlockInitialVariableID(blk, endo)];
max_endo_lag = max(max_endo_lag, -lag); max_endo_lag = max(max_endo_lag, -lag);
max_endo_lead = max(max_endo_lead, lag); max_endo_lead = max(max_endo_lead, lag);
} }
...@@ -597,11 +621,11 @@ ModelTree::computeSimulationTypeOfBlock(int blk) ...@@ -597,11 +621,11 @@ ModelTree::computeSimulationTypeOfBlock(int blk)
bool can_eval = (getBlockEquationType(blk, 0) == EquationType::evaluate bool can_eval = (getBlockEquationType(blk, 0) == EquationType::evaluate
|| getBlockEquationType(blk, 0) == EquationType::evaluateRenormalized); || getBlockEquationType(blk, 0) == EquationType::evaluateRenormalized);
if (blocks[blk].max_endo_lead > 0) if (blocks[blk].max_endo_lead > 0)
type = can_eval ? BlockSimulationType::evaluateBackward : type = can_eval ? BlockSimulationType::evaluateBackward
BlockSimulationType::solveBackwardSimple; : BlockSimulationType::solveBackwardSimple;
else else
type = can_eval ? BlockSimulationType::evaluateForward : type = can_eval ? BlockSimulationType::evaluateForward
BlockSimulationType::solveForwardSimple; : BlockSimulationType::solveForwardSimple;
} }
} }
...@@ -642,10 +666,8 @@ ModelTree::computeBlockDecomposition(int prologue, int epilogue) ...@@ -642,10 +666,8 @@ ModelTree::computeBlockDecomposition(int prologue, int epilogue)
for (const auto& [key, value] : computeSymbolicJacobian(time_recursive_block_decomposition)) for (const auto& [key, value] : computeSymbolicJacobian(time_recursive_block_decomposition))
{ {
auto [eq, endo] = key; auto [eq, endo] = key;
if (eq_idx_orig2block[eq] >= prologue if (eq_idx_orig2block[eq] >= prologue && eq_idx_orig2block[eq] < nb_var - epilogue
&& eq_idx_orig2block[eq] < nb_var - epilogue && endo_idx_orig2block[endo] >= prologue && endo_idx_orig2block[endo] < nb_var - epilogue
&& endo_idx_orig2block[endo] >= prologue
&& endo_idx_orig2block[endo] < nb_var - epilogue
&& eq != endo2eq[endo]) && eq != endo2eq[endo])
add_edge(vertex(eq_idx_orig2block[endo2eq[endo]] - prologue, G), add_edge(vertex(eq_idx_orig2block[endo2eq[endo]] - prologue, G),
vertex(eq_idx_orig2block[eq] - prologue, G), G); vertex(eq_idx_orig2block[eq] - prologue, G), G);
...@@ -698,20 +720,23 @@ ModelTree::computeBlockDecomposition(int prologue, int epilogue) ...@@ -698,20 +720,23 @@ ModelTree::computeBlockDecomposition(int prologue, int epilogue)
related to lead/lag variables. This forces those vertices to belong to the related to lead/lag variables. This forces those vertices to belong to the
feedback set */ feedback set */
for (int i = 0; i < nb_simvars; i++) for (int i = 0; i < nb_simvars; i++)
if (equation_type_and_normalized_equation[eq_idx_block2orig[i+prologue]].first == EquationType::solve if (equation_type_and_normalized_equation[eq_idx_block2orig[i + prologue]].first
|| (!time_recursive_block_decomposition && == EquationType::solve
(variable_lag_lead[endo_idx_block2orig[i+prologue]].first > 0 || (!time_recursive_block_decomposition
&& (variable_lag_lead[endo_idx_block2orig[i + prologue]].first > 0
|| variable_lag_lead[endo_idx_block2orig[i + prologue]].second > 0 || variable_lag_lead[endo_idx_block2orig[i + prologue]].second > 0
|| equation_lag_lead[eq_idx_block2orig[i + prologue]].first > 0 || equation_lag_lead[eq_idx_block2orig[i + prologue]].first > 0
|| equation_lag_lead[eq_idx_block2orig[i + prologue]].second > 0)) || equation_lag_lead[eq_idx_block2orig[i + prologue]].second > 0))
|| getMFS() == 0) || getMFS() == 0)
add_edge(vertex(i, G), vertex(i, G), G); add_edge(vertex(i, G), vertex(i, G), G);
const vector<int> old_eq_idx_block2orig(eq_idx_block2orig), old_endo_idx_block2orig(endo_idx_block2orig); const vector<int> old_eq_idx_block2orig(eq_idx_block2orig),
old_endo_idx_block2orig(endo_idx_block2orig);
int ordidx = prologue; int ordidx = prologue;
for (int blk = prologue; blk < prologue + num_simblocks; blk++) for (int blk = prologue; blk < prologue + num_simblocks; blk++)
{ {
blocks[blk].first_equation = (blk == 0 ? 0 : blocks[blk-1].first_equation + blocks[blk-1].size); blocks[blk].first_equation
= (blk == 0 ? 0 : blocks[blk - 1].first_equation + blocks[blk - 1].size);
auto subG = G.extractSubgraph(simblock2simvars[blk - prologue]); auto subG = G.extractSubgraph(simblock2simvars[blk - prologue]);
auto feed_back_vertices = subG.minimalSetOfFeedbackVertices(); auto feed_back_vertices = subG.minimalSetOfFeedbackVertices();
blocks[blk].mfs_size = feed_back_vertices.size(); blocks[blk].mfs_size = feed_back_vertices.size();
...@@ -761,8 +786,7 @@ ModelTree::printBlockDecomposition() const ...@@ -761,8 +786,7 @@ ModelTree::printBlockDecomposition() const
|| simulation_type == BlockSimulationType::solveTwoBoundariesComplete) || simulation_type == BlockSimulationType::solveTwoBoundariesComplete)
{ {
Nb_SimulBlocks++; Nb_SimulBlocks++;
if (int size = blocks[block].size; if (int size = blocks[block].size; size > largest_block)
size > largest_block)
{ {
largest_block = size; largest_block = size;
Nb_feedback_variable = blocks[block].mfs_size; Nb_feedback_variable = blocks[block].mfs_size;
...@@ -771,9 +795,11 @@ ModelTree::printBlockDecomposition() const ...@@ -771,9 +795,11 @@ ModelTree::printBlockDecomposition() const
int Nb_RecursBlocks = Nb_TotalBlocks - Nb_SimulBlocks; int Nb_RecursBlocks = Nb_TotalBlocks - Nb_SimulBlocks;
cout << Nb_TotalBlocks << " block(s) found:" << endl cout << Nb_TotalBlocks << " block(s) found:" << endl
<< " " << Nb_RecursBlocks << " recursive block(s) and " << Nb_SimulBlocks << " simultaneous block(s)." << endl << " " << Nb_RecursBlocks << " recursive block(s) and " << Nb_SimulBlocks
<< " simultaneous block(s)." << endl
<< " the largest simultaneous block has " << largest_block << " equation(s)" << endl << " the largest simultaneous block has " << largest_block << " equation(s)" << endl
<< " and " << Nb_feedback_variable << " feedback variable(s)." << endl; << " and " << Nb_feedback_variable
<< " feedback variable(s)." << endl;
} }
void void
...@@ -796,11 +822,9 @@ ModelTree::reduceBlockDecomposition() ...@@ -796,11 +822,9 @@ ModelTree::reduceBlockDecomposition()
} }
if ((blocks[blk - 1].simulation_type == BlockSimulationType::evaluateForward if ((blocks[blk - 1].simulation_type == BlockSimulationType::evaluateForward
&& blocks[blk].simulation_type == BlockSimulationType::evaluateForward && blocks[blk].simulation_type == BlockSimulationType::evaluateForward && !is_lead)
&& !is_lead)
|| (blocks[blk - 1].simulation_type == BlockSimulationType::evaluateBackward || (blocks[blk - 1].simulation_type == BlockSimulationType::evaluateBackward
&& blocks[blk].simulation_type == BlockSimulationType::evaluateBackward && blocks[blk].simulation_type == BlockSimulationType::evaluateBackward && !is_lag))
&& !is_lag))
{ {
// Merge the current block into the previous one // Merge the current block into the previous one
blocks[blk - 1].size++; blocks[blk - 1].size++;
...@@ -871,7 +895,7 @@ ModelTree::determineLinearBlocks() ...@@ -871,7 +895,7 @@ ModelTree::determineLinearBlocks()
int int
ModelTree::equation_number() const ModelTree::equation_number() const
{ {
return (equations.size()); return equations.size();
} }
void void
...@@ -898,8 +922,10 @@ ModelTree::computeDerivatives(int order, const set<int> &vars) ...@@ -898,8 +922,10 @@ ModelTree::computeDerivatives(int order, const set<int> &vars)
// Compute the sparse representation of the Jacobian // Compute the sparse representation of the Jacobian
for (const auto& [indices, d1] : derivatives[1]) for (const auto& [indices, d1] : derivatives[1])
jacobian_sparse_column_major_order.try_emplace({indices[0], getJacobianCol(indices[1], true)}, d1); jacobian_sparse_column_major_order.try_emplace({indices[0], getJacobianCol(indices[1], true)},
jacobian_sparse_colptr = computeCSCColPtr(jacobian_sparse_column_major_order, getJacobianColsNbr(true)); d1);
jacobian_sparse_colptr
= computeCSCColPtr(jacobian_sparse_column_major_order, getJacobianColsNbr(true));
// Higher-order derivatives // Higher-order derivatives
for (int o = 2; o <= order; o++) for (int o = 2; o <= order; o++)
...@@ -930,8 +956,7 @@ ModelTree::computeTemporaryTerms(bool is_matlab, bool no_tmp_terms) ...@@ -930,8 +956,7 @@ ModelTree::computeTemporaryTerms(bool is_matlab, bool no_tmp_terms)
{ {
/* Ensure that we don’t have any model-local variable in the model at this /* Ensure that we don’t have any model-local variable in the model at this
point (we used to treat them as temporary terms) */ point (we used to treat them as temporary terms) */
assert([&] assert([&] {
{
set<int> used_local_vars; set<int> used_local_vars;
for (auto& equation : equations) for (auto& equation : equations)
equation->collectVariables(SymbolType::modelLocalVariable, used_local_vars); equation->collectVariables(SymbolType::modelLocalVariable, used_local_vars);
...@@ -943,32 +968,26 @@ ModelTree::computeTemporaryTerms(bool is_matlab, bool no_tmp_terms) ...@@ -943,32 +968,26 @@ ModelTree::computeTemporaryTerms(bool is_matlab, bool no_tmp_terms)
unordered_map<expr_t, pair<int, pair<int, int>>> reference_count; unordered_map<expr_t, pair<int, pair<int, int>>> reference_count;
for (auto& equation : equations) for (auto& equation : equations)
equation->computeTemporaryTerms({ 0, 0 }, equation->computeTemporaryTerms({0, 0}, temp_terms_map, reference_count, is_matlab);
temp_terms_map,
reference_count,
is_matlab);
for (int order = 1; order < static_cast<int>(derivatives.size()); order++) for (int order = 1; order < static_cast<int>(derivatives.size()); order++)
for (const auto& it : derivatives[order]) for (const auto& it : derivatives[order])
it.second->computeTemporaryTerms({ order, 0 }, it.second->computeTemporaryTerms({order, 0}, temp_terms_map, reference_count, is_matlab);
temp_terms_map,
reference_count,
is_matlab);
/* If the user has specified the notmpterms option, clear all temporary /* If the user has specified the notmpterms option, clear all temporary
terms, except those that correspond to external functions (since they are terms, except those that correspond to external functions (since they are
not optional) */ not optional) */
if (no_tmp_terms) if (no_tmp_terms)
for (auto& it : temp_terms_map) for (auto& it : temp_terms_map)
erase_if(it.second, erase_if(it.second, [](expr_t e) { return !dynamic_cast<AbstractExternalFunctionNode*>(e); });
[](expr_t e) { return !dynamic_cast<AbstractExternalFunctionNode *>(e); });
// Fill the structures // Fill the structures
temporary_terms_derivatives.clear(); temporary_terms_derivatives.clear();
temporary_terms_derivatives.resize(derivatives.size()); temporary_terms_derivatives.resize(derivatives.size());
for (int order = 0; order < static_cast<int>(derivatives.size()); order++) for (int order = 0; order < static_cast<int>(derivatives.size()); order++)
copy(temp_terms_map[{ order, 0 }].begin(), temp_terms_map[{ order, 0 }].end(), ranges::copy(temp_terms_map[{order, 0}],
inserter(temporary_terms_derivatives.at(order), temporary_terms_derivatives.at(order).begin())); inserter(temporary_terms_derivatives.at(order),
temporary_terms_derivatives.at(order).begin()));
// Compute indices in MATLAB/Julia vector // Compute indices in MATLAB/Julia vector
for (int order {0}, idx {0}; order < static_cast<int>(derivatives.size()); order++) for (int order {0}, idx {0}; order < static_cast<int>(derivatives.size()); order++)
...@@ -998,9 +1017,11 @@ ModelTree::computeBlockTemporaryTerms(bool no_tmp_terms) ...@@ -998,9 +1017,11 @@ ModelTree::computeBlockTemporaryTerms(bool no_tmp_terms)
|| blocks[blk].simulation_type == BlockSimulationType::evaluateForward || blocks[blk].simulation_type == BlockSimulationType::evaluateForward
|| eq < blocks[blk].getRecursiveSize()) || eq < blocks[blk].getRecursiveSize())
&& isBlockEquationRenormalized(blk, eq)) && isBlockEquationRenormalized(blk, eq))
getBlockEquationRenormalizedExpr(blk, eq)->computeBlockTemporaryTerms(blk, eq, temp_terms, reference_count); getBlockEquationRenormalizedExpr(blk, eq)->computeBlockTemporaryTerms(
blk, eq, temp_terms, reference_count);
else else
getBlockEquationExpr(blk, eq)->computeBlockTemporaryTerms(blk, eq, temp_terms, reference_count); getBlockEquationExpr(blk, eq)->computeBlockTemporaryTerms(blk, eq, temp_terms,
reference_count);
} }
for (const auto& [ignore, d] : blocks_derivatives[blk]) for (const auto& [ignore, d] : blocks_derivatives[blk])
d->computeBlockTemporaryTerms(blk, blocks[blk].size, temp_terms, reference_count); d->computeBlockTemporaryTerms(blk, blocks[blk].size, temp_terms, reference_count);
...@@ -1019,30 +1040,29 @@ ModelTree::computeBlockTemporaryTerms(bool no_tmp_terms) ...@@ -1019,30 +1040,29 @@ ModelTree::computeBlockTemporaryTerms(bool no_tmp_terms)
{ {
blocks_temporary_terms.at(blk).resize(temp_terms.at(blk).size()); blocks_temporary_terms.at(blk).resize(temp_terms.at(blk).size());
for (size_t i {0}; i < temp_terms.at(blk).size(); i++) for (size_t i {0}; i < temp_terms.at(blk).size(); i++)
copy(temp_terms.at(blk).at(i).begin(), temp_terms.at(blk).at(i).end(), inserter(blocks_temporary_terms.at(blk).at(i), blocks_temporary_terms.at(blk).at(i).begin())); ranges::copy(temp_terms.at(blk).at(i),
inserter(blocks_temporary_terms.at(blk).at(i),
blocks_temporary_terms.at(blk).at(i).begin()));
} }
// Compute indices in the temporary terms vector // Compute indices in the temporary terms vector
blocks_temporary_terms_idxs.clear(); blocks_temporary_terms_idxs.clear();
for (int idx{0}; for (int idx {0}; auto& blk_tt : blocks_temporary_terms)
auto &blk_tt : blocks_temporary_terms)
for (auto& eq_tt : blk_tt) for (auto& eq_tt : blk_tt)
for (auto tt : eq_tt) for (auto tt : eq_tt)
blocks_temporary_terms_idxs[tt] = idx++; blocks_temporary_terms_idxs[tt] = idx++;
} }
void void
ModelTree::writeJsonTemporaryTerms(const temporary_terms_t &tt, ModelTree::writeJsonTemporaryTerms(const temporary_terms_t& tt, temporary_terms_t& temp_term_union,
temporary_terms_t &temp_term_union, ostream& output, deriv_node_temp_terms_t& tef_terms,
ostream &output, const string& concat) const
deriv_node_temp_terms_t &tef_terms, const string &concat) const
{ {
// Local var used to keep track of temp nodes already written // Local var used to keep track of temp nodes already written
temporary_terms_t tt2 = temp_term_union; temporary_terms_t tt2 = temp_term_union;
output << R"("external_functions_temporary_terms_)" << concat << R"(": [)"; output << R"("external_functions_temporary_terms_)" << concat << R"(": [)";
for (bool printed_term{false}; for (bool printed_term {false}; auto it : tt)
auto it : tt)
{ {
if (dynamic_cast<AbstractExternalFunctionNode*>(it)) if (dynamic_cast<AbstractExternalFunctionNode*>(it))
{ {
...@@ -1050,8 +1070,7 @@ ModelTree::writeJsonTemporaryTerms(const temporary_terms_t &tt, ...@@ -1050,8 +1070,7 @@ ModelTree::writeJsonTemporaryTerms(const temporary_terms_t &tt,
output << ", "; output << ", ";
vector<string> efout; vector<string> efout;
it->writeJsonExternalFunctionOutput(efout, tt2, tef_terms); it->writeJsonExternalFunctionOutput(efout, tt2, tef_terms);
for (bool printed_efout{false}; for (bool printed_efout {false}; auto& it : efout)
auto &it : efout)
{ {
if (exchange(printed_efout, true)) if (exchange(printed_efout, true))
output << ", "; output << ", ";
...@@ -1063,8 +1082,7 @@ ModelTree::writeJsonTemporaryTerms(const temporary_terms_t &tt, ...@@ -1063,8 +1082,7 @@ ModelTree::writeJsonTemporaryTerms(const temporary_terms_t &tt,
output << "]" output << "]"
<< R"(, "temporary_terms_)" << concat << R"(": [)"; << R"(, "temporary_terms_)" << concat << R"(": [)";
for (bool printed_term{false}; for (bool printed_term {false}; const auto& it : tt)
const auto &it : tt)
{ {
if (exchange(printed_term, true)) if (exchange(printed_term, true))
output << ", "; output << ", ";
...@@ -1081,7 +1099,8 @@ ModelTree::writeJsonTemporaryTerms(const temporary_terms_t &tt, ...@@ -1081,7 +1099,8 @@ ModelTree::writeJsonTemporaryTerms(const temporary_terms_t &tt,
} }
void void
ModelTree::fixNestedParenthesis(ostringstream &output, map<string, string> &tmp_paren_vars, bool &message_printed) const ModelTree::fixNestedParenthesis(ostringstream& output, map<string, string>& tmp_paren_vars,
bool& message_printed) const
{ {
string str = output.str(); string str = output.str();
if (!testNestedParenthesis(str)) if (!testNestedParenthesis(str))
...@@ -1112,11 +1131,23 @@ ModelTree::fixNestedParenthesis(ostringstream &output, map<string, string> &tmp_ ...@@ -1112,11 +1131,23 @@ ModelTree::fixNestedParenthesis(ostringstream &output, map<string, string> &tmp_
{ {
if (!message_printed) if (!message_printed)
{ {
cerr << "Warning: A .m file created by Dynare will have more than 32 nested parenthesis. MATLAB cannot support this. " << endl cerr << "Warning: A .m file created by Dynare will have more than 32 nested "
<< " We are going to modify, albeit inefficiently, this output to have fewer than 32 nested parenthesis. " << endl "parenthesis. MATLAB cannot support this. "
<< " It would hence behoove you to use the use_dll option of the model block to circumnavigate this problem." << endl << endl
<< " If you have not yet set up a compiler on your system, see the MATLAB documentation for doing so." << endl << " We are going to modify, albeit inefficiently, this output to have "
<< " For Windows, see: https://www.mathworks.com/help/matlab/matlab_external/install-mingw-support-package.html" << endl << endl; "fewer than 32 nested parenthesis. "
<< endl
<< " It would hence behoove you to use the use_dll option of the model "
"block to circumnavigate this problem."
<< endl
<< " If you have not yet set up a compiler on your system, see the "
"MATLAB documentation for doing so."
<< endl
<< " For Windows, see: "
"https://www.mathworks.com/help/matlab/matlab_external/"
"install-mingw-support-package.html"
<< endl
<< endl;
message_printed = true; message_printed = true;
} }
string str1 = str.substr(first_open_paren, matching_paren - first_open_paren + 1); string str1 = str.substr(first_open_paren, matching_paren - first_open_paren + 1);
...@@ -1146,12 +1177,12 @@ ModelTree::fixNestedParenthesis(ostringstream &output, map<string, string> &tmp_ ...@@ -1146,12 +1177,12 @@ ModelTree::fixNestedParenthesis(ostringstream &output, map<string, string> &tmp_
if (open_paren_idx != string::npos && match_paren_idx != string::npos) if (open_paren_idx != string::npos && match_paren_idx != string::npos)
{ {
string val = str1.substr(open_paren_idx, match_paren_idx - open_paren_idx + 1); string val
if (auto it = tmp_paren_vars.find(val); = str1.substr(open_paren_idx, match_paren_idx - open_paren_idx + 1);
it == tmp_paren_vars.end()) if (auto it = tmp_paren_vars.find(val); it == tmp_paren_vars.end())
{ {
varname = "paren32_tmp_var_" + to_string(i1++); varname = "paren32_tmp_var_" + to_string(i1++);
repstr = repstr + varname + " = " + val + ";\n"; repstr += varname + " = " + val + ";\n";
tmp_paren_vars[val] = varname; tmp_paren_vars[val] = varname;
} }
else else
...@@ -1161,16 +1192,15 @@ ModelTree::fixNestedParenthesis(ostringstream &output, map<string, string> &tmp_ ...@@ -1161,16 +1192,15 @@ ModelTree::fixNestedParenthesis(ostringstream &output, map<string, string> &tmp_
} }
} }
} }
if (auto it = tmp_paren_vars.find(str1); if (auto it = tmp_paren_vars.find(str1); it == tmp_paren_vars.end())
it == tmp_paren_vars.end())
{ {
varname = "paren32_tmp_var_" + to_string(i1++); varname = "paren32_tmp_var_" + to_string(i1++);
repstr = repstr + varname + " = " + str1 + ";\n"; repstr += varname + " = " + str1 + ";\n";
} }
else else
varname = it->second; varname = it->second;
str.replace(first_open_paren, matching_paren - first_open_paren + 1, varname); str.replace(first_open_paren, matching_paren - first_open_paren + 1, varname);
size_t insertLoc = str.find_last_of("\n", first_open_paren); size_t insertLoc = str.find_last_of('\n', first_open_paren);
str.insert(insertLoc + 1, repstr); str.insert(insertLoc + 1, repstr);
hit_limit = false; hit_limit = false;
i = -1; i = -1;
...@@ -1183,8 +1213,7 @@ ModelTree::fixNestedParenthesis(ostringstream &output, map<string, string> &tmp_ ...@@ -1183,8 +1213,7 @@ ModelTree::fixNestedParenthesis(ostringstream &output, map<string, string> &tmp_
bool bool
ModelTree::testNestedParenthesis(const string& str) const ModelTree::testNestedParenthesis(const string& str) const
{ {
for (int open{0}; for (int open {0}; char i : str)
char i : str)
{ {
if (i == '(') if (i == '(')
open++; open++;
...@@ -1197,7 +1226,8 @@ ModelTree::testNestedParenthesis(const string &str) const ...@@ -1197,7 +1226,8 @@ ModelTree::testNestedParenthesis(const string &str) const
} }
void void
ModelTree::writeJsonModelLocalVariables(ostream &output, bool write_tef_terms, deriv_node_temp_terms_t &tef_terms) const ModelTree::writeJsonModelLocalVariables(ostream& output, bool write_tef_terms,
deriv_node_temp_terms_t& tef_terms) const
{ {
/* Collect all model local variables appearing in equations, and print only /* Collect all model local variables appearing in equations, and print only
them. Printing unused model local variables can lead to a crash (see them. Printing unused model local variables can lead to a crash (see
...@@ -1208,8 +1238,7 @@ ModelTree::writeJsonModelLocalVariables(ostream &output, bool write_tef_terms, d ...@@ -1208,8 +1238,7 @@ ModelTree::writeJsonModelLocalVariables(ostream &output, bool write_tef_terms, d
equation->collectVariables(SymbolType::modelLocalVariable, used_local_vars); equation->collectVariables(SymbolType::modelLocalVariable, used_local_vars);
output << R"("model_local_variables": [)"; output << R"("model_local_variables": [)";
for (bool printed_something{false}; for (bool printed_something {false}; int id : local_variables_vector)
int id : local_variables_vector)
if (used_local_vars.contains(id)) if (used_local_vars.contains(id))
{ {
if (exchange(printed_something, true)) if (exchange(printed_something, true))
...@@ -1220,8 +1249,7 @@ ModelTree::writeJsonModelLocalVariables(ostream &output, bool write_tef_terms, d ...@@ -1220,8 +1249,7 @@ ModelTree::writeJsonModelLocalVariables(ostream &output, bool write_tef_terms, d
{ {
vector<string> efout; vector<string> efout;
value->writeJsonExternalFunctionOutput(efout, {}, tef_terms); value->writeJsonExternalFunctionOutput(efout, {}, tef_terms);
for (bool printed_efout{false}; for (bool printed_efout {false}; auto& it : efout)
auto &it : efout)
{ {
if (exchange(printed_efout, true)) if (exchange(printed_efout, true))
output << ", "; output << ", ";
...@@ -1232,8 +1260,7 @@ ModelTree::writeJsonModelLocalVariables(ostream &output, bool write_tef_terms, d ...@@ -1232,8 +1260,7 @@ ModelTree::writeJsonModelLocalVariables(ostream &output, bool write_tef_terms, d
output << ", "; output << ", ";
} }
output << R"({"variable": ")" << symbol_table.getName(id) output << R"({"variable": ")" << symbol_table.getName(id) << R"(", "value": ")";
<< R"(", "value": ")";
value->writeJsonOutput(output, {}, tef_terms); value->writeJsonOutput(output, {}, tef_terms);
output << R"("})" << endl; output << R"("})" << endl;
} }
...@@ -1251,8 +1278,7 @@ ModelTree::writeBytecodeBinFile(const filesystem::path &filename, bool is_two_bo ...@@ -1251,8 +1278,7 @@ ModelTree::writeBytecodeBinFile(const filesystem::path &filename, bool is_two_bo
} }
int u_count {0}; int u_count {0};
for (const auto& [indices, d1] : derivatives[1]) for (const auto& [indices, d1] : derivatives[1])
if (int deriv_id {indices[1]}; if (int deriv_id {indices[1]}; getTypeByDerivID(deriv_id) == SymbolType::endogenous)
getTypeByDerivID(deriv_id) == SymbolType::endogenous)
{ {
int eq {indices[0]}; int eq {indices[0]};
SaveCode.write(reinterpret_cast<char*>(&eq), sizeof eq); SaveCode.write(reinterpret_cast<char*>(&eq), sizeof eq);
...@@ -1319,7 +1345,8 @@ ModelTree::writeBlockBytecodeBinFile(ofstream &bin_file, int block) const ...@@ -1319,7 +1345,8 @@ ModelTree::writeBlockBytecodeBinFile(ofstream &bin_file, int block) const
} }
void void
ModelTree::writeLatexModelFile(const string &mod_basename, const string &latex_basename, ExprNodeOutputType output_type, bool write_equation_tags) const ModelTree::writeLatexModelFile(const string& mod_basename, const string& latex_basename,
ExprNodeOutputType output_type, bool write_equation_tags) const
{ {
filesystem::create_directories(mod_basename + "/latex"); filesystem::create_directories(mod_basename + "/latex");
...@@ -1352,8 +1379,7 @@ ModelTree::writeLatexModelFile(const string &mod_basename, const string &latex_b ...@@ -1352,8 +1379,7 @@ ModelTree::writeLatexModelFile(const string &mod_basename, const string &latex_b
{ {
expr_t value = local_variables_table.at(id); expr_t value = local_variables_table.at(id);
content_output << R"(\begin{dmath*})" << endl content_output << R"(\begin{dmath*})" << endl << symbol_table.getTeXName(id) << " = ";
<< symbol_table.getTeXName(id) << " = ";
// Use an empty set for the temporary terms // Use an empty set for the temporary terms
value->writeOutput(content_output, output_type); value->writeOutput(content_output, output_type);
content_output << endl << R"(\end{dmath*})" << endl; content_output << endl << R"(\end{dmath*})" << endl;
...@@ -1366,7 +1392,8 @@ ModelTree::writeLatexModelFile(const string &mod_basename, const string &latex_b ...@@ -1366,7 +1392,8 @@ ModelTree::writeLatexModelFile(const string &mod_basename, const string &latex_b
equation_tags.writeLatexOutput(content_output, eq); equation_tags.writeLatexOutput(content_output, eq);
content_output << R"(\begin{dmath})" << endl; content_output << R"(\begin{dmath})" << endl;
// Here it is necessary to cast to superclass ExprNode, otherwise the overloaded writeOutput() method is not found // Here it is necessary to cast to superclass ExprNode, otherwise the overloaded writeOutput()
// method is not found
dynamic_cast<ExprNode*>(equations[eq])->writeOutput(content_output, output_type); dynamic_cast<ExprNode*>(equations[eq])->writeOutput(content_output, output_type);
content_output << endl << R"(\end{dmath})" << endl; content_output << endl << R"(\end{dmath})" << endl;
} }
...@@ -1379,28 +1406,33 @@ ModelTree::writeLatexModelFile(const string &mod_basename, const string &latex_b ...@@ -1379,28 +1406,33 @@ ModelTree::writeLatexModelFile(const string &mod_basename, const string &latex_b
} }
void void
ModelTree::addEquation(expr_t eq, optional<int> lineno) ModelTree::addEquation(expr_t eq, const optional<int>& lineno,
optional<tuple<int, expr_t, expr_t>> complementarity_condition)
{ {
auto beq = dynamic_cast<BinaryOpNode*>(eq); auto beq = dynamic_cast<BinaryOpNode*>(eq);
assert(beq && beq->op_code == BinaryOpcode::equal); assert(beq && beq->op_code == BinaryOpcode::equal);
equations.push_back(beq); equations.push_back(beq);
equations_lineno.push_back(move(lineno)); equations_lineno.push_back(lineno);
complementarity_conditions.push_back(move(complementarity_condition));
} }
void void
ModelTree::findConstantEquationsWithoutMcpTag(map<VariableNode *, NumConstNode *> &subst_table) const ModelTree::findConstantEquationsWithoutComplementarityCondition(
map<VariableNode*, NumConstNode*>& subst_table) const
{ {
for (size_t i = 0; i < equations.size(); i++) for (size_t i = 0; i < equations.size(); i++)
if (!equation_tags.exists(i, "mcp")) if (!complementarity_conditions[i])
equations[i]->findConstantEquations(subst_table); equations[i]->findConstantEquations(subst_table);
} }
void void
ModelTree::addEquation(expr_t eq, optional<int> lineno, map<string, string> eq_tags) ModelTree::addEquation(expr_t eq, const optional<int>& lineno,
optional<tuple<int, expr_t, expr_t>> complementarity_condition,
map<string, string> eq_tags)
{ {
equation_tags.add(equations.size(), move(eq_tags)); equation_tags.add(equations.size(), move(eq_tags));
addEquation(eq, move(lineno)); addEquation(eq, lineno, move(complementarity_condition));
} }
void void
...@@ -1423,7 +1455,8 @@ ModelTree::addTrendVariables(const vector<int> &trend_vars, expr_t growth_factor ...@@ -1423,7 +1455,8 @@ ModelTree::addTrendVariables(const vector<int> &trend_vars, expr_t growth_factor
} }
void void
ModelTree::addNonstationaryVariables(const vector<int> &nonstationary_vars, bool log_deflator, expr_t deflator) noexcept(false) ModelTree::addNonstationaryVariables(const vector<int>& nonstationary_vars, bool log_deflator,
expr_t deflator) noexcept(false)
{ {
for (int id : nonstationary_vars) for (int id : nonstationary_vars)
if (nonstationary_symbols_map.contains(id)) if (nonstationary_symbols_map.contains(id))
...@@ -1493,7 +1526,8 @@ ModelTree::computeParamsDerivatives(int paramsDerivsOrder) ...@@ -1493,7 +1526,8 @@ ModelTree::computeParamsDerivatives(int paramsDerivsOrder)
continue; continue;
vector<int> indices {lower_indices}; vector<int> indices {lower_indices};
indices.push_back(param); indices.push_back(param);
// At this point, indices of both endogenous and parameters are sorted in non-decreasing order // At this point, indices of both endogenous and parameters are sorted in non-decreasing
// order
params_derivatives[{endoOrd, paramOrd}][indices] = d; params_derivatives[{endoOrd, paramOrd}][indices] = d;
} }
} }
...@@ -1511,11 +1545,10 @@ ModelTree::computeParamsDerivativesTemporaryTerms() ...@@ -1511,11 +1545,10 @@ ModelTree::computeParamsDerivativesTemporaryTerms()
d->computeTemporaryTerms(order, temp_terms_map, reference_count, true); d->computeTemporaryTerms(order, temp_terms_map, reference_count, true);
for (const auto& [order, tts] : temp_terms_map) for (const auto& [order, tts] : temp_terms_map)
copy(temp_terms_map[order].begin(), temp_terms_map[order].end(), ranges::copy(temp_terms_map[order], inserter(params_derivs_temporary_terms[order],
inserter(params_derivs_temporary_terms[order], params_derivs_temporary_terms[order].begin())); params_derivs_temporary_terms[order].begin()));
for (int idx {0}; for (int idx {0}; const auto& [order, tts] : params_derivs_temporary_terms)
const auto &[order, tts] : params_derivs_temporary_terms)
for (const auto& tt : tts) for (const auto& tt : tts)
params_derivs_temporary_terms_idxs[tt] = idx++; params_derivs_temporary_terms_idxs[tt] = idx++;
} }
...@@ -1563,12 +1596,10 @@ ModelTree::writeJsonModelEquations(ostream &output, bool residuals) const ...@@ -1563,12 +1596,10 @@ ModelTree::writeJsonModelEquations(ostream &output, bool residuals) const
if (equations_lineno[eq]) if (equations_lineno[eq])
output << R"(, "line": )" << *equations_lineno[eq]; output << R"(, "line": )" << *equations_lineno[eq];
if (auto eqtags = equation_tags.getTagsByEqn(eq); if (auto eqtags = equation_tags.getTagsByEqn(eq); !eqtags.empty())
!eqtags.empty())
{ {
output << R"(, "tags": {)"; output << R"(, "tags": {)";
for (bool printed_something{false}; for (bool printed_something {false}; const auto& [name, value] : eqtags)
const auto &[name, value] : eqtags)
{ {
if (exchange(printed_something, true)) if (exchange(printed_something, true))
output << ", "; output << ", ";
...@@ -1578,6 +1609,27 @@ ModelTree::writeJsonModelEquations(ostream &output, bool residuals) const ...@@ -1578,6 +1609,27 @@ ModelTree::writeJsonModelEquations(ostream &output, bool residuals) const
eqtags.clear(); eqtags.clear();
} }
} }
if (complementarity_conditions[eq])
{
auto& [symb_id, lower_bound, upper_bound] = *complementarity_conditions[eq];
output << R"(, "complementarity_condition": {"variable": ")"
<< symbol_table.getName(symb_id) << '"';
if (lower_bound)
{
output << R"(, "lower_bound": ")";
lower_bound->writeJsonOutput(output, {}, {});
output << '"';
}
if (upper_bound)
{
output << R"(, "upper_bound": ")";
upper_bound->writeJsonOutput(output, {}, {});
output << '"';
}
output << "}";
}
output << "}" << endl; output << "}" << endl;
} }
output << endl << "]" << endl; output << endl << "]" << endl;
...@@ -1605,7 +1657,8 @@ ModelTree::matlab_arch(const string &mexext) ...@@ -1605,7 +1657,8 @@ ModelTree::matlab_arch(const string &mexext)
return "maca64"; return "maca64";
else else
{ {
cerr << "ERROR: 'mexext' option to preprocessor incorrectly set, needed with 'use_dll'" << endl; cerr << "ERROR: 'mexext' option to preprocessor incorrectly set, needed with 'use_dll'"
<< endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
...@@ -1621,7 +1674,7 @@ ModelTree::findCompilerOnMacos(const string &mexext) ...@@ -1621,7 +1674,7 @@ ModelTree::findCompilerOnMacos(const string &mexext)
Apple’s clang is located both in /usr/bin/gcc and /usr/bin/clang, it Apple’s clang is located both in /usr/bin/gcc and /usr/bin/clang, it
automatically selects x86_64 or arm64 depending on the compile-time automatically selects x86_64 or arm64 depending on the compile-time
environment. */ environment. */
const string macos_gcc_version {"13"}; const string macos_gcc_version {"15"};
if (filesystem::path global_gcc_path {"/usr/local/bin/gcc-" + macos_gcc_version}; if (filesystem::path global_gcc_path {"/usr/local/bin/gcc-" + macos_gcc_version};
exists(global_gcc_path) && mexext == "mexmaci64") exists(global_gcc_path) && mexext == "mexmaci64")
...@@ -1633,26 +1686,32 @@ ModelTree::findCompilerOnMacos(const string &mexext) ...@@ -1633,26 +1686,32 @@ ModelTree::findCompilerOnMacos(const string &mexext)
return {global_clang_path, true}; return {global_clang_path, true};
else else
{ {
cerr << "ERROR: You must install gcc-" << macos_gcc_version cerr << "ERROR: You must install gcc@" << macos_gcc_version
<< " on your system before using the `use_dll` option of Dynare. " << " on your system before using the `use_dll` option of Dynare. "
<< "You should install Homebrew"; << "You should install Homebrew";
if (mexext == "mexmaca64") if (mexext == "mexmaca64")
cerr << " for arm64"; cerr << " for arm64";
else if (mexext == "mexmaci64") else if (mexext == "mexmaci64")
cerr << " for x86_64"; cerr << " for x86_64";
cerr << " and run `brew install gcc-" << macos_gcc_version << "` in a terminal." << endl; cerr << " and run `brew install gcc@" << macos_gcc_version << "` in a terminal." << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
#endif #endif
filesystem::path filesystem::path
ModelTree::compileMEX(const filesystem::path &output_dir, const string &output_basename, const string &mexext, const vector<filesystem::path> &input_files, const filesystem::path &matlabroot, bool link) const ModelTree::compileMEX(const filesystem::path& output_dir, const string& output_basename,
const string& mexext, const vector<filesystem::path>& input_files,
const filesystem::path& matlabroot, bool link) const
{ {
assert(!mex_compilation_workers.empty()); assert(!mex_compilation_workers.empty());
const string gcc_opt_flags { "-O3 -g0 --param ira-max-conflict-table-size=1 -fno-forward-propagate -fno-gcse -fno-dce -fno-dse -fno-tree-fre -fno-tree-pre -fno-tree-cselim -fno-tree-dse -fno-tree-dce -fno-tree-pta -fno-gcse-after-reload" }; const string gcc_opt_flags {
const string clang_opt_flags { "-O3 -g0 --param ira-max-conflict-table-size=1 -Wno-unused-command-line-argument" }; "-O3 -g0 --param ira-max-conflict-table-size=1 -fno-forward-propagate -fno-gcse -fno-dce "
"-fno-dse -fno-tree-fre -fno-tree-pre -fno-tree-cselim -fno-tree-dse -fno-tree-dce "
"-fno-tree-pta -fno-gcse-after-reload"};
const string clang_opt_flags {
"-O3 -g0 --param ira-max-conflict-table-size=1 -Wno-unused-command-line-argument"};
filesystem::path compiler; filesystem::path compiler;
ostringstream flags; ostringstream flags;
...@@ -1661,7 +1720,8 @@ ModelTree::compileMEX(const filesystem::path &output_dir, const string &output_b ...@@ -1661,7 +1720,8 @@ ModelTree::compileMEX(const filesystem::path &output_dir, const string &output_b
if (matlabroot.empty()) if (matlabroot.empty())
{ {
cerr << "ERROR: 'matlabroot' option to preprocessor is not set, needed with 'use_dll'" << endl; cerr << "ERROR: 'matlabroot' option to preprocessor is not set, needed with 'use_dll'"
<< endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
...@@ -1719,10 +1779,10 @@ ModelTree::compileMEX(const filesystem::path &output_dir, const string &output_b ...@@ -1719,10 +1779,10 @@ ModelTree::compileMEX(const filesystem::path &output_dir, const string &output_b
if (user_set_compiler.empty()) if (user_set_compiler.empty())
cmd << compiler << " "; cmd << compiler << " ";
else else if (!filesystem::exists(user_set_compiler))
if (!filesystem::exists(user_set_compiler))
{ {
cerr << "Error: The specified compiler '" << user_set_compiler << "' cannot be found on your system" << endl; cerr << "Error: The specified compiler '" << user_set_compiler
<< "' cannot be found on your system" << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
else else
...@@ -1760,11 +1820,8 @@ ModelTree::compileMEX(const filesystem::path &output_dir, const string &output_b ...@@ -1760,11 +1820,8 @@ ModelTree::compileMEX(const filesystem::path &output_dir, const string &output_b
// The prerequisites are the object files among the input files // The prerequisites are the object files among the input files
set<filesystem::path> prerequisites; set<filesystem::path> prerequisites;
copy_if(input_files.begin(), input_files.end(), ranges::copy_if(input_files, inserter(prerequisites, prerequisites.end()),
inserter(prerequisites, prerequisites.end()), [](const auto &p) [](const auto& p) { return p.extension() == ".o"; });
{
return p.extension() == ".o";
});
unique_lock<mutex> lk {mex_compilation_mut}; unique_lock<mutex> lk {mex_compilation_mut};
mex_compilation_queue.emplace_back(output_filename, prerequisites, cmd.str()); mex_compilation_queue.emplace_back(output_filename, prerequisites, cmd.str());
...@@ -1799,8 +1856,7 @@ ModelTree::reorderAuxiliaryEquations() ...@@ -1799,8 +1856,7 @@ ModelTree::reorderAuxiliaryEquations()
set<int> endos; set<int> endos;
aux_equations[i]->collectVariables(SymbolType::endogenous, endos); aux_equations[i]->collectVariables(SymbolType::endogenous, endos);
for (int endo : endos) for (int endo : endos)
if (auto it = auxEndoToEq.find(endo); if (auto it = auxEndoToEq.find(endo); it != auxEndoToEq.end() && it->second != i)
it != auxEndoToEq.end() && it->second != i)
add_edge(i, it->second, g); add_edge(i, it->second, g);
} }
...@@ -1853,7 +1909,8 @@ ModelTree::computeLeftHandSideSymbolicJacobian() const ...@@ -1853,7 +1909,8 @@ ModelTree::computeLeftHandSideSymbolicJacobian() const
auto not_contemporaneous = [](const pair<int, int>& p) { return p.second != 0; }; auto not_contemporaneous = [](const pair<int, int>& p) { return p.second != 0; };
for (int eq {0}; eq < static_cast<int>(equations.size()); eq++) for (int eq {0}; eq < static_cast<int>(equations.size()); eq++)
if (equations_lineno[eq]) // Hand-written equation: test whether LHS has single contemporaneous endo if (equations_lineno[eq]) // Hand-written equation: test whether LHS has single contemporaneous
// endo
{ {
set<pair<int, int>> endos_and_lags; set<pair<int, int>> endos_and_lags;
equations[eq]->arg1->collectEndogenous(endos_and_lags); equations[eq]->arg1->collectEndogenous(endos_and_lags);
...@@ -1907,8 +1964,13 @@ ModelTree::initializeMEXCompilationWorkers(int numworkers, const filesystem::pat ...@@ -1907,8 +1964,13 @@ ModelTree::initializeMEXCompilationWorkers(int numworkers, const filesystem::pat
cout << "Spawning " << numworkers << " threads for compiling MEX files." << endl; cout << "Spawning " << numworkers << " threads for compiling MEX files." << endl;
for (int i {0}; i < numworkers; i++) for (int i {0}; i < numworkers; i++)
mex_compilation_workers.emplace_back([](stop_token stoken) /* Passing the stop_token by const reference is ok (and makes clang-tidy happier),
{ since the std::jthread constructor calls the lambda with the return argument of the
get_stop_token() method, which returns a stop_token by value; hence there is no lifetime
issue. See:
https://stackoverflow.com/questions/72990607/const-stdstop-token-or-just-stdstop-token-as-parameter-for-thread-funct
*/
mex_compilation_workers.emplace_back([](const stop_token& stoken) {
unique_lock<mutex> lk {mex_compilation_mut}; unique_lock<mutex> lk {mex_compilation_mut};
filesystem::path output; filesystem::path output;
string cmd; string cmd;
...@@ -1916,12 +1978,10 @@ ModelTree::initializeMEXCompilationWorkers(int numworkers, const filesystem::pat ...@@ -1916,12 +1978,10 @@ ModelTree::initializeMEXCompilationWorkers(int numworkers, const filesystem::pat
/* Look for an object to compile, whose prerequisites are already /* Look for an object to compile, whose prerequisites are already
compiled. If found, remove it from the queue, save the output path and compiled. If found, remove it from the queue, save the output path and
the compilation command, and return true. Must be run under the lock. */ the compilation command, and return true. Must be run under the lock. */
auto pick_job = [&cmd, &output] auto pick_job = [&cmd, &output] {
{
for (auto it {mex_compilation_queue.begin()}; it != mex_compilation_queue.end(); ++it) for (auto it {mex_compilation_queue.begin()}; it != mex_compilation_queue.end(); ++it)
if (const auto& prerequisites {get<1>(*it)}; // Will become dangling after erase if (const auto& prerequisites {get<1>(*it)}; // Will become dangling after erase
includes(mex_compilation_done.begin(), mex_compilation_done.end(), ranges::includes(mex_compilation_done, prerequisites))
prerequisites.begin(), prerequisites.end()))
{ {
output = get<0>(*it); output = get<0>(*it);
cmd = get<2>(*it); cmd = get<2>(*it);
...@@ -1997,7 +2057,8 @@ ModelTree::waitForMEXCompilationWorkers() ...@@ -1997,7 +2057,8 @@ ModelTree::waitForMEXCompilationWorkers()
unique_lock<mutex> lk {mex_compilation_mut}; unique_lock<mutex> lk {mex_compilation_mut};
mex_compilation_cv.wait(lk, [] { mex_compilation_cv.wait(lk, [] {
return (mex_compilation_queue.empty() && mex_compilation_ongoing.empty()) return (mex_compilation_queue.empty() && mex_compilation_ongoing.empty())
|| !mex_compilation_failed.empty(); }); || !mex_compilation_failed.empty();
});
if (!mex_compilation_failed.empty()) if (!mex_compilation_failed.empty())
{ {
cerr << "Compilation failed for: "; cerr << "Compilation failed for: ";
...@@ -2012,6 +2073,8 @@ ModelTree::waitForMEXCompilationWorkers() ...@@ -2012,6 +2073,8 @@ ModelTree::waitForMEXCompilationWorkers()
void void
ModelTree::computingPassBlock(const eval_context_t& eval_context, bool no_tmp_terms) ModelTree::computingPassBlock(const eval_context_t& eval_context, bool no_tmp_terms)
{ {
if (!heterogeneity_table.empty())
return;
if (!computeNonSingularNormalization(eval_context)) if (!computeNonSingularNormalization(eval_context))
return; return;
auto [prologue, epilogue] = computePrologueAndEpilogue(); auto [prologue, epilogue] = computePrologueAndEpilogue();
...@@ -2031,8 +2094,7 @@ vector<int> ...@@ -2031,8 +2094,7 @@ vector<int>
ModelTree::computeCSCColPtr(const SparseColumnMajorOrderMatrix& matrix, int ncols) ModelTree::computeCSCColPtr(const SparseColumnMajorOrderMatrix& matrix, int ncols)
{ {
vector<int> colptr(ncols + 1, matrix.size()); vector<int> colptr(ncols + 1, matrix.size());
for (int k {0}, current_col {0}; for (int k {0}, current_col {0}; const auto& [indices, d1] : matrix)
const auto &[indices, d1] : matrix)
{ {
while (indices.second >= current_col) while (indices.second >= current_col)
colptr[current_col++] = k; colptr[current_col++] = k;
...@@ -2054,3 +2116,70 @@ ModelTree::writeAuxVarRecursiveDefinitions(ostream &output, ExprNodeOutputType o ...@@ -2054,3 +2116,70 @@ ModelTree::writeAuxVarRecursiveDefinitions(ostream &output, ExprNodeOutputType o
output << ";" << endl; output << ";" << endl;
} }
} }
void
ModelTree::computeMCPEquationsReordering(const optional<int>& heterogeneous_dimension)
{
/* Optimal policy models (discretionary, or Ramsey before computing FOCs) do not have as many
equations as variables. Do not even try to compute the reordering. */
if (static_cast<int>(equations.size())
!= (heterogeneous_dimension ? symbol_table.het_endo_nbr(*heterogeneous_dimension)
: symbol_table.endo_nbr()))
return;
assert(equations.size() == complementarity_conditions.size());
mcp_equations_reordering.resize(equations.size());
iota(mcp_equations_reordering.begin(), mcp_equations_reordering.end(), 0);
set<int> endos;
for (int eq {0}; eq < static_cast<int>(equations.size()); eq++)
if (complementarity_conditions.at(eq))
{
int symb_id {get<0>(*complementarity_conditions[eq])};
auto [ignore, inserted] = endos.insert(symb_id);
if (!inserted)
{
cerr << "ERROR: variable " << symbol_table.getName(symb_id)
<< " appears in two complementarity conditions" << endl;
exit(EXIT_FAILURE);
}
int endo_id {symbol_table.getTypeSpecificID(symb_id)};
auto it = ranges::find(mcp_equations_reordering, eq);
assert(it != mcp_equations_reordering.end());
swap(mcp_equations_reordering[endo_id], *it);
}
}
void
ModelTree::writeDriverSparseIndicesHelper(const string& prefix, ostream& output) const
{
// Write indices for the sparse Jacobian (both naive and CSC storage)
output << "M_." << prefix << "_g1_sparse_rowval = int32([";
for (const auto& [indices, d1] : jacobian_sparse_column_major_order)
output << indices.first + 1 << ' ';
output << "]);" << endl << "M_." << prefix << "_g1_sparse_colval = int32([";
for (const auto& [indices, d1] : jacobian_sparse_column_major_order)
output << indices.second + 1 << ' ';
output << "]);" << endl << "M_." << prefix << "_g1_sparse_colptr = int32([";
for (int it : jacobian_sparse_colptr)
output << it + 1 << ' ';
output << "]);" << endl;
// Write indices for the sparse higher-order derivatives
for (int i {2}; i <= computed_derivs_order; i++)
{
output << "M_." << prefix << "_g" << i << "_sparse_indices = int32([";
for (const auto& [vidx, d] : derivatives[i])
{
for (bool row_number {true}; // First element of vidx is row number
int it : vidx)
output << (exchange(row_number, false) ? it : getJacobianCol(it, true)) + 1 << ' ';
output << ';' << endl;
}
output << "]);" << endl;
}
}
/* /*
* Copyright © 2003-2023 Dynare Team * Copyright © 2003-2025 Dynare Team
* *
* This file is part of Dynare. * This file is part of Dynare.
* *
...@@ -17,26 +17,26 @@ ...@@ -17,26 +17,26 @@
* along with Dynare. If not, see <https://www.gnu.org/licenses/>. * along with Dynare. If not, see <https://www.gnu.org/licenses/>.
*/ */
#ifndef _MODELTREE_HH #ifndef MODEL_TREE_HH
#define _MODELTREE_HH #define MODEL_TREE_HH
#include <string>
#include <vector>
#include <deque>
#include <map>
#include <ostream>
#include <array> #include <array>
#include <cassert>
#include <condition_variable>
#include <deque>
#include <filesystem> #include <filesystem>
#include <map>
#include <mutex>
#include <optional> #include <optional>
#include <cassert> #include <ostream>
#include <string>
#include <thread> #include <thread>
#include <mutex> #include <vector>
#include <condition_variable>
#include "Bytecode.hh"
#include "DataTree.hh" #include "DataTree.hh"
#include "EquationTags.hh" #include "EquationTags.hh"
#include "ExtendedPreprocessorTypes.hh" #include "ExtendedPreprocessorTypes.hh"
#include "Bytecode.hh"
using namespace std; using namespace std;
...@@ -55,7 +55,8 @@ vectorToTuple(const vector<T> &v) ...@@ -55,7 +55,8 @@ vectorToTuple(const vector<T> &v)
return vectorToTupleHelper(v, make_index_sequence<N>()); return vectorToTupleHelper(v, make_index_sequence<N>());
} }
//! Vector describing equations: BlockSimulationType, if BlockSimulationType == EVALUATE_s then a expr_t on the new normalized equation //! Vector describing equations: BlockSimulationType, if BlockSimulationType == EVALUATE_s then a
//! expr_t on the new normalized equation
using equation_type_and_normalized_equation_t = vector<pair<EquationType, BinaryOpNode*>>; using equation_type_and_normalized_equation_t = vector<pair<EquationType, BinaryOpNode*>>;
//! Vector describing variables: max_lag in the block, max_lead in the block //! Vector describing variables: max_lag in the block, max_lead in the block
...@@ -66,9 +67,12 @@ class ModelTree : public DataTree ...@@ -66,9 +67,12 @@ class ModelTree : public DataTree
{ {
friend class DynamicModel; friend class DynamicModel;
friend class StaticModel; friend class StaticModel;
public: public:
// Set via the `compiler` command // Set via the `compiler` command
string user_set_add_flags, user_set_subst_flags, user_set_add_libs, user_set_subst_libs, user_set_compiler; string user_set_add_flags, user_set_subst_flags, user_set_add_libs, user_set_subst_libs,
user_set_compiler;
protected: protected:
/* /*
* ************** BEGIN ************** * ************** BEGIN **************
...@@ -87,6 +91,9 @@ protected: ...@@ -87,6 +91,9 @@ protected:
vector<optional<int>> equations_lineno; vector<optional<int>> equations_lineno;
//! Stores equation tags //! Stores equation tags
EquationTags equation_tags; EquationTags equation_tags;
/* The tuple is: endogenous symbol ID, lower bound (possibly nullptr), upper bound (possibly
nullptr). */
vector<optional<tuple<int, expr_t, expr_t>>> complementarity_conditions;
/* /*
* ************** END ************** * ************** END **************
*/ */
...@@ -179,7 +186,8 @@ protected: ...@@ -179,7 +186,8 @@ protected:
Set by updateReverseVariableEquationOrderings() */ Set by updateReverseVariableEquationOrderings() */
vector<int> eq_idx_orig2block, endo_idx_orig2block; vector<int> eq_idx_orig2block, endo_idx_orig2block;
//! Vector describing equations: BlockSimulationType, if BlockSimulationType == EVALUATE_s then a expr_t on the new normalized equation //! Vector describing equations: BlockSimulationType, if BlockSimulationType == EVALUATE_s then a
//! expr_t on the new normalized equation
equation_type_and_normalized_equation_t equation_type_and_normalized_equation; equation_type_and_normalized_equation_t equation_type_and_normalized_equation;
/* Stores derivatives of each block w.r.t. endogenous that belong to it. /* Stores derivatives of each block w.r.t. endogenous that belong to it.
...@@ -195,13 +203,15 @@ protected: ...@@ -195,13 +203,15 @@ protected:
int size {0}; int size {0};
int mfs_size {0}; // Size of the minimal feedback set int mfs_size {0}; // Size of the minimal feedback set
bool linear {true}; // Whether the block is linear in endogenous variable bool linear {true}; // Whether the block is linear in endogenous variable
int max_endo_lag{0}, max_endo_lead{0}; // Maximum lag/lead on endos that appear in and *that belong to* the block int max_endo_lag {0},
max_endo_lead {
0}; // Maximum lag/lead on endos that appear in and *that belong to* the block
int [[nodiscard]] int
getRecursiveSize() const getRecursiveSize() const
{ {
return size - mfs_size; return size - mfs_size;
}; }
}; };
// Whether block decomposition has been successfully computed // Whether block decomposition has been successfully computed
...@@ -264,6 +274,12 @@ protected: ...@@ -264,6 +274,12 @@ protected:
Same remark as above regarding blocks of type “evaluate”. */ Same remark as above regarding blocks of type “evaluate”. */
vector<vector<int>> blocks_jacobian_sparse_colptr; vector<vector<int>> blocks_jacobian_sparse_colptr;
/* Indices of reordered equations for use with an MCP solver. Contains a permutation of the
equation indices, so that reordered equations appear at the (type-specific) index of the
endogenous to which they are associated through the complementarity condition.
Also see computeMCPEquationsReordering() method. */
vector<int> mcp_equations_reordering;
//! Computes derivatives //! Computes derivatives
/*! \param order the derivation order /*! \param order the derivation order
\param vars the derivation IDs w.r.t. which compute the derivatives */ \param vars the derivation IDs w.r.t. which compute the derivatives */
...@@ -277,15 +293,24 @@ protected: ...@@ -277,15 +293,24 @@ protected:
//! Computes temporary terms for the file containing parameters derivatives //! Computes temporary terms for the file containing parameters derivatives
void computeParamsDerivativesTemporaryTerms(); void computeParamsDerivativesTemporaryTerms();
/* Computes the mcp_equations_reordering vector.
Also checks that a variable does not appear as constrained in two different equations. */
void computeMCPEquationsReordering(const optional<int>& heterogeneous_dimension = nullopt);
//! Writes temporary terms //! Writes temporary terms
template<ExprNodeOutputType output_type> template<ExprNodeOutputType output_type>
void writeTemporaryTerms(const temporary_terms_t &tt, temporary_terms_t &temp_term_union, const temporary_terms_idxs_t &tt_idxs, ostream &output, deriv_node_temp_terms_t &tef_terms) const; void writeTemporaryTerms(const temporary_terms_t& tt, temporary_terms_t& temp_term_union,
void writeJsonTemporaryTerms(const temporary_terms_t &tt, temporary_terms_t &temp_term_union, ostream &output, deriv_node_temp_terms_t &tef_terms, const string &concat) const; const temporary_terms_idxs_t& tt_idxs, ostream& output,
deriv_node_temp_terms_t& tef_terms) const;
void writeJsonTemporaryTerms(const temporary_terms_t& tt, temporary_terms_t& temp_term_union,
ostream& output, deriv_node_temp_terms_t& tef_terms,
const string& concat) const;
//! Writes temporary terms in bytecode //! Writes temporary terms in bytecode
template<ExprNodeBytecodeOutputType output_type> template<ExprNodeBytecodeOutputType output_type>
void writeBytecodeTemporaryTerms(const temporary_terms_t& tt, void writeBytecodeTemporaryTerms(const temporary_terms_t& tt,
temporary_terms_t& temporary_terms_union, temporary_terms_t& temporary_terms_union,
BytecodeWriter &code_file, Bytecode::Writer& code_file,
deriv_node_temp_terms_t& tef_terms) const; deriv_node_temp_terms_t& tef_terms) const;
/* Adds information for (non-block) bytecode simulation in a separate .bin /* Adds information for (non-block) bytecode simulation in a separate .bin
file. file.
...@@ -295,7 +320,8 @@ protected: ...@@ -295,7 +320,8 @@ protected:
int writeBlockBytecodeBinFile(ofstream& bin_file, int block) const; int writeBlockBytecodeBinFile(ofstream& bin_file, int block) const;
//! Fixes output when there are more than 32 nested parens, Issue #1201 //! Fixes output when there are more than 32 nested parens, Issue #1201
void fixNestedParenthesis(ostringstream &output, map<string, string> &tmp_paren_vars, bool &message_printed) const; void fixNestedParenthesis(ostringstream& output, map<string, string>& tmp_paren_vars,
bool& message_printed) const;
//! Tests if string contains more than 32 nested parens, Issue #1201 //! Tests if string contains more than 32 nested parens, Issue #1201
bool testNestedParenthesis(const string& str) const; bool testNestedParenthesis(const string& str) const;
...@@ -309,7 +335,8 @@ protected: ...@@ -309,7 +335,8 @@ protected:
// Writes and compiles dynamic/static file (C version, legacy representation) // Writes and compiles dynamic/static file (C version, legacy representation)
template<bool dynamic> template<bool dynamic>
void writeModelCFile(const string &basename, const string &mexext, const filesystem::path &matlabroot) const; void writeModelCFile(const string& basename, const string& mexext,
const filesystem::path& matlabroot) const;
// Writes per-block residuals and temporary terms (incl. for derivatives) // Writes per-block residuals and temporary terms (incl. for derivatives)
template<ExprNodeOutputType output_type> template<ExprNodeOutputType output_type>
...@@ -318,26 +345,29 @@ protected: ...@@ -318,26 +345,29 @@ protected:
// Writes per-block Jacobian (sparse representation) // Writes per-block Jacobian (sparse representation)
// Assumes temporary terms for derivatives are already set. // Assumes temporary terms for derivatives are already set.
template<ExprNodeOutputType output_type> template<ExprNodeOutputType output_type>
void writeSparsePerBlockJacobianHelper(int blk, ostream &output, temporary_terms_t &temporary_terms) const; void writeSparsePerBlockJacobianHelper(int blk, ostream& output,
temporary_terms_t& temporary_terms) const;
/* Helper for writing derivatives w.r.t. parameters. /* Helper for writing derivatives w.r.t. parameters.
Returns { tt, rp, gp, rpp, gpp, hp, g3p }. Returns { tt, rp, gp, rpp, gpp, hp, g3p }.
g3p is empty if requesting a static output type. */ g3p is empty if requesting a static output type. */
template<ExprNodeOutputType output_type> template<ExprNodeOutputType output_type>
tuple<ostringstream, ostringstream, ostringstream, ostringstream, tuple<ostringstream, ostringstream, ostringstream, ostringstream, ostringstream, ostringstream,
ostringstream, ostringstream, ostringstream> writeParamsDerivativesFileHelper() const; ostringstream>
writeParamsDerivativesFileHelper() const;
// Helper for writing bytecode (without block decomposition) // Helper for writing bytecode (without block decomposition)
template<bool dynamic> template<bool dynamic>
void writeBytecodeHelper(BytecodeWriter &code_file) const; void writeBytecodeHelper(Bytecode::Writer& code_file) const;
// Helper for writing blocks in bytecode // Helper for writing blocks in bytecode
template<bool dynamic> template<bool dynamic>
void writeBlockBytecodeHelper(BytecodeWriter &code_file, int block, temporary_terms_t &temporary_terms_union) const; void writeBlockBytecodeHelper(Bytecode::Writer& code_file, int block,
temporary_terms_t& temporary_terms_union) const;
// Helper for writing sparse derivatives indices in MATLAB/Octave driver file /* Helper for writing sparse derivatives indices in MATLAB/Octave driver file.
template<bool dynamic> The “prefix” will be prepended to variable names to construct objects under M_. */
void writeDriverSparseIndicesHelper(ostream &output) const; void writeDriverSparseIndicesHelper(const string& prefix, ostream& output) const;
// Helper for writing sparse derivatives indices in JSON // Helper for writing sparse derivatives indices in JSON
template<bool dynamic> template<bool dynamic>
...@@ -350,14 +380,16 @@ protected: ...@@ -350,14 +380,16 @@ protected:
/* Helper for writing JSON output for residuals and derivatives. /* Helper for writing JSON output for residuals and derivatives.
Returns mlv and derivatives output at each derivation order. */ Returns mlv and derivatives output at each derivation order. */
template<bool dynamic> template<bool dynamic>
pair<ostringstream, vector<ostringstream>> writeJsonComputingPassOutputHelper(bool writeDetails) const; pair<ostringstream, vector<ostringstream>>
writeJsonComputingPassOutputHelper(bool writeDetails) const;
/* Helper for writing JSON derivatives w.r.t. parameters. /* Helper for writing JSON derivatives w.r.t. parameters.
Returns { mlv, tt, rp, gp, rpp, gpp, hp, g3p }. Returns { mlv, tt, rp, gp, rpp, gpp, hp, g3p }.
g3p is empty if requesting a static output type. */ g3p is empty if requesting a static output type. */
template<bool dynamic> template<bool dynamic>
tuple<ostringstream, ostringstream, ostringstream, ostringstream, ostringstream, tuple<ostringstream, ostringstream, ostringstream, ostringstream, ostringstream, ostringstream,
ostringstream, ostringstream, ostringstream> writeJsonParamsDerivativesHelper(bool writeDetails) const; ostringstream, ostringstream>
writeJsonParamsDerivativesHelper(bool writeDetails) const;
//! Writes JSON model equations //! Writes JSON model equations
//! if residuals = true, we are writing the dynamic/static model. //! if residuals = true, we are writing the dynamic/static model.
...@@ -365,19 +397,24 @@ protected: ...@@ -365,19 +397,24 @@ protected:
void writeJsonModelEquations(ostream& output, bool residuals) const; void writeJsonModelEquations(ostream& output, bool residuals) const;
/* Writes JSON model local variables. /* Writes JSON model local variables.
Optionally put the external function variable calls into TEF terms */ Optionally put the external function variable calls into TEF terms */
void writeJsonModelLocalVariables(ostream &output, bool write_tef_terms, deriv_node_temp_terms_t &tef_terms) const; void writeJsonModelLocalVariables(ostream& output, bool write_tef_terms,
deriv_node_temp_terms_t& tef_terms) const;
//! Writes model equations in bytecode //! Writes model equations in bytecode
template<ExprNodeBytecodeOutputType output_type> template<ExprNodeBytecodeOutputType output_type>
void writeBytecodeModelEquations(BytecodeWriter &code_file, const temporary_terms_t &temporary_terms, const deriv_node_temp_terms_t &tef_terms) const; void writeBytecodeModelEquations(Bytecode::Writer& code_file,
const temporary_terms_t& temporary_terms,
const deriv_node_temp_terms_t& tef_terms) const;
// Writes the sparse representation of the model in MATLAB/Octave // Writes the sparse representation of the model in MATLAB/Octave
template<bool dynamic> template<bool dynamic>
void writeSparseModelMFiles(const string &basename) const; void writeSparseModelMFiles(const string& basename,
const optional<int>& heterogeneous_dimension = nullopt) const;
// Writes and compiles the sparse representation of the model in C // Writes and compiles the sparse representation of the model in C
template<bool dynamic> template<bool dynamic>
void writeSparseModelCFiles(const string &basename, const string &mexext, const filesystem::path &matlabroot) const; void writeSparseModelCFiles(const string& basename, const string& mexext,
const filesystem::path& matlabroot) const;
// Writes the sparse representation of the model in Julia // Writes the sparse representation of the model in Julia
// Assumes that the directory <MODFILE>/model/julia/ already exists // Assumes that the directory <MODFILE>/model/julia/ already exists
...@@ -385,7 +422,8 @@ protected: ...@@ -385,7 +422,8 @@ protected:
void writeSparseModelJuliaFiles(const string& basename) const; void writeSparseModelJuliaFiles(const string& basename) const;
//! Writes LaTeX model file //! Writes LaTeX model file
void writeLatexModelFile(const string &mod_basename, const string &latex_basename, ExprNodeOutputType output_type, bool write_equation_tags) const; void writeLatexModelFile(const string& mod_basename, const string& latex_basename,
ExprNodeOutputType output_type, bool write_equation_tags) const;
/* Write files for helping a user to debug their model (MATLAB/Octave, /* Write files for helping a user to debug their model (MATLAB/Octave,
sparse representation). sparse representation).
...@@ -400,6 +438,11 @@ protected: ...@@ -400,6 +438,11 @@ protected:
template<bool dynamic> template<bool dynamic>
void writeSetAuxiliaryVariablesFile(const string& basename, bool julia) const; void writeSetAuxiliaryVariablesFile(const string& basename, bool julia) const;
template<bool dynamic>
void writeComplementarityConditionsFile(const string& basename,
const optional<int>& heterogeneous_dimension
= nullopt) const;
private: private:
//! Sparse matrix of double to store the values of the static Jacobian //! Sparse matrix of double to store the values of the static Jacobian
/*! First index is equation number, second index is endogenous type specific ID */ /*! First index is equation number, second index is endogenous type specific ID */
...@@ -452,20 +495,23 @@ private: ...@@ -452,20 +495,23 @@ private:
string unmatched_endo; // Name of endogenous not in maximum cardinality matching string unmatched_endo; // Name of endogenous not in maximum cardinality matching
}; };
//! Compute the matching between endogenous and variable using the jacobian contemporaneous_jacobian //! Compute the matching between endogenous and variable using the jacobian
//! contemporaneous_jacobian
/*! /*!
\param contemporaneous_jacobian Jacobian used as an incidence matrix: all elements declared in the map (even if they are zero), are used as vertices of the incidence matrix \param contemporaneous_jacobian Jacobian used as an incidence matrix: all elements declared in
\return True if a complete normalization has been achieved the map (even if they are zero), are used as vertices of the incidence matrix \return True if a
complete normalization has been achieved
*/ */
void computeNormalization(const jacob_map_t& contemporaneous_jacobian); void computeNormalization(const jacob_map_t& contemporaneous_jacobian);
//! Try to compute the matching between endogenous and variable using a decreasing cutoff //! Try to compute the matching between endogenous and variable using a decreasing cutoff
/*! /*!
Applied to the jacobian contemporaneous_jacobian and stop when a matching is found. Applied to the jacobian contemporaneous_jacobian and stop when a matching is found.
If no matching is found using a strictly positive cutoff, then a zero cutoff is applied (i.e. use a symbolic normalization); in that case, the method adds zeros in the jacobian matrices to reflect all the edges in the symbolic incidence matrix. If no matching is found using a strictly positive cutoff, then a zero cutoff is applied (i.e.
If no matching is found with a zero cutoff, an error message is printed. use a symbolic normalization); in that case, the method adds zeros in the jacobian matrices to
The resulting normalization is stored in endo2eq. reflect all the edges in the symbolic incidence matrix. If no matching is found with a zero
Returns a boolean indicating success. cutoff, an error message is printed. The resulting normalization is stored in endo2eq. Returns a
boolean indicating success.
*/ */
bool computeNonSingularNormalization(const eval_context_t& eval_context); bool computeNonSingularNormalization(const eval_context_t& eval_context);
//! Evaluate the jacobian (w.r.t. endogenous) and suppress all the elements below the cutoff //! Evaluate the jacobian (w.r.t. endogenous) and suppress all the elements below the cutoff
...@@ -478,7 +524,8 @@ private: ...@@ -478,7 +524,8 @@ private:
Returns the sizes of the prologue and epilogue. */ Returns the sizes of the prologue and epilogue. */
pair<int, int> computePrologueAndEpilogue(); pair<int, int> computePrologueAndEpilogue();
//! Determine the type of each equation of model and try to normalize the unnormalized equation //! Determine the type of each equation of model and try to normalize the unnormalized equation
void equationTypeDetermination(const map<tuple<int, int, int>, expr_t> &first_order_endo_derivatives); void
equationTypeDetermination(const map<tuple<int, int, int>, expr_t>& first_order_endo_derivatives);
/* Fills the max lags/leads and n_{static,mixed,forward,backward} fields of a /* Fills the max lags/leads and n_{static,mixed,forward,backward} fields of a
given block. given block.
Needs the fields size and first_equation. */ Needs the fields size and first_equation. */
...@@ -512,50 +559,54 @@ protected: ...@@ -512,50 +559,54 @@ protected:
EquationType EquationType
getBlockEquationType(int blk, int eq) const getBlockEquationType(int blk, int eq) const
{ {
return equation_type_and_normalized_equation[eq_idx_block2orig[blocks[blk].first_equation+eq]].first; return equation_type_and_normalized_equation[eq_idx_block2orig[blocks[blk].first_equation + eq]]
}; .first;
}
//! Return true if the equation has been normalized //! Return true if the equation has been normalized
bool bool
isBlockEquationRenormalized(int blk, int eq) const isBlockEquationRenormalized(int blk, int eq) const
{ {
return equation_type_and_normalized_equation[eq_idx_block2orig[blocks[blk].first_equation + eq]].first == EquationType::evaluateRenormalized; return equation_type_and_normalized_equation[eq_idx_block2orig[blocks[blk].first_equation + eq]]
}; .first
== EquationType::evaluateRenormalized;
}
//! Return the expr_t of equation belonging to the block //! Return the expr_t of equation belonging to the block
BinaryOpNode* BinaryOpNode*
getBlockEquationExpr(int blk, int eq) const getBlockEquationExpr(int blk, int eq) const
{ {
return equations[eq_idx_block2orig[blocks[blk].first_equation + eq]]; return equations[eq_idx_block2orig[blocks[blk].first_equation + eq]];
}; }
//! Return the expr_t of renormalized equation belonging to the block //! Return the expr_t of renormalized equation belonging to the block
BinaryOpNode* BinaryOpNode*
getBlockEquationRenormalizedExpr(int blk, int eq) const getBlockEquationRenormalizedExpr(int blk, int eq) const
{ {
return equation_type_and_normalized_equation[eq_idx_block2orig[blocks[blk].first_equation + eq]].second; return equation_type_and_normalized_equation[eq_idx_block2orig[blocks[blk].first_equation + eq]]
}; .second;
}
//! Return the original number of equation belonging to the block //! Return the original number of equation belonging to the block
int int
getBlockEquationID(int blk, int eq) const getBlockEquationID(int blk, int eq) const
{ {
return eq_idx_block2orig[blocks[blk].first_equation + eq]; return eq_idx_block2orig[blocks[blk].first_equation + eq];
}; }
//! Return the original number of variable belonging to the block //! Return the original number of variable belonging to the block
int int
getBlockVariableID(int blk, int var) const getBlockVariableID(int blk, int var) const
{ {
return endo_idx_block2orig[blocks[blk].first_equation + var]; return endo_idx_block2orig[blocks[blk].first_equation + var];
}; }
//! Return the position of an equation (given by its original index) inside its block //! Return the position of an equation (given by its original index) inside its block
int int
getBlockInitialEquationID(int blk, int eq) const getBlockInitialEquationID(int blk, int eq) const
{ {
return eq_idx_orig2block[eq] - blocks[blk].first_equation; return eq_idx_orig2block[eq] - blocks[blk].first_equation;
}; }
//! Return the position of a variable (given by its original index) inside its block //! Return the position of a variable (given by its original index) inside its block
int int
getBlockInitialVariableID(int blk, int var) const getBlockInitialVariableID(int blk, int var) const
{ {
return endo_idx_orig2block[var] - blocks[blk].first_equation; return endo_idx_orig2block[var] - blocks[blk].first_equation;
}; }
//! Initialize equation_reordered & variable_reordered //! Initialize equation_reordered & variable_reordered
void initializeVariablesAndEquations(); void initializeVariablesAndEquations();
...@@ -604,13 +655,14 @@ private: ...@@ -604,13 +655,14 @@ private:
blocking. The dependency of a linked MEX file upon intermediary objects is blocking. The dependency of a linked MEX file upon intermediary objects is
nicely handled. Returns the name of the output file (to be reused later as nicely handled. Returns the name of the output file (to be reused later as
input file if link=false). */ input file if link=false). */
filesystem::path compileMEX(const filesystem::path &output_dir, const string &output_basename, const string &mexext, const vector<filesystem::path> &input_files, const filesystem::path &matlabroot, bool link = true) const; filesystem::path compileMEX(const filesystem::path& output_dir, const string& output_basename,
const string& mexext, const vector<filesystem::path>& input_files,
const filesystem::path& matlabroot, bool link = true) const;
public: public:
ModelTree(SymbolTable &symbol_table_arg, ModelTree(SymbolTable& symbol_table_arg, NumericalConstants& num_constants_arg,
NumericalConstants &num_constants_arg,
ExternalFunctionsTable& external_functions_table_arg, ExternalFunctionsTable& external_functions_table_arg,
bool is_dynamic_arg = false); HeterogeneityTable& heterogeneity_table_arg, bool is_dynamic_arg = false);
protected: protected:
ModelTree(const ModelTree& m); ModelTree(const ModelTree& m);
...@@ -619,26 +671,34 @@ protected: ...@@ -619,26 +671,34 @@ protected:
public: public:
//! Absolute value under which a number is considered to be zero //! Absolute value under which a number is considered to be zero
double cutoff {1e-15}; double cutoff {1e-15};
//! Declare a node as an equation of the model; also give its line number /* Declare a node as an equation of the model; also give its line number and complementarity
void addEquation(expr_t eq, optional<int> lineno); condition */
void addEquation(expr_t eq, const optional<int>& lineno,
optional<tuple<int, expr_t, expr_t>> complementarity_condition = nullopt);
//! Declare a node as an equation of the model, also giving its tags //! Declare a node as an equation of the model, also giving its tags
void addEquation(expr_t eq, optional<int> lineno, map<string, string> eq_tags); void addEquation(expr_t eq, const optional<int>& lineno,
//! Declare a node as an auxiliary equation of the model, adding it at the end of the list of auxiliary equations optional<tuple<int, expr_t, expr_t>> complementarity_condition,
map<string, string> eq_tags);
//! Declare a node as an auxiliary equation of the model, adding it at the end of the list of
//! auxiliary equations
void addAuxEquation(expr_t eq); void addAuxEquation(expr_t eq);
//! Returns the number of equations in the model //! Returns the number of equations in the model
int equation_number() const; int equation_number() const;
//! Adds a trend variable with its growth factor //! Adds a trend variable with its growth factor
void addTrendVariables(const vector<int>& trend_vars, expr_t growth_factor) noexcept(false); void addTrendVariables(const vector<int>& trend_vars, expr_t growth_factor) noexcept(false);
//! Adds a nonstationary variables with their (common) deflator //! Adds a nonstationary variables with their (common) deflator
void addNonstationaryVariables(const vector<int> &nonstationary_vars, bool log_deflator, expr_t deflator) noexcept(false); void addNonstationaryVariables(const vector<int>& nonstationary_vars, bool log_deflator,
expr_t deflator) noexcept(false);
//! Is a given variable non-stationary? //! Is a given variable non-stationary?
bool isNonstationary(int symb_id) const; bool isNonstationary(int symb_id) const;
void set_cutoff_to_zero(); void set_cutoff_to_zero();
/*! Reorder auxiliary variables so that they appear in recursive order in /*! Reorder auxiliary variables so that they appear in recursive order in
set_auxiliary_variables.m and dynamic_set_auxiliary_series.m */ set_auxiliary_variables.m and dynamic_set_auxiliary_series.m */
void reorderAuxiliaryEquations(); void reorderAuxiliaryEquations();
//! Find equations of the form “variable=constant”, excluding equations with “mcp” tag (see dynare#1697) /* Find equations of the form “variable=constant”, excluding equations with a complementarity
void findConstantEquationsWithoutMcpTag(map<VariableNode *, NumConstNode *> &subst_table) const; condition (see dynare#1697) */
void findConstantEquationsWithoutComplementarityCondition(
map<VariableNode*, NumConstNode*>& subst_table) const;
/* Given an expression, searches for the first equation that has exactly this /* Given an expression, searches for the first equation that has exactly this
expression on the LHS, and returns the RHS of that equation. expression on the LHS, and returns the RHS of that equation.
If no such equation can be found, throws an ExprNode::MatchFailureExpression */ If no such equation can be found, throws an ExprNode::MatchFailureExpression */
...@@ -655,27 +715,6 @@ public: ...@@ -655,27 +715,6 @@ public:
// Write the definitions of the auxiliary variables (assumed to be in recursive order) // Write the definitions of the auxiliary variables (assumed to be in recursive order)
void writeAuxVarRecursiveDefinitions(ostream& output, ExprNodeOutputType output_type) const; void writeAuxVarRecursiveDefinitions(ostream& output, ExprNodeOutputType output_type) const;
//! Returns the vector of non-zero derivative counts
const vector<int> &
getNNZDerivatives() const
{
return NNZDerivatives;
}
//! Returns the vector of temporary terms derivatives
const vector<temporary_terms_t> &
getTemporaryTermsDerivatives() const
{
return temporary_terms_derivatives;
}
//!Returns the maximum order of computed derivatives
int
getComputedDerivsOrder() const
{
return computed_derivs_order;
}
static string static string
BlockSim(BlockSimulationType type) BlockSim(BlockSimulationType type)
{ {
...@@ -709,16 +748,16 @@ public: ...@@ -709,16 +748,16 @@ public:
template<ExprNodeOutputType output_type> template<ExprNodeOutputType output_type>
void void
ModelTree::writeTemporaryTerms(const temporary_terms_t &tt, ModelTree::writeTemporaryTerms(const temporary_terms_t& tt, temporary_terms_t& temp_term_union,
temporary_terms_t &temp_term_union, const temporary_terms_idxs_t& tt_idxs, ostream& output,
const temporary_terms_idxs_t &tt_idxs, deriv_node_temp_terms_t& tef_terms) const
ostream &output, deriv_node_temp_terms_t &tef_terms) const
{ {
for (auto it : tt) for (auto it : tt)
{ {
if (dynamic_cast<AbstractExternalFunctionNode*>(it)) if (dynamic_cast<AbstractExternalFunctionNode*>(it))
it->writeExternalFunctionOutput(output, output_type, temp_term_union, tt_idxs, tef_terms); it->writeExternalFunctionOutput(output, output_type, temp_term_union, tt_idxs, tef_terms);
// NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage)
it->writeOutput(output, output_type, tt, tt_idxs, tef_terms); it->writeOutput(output, output_type, tt, tt_idxs, tef_terms);
output << " = "; output << " = ";
it->writeOutput(output, output_type, temp_term_union, tt_idxs, tef_terms); it->writeOutput(output, output_type, temp_term_union, tt_idxs, tef_terms);
...@@ -740,7 +779,7 @@ ModelTree::writeModelEquations(ostream &output, const temporary_terms_t &tempora ...@@ -740,7 +779,7 @@ ModelTree::writeModelEquations(ostream &output, const temporary_terms_t &tempora
BinaryOpNode* eq_node {equations[eq]}; BinaryOpNode* eq_node {equations[eq]};
expr_t lhs {eq_node->arg1}, rhs {eq_node->arg2}; expr_t lhs {eq_node->arg1}, rhs {eq_node->arg2};
// Test if the right hand side of the equation is empty. // Test if the right-hand side of the equation is empty.
double vrhs {1.0}; double vrhs {1.0};
try try
{ {
...@@ -750,22 +789,20 @@ ModelTree::writeModelEquations(ostream &output, const temporary_terms_t &tempora ...@@ -750,22 +789,20 @@ ModelTree::writeModelEquations(ostream &output, const temporary_terms_t &tempora
{ {
} }
if (vrhs != 0) // The right hand side of the equation is not empty ==> residual=lhs-rhs; if (vrhs != 0) // The right-hand side of the equation is not empty ==> residual=lhs-rhs;
{ {
output << " residual" << LEFT_ARRAY_SUBSCRIPT(output_type) output << " residual" << LEFT_ARRAY_SUBSCRIPT(output_type)
<< eq + ARRAY_SUBSCRIPT_OFFSET(output_type) << eq + ARRAY_SUBSCRIPT_OFFSET(output_type) << RIGHT_ARRAY_SUBSCRIPT(output_type)
<< RIGHT_ARRAY_SUBSCRIPT(output_type)
<< " = ("; << " = (";
lhs->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs); lhs->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs);
output << ") - ("; output << ") - (";
rhs->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs); rhs->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs);
output << ");" << endl; output << ");" << endl;
} }
else // The right hand side of the equation is empty ==> residual=lhs; else // The right-hand side of the equation is empty ==> residual=lhs;
{ {
output << "residual" << LEFT_ARRAY_SUBSCRIPT(output_type) output << "residual" << LEFT_ARRAY_SUBSCRIPT(output_type)
<< eq + ARRAY_SUBSCRIPT_OFFSET(output_type) << eq + ARRAY_SUBSCRIPT_OFFSET(output_type) << RIGHT_ARRAY_SUBSCRIPT(output_type)
<< RIGHT_ARRAY_SUBSCRIPT(output_type)
<< " = "; << " = ";
lhs->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs); lhs->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs);
output << ";" << endl; output << ";" << endl;
...@@ -779,7 +816,8 @@ ModelTree::writeModelFileHelper() const ...@@ -779,7 +816,8 @@ ModelTree::writeModelFileHelper() const
{ {
constexpr bool sparse {isSparseModelOutput(output_type)}; constexpr bool sparse {isSparseModelOutput(output_type)};
vector<ostringstream> d_output(derivatives.size()); // Derivatives output (at all orders, including 0=residual) vector<ostringstream> d_output(
derivatives.size()); // Derivatives output (at all orders, including 0=residual)
vector<ostringstream> tt_output(derivatives.size()); // Temp terms output (at all orders) vector<ostringstream> tt_output(derivatives.size()); // Temp terms output (at all orders)
deriv_node_temp_terms_t tef_terms; deriv_node_temp_terms_t tef_terms;
...@@ -799,14 +837,15 @@ ModelTree::writeModelFileHelper() const ...@@ -799,14 +837,15 @@ ModelTree::writeModelFileHelper() const
if constexpr (sparse) if constexpr (sparse)
{ {
// NB: we iterate over the Jacobian reordered in column-major order // NB: we iterate over the Jacobian reordered in column-major order
// Indices of rows and columns are output in M_ and the JSON file (since they are constant) // Indices of rows and columns are output in M_ and the JSON file (since they are
for (int k {0}; // constant)
const auto &[row_col, d1] : jacobian_sparse_column_major_order) for (int k {0}; const auto& [row_col, d1] : jacobian_sparse_column_major_order)
{ {
d_output[1] << "g1_v" << LEFT_ARRAY_SUBSCRIPT(output_type) d_output[1] << "g1_v" << LEFT_ARRAY_SUBSCRIPT(output_type)
<< k + ARRAY_SUBSCRIPT_OFFSET(output_type) << k + ARRAY_SUBSCRIPT_OFFSET(output_type)
<< RIGHT_ARRAY_SUBSCRIPT(output_type) << "="; << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=";
d1->writeOutput(d_output[1], output_type, temp_term_union, temporary_terms_idxs, tef_terms); d1->writeOutput(d_output[1], output_type, temp_term_union, temporary_terms_idxs,
tef_terms);
d_output[1] << ";" << endl; d_output[1] << ";" << endl;
k++; k++;
} }
...@@ -823,8 +862,8 @@ ModelTree::writeModelFileHelper() const ...@@ -823,8 +862,8 @@ ModelTree::writeModelFileHelper() const
else else
d_output[1] << eq + getJacobianCol(var, sparse) * equations.size(); d_output[1] << eq + getJacobianCol(var, sparse) * equations.size();
d_output[1] << RIGHT_ARRAY_SUBSCRIPT(output_type) << "="; d_output[1] << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=";
d1->writeOutput(d_output[1], output_type, d1->writeOutput(d_output[1], output_type, temp_term_union, temporary_terms_idxs,
temp_term_union, temporary_terms_idxs, tef_terms); tef_terms);
d_output[1] << ";" << endl; d_output[1] << ";" << endl;
} }
} }
...@@ -842,13 +881,13 @@ ModelTree::writeModelFileHelper() const ...@@ -842,13 +881,13 @@ ModelTree::writeModelFileHelper() const
/* List non-zero elements of the tensor in row-major order (this is /* List non-zero elements of the tensor in row-major order (this is
suitable for the k-order solver according to Normann). */ suitable for the k-order solver according to Normann). */
// Tensor indices are output in M_ and the JSON file (since they are constant) // Tensor indices are output in M_ and the JSON file (since they are constant)
for (int k {0}; for (int k {0}; const auto& [vidx, d] : derivatives[i])
const auto &[vidx, d] : derivatives[i])
{ {
d_output[i] << "g" << i << "_v" << LEFT_ARRAY_SUBSCRIPT(output_type) d_output[i] << "g" << i << "_v" << LEFT_ARRAY_SUBSCRIPT(output_type)
<< k + ARRAY_SUBSCRIPT_OFFSET(output_type) << k + ARRAY_SUBSCRIPT_OFFSET(output_type)
<< RIGHT_ARRAY_SUBSCRIPT(output_type) << "="; << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=";
d->writeOutput(d_output[i], output_type, temp_term_union, temporary_terms_idxs, tef_terms); d->writeOutput(d_output[i], output_type, temp_term_union, temporary_terms_idxs,
tef_terms);
d_output[i] << ";" << endl; d_output[i] << ";" << endl;
k++; k++;
} }
...@@ -877,23 +916,24 @@ ModelTree::writeModelFileHelper() const ...@@ -877,23 +916,24 @@ ModelTree::writeModelFileHelper() const
if constexpr (isJuliaOutput(output_type)) if constexpr (isJuliaOutput(output_type))
{ {
d_output[i] << " g" << i << "[" << eq + 1 << "," << col_idx + 1 << "] = "; d_output[i] << " g" << i << "[" << eq + 1 << "," << col_idx + 1 << "] = ";
d->writeOutput(d_output[i], output_type, temp_term_union, temporary_terms_idxs, tef_terms); d->writeOutput(d_output[i], output_type, temp_term_union, temporary_terms_idxs,
tef_terms);
d_output[i] << endl; d_output[i] << endl;
} }
else else
{ {
i_output << "g" << i << "_i" << LEFT_ARRAY_SUBSCRIPT(output_type) i_output << "g" << i << "_i" << LEFT_ARRAY_SUBSCRIPT(output_type)
<< k + ARRAY_SUBSCRIPT_OFFSET(output_type) << k + ARRAY_SUBSCRIPT_OFFSET(output_type)
<< RIGHT_ARRAY_SUBSCRIPT(output_type) << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << eq + 1 << ";" << endl;
<< "=" << eq + 1 << ";" << endl;
j_output << "g" << i << "_j" << LEFT_ARRAY_SUBSCRIPT(output_type) j_output << "g" << i << "_j" << LEFT_ARRAY_SUBSCRIPT(output_type)
<< k + ARRAY_SUBSCRIPT_OFFSET(output_type) << k + ARRAY_SUBSCRIPT_OFFSET(output_type)
<< RIGHT_ARRAY_SUBSCRIPT(output_type) << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << col_idx + 1 << ";"
<< "=" << col_idx + 1 << ";" << endl; << endl;
v_output << "g" << i << "_v" << LEFT_ARRAY_SUBSCRIPT(output_type) v_output << "g" << i << "_v" << LEFT_ARRAY_SUBSCRIPT(output_type)
<< k + ARRAY_SUBSCRIPT_OFFSET(output_type) << k + ARRAY_SUBSCRIPT_OFFSET(output_type)
<< RIGHT_ARRAY_SUBSCRIPT(output_type) << "="; << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=";
d->writeOutput(v_output, output_type, temp_term_union, temporary_terms_idxs, tef_terms); d->writeOutput(v_output, output_type, temp_term_union, temporary_terms_idxs,
tef_terms);
v_output << ";" << endl; v_output << ";" << endl;
k++; k++;
...@@ -902,7 +942,8 @@ ModelTree::writeModelFileHelper() const ...@@ -902,7 +942,8 @@ ModelTree::writeModelFileHelper() const
// Output symetric elements at order 2 // Output symetric elements at order 2
if (i == 2 && vidx[1] != vidx[2]) if (i == 2 && vidx[1] != vidx[2])
{ {
int col_idx_sym{getJacobianCol(vidx[2], sparse) * getJacobianColsNbr(sparse) + getJacobianCol(vidx[1], sparse)}; int col_idx_sym {getJacobianCol(vidx[2], sparse) * getJacobianColsNbr(sparse)
+ getJacobianCol(vidx[1], sparse)};
if constexpr (isJuliaOutput(output_type)) if constexpr (isJuliaOutput(output_type))
d_output[2] << " g2[" << eq + 1 << "," << col_idx_sym + 1 << "] = " d_output[2] << " g2[" << eq + 1 << "," << col_idx_sym + 1 << "] = "
...@@ -911,12 +952,12 @@ ModelTree::writeModelFileHelper() const ...@@ -911,12 +952,12 @@ ModelTree::writeModelFileHelper() const
{ {
i_output << "g" << i << "_i" << LEFT_ARRAY_SUBSCRIPT(output_type) i_output << "g" << i << "_i" << LEFT_ARRAY_SUBSCRIPT(output_type)
<< k + ARRAY_SUBSCRIPT_OFFSET(output_type) << k + ARRAY_SUBSCRIPT_OFFSET(output_type)
<< RIGHT_ARRAY_SUBSCRIPT(output_type) << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << eq + 1 << ";"
<< "=" << eq + 1 << ";" << endl; << endl;
j_output << "g" << i << "_j" << LEFT_ARRAY_SUBSCRIPT(output_type) j_output << "g" << i << "_j" << LEFT_ARRAY_SUBSCRIPT(output_type)
<< k + ARRAY_SUBSCRIPT_OFFSET(output_type) << k + ARRAY_SUBSCRIPT_OFFSET(output_type)
<< RIGHT_ARRAY_SUBSCRIPT(output_type) << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << col_idx_sym + 1
<< "=" << col_idx_sym + 1 << ";" << endl; << ";" << endl;
v_output << "g" << i << "_v" << LEFT_ARRAY_SUBSCRIPT(output_type) v_output << "g" << i << "_v" << LEFT_ARRAY_SUBSCRIPT(output_type)
<< k + ARRAY_SUBSCRIPT_OFFSET(output_type) << k + ARRAY_SUBSCRIPT_OFFSET(output_type)
<< RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << RIGHT_ARRAY_SUBSCRIPT(output_type) << "="
...@@ -935,7 +976,8 @@ ModelTree::writeModelFileHelper() const ...@@ -935,7 +976,8 @@ ModelTree::writeModelFileHelper() const
if constexpr (isMatlabOutput(output_type)) if constexpr (isMatlabOutput(output_type))
{ {
// Check that we don't have more than 32 nested parenthesis because MATLAB does not suppor this. See Issue #1201 // Check that we don't have more than 32 nested parenthesis because MATLAB does not suppor
// this. See Issue #1201
map<string, string> tmp_paren_vars; map<string, string> tmp_paren_vars;
bool message_printed {false}; bool message_printed {false};
for (auto& it : tt_output) for (auto& it : tt_output)
...@@ -953,8 +995,7 @@ ModelTree::writeModelCFile(const string &basename, const string &mexext, ...@@ -953,8 +995,7 @@ ModelTree::writeModelCFile(const string &basename, const string &mexext,
const filesystem::path& matlabroot) const const filesystem::path& matlabroot) const
{ {
ofstream output; ofstream output;
auto open_file = [&output](const filesystem::path &p) auto open_file = [&output](const filesystem::path& p) {
{
output.open(p, ios::out | ios::binary); output.open(p, ios::out | ios::binary);
if (!output.is_open()) if (!output.is_open())
{ {
...@@ -965,10 +1006,10 @@ ModelTree::writeModelCFile(const string &basename, const string &mexext, ...@@ -965,10 +1006,10 @@ ModelTree::writeModelCFile(const string &basename, const string &mexext,
const filesystem::path model_src_dir {filesystem::path {basename} / "model" / "src"}; const filesystem::path model_src_dir {filesystem::path {basename} / "model" / "src"};
auto [d_output, tt_output] = writeModelFileHelper<dynamic ? ExprNodeOutputType::CDynamicModel : ExprNodeOutputType::CStaticModel>(); auto [d_output, tt_output] = writeModelFileHelper<dynamic ? ExprNodeOutputType::CDynamicModel
: ExprNodeOutputType::CStaticModel>();
vector<filesystem::path> header_files, object_files; vector<filesystem::path> header_files, object_files;
// TODO: when C++20 support is complete, mark the following strings constexpr
const string prefix {dynamic ? "dynamic_" : "static_"}; const string prefix {dynamic ? "dynamic_" : "static_"};
const string ss_it_argin {dynamic ? ", const double *restrict steady_state, int it_" : ""}; const string ss_it_argin {dynamic ? ", const double *restrict steady_state, int it_" : ""};
const string ss_it_argout {dynamic ? ", steady_state, it_" : ""}; const string ss_it_argout {dynamic ? ", steady_state, it_" : ""};
...@@ -979,7 +1020,10 @@ ModelTree::writeModelCFile(const string &basename, const string &mexext, ...@@ -979,7 +1020,10 @@ ModelTree::writeModelCFile(const string &basename, const string &mexext,
{ {
const string funcname {prefix + (i == 0 ? "resid" : "g" + to_string(i))}; const string funcname {prefix + (i == 0 ? "resid" : "g" + to_string(i))};
const string prototype_tt { "void " + funcname + "_tt(const double *restrict y, const double *restrict x" + nb_row_x_argin + ", const double *restrict params" + ss_it_argin + ", double *restrict T)" }; const string prototype_tt {"void " + funcname
+ "_tt(const double *restrict y, const double *restrict x"
+ nb_row_x_argin + ", const double *restrict params" + ss_it_argin
+ ", double *restrict T)"};
const filesystem::path header_tt {model_src_dir / (funcname + "_tt.h")}; const filesystem::path header_tt {model_src_dir / (funcname + "_tt.h")};
open_file(header_tt); open_file(header_tt);
...@@ -996,29 +1040,26 @@ ModelTree::writeModelCFile(const string &basename, const string &mexext, ...@@ -996,29 +1040,26 @@ ModelTree::writeModelCFile(const string &basename, const string &mexext,
output << endl output << endl
<< prototype_tt << endl << prototype_tt << endl
<< "{" << endl << "{" << endl
<< tt_output[i].str() << tt_output[i].str() << "}" << endl
<< "}" << endl
<< endl; << endl;
output.close(); output.close();
object_files.push_back(compileMEX(model_src_dir, funcname + "_tt" , mexext, { source_tt }, object_files.push_back(
matlabroot, false)); compileMEX(model_src_dir, funcname + "_tt", mexext, {source_tt}, matlabroot, false));
const string prototype_main const string prototype_main {[&funcname, &ss_it_argin, &nb_row_x_argin, i] {
{ string p = "void " + funcname + "(const double *restrict y, const double *restrict x"
[&funcname, &ss_it_argin, &nb_row_x_argin, i] + nb_row_x_argin + ", const double *restrict params" + ss_it_argin
{ + ", const double *restrict T, ";
string p = "void " + funcname + "(const double *restrict y, const double *restrict x" + nb_row_x_argin + ", const double *restrict params" + ss_it_argin + ", const double *restrict T, ";
if (i == 0) if (i == 0)
p += "double *restrict residual"; p += "double *restrict residual";
else if (i == 1) else if (i == 1)
p += "double *restrict g1"; p += "double *restrict g1";
else else
p += "double *restrict g" + to_string(i) + "_i, double *restrict g" + p += "double *restrict g" + to_string(i) + "_i, double *restrict g" + to_string(i)
to_string(i) + "_j, double *restrict g" + to_string(i) + "_v"; + "_j, double *restrict g" + to_string(i) + "_v";
p += ")"; p += ")";
return p; return p;
}() }()};
};
const filesystem::path header_main {model_src_dir / (funcname + ".h")}; const filesystem::path header_main {model_src_dir / (funcname + ".h")};
open_file(header_main); open_file(header_main);
...@@ -1033,21 +1074,23 @@ ModelTree::writeModelCFile(const string &basename, const string &mexext, ...@@ -1033,21 +1074,23 @@ ModelTree::writeModelCFile(const string &basename, const string &mexext,
<< endl; << endl;
writeCHelpersDefinition(output); writeCHelpersDefinition(output);
if (i == 0) if (i == 0)
writeCHelpersDeclaration(output); // Provide external definition of helpers in resid main file writeCHelpersDeclaration(
output); // Provide external definition of helpers in resid main file
output << endl output << endl
<< prototype_main << endl << prototype_main << endl
<< "{" << endl << "{" << endl
<< d_output[i].str() << d_output[i].str() << "}" << endl
<< "}" << endl
<< endl; << endl;
output.close(); output.close();
object_files.push_back(compileMEX(model_src_dir, funcname, mexext, { source_main }, object_files.push_back(
matlabroot, false)); compileMEX(model_src_dir, funcname, mexext, {source_main}, matlabroot, false));
} }
const filesystem::path filename {model_src_dir / (dynamic ? "dynamic.c" : "static.c")}; const filesystem::path filename {model_src_dir / (dynamic ? "dynamic.c" : "static.c")};
const int ntt { static_cast<int>(temporary_terms_derivatives[0].size() + temporary_terms_derivatives[1].size() + temporary_terms_derivatives[2].size() + temporary_terms_derivatives[3].size()) }; const int ntt {static_cast<int>(
temporary_terms_derivatives[0].size() + temporary_terms_derivatives[1].size()
+ temporary_terms_derivatives[2].size() + temporary_terms_derivatives[3].size())};
open_file(filename); open_file(filename);
output << "/*" << endl output << "/*" << endl
...@@ -1066,8 +1109,10 @@ ModelTree::writeModelCFile(const string &basename, const string &mexext, ...@@ -1066,8 +1109,10 @@ ModelTree::writeModelCFile(const string &basename, const string &mexext,
<< "void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])" << endl << "void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])" << endl
<< "{" << endl; << "{" << endl;
if constexpr (dynamic) if constexpr (dynamic)
output << " if (nlhs > " << min(computed_derivs_order + 1, 4) << ")" << endl output
<< R"( mexErrMsgTxt("Derivatives of higher order than computed have been requested");)" << endl << " if (nlhs > " << min(computed_derivs_order + 1, 4) << ")" << endl
<< R"( mexErrMsgTxt("Derivatives of higher order than computed have been requested");)"
<< endl
<< " if (nrhs != 5)" << endl << " if (nrhs != 5)" << endl
<< R"( mexErrMsgTxt("Requires exactly 5 input arguments");)" << endl; << R"( mexErrMsgTxt("Requires exactly 5 input arguments");)" << endl;
else else
...@@ -1090,27 +1135,38 @@ ModelTree::writeModelCFile(const string &basename, const string &mexext, ...@@ -1090,27 +1135,38 @@ ModelTree::writeModelCFile(const string &basename, const string &mexext,
<< " {" << endl << " {" << endl
<< " plhs[0] = mxCreateDoubleMatrix(" << equations.size() << ",1, mxREAL);" << endl << " plhs[0] = mxCreateDoubleMatrix(" << equations.size() << ",1, mxREAL);" << endl
<< " double *residual = mxGetPr(plhs[0]);" << endl << " double *residual = mxGetPr(plhs[0]);" << endl
<< " " << prefix << "resid_tt(y, x" << nb_row_x_argout << ", params" << ss_it_argout << ", T);" << endl << " " << prefix << "resid_tt(y, x" << nb_row_x_argout << ", params" << ss_it_argout
<< " " << prefix << "resid(y, x" << nb_row_x_argout << ", params" << ss_it_argout << ", T, residual);" << endl << ", T);" << endl
<< " " << prefix << "resid(y, x" << nb_row_x_argout << ", params" << ss_it_argout
<< ", T, residual);" << endl
<< " }" << endl << " }" << endl
<< endl << endl
<< " if (nlhs >= 2)" << endl << " if (nlhs >= 2)" << endl
<< " {" << endl << " {" << endl
<< " plhs[1] = mxCreateDoubleMatrix(" << equations.size() << ", " << getJacobianColsNbr(false) << ", mxREAL);" << endl << " plhs[1] = mxCreateDoubleMatrix(" << equations.size() << ", "
<< getJacobianColsNbr(false) << ", mxREAL);" << endl
<< " double *g1 = mxGetPr(plhs[1]);" << endl << " double *g1 = mxGetPr(plhs[1]);" << endl
<< " " << prefix << "g1_tt(y, x" << nb_row_x_argout << ", params" << ss_it_argout << ", T);" << endl << " " << prefix << "g1_tt(y, x" << nb_row_x_argout << ", params" << ss_it_argout
<< " " << prefix << "g1(y, x" << nb_row_x_argout << ", params" << ss_it_argout << ", T, g1);" << endl << ", T);" << endl
<< " " << prefix << "g1(y, x" << nb_row_x_argout << ", params" << ss_it_argout
<< ", T, g1);" << endl
<< " }" << endl << " }" << endl
<< endl << endl
<< " if (nlhs >= 3)" << endl << " if (nlhs >= 3)" << endl
<< " {" << endl << " {" << endl
<< " mxArray *g2_i = mxCreateDoubleMatrix(" << NNZDerivatives[2] << ", " << 1 << ", mxREAL);" << endl << " mxArray *g2_i = mxCreateDoubleMatrix(" << NNZDerivatives[2] << ", " << 1
<< " mxArray *g2_j = mxCreateDoubleMatrix(" << NNZDerivatives[2] << ", " << 1 << ", mxREAL);" << endl << ", mxREAL);" << endl
<< " mxArray *g2_v = mxCreateDoubleMatrix(" << NNZDerivatives[2] << ", " << 1 << ", mxREAL);" << endl << " mxArray *g2_j = mxCreateDoubleMatrix(" << NNZDerivatives[2] << ", " << 1
<< " " << prefix << "g2_tt(y, x" << nb_row_x_argout << ", params" << ss_it_argout << ", T);" << endl << ", mxREAL);" << endl
<< " " << prefix << "g2(y, x" << nb_row_x_argout << ", params" << ss_it_argout << ", T, mxGetPr(g2_i), mxGetPr(g2_j), mxGetPr(g2_v));" << endl << " mxArray *g2_v = mxCreateDoubleMatrix(" << NNZDerivatives[2] << ", " << 1
<< ", mxREAL);" << endl
<< " " << prefix << "g2_tt(y, x" << nb_row_x_argout << ", params" << ss_it_argout
<< ", T);" << endl
<< " " << prefix << "g2(y, x" << nb_row_x_argout << ", params" << ss_it_argout
<< ", T, mxGetPr(g2_i), mxGetPr(g2_j), mxGetPr(g2_v));" << endl
<< " mxArray *m = mxCreateDoubleScalar(" << equations.size() << ");" << endl << " mxArray *m = mxCreateDoubleScalar(" << equations.size() << ");" << endl
<< " mxArray *n = mxCreateDoubleScalar(" << getJacobianColsNbr(false)*getJacobianColsNbr(false) << ");" << endl << " mxArray *n = mxCreateDoubleScalar("
<< getJacobianColsNbr(false) * getJacobianColsNbr(false) << ");" << endl
<< " mxArray *plhs_sparse[1], *prhs_sparse[5] = { g2_i, g2_j, g2_v, m, n };" << endl << " mxArray *plhs_sparse[1], *prhs_sparse[5] = { g2_i, g2_j, g2_v, m, n };" << endl
<< R"( mexCallMATLAB(1, plhs_sparse, 5, prhs_sparse, "sparse");)" << endl << R"( mexCallMATLAB(1, plhs_sparse, 5, prhs_sparse, "sparse");)" << endl
<< " plhs[2] = plhs_sparse[0];" << endl << " plhs[2] = plhs_sparse[0];" << endl
...@@ -1124,13 +1180,20 @@ ModelTree::writeModelCFile(const string &basename, const string &mexext, ...@@ -1124,13 +1180,20 @@ ModelTree::writeModelCFile(const string &basename, const string &mexext,
if constexpr (dynamic) if constexpr (dynamic)
output << " if (nlhs >= 4)" << endl output << " if (nlhs >= 4)" << endl
<< " {" << endl << " {" << endl
<< " mxArray *g3_i = mxCreateDoubleMatrix(" << NNZDerivatives[3] << ", " << 1 << ", mxREAL);" << endl << " mxArray *g3_i = mxCreateDoubleMatrix(" << NNZDerivatives[3] << ", " << 1
<< " mxArray *g3_j = mxCreateDoubleMatrix(" << NNZDerivatives[3] << ", " << 1 << ", mxREAL);" << endl << ", mxREAL);" << endl
<< " mxArray *g3_v = mxCreateDoubleMatrix(" << NNZDerivatives[3] << ", " << 1 << ", mxREAL);" << endl << " mxArray *g3_j = mxCreateDoubleMatrix(" << NNZDerivatives[3] << ", " << 1
<< " " << prefix << "g3_tt(y, x" << nb_row_x_argout << ", params" << ss_it_argout << ", T);" << endl << ", mxREAL);" << endl
<< " " << prefix << "g3(y, x" << nb_row_x_argout << ", params" << ss_it_argout << ", T, mxGetPr(g3_i), mxGetPr(g3_j), mxGetPr(g3_v));" << endl << " mxArray *g3_v = mxCreateDoubleMatrix(" << NNZDerivatives[3] << ", " << 1
<< ", mxREAL);" << endl
<< " " << prefix << "g3_tt(y, x" << nb_row_x_argout << ", params" << ss_it_argout
<< ", T);" << endl
<< " " << prefix << "g3(y, x" << nb_row_x_argout << ", params" << ss_it_argout
<< ", T, mxGetPr(g3_i), mxGetPr(g3_j), mxGetPr(g3_v));" << endl
<< " mxArray *m = mxCreateDoubleScalar(" << equations.size() << ");" << endl << " mxArray *m = mxCreateDoubleScalar(" << equations.size() << ");" << endl
<< " mxArray *n = mxCreateDoubleScalar(" << getJacobianColsNbr(false)*getJacobianColsNbr(false)*getJacobianColsNbr(false) << ");" << endl << " mxArray *n = mxCreateDoubleScalar("
<< getJacobianColsNbr(false) * getJacobianColsNbr(false) * getJacobianColsNbr(false)
<< ");" << endl
<< " mxArray *plhs_sparse[1], *prhs_sparse[5] = { g3_i, g3_j, g3_v, m, n };" << endl << " mxArray *plhs_sparse[1], *prhs_sparse[5] = { g3_i, g3_j, g3_v, m, n };" << endl
<< R"( mexCallMATLAB(1, plhs_sparse, 5, prhs_sparse, "sparse");)" << endl << R"( mexCallMATLAB(1, plhs_sparse, 5, prhs_sparse, "sparse");)" << endl
<< " plhs[3] = plhs_sparse[0];" << endl << " plhs[3] = plhs_sparse[0];" << endl
...@@ -1142,12 +1205,12 @@ ModelTree::writeModelCFile(const string &basename, const string &mexext, ...@@ -1142,12 +1205,12 @@ ModelTree::writeModelCFile(const string &basename, const string &mexext,
<< " }" << endl << " }" << endl
<< endl; << endl;
output << " free(T);" << endl output << " free(T);" << endl << "}" << endl;
<< "}" << endl;
output.close(); output.close();
object_files.push_back(filename); object_files.push_back(filename);
compileMEX(packageDir(basename), dynamic ? "dynamic" : "static", mexext, object_files, matlabroot); compileMEX(packageDir(basename), dynamic ? "dynamic" : "static", mexext, object_files,
matlabroot);
} }
template<ExprNodeOutputType output_type> template<ExprNodeOutputType output_type>
...@@ -1159,17 +1222,19 @@ ModelTree::writePerBlockHelper(int blk, ostream &output, temporary_terms_t &temp ...@@ -1159,17 +1222,19 @@ ModelTree::writePerBlockHelper(int blk, ostream &output, temporary_terms_t &temp
// The equations // The equations
deriv_node_temp_terms_t tef_terms; deriv_node_temp_terms_t tef_terms;
auto write_eq_tt = [&](int eq) auto write_eq_tt = [&](int eq) {
{
for (auto it : blocks_temporary_terms[blk][eq]) for (auto it : blocks_temporary_terms[blk][eq])
{ {
if (dynamic_cast<AbstractExternalFunctionNode*>(it)) if (dynamic_cast<AbstractExternalFunctionNode*>(it))
it->writeExternalFunctionOutput(output, output_type, temporary_terms, blocks_temporary_terms_idxs, tef_terms); it->writeExternalFunctionOutput(output, output_type, temporary_terms,
blocks_temporary_terms_idxs, tef_terms);
output << " "; output << " ";
it->writeOutput(output, output_type, blocks_temporary_terms[blk][eq], blocks_temporary_terms_idxs, tef_terms); it->writeOutput(output, output_type, blocks_temporary_terms[blk][eq],
blocks_temporary_terms_idxs, tef_terms);
output << '='; output << '=';
it->writeOutput(output, output_type, temporary_terms, blocks_temporary_terms_idxs, tef_terms); it->writeOutput(output, output_type, temporary_terms, blocks_temporary_terms_idxs,
tef_terms);
temporary_terms.insert(it); temporary_terms.insert(it);
output << ';' << endl; output << ';' << endl;
} }
...@@ -1229,8 +1294,8 @@ ModelTree::writePerBlockHelper(int blk, ostream &output, temporary_terms_t &temp ...@@ -1229,8 +1294,8 @@ ModelTree::writePerBlockHelper(int blk, ostream &output, temporary_terms_t &temp
} }
template<ExprNodeOutputType output_type> template<ExprNodeOutputType output_type>
tuple<ostringstream, ostringstream, ostringstream, ostringstream, tuple<ostringstream, ostringstream, ostringstream, ostringstream, ostringstream, ostringstream,
ostringstream, ostringstream, ostringstream> ostringstream>
ModelTree::writeParamsDerivativesFileHelper() const ModelTree::writeParamsDerivativesFileHelper() const
{ {
static_assert(!isCOutput(output_type), "C output is not implemented"); static_assert(!isCOutput(output_type), "C output is not implemented");
...@@ -1243,7 +1308,8 @@ ModelTree::writeParamsDerivativesFileHelper() const ...@@ -1243,7 +1308,8 @@ ModelTree::writeParamsDerivativesFileHelper() const
ostringstream rpp_output; // 2nd deriv of residuals w.r.t. parameters ostringstream rpp_output; // 2nd deriv of residuals w.r.t. parameters
ostringstream gpp_output; // 2nd deriv of Jacobian w.r.t. parameters ostringstream gpp_output; // 2nd deriv of Jacobian w.r.t. parameters
ostringstream hp_output; // 1st deriv. of Hessian w.r.t. parameters ostringstream hp_output; // 1st deriv. of Hessian w.r.t. parameters
ostringstream g3p_output; // 1st deriv. of 3rd deriv. matrix w.r.t. parameters (only in dynamic case) ostringstream
g3p_output; // 1st deriv. of 3rd deriv. matrix w.r.t. parameters (only in dynamic case)
temporary_terms_t temp_term_union; temporary_terms_t temp_term_union;
deriv_node_temp_terms_t tef_terms; deriv_node_temp_terms_t tef_terms;
...@@ -1260,7 +1326,8 @@ ModelTree::writeParamsDerivativesFileHelper() const ...@@ -1260,7 +1326,8 @@ ModelTree::writeParamsDerivativesFileHelper() const
rp_output << "rp" << LEFT_ARRAY_SUBSCRIPT(output_type) << eq + 1 << ", " << param_col rp_output << "rp" << LEFT_ARRAY_SUBSCRIPT(output_type) << eq + 1 << ", " << param_col
<< RIGHT_ARRAY_SUBSCRIPT(output_type) << " = "; << RIGHT_ARRAY_SUBSCRIPT(output_type) << " = ";
d1->writeOutput(rp_output, output_type, temp_term_union, params_derivs_temporary_terms_idxs, tef_terms); d1->writeOutput(rp_output, output_type, temp_term_union, params_derivs_temporary_terms_idxs,
tef_terms);
rp_output << ";" << endl; rp_output << ";" << endl;
} }
...@@ -1271,14 +1338,14 @@ ModelTree::writeParamsDerivativesFileHelper() const ...@@ -1271,14 +1338,14 @@ ModelTree::writeParamsDerivativesFileHelper() const
int var_col {getJacobianCol(var, sparse) + 1}; int var_col {getJacobianCol(var, sparse) + 1};
int param_col {getTypeSpecificIDByDerivID(param) + 1}; int param_col {getTypeSpecificIDByDerivID(param) + 1};
gp_output << "gp" << LEFT_ARRAY_SUBSCRIPT(output_type) << eq+1 << ", " << var_col gp_output << "gp" << LEFT_ARRAY_SUBSCRIPT(output_type) << eq + 1 << ", " << var_col << ", "
<< ", " << param_col << RIGHT_ARRAY_SUBSCRIPT(output_type) << " = "; << param_col << RIGHT_ARRAY_SUBSCRIPT(output_type) << " = ";
d2->writeOutput(gp_output, output_type, temp_term_union, params_derivs_temporary_terms_idxs, tef_terms); d2->writeOutput(gp_output, output_type, temp_term_union, params_derivs_temporary_terms_idxs,
tef_terms);
gp_output << ";" << endl; gp_output << ";" << endl;
} }
for (int i {1}; for (int i {1}; const auto& [indices, d2] : params_derivatives.at({0, 2}))
const auto &[indices, d2] : params_derivatives.at({ 0, 2 }))
{ {
auto [eq, param1, param2] {vectorToTuple<3>(indices)}; auto [eq, param1, param2] {vectorToTuple<3>(indices)};
...@@ -1293,7 +1360,8 @@ ModelTree::writeParamsDerivativesFileHelper() const ...@@ -1293,7 +1360,8 @@ ModelTree::writeParamsDerivativesFileHelper() const
<< RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << param2_col << ";" << endl << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << param2_col << ";" << endl
<< "rpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",4" << "rpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",4"
<< RIGHT_ARRAY_SUBSCRIPT(output_type) << "="; << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=";
d2->writeOutput(rpp_output, output_type, temp_term_union, params_derivs_temporary_terms_idxs, tef_terms); d2->writeOutput(rpp_output, output_type, temp_term_union, params_derivs_temporary_terms_idxs,
tef_terms);
rpp_output << ";" << endl; rpp_output << ";" << endl;
i++; i++;
...@@ -1308,15 +1376,14 @@ ModelTree::writeParamsDerivativesFileHelper() const ...@@ -1308,15 +1376,14 @@ ModelTree::writeParamsDerivativesFileHelper() const
<< "rpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",3" << "rpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",3"
<< RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << param1_col << ";" << endl << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << param1_col << ";" << endl
<< "rpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",4" << "rpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",4"
<< RIGHT_ARRAY_SUBSCRIPT(output_type) << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=rpp"
<< "=rpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i-1 << ",4" << LEFT_ARRAY_SUBSCRIPT(output_type) << i - 1 << ",4"
<< RIGHT_ARRAY_SUBSCRIPT(output_type) << ";" << endl; << RIGHT_ARRAY_SUBSCRIPT(output_type) << ";" << endl;
i++; i++;
} }
} }
for (int i {1}; for (int i {1}; const auto& [indices, d2] : params_derivatives.at({1, 2}))
const auto &[indices, d2] : params_derivatives.at({ 1, 2 }))
{ {
auto [eq, var, param1, param2] {vectorToTuple<4>(indices)}; auto [eq, var, param1, param2] {vectorToTuple<4>(indices)};
...@@ -1334,7 +1401,8 @@ ModelTree::writeParamsDerivativesFileHelper() const ...@@ -1334,7 +1401,8 @@ ModelTree::writeParamsDerivativesFileHelper() const
<< RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << param2_col << ";" << endl << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << param2_col << ";" << endl
<< "gpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",5" << "gpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",5"
<< RIGHT_ARRAY_SUBSCRIPT(output_type) << "="; << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=";
d2->writeOutput(gpp_output, output_type, temp_term_union, params_derivs_temporary_terms_idxs, tef_terms); d2->writeOutput(gpp_output, output_type, temp_term_union, params_derivs_temporary_terms_idxs,
tef_terms);
gpp_output << ";" << endl; gpp_output << ";" << endl;
i++; i++;
...@@ -1351,15 +1419,14 @@ ModelTree::writeParamsDerivativesFileHelper() const ...@@ -1351,15 +1419,14 @@ ModelTree::writeParamsDerivativesFileHelper() const
<< "gpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",4" << "gpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",4"
<< RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << param1_col << ";" << endl << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << param1_col << ";" << endl
<< "gpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",5" << "gpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",5"
<< RIGHT_ARRAY_SUBSCRIPT(output_type) << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=gpp"
<< "=gpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i-1 << ",5" << LEFT_ARRAY_SUBSCRIPT(output_type) << i - 1 << ",5"
<< RIGHT_ARRAY_SUBSCRIPT(output_type) << ";" << endl; << RIGHT_ARRAY_SUBSCRIPT(output_type) << ";" << endl;
i++; i++;
} }
} }
for (int i {1}; for (int i {1}; const auto& [indices, d2] : params_derivatives.at({2, 1}))
const auto &[indices, d2] : params_derivatives.at({ 2, 1 }))
{ {
auto [eq, var1, var2, param] {vectorToTuple<4>(indices)}; auto [eq, var1, var2, param] {vectorToTuple<4>(indices)};
...@@ -1377,7 +1444,8 @@ ModelTree::writeParamsDerivativesFileHelper() const ...@@ -1377,7 +1444,8 @@ ModelTree::writeParamsDerivativesFileHelper() const
<< RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << param_col << ";" << endl << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << param_col << ";" << endl
<< "hp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",5" << "hp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",5"
<< RIGHT_ARRAY_SUBSCRIPT(output_type) << "="; << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=";
d2->writeOutput(hp_output, output_type, temp_term_union, params_derivs_temporary_terms_idxs, tef_terms); d2->writeOutput(hp_output, output_type, temp_term_union, params_derivs_temporary_terms_idxs,
tef_terms);
hp_output << ";" << endl; hp_output << ";" << endl;
i++; i++;
...@@ -1394,8 +1462,8 @@ ModelTree::writeParamsDerivativesFileHelper() const ...@@ -1394,8 +1462,8 @@ ModelTree::writeParamsDerivativesFileHelper() const
<< "hp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",4" << "hp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",4"
<< RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << param_col << ";" << endl << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << param_col << ";" << endl
<< "hp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",5" << "hp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",5"
<< RIGHT_ARRAY_SUBSCRIPT(output_type) << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=hp"
<< "=hp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i-1 << ",5" << LEFT_ARRAY_SUBSCRIPT(output_type) << i - 1 << ",5"
<< RIGHT_ARRAY_SUBSCRIPT(output_type) << ";" << endl; << RIGHT_ARRAY_SUBSCRIPT(output_type) << ";" << endl;
i++; i++;
} }
...@@ -1403,8 +1471,7 @@ ModelTree::writeParamsDerivativesFileHelper() const ...@@ -1403,8 +1471,7 @@ ModelTree::writeParamsDerivativesFileHelper() const
if constexpr (output_type == ExprNodeOutputType::matlabDynamicModel if constexpr (output_type == ExprNodeOutputType::matlabDynamicModel
|| output_type == ExprNodeOutputType::juliaDynamicModel) || output_type == ExprNodeOutputType::juliaDynamicModel)
for (int i {1}; for (int i {1}; const auto& [indices, d2] : params_derivatives.at({3, 1}))
const auto &[indices, d2] : params_derivatives.at({ 3, 1 }))
{ {
auto [eq, var1, var2, var3, param] {vectorToTuple<5>(indices)}; auto [eq, var1, var2, var3, param] {vectorToTuple<5>(indices)};
...@@ -1425,7 +1492,8 @@ ModelTree::writeParamsDerivativesFileHelper() const ...@@ -1425,7 +1492,8 @@ ModelTree::writeParamsDerivativesFileHelper() const
<< RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << param_col << ";" << endl << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << param_col << ";" << endl
<< "g3p" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",6" << "g3p" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",6"
<< RIGHT_ARRAY_SUBSCRIPT(output_type) << "="; << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=";
d2->writeOutput(g3p_output, output_type, temp_term_union, params_derivs_temporary_terms_idxs, tef_terms); d2->writeOutput(g3p_output, output_type, temp_term_union,
params_derivs_temporary_terms_idxs, tef_terms);
g3p_output << ";" << endl; g3p_output << ";" << endl;
i++; i++;
...@@ -1433,7 +1501,8 @@ ModelTree::writeParamsDerivativesFileHelper() const ...@@ -1433,7 +1501,8 @@ ModelTree::writeParamsDerivativesFileHelper() const
if constexpr (isMatlabOutput(output_type)) if constexpr (isMatlabOutput(output_type))
{ {
// Check that we don't have more than 32 nested parenthesis because MATLAB does not support this. See Issue #1201 // Check that we don't have more than 32 nested parenthesis because MATLAB does not support
// this. See Issue #1201
map<string, string> tmp_paren_vars; map<string, string> tmp_paren_vars;
bool message_printed {false}; bool message_printed {false};
fixNestedParenthesis(tt_output, tmp_paren_vars, message_printed); fixNestedParenthesis(tt_output, tmp_paren_vars, message_printed);
...@@ -1445,32 +1514,34 @@ ModelTree::writeParamsDerivativesFileHelper() const ...@@ -1445,32 +1514,34 @@ ModelTree::writeParamsDerivativesFileHelper() const
fixNestedParenthesis(g3p_output, tmp_paren_vars, message_printed); fixNestedParenthesis(g3p_output, tmp_paren_vars, message_printed);
} }
return { move(tt_output), move(rp_output), move(gp_output), return {move(tt_output), move(rp_output), move(gp_output), move(rpp_output),
move(rpp_output), move(gpp_output), move(hp_output), move(g3p_output) }; move(gpp_output), move(hp_output), move(g3p_output)};
} }
template<ExprNodeBytecodeOutputType output_type> template<ExprNodeBytecodeOutputType output_type>
void void
ModelTree::writeBytecodeTemporaryTerms(const temporary_terms_t& tt, ModelTree::writeBytecodeTemporaryTerms(const temporary_terms_t& tt,
temporary_terms_t& temporary_terms_union, temporary_terms_t& temporary_terms_union,
BytecodeWriter &code_file, Bytecode::Writer& code_file,
deriv_node_temp_terms_t& tef_terms) const deriv_node_temp_terms_t& tef_terms) const
{ {
for (auto it : tt) for (auto it : tt)
{ {
if (dynamic_cast<AbstractExternalFunctionNode*>(it)) if (dynamic_cast<AbstractExternalFunctionNode*>(it))
it->writeBytecodeExternalFunctionOutput(code_file, output_type, temporary_terms_union, temporary_terms_idxs, tef_terms); it->writeBytecodeExternalFunctionOutput(code_file, output_type, temporary_terms_union,
temporary_terms_idxs, tef_terms);
int idx {temporary_terms_idxs.at(it)}; int idx {temporary_terms_idxs.at(it)};
code_file << FNUMEXPR_{ExpressionType::TemporaryTerm, idx}; code_file << Bytecode::FNUMEXPR {Bytecode::ExpressionType::TemporaryTerm, idx};
it->writeBytecodeOutput(code_file, output_type, temporary_terms_union, temporary_terms_idxs, tef_terms); it->writeBytecodeOutput(code_file, output_type, temporary_terms_union, temporary_terms_idxs,
tef_terms);
static_assert(output_type == ExprNodeBytecodeOutputType::dynamicModel static_assert(output_type == ExprNodeBytecodeOutputType::dynamicModel
|| output_type == ExprNodeBytecodeOutputType::staticModel); || output_type == ExprNodeBytecodeOutputType::staticModel);
if constexpr (output_type == ExprNodeBytecodeOutputType::dynamicModel) if constexpr (output_type == ExprNodeBytecodeOutputType::dynamicModel)
code_file << FSTPT_{idx}; code_file << Bytecode::FSTPT {idx};
else else
code_file << FSTPST_{idx}; code_file << Bytecode::FSTPST {idx};
temporary_terms_union.insert(it); temporary_terms_union.insert(it);
} }
...@@ -1478,14 +1549,16 @@ ModelTree::writeBytecodeTemporaryTerms(const temporary_terms_t &tt, ...@@ -1478,14 +1549,16 @@ ModelTree::writeBytecodeTemporaryTerms(const temporary_terms_t &tt,
template<ExprNodeBytecodeOutputType output_type> template<ExprNodeBytecodeOutputType output_type>
void void
ModelTree::writeBytecodeModelEquations(BytecodeWriter &code_file, const temporary_terms_t &temporary_terms, const deriv_node_temp_terms_t &tef_terms) const ModelTree::writeBytecodeModelEquations(Bytecode::Writer& code_file,
const temporary_terms_t& temporary_terms,
const deriv_node_temp_terms_t& tef_terms) const
{ {
for (int eq {0}; eq < static_cast<int>(equations.size()); eq++) for (int eq {0}; eq < static_cast<int>(equations.size()); eq++)
{ {
BinaryOpNode* eq_node {equations[eq]}; BinaryOpNode* eq_node {equations[eq]};
expr_t lhs {eq_node->arg1}, rhs {eq_node->arg2}; expr_t lhs {eq_node->arg1}, rhs {eq_node->arg2};
code_file << FNUMEXPR_{ExpressionType::ModelEquation, eq}; code_file << Bytecode::FNUMEXPR {Bytecode::ExpressionType::ModelEquation, eq};
// Test if the right hand side of the equation is empty. // Test if the right-hand side of the equation is empty.
double vrhs {1.0}; double vrhs {1.0};
try try
{ {
...@@ -1495,44 +1568,50 @@ ModelTree::writeBytecodeModelEquations(BytecodeWriter &code_file, const temporar ...@@ -1495,44 +1568,50 @@ ModelTree::writeBytecodeModelEquations(BytecodeWriter &code_file, const temporar
{ {
} }
if (vrhs != 0) // The right hand side of the equation is not empty ⇒ residual=lhs-rhs if (vrhs != 0) // The right-hand side of the equation is not empty ⇒ residual=lhs-rhs
{ {
lhs->writeBytecodeOutput(code_file, output_type, temporary_terms, temporary_terms_idxs, tef_terms); lhs->writeBytecodeOutput(code_file, output_type, temporary_terms, temporary_terms_idxs,
rhs->writeBytecodeOutput(code_file, output_type, temporary_terms, temporary_terms_idxs, tef_terms); tef_terms);
rhs->writeBytecodeOutput(code_file, output_type, temporary_terms, temporary_terms_idxs,
tef_terms);
code_file << FBINARY_{BinaryOpcode::minus} << FSTPR_{eq}; code_file << Bytecode::FBINARY {BinaryOpcode::minus} << Bytecode::FSTPR {eq};
} }
else // The right hand side of the equation is empty ⇒ residual=lhs else // The right-hand side of the equation is empty ⇒ residual=lhs
{ {
lhs->writeBytecodeOutput(code_file, output_type, temporary_terms, temporary_terms_idxs, tef_terms); lhs->writeBytecodeOutput(code_file, output_type, temporary_terms, temporary_terms_idxs,
code_file << FSTPR_{eq}; tef_terms);
code_file << Bytecode::FSTPR {eq};
} }
} }
} }
template<bool dynamic> template<bool dynamic>
void void
ModelTree::writeBytecodeHelper(BytecodeWriter &code_file) const ModelTree::writeBytecodeHelper(Bytecode::Writer& code_file) const
{ {
constexpr ExprNodeBytecodeOutputType output_type { dynamic ? ExprNodeBytecodeOutputType::dynamicModel : ExprNodeBytecodeOutputType::staticModel }; constexpr ExprNodeBytecodeOutputType output_type {
dynamic ? ExprNodeBytecodeOutputType::dynamicModel : ExprNodeBytecodeOutputType::staticModel};
temporary_terms_t temporary_terms_union; temporary_terms_t temporary_terms_union;
deriv_node_temp_terms_t tef_terms; deriv_node_temp_terms_t tef_terms;
writeBytecodeTemporaryTerms<output_type>(temporary_terms_derivatives[0], temporary_terms_union, code_file, tef_terms); writeBytecodeTemporaryTerms<output_type>(temporary_terms_derivatives[0], temporary_terms_union,
code_file, tef_terms);
writeBytecodeModelEquations<output_type>(code_file, temporary_terms_union, tef_terms); writeBytecodeModelEquations<output_type>(code_file, temporary_terms_union, tef_terms);
code_file << FENDEQU_{}; code_file << Bytecode::FENDEQU {};
// Temporary terms for the Jacobian // Temporary terms for the Jacobian
writeBytecodeTemporaryTerms<output_type>(temporary_terms_derivatives[1], temporary_terms_union, code_file, tef_terms); writeBytecodeTemporaryTerms<output_type>(temporary_terms_derivatives[1], temporary_terms_union,
code_file, tef_terms);
// Get the current code_file position and jump if “evaluate” mode // Get the current code_file position and jump if “evaluate” mode
int pos_jmpifeval {code_file.getInstructionCounter()}; int pos_jmpifeval {code_file.getInstructionCounter()};
code_file << FJMPIFEVAL_{0}; // Use 0 as jump offset for the time being code_file << Bytecode::FJMPIFEVAL {0}; // Use 0 as jump offset for the time being
// The Jacobian in “simulate” mode // The Jacobian in “simulate” mode
vector<vector<tuple<int, int, int>>> my_derivatives(symbol_table.endo_nbr());; vector<vector<tuple<int, int, int>>> my_derivatives(symbol_table.endo_nbr());
int count_u {symbol_table.endo_nbr()}; int count_u {symbol_table.endo_nbr()};
for (const auto& [indices, d1] : derivatives[1]) for (const auto& [indices, d1] : derivatives[1])
{ {
...@@ -1542,49 +1621,53 @@ ModelTree::writeBytecodeHelper(BytecodeWriter &code_file) const ...@@ -1542,49 +1621,53 @@ ModelTree::writeBytecodeHelper(BytecodeWriter &code_file) const
int tsid {getTypeSpecificIDByDerivID(deriv_id)}; int tsid {getTypeSpecificIDByDerivID(deriv_id)};
int lag {getLagByDerivID(deriv_id)}; int lag {getLagByDerivID(deriv_id)};
if constexpr (dynamic) if constexpr (dynamic)
code_file << FNUMEXPR_{ExpressionType::FirstEndoDerivative, eq, tsid, lag}; code_file << Bytecode::FNUMEXPR {Bytecode::ExpressionType::FirstEndoDerivative, eq,
tsid, lag};
else else
code_file << FNUMEXPR_{ExpressionType::FirstEndoDerivative, eq, tsid}; code_file << Bytecode::FNUMEXPR {Bytecode::ExpressionType::FirstEndoDerivative, eq,
tsid};
if (!my_derivatives[eq].size()) if (!my_derivatives[eq].size())
my_derivatives[eq].clear(); my_derivatives[eq].clear();
my_derivatives[eq].emplace_back(tsid, lag, count_u); my_derivatives[eq].emplace_back(tsid, lag, count_u);
d1->writeBytecodeOutput(code_file, output_type, temporary_terms_union, temporary_terms_idxs, tef_terms); d1->writeBytecodeOutput(code_file, output_type, temporary_terms_union,
temporary_terms_idxs, tef_terms);
if constexpr (dynamic) if constexpr (dynamic)
code_file << FSTPU_{count_u}; code_file << Bytecode::FSTPU {count_u};
else else
code_file << FSTPSU_{count_u}; code_file << Bytecode::FSTPSU {count_u};
count_u++; count_u++;
} }
} }
for (int i {0}; i < symbol_table.endo_nbr(); i++) for (int i {0}; i < symbol_table.endo_nbr(); i++)
{ {
code_file << FLDR_{i}; code_file << Bytecode::FLDR {i};
if (my_derivatives[i].size()) if (my_derivatives[i].size())
{ {
for (bool first_term {true}; for (bool first_term {true}; const auto& [tsid, lag, uidx] : my_derivatives[i])
const auto &[tsid, lag, uidx] : my_derivatives[i])
{ {
if constexpr (dynamic) if constexpr (dynamic)
code_file << FLDU_{uidx} << FLDV_{SymbolType::endogenous, tsid, lag}; code_file << Bytecode::FLDU {uidx}
<< Bytecode::FLDV {SymbolType::endogenous, tsid, lag};
else else
code_file << FLDSU_{uidx} << FLDSV_{SymbolType::endogenous, tsid}; code_file << Bytecode::FLDSU {uidx}
code_file << FBINARY_{BinaryOpcode::times}; << Bytecode::FLDSV {SymbolType::endogenous, tsid};
code_file << Bytecode::FBINARY {BinaryOpcode::times};
if (!exchange(first_term, false)) if (!exchange(first_term, false))
code_file << FBINARY_{BinaryOpcode::plus}; code_file << Bytecode::FBINARY {BinaryOpcode::plus};
} }
code_file << FBINARY_{BinaryOpcode::minus}; code_file << Bytecode::FBINARY {BinaryOpcode::minus};
} }
if constexpr (dynamic) if constexpr (dynamic)
code_file << FSTPU_{i}; code_file << Bytecode::FSTPU {i};
else else
code_file << FSTPSU_{i}; code_file << Bytecode::FSTPSU {i};
} }
// Jump unconditionally after the block // Jump unconditionally after the block
int pos_jmp {code_file.getInstructionCounter()}; int pos_jmp {code_file.getInstructionCounter()};
code_file << FJMP_{0}; // Use 0 as jump offset for the time being code_file << Bytecode::FJMP {0}; // Use 0 as jump offset for the time being
// Update jump offset for previous JMPIFEVAL // Update jump offset for previous JMPIFEVAL
code_file.overwriteInstruction(pos_jmpifeval, FJMPIFEVAL_{pos_jmp-pos_jmpifeval}); code_file.overwriteInstruction(pos_jmpifeval, Bytecode::FJMPIFEVAL {pos_jmp - pos_jmpifeval});
// The Jacobian in “evaluate” mode // The Jacobian in “evaluate” mode
for (const auto& [indices, d1] : derivatives[1]) for (const auto& [indices, d1] : derivatives[1])
...@@ -1596,56 +1679,59 @@ ModelTree::writeBytecodeHelper(BytecodeWriter &code_file) const ...@@ -1596,56 +1679,59 @@ ModelTree::writeBytecodeHelper(BytecodeWriter &code_file) const
if constexpr (dynamic) if constexpr (dynamic)
{ {
ExpressionType expr_type; Bytecode::ExpressionType expr_type;
switch (type) switch (type)
{ {
case SymbolType::endogenous: case SymbolType::endogenous:
expr_type = ExpressionType::FirstEndoDerivative; expr_type = Bytecode::ExpressionType::FirstEndoDerivative;
break; break;
case SymbolType::exogenous: case SymbolType::exogenous:
expr_type = ExpressionType::FirstExoDerivative; expr_type = Bytecode::ExpressionType::FirstExoDerivative;
break; break;
case SymbolType::exogenousDet: case SymbolType::exogenousDet:
expr_type = ExpressionType::FirstExodetDerivative; expr_type = Bytecode::ExpressionType::FirstExodetDerivative;
break; break;
default: default:
assert(false); assert(false);
break; break;
} }
code_file << FNUMEXPR_{expr_type, eq, tsid, lag}; code_file << Bytecode::FNUMEXPR {expr_type, eq, tsid, lag};
} }
else else
{ {
assert(type == SymbolType::endogenous); assert(type == SymbolType::endogenous);
code_file << FNUMEXPR_{ExpressionType::FirstEndoDerivative, eq, tsid}; code_file << Bytecode::FNUMEXPR {Bytecode::ExpressionType::FirstEndoDerivative, eq, tsid};
} }
d1->writeBytecodeOutput(code_file, output_type, temporary_terms_union, temporary_terms_idxs, tef_terms); d1->writeBytecodeOutput(code_file, output_type, temporary_terms_union, temporary_terms_idxs,
tef_terms);
if constexpr (dynamic) if constexpr (dynamic)
{ {
// Bytecode MEX uses a separate matrix for exogenous and exodet Jacobians // Bytecode MEX uses a separate matrix for exogenous and exodet Jacobians
int jacob_col {type == SymbolType::endogenous ? getJacobianCol(deriv_id, false) : tsid}; int jacob_col {type == SymbolType::endogenous ? getJacobianCol(deriv_id, false) : tsid};
code_file << FSTPG3_{eq, tsid, lag, jacob_col}; code_file << Bytecode::FSTPG2 {eq, jacob_col};
} }
else else
code_file << FSTPG2_{eq, tsid}; code_file << Bytecode::FSTPG2 {eq, tsid};
} }
// Update jump offset for previous JMP // Update jump offset for previous JMP
int pos_end_block {code_file.getInstructionCounter()}; int pos_end_block {code_file.getInstructionCounter()};
code_file.overwriteInstruction(pos_jmp, FJMP_{pos_end_block-pos_jmp-1}); code_file.overwriteInstruction(pos_jmp, Bytecode::FJMP {pos_end_block - pos_jmp - 1});
code_file << FENDBLOCK_{} << FEND_{}; code_file << Bytecode::FENDBLOCK {} << Bytecode::FEND {};
} }
template<bool dynamic> template<bool dynamic>
void void
ModelTree::writeBlockBytecodeHelper(BytecodeWriter &code_file, int block, temporary_terms_t &temporary_terms_union) const ModelTree::writeBlockBytecodeHelper(Bytecode::Writer& code_file, int block,
temporary_terms_t& temporary_terms_union) const
{ {
constexpr ExprNodeBytecodeOutputType output_type constexpr ExprNodeBytecodeOutputType output_type {
{ dynamic ? ExprNodeBytecodeOutputType::dynamicModel : ExprNodeBytecodeOutputType::staticModel }; dynamic ? ExprNodeBytecodeOutputType::dynamicModel : ExprNodeBytecodeOutputType::staticModel};
constexpr ExprNodeBytecodeOutputType assignment_lhs_output_type constexpr ExprNodeBytecodeOutputType assignment_lhs_output_type {
{ dynamic ? ExprNodeBytecodeOutputType::dynamicAssignmentLHS : ExprNodeBytecodeOutputType::staticAssignmentLHS }; dynamic ? ExprNodeBytecodeOutputType::dynamicAssignmentLHS
: ExprNodeBytecodeOutputType::staticAssignmentLHS};
const BlockSimulationType simulation_type {blocks[block].simulation_type}; const BlockSimulationType simulation_type {blocks[block].simulation_type};
const int block_size {blocks[block].size}; const int block_size {blocks[block].size};
...@@ -1654,19 +1740,21 @@ ModelTree::writeBlockBytecodeHelper(BytecodeWriter &code_file, int block, tempor ...@@ -1654,19 +1740,21 @@ ModelTree::writeBlockBytecodeHelper(BytecodeWriter &code_file, int block, tempor
deriv_node_temp_terms_t tef_terms; deriv_node_temp_terms_t tef_terms;
auto write_eq_tt = [&](int eq) auto write_eq_tt = [&](int eq) {
{
for (auto it : blocks_temporary_terms[block][eq]) for (auto it : blocks_temporary_terms[block][eq])
{ {
if (dynamic_cast<AbstractExternalFunctionNode*>(it)) if (dynamic_cast<AbstractExternalFunctionNode*>(it))
it->writeBytecodeExternalFunctionOutput(code_file, output_type, temporary_terms_union, blocks_temporary_terms_idxs, tef_terms); it->writeBytecodeExternalFunctionOutput(code_file, output_type, temporary_terms_union,
blocks_temporary_terms_idxs, tef_terms);
code_file << FNUMEXPR_{ExpressionType::TemporaryTerm, blocks_temporary_terms_idxs.at(it)}; code_file << Bytecode::FNUMEXPR {Bytecode::ExpressionType::TemporaryTerm,
it->writeBytecodeOutput(code_file, output_type, temporary_terms_union, blocks_temporary_terms_idxs, tef_terms); blocks_temporary_terms_idxs.at(it)};
it->writeBytecodeOutput(code_file, output_type, temporary_terms_union,
blocks_temporary_terms_idxs, tef_terms);
if constexpr (dynamic) if constexpr (dynamic)
code_file << FSTPT_{blocks_temporary_terms_idxs.at(it)}; code_file << Bytecode::FSTPT {blocks_temporary_terms_idxs.at(it)};
else else
code_file << FSTPST_{blocks_temporary_terms_idxs.at(it)}; code_file << Bytecode::FSTPST {blocks_temporary_terms_idxs.at(it)};
temporary_terms_union.insert(it); temporary_terms_union.insert(it);
} }
}; };
...@@ -1692,9 +1780,12 @@ ModelTree::writeBlockBytecodeHelper(BytecodeWriter &code_file, int block, tempor ...@@ -1692,9 +1780,12 @@ ModelTree::writeBlockBytecodeHelper(BytecodeWriter &code_file, int block, tempor
} }
else else
assert(equ_type == EquationType::evaluate); assert(equ_type == EquationType::evaluate);
code_file << FNUMEXPR_{ExpressionType::ModelEquation, getBlockEquationID(block, i)}; code_file << Bytecode::FNUMEXPR {Bytecode::ExpressionType::ModelEquation,
rhs->writeBytecodeOutput(code_file, output_type, temporary_terms_union, blocks_temporary_terms_idxs, tef_terms); getBlockEquationID(block, i)};
lhs->writeBytecodeOutput(code_file, assignment_lhs_output_type, temporary_terms_union, blocks_temporary_terms_idxs, tef_terms); rhs->writeBytecodeOutput(code_file, output_type, temporary_terms_union,
blocks_temporary_terms_idxs, tef_terms);
lhs->writeBytecodeOutput(code_file, assignment_lhs_output_type, temporary_terms_union,
blocks_temporary_terms_idxs, tef_terms);
break; break;
case BlockSimulationType::solveBackwardComplete: case BlockSimulationType::solveBackwardComplete:
case BlockSimulationType::solveForwardComplete: case BlockSimulationType::solveForwardComplete:
...@@ -1705,10 +1796,14 @@ ModelTree::writeBlockBytecodeHelper(BytecodeWriter &code_file, int block, tempor ...@@ -1705,10 +1796,14 @@ ModelTree::writeBlockBytecodeHelper(BytecodeWriter &code_file, int block, tempor
[[fallthrough]]; [[fallthrough]];
case BlockSimulationType::solveBackwardSimple: case BlockSimulationType::solveBackwardSimple:
case BlockSimulationType::solveForwardSimple: case BlockSimulationType::solveForwardSimple:
code_file << FNUMEXPR_{ExpressionType::ModelEquation, getBlockEquationID(block, i)}; code_file << Bytecode::FNUMEXPR {Bytecode::ExpressionType::ModelEquation,
lhs->writeBytecodeOutput(code_file, output_type, temporary_terms_union, blocks_temporary_terms_idxs, tef_terms); getBlockEquationID(block, i)};
rhs->writeBytecodeOutput(code_file, output_type, temporary_terms_union, blocks_temporary_terms_idxs, tef_terms); lhs->writeBytecodeOutput(code_file, output_type, temporary_terms_union,
code_file << FBINARY_{BinaryOpcode::minus} << FSTPR_{i - block_recursive}; blocks_temporary_terms_idxs, tef_terms);
rhs->writeBytecodeOutput(code_file, output_type, temporary_terms_union,
blocks_temporary_terms_idxs, tef_terms);
code_file << Bytecode::FBINARY {BinaryOpcode::minus}
<< Bytecode::FSTPR {i - block_recursive};
break; break;
} }
} }
...@@ -1723,11 +1818,11 @@ ModelTree::writeBlockBytecodeHelper(BytecodeWriter &code_file, int block, tempor ...@@ -1723,11 +1818,11 @@ ModelTree::writeBlockBytecodeHelper(BytecodeWriter &code_file, int block, tempor
be needed in subsequent blocks. */ be needed in subsequent blocks. */
write_eq_tt(blocks[block].size); write_eq_tt(blocks[block].size);
code_file << FENDEQU_{}; code_file << Bytecode::FENDEQU {};
// Get the current code_file position and jump if evaluating // Get the current code_file position and jump if evaluating
int pos_jmpifeval {code_file.getInstructionCounter()}; int pos_jmpifeval {code_file.getInstructionCounter()};
code_file << FJMPIFEVAL_{0}; // Use 0 as jump offset for the time being code_file << Bytecode::FJMPIFEVAL {0}; // Use 0 as jump offset for the time being
/* Write the derivatives for the “simulate” mode (not needed if the block /* Write the derivatives for the “simulate” mode (not needed if the block
is of type “evaluate backward/forward”) */ is of type “evaluate backward/forward”) */
...@@ -1741,14 +1836,16 @@ ModelTree::writeBlockBytecodeHelper(BytecodeWriter &code_file, int block, tempor ...@@ -1741,14 +1836,16 @@ ModelTree::writeBlockBytecodeHelper(BytecodeWriter &code_file, int block, tempor
{ {
int eqr {getBlockEquationID(block, 0)}; int eqr {getBlockEquationID(block, 0)};
int varr {getBlockVariableID(block, 0)}; int varr {getBlockVariableID(block, 0)};
code_file << FNUMEXPR_{ExpressionType::FirstEndoDerivative, eqr, varr, 0}; code_file << Bytecode::FNUMEXPR {Bytecode::ExpressionType::FirstEndoDerivative, eqr,
varr, 0};
// Get contemporaneous derivative of the single variable in the block // Get contemporaneous derivative of the single variable in the block
if (auto it {blocks_derivatives[block].find({0, 0, 0})}; if (auto it {blocks_derivatives[block].find({0, 0, 0})};
it != blocks_derivatives[block].end()) it != blocks_derivatives[block].end())
it->second->writeBytecodeOutput(code_file, output_type, temporary_terms_union, blocks_temporary_terms_idxs, tef_terms); it->second->writeBytecodeOutput(code_file, output_type, temporary_terms_union,
blocks_temporary_terms_idxs, tef_terms);
else else
code_file << FLDZ_{}; code_file << Bytecode::FLDZ {};
code_file << FSTPG_{0}; code_file << Bytecode::FSTPG {};
} }
break; break;
...@@ -1760,8 +1857,7 @@ ModelTree::writeBlockBytecodeHelper(BytecodeWriter &code_file, int block, tempor ...@@ -1760,8 +1857,7 @@ ModelTree::writeBlockBytecodeHelper(BytecodeWriter &code_file, int block, tempor
// For each equation, stores a list of tuples (index_u, var, lag) // For each equation, stores a list of tuples (index_u, var, lag)
vector<vector<tuple<int, int, int>>> Uf(symbol_table.endo_nbr()); vector<vector<tuple<int, int, int>>> Uf(symbol_table.endo_nbr());
for (int count_u {block_mfs}; for (int count_u {block_mfs}; const auto& [indices, d1] : blocks_derivatives[block])
const auto &[indices, d1] : blocks_derivatives[block])
{ {
const auto& [eq, var, lag] {indices}; const auto& [eq, var, lag] {indices};
int eqr {getBlockEquationID(block, eq)}; int eqr {getBlockEquationID(block, eq)};
...@@ -1774,12 +1870,14 @@ ModelTree::writeBlockBytecodeHelper(BytecodeWriter &code_file, int block, tempor ...@@ -1774,12 +1870,14 @@ ModelTree::writeBlockBytecodeHelper(BytecodeWriter &code_file, int block, tempor
&& (simulation_type == BlockSimulationType::solveForwardComplete && (simulation_type == BlockSimulationType::solveForwardComplete
|| simulation_type == BlockSimulationType::solveBackwardComplete)) || simulation_type == BlockSimulationType::solveBackwardComplete))
continue; continue;
code_file << FNUMEXPR_{ExpressionType::FirstEndoDerivative, eqr, varr, lag}; code_file << Bytecode::FNUMEXPR {Bytecode::ExpressionType::FirstEndoDerivative,
d1->writeBytecodeOutput(code_file, output_type, temporary_terms_union, blocks_temporary_terms_idxs, tef_terms); eqr, varr, lag};
d1->writeBytecodeOutput(code_file, output_type, temporary_terms_union,
blocks_temporary_terms_idxs, tef_terms);
if constexpr (dynamic) if constexpr (dynamic)
code_file << FSTPU_{count_u}; code_file << Bytecode::FSTPU {count_u};
else else
code_file << FSTPSU_{count_u}; code_file << Bytecode::FSTPSU {count_u};
Uf[eqr].emplace_back(count_u, varr, lag); Uf[eqr].emplace_back(count_u, varr, lag);
count_u++; count_u++;
} }
...@@ -1787,25 +1885,25 @@ ModelTree::writeBlockBytecodeHelper(BytecodeWriter &code_file, int block, tempor ...@@ -1787,25 +1885,25 @@ ModelTree::writeBlockBytecodeHelper(BytecodeWriter &code_file, int block, tempor
for (int i {0}; i < block_size; i++) for (int i {0}; i < block_size; i++)
if (i >= block_recursive) if (i >= block_recursive)
{ {
code_file << FLDR_{i-block_recursive} << FLDZ_{}; code_file << Bytecode::FLDR {i - block_recursive} << Bytecode::FLDZ {};
int eqr {getBlockEquationID(block, i)}; int eqr {getBlockEquationID(block, i)};
for (const auto& [index_u, var, lag] : Uf[eqr]) for (const auto& [index_u, var, lag] : Uf[eqr])
{ {
if constexpr (dynamic) if constexpr (dynamic)
code_file << FLDU_{index_u} code_file << Bytecode::FLDU {index_u}
<< FLDV_{SymbolType::endogenous, var, lag}; << Bytecode::FLDV {SymbolType::endogenous, var, lag};
else else
code_file << FLDSU_{index_u} code_file << Bytecode::FLDSU {index_u}
<< FLDSV_{SymbolType::endogenous, var}; << Bytecode::FLDSV {SymbolType::endogenous, var};
code_file << FBINARY_{BinaryOpcode::times} code_file << Bytecode::FBINARY {BinaryOpcode::times}
<< FBINARY_{BinaryOpcode::plus}; << Bytecode::FBINARY {BinaryOpcode::plus};
} }
code_file << FBINARY_{BinaryOpcode::minus}; code_file << Bytecode::FBINARY {BinaryOpcode::minus};
if constexpr (dynamic) if constexpr (dynamic)
code_file << FSTPU_{i - block_recursive}; code_file << Bytecode::FSTPU {i - block_recursive};
else else
code_file << FSTPSU_{i - block_recursive}; code_file << Bytecode::FSTPSU {i - block_recursive};
} }
} }
break; break;
...@@ -1816,9 +1914,9 @@ ModelTree::writeBlockBytecodeHelper(BytecodeWriter &code_file, int block, tempor ...@@ -1816,9 +1914,9 @@ ModelTree::writeBlockBytecodeHelper(BytecodeWriter &code_file, int block, tempor
// Jump unconditionally after the block // Jump unconditionally after the block
int pos_jmp {code_file.getInstructionCounter()}; int pos_jmp {code_file.getInstructionCounter()};
code_file << FJMP_{0}; // Use 0 as jump offset for the time being code_file << Bytecode::FJMP {0}; // Use 0 as jump offset for the time being
// Update jump offset for previous JMPIFEVAL // Update jump offset for previous JMPIFEVAL
code_file.overwriteInstruction(pos_jmpifeval, FJMPIFEVAL_{pos_jmp-pos_jmpifeval}); code_file.overwriteInstruction(pos_jmpifeval, Bytecode::FJMPIFEVAL {pos_jmp - pos_jmpifeval});
// Write the derivatives for the “evaluate” mode // Write the derivatives for the “evaluate” mode
for (const auto& [indices, d] : blocks_derivatives[block]) for (const auto& [indices, d] : blocks_derivatives[block])
...@@ -1826,20 +1924,20 @@ ModelTree::writeBlockBytecodeHelper(BytecodeWriter &code_file, int block, tempor ...@@ -1826,20 +1924,20 @@ ModelTree::writeBlockBytecodeHelper(BytecodeWriter &code_file, int block, tempor
const auto& [eq, var, lag] {indices}; const auto& [eq, var, lag] {indices};
int eqr {getBlockEquationID(block, eq)}; int eqr {getBlockEquationID(block, eq)};
int varr {getBlockVariableID(block, var)}; int varr {getBlockVariableID(block, var)};
code_file << FNUMEXPR_{ExpressionType::FirstEndoDerivative, eqr, varr, lag}; code_file << Bytecode::FNUMEXPR {Bytecode::ExpressionType::FirstEndoDerivative, eqr, varr,
d->writeBytecodeOutput(code_file, output_type, temporary_terms_union, blocks_temporary_terms_idxs, tef_terms); lag};
d->writeBytecodeOutput(code_file, output_type, temporary_terms_union,
blocks_temporary_terms_idxs, tef_terms);
assert(eq >= block_recursive); assert(eq >= block_recursive);
if constexpr(dynamic) code_file << Bytecode::FSTPG2 {eq - block_recursive,
code_file << FSTPG3_{eq-block_recursive, var, lag, getBlockJacobianEndoCol(block, var, lag)}; getBlockJacobianEndoCol(block, var, lag)};
else
code_file << FSTPG2_{eq-block_recursive, getBlockJacobianEndoCol(block, var, lag)};
} }
// Update jump offset for previous JMP // Update jump offset for previous JMP
int pos_end_block {code_file.getInstructionCounter()}; int pos_end_block {code_file.getInstructionCounter()};
code_file.overwriteInstruction(pos_jmp, FJMP_{pos_end_block-pos_jmp-1}); code_file.overwriteInstruction(pos_jmp, Bytecode::FJMP {pos_end_block - pos_jmp - 1});
code_file << FENDBLOCK_{}; code_file << Bytecode::FENDBLOCK {};
} }
template<bool dynamic> template<bool dynamic>
...@@ -1847,30 +1945,35 @@ pair<ostringstream, vector<ostringstream>> ...@@ -1847,30 +1945,35 @@ pair<ostringstream, vector<ostringstream>>
ModelTree::writeJsonComputingPassOutputHelper(bool writeDetails) const ModelTree::writeJsonComputingPassOutputHelper(bool writeDetails) const
{ {
ostringstream mlv_output; // Used for storing model local vars ostringstream mlv_output; // Used for storing model local vars
vector<ostringstream> d_output(derivatives.size()); // Derivatives output (at all orders, including 0=residual) vector<ostringstream> d_output(
derivatives.size()); // Derivatives output (at all orders, including 0=residual)
deriv_node_temp_terms_t tef_terms; deriv_node_temp_terms_t tef_terms;
temporary_terms_t temp_term_union; temporary_terms_t temp_term_union;
writeJsonModelLocalVariables(mlv_output, true, tef_terms); writeJsonModelLocalVariables(mlv_output, true, tef_terms);
writeJsonTemporaryTerms(temporary_terms_derivatives[0], temp_term_union, d_output[0], tef_terms, ""); writeJsonTemporaryTerms(temporary_terms_derivatives[0], temp_term_union, d_output[0], tef_terms,
"");
d_output[0] << ", "; d_output[0] << ", ";
writeJsonModelEquations(d_output[0], true); writeJsonModelEquations(d_output[0], true);
int ncols {getJacobianColsNbr(false)}; int ncols {getJacobianColsNbr(false)};
for (size_t i {1}; i < derivatives.size(); i++) for (size_t i {1}; i < derivatives.size(); i++)
{ {
string matrix_name { i == 1 ? "jacobian" : i == 2 ? "hessian" : i == 3 ? "third_derivative" : to_string(i) + "th_derivative"}; string matrix_name {i == 1 ? "jacobian"
writeJsonTemporaryTerms(temporary_terms_derivatives[i], temp_term_union, d_output[i], tef_terms, matrix_name); : i == 2 ? "hessian"
temp_term_union.insert(temporary_terms_derivatives[i].begin(), temporary_terms_derivatives[i].end()); : i == 3 ? "third_derivative"
: to_string(i) + "th_derivative"};
writeJsonTemporaryTerms(temporary_terms_derivatives[i], temp_term_union, d_output[i],
tef_terms, matrix_name);
temp_term_union.insert(temporary_terms_derivatives[i].begin(),
temporary_terms_derivatives[i].end());
d_output[i] << R"(, ")" << matrix_name << R"(": {)" d_output[i] << R"(, ")" << matrix_name << R"(": {)"
<< R"( "nrows": )" << equations.size() << R"( "nrows": )" << equations.size() << R"(, "ncols": )" << ncols
<< R"(, "ncols": )" << ncols
<< R"(, "entries": [)"; << R"(, "entries": [)";
for (bool printed_something {false}; for (bool printed_something {false}; const auto& [vidx, d] : derivatives[i])
const auto &[vidx, d] : derivatives[i])
{ {
if (exchange(printed_something, true)) if (exchange(printed_something, true))
d_output[i] << ", "; d_output[i] << ", ";
...@@ -1893,7 +1996,8 @@ ModelTree::writeJsonComputingPassOutputHelper(bool writeDetails) const ...@@ -1893,7 +1996,8 @@ ModelTree::writeJsonComputingPassOutputHelper(bool writeDetails) const
if (i == 2 && vidx[1] != vidx[2]) // Symmetric elements in hessian if (i == 2 && vidx[1] != vidx[2]) // Symmetric elements in hessian
{ {
int col_idx_sym { getJacobianCol(vidx[2], false) * getJacobianColsNbr(false) + getJacobianCol(vidx[1], false)}; int col_idx_sym {getJacobianCol(vidx[2], false) * getJacobianColsNbr(false)
+ getJacobianCol(vidx[1], false)};
d_output[i] << ", " << col_idx_sym + 1; d_output[i] << ", " << col_idx_sym + 1;
} }
if (i > 1) if (i > 1)
...@@ -1902,9 +2006,11 @@ ModelTree::writeJsonComputingPassOutputHelper(bool writeDetails) const ...@@ -1902,9 +2006,11 @@ ModelTree::writeJsonComputingPassOutputHelper(bool writeDetails) const
if (writeDetails) if (writeDetails)
for (size_t j = 1; j < vidx.size(); j++) for (size_t j = 1; j < vidx.size(); j++)
{ {
d_output[i] << R"(, "var)" << (i > 1 ? to_string(j) : "") << R"(": ")" << getNameByDerivID(vidx[j]) << R"(")"; d_output[i] << R"(, "var)" << (i > 1 ? to_string(j) : "") << R"(": ")"
<< getNameByDerivID(vidx[j]) << R"(")";
if constexpr (dynamic) if constexpr (dynamic)
d_output[i] << R"(, "shift)" << (i > 1 ? to_string(j) : "") << R"(": )" << getLagByDerivID(vidx[j]); d_output[i] << R"(, "shift)" << (i > 1 ? to_string(j) : "") << R"(": )"
<< getLagByDerivID(vidx[j]);
} }
d_output[i] << R"(, "val": ")"; d_output[i] << R"(, "val": ")";
...@@ -1920,8 +2026,8 @@ ModelTree::writeJsonComputingPassOutputHelper(bool writeDetails) const ...@@ -1920,8 +2026,8 @@ ModelTree::writeJsonComputingPassOutputHelper(bool writeDetails) const
} }
template<bool dynamic> template<bool dynamic>
tuple<ostringstream, ostringstream, ostringstream, ostringstream, tuple<ostringstream, ostringstream, ostringstream, ostringstream, ostringstream, ostringstream,
ostringstream, ostringstream, ostringstream, ostringstream> ostringstream, ostringstream>
ModelTree::writeJsonParamsDerivativesHelper(bool writeDetails) const ModelTree::writeJsonParamsDerivativesHelper(bool writeDetails) const
{ {
ostringstream mlv_output; // Used for storing model local vars ostringstream mlv_output; // Used for storing model local vars
...@@ -1941,11 +2047,9 @@ ModelTree::writeJsonParamsDerivativesHelper(bool writeDetails) const ...@@ -1941,11 +2047,9 @@ ModelTree::writeJsonParamsDerivativesHelper(bool writeDetails) const
writeJsonTemporaryTerms(tts, temp_term_union, tt_output, tef_terms, "all"); writeJsonTemporaryTerms(tts, temp_term_union, tt_output, tef_terms, "all");
rp_output << R"("deriv_wrt_params": {)" rp_output << R"("deriv_wrt_params": {)"
<< R"( "neqs": )" << equations.size() << R"( "neqs": )" << equations.size() << R"(, "nparamcols": )"
<< R"(, "nparamcols": )" << symbol_table.param_nbr() << symbol_table.param_nbr() << R"(, "entries": [)";
<< R"(, "entries": [)"; for (bool printed_something {false}; const auto& [vidx, d] : params_derivatives.at({0, 1}))
for (bool printed_something {false};
const auto &[vidx, d] : params_derivatives.at({ 0, 1 }))
{ {
if (exchange(printed_something, true)) if (exchange(printed_something, true))
rp_output << ", "; rp_output << ", ";
...@@ -1971,12 +2075,10 @@ ModelTree::writeJsonParamsDerivativesHelper(bool writeDetails) const ...@@ -1971,12 +2075,10 @@ ModelTree::writeJsonParamsDerivativesHelper(bool writeDetails) const
rp_output << "]}"; rp_output << "]}";
gp_output << R"("deriv_jacobian_wrt_params": {)" gp_output << R"("deriv_jacobian_wrt_params": {)"
<< R"( "neqs": )" << equations.size() << R"( "neqs": )" << equations.size() << R"(, "nvarcols": )"
<< R"(, "nvarcols": )" << getJacobianColsNbr(false) << getJacobianColsNbr(false) << R"(, "nparamcols": )" << symbol_table.param_nbr()
<< R"(, "nparamcols": )" << symbol_table.param_nbr()
<< R"(, "entries": [)"; << R"(, "entries": [)";
for (bool printed_something {false}; for (bool printed_something {false}; const auto& [vidx, d] : params_derivatives.at({1, 1}))
const auto &[vidx, d] : params_derivatives.at({ 1, 1 }))
{ {
if (exchange(printed_something, true)) if (exchange(printed_something, true))
gp_output << ", "; gp_output << ", ";
...@@ -1991,8 +2093,7 @@ ModelTree::writeJsonParamsDerivativesHelper(bool writeDetails) const ...@@ -1991,8 +2093,7 @@ ModelTree::writeJsonParamsDerivativesHelper(bool writeDetails) const
else else
gp_output << R"({"row": )" << eq + 1; gp_output << R"({"row": )" << eq + 1;
gp_output << R"(, "var_col": )" << var_col gp_output << R"(, "var_col": )" << var_col << R"(, "param_col": )" << param_col;
<< R"(, "param_col": )" << param_col;
if (writeDetails) if (writeDetails)
{ {
...@@ -2009,12 +2110,10 @@ ModelTree::writeJsonParamsDerivativesHelper(bool writeDetails) const ...@@ -2009,12 +2110,10 @@ ModelTree::writeJsonParamsDerivativesHelper(bool writeDetails) const
gp_output << "]}"; gp_output << "]}";
rpp_output << R"("second_deriv_residuals_wrt_params": {)" rpp_output << R"("second_deriv_residuals_wrt_params": {)"
<< R"( "nrows": )" << equations.size() << R"( "nrows": )" << equations.size() << R"(, "nparam1cols": )"
<< R"(, "nparam1cols": )" << symbol_table.param_nbr() << symbol_table.param_nbr() << R"(, "nparam2cols": )" << symbol_table.param_nbr()
<< R"(, "nparam2cols": )" << symbol_table.param_nbr()
<< R"(, "entries": [)"; << R"(, "entries": [)";
for (bool printed_something {false}; for (bool printed_something {false}; const auto& [vidx, d] : params_derivatives.at({0, 2}))
const auto &[vidx, d] : params_derivatives.at({ 0, 2 }))
{ {
if (exchange(printed_something, true)) if (exchange(printed_something, true))
rpp_output << ", "; rpp_output << ", ";
...@@ -2028,8 +2127,7 @@ ModelTree::writeJsonParamsDerivativesHelper(bool writeDetails) const ...@@ -2028,8 +2127,7 @@ ModelTree::writeJsonParamsDerivativesHelper(bool writeDetails) const
rpp_output << R"({"eq": )" << eq + 1; rpp_output << R"({"eq": )" << eq + 1;
else else
rpp_output << R"({"row": )" << eq + 1; rpp_output << R"({"row": )" << eq + 1;
rpp_output << R"(, "param1_col": )" << param1_col rpp_output << R"(, "param1_col": )" << param1_col << R"(, "param2_col": )" << param2_col;
<< R"(, "param2_col": )" << param2_col;
if (writeDetails) if (writeDetails)
rpp_output << R"(, "param1": ")" << getNameByDerivID(param1) << R"(")" rpp_output << R"(, "param1": ")" << getNameByDerivID(param1) << R"(")"
...@@ -2042,13 +2140,10 @@ ModelTree::writeJsonParamsDerivativesHelper(bool writeDetails) const ...@@ -2042,13 +2140,10 @@ ModelTree::writeJsonParamsDerivativesHelper(bool writeDetails) const
rpp_output << "]}"; rpp_output << "]}";
gpp_output << R"("second_deriv_jacobian_wrt_params": {)" gpp_output << R"("second_deriv_jacobian_wrt_params": {)"
<< R"( "neqs": )" << equations.size() << R"( "neqs": )" << equations.size() << R"(, "nvarcols": )"
<< R"(, "nvarcols": )" << getJacobianColsNbr(false) << getJacobianColsNbr(false) << R"(, "nparam1cols": )" << symbol_table.param_nbr()
<< R"(, "nparam1cols": )" << symbol_table.param_nbr() << R"(, "nparam2cols": )" << symbol_table.param_nbr() << R"(, "entries": [)";
<< R"(, "nparam2cols": )" << symbol_table.param_nbr() for (bool printed_something {false}; const auto& [vidx, d] : params_derivatives.at({1, 2}))
<< R"(, "entries": [)";
for (bool printed_something {false};
const auto &[vidx, d] : params_derivatives.at({ 1, 2 }))
{ {
if (exchange(printed_something, true)) if (exchange(printed_something, true))
gpp_output << ", "; gpp_output << ", ";
...@@ -2064,8 +2159,7 @@ ModelTree::writeJsonParamsDerivativesHelper(bool writeDetails) const ...@@ -2064,8 +2159,7 @@ ModelTree::writeJsonParamsDerivativesHelper(bool writeDetails) const
else else
gpp_output << R"({"row": )" << eq + 1; gpp_output << R"({"row": )" << eq + 1;
gpp_output << R"(, "var_col": )" << var_col gpp_output << R"(, "var_col": )" << var_col << R"(, "param1_col": )" << param1_col
<< R"(, "param1_col": )" << param1_col
<< R"(, "param2_col": )" << param2_col; << R"(, "param2_col": )" << param2_col;
if (writeDetails) if (writeDetails)
...@@ -2084,13 +2178,10 @@ ModelTree::writeJsonParamsDerivativesHelper(bool writeDetails) const ...@@ -2084,13 +2178,10 @@ ModelTree::writeJsonParamsDerivativesHelper(bool writeDetails) const
gpp_output << "]}" << endl; gpp_output << "]}" << endl;
hp_output << R"("derivative_hessian_wrt_params": {)" hp_output << R"("derivative_hessian_wrt_params": {)"
<< R"( "neqs": )" << equations.size() << R"( "neqs": )" << equations.size() << R"(, "nvar1cols": )"
<< R"(, "nvar1cols": )" << getJacobianColsNbr(false) << getJacobianColsNbr(false) << R"(, "nvar2cols": )" << getJacobianColsNbr(false)
<< R"(, "nvar2cols": )" << getJacobianColsNbr(false) << R"(, "nparamcols": )" << symbol_table.param_nbr() << R"(, "entries": [)";
<< R"(, "nparamcols": )" << symbol_table.param_nbr() for (bool printed_something {false}; const auto& [vidx, d] : params_derivatives.at({2, 1}))
<< R"(, "entries": [)";
for (bool printed_something {false};
const auto &[vidx, d] : params_derivatives.at({ 2, 1 }))
{ {
if (exchange(printed_something, true)) if (exchange(printed_something, true))
hp_output << ", "; hp_output << ", ";
...@@ -2106,8 +2197,7 @@ ModelTree::writeJsonParamsDerivativesHelper(bool writeDetails) const ...@@ -2106,8 +2197,7 @@ ModelTree::writeJsonParamsDerivativesHelper(bool writeDetails) const
else else
hp_output << R"({"row": )" << eq + 1; hp_output << R"({"row": )" << eq + 1;
hp_output << R"(, "var1_col": )" << var1_col hp_output << R"(, "var1_col": )" << var1_col << R"(, "var2_col": )" << var2_col
<< R"(, "var2_col": )" << var2_col
<< R"(, "param_col": )" << param_col; << R"(, "param_col": )" << param_col;
if (writeDetails) if (writeDetails)
...@@ -2130,14 +2220,11 @@ ModelTree::writeJsonParamsDerivativesHelper(bool writeDetails) const ...@@ -2130,14 +2220,11 @@ ModelTree::writeJsonParamsDerivativesHelper(bool writeDetails) const
if constexpr (dynamic) if constexpr (dynamic)
{ {
g3p_output << R"("derivative_g3_wrt_params": {)" g3p_output << R"("derivative_g3_wrt_params": {)"
<< R"( "neqs": )" << equations.size() << R"( "neqs": )" << equations.size() << R"(, "nvar1cols": )"
<< R"(, "nvar1cols": )" << getJacobianColsNbr(false) << getJacobianColsNbr(false) << R"(, "nvar2cols": )" << getJacobianColsNbr(false)
<< R"(, "nvar2cols": )" << getJacobianColsNbr(false) << R"(, "nvar3cols": )" << getJacobianColsNbr(false) << R"(, "nparamcols": )"
<< R"(, "nvar3cols": )" << getJacobianColsNbr(false) << symbol_table.param_nbr() << R"(, "entries": [)";
<< R"(, "nparamcols": )" << symbol_table.param_nbr() for (bool printed_something {false}; const auto& [vidx, d] : params_derivatives.at({3, 1}))
<< R"(, "entries": [)";
for (bool printed_something {false};
const auto &[vidx, d] : params_derivatives.at({ 3, 1 }))
{ {
if (exchange(printed_something, true)) if (exchange(printed_something, true))
g3p_output << ", "; g3p_output << ", ";
...@@ -2154,19 +2241,18 @@ ModelTree::writeJsonParamsDerivativesHelper(bool writeDetails) const ...@@ -2154,19 +2241,18 @@ ModelTree::writeJsonParamsDerivativesHelper(bool writeDetails) const
else else
g3p_output << R"({"row": )" << eq + 1; g3p_output << R"({"row": )" << eq + 1;
g3p_output << R"(, "var1_col": )" << var1_col + 1 g3p_output << R"(, "var1_col": )" << var1_col + 1 << R"(, "var2_col": )" << var2_col + 1
<< R"(, "var2_col": )" << var2_col + 1 << R"(, "var3_col": )" << var3_col + 1 << R"(, "param_col": )"
<< R"(, "var3_col": )" << var3_col + 1 << param_col + 1;
<< R"(, "param_col": )" << param_col + 1;
if (writeDetails) if (writeDetails)
g3p_output << R"(, "var1": ")" << getNameByDerivID(var1) << R"(")" g3p_output << R"(, "var1": ")" << getNameByDerivID(var1) << R"(")"
<< R"(, "lag1": )" << getLagByDerivID(var1) << R"(, "lag1": )" << getLagByDerivID(var1) << R"(, "var2": ")"
<< R"(, "var2": ")" << getNameByDerivID(var2) << R"(")" << getNameByDerivID(var2) << R"(")"
<< R"(, "lag2": )" << getLagByDerivID(var2) << R"(, "lag2": )" << getLagByDerivID(var2) << R"(, "var3": ")"
<< R"(, "var3": ")" << getNameByDerivID(var3) << R"(")" << getNameByDerivID(var3) << R"(")"
<< R"(, "lag3": )" << getLagByDerivID(var3) << R"(, "lag3": )" << getLagByDerivID(var3) << R"(, "param": ")"
<< R"(, "param": ")" << getNameByDerivID(param) << R"(")"; << getNameByDerivID(param) << R"(")";
g3p_output << R"(, "val": ")"; g3p_output << R"(, "val": ")";
d->writeJsonOutput(g3p_output, temp_term_union, tef_terms); d->writeJsonOutput(g3p_output, temp_term_union, tef_terms);
...@@ -2179,47 +2265,10 @@ ModelTree::writeJsonParamsDerivativesHelper(bool writeDetails) const ...@@ -2179,47 +2265,10 @@ ModelTree::writeJsonParamsDerivativesHelper(bool writeDetails) const
move(rpp_output), move(gpp_output), move(hp_output), move(g3p_output)}; move(rpp_output), move(gpp_output), move(hp_output), move(g3p_output)};
} }
template<bool dynamic>
void
ModelTree::writeDriverSparseIndicesHelper(ostream &output) const
{
// TODO: when C++20 support is complete, mark this constexpr
const string model_name {dynamic ? "dynamic" : "static"};
// Write indices for the sparse Jacobian (both naive and CSC storage)
output << "M_." << model_name << "_g1_sparse_rowval = int32([";
for (const auto &[indices, d1] : jacobian_sparse_column_major_order)
output << indices.first+1 << ' ';
output << "]);" << endl
<< "M_." << model_name << "_g1_sparse_colval = int32([";
for (const auto &[indices, d1] : jacobian_sparse_column_major_order)
output << indices.second+1 << ' ';
output << "]);" << endl
<< "M_." << model_name << "_g1_sparse_colptr = int32([";
for (int it : jacobian_sparse_colptr)
output << it+1 << ' ';
output << "]);" << endl;
// Write indices for the sparse higher-order derivatives
for (int i {2}; i <= computed_derivs_order; i++)
{
output << "M_." << model_name << "_g" << i << "_sparse_indices = int32([";
for (const auto &[vidx, d] : derivatives[i])
{
for (bool row_number {true}; // First element of vidx is row number
int it : vidx)
output << (exchange(row_number, false) ? it : getJacobianCol(it, true))+1 << ' ';
output << ';' << endl;
}
output << "]);" << endl;
}
}
template<bool dynamic> template<bool dynamic>
void void
ModelTree::writeJsonSparseIndicesHelper(ostream& output) const ModelTree::writeJsonSparseIndicesHelper(ostream& output) const
{ {
// TODO: when C++20 support is complete, mark this constexpr
const string model_name {dynamic ? "dynamic" : "static"}; const string model_name {dynamic ? "dynamic" : "static"};
// Write indices for the sparse Jacobian (both naive and CSC storage) // Write indices for the sparse Jacobian (both naive and CSC storage)
...@@ -2231,8 +2280,7 @@ ModelTree::writeJsonSparseIndicesHelper(ostream &output) const ...@@ -2231,8 +2280,7 @@ ModelTree::writeJsonSparseIndicesHelper(ostream &output) const
output << ", "; output << ", ";
output << indices.first + 1; output << indices.first + 1;
} }
output << "], " << endl output << "], " << endl << '"' << model_name << R"(_g1_sparse_colval": [)";
<< '"' << model_name << R"(_g1_sparse_colval": [)";
for (bool printed_something {false}; for (bool printed_something {false};
const auto& [indices, d1] : jacobian_sparse_column_major_order) const auto& [indices, d1] : jacobian_sparse_column_major_order)
{ {
...@@ -2240,10 +2288,8 @@ ModelTree::writeJsonSparseIndicesHelper(ostream &output) const ...@@ -2240,10 +2288,8 @@ ModelTree::writeJsonSparseIndicesHelper(ostream &output) const
output << ", "; output << ", ";
output << indices.second + 1; output << indices.second + 1;
} }
output << "], " << endl output << "], " << endl << '"' << model_name << R"(_g1_sparse_colptr": [)";
<< '"' << model_name << R"(_g1_sparse_colptr": [)"; for (bool printed_something {false}; int it : jacobian_sparse_colptr)
for (bool printed_something {false};
int it : jacobian_sparse_colptr)
{ {
if (exchange(printed_something, true)) if (exchange(printed_something, true))
output << ", "; output << ", ";
...@@ -2255,14 +2301,12 @@ ModelTree::writeJsonSparseIndicesHelper(ostream &output) const ...@@ -2255,14 +2301,12 @@ ModelTree::writeJsonSparseIndicesHelper(ostream &output) const
for (int i {2}; i <= computed_derivs_order; i++) for (int i {2}; i <= computed_derivs_order; i++)
{ {
output << R"(, ")" << model_name << "_g" << i << R"(_sparse_indices": [)"; output << R"(, ")" << model_name << "_g" << i << R"(_sparse_indices": [)";
for (bool printed_something {false}; for (bool printed_something {false}; const auto& [vidx, d] : derivatives[i])
const auto &[vidx, d] : derivatives[i])
{ {
if (exchange(printed_something, true)) if (exchange(printed_something, true))
output << ", "; output << ", ";
output << '['; output << '[';
for (bool printed_something2 {false}; for (bool printed_something2 {false}; int it : vidx)
int it : vidx)
{ {
if (printed_something2) if (printed_something2)
output << ", "; output << ", ";
...@@ -2281,18 +2325,17 @@ ModelTree::writeBlockDriverSparseIndicesHelper(ostream &output) const ...@@ -2281,18 +2325,17 @@ ModelTree::writeBlockDriverSparseIndicesHelper(ostream &output) const
{ {
for (int blk {0}; blk < static_cast<int>(blocks.size()); blk++) for (int blk {0}; blk < static_cast<int>(blocks.size()); blk++)
{ {
const string struct_name { "M_.block_structure"s + (dynamic ? "" : "_stat") + ".block(" + to_string(blk+1) + ")." }; const string struct_name {"M_.block_structure"s + (dynamic ? "" : "_stat") + ".block("
+ to_string(blk + 1) + ")."};
// Write indices for the sparse Jacobian (both naive and CSC storage) // Write indices for the sparse Jacobian (both naive and CSC storage)
output << struct_name << "g1_sparse_rowval = int32(["; output << struct_name << "g1_sparse_rowval = int32([";
for (const auto& [indices, d1] : blocks_jacobian_sparse_column_major_order.at(blk)) for (const auto& [indices, d1] : blocks_jacobian_sparse_column_major_order.at(blk))
output << indices.first + 1 << ' '; output << indices.first + 1 << ' ';
output << "]);" << endl output << "]);" << endl << struct_name << "g1_sparse_colval = int32([";
<< struct_name << "g1_sparse_colval = int32([";
for (const auto& [indices, d1] : blocks_jacobian_sparse_column_major_order.at(blk)) for (const auto& [indices, d1] : blocks_jacobian_sparse_column_major_order.at(blk))
output << indices.second + 1 << ' '; output << indices.second + 1 << ' ';
output << "]);" << endl output << "]);" << endl << struct_name << "g1_sparse_colptr = int32([";
<< struct_name << "g1_sparse_colptr = int32([";
for (int it : blocks_jacobian_sparse_colptr.at(blk)) for (int it : blocks_jacobian_sparse_colptr.at(blk))
output << it + 1 << ' '; output << it + 1 << ' ';
output << "]);" << endl; output << "]);" << endl;
...@@ -2301,7 +2344,8 @@ ModelTree::writeBlockDriverSparseIndicesHelper(ostream &output) const ...@@ -2301,7 +2344,8 @@ ModelTree::writeBlockDriverSparseIndicesHelper(ostream &output) const
template<ExprNodeOutputType output_type> template<ExprNodeOutputType output_type>
void void
ModelTree::writeSparsePerBlockJacobianHelper(int blk, ostream &output, temporary_terms_t &temporary_terms) const ModelTree::writeSparsePerBlockJacobianHelper(int blk, ostream& output,
temporary_terms_t& temporary_terms) const
{ {
static_assert(isSparseModelOutput(output_type)); static_assert(isSparseModelOutput(output_type));
...@@ -2317,8 +2361,8 @@ ModelTree::writeSparsePerBlockJacobianHelper(int blk, ostream &output, temporary ...@@ -2317,8 +2361,8 @@ ModelTree::writeSparsePerBlockJacobianHelper(int blk, ostream &output, temporary
for (const auto& [row_col, d1] : blocks_jacobian_sparse_column_major_order[blk]) for (const auto& [row_col, d1] : blocks_jacobian_sparse_column_major_order[blk])
{ {
output << "g1_v" << LEFT_ARRAY_SUBSCRIPT(output_type) output << "g1_v" << LEFT_ARRAY_SUBSCRIPT(output_type)
<< k + ARRAY_SUBSCRIPT_OFFSET(output_type) << k + ARRAY_SUBSCRIPT_OFFSET(output_type) << RIGHT_ARRAY_SUBSCRIPT(output_type)
<< RIGHT_ARRAY_SUBSCRIPT(output_type) << "="; << "=";
d1->writeOutput(output, output_type, temporary_terms, blocks_temporary_terms_idxs); d1->writeOutput(output, output_type, temporary_terms, blocks_temporary_terms_idxs);
output << ";" << endl; output << ";" << endl;
k++; k++;
...@@ -2329,10 +2373,13 @@ template<bool dynamic> ...@@ -2329,10 +2373,13 @@ template<bool dynamic>
void void
ModelTree::writeSparseModelJuliaFiles(const string& basename) const ModelTree::writeSparseModelJuliaFiles(const string& basename) const
{ {
auto [d_sparse_output, tt_sparse_output] = writeModelFileHelper<dynamic ? ExprNodeOutputType::juliaSparseDynamicModel : ExprNodeOutputType::juliaSparseStaticModel>(); assert(heterogeneity_table.empty());
auto [d_sparse_output, tt_sparse_output]
= writeModelFileHelper<dynamic ? ExprNodeOutputType::juliaSparseDynamicModel
: ExprNodeOutputType::juliaSparseStaticModel>();
filesystem::path julia_dir {filesystem::path {basename} / "model" / "julia"}; filesystem::path julia_dir {filesystem::path {basename} / "model" / "julia"};
// TODO: when C++20 support is complete, mark the following strings constexpr
const string prefix {dynamic ? "SparseDynamic" : "SparseStatic"}; const string prefix {dynamic ? "SparseDynamic" : "SparseStatic"};
const string ss_argin {dynamic ? ", steady_state::Vector{<: Real}" : ""}; const string ss_argin {dynamic ? ", steady_state::Vector{<: Real}" : ""};
const string ss_argout {dynamic ? ", steady_state" : ""}; const string ss_argout {dynamic ? ", steady_state" : ""};
...@@ -2345,31 +2392,30 @@ ModelTree::writeSparseModelJuliaFiles(const string &basename) const ...@@ -2345,31 +2392,30 @@ ModelTree::writeSparseModelJuliaFiles(const string &basename) const
// ResidTT! // ResidTT!
output << "function " << prefix << "ResidTT!(T::Vector{<: Real}, " output << "function " << prefix << "ResidTT!(T::Vector{<: Real}, "
<< "y::Vector{<: Real}, x::Vector{<: Real}, params::Vector{<: Real}" << "y::Vector{<: Real}, x::Vector{<: Real}, params::Vector{<: Real}" << ss_argin << ")"
<< ss_argin << ")" << endl << endl
<< "@inbounds begin" << endl << "@inbounds begin" << endl
<< tt_sparse_output[0].str() << tt_sparse_output[0].str() << "end" << endl
<< "end" << endl
<< " return nothing" << endl << " return nothing" << endl
<< "end" << endl << endl; << "end" << endl
<< endl;
writeToFileIfModified(output, julia_dir / (prefix + "ResidTT!.jl")); writeToFileIfModified(output, julia_dir / (prefix + "ResidTT!.jl"));
ttlen += temporary_terms_derivatives[0].size(); ttlen += temporary_terms_derivatives[0].size();
// Resid! // Resid!
output.str(""); output.str("");
output << "function " << prefix << "Resid!(T::Vector{<: Real}, residual::AbstractVector{<: Real}, " output << "function " << prefix
<< "y::Vector{<: Real}, x::Vector{<: Real}, params::Vector{<: Real}" << "Resid!(T::Vector{<: Real}, residual::AbstractVector{<: Real}, "
<< ss_argin << ")" << endl << "y::Vector{<: Real}, x::Vector{<: Real}, params::Vector{<: Real}" << ss_argin << ")"
<< endl
<< " @assert length(T) >= " << ttlen << endl << " @assert length(T) >= " << ttlen << endl
<< " @assert length(residual) == " << equations.size() << endl << " @assert length(residual) == " << equations.size() << endl
<< " @assert length(y) == " << ylen << endl << " @assert length(y) == " << ylen << endl
<< " @assert length(x) == " << xlen << endl << " @assert length(x) == " << xlen << endl
<< " @assert length(params) == " << symbol_table.param_nbr() << endl << " @assert length(params) == " << symbol_table.param_nbr() << endl
<< "@inbounds begin" << endl << "@inbounds begin" << endl
<< d_sparse_output[0].str() << d_sparse_output[0].str() << "end" << endl;
<< "end" << endl; output << " return nothing" << endl << "end" << endl << endl;
output << " return nothing" << endl
<< "end" << endl << endl;
writeToFileIfModified(output, julia_dir / (prefix + "Resid!.jl")); writeToFileIfModified(output, julia_dir / (prefix + "Resid!.jl"));
// G1TT! // G1TT!
...@@ -2378,28 +2424,26 @@ ModelTree::writeSparseModelJuliaFiles(const string &basename) const ...@@ -2378,28 +2424,26 @@ ModelTree::writeSparseModelJuliaFiles(const string &basename) const
<< "x::Vector{<: Real}, params::Vector{<: Real}" << ss_argin << ")" << endl << "x::Vector{<: Real}, params::Vector{<: Real}" << ss_argin << ")" << endl
<< " " << prefix << "ResidTT!(T, y, x, params" << ss_argout << ")" << endl << " " << prefix << "ResidTT!(T, y, x, params" << ss_argout << ")" << endl
<< "@inbounds begin" << endl << "@inbounds begin" << endl
<< tt_sparse_output[1].str() << tt_sparse_output[1].str() << "end" << endl
<< "end" << endl
<< " return nothing" << endl << " return nothing" << endl
<< "end" << endl << endl; << "end" << endl
<< endl;
writeToFileIfModified(output, julia_dir / (prefix + "G1TT!.jl")); writeToFileIfModified(output, julia_dir / (prefix + "G1TT!.jl"));
ttlen += temporary_terms_derivatives[1].size(); ttlen += temporary_terms_derivatives[1].size();
// G1! // G1!
output.str(""); output.str("");
output << "function " << prefix << "G1!(T::Vector{<: Real}, g1_v::Vector{<: Real}, " output << "function " << prefix << "G1!(T::Vector{<: Real}, g1_v::Vector{<: Real}, "
<< "y::Vector{<: Real}, x::Vector{<: Real}, params::Vector{<: Real}" << "y::Vector{<: Real}, x::Vector{<: Real}, params::Vector{<: Real}" << ss_argin << ")"
<< ss_argin << ")" << endl << endl
<< " @assert length(T) >= " << ttlen << endl << " @assert length(T) >= " << ttlen << endl
<< " @assert length(g1_v) == " << derivatives[1].size() << endl << " @assert length(g1_v) == " << derivatives[1].size() << endl
<< " @assert length(y) == " << ylen << endl << " @assert length(y) == " << ylen << endl
<< " @assert length(x) == " << xlen << endl << " @assert length(x) == " << xlen << endl
<< " @assert length(params) == " << symbol_table.param_nbr() << endl << " @assert length(params) == " << symbol_table.param_nbr() << endl
<< "@inbounds begin" << endl << "@inbounds begin" << endl
<< d_sparse_output[1].str() << d_sparse_output[1].str() << "end" << endl;
<< "end" << endl; output << " return nothing" << endl << "end" << endl << endl;
output << " return nothing" << endl
<< "end" << endl << endl;
writeToFileIfModified(output, julia_dir / (prefix + "G1!.jl")); writeToFileIfModified(output, julia_dir / (prefix + "G1!.jl"));
for (int i {2}; i <= computed_derivs_order; i++) for (int i {2}; i <= computed_derivs_order; i++)
...@@ -2408,53 +2452,62 @@ ModelTree::writeSparseModelJuliaFiles(const string &basename) const ...@@ -2408,53 +2452,62 @@ ModelTree::writeSparseModelJuliaFiles(const string &basename) const
output.str(""); output.str("");
output << "function " << prefix << "G" << i << "TT!(T::Vector{<: Real}, y::Vector{<: Real}, " output << "function " << prefix << "G" << i << "TT!(T::Vector{<: Real}, y::Vector{<: Real}, "
<< "x::Vector{<: Real}, params::Vector{<: Real}" << ss_argin << ")" << endl << "x::Vector{<: Real}, params::Vector{<: Real}" << ss_argin << ")" << endl
<< " " << prefix << "G" << to_string(i-1) << "TT!(T, y, x, params" << ss_argout << ")" << endl << " " << prefix << "G" << to_string(i - 1) << "TT!(T, y, x, params" << ss_argout
<< ")" << endl
<< "@inbounds begin" << endl << "@inbounds begin" << endl
<< tt_sparse_output[i].str() << tt_sparse_output[i].str() << "end" << endl
<< "end" << endl
<< " return nothing" << endl << " return nothing" << endl
<< "end" << endl << endl; << "end" << endl
<< endl;
writeToFileIfModified(output, julia_dir / (prefix + "G" + to_string(i) + "TT!.jl")); writeToFileIfModified(output, julia_dir / (prefix + "G" + to_string(i) + "TT!.jl"));
ttlen += temporary_terms_derivatives[i].size(); ttlen += temporary_terms_derivatives[i].size();
// G<i>! // G<i>!
output.str(""); output.str("");
output << "function " << prefix << "G" << i << "!(T::Vector{<: Real}, g" << i << "_v::Vector{<: Real}, " output << "function " << prefix << "G" << i << "!(T::Vector{<: Real}, g" << i
<< "y::Vector{<: Real}, x::Vector{<: Real}, params::Vector{<: Real}" << "_v::Vector{<: Real}, "
<< ss_argin << ")" << endl << "y::Vector{<: Real}, x::Vector{<: Real}, params::Vector{<: Real}" << ss_argin << ")"
<< endl
<< " @assert length(T) >= " << ttlen << endl << " @assert length(T) >= " << ttlen << endl
<< " @assert length(g" << i << "_v) == " << derivatives[i].size() << endl << " @assert length(g" << i << "_v) == " << derivatives[i].size() << endl
<< " @assert length(y) == " << ylen << endl << " @assert length(y) == " << ylen << endl
<< " @assert length(x) == " << xlen << endl << " @assert length(x) == " << xlen << endl
<< " @assert length(params) == " << symbol_table.param_nbr() << endl << " @assert length(params) == " << symbol_table.param_nbr() << endl
<< "@inbounds begin" << endl << "@inbounds begin" << endl
<< d_sparse_output[i].str() << d_sparse_output[i].str() << "end" << endl
<< "end" << endl
<< " return nothing" << endl << " return nothing" << endl
<< "end" << endl << endl; << "end" << endl
<< endl;
writeToFileIfModified(output, julia_dir / (prefix + "G" + to_string(i) + "!.jl")); writeToFileIfModified(output, julia_dir / (prefix + "G" + to_string(i) + "!.jl"));
} }
} }
#endif
template<bool dynamic> template<bool dynamic>
void void
ModelTree::writeSparseModelMFiles(const string &basename) const ModelTree::writeSparseModelMFiles(const string& basename,
const optional<int>& heterogeneous_dimension) const
{ {
constexpr ExprNodeOutputType output_type {dynamic ? ExprNodeOutputType::matlabSparseDynamicModel : ExprNodeOutputType::matlabSparseStaticModel}; constexpr ExprNodeOutputType output_type {dynamic ? ExprNodeOutputType::matlabSparseDynamicModel
: ExprNodeOutputType::matlabSparseStaticModel};
auto [d_sparse_output, tt_sparse_output] = writeModelFileHelper<output_type>(); auto [d_sparse_output, tt_sparse_output] = writeModelFileHelper<output_type>();
const filesystem::path m_dir {packageDir(basename) / "+sparse"}; const filesystem::path m_dir {packageDir(basename) / "+sparse"};
// TODO: when C++20 support is complete, mark the following strings constexpr const string prefix {
const string prefix { dynamic ? "dynamic_" : "static_" }; (dynamic ? "dynamic_"s : "static_"s)
+ (heterogeneous_dimension ? "het"s + to_string(*heterogeneous_dimension + 1) + "_"s : ""s)};
const string full_prefix {basename + ".sparse." + prefix}; const string full_prefix {basename + ".sparse." + prefix};
const string ss_arg { dynamic ? ", steady_state" : "" }; const string extra_args {(dynamic ? ", steady_state"s : ""s)
+ (heterogeneous_dimension
? ", yh, xh, paramsh"s
: (heterogeneity_table.empty() ? ""s : ", yagg"s))};
const int nextra_args {
static_cast<int>(dynamic)
+ (heterogeneous_dimension ? 3 : static_cast<int>(!heterogeneity_table.empty()))};
size_t ttlen {temporary_terms_derivatives[0].size()}; size_t ttlen {temporary_terms_derivatives[0].size()};
ofstream output; ofstream output;
auto open_file = [&output](const filesystem::path &p) auto open_file = [&output](const filesystem::path& p) {
{
output.open(p, ios::out | ios::binary); output.open(p, ios::out | ios::binary);
if (!output.is_open()) if (!output.is_open())
{ {
...@@ -2465,7 +2518,8 @@ ModelTree::writeSparseModelMFiles(const string &basename) const ...@@ -2465,7 +2518,8 @@ ModelTree::writeSparseModelMFiles(const string &basename) const
// Residuals (non-block) // Residuals (non-block)
open_file(m_dir / (prefix + "resid_tt.m")); open_file(m_dir / (prefix + "resid_tt.m"));
output << "function [T_order, T] = " << prefix << "resid_tt(y, x, params" << ss_arg << ", T_order, T)" << endl output << "function [T_order, T] = " << prefix << "resid_tt(y, x, params" << extra_args
<< ", T_order, T)" << endl
<< "if T_order >= 0" << endl << "if T_order >= 0" << endl
<< " return" << endl << " return" << endl
<< "end" << endl << "end" << endl
...@@ -2473,17 +2527,18 @@ ModelTree::writeSparseModelMFiles(const string &basename) const ...@@ -2473,17 +2527,18 @@ ModelTree::writeSparseModelMFiles(const string &basename) const
<< "if size(T, 1) < " << ttlen << endl << "if size(T, 1) < " << ttlen << endl
<< " T = [T; NaN(" << ttlen << " - size(T, 1), 1)];" << endl << " T = [T; NaN(" << ttlen << " - size(T, 1), 1)];" << endl
<< "end" << endl << "end" << endl
<< tt_sparse_output[0].str() << tt_sparse_output[0].str() << "end" << endl;
<< "end" << endl;
output.close(); output.close();
open_file(m_dir / (prefix + "resid.m")); open_file(m_dir / (prefix + "resid.m"));
output << "function [residual, T_order, T] = " << prefix << "resid(y, x, params" << ss_arg << ", T_order, T)" << endl output << "function [residual, T_order, T] = " << prefix << "resid(y, x, params" << extra_args
<< "if nargin < " << 5+static_cast<int>(dynamic) << endl << ", T_order, T)" << endl
<< "if nargin < " << 5 + nextra_args << endl
<< " T_order = -1;" << endl << " T_order = -1;" << endl
<< " T = NaN(" << ttlen << ", 1);" << endl << " T = NaN(" << ttlen << ", 1);" << endl
<< "end" << endl << "end" << endl
<< "[T_order, T] = " << full_prefix << "resid_tt(y, x, params" << ss_arg << ", T_order, T);" << endl << "[T_order, T] = " << full_prefix << "resid_tt(y, x, params" << extra_args
<< ", T_order, T);" << endl
<< "residual = NaN(" << equations.size() << ", 1);" << endl << "residual = NaN(" << equations.size() << ", 1);" << endl
<< d_sparse_output[0].str(); << d_sparse_output[0].str();
output << "end" << endl; output << "end" << endl;
...@@ -2493,35 +2548,34 @@ ModelTree::writeSparseModelMFiles(const string &basename) const ...@@ -2493,35 +2548,34 @@ ModelTree::writeSparseModelMFiles(const string &basename) const
ttlen += temporary_terms_derivatives[1].size(); ttlen += temporary_terms_derivatives[1].size();
open_file(m_dir / (prefix + "g1_tt.m")); open_file(m_dir / (prefix + "g1_tt.m"));
output << "function [T_order, T] = " << prefix << "g1_tt(y, x, params" << ss_arg << ", T_order, T)" << endl output << "function [T_order, T] = " << prefix << "g1_tt(y, x, params" << extra_args
<< ", T_order, T)" << endl
<< "if T_order >= 1" << endl << "if T_order >= 1" << endl
<< " return" << endl << " return" << endl
<< "end" << endl << "end" << endl
<< "[T_order, T] = " << full_prefix << "resid_tt(y, x, params" << ss_arg << ", T_order, T);" << endl << "[T_order, T] = " << full_prefix << "resid_tt(y, x, params" << extra_args
<< ", T_order, T);" << endl
<< "T_order = 1;" << endl << "T_order = 1;" << endl
<< "if size(T, 1) < " << ttlen << endl << "if size(T, 1) < " << ttlen << endl
<< " T = [T; NaN(" << ttlen << " - size(T, 1), 1)];" << endl << " T = [T; NaN(" << ttlen << " - size(T, 1), 1)];" << endl
<< "end" << endl << "end" << endl
<< tt_sparse_output[1].str() << tt_sparse_output[1].str() << "end" << endl;
<< "end" << endl;
output.close(); output.close();
open_file(m_dir / (prefix + "g1.m")); open_file(m_dir / (prefix + "g1.m"));
// NB: At first order, sparse indices are passed as extra arguments // NB: At first order, sparse indices are passed as extra arguments
output << "function [g1, T_order, T] = " << prefix << "g1(y, x, params" << ss_arg << ", sparse_rowval, sparse_colval, sparse_colptr, T_order, T)" << endl output << "function [g1, T_order, T] = " << prefix << "g1(y, x, params" << extra_args
<< "if nargin < " << 8+static_cast<int>(dynamic) << endl << ", sparse_rowval, sparse_colval, sparse_colptr, T_order, T)" << endl
<< "if nargin < " << 8 + nextra_args << endl
<< " T_order = -1;" << endl << " T_order = -1;" << endl
<< " T = NaN(" << ttlen << ", 1);" << endl << " T = NaN(" << ttlen << ", 1);" << endl
<< "end" << endl << "end" << endl
<< "[T_order, T] = " << full_prefix << "g1_tt(y, x, params" << ss_arg << ", T_order, T);" << endl << "[T_order, T] = " << full_prefix << "g1_tt(y, x, params" << extra_args
<< ", T_order, T);" << endl
<< "g1_v = NaN(" << jacobian_sparse_column_major_order.size() << ", 1);" << endl << "g1_v = NaN(" << jacobian_sparse_column_major_order.size() << ", 1);" << endl
<< d_sparse_output[1].str(); << d_sparse_output[1].str();
// On MATLAB < R2020a, sparse() does not accept int32 indices output << "g1 = sparse(sparse_rowval, sparse_colval, g1_v, " << equations.size() << ", "
output << "if ~isoctave && matlab_ver_less_than('9.8')" << endl << getJacobianColsNbr(true) << ");" << endl
<< " sparse_rowval = double(sparse_rowval);" << endl
<< " sparse_colval = double(sparse_colval);" << endl
<< "end" << endl
<< "g1 = sparse(sparse_rowval, sparse_colval, g1_v, " << equations.size() << ", " << getJacobianColsNbr(true) << ");" << endl
<< "end" << endl; << "end" << endl;
output.close(); output.close();
...@@ -2531,29 +2585,31 @@ ModelTree::writeSparseModelMFiles(const string &basename) const ...@@ -2531,29 +2585,31 @@ ModelTree::writeSparseModelMFiles(const string &basename) const
ttlen += temporary_terms_derivatives[i].size(); ttlen += temporary_terms_derivatives[i].size();
open_file(m_dir / (prefix + "g" + to_string(i) + "_tt.m")); open_file(m_dir / (prefix + "g" + to_string(i) + "_tt.m"));
output << "function T = " << prefix << "g" << i << "_tt(y, x, params" << ss_arg << ")" << endl output << "function [T_order, T] = " << prefix << "g" << i << "_tt(y, x, params" << extra_args
<< ", T_order, T)" << endl
<< "if T_order >= " << i << endl << "if T_order >= " << i << endl
<< " return" << endl << " return" << endl
<< "end" << endl << "end" << endl
<< "[T_order, T] = " << full_prefix << "g" << i-1 << "_tt(y, x, params" << ss_arg << ", T_order, T);" << endl << "[T_order, T] = " << full_prefix << "g" << i - 1 << "_tt(y, x, params" << extra_args
<< ", T_order, T);" << endl
<< "T_order = " << i << ";" << endl << "T_order = " << i << ";" << endl
<< "if size(T, 1) < " << ttlen << endl << "if size(T, 1) < " << ttlen << endl
<< " T = [T; NaN(" << ttlen << " - size(T, 1), 1)];" << endl << " T = [T; NaN(" << ttlen << " - size(T, 1), 1)];" << endl
<< "end" << endl << "end" << endl
<< tt_sparse_output[i].str() << tt_sparse_output[i].str() << "end" << endl;
<< "end" << endl;
output.close(); output.close();
open_file(m_dir / (prefix + "g" + to_string(i) + ".m")); open_file(m_dir / (prefix + "g" + to_string(i) + ".m"));
output << "function [g" << i << "_v, T_order, T] = " << prefix << "g" << i << "(y, x, params" << ss_arg << ", T_order, T)" << endl output << "function [g" << i << "_v, T_order, T] = " << prefix << "g" << i << "(y, x, params"
<< "if nargin < " << 5+static_cast<int>(dynamic) << endl << extra_args << ", T_order, T)" << endl
<< "if nargin < " << 5 + nextra_args << endl
<< " T_order = -1;" << endl << " T_order = -1;" << endl
<< " T = NaN(" << ttlen << ", 1);" << endl << " T = NaN(" << ttlen << ", 1);" << endl
<< "end" << endl << "end" << endl
<< "[T_order, T] = " << full_prefix << "g" << i << "_tt(y, x, params" << ss_arg << ", T_order, T);" << endl << "[T_order, T] = " << full_prefix << "g" << i << "_tt(y, x, params" << extra_args
<< ", T_order, T);" << endl
<< "g" << i << "_v = NaN(" << derivatives[i].size() << ", 1);" << endl << "g" << i << "_v = NaN(" << derivatives[i].size() << ", 1);" << endl
<< d_sparse_output[i].str() << d_sparse_output[i].str() << "end" << endl;
<< "end" << endl;
output.close(); output.close();
} }
...@@ -2570,7 +2626,8 @@ ModelTree::writeSparseModelMFiles(const string &basename) const ...@@ -2570,7 +2626,8 @@ ModelTree::writeSparseModelMFiles(const string &basename) const
|| simulation_type == BlockSimulationType::evaluateBackward}; || simulation_type == BlockSimulationType::evaluateBackward};
const string resid_g1_arg {evaluate ? "" : ", residual, g1"}; const string resid_g1_arg {evaluate ? "" : ", residual, g1"};
open_file(block_dir / (funcname + ".m")); open_file(block_dir / (funcname + ".m"));
output << "function [y, T" << resid_g1_arg << "] = " << funcname << "(y, x, params" << ss_arg << ", sparse_rowval, sparse_colval, sparse_colptr, T)" << endl; output << "function [y, T" << resid_g1_arg << "] = " << funcname << "(y, x, params"
<< extra_args << ", sparse_rowval, sparse_colval, sparse_colptr, T)" << endl;
if (!evaluate) if (!evaluate)
output << "residual=NaN(" << blocks[blk].mfs_size << ", 1);" << endl; output << "residual=NaN(" << blocks[blk].mfs_size << ", 1);" << endl;
...@@ -2580,19 +2637,17 @@ ModelTree::writeSparseModelMFiles(const string &basename) const ...@@ -2580,19 +2637,17 @@ ModelTree::writeSparseModelMFiles(const string &basename) const
// Write Jacobian // Write Jacobian
if (!evaluate) if (!evaluate)
{ {
const bool one_boundary {simulation_type == BlockSimulationType::solveBackwardSimple const bool one_boundary {
simulation_type == BlockSimulationType::solveBackwardSimple
|| simulation_type == BlockSimulationType::solveForwardSimple || simulation_type == BlockSimulationType::solveForwardSimple
|| simulation_type == BlockSimulationType::solveBackwardComplete || simulation_type == BlockSimulationType::solveBackwardComplete
|| simulation_type == BlockSimulationType::solveForwardComplete}; || simulation_type == BlockSimulationType::solveForwardComplete};
output << "if nargout > 3" << endl output << "if nargout > 3" << endl
<< " g1_v = NaN(" << blocks_jacobian_sparse_column_major_order[blk].size() << ", 1);" << endl; << " g1_v = NaN(" << blocks_jacobian_sparse_column_major_order[blk].size()
<< ", 1);" << endl;
writeSparsePerBlockJacobianHelper<output_type>(blk, output, temporary_terms_written); writeSparsePerBlockJacobianHelper<output_type>(blk, output, temporary_terms_written);
// On MATLAB < R2020a, sparse() does not accept int32 indices output << " g1 = sparse(sparse_rowval, sparse_colval, g1_v, "
output << " if ~isoctave && matlab_ver_less_than('9.8')" << endl << blocks[blk].mfs_size << ", "
<< " sparse_rowval = double(sparse_rowval);" << endl
<< " sparse_colval = double(sparse_colval);" << endl
<< " end" << endl
<< " g1 = sparse(sparse_rowval, sparse_colval, g1_v, " << blocks[blk].mfs_size << ", "
<< (one_boundary ? 1 : 3) * blocks[blk].mfs_size << ");" << endl << (one_boundary ? 1 : 3) * blocks[blk].mfs_size << ");" << endl
<< "end" << endl; << "end" << endl;
} }
...@@ -2607,23 +2662,27 @@ void ...@@ -2607,23 +2662,27 @@ void
ModelTree::writeSparseModelCFiles(const string& basename, const string& mexext, ModelTree::writeSparseModelCFiles(const string& basename, const string& mexext,
const filesystem::path& matlabroot) const const filesystem::path& matlabroot) const
{ {
constexpr ExprNodeOutputType output_type {dynamic ? ExprNodeOutputType::CSparseDynamicModel : ExprNodeOutputType::CSparseStaticModel}; constexpr ExprNodeOutputType output_type {dynamic ? ExprNodeOutputType::CSparseDynamicModel
: ExprNodeOutputType::CSparseStaticModel};
auto [d_sparse_output, tt_sparse_output] = writeModelFileHelper<output_type>(); auto [d_sparse_output, tt_sparse_output] = writeModelFileHelper<output_type>();
const filesystem::path mex_dir {packageDir(basename) / "+sparse"}; const filesystem::path mex_dir {packageDir(basename) / "+sparse"};
const filesystem::path model_src_dir {filesystem::path {basename} / "model" / "src" / "sparse"}; const filesystem::path model_src_dir {filesystem::path {basename} / "model" / "src" / "sparse"};
// TODO: when C++20 support is complete, mark the following strings constexpr
const string prefix {dynamic ? "dynamic_" : "static_"}; const string prefix {dynamic ? "dynamic_" : "static_"};
const string ss_argin { dynamic ? ", const double *restrict steady_state" : "" }; const string extra_argin {
const string ss_argout { dynamic ? ", steady_state" : "" }; (dynamic ? ", const double *restrict steady_state"s : ""s)
+ (heterogeneity_table.empty() ? ""s : ", const double *restrict yagg"s)};
const string extra_argout {(dynamic ? ", steady_state"s : ""s)
+ (heterogeneity_table.empty() ? ""s : ", yagg"s)};
const int nextra_args {static_cast<int>(dynamic)
+ static_cast<int>(!heterogeneity_table.empty())};
const int ylen {(dynamic ? 3 : 1) * symbol_table.endo_nbr()}; const int ylen {(dynamic ? 3 : 1) * symbol_table.endo_nbr()};
const int xlen {symbol_table.exo_nbr() + symbol_table.exo_det_nbr()}; const int xlen {symbol_table.exo_nbr() + symbol_table.exo_det_nbr()};
vector<filesystem::path> tt_object_files; vector<filesystem::path> tt_object_files;
ofstream output; ofstream output;
auto open_file = [&output](const filesystem::path &p) auto open_file = [&output](const filesystem::path& p) {
{
output.open(p, ios::out | ios::binary); output.open(p, ios::out | ios::binary);
if (!output.is_open()) if (!output.is_open())
{ {
...@@ -2634,48 +2693,77 @@ ModelTree::writeSparseModelCFiles(const string &basename, const string &mexext, ...@@ -2634,48 +2693,77 @@ ModelTree::writeSparseModelCFiles(const string &basename, const string &mexext,
size_t ttlen {0}; size_t ttlen {0};
// Helper for dealing with y, x, params and steady_state inputs (shared with block case) // Helper for dealing with y, x, params, steady_state and yagg inputs (shared with block case)
auto y_x_params_ss_inputs = [&](bool assign_y) auto y_x_params_ss_yagg_inputs = [&](bool assign_y) {
{ output << " if (!(mxIsDouble(prhs[0]) && !mxIsComplex(prhs[0]) && !mxIsSparse(prhs[0]) && "
output << " if (!(mxIsDouble(prhs[0]) && !mxIsComplex(prhs[0]) && !mxIsSparse(prhs[0]) && mxGetNumberOfElements(prhs[0]) == " << ylen << "))" << endl "mxGetNumberOfElements(prhs[0]) == "
<< R"( mexErrMsgTxt("y must be a real dense numeric array with )" << ylen << R"( elements");)" << endl; << ylen << "))" << endl
<< R"( mexErrMsgTxt("y must be a real dense numeric array with )" << ylen
<< R"( elements");)" << endl;
if (assign_y) if (assign_y)
output << " const double *restrict y = mxGetPr(prhs[0]);" << endl; output << " const double *restrict y = mxGetPr(prhs[0]);" << endl;
output << " if (!(mxIsDouble(prhs[1]) && !mxIsComplex(prhs[1]) && !mxIsSparse(prhs[1]) && mxGetNumberOfElements(prhs[1]) == " << xlen << "))" << endl output << " if (!(mxIsDouble(prhs[1]) && !mxIsComplex(prhs[1]) && !mxIsSparse(prhs[1]) && "
<< R"( mexErrMsgTxt("x must be a real dense numeric array with )" << xlen << R"( elements");)" << endl "mxGetNumberOfElements(prhs[1]) == "
<< xlen << "))" << endl
<< R"( mexErrMsgTxt("x must be a real dense numeric array with )" << xlen
<< R"( elements");)" << endl
<< " const double *restrict x = mxGetPr(prhs[1]);" << endl << " const double *restrict x = mxGetPr(prhs[1]);" << endl
<< " if (!(mxIsDouble(prhs[2]) && !mxIsComplex(prhs[2]) && !mxIsSparse(prhs[2]) && mxGetNumberOfElements(prhs[2]) == " << symbol_table.param_nbr() << "))" << endl << " if (!(mxIsDouble(prhs[2]) && !mxIsComplex(prhs[2]) && !mxIsSparse(prhs[2]) && "
<< R"( mexErrMsgTxt("params must be a real dense numeric array with )" << symbol_table.param_nbr() << R"( elements");)" << endl "mxGetNumberOfElements(prhs[2]) == "
<< symbol_table.param_nbr() << "))" << endl
<< R"( mexErrMsgTxt("params must be a real dense numeric array with )"
<< symbol_table.param_nbr() << R"( elements");)" << endl
<< " const double *restrict params = mxGetPr(prhs[2]);" << endl; << " const double *restrict params = mxGetPr(prhs[2]);" << endl;
if constexpr (dynamic) if constexpr (dynamic)
output << " if (!(mxIsDouble(prhs[3]) && !mxIsComplex(prhs[3]) && !mxIsSparse(prhs[3]) && mxGetNumberOfElements(prhs[3]) == " << symbol_table.endo_nbr() << "))" << endl output << " if (!(mxIsDouble(prhs[3]) && !mxIsComplex(prhs[3]) && !mxIsSparse(prhs[3]) && "
<< R"( mexErrMsgTxt("steady_state must be a real dense numeric array with )" << symbol_table.endo_nbr() << R"( elements");)" << endl "mxGetNumberOfElements(prhs[3]) == "
<< symbol_table.endo_nbr() << "))" << endl
<< R"( mexErrMsgTxt("steady_state must be a real dense numeric array with )"
<< symbol_table.endo_nbr() << R"( elements");)" << endl
<< " const double *restrict steady_state = mxGetPr(prhs[3]);" << endl; << " const double *restrict steady_state = mxGetPr(prhs[3]);" << endl;
if (!heterogeneity_table.empty())
{
const int idx {3 + static_cast<int>(dynamic)};
output << " if (!(mxIsDouble(prhs[" << idx << "]) && !mxIsComplex(prhs[" << idx
<< "]) && !mxIsSparse(prhs[" << idx << "]) && mxGetNumberOfElements(prhs[" << idx
<< "]) == " << heterogeneity_table.aggregateEndoSize() << "))" << endl
<< R"( mexErrMsgTxt("yagg must be a real dense numeric array with )"
<< heterogeneity_table.aggregateEndoSize() << R"( elements");)" << endl
<< " const double *restrict yagg = mxGetPr(prhs[" << idx << "]);" << endl;
}
}; };
// Helper for dealing with sparse_rowval and sparse_colptr inputs (shared with block case) // Helper for dealing with sparse_rowval and sparse_colptr inputs (shared with block case)
auto sparse_indices_inputs = [&](int ncols, int nzval) auto sparse_indices_inputs = [&](int ncols, int nzval) {
{
// We use sparse_rowval and sparse_colptr (sparse_colval is unused) // We use sparse_rowval and sparse_colptr (sparse_colval is unused)
const int row_idx {3+static_cast<int>(dynamic)}, col_idx {row_idx+2}; const int row_idx {3 + nextra_args}, col_idx {row_idx + 2};
output << " if (!(mxIsInt32(prhs[" << row_idx << "]) && mxGetNumberOfElements(prhs[" << row_idx << "]) == " << nzval << "))" << endl output << " if (!(mxIsInt32(prhs[" << row_idx << "]) && mxGetNumberOfElements(prhs[" << row_idx
<< R"( mexErrMsgTxt("sparse_rowval must be an int32 array with )" << nzval << R"( elements");)" << endl << "]) == " << nzval << "))" << endl
<< " if (!(mxIsInt32(prhs[" << col_idx << "]) && mxGetNumberOfElements(prhs[" << col_idx << "]) == " << ncols+1 << "))" << endl << R"( mexErrMsgTxt("sparse_rowval must be an int32 array with )" << nzval
<< R"( mexErrMsgTxt("sparse_colptr must be an int32 array with )" << ncols+1 << R"( elements");)" << endl << R"( elements");)" << endl
<< " if (!(mxIsInt32(prhs[" << col_idx << "]) && mxGetNumberOfElements(prhs[" << col_idx
<< "]) == " << ncols + 1 << "))" << endl
<< R"( mexErrMsgTxt("sparse_colptr must be an int32 array with )" << ncols + 1
<< R"( elements");)" << endl
<< "#if MX_HAS_INTERLEAVED_COMPLEX" << endl << "#if MX_HAS_INTERLEAVED_COMPLEX" << endl
<< " const int32_T *restrict sparse_rowval = mxGetInt32s(prhs[" << row_idx << "]);" << endl << " const int32_T *restrict sparse_rowval = mxGetInt32s(prhs[" << row_idx << "]);"
<< " const int32_T *restrict sparse_colptr = mxGetInt32s(prhs[" << col_idx << "]);" << endl << endl
<< " const int32_T *restrict sparse_colptr = mxGetInt32s(prhs[" << col_idx << "]);"
<< endl
<< "#else" << endl << "#else" << endl
<< " const int32_T *restrict sparse_rowval = (int32_T *) mxGetData(prhs[" << row_idx << "]);" << endl << " const int32_T *restrict sparse_rowval = (int32_T *) mxGetData(prhs[" << row_idx
<< " const int32_T *restrict sparse_colptr = (int32_T *) mxGetData(prhs[" << col_idx << "]);" << endl << "]);" << endl
<< " const int32_T *restrict sparse_colptr = (int32_T *) mxGetData(prhs[" << col_idx
<< "]);" << endl
<< "#endif" << endl; << "#endif" << endl;
}; };
// Helper for creating sparse Jacobian (shared with block case) // Helper for creating sparse Jacobian (shared with block case)
auto sparse_jacobian_create = [&](int argidx, int nrows, int ncols, int nzval) auto sparse_jacobian_create = [&](int argidx, int nrows, int ncols, int nzval) {
{ output << " plhs[" << argidx << "] = mxCreateSparse(" << nrows << ", " << ncols << ", "
output << " plhs[" << argidx << "] = mxCreateSparse(" << nrows << ", " << ncols << ", " << nzval << ", mxREAL);" << endl << nzval << ", mxREAL);" << endl
<< " mwIndex *restrict ir = mxGetIr(plhs[" << argidx << "]), *restrict jc = mxGetJc(plhs[" << argidx << "]);" << endl << " mwIndex *restrict ir = mxGetIr(plhs[" << argidx
<< "]), *restrict jc = mxGetJc(plhs[" << argidx << "]);" << endl
<< " for (mwSize i = 0; i < " << nzval << "; i++)" << endl << " for (mwSize i = 0; i < " << nzval << "; i++)" << endl
<< " *ir++ = *sparse_rowval++ - 1;" << endl << " *ir++ = *sparse_rowval++ - 1;" << endl
<< " for (mwSize i = 0; i < " << ncols + 1 << "; i++)" << endl << " for (mwSize i = 0; i < " << ncols + 1 << "; i++)" << endl
...@@ -2687,7 +2775,10 @@ ModelTree::writeSparseModelCFiles(const string &basename, const string &mexext, ...@@ -2687,7 +2775,10 @@ ModelTree::writeSparseModelCFiles(const string &basename, const string &mexext,
const string funcname {prefix + (i == 0 ? "resid" : "g" + to_string(i))}; const string funcname {prefix + (i == 0 ? "resid" : "g" + to_string(i))};
ttlen += temporary_terms_derivatives[i].size(); ttlen += temporary_terms_derivatives[i].size();
const string prototype_tt { "void " + funcname + "_tt(const double *restrict y, const double *restrict x, const double *restrict params" + ss_argin + ", double *restrict T)" }; const string prototype_tt {
"void " + funcname
+ "_tt(const double *restrict y, const double *restrict x, const double *restrict params"
+ extra_argin + ", double *restrict T)"};
const filesystem::path header_tt {model_src_dir / (funcname + "_tt.h")}; const filesystem::path header_tt {model_src_dir / (funcname + "_tt.h")};
open_file(header_tt); open_file(header_tt);
...@@ -2703,14 +2794,17 @@ ModelTree::writeSparseModelCFiles(const string &basename, const string &mexext, ...@@ -2703,14 +2794,17 @@ ModelTree::writeSparseModelCFiles(const string &basename, const string &mexext,
output << endl output << endl
<< prototype_tt << endl << prototype_tt << endl
<< "{" << endl << "{" << endl
<< tt_sparse_output[i].str() << tt_sparse_output[i].str() << "}" << endl
<< "}" << endl
<< endl; << endl;
output.close(); output.close();
tt_object_files.push_back(compileMEX(model_src_dir, funcname + "_tt", mexext, { source_tt }, tt_object_files.push_back(
matlabroot, false)); compileMEX(model_src_dir, funcname + "_tt", mexext, {source_tt}, matlabroot, false));
const string prototype_main {"void " + funcname + "(const double *restrict y, const double *restrict x, const double *restrict params" + ss_argin + ", const double *restrict T, double *restrict " + (i == 0 ? "residual" : "g" + to_string(i) + "_v") + ")"}; const string prototype_main {
"void " + funcname
+ "(const double *restrict y, const double *restrict x, const double *restrict params"
+ extra_argin + ", const double *restrict T, double *restrict "
+ (i == 0 ? "residual" : "g" + to_string(i) + "_v") + ")"};
const filesystem::path header_main {model_src_dir / (funcname + ".h")}; const filesystem::path header_main {model_src_dir / (funcname + ".h")};
open_file(header_main); open_file(header_main);
...@@ -2727,37 +2821,40 @@ ModelTree::writeSparseModelCFiles(const string &basename, const string &mexext, ...@@ -2727,37 +2821,40 @@ ModelTree::writeSparseModelCFiles(const string &basename, const string &mexext,
output << endl output << endl
<< prototype_main << endl << prototype_main << endl
<< "{" << endl << "{" << endl
<< d_sparse_output[i].str() << d_sparse_output[i].str() << "}" << endl
<< "}" << endl
<< endl; << endl;
output.close(); output.close();
auto main_object_file {compileMEX(model_src_dir, funcname, mexext, { source_main }, auto main_object_file {
matlabroot, false)}; compileMEX(model_src_dir, funcname, mexext, {source_main}, matlabroot, false)};
const filesystem::path source_mex {model_src_dir / (funcname + "_mex.c")}; const filesystem::path source_mex {model_src_dir / (funcname + "_mex.c")};
int nargin {5+static_cast<int>(dynamic)+3*static_cast<int>(i == 1)}; int nargin {5 + nextra_args + 3 * static_cast<int>(i == 1)};
open_file(source_mex); open_file(source_mex);
output << "#include <string.h>" << endl // For memcpy() output << "#include <string.h>" << endl // For memcpy()
<< R"(#include "mex.h")" << endl << R"(#include "mex.h")" << endl
<< R"(#include ")" << funcname << R"(.h")" << endl; << R"(#include ")" << funcname << R"(.h")" << endl;
for (int j {0}; j <= i; j++) for (int j {0}; j <= i; j++)
output << R"(#include ")" << prefix << (j == 0 ? "resid" : "g" + to_string(j)) << R"(_tt.h")" << endl; output << R"(#include ")" << prefix << (j == 0 ? "resid" : "g" + to_string(j))
<< R"(_tt.h")" << endl;
output << endl output << endl
<< "#define max(a, b) ((a > b) ? (a) : (b))" << endl << "#define max(a, b) ((a > b) ? (a) : (b))" << endl
<< endl << endl
<< "void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])" << endl << "void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])"
<< endl
<< "{" << endl << "{" << endl
<< " if (nrhs != " << nargin - 2 << " && nrhs != " << nargin << ")" << endl << " if (nrhs != " << nargin - 2 << " && nrhs != " << nargin << ")" << endl
<< R"( mexErrMsgTxt("Accepts exactly )" << nargin-2 << " or " << nargin << R"( input arguments");)" << endl << R"( mexErrMsgTxt("Accepts exactly )" << nargin - 2 << " or " << nargin
<< R"( input arguments");)" << endl
<< " if (nlhs != 1 && nlhs != 3)" << endl << " if (nlhs != 1 && nlhs != 3)" << endl
<< R"( mexErrMsgTxt("Accepts exactly 1 or 3 output arguments");)" << endl; << R"( mexErrMsgTxt("Accepts exactly 1 or 3 output arguments");)" << endl;
y_x_params_ss_inputs(true); y_x_params_ss_yagg_inputs(true);
if (i == 1) if (i == 1)
sparse_indices_inputs(getJacobianColsNbr(true), jacobian_sparse_column_major_order.size()); sparse_indices_inputs(getJacobianColsNbr(true), jacobian_sparse_column_major_order.size());
output << " mxArray *T_mx, *T_order_mx;" << endl output
<< " mxArray *T_mx, *T_order_mx;" << endl
<< " int T_order_on_input;" << endl << " int T_order_on_input;" << endl
<< " if (nrhs > " << nargin - 2 << ")" << endl << " if (nrhs > " << nargin - 2 << ")" << endl
<< " {" << endl << " {" << endl
...@@ -2765,18 +2862,23 @@ ModelTree::writeSparseModelCFiles(const string &basename, const string &mexext, ...@@ -2765,18 +2862,23 @@ ModelTree::writeSparseModelCFiles(const string &basename, const string &mexext,
<< " T_mx = (mxArray *) prhs[" << nargin - 1 << "];" << endl << " T_mx = (mxArray *) prhs[" << nargin - 1 << "];" << endl
<< " if (!(mxIsScalar(T_order_mx) && mxIsNumeric(T_order_mx)))" << endl << " if (!(mxIsScalar(T_order_mx) && mxIsNumeric(T_order_mx)))" << endl
<< R"( mexErrMsgTxt("T_order should be a numeric scalar");)" << endl << R"( mexErrMsgTxt("T_order should be a numeric scalar");)" << endl
<< " if (!(mxIsDouble(T_mx) && !mxIsComplex(T_mx) && !mxIsSparse(T_mx) && mxGetN(T_mx) == 1))" << endl << " if (!(mxIsDouble(T_mx) && !mxIsComplex(T_mx) && !mxIsSparse(T_mx) && "
"mxGetN(T_mx) == 1))"
<< endl
<< R"( mexErrMsgTxt("T_mx should be a real dense column vector");)" << endl << R"( mexErrMsgTxt("T_mx should be a real dense column vector");)" << endl
<< " T_order_on_input = mxGetScalar(T_order_mx);" << endl << " T_order_on_input = mxGetScalar(T_order_mx);" << endl
<< " if (T_order_on_input < " << i << ")" << endl << " if (T_order_on_input < " << i << ")" << endl
<< " {" << endl << " {" << endl
<< " T_order_mx = mxCreateDoubleScalar(" << i << ");" << endl << " T_order_mx = mxCreateDoubleScalar(" << i << ");" << endl
<< " const mxArray *T_old_mx = T_mx;" << endl << " const mxArray *T_old_mx = T_mx;" << endl
<< " T_mx = mxCreateDoubleMatrix(max(" << ttlen << ", mxGetM(T_old_mx)), 1, mxREAL);" << endl << " T_mx = mxCreateDoubleMatrix(max(" << ttlen
<< " memcpy(mxGetPr(T_mx), mxGetPr(T_old_mx), mxGetM(T_old_mx)*sizeof(double));" << endl << ", mxGetM(T_old_mx)), 1, mxREAL);" << endl
<< " memcpy(mxGetPr(T_mx), mxGetPr(T_old_mx), mxGetM(T_old_mx)*sizeof(double));"
<< endl
<< " }" << endl << " }" << endl
<< " else if (mxGetM(T_mx) < " << ttlen << ")" << endl << " else if (mxGetM(T_mx) < " << ttlen << ")" << endl
<< R"( mexErrMsgTxt("T_mx should have at least )" << ttlen << R"( elements");)" << endl << R"( mexErrMsgTxt("T_mx should have at least )" << ttlen << R"( elements");)"
<< endl
<< " }" << endl << " }" << endl
<< " else" << endl << " else" << endl
<< " {" << endl << " {" << endl
...@@ -2791,19 +2893,20 @@ ModelTree::writeSparseModelCFiles(const string &basename, const string &mexext, ...@@ -2791,19 +2893,20 @@ ModelTree::writeSparseModelCFiles(const string &basename, const string &mexext,
for (int j {0}; j <= i; j++) for (int j {0}; j <= i; j++)
{ {
if (j == 0) if (j == 0)
output << " default:" << endl output << " default:" << endl << " " << prefix << "resid";
<< " " << prefix << "resid";
else else
output << " case " << j-1 << ":" << endl output << " case " << j - 1 << ":" << endl << " " << prefix << "g" << j;
<< " " << prefix << "g" << j; output << "_tt(y, x, params" << extra_argout << ", T);" << endl;
output << "_tt(y, x, params" << ss_argout << ", T);" << endl;
} }
output << " }" << endl; output << " }" << endl;
if (i == 1) if (i == 1)
sparse_jacobian_create(0, equations.size(), getJacobianColsNbr(true), jacobian_sparse_column_major_order.size()); sparse_jacobian_create(0, equations.size(), getJacobianColsNbr(true),
jacobian_sparse_column_major_order.size());
else else
output << " plhs[0] = mxCreateDoubleMatrix(" << (i == 0 ? equations.size() : derivatives[i].size()) << ", 1, mxREAL);" << endl; output << " plhs[0] = mxCreateDoubleMatrix("
output << " " << prefix << (i == 0 ? "resid" : "g" + to_string(i)) << "(y, x, params" << ss_argout << ", T, mxGetPr(plhs[0]));" << endl << (i == 0 ? equations.size() : derivatives[i].size()) << ", 1, mxREAL);" << endl;
output << " " << prefix << (i == 0 ? "resid" : "g" + to_string(i)) << "(y, x, params"
<< extra_argout << ", T, mxGetPr(plhs[0]));" << endl
<< " if (nlhs == 3)" << endl << " if (nlhs == 3)" << endl
<< " {" << endl << " {" << endl
<< " plhs[1] = T_order_mx;" << endl << " plhs[1] = T_order_mx;" << endl
...@@ -2838,16 +2941,42 @@ ModelTree::writeSparseModelCFiles(const string &basename, const string &mexext, ...@@ -2838,16 +2941,42 @@ ModelTree::writeSparseModelCFiles(const string &basename, const string &mexext,
// The following two variables are not used for “evaluate” blocks // The following two variables are not used for “evaluate” blocks
int g1_ncols {(one_boundary ? 1 : 3) * blocks[blk].mfs_size}; int g1_ncols {(one_boundary ? 1 : 3) * blocks[blk].mfs_size};
open_file(source_mex); open_file(source_mex);
output << "#include <math.h>" << endl output << "#include <math.h>" << endl << R"(#include "mex.h")" << endl << endl;
<< R"(#include "mex.h")" << endl
<< endl;
writeCHelpersDefinition(output); writeCHelpersDefinition(output);
writeCHelpersDeclaration(output); // Provide external definition of helpers writeCHelpersDeclaration(output); // Provide external definition of helpers
// Function computing residuals and/or endogenous, and temporary terms (incl. those for
// derivatives)
output << endl output << endl
<< "void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])" << endl << "void " << funcname
<< "_resid(double *restrict y, const double *restrict x, const double *restrict "
"params"
<< extra_argin << ", double *restrict T"
<< (evaluate ? "" : ", double *restrict residual") << ")" << endl
<< "{" << endl;
writePerBlockHelper<output_type>(blk, output, temporary_terms_written);
output << "}" << endl;
// Function computing the Jacobian
if (!evaluate)
{
output << endl
<< "void " << funcname
<< "_g1(const double *restrict y, const double *restrict x, const double "
"*restrict params"
<< extra_argin << ", double *restrict T, double *restrict g1_v)" << endl
<< "{" << endl;
writeSparsePerBlockJacobianHelper<output_type>(blk, output, temporary_terms_written);
output << "}" << endl;
}
output << endl
<< "void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])"
<< endl
<< "{" << endl << "{" << endl
<< " if (nrhs != " << nargin << ")" << endl << " if (nrhs != " << nargin << ")" << endl
<< R"( mexErrMsgTxt("Accepts exactly )" << nargin << R"( input arguments");)" << endl; << R"( mexErrMsgTxt("Accepts exactly )" << nargin << R"( input arguments");)"
<< endl;
if (evaluate) if (evaluate)
output << " if (nlhs != 2)" << endl output << " if (nlhs != 2)" << endl
<< R"( mexErrMsgTxt("Accepts exactly 2 output arguments");)" << endl; << R"( mexErrMsgTxt("Accepts exactly 2 output arguments");)" << endl;
...@@ -2856,7 +2985,7 @@ ModelTree::writeSparseModelCFiles(const string &basename, const string &mexext, ...@@ -2856,7 +2985,7 @@ ModelTree::writeSparseModelCFiles(const string &basename, const string &mexext,
evaluate the recursive variables. */ evaluate the recursive variables. */
output << " if (nlhs < 2 || nlhs > 4)" << endl output << " if (nlhs < 2 || nlhs > 4)" << endl
<< R"( mexErrMsgTxt("Accepts 2 to 4 output arguments");)" << endl; << R"( mexErrMsgTxt("Accepts 2 to 4 output arguments");)" << endl;
y_x_params_ss_inputs(false); y_x_params_ss_yagg_inputs(false);
/* We’d like to avoid copying y if this is a “solve” block without /* We’d like to avoid copying y if this is a “solve” block without
recursive variables. Unfortunately “plhs[0]=prhs[0]” leads to recursive variables. Unfortunately “plhs[0]=prhs[0]” leads to
...@@ -2867,42 +2996,54 @@ ModelTree::writeSparseModelCFiles(const string &basename, const string &mexext, ...@@ -2867,42 +2996,54 @@ ModelTree::writeSparseModelCFiles(const string &basename, const string &mexext,
Octave and older MATLAB (and also C++ does not have the “restrict” Octave and older MATLAB (and also C++ does not have the “restrict”
qualifier, so it may not be a net gain). See: qualifier, so it may not be a net gain). See:
https://fr.mathworks.com/matlabcentral/answers/77048-return-large-unchange-mxarray-from-mex https://fr.mathworks.com/matlabcentral/answers/77048-return-large-unchange-mxarray-from-mex
https://fr.mathworks.com/matlabcentral/answers/422751-how-to-output-a-mexfunction-input-without-copy */ https://fr.mathworks.com/matlabcentral/answers/422751-how-to-output-a-mexfunction-input-without-copy
*/
output << " plhs[0] = mxDuplicateArray(prhs[0]);" << endl output << " plhs[0] = mxDuplicateArray(prhs[0]);" << endl
<< " double *restrict y = mxGetPr(plhs[0]);" << endl; << " double *restrict y = mxGetPr(plhs[0]);" << endl;
// NB: For “evaluate” blocks, sparse_{rowval,colval,colptr} arguments are present but ignored // NB: For “evaluate” blocks, sparse_{rowval,colval,colptr} arguments are present but
// ignored
if (!evaluate) if (!evaluate)
sparse_indices_inputs(g1_ncols, blocks_jacobian_sparse_column_major_order[blk].size()); sparse_indices_inputs(g1_ncols, blocks_jacobian_sparse_column_major_order[blk].size());
for (const auto& it : blocks_temporary_terms[blk]) for (const auto& it : blocks_temporary_terms[blk])
total_blk_ttlen += it.size(); total_blk_ttlen += it.size();
output << " if (!(mxIsDouble(prhs[" << nargin-1 << "]) && !mxIsComplex(prhs[" << nargin-1 << "]) && !mxIsSparse(prhs[" << nargin-1 << "]) && mxGetNumberOfElements(prhs[" << nargin-1 << "]) >= " << total_blk_ttlen << "))" << endl output << " if (!(mxIsDouble(prhs[" << nargin - 1 << "]) && !mxIsComplex(prhs["
<< R"( mexErrMsgTxt("T must be a real dense numeric array with at least )" << total_blk_ttlen << R"( elements");)" << endl << nargin - 1 << "]) && !mxIsSparse(prhs[" << nargin - 1
<< "]) && mxGetNumberOfElements(prhs[" << nargin - 1 << "]) >= " << total_blk_ttlen
<< "))" << endl
<< R"( mexErrMsgTxt("T must be a real dense numeric array with at least )"
<< total_blk_ttlen << R"( elements");)"
<< endl
/* We’d like to avoid copying T when the block has no temporary /* We’d like to avoid copying T when the block has no temporary
terms, but the same remark as above applies. */ terms, but the same remark as above applies. */
<< " plhs[1] = mxDuplicateArray(prhs[" << nargin - 1 << "]);" << endl << " plhs[1] = mxDuplicateArray(prhs[" << nargin - 1 << "]);" << endl
<< " double *restrict T = mxGetPr(plhs[1]);" << endl; << " double *restrict T = mxGetPr(plhs[1]);" << endl;
if (!evaluate) if (!evaluate)
output << " mxArray *residual_mx = mxCreateDoubleMatrix(" << blocks[blk].mfs_size << ", 1, mxREAL);" << endl output << " mxArray *residual_mx = mxCreateDoubleMatrix(" << blocks[blk].mfs_size
<< " double *restrict residual = mxGetPr(residual_mx);" << endl << ", 1, mxREAL);" << endl
<< " if (nlhs > 2)" << endl << " double *restrict residual = mxGetPr(residual_mx);" << endl;
<< " plhs[2] = residual_mx;" << endl;
// Write residuals and temporary terms (incl. those for derivatives) output << " " << funcname << "_resid(y, x, params" << extra_argout << ", T"
writePerBlockHelper<output_type>(blk, output, temporary_terms_written); << (evaluate ? "" : ", residual") << ");" << endl;
if (!evaluate) if (!evaluate)
{ {
output << " if (nlhs > 2)" << endl
<< " plhs[2] = residual_mx;" << endl
<< " else" << endl
<< " mxDestroyArray(residual_mx);" << endl;
// Write Jacobian // Write Jacobian
output << " if (nlhs > 3)" << endl output << " if (nlhs > 3)" << endl << " {" << endl;
<< " {" << endl; sparse_jacobian_create(3, blocks[blk].mfs_size, g1_ncols,
sparse_jacobian_create(3, blocks[blk].mfs_size, g1_ncols, blocks_jacobian_sparse_column_major_order[blk].size()); blocks_jacobian_sparse_column_major_order[blk].size());
output << " double *restrict g1_v = mxGetPr(plhs[3]);" << endl; output << " double *restrict g1_v = mxGetPr(plhs[3]);" << endl
writeSparsePerBlockJacobianHelper<output_type>(blk, output, temporary_terms_written); << " " << funcname << "_g1(y, x, params" << extra_argout << ", T, g1_v);"
output << " }" << endl; << endl
<< " }" << endl;
} }
output << "}" << endl; output << "}" << endl;
output.close(); output.close();
...@@ -2915,12 +3056,11 @@ template<bool dynamic> ...@@ -2915,12 +3056,11 @@ template<bool dynamic>
void void
ModelTree::writeDebugModelMFiles(const string& basename) const ModelTree::writeDebugModelMFiles(const string& basename) const
{ {
constexpr ExprNodeOutputType output_type {dynamic ? ExprNodeOutputType::matlabSparseDynamicModel : ExprNodeOutputType::matlabSparseStaticModel}; constexpr ExprNodeOutputType output_type {dynamic ? ExprNodeOutputType::matlabSparseDynamicModel
: ExprNodeOutputType::matlabSparseStaticModel};
const filesystem::path m_dir {packageDir(basename) / "+debug"}; const filesystem::path m_dir {packageDir(basename) / "+debug"};
// TODO: when C++20 support is complete, mark the following strings constexpr
const string prefix {dynamic ? "dynamic_" : "static_"}; const string prefix {dynamic ? "dynamic_" : "static_"};
const string ss_arg { dynamic ? ", steady_state" : "" };
const filesystem::path resid_filename {m_dir / (prefix + "resid.m")}; const filesystem::path resid_filename {m_dir / (prefix + "resid.m")};
ofstream output {resid_filename, ios::out | ios::binary}; ofstream output {resid_filename, ios::out | ios::binary};
...@@ -2930,7 +3070,13 @@ ModelTree::writeDebugModelMFiles(const string &basename) const ...@@ -2930,7 +3070,13 @@ ModelTree::writeDebugModelMFiles(const string &basename) const
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
output << "function [lhs, rhs] = " << prefix << "resid(y, x, params" << ss_arg << ")" << endl output << "function [lhs, rhs] = " << prefix << "resid(y, x, params";
if (dynamic)
output << ", steady_state";
if (!heterogeneity_table.empty())
output << ", yagg";
output << ")" << endl
<< "T = NaN(" << temporary_terms_derivatives[0].size() << ", 1);" << endl << "T = NaN(" << temporary_terms_derivatives[0].size() << ", 1);" << endl
<< "lhs = NaN(" << equations.size() << ", 1);" << endl << "lhs = NaN(" << equations.size() << ", 1);" << endl
<< "rhs = NaN(" << equations.size() << ", 1);" << endl; << "rhs = NaN(" << equations.size() << ", 1);" << endl;
...@@ -2941,13 +3087,13 @@ ModelTree::writeDebugModelMFiles(const string &basename) const ...@@ -2941,13 +3087,13 @@ ModelTree::writeDebugModelMFiles(const string &basename) const
for (size_t eq {0}; eq < equations.size(); eq++) for (size_t eq {0}; eq < equations.size(); eq++)
{ {
output << "lhs" << LEFT_ARRAY_SUBSCRIPT(output_type) output << "lhs" << LEFT_ARRAY_SUBSCRIPT(output_type)
<< eq + ARRAY_SUBSCRIPT_OFFSET(output_type) << eq + ARRAY_SUBSCRIPT_OFFSET(output_type) << RIGHT_ARRAY_SUBSCRIPT(output_type)
<< RIGHT_ARRAY_SUBSCRIPT(output_type) << " = "; << " = ";
equations[eq]->arg1->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs); equations[eq]->arg1->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs);
output << ";" << endl output << ";" << endl
<< "rhs" << LEFT_ARRAY_SUBSCRIPT(output_type) << "rhs" << LEFT_ARRAY_SUBSCRIPT(output_type)
<< eq + ARRAY_SUBSCRIPT_OFFSET(output_type) << eq + ARRAY_SUBSCRIPT_OFFSET(output_type) << RIGHT_ARRAY_SUBSCRIPT(output_type)
<< RIGHT_ARRAY_SUBSCRIPT(output_type) << " = "; << " = ";
equations[eq]->arg2->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs); equations[eq]->arg2->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs);
output << ";" << endl; output << ";" << endl;
} }
...@@ -2961,8 +3107,10 @@ template<bool dynamic> ...@@ -2961,8 +3107,10 @@ template<bool dynamic>
void void
ModelTree::writeSetAuxiliaryVariablesFile(const string& basename, bool julia) const ModelTree::writeSetAuxiliaryVariablesFile(const string& basename, bool julia) const
{ {
const auto output_type { julia ? (dynamic ? ExprNodeOutputType::juliaTimeDataFrame : ExprNodeOutputType::juliaStaticModel) const auto output_type {julia ? (dynamic ? ExprNodeOutputType::juliaTimeDataFrame
: (dynamic ? ExprNodeOutputType::matlabDseries : ExprNodeOutputType::matlabStaticModel) }; : ExprNodeOutputType::juliaStaticModel)
: (dynamic ? ExprNodeOutputType::matlabDseries
: ExprNodeOutputType::matlabStaticModel)};
ostringstream output_func_body; ostringstream output_func_body;
writeAuxVarRecursiveDefinitions(output_func_body, output_type); writeAuxVarRecursiveDefinitions(output_func_body, output_type);
...@@ -2995,13 +3143,14 @@ ModelTree::writeSetAuxiliaryVariablesFile(const string &basename, bool julia) co ...@@ -2995,13 +3143,14 @@ ModelTree::writeSetAuxiliaryVariablesFile(const string &basename, bool julia) co
<< comment << endl; << comment << endl;
if (julia) if (julia)
output << "@inbounds begin" << endl; output << "@inbounds begin" << endl;
output << output_func_body.str() output << output_func_body.str() << "end" << endl;
<< "end" << endl;
if (julia) if (julia)
output << "end" << endl; output << "end" << endl;
if (julia) if (julia)
writeToFileIfModified(output, filesystem::path{basename} / "model" / "julia" / (dynamic ? "DynamicSetAuxiliarySeries.jl" : "SetAuxiliaryVariables.jl")); writeToFileIfModified(
output, filesystem::path {basename} / "model" / "julia"
/ (dynamic ? "DynamicSetAuxiliarySeries.jl" : "SetAuxiliaryVariables.jl"));
else else
{ {
/* Calling writeToFileIfModified() is useless here since we write inside /* Calling writeToFileIfModified() is useless here since we write inside
...@@ -3017,3 +3166,54 @@ ModelTree::writeSetAuxiliaryVariablesFile(const string &basename, bool julia) co ...@@ -3017,3 +3166,54 @@ ModelTree::writeSetAuxiliaryVariablesFile(const string &basename, bool julia) co
output_file.close(); output_file.close();
} }
} }
template<bool dynamic>
void
ModelTree::writeComplementarityConditionsFile(const string& basename,
const optional<int>& heterogeneous_dimension) const
{
const string funcname {
(dynamic ? "dynamic"s : "static"s)
+ (heterogeneous_dimension ? "_het"s + to_string(*heterogeneous_dimension + 1) : ""s)
+ "_complementarity_conditions"};
const filesystem::path filename {packageDir(basename) / (funcname + ".m")};
/* Can’t use matlabOutsideModel for output type, since it uses M_.
Static is ok even for the dynamic model, since there are no leads/lags. */
constexpr ExprNodeOutputType output_type {ExprNodeOutputType::matlabStaticModel};
ofstream output {filename, ios::out | ios::binary};
if (!output.is_open())
{
cerr << "ERROR: Can't open file " << filename.string() << " for writing" << endl;
exit(EXIT_FAILURE);
}
output << "function [lb, ub] = " << funcname << "(params)" << endl
<< "ub = inf(" << equations.size() << ",1);" << endl
<< "lb = -ub;" << endl;
for (const auto& it : complementarity_conditions)
if (it)
{
const auto& [symb_id, lb, ub] = *it;
int endo_id {symbol_table.getTypeSpecificID(symb_id)};
if (lb)
{
output << "lb(" << endo_id + 1 << ")=";
lb->writeOutput(output, output_type);
output << ";" << endl;
}
if (ub)
{
output << "ub(" << endo_id + 1 << ")=";
ub->writeOutput(output, output_type);
output << ";" << endl;
}
}
output << "end" << endl;
output.close();
}
#endif