1 /*
2  * Copyright © 2003-2019 Dynare Team
3  *
4  * This file is part of Dynare.
5  *
6  * Dynare is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Dynare is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Dynare.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <iostream>
21 #include <cmath>
22 #include <cstdlib>
23 #include <cassert>
24 #include <algorithm>
25 #include <numeric>
26 #include <regex>
27 
28 #include "DynamicModel.hh"
29 
30 void
copyHelper(const DynamicModel & m)31 DynamicModel::copyHelper(const DynamicModel &m)
32 {
33   auto f = [this](const ExprNode *e) { return e->clone(*this); };
34 
35   for (const auto &it : m.static_only_equations)
36     static_only_equations.push_back(dynamic_cast<BinaryOpNode *>(f(it)));
37 
38   auto convert_vector_tt = [f](vector<temporary_terms_t> vtt)
39                            {
40                              vector<temporary_terms_t> vtt2;
41                              for (const auto &tt : vtt)
42                                {
43                                  temporary_terms_t tt2;
44                                  for (const auto &it : tt)
45                                    tt2.insert(f(it));
46                                  vtt2.push_back(tt2);
47                                }
48                              return vtt2;
49                            };
50 
51   for (const auto &it : m.v_temporary_terms)
52     v_temporary_terms.push_back(convert_vector_tt(it));
53 
54   for (const auto &it : m.first_chain_rule_derivatives)
55     first_chain_rule_derivatives[it.first] = f(it.second);
56 
57   for (const auto &it : m.equation_type_and_normalized_equation)
58     equation_type_and_normalized_equation.emplace_back(it.first, f(it.second));
59 
60   for (const auto &it : m.blocks_derivatives)
61     {
62       block_derivatives_equation_variable_laglead_nodeid_t v;
63       for (const auto &it2 : it)
64         v.emplace_back(get<0>(it2), get<1>(it2), get<2>(it2), f(get<3>(it2)));
65       blocks_derivatives.push_back(v);
66     }
67 
68   for (const auto &it : m.dynamic_jacobian)
69     dynamic_jacobian[it.first] = f(it.second);
70 
71   auto convert_derivative_t = [f](derivative_t dt)
72                               {
73                                 derivative_t dt2;
74                                 for (const auto &it : dt)
75                                   dt2[it.first] = f(it.second);
76                                 return dt2;
77                               };
78   for (const auto &it : m.derivative_endo)
79     derivative_endo.push_back(convert_derivative_t(it));
80   for (const auto &it : m.derivative_other_endo)
81     derivative_other_endo.push_back(convert_derivative_t(it));
82   for (const auto &it : m.derivative_exo)
83     derivative_exo.push_back(convert_derivative_t(it));
84   for (const auto &it : m.derivative_exo_det)
85     derivative_exo_det.push_back(convert_derivative_t(it));
86 }
87 
DynamicModel(SymbolTable & symbol_table_arg,NumericalConstants & num_constants_arg,ExternalFunctionsTable & external_functions_table_arg,TrendComponentModelTable & trend_component_model_table_arg,VarModelTable & var_model_table_arg)88 DynamicModel::DynamicModel(SymbolTable &symbol_table_arg,
89                            NumericalConstants &num_constants_arg,
90                            ExternalFunctionsTable &external_functions_table_arg,
91                            TrendComponentModelTable &trend_component_model_table_arg,
92                            VarModelTable &var_model_table_arg) :
93   ModelTree{symbol_table_arg, num_constants_arg, external_functions_table_arg, true},
94   trend_component_model_table{trend_component_model_table_arg},
95   var_model_table{var_model_table_arg}
96 {
97 }
98 
DynamicModel(const DynamicModel & m)99 DynamicModel::DynamicModel(const DynamicModel &m) :
100   ModelTree{m},
101   trend_component_model_table{m.trend_component_model_table},
102   var_model_table{m.var_model_table},
103   balanced_growth_test_tol{m.balanced_growth_test_tol},
104   static_only_equations_lineno{m.static_only_equations_lineno},
105   static_only_equations_equation_tags{m.static_only_equations_equation_tags},
106   static_only_equation_tags_xref{m.static_only_equation_tags_xref},
107   deriv_id_table{m.deriv_id_table},
108   inv_deriv_id_table{m.inv_deriv_id_table},
109   dyn_jacobian_cols_table{m.dyn_jacobian_cols_table},
110   max_lag{m.max_lag},
111   max_lead{m.max_lead},
112   max_endo_lag{m.max_endo_lag},
113   max_endo_lead{m.max_endo_lead},
114   max_exo_lag{m.max_exo_lag},
115   max_exo_lead{m.max_exo_lead},
116   max_exo_det_lag{m.max_exo_det_lag},
117   max_exo_det_lead{m.max_exo_det_lead},
118   max_lag_orig{m.max_lag_orig},
119   max_lead_orig{m.max_lead_orig},
120   max_lag_with_diffs_expanded_orig{m.max_lag_with_diffs_expanded_orig},
121   max_endo_lag_orig{m.max_endo_lag_orig},
122   max_endo_lead_orig{m.max_endo_lead_orig},
123   max_exo_lag_orig{m.max_exo_lag_orig},
124   max_exo_lead_orig{m.max_exo_lead_orig},
125   max_exo_det_lag_orig{m.max_exo_det_lag_orig},
126   max_exo_det_lead_orig{m.max_exo_det_lead_orig},
127   xrefs{m.xrefs},
128   xref_param{m.xref_param},
129   xref_endo{m.xref_endo},
130   xref_exo{m.xref_exo},
131   xref_exo_det{m.xref_exo_det},
132   nonzero_hessian_eqs{m.nonzero_hessian_eqs},
133   dynJacobianColsNbr{m.dynJacobianColsNbr},
134   v_temporary_terms_inuse{m.v_temporary_terms_inuse},
135   variableMapping{m.variableMapping},
136   map_idx{m.map_idx},
137   global_temporary_terms{m.global_temporary_terms},
138   block_type_firstequation_size_mfs{m.block_type_firstequation_size_mfs},
139   blocks_linear{m.blocks_linear},
140   other_endo_block{m.other_endo_block},
141   exo_block{m.exo_block},
142   exo_det_block{m.exo_det_block},
143   block_var_exo{m.block_var_exo},
144   block_exo_index{m.block_exo_index},
145   block_det_exo_index{m.block_det_exo_index},
146   block_other_endo_index{m.block_other_endo_index},
147   block_col_type{m.block_col_type},
148   variable_block_lead_lag{m.variable_block_lead_lag},
149   equation_block{m.equation_block},
150   var_expectation_functions_to_write{m.var_expectation_functions_to_write},
151   endo_max_leadlag_block{m.endo_max_leadlag_block},
152   other_endo_max_leadlag_block{m.other_endo_max_leadlag_block},
153   exo_max_leadlag_block{m.exo_max_leadlag_block},
154   exo_det_max_leadlag_block{m.exo_det_max_leadlag_block},
155   max_leadlag_block{m.max_leadlag_block}
156 {
157   copyHelper(m);
158 }
159 
160 DynamicModel &
operator =(const DynamicModel & m)161 DynamicModel::operator=(const DynamicModel &m)
162 {
163   ModelTree::operator=(m);
164 
165   assert(&trend_component_model_table == &m.trend_component_model_table);
166   assert(&var_model_table == &m.var_model_table);
167   balanced_growth_test_tol = m.balanced_growth_test_tol;
168 
169   static_only_equations_lineno = m.static_only_equations_lineno;
170   static_only_equations_equation_tags = m.static_only_equations_equation_tags;
171   static_only_equation_tags_xref = m.static_only_equation_tags_xref;
172   deriv_id_table = m.deriv_id_table;
173   inv_deriv_id_table = m.inv_deriv_id_table;
174   dyn_jacobian_cols_table = m.dyn_jacobian_cols_table;
175   max_lag = m.max_lag;
176   max_lead = m.max_lead;
177   max_endo_lag = m.max_endo_lag;
178   max_endo_lead = m.max_endo_lead;
179   max_exo_lag = m.max_exo_lag;
180   max_exo_lead = m.max_exo_lead;
181   max_exo_det_lag = m.max_exo_det_lag;
182   max_exo_det_lead = m.max_exo_det_lead;
183   max_lag_orig = m.max_lag_orig;
184   max_lead_orig = m.max_lead_orig;
185   max_lag_with_diffs_expanded_orig = m.max_lag_with_diffs_expanded_orig;
186   max_endo_lag_orig = m.max_endo_lag_orig;
187   max_endo_lead_orig = m.max_endo_lead_orig;
188   max_exo_lag_orig = m.max_exo_lag_orig;
189   max_exo_lead_orig = m.max_exo_lead_orig;
190   max_exo_det_lag_orig = m.max_exo_det_lag_orig;
191   max_exo_det_lead_orig = m.max_exo_det_lead_orig;
192   xrefs = m.xrefs;
193   xref_param = m.xref_param;
194   xref_endo = m.xref_endo;
195   xref_exo = m.xref_exo;
196   xref_exo_det = m.xref_exo_det;
197   nonzero_hessian_eqs = m.nonzero_hessian_eqs;
198 
199   dynJacobianColsNbr = m.dynJacobianColsNbr;
200 
201   v_temporary_terms.clear();
202 
203   v_temporary_terms_inuse = m.v_temporary_terms_inuse;
204 
205   variableMapping = m.variableMapping;
206 
207   first_chain_rule_derivatives.clear();
208 
209   map_idx = m.map_idx;
210   global_temporary_terms = m.global_temporary_terms;
211 
212   equation_type_and_normalized_equation.clear();
213 
214   block_type_firstequation_size_mfs = m.block_type_firstequation_size_mfs;
215 
216   blocks_derivatives.clear();
217   dynamic_jacobian.clear();
218 
219   blocks_linear = m.blocks_linear;
220 
221   derivative_endo.clear();
222   derivative_other_endo.clear();
223   derivative_exo.clear();
224   derivative_exo_det.clear();
225 
226   other_endo_block = m.other_endo_block;
227   exo_block = m.exo_block;
228   exo_det_block = m.exo_det_block;
229   block_var_exo = m.block_var_exo;
230   block_exo_index = m.block_exo_index;
231   block_det_exo_index = m.block_det_exo_index;
232   block_other_endo_index = m.block_other_endo_index;
233   block_col_type = m.block_col_type;
234   variable_block_lead_lag = m.variable_block_lead_lag;
235   equation_block = m.equation_block;
236   var_expectation_functions_to_write = m.var_expectation_functions_to_write;
237 
238   endo_max_leadlag_block = m.endo_max_leadlag_block;
239   other_endo_max_leadlag_block = m.other_endo_max_leadlag_block;
240   exo_max_leadlag_block = m.exo_max_leadlag_block;
241   exo_det_max_leadlag_block = m.exo_det_max_leadlag_block;
242   max_leadlag_block = m.max_leadlag_block;
243 
244   copyHelper(m);
245 
246   return *this;
247 }
248 
249 void
compileDerivative(ofstream & code_file,unsigned int & instruction_number,int eq,int symb_id,int lag,const map_idx_t & map_idx) const250 DynamicModel::compileDerivative(ofstream &code_file, unsigned int &instruction_number, int eq, int symb_id, int lag, const map_idx_t &map_idx) const
251 {
252   if (auto it = derivatives[1].find({ eq, getDerivID(symbol_table.getID(SymbolType::endogenous, symb_id), lag) });
253       it != derivatives[1].end())
254     it->second->compile(code_file, instruction_number, false, temporary_terms, map_idx, true, false);
255   else
256     {
257       FLDZ_ fldz;
258       fldz.write(code_file, instruction_number);
259     }
260 }
261 
262 void
compileChainRuleDerivative(ofstream & code_file,unsigned int & instruction_number,int eqr,int varr,int lag,const map_idx_t & map_idx) const263 DynamicModel::compileChainRuleDerivative(ofstream &code_file, unsigned int &instruction_number, int eqr, int varr, int lag, const map_idx_t &map_idx) const
264 {
265   if (auto it = first_chain_rule_derivatives.find({ eqr, varr, lag }); it != first_chain_rule_derivatives.end())
266     it->second->compile(code_file, instruction_number, false, temporary_terms, map_idx, true, false);
267   else
268     {
269       FLDZ_ fldz;
270       fldz.write(code_file, instruction_number);
271     }
272 }
273 
274 void
computeTemporaryTermsOrdered()275 DynamicModel::computeTemporaryTermsOrdered()
276 {
277   map<expr_t, pair<int, int>> first_occurence;
278   map<expr_t, int> reference_count;
279   BinaryOpNode *eq_node;
280   first_chain_rule_derivatives_t::const_iterator it_chr;
281   ostringstream tmp_s;
282   v_temporary_terms.clear();
283   map_idx.clear();
284 
285   unsigned int nb_blocks = getNbBlocks();
286   v_temporary_terms = vector<vector<temporary_terms_t>>(nb_blocks);
287   v_temporary_terms_inuse = vector<temporary_terms_inuse_t>(nb_blocks);
288   temporary_terms.clear();
289 
290   if (!global_temporary_terms)
291     {
292       for (unsigned int block = 0; block < nb_blocks; block++)
293         {
294           reference_count.clear();
295           temporary_terms.clear();
296           unsigned int block_size = getBlockSize(block);
297           unsigned int block_nb_mfs = getBlockMfs(block);
298           unsigned int block_nb_recursives = block_size - block_nb_mfs;
299           v_temporary_terms[block] = vector<temporary_terms_t>(block_size);
300           for (unsigned int i = 0; i < block_size; i++)
301             {
302               if (i < block_nb_recursives && isBlockEquationRenormalized(block, i))
303                 getBlockEquationRenormalizedExpr(block, i)->computeTemporaryTerms(reference_count, temporary_terms, first_occurence, block, v_temporary_terms, i);
304               else
305                 {
306                   eq_node = static_cast<BinaryOpNode *>(getBlockEquationExpr(block, i));
307                   eq_node->computeTemporaryTerms(reference_count, temporary_terms, first_occurence, block, v_temporary_terms, i);
308                 }
309             }
310           for (const auto &it : blocks_derivatives[block])
311             {
312               expr_t id = get<3>(it);
313               id->computeTemporaryTerms(reference_count, temporary_terms, first_occurence, block, v_temporary_terms, block_size-1);
314             }
315           for (const auto &it : derivative_other_endo[block])
316             it.second->computeTemporaryTerms(reference_count, temporary_terms, first_occurence, block, v_temporary_terms, block_size-1);
317           v_temporary_terms_inuse[block] = {};
318         }
319     }
320   else
321     {
322       for (unsigned int block = 0; block < nb_blocks; block++)
323         {
324           // Compute the temporary terms reordered
325           unsigned int block_size = getBlockSize(block);
326           unsigned int block_nb_mfs = getBlockMfs(block);
327           unsigned int block_nb_recursives = block_size - block_nb_mfs;
328           v_temporary_terms[block] = vector<temporary_terms_t>(block_size);
329           for (unsigned int i = 0; i < block_size; i++)
330             {
331               if (i < block_nb_recursives && isBlockEquationRenormalized(block, i))
332                 getBlockEquationRenormalizedExpr(block, i)->computeTemporaryTerms(reference_count, temporary_terms, first_occurence, block, v_temporary_terms, i);
333               else
334                 {
335                   eq_node = static_cast<BinaryOpNode *>(getBlockEquationExpr(block, i));
336                   eq_node->computeTemporaryTerms(reference_count, temporary_terms, first_occurence, block, v_temporary_terms, i);
337                 }
338             }
339           for (const auto &it : blocks_derivatives[block])
340             {
341               expr_t id = get<3>(it);
342               id->computeTemporaryTerms(reference_count, temporary_terms, first_occurence, block, v_temporary_terms, block_size-1);
343             }
344           for (const auto &it : derivative_other_endo[block])
345             it.second->computeTemporaryTerms(reference_count, temporary_terms, first_occurence, block, v_temporary_terms, block_size-1);
346         }
347       for (unsigned int block = 0; block < nb_blocks; block++)
348         {
349           // Collect the temporary terms reordered
350           unsigned int block_size = getBlockSize(block);
351           unsigned int block_nb_mfs = getBlockMfs(block);
352           unsigned int block_nb_recursives = block_size - block_nb_mfs;
353           set<int> temporary_terms_in_use;
354           for (unsigned int i = 0; i < block_size; i++)
355             {
356               if (i < block_nb_recursives && isBlockEquationRenormalized(block, i))
357                 getBlockEquationRenormalizedExpr(block, i)->collectTemporary_terms(temporary_terms, temporary_terms_in_use, block);
358               else
359                 {
360                   eq_node = static_cast<BinaryOpNode *>(getBlockEquationExpr(block, i));
361                   eq_node->collectTemporary_terms(temporary_terms, temporary_terms_in_use, block);
362                 }
363             }
364           for (const auto &it : blocks_derivatives[block])
365             {
366               expr_t id = get<3>(it);
367               id->collectTemporary_terms(temporary_terms, temporary_terms_in_use, block);
368             }
369           for (const auto &it : derivative_other_endo[block])
370             it.second->collectTemporary_terms(temporary_terms, temporary_terms_in_use, block);
371           for (const auto &it : derivative_exo[block])
372             it.second->collectTemporary_terms(temporary_terms, temporary_terms_in_use, block);
373           for (const auto &it : derivative_exo_det[block])
374             it.second->collectTemporary_terms(temporary_terms, temporary_terms_in_use, block);
375           v_temporary_terms_inuse[block] = temporary_terms_in_use;
376         }
377       computeTemporaryTermsMapping();
378     }
379 }
380 
381 void
computeTemporaryTermsMapping()382 DynamicModel::computeTemporaryTermsMapping()
383 {
384   // Add a mapping form node ID to temporary terms order
385   int j = 0;
386   for (auto temporary_term : temporary_terms)
387     map_idx[temporary_term->idx] = j++;
388 }
389 
390 void
writeModelEquationsOrdered_M(const string & basename) const391 DynamicModel::writeModelEquationsOrdered_M(const string &basename) const
392 {
393   string tmp_s, sps;
394   ostringstream tmp_output, tmp1_output, global_output;
395   expr_t lhs = nullptr, rhs = nullptr;
396   BinaryOpNode *eq_node;
397   ostringstream Ufoss;
398   vector<string> Uf(symbol_table.endo_nbr(), "");
399   map<expr_t, int> reference_count;
400   temporary_terms_t local_temporary_terms;
401   ofstream output;
402   int nze, nze_exo, nze_exo_det, nze_other_endo;
403   vector<int> feedback_variables;
404   ExprNodeOutputType local_output_type;
405 
406   local_output_type = ExprNodeOutputType::matlabDynamicModelSparse;
407   if (global_temporary_terms)
408     local_temporary_terms = temporary_terms;
409 
410   //----------------------------------------------------------------------
411   //For each block
412   for (unsigned int block = 0; block < getNbBlocks(); block++)
413     {
414 
415       //recursive_variables.clear();
416       feedback_variables.clear();
417       //For a block composed of a single equation determines wether we have to evaluate or to solve the equation
418       nze = blocks_derivatives[block].size();
419       nze_other_endo = derivative_other_endo[block].size();
420       nze_exo = derivative_exo[block].size();
421       nze_exo_det = derivative_exo_det[block].size();
422       BlockSimulationType simulation_type = getBlockSimulationType(block);
423       unsigned int block_size = getBlockSize(block);
424       unsigned int block_mfs = getBlockMfs(block);
425       unsigned int block_recursive = block_size - block_mfs;
426       deriv_node_temp_terms_t tef_terms;
427       local_output_type = ExprNodeOutputType::matlabDynamicModelSparse;
428       if (global_temporary_terms)
429         local_temporary_terms = temporary_terms;
430 
431       int prev_lag;
432       unsigned int prev_var, count_col, count_col_endo, count_col_exo, count_col_exo_det, count_col_other_endo;
433       map<tuple<int, int, int>, expr_t> tmp_block_endo_derivative;
434       for (const auto &it : blocks_derivatives[block])
435         tmp_block_endo_derivative[{ get<2>(it), get<1>(it), get<0>(it) }] = get<3>(it);
436       prev_var = 999999999;
437       prev_lag = -9999999;
438       count_col_endo = 0;
439       for (const auto &it : tmp_block_endo_derivative)
440         {
441           int lag = get<0>(it.first);
442           unsigned int var = get<1>(it.first);
443           if (var != prev_var || lag != prev_lag)
444             {
445               prev_var = var;
446               prev_lag = lag;
447               count_col_endo++;
448             }
449         }
450       map<tuple<int, int, int>, expr_t> tmp_block_exo_derivative;
451       for (const auto &it : derivative_exo[block])
452         tmp_block_exo_derivative[{ get<0>(it.first), get<2>(it.first), get<1>(it.first) }] = it.second;
453       prev_var = 999999999;
454       prev_lag = -9999999;
455       count_col_exo = 0;
456       for (const auto &it : tmp_block_exo_derivative)
457         {
458           int lag = get<0>(it.first);
459           unsigned int var = get<1>(it.first);
460           if (var != prev_var || lag != prev_lag)
461             {
462               prev_var = var;
463               prev_lag = lag;
464               count_col_exo++;
465             }
466         }
467       map<tuple<int, int, int>, expr_t> tmp_block_exo_det_derivative;
468       for (const auto &it : derivative_exo_det[block])
469         tmp_block_exo_det_derivative[{ get<0>(it.first), get<2>(it.first), get<1>(it.first) }] = it.second;
470       prev_var = 999999999;
471       prev_lag = -9999999;
472       count_col_exo_det = 0;
473       for (const auto &it : tmp_block_exo_det_derivative)
474         {
475           int lag = get<0>(it.first);
476           unsigned int var = get<1>(it.first);
477           if (var != prev_var || lag != prev_lag)
478             {
479               prev_var = var;
480               prev_lag = lag;
481               count_col_exo_det++;
482             }
483         }
484       map<tuple<int, int, int>, expr_t> tmp_block_other_endo_derivative;
485       for (const auto &it : derivative_other_endo[block])
486         tmp_block_other_endo_derivative[{ get<0>(it.first), get<2>(it.first), get<1>(it.first) }] = it.second;
487       prev_var = 999999999;
488       prev_lag = -9999999;
489       count_col_other_endo = 0;
490       for (const auto &it : tmp_block_other_endo_derivative)
491         {
492           int lag = get<0>(it.first);
493           unsigned int var = get<1>(it.first);
494           if (var != prev_var || lag != prev_lag)
495             {
496               prev_var = var;
497               prev_lag = lag;
498               count_col_other_endo++;
499             }
500         }
501 
502       tmp1_output.str("");
503       tmp1_output << packageDir(basename + ".block") << "/dynamic_" << block+1 << ".m";
504       output.open(tmp1_output.str(), ios::out | ios::binary);
505       output << "%" << endl
506              << "% " << tmp1_output.str() << " : Computes dynamic model for Dynare" << endl
507              << "%" << endl
508              << "% Warning : this file is generated automatically by Dynare" << endl
509              << "%           from model file (.mod)" << endl << endl
510              << "%/" << endl;
511       if (simulation_type == EVALUATE_BACKWARD || simulation_type == EVALUATE_FORWARD)
512         {
513           output << "function [y, g1, g2, g3, varargout] = dynamic_" << block+1 << "(y, x, params, steady_state, jacobian_eval, y_kmin, periods)" << endl;
514         }
515       else if (simulation_type == SOLVE_FORWARD_COMPLETE || simulation_type == SOLVE_BACKWARD_COMPLETE)
516         output << "function [residual, y, g1, g2, g3, varargout] = dynamic_" << block+1 << "(y, x, params, steady_state, it_, jacobian_eval)" << endl;
517       else if (simulation_type == SOLVE_BACKWARD_SIMPLE || simulation_type == SOLVE_FORWARD_SIMPLE)
518         output << "function [residual, y, g1, g2, g3, varargout] = dynamic_" << block+1 << "(y, x, params, steady_state, it_, jacobian_eval)" << endl;
519       else
520         output << "function [residual, y, g1, g2, g3, b, varargout] = dynamic_" << block+1 << "(y, x, params, steady_state, periods, jacobian_eval, y_kmin, y_size, Periods)" << endl;
521       BlockType block_type;
522       if (simulation_type == SOLVE_TWO_BOUNDARIES_COMPLETE || simulation_type == SOLVE_TWO_BOUNDARIES_SIMPLE)
523         block_type = SIMULTAN;
524       else if (simulation_type == SOLVE_FORWARD_COMPLETE || simulation_type == SOLVE_BACKWARD_COMPLETE)
525         block_type = SIMULTANS;
526       else if ((simulation_type == SOLVE_FORWARD_SIMPLE || simulation_type == SOLVE_BACKWARD_SIMPLE
527                 || simulation_type == EVALUATE_BACKWARD || simulation_type == EVALUATE_FORWARD)
528                && getBlockFirstEquation(block) < prologue)
529         block_type = PROLOGUE;
530       else if ((simulation_type == SOLVE_FORWARD_SIMPLE || simulation_type == SOLVE_BACKWARD_SIMPLE
531                 || simulation_type == EVALUATE_BACKWARD || simulation_type == EVALUATE_FORWARD)
532                && getBlockFirstEquation(block) >= equations.size() - epilogue)
533         block_type = EPILOGUE;
534       else
535         block_type = SIMULTANS;
536       output << "  % ////////////////////////////////////////////////////////////////////////" << endl
537              << "  % //" << string("                     Block ").substr(int (log10(block + 1))) << block + 1 << " " << BlockType0(block_type)
538              << "          //" << endl
539              << "  % //                     Simulation type "
540              << BlockSim(simulation_type) << "  //" << endl
541              << "  % ////////////////////////////////////////////////////////////////////////" << endl;
542       //The Temporary terms
543       if (simulation_type == EVALUATE_BACKWARD || simulation_type == EVALUATE_FORWARD)
544         {
545           output << "  if(jacobian_eval)" << endl
546                  << "    g1 = spalloc(" << block_mfs  << ", " << count_col_endo << ", " << nze << ");" << endl
547                  << "    g1_x=spalloc(" << block_size << ", " << count_col_exo  << ", " << nze_exo << ");" << endl
548                  << "    g1_xd=spalloc(" << block_size << ", " << count_col_exo_det  << ", " << nze_exo_det << ");" << endl
549                  << "    g1_o=spalloc(" << block_size << ", " << count_col_other_endo << ", " << nze_other_endo << ");" << endl
550                  << "  end;" << endl;
551         }
552       else
553         {
554           output << "  if(jacobian_eval)" << endl
555                  << "    g1 = spalloc(" << block_size << ", " << count_col_endo << ", " << nze << ");" << endl
556                  << "    g1_x=spalloc(" << block_size << ", " << count_col_exo  << ", " << nze_exo << ");" << endl
557                  << "    g1_xd=spalloc(" << block_size << ", " << count_col_exo_det  << ", " << nze_exo_det << ");" << endl
558                  << "    g1_o=spalloc(" << block_size << ", " << count_col_other_endo << ", " << nze_other_endo << ");" << endl
559                  << "  else" << endl;
560           if (simulation_type == SOLVE_TWO_BOUNDARIES_COMPLETE || simulation_type == SOLVE_TWO_BOUNDARIES_SIMPLE)
561             output << "    g1 = spalloc(" << block_mfs << "*Periods, "
562                    << block_mfs << "*(Periods+" << max_leadlag_block[block].first+max_leadlag_block[block].second+1 << ")"
563                    << ", " << nze << "*Periods);" << endl;
564           else
565             output << "    g1 = spalloc(" << block_mfs
566                    << ", " << block_mfs << ", " << nze << ");" << endl;
567           output << "  end;" << endl;
568         }
569 
570       output << "  g2=0;g3=0;" << endl;
571       if (v_temporary_terms_inuse[block].size())
572         {
573           tmp_output.str("");
574           for (int it : v_temporary_terms_inuse[block])
575             tmp_output << " T" << it;
576           output << "  global" << tmp_output.str() << ";" << endl;
577         }
578       if (simulation_type == SOLVE_TWO_BOUNDARIES_COMPLETE || simulation_type == SOLVE_TWO_BOUNDARIES_SIMPLE)
579         {
580           temporary_terms_t tt2;
581           for (int i = 0; i < static_cast<int>(block_size); i++)
582             {
583               if (v_temporary_terms[block][i].size() && global_temporary_terms)
584                 {
585                   output << "  " << "% //Temporary variables initialization" << endl
586                          << "  " << "T_zeros = zeros(y_kmin+periods, 1);" << endl;
587                   for (auto it : v_temporary_terms[block][i])
588                     {
589                       output << "  ";
590                       // In the following, "Static" is used to avoid getting the "(it_)" subscripting
591                       it->writeOutput(output, ExprNodeOutputType::matlabStaticModelSparse, local_temporary_terms, {});
592                       output << " = T_zeros;" << endl;
593                     }
594                 }
595             }
596         }
597       if (simulation_type == SOLVE_BACKWARD_SIMPLE || simulation_type == SOLVE_FORWARD_SIMPLE || simulation_type == SOLVE_BACKWARD_COMPLETE || simulation_type == SOLVE_FORWARD_COMPLETE)
598         output << "  residual=zeros(" << block_mfs << ",1);" << endl;
599       else if (simulation_type == SOLVE_TWO_BOUNDARIES_COMPLETE || simulation_type == SOLVE_TWO_BOUNDARIES_SIMPLE)
600         output << "  residual=zeros(" << block_mfs << ",y_kmin+periods);" << endl;
601       if (simulation_type == EVALUATE_BACKWARD)
602         output << "  for it_ = (y_kmin+periods):-1:y_kmin+1" << endl;
603       if (simulation_type == EVALUATE_FORWARD)
604         output << "  for it_ = y_kmin+1:(y_kmin+periods)" << endl;
605 
606       if (simulation_type == SOLVE_TWO_BOUNDARIES_COMPLETE || simulation_type == SOLVE_TWO_BOUNDARIES_SIMPLE)
607         {
608           output << "  b = zeros(periods*y_size,1);" << endl
609                  << "  for it_ = y_kmin+1:(periods+y_kmin)" << endl
610                  << "    Per_y_=it_*y_size;" << endl
611                  << "    Per_J_=(it_-y_kmin-1)*y_size;" << endl
612                  << "    Per_K_=(it_-1)*y_size;" << endl;
613           sps = "  ";
614         }
615       else
616         if (simulation_type == EVALUATE_BACKWARD || simulation_type == EVALUATE_FORWARD)
617           sps = "  ";
618         else
619           sps = "";
620       // The equations
621       temporary_terms_idxs_t temporary_terms_idxs;
622       for (unsigned int i = 0; i < block_size; i++)
623         {
624           temporary_terms_t tt2;
625           if (v_temporary_terms[block].size())
626             {
627               output << "  " << "% //Temporary variables" << endl;
628               for (auto it : v_temporary_terms[block][i])
629                 {
630                   if (dynamic_cast<AbstractExternalFunctionNode *>(it) != nullptr)
631                     it->writeExternalFunctionOutput(output, local_output_type, tt2, temporary_terms_idxs, tef_terms);
632 
633                   output << "  " <<  sps;
634                   it->writeOutput(output, local_output_type, local_temporary_terms, {}, tef_terms);
635                   output << " = ";
636                   it->writeOutput(output, local_output_type, tt2, {}, tef_terms);
637                   // Insert current node into tt2
638                   tt2.insert(it);
639                   output << ";" << endl;
640                 }
641             }
642 
643           int variable_ID = getBlockVariableID(block, i);
644           int equation_ID = getBlockEquationID(block, i);
645           EquationType equ_type = getBlockEquationType(block, i);
646           string sModel = symbol_table.getName(symbol_table.getID(SymbolType::endogenous, variable_ID));
647           eq_node = static_cast<BinaryOpNode *>(getBlockEquationExpr(block, i));
648           lhs = eq_node->arg1;
649           rhs = eq_node->arg2;
650           tmp_output.str("");
651           lhs->writeOutput(tmp_output, local_output_type, local_temporary_terms, {});
652           switch (simulation_type)
653             {
654             case EVALUATE_BACKWARD:
655             case EVALUATE_FORWARD:
656             evaluation:     if (simulation_type == SOLVE_TWO_BOUNDARIES_COMPLETE || simulation_type == SOLVE_TWO_BOUNDARIES_SIMPLE)
657                 output << "    % equation " << getBlockEquationID(block, i)+1 << " variable : " << sModel
658                        << " (" << variable_ID+1 << ") " << c_Equation_Type(equ_type) << endl;
659               output << "    ";
660               if (equ_type == E_EVALUATE)
661                 {
662                   output << tmp_output.str();
663                   output << " = ";
664                   rhs->writeOutput(output, local_output_type, local_temporary_terms, {});
665                 }
666               else if (equ_type == E_EVALUATE_S)
667                 {
668                   output << "%" << tmp_output.str();
669                   output << " = ";
670                   if (isBlockEquationRenormalized(block, i))
671                     {
672                       rhs->writeOutput(output, local_output_type, local_temporary_terms, {});
673                       output << endl << "    ";
674                       tmp_output.str("");
675                       eq_node = static_cast<BinaryOpNode *>(getBlockEquationRenormalizedExpr(block, i));
676                       lhs = eq_node->arg1;
677                       rhs = eq_node->arg2;
678                       lhs->writeOutput(output, local_output_type, local_temporary_terms, {});
679                       output << " = ";
680                       rhs->writeOutput(output, local_output_type, local_temporary_terms, {});
681                     }
682                 }
683               else
684                 {
685                   cerr << "Type mismatch for equation " << equation_ID+1  << endl;
686                   exit(EXIT_FAILURE);
687                 }
688               output << ";" << endl;
689               break;
690             case SOLVE_BACKWARD_SIMPLE:
691             case SOLVE_FORWARD_SIMPLE:
692             case SOLVE_BACKWARD_COMPLETE:
693             case SOLVE_FORWARD_COMPLETE:
694               if (i < block_recursive)
695                 goto evaluation;
696               feedback_variables.push_back(variable_ID);
697               output << "  % equation " << equation_ID+1 << " variable : " << sModel
698                      << " (" << variable_ID+1 << ") " << c_Equation_Type(equ_type) << " symb_id=" << symbol_table.getID(SymbolType::endogenous, variable_ID) << endl;
699               output << "  " << "residual(" << i+1-block_recursive << ") = (";
700               goto end;
701             case SOLVE_TWO_BOUNDARIES_COMPLETE:
702             case SOLVE_TWO_BOUNDARIES_SIMPLE:
703               if (i < block_recursive)
704                 goto evaluation;
705               feedback_variables.push_back(variable_ID);
706               output << "    % equation " << equation_ID+1 << " variable : " << sModel
707                      << " (" << variable_ID+1 << ") " << c_Equation_Type(equ_type) << " symb_id=" << symbol_table.getID(SymbolType::endogenous, variable_ID) << endl;
708               Ufoss << "    b(" << i+1-block_recursive << "+Per_J_) = -residual(" << i+1-block_recursive << ", it_)";
709               Uf[equation_ID] += Ufoss.str();
710               Ufoss.str("");
711               output << "    residual(" << i+1-block_recursive << ", it_) = (";
712               goto end;
713             default:
714             end:
715               output << tmp_output.str();
716               output << ") - (";
717               rhs->writeOutput(output, local_output_type, local_temporary_terms, {});
718               output << ");" << endl;
719 #ifdef CONDITION
720               if (simulation_type == SOLVE_TWO_BOUNDARIES_COMPLETE || simulation_type == SOLVE_TWO_BOUNDARIES_SIMPLE)
721                 output << "  condition(" << i+1 << ")=0;" << endl;
722 #endif
723             }
724         }
725       // The Jacobian if we have to solve the block
726       if (simulation_type == SOLVE_TWO_BOUNDARIES_SIMPLE || simulation_type == SOLVE_TWO_BOUNDARIES_COMPLETE)
727         output << "  " << sps << "% Jacobian  " << endl << "    if jacobian_eval" << endl;
728       else
729         if (simulation_type == SOLVE_BACKWARD_SIMPLE || simulation_type == SOLVE_FORWARD_SIMPLE
730             || simulation_type == SOLVE_BACKWARD_COMPLETE || simulation_type == SOLVE_FORWARD_COMPLETE)
731           output << "  % Jacobian  " << endl << "  if jacobian_eval" << endl;
732         else
733           output << "    % Jacobian  " << endl << "    if jacobian_eval" << endl;
734       prev_var = 999999999;
735       prev_lag = -9999999;
736       count_col = 0;
737       for (const auto &it : tmp_block_endo_derivative)
738         {
739           int lag;
740           unsigned int var, eq;
741           tie(lag, var, eq) = it.first;
742           int eqr = getBlockEquationID(block, eq);
743           int varr = getBlockVariableID(block, var);
744           if (var != prev_var || lag != prev_lag)
745             {
746               prev_var = var;
747               prev_lag = lag;
748               count_col++;
749             }
750 
751           expr_t id = it.second;
752 
753           output << "      g1(" << eq+1 << ", " << count_col << ") = ";
754           id->writeOutput(output, local_output_type, local_temporary_terms, {});
755           output << "; % variable=" << symbol_table.getName(symbol_table.getID(SymbolType::endogenous, varr))
756                  << "(" << lag
757                  << ") " << varr+1 << ", " << var+1
758                  << ", equation=" << eqr+1 << ", " << eq+1 << endl;
759         }
760       prev_var = 999999999;
761       prev_lag = -9999999;
762       count_col = 0;
763       for (const auto &it : tmp_block_exo_derivative)
764         {
765           int lag;
766           unsigned int var, eq;
767           tie(lag, var, eq) = it.first;
768           int eqr = getBlockInitialEquationID(block, eq);
769           if (var != prev_var || lag != prev_lag)
770             {
771               prev_var = var;
772               prev_lag = lag;
773               count_col++;
774             }
775           expr_t id = it.second;
776           output << "      g1_x(" << eqr+1 << ", " << count_col << ") = ";
777           id->writeOutput(output, local_output_type, local_temporary_terms, {});
778           output << "; % variable=" << symbol_table.getName(symbol_table.getID(SymbolType::exogenous, var))
779                  << "(" << lag
780                  << ") " << var+1
781                  << ", equation=" << eq+1 << endl;
782         }
783       prev_var = 999999999;
784       prev_lag = -9999999;
785       count_col = 0;
786       for (const auto &it : tmp_block_exo_det_derivative)
787         {
788           int lag;
789           unsigned int var, eq;
790           tie(lag, var, eq) = it.first;
791           int eqr = getBlockInitialEquationID(block, eq);
792           if (var != prev_var || lag != prev_lag)
793             {
794               prev_var = var;
795               prev_lag = lag;
796               count_col++;
797             }
798           expr_t id = it.second;
799           output << "      g1_xd(" << eqr+1 << ", " << count_col << ") = ";
800           id->writeOutput(output, local_output_type, local_temporary_terms, {});
801           output << "; % variable=" << symbol_table.getName(symbol_table.getID(SymbolType::exogenous, var))
802                  << "(" << lag
803                  << ") " << var+1
804                  << ", equation=" << eq+1 << endl;
805         }
806       prev_var = 999999999;
807       prev_lag = -9999999;
808       count_col = 0;
809       for (const auto &it : tmp_block_other_endo_derivative)
810         {
811           int lag;
812           unsigned int var, eq;
813           tie(lag, var, eq) = it.first;
814           int eqr = getBlockInitialEquationID(block, eq);
815           if (var != prev_var || lag != prev_lag)
816             {
817               prev_var = var;
818               prev_lag = lag;
819               count_col++;
820             }
821           expr_t id = it.second;
822 
823           output << "      g1_o(" << eqr+1 << ", " << /*var+1+(lag+block_max_lag)*block_size*/ count_col << ") = ";
824           id->writeOutput(output, local_output_type, local_temporary_terms, {});
825           output << "; % variable=" << symbol_table.getName(symbol_table.getID(SymbolType::endogenous, var))
826                  << "(" << lag
827                  << ") " << var+1
828                  << ", equation=" << eq+1 << endl;
829         }
830       output << "      varargout{1}=g1_x;" << endl
831              << "      varargout{2}=g1_xd;" << endl
832              << "      varargout{3}=g1_o;" << endl;
833 
834       switch (simulation_type)
835         {
836         case EVALUATE_FORWARD:
837         case EVALUATE_BACKWARD:
838           output << "    end;" << endl
839                  << "  end;" << endl;
840           break;
841         case SOLVE_BACKWARD_SIMPLE:
842         case SOLVE_FORWARD_SIMPLE:
843         case SOLVE_BACKWARD_COMPLETE:
844         case SOLVE_FORWARD_COMPLETE:
845           output << "  else" << endl;
846           for (const auto &it : blocks_derivatives[block])
847             {
848               unsigned int eq, var;
849               expr_t id;
850               int lag;
851               tie(eq, var, lag, id) = it;
852               unsigned int eqr = getBlockEquationID(block, eq);
853               unsigned int varr = getBlockVariableID(block, var);
854               if (lag == 0)
855                 {
856                   output << "    g1(" << eq+1 << ", " << var+1-block_recursive << ") = ";
857                   id->writeOutput(output, local_output_type, local_temporary_terms, {});
858                   output << "; % variable=" << symbol_table.getName(symbol_table.getID(SymbolType::endogenous, varr))
859                          << "(" << lag
860                          << ") " << varr+1
861                          << ", equation=" << eqr+1 << endl;
862                 }
863 
864             }
865           output << "  end;" << endl;
866           break;
867         case SOLVE_TWO_BOUNDARIES_SIMPLE:
868         case SOLVE_TWO_BOUNDARIES_COMPLETE:
869           output << "    else" << endl;
870           for (const auto &it : blocks_derivatives[block])
871             {
872               unsigned int eq, var;
873               int lag;
874               expr_t id;
875               tie(eq, var, lag, id) = it;
876               unsigned int eqr = getBlockEquationID(block, eq);
877               unsigned int varr = getBlockVariableID(block, var);
878               ostringstream tmp_output;
879               if (eq >= block_recursive && var >= block_recursive)
880                 {
881                   if (lag == 0)
882                     Ufoss << "+g1(" << eq+1-block_recursive
883                           << "+Per_J_, " << var+1-block_recursive
884                           << "+Per_K_)*y(it_, " << varr+1 << ")";
885                   else if (lag == 1)
886                     Ufoss << "+g1(" << eq+1-block_recursive
887                           << "+Per_J_, " << var+1-block_recursive
888                           << "+Per_y_)*y(it_+1, " << varr+1 << ")";
889                   else if (lag > 0)
890                     Ufoss << "+g1(" << eq+1-block_recursive
891                           << "+Per_J_, " << var+1-block_recursive
892                           << "+y_size*(it_+" << lag-1 << "))*y(it_+" << lag << ", " << varr+1 << ")";
893                   else
894                     Ufoss << "+g1(" << eq+1-block_recursive
895                           << "+Per_J_, " << var+1-block_recursive
896                           << "+y_size*(it_" << lag-1 << "))*y(it_" << lag << ", " << varr+1 << ")";
897                   Uf[eqr] += Ufoss.str();
898                   Ufoss.str("");
899 
900                   if (lag == 0)
901                     tmp_output << "     g1(" << eq+1-block_recursive << "+Per_J_, "
902                                << var+1-block_recursive << "+Per_K_) = ";
903                   else if (lag == 1)
904                     tmp_output << "     g1(" << eq+1-block_recursive << "+Per_J_, "
905                                << var+1-block_recursive << "+Per_y_) = ";
906                   else if (lag > 0)
907                     tmp_output << "     g1(" << eq+1-block_recursive << "+Per_J_, "
908                                << var+1-block_recursive << "+y_size*(it_+" << lag-1 << ")) = ";
909                   else if (lag < 0)
910                     tmp_output << "     g1(" << eq+1-block_recursive << "+Per_J_, "
911                                << var+1-block_recursive << "+y_size*(it_" << lag-1 << ")) = ";
912                   output << " " << tmp_output.str();
913                   id->writeOutput(output, local_output_type, local_temporary_terms, {});
914                   output << ";";
915                   output << " %2 variable=" << symbol_table.getName(symbol_table.getID(SymbolType::endogenous, varr))
916                          << "(" << lag << ") " << varr+1
917                          << ", equation=" << eqr+1 << " (" << eq+1 << ")" << endl;
918                 }
919 
920 #ifdef CONDITION
921               output << "  if (fabs(condition[" << eqr << "])<fabs(u[" << u << "+Per_u_]))" << endl
922                      << "    condition(" << eqr << ")=u(" << u << "+Per_u_);" << endl;
923 #endif
924             }
925           for (unsigned int i = 0; i < block_size; i++)
926             {
927               if (i >= block_recursive)
928                 output << "  " << Uf[getBlockEquationID(block, i)] << ";" << endl;
929 #ifdef CONDITION
930               output << "  if (fabs(condition(" << i+1 << "))<fabs(u(" << i << "+Per_u_)))" << endl
931                      << "    condition(" << i+1 << ")=u(" << i+1 << "+Per_u_);" << endl;
932 #endif
933             }
934 #ifdef CONDITION
935           for (m = 0; m <= ModelBlock->Block_List[block].Max_Lead+ModelBlock->Block_List[block].Max_Lag; m++)
936             {
937               k = m-ModelBlock->Block_List[block].Max_Lag;
938               for (i = 0; i < ModelBlock->Block_List[block].IM_lead_lag[m].size; i++)
939                 {
940                   unsigned int eq = ModelBlock->Block_List[block].IM_lead_lag[m].Equ_Index[i];
941                   unsigned int var = ModelBlock->Block_List[block].IM_lead_lag[m].Var_Index[i];
942                   unsigned int u = ModelBlock->Block_List[block].IM_lead_lag[m].u[i];
943                   unsigned int eqr = ModelBlock->Block_List[block].IM_lead_lag[m].Equ[i];
944                   output << "  u(" << u+1 << "+Per_u_) = u(" << u+1 << "+Per_u_) / condition(" << eqr+1 << ");" << endl;
945                 }
946             }
947           for (i = 0; i < ModelBlock->Block_List[block].Size; i++)
948             output << "  u(" << i+1 << "+Per_u_) = u(" << i+1 << "+Per_u_) / condition(" << i+1 << ");" << endl;
949 #endif
950           output << "    end;" << endl
951                  << "  end;" << endl;
952           break;
953         default:
954           break;
955         }
956       output << "end" << endl;
957       output.close();
958     }
959 }
960 
961 void
writeModelEquationsCode(const string & basename,const map_idx_t & map_idx) const962 DynamicModel::writeModelEquationsCode(const string &basename, const map_idx_t &map_idx) const
963 {
964 
965   ostringstream tmp_output;
966   ofstream code_file;
967   unsigned int instruction_number = 0;
968   bool file_open = false;
969 
970   filesystem::create_directories(basename + "/model/bytecode");
971 
972   string main_name = basename + "/model/bytecode/dynamic.cod";
973   code_file.open(main_name, ios::out | ios::binary | ios::ate);
974   if (!code_file.is_open())
975     {
976       cerr << R"(Error : Can't open file ")" << main_name << R"(" for writing)" << endl;
977       exit(EXIT_FAILURE);
978     }
979 
980   int count_u;
981   int u_count_int = 0;
982   BlockSimulationType simulation_type;
983   if ((max_endo_lag > 0) && (max_endo_lead > 0))
984     simulation_type = SOLVE_TWO_BOUNDARIES_COMPLETE;
985   else if ((max_endo_lag >= 0) && (max_endo_lead == 0))
986     simulation_type = SOLVE_FORWARD_COMPLETE;
987   else
988     simulation_type = SOLVE_BACKWARD_COMPLETE;
989 
990   Write_Inf_To_Bin_File(basename + "/model/bytecode/dynamic.bin", u_count_int, file_open, simulation_type == SOLVE_TWO_BOUNDARIES_COMPLETE, symbol_table.endo_nbr());
991   file_open = true;
992 
993   //Temporary variables declaration
994   FDIMT_ fdimt(temporary_terms.size());
995   fdimt.write(code_file, instruction_number);
996 
997   vector<unsigned int> exo, exo_det, other_endo;
998 
999   for (int i = 0; i < symbol_table.exo_det_nbr(); i++)
1000     exo_det.push_back(i);
1001   for (int i = 0; i < symbol_table.exo_nbr(); i++)
1002     exo.push_back(i);
1003 
1004   map<tuple<int, int, int>, expr_t> first_derivatives_reordered_endo;
1005   map<tuple<int, SymbolType, int, int>, expr_t> first_derivatives_reordered_exo;
1006   for (const auto & [indices, d1] : derivatives[1])
1007     {
1008       int deriv_id = indices[1];
1009       unsigned int eq = indices[0];
1010       int symb = getSymbIDByDerivID(deriv_id);
1011       unsigned int var = symbol_table.getTypeSpecificID(symb);
1012       int lag = getLagByDerivID(deriv_id);
1013       if (getTypeByDerivID(deriv_id) == SymbolType::endogenous)
1014         first_derivatives_reordered_endo[{ lag, var, eq }] = d1;
1015       else if (getTypeByDerivID(deriv_id) == SymbolType::exogenous || getTypeByDerivID(deriv_id) == SymbolType::exogenousDet)
1016         first_derivatives_reordered_exo[{ lag, getTypeByDerivID(deriv_id), var, eq }] = d1;
1017     }
1018   int prev_var = -1;
1019   int prev_lag = -999999999;
1020   int count_col_endo = 0;
1021   for (const auto &it : first_derivatives_reordered_endo)
1022     {
1023       int var, lag;
1024       tie(lag, var, ignore) = it.first;
1025       if (prev_var != var || prev_lag != lag)
1026         {
1027           prev_var = var;
1028           prev_lag = lag;
1029           count_col_endo++;
1030         }
1031     }
1032   prev_var = -1;
1033   prev_lag = -999999999;
1034   SymbolType prev_type{SymbolType::unusedEndogenous}; // Any non-exogenous type would do here
1035   int count_col_exo = 0;
1036   int count_col_det_exo = 0;
1037 
1038   for (const auto &it : first_derivatives_reordered_exo)
1039     {
1040       int var, lag;
1041       SymbolType type;
1042       tie(lag, type, var, ignore) = it.first;
1043       if (prev_var != var || prev_lag != lag || prev_type != type)
1044         {
1045           prev_var = var;
1046           prev_lag = lag;
1047           prev_type = type;
1048           if (type == SymbolType::exogenous)
1049             count_col_exo++;
1050           else if (type == SymbolType::exogenousDet)
1051             count_col_det_exo++;
1052         }
1053     }
1054 
1055   FBEGINBLOCK_ fbeginblock(symbol_table.endo_nbr(),
1056                            simulation_type,
1057                            0,
1058                            symbol_table.endo_nbr(),
1059                            variable_reordered,
1060                            equation_reordered,
1061                            false,
1062                            symbol_table.endo_nbr(),
1063                            max_endo_lag,
1064                            max_endo_lead,
1065                            u_count_int,
1066                            count_col_endo,
1067                            symbol_table.exo_det_nbr(),
1068                            count_col_det_exo,
1069                            symbol_table.exo_nbr(),
1070                            count_col_exo,
1071                            0,
1072                            0,
1073                            exo_det,
1074                            exo,
1075                            other_endo
1076                            );
1077   fbeginblock.write(code_file, instruction_number);
1078 
1079   compileTemporaryTerms(code_file, instruction_number, temporary_terms, map_idx, true, false);
1080 
1081   compileModelEquations(code_file, instruction_number, temporary_terms, map_idx, true, false);
1082 
1083   FENDEQU_ fendequ;
1084   fendequ.write(code_file, instruction_number);
1085 
1086   // Get the current code_file position and jump if eval = true
1087   streampos pos1 = code_file.tellp();
1088   FJMPIFEVAL_ fjmp_if_eval(0);
1089   fjmp_if_eval.write(code_file, instruction_number);
1090   int prev_instruction_number = instruction_number;
1091 
1092   vector<vector<tuple<int, int, int>>> my_derivatives(symbol_table.endo_nbr());;
1093   count_u = symbol_table.endo_nbr();
1094   for (const auto & [indices, d1] : derivatives[1])
1095     {
1096       int deriv_id = indices[1];
1097       if (getTypeByDerivID(deriv_id) == SymbolType::endogenous)
1098         {
1099           unsigned int eq = indices[0];
1100           int symb = getSymbIDByDerivID(deriv_id);
1101           unsigned int var = symbol_table.getTypeSpecificID(symb);
1102           int lag = getLagByDerivID(deriv_id);
1103           FNUMEXPR_ fnumexpr(FirstEndoDerivative, eq, var, lag);
1104           fnumexpr.write(code_file, instruction_number);
1105           if (!my_derivatives[eq].size())
1106             my_derivatives[eq].clear();
1107           my_derivatives[eq].emplace_back(var, lag, count_u);
1108           d1->compile(code_file, instruction_number, false, temporary_terms, map_idx, true, false);
1109 
1110           FSTPU_ fstpu(count_u);
1111           fstpu.write(code_file, instruction_number);
1112           count_u++;
1113         }
1114     }
1115   for (int i = 0; i < symbol_table.endo_nbr(); i++)
1116     {
1117       FLDR_ fldr(i);
1118       fldr.write(code_file, instruction_number);
1119       if (my_derivatives[i].size())
1120         {
1121           for (auto it = my_derivatives[i].begin(); it != my_derivatives[i].end(); ++it)
1122             {
1123               FLDU_ fldu(get<2>(*it));
1124               fldu.write(code_file, instruction_number);
1125               FLDV_ fldv{static_cast<int>(SymbolType::endogenous), static_cast<unsigned int>(get<0>(*it)), get<1>(*it)};
1126               fldv.write(code_file, instruction_number);
1127               FBINARY_ fbinary{static_cast<int>(BinaryOpcode::times)};
1128               fbinary.write(code_file, instruction_number);
1129               if (it != my_derivatives[i].begin())
1130                 {
1131                   FBINARY_ fbinary{static_cast<int>(BinaryOpcode::plus)};
1132                   fbinary.write(code_file, instruction_number);
1133                 }
1134             }
1135           FBINARY_ fbinary{static_cast<int>(BinaryOpcode::minus)};
1136           fbinary.write(code_file, instruction_number);
1137         }
1138       FSTPU_ fstpu(i);
1139       fstpu.write(code_file, instruction_number);
1140     }
1141 
1142   // Get the current code_file position and jump = true
1143   streampos pos2 = code_file.tellp();
1144   FJMP_ fjmp(0);
1145   fjmp.write(code_file, instruction_number);
1146   // Set code_file position to previous JMPIFEVAL_ and set the number of instructions to jump
1147   streampos pos3 = code_file.tellp();
1148   code_file.seekp(pos1);
1149   FJMPIFEVAL_ fjmp_if_eval1(instruction_number - prev_instruction_number);
1150   fjmp_if_eval1.write(code_file, instruction_number);
1151   code_file.seekp(pos3);
1152   prev_instruction_number = instruction_number;
1153 
1154   // The Jacobian
1155   prev_var = -1;
1156   prev_lag = -999999999;
1157   count_col_endo = 0;
1158   for (const auto &it : first_derivatives_reordered_endo)
1159     {
1160       unsigned int eq;
1161       int var, lag;
1162       tie(lag, var, eq) = it.first;
1163       expr_t d1 = it.second;
1164       FNUMEXPR_ fnumexpr(FirstEndoDerivative, eq, var, lag);
1165       fnumexpr.write(code_file, instruction_number);
1166       if (prev_var != var || prev_lag != lag)
1167         {
1168           prev_var = var;
1169           prev_lag = lag;
1170           count_col_endo++;
1171         }
1172       d1->compile(code_file, instruction_number, false, temporary_terms, map_idx, true, false);
1173       FSTPG3_ fstpg3(eq, var, lag, count_col_endo-1);
1174       fstpg3.write(code_file, instruction_number);
1175     }
1176   prev_var = -1;
1177   prev_lag = -999999999;
1178   count_col_exo = 0;
1179   for (const auto &it : first_derivatives_reordered_exo)
1180     {
1181       int lag, var, eq;
1182       tie(lag, ignore, var, eq) = it.first;
1183       expr_t d1 = it.second;
1184       FNUMEXPR_ fnumexpr(FirstExoDerivative, eq, var, lag);
1185       fnumexpr.write(code_file, instruction_number);
1186       if (prev_var != var || prev_lag != lag)
1187         {
1188           prev_var = var;
1189           prev_lag = lag;
1190           count_col_exo++;
1191         }
1192       d1->compile(code_file, instruction_number, false, temporary_terms, map_idx, true, false);
1193       FSTPG3_ fstpg3(eq, var, lag, count_col_exo-1);
1194       fstpg3.write(code_file, instruction_number);
1195     }
1196   // Set codefile position to previous JMP_ and set the number of instructions to jump
1197   pos1 = code_file.tellp();
1198   code_file.seekp(pos2);
1199   FJMP_ fjmp1(instruction_number - prev_instruction_number);
1200   fjmp1.write(code_file, instruction_number);
1201   code_file.seekp(pos1);
1202 
1203   FENDBLOCK_ fendblock;
1204   fendblock.write(code_file, instruction_number);
1205   FEND_ fend;
1206   fend.write(code_file, instruction_number);
1207   code_file.close();
1208 }
1209 
1210 void
writeModelEquationsCode_Block(const string & basename,const map_idx_t & map_idx,bool linear_decomposition) const1211 DynamicModel::writeModelEquationsCode_Block(const string &basename, const map_idx_t &map_idx, bool linear_decomposition) const
1212 {
1213   struct Uff_l
1214   {
1215     int u, var, lag;
1216     Uff_l *pNext;
1217   };
1218 
1219   struct Uff
1220   {
1221     Uff_l *Ufl, *Ufl_First;
1222   };
1223 
1224   int i, v;
1225   string tmp_s;
1226   ostringstream tmp_output;
1227   ofstream code_file;
1228   unsigned int instruction_number = 0;
1229   expr_t lhs = nullptr, rhs = nullptr;
1230   BinaryOpNode *eq_node;
1231   Uff Uf[symbol_table.endo_nbr()];
1232   map<expr_t, int> reference_count;
1233   deriv_node_temp_terms_t tef_terms;
1234   vector<int> feedback_variables;
1235   bool file_open = false;
1236   string main_name;
1237   filesystem::create_directories(basename + "/model/bytecode");
1238   if (linear_decomposition)
1239     main_name = basename + "/model/bytecode/non_linear.cod";
1240   else
1241     main_name = basename + "/model/bytecode/dynamic.cod";
1242   code_file.open(main_name, ios::out | ios::binary | ios::ate);
1243   if (!code_file.is_open())
1244     {
1245       cerr << R"(Error : Can't open file ")" << main_name << R"(" for writing)" << endl;
1246       exit(EXIT_FAILURE);
1247     }
1248   //Temporary variables declaration
1249 
1250   FDIMT_ fdimt(temporary_terms.size());
1251   fdimt.write(code_file, instruction_number);
1252 
1253   for (unsigned int block = 0; block < getNbBlocks(); block++)
1254     {
1255       feedback_variables.clear();
1256       if (block > 0)
1257         {
1258           FENDBLOCK_ fendblock;
1259           fendblock.write(code_file, instruction_number);
1260         }
1261       int count_u;
1262       int u_count_int = 0;
1263       BlockSimulationType simulation_type = getBlockSimulationType(block);
1264       unsigned int block_size = getBlockSize(block);
1265       unsigned int block_mfs = getBlockMfs(block);
1266       unsigned int block_recursive = block_size - block_mfs;
1267       int block_max_lag = max_leadlag_block[block].first;
1268       int block_max_lead = max_leadlag_block[block].second;
1269 
1270       if (simulation_type == SOLVE_TWO_BOUNDARIES_SIMPLE || simulation_type == SOLVE_TWO_BOUNDARIES_COMPLETE
1271           || simulation_type == SOLVE_BACKWARD_COMPLETE || simulation_type == SOLVE_FORWARD_COMPLETE)
1272         {
1273           Write_Inf_To_Bin_File_Block(basename, block, u_count_int, file_open,
1274                                       simulation_type == SOLVE_TWO_BOUNDARIES_COMPLETE || simulation_type == SOLVE_TWO_BOUNDARIES_SIMPLE, linear_decomposition);
1275           file_open = true;
1276         }
1277       map<tuple<int, int, int>, expr_t> tmp_block_endo_derivative;
1278       for (const auto &it : blocks_derivatives[block])
1279         tmp_block_endo_derivative[{ get<2>(it), get<1>(it), get<0>(it) }] = get<3>(it);
1280       map<tuple<int, int, int>, expr_t> tmp_exo_derivative;
1281       for (const auto &it : derivative_exo[block])
1282         tmp_exo_derivative[{ get<0>(it.first), get<2>(it.first), get<1>(it.first) }] = it.second;
1283       map<tuple<int, int, int>, expr_t> tmp_exo_det_derivative;
1284       for (const auto &it : derivative_exo_det[block])
1285         tmp_exo_det_derivative[{ get<0>(it.first), get<2>(it.first), get<1>(it.first) }] = it.second;
1286       map<tuple<int, int, int>, expr_t> tmp_other_endo_derivative;
1287       for (const auto &it : derivative_other_endo[block])
1288         tmp_other_endo_derivative[{ get<0>(it.first), get<2>(it.first), get<1>(it.first) }] = it.second;
1289       int prev_var = -1;
1290       int prev_lag = -999999999;
1291       int count_col_endo = 0;
1292       for (const auto &it : tmp_block_endo_derivative)
1293         {
1294           int lag, var;
1295           tie(lag, var, ignore) = it.first;
1296           if (prev_var != var || prev_lag != lag)
1297             {
1298               prev_var = var;
1299               prev_lag = lag;
1300               count_col_endo++;
1301             }
1302         }
1303       unsigned int count_col_det_exo = 0;
1304       vector<unsigned int> exo_det;
1305       for (const auto &it : exo_det_block[block])
1306         for (const auto &it1 : it.second)
1307           {
1308             count_col_det_exo++;
1309             if (find(exo_det.begin(), exo_det.end(), it1) == exo_det.end())
1310               exo_det.push_back(it1);
1311           }
1312 
1313       unsigned int count_col_exo = 0;
1314       vector<unsigned int> exo;
1315       for (const auto &it : exo_block[block])
1316         for (const auto &it1 : it.second)
1317           {
1318             count_col_exo++;
1319             if (find(exo.begin(), exo.end(), it1) == exo.end())
1320               exo.push_back(it1);
1321           }
1322 
1323       vector<unsigned int> other_endo;
1324       unsigned int count_col_other_endo = 0;
1325       for (const auto &it : other_endo_block[block])
1326         for (const auto &it1 : it.second)
1327           {
1328             count_col_other_endo++;
1329             if (find(other_endo.begin(), other_endo.end(), it1) == other_endo.end())
1330               other_endo.push_back(it1);
1331           }
1332 
1333       FBEGINBLOCK_ fbeginblock(block_mfs,
1334                                simulation_type,
1335                                getBlockFirstEquation(block),
1336                                block_size,
1337                                variable_reordered,
1338                                equation_reordered,
1339                                blocks_linear[block],
1340                                symbol_table.endo_nbr(),
1341                                block_max_lag,
1342                                block_max_lead,
1343                                u_count_int,
1344                                count_col_endo,
1345                                exo_det.size(),
1346                                count_col_det_exo,
1347                                exo.size(),
1348                                getBlockExoColSize(block),
1349                                other_endo.size(),
1350                                count_col_other_endo,
1351                                exo_det,
1352                                exo,
1353                                other_endo
1354                                );
1355       fbeginblock.write(code_file, instruction_number);
1356 
1357       if (linear_decomposition)
1358         compileTemporaryTerms(code_file, instruction_number, temporary_terms, map_idx, true, false);
1359 
1360       // The equations
1361       for (i = 0; i < static_cast<int>(block_size); i++)
1362         {
1363           //The Temporary terms
1364           temporary_terms_t tt2;
1365           if (v_temporary_terms[block][i].size() && !linear_decomposition)
1366             {
1367               for (auto it : v_temporary_terms[block][i])
1368                 {
1369                   if (dynamic_cast<AbstractExternalFunctionNode *>(it) != nullptr)
1370                     it->compileExternalFunctionOutput(code_file, instruction_number, false, tt2, map_idx, true, false, tef_terms);
1371 
1372                   FNUMEXPR_ fnumexpr(TemporaryTerm, static_cast<int>(map_idx.find(it->idx)->second));
1373                   fnumexpr.write(code_file, instruction_number);
1374                   it->compile(code_file, instruction_number, false, tt2, map_idx, true, false, tef_terms);
1375                   FSTPT_ fstpt(static_cast<int>(map_idx.find(it->idx)->second));
1376                   fstpt.write(code_file, instruction_number);
1377                   // Insert current node into tt2
1378                   tt2.insert(it);
1379 #ifdef DEBUGC
1380                   cout << "FSTPT " << v << endl;
1381                   instruction_number++;
1382                   code_file.write(&FOK, sizeof(FOK));
1383                   code_file.write(reinterpret_cast<char *>(&k), sizeof(k));
1384                   ki++;
1385 #endif
1386 
1387                 }
1388             }
1389 #ifdef DEBUGC
1390           for (const auto &it : v_temporary_terms[block][i])
1391             {
1392               auto ii = map_idx.find(it->idx);
1393               cout << "map_idx[" << it->idx <<"]=" << ii->second << endl;
1394             }
1395 #endif
1396 
1397           int variable_ID, equation_ID;
1398           EquationType equ_type;
1399 
1400           switch (simulation_type)
1401             {
1402             evaluation:
1403             case EVALUATE_BACKWARD:
1404             case EVALUATE_FORWARD:
1405               equ_type = getBlockEquationType(block, i);
1406               {
1407                 FNUMEXPR_ fnumexpr(ModelEquation, getBlockEquationID(block, i));
1408                 fnumexpr.write(code_file, instruction_number);
1409               }
1410               if (equ_type == E_EVALUATE)
1411                 {
1412                   eq_node = static_cast<BinaryOpNode *>(getBlockEquationExpr(block, i));
1413                   lhs = eq_node->arg1;
1414                   rhs = eq_node->arg2;
1415                   rhs->compile(code_file, instruction_number, false, temporary_terms, map_idx, true, false);
1416                   lhs->compile(code_file, instruction_number, true, temporary_terms, map_idx, true, false);
1417                 }
1418               else if (equ_type == E_EVALUATE_S)
1419                 {
1420                   eq_node = static_cast<BinaryOpNode *>(getBlockEquationRenormalizedExpr(block, i));
1421                   lhs = eq_node->arg1;
1422                   rhs = eq_node->arg2;
1423                   rhs->compile(code_file, instruction_number, false, temporary_terms, map_idx, true, false);
1424                   lhs->compile(code_file, instruction_number, true, temporary_terms, map_idx, true, false);
1425                 }
1426               break;
1427             case SOLVE_BACKWARD_COMPLETE:
1428             case SOLVE_FORWARD_COMPLETE:
1429             case SOLVE_TWO_BOUNDARIES_COMPLETE:
1430             case SOLVE_TWO_BOUNDARIES_SIMPLE:
1431               if (i < static_cast<int>(block_recursive))
1432                 goto evaluation;
1433               variable_ID = getBlockVariableID(block, i);
1434               equation_ID = getBlockEquationID(block, i);
1435               feedback_variables.push_back(variable_ID);
1436               Uf[equation_ID].Ufl = nullptr;
1437               goto end;
1438             default:
1439             end:
1440               FNUMEXPR_ fnumexpr(ModelEquation, getBlockEquationID(block, i));
1441               fnumexpr.write(code_file, instruction_number);
1442               eq_node = static_cast<BinaryOpNode *>(getBlockEquationExpr(block, i));
1443               lhs = eq_node->arg1;
1444               rhs = eq_node->arg2;
1445               lhs->compile(code_file, instruction_number, false, temporary_terms, map_idx, true, false);
1446               rhs->compile(code_file, instruction_number, false, temporary_terms, map_idx, true, false);
1447 
1448               FBINARY_ fbinary{static_cast<int>(BinaryOpcode::minus)};
1449               fbinary.write(code_file, instruction_number);
1450               FSTPR_ fstpr(i - block_recursive);
1451               fstpr.write(code_file, instruction_number);
1452             }
1453         }
1454       FENDEQU_ fendequ;
1455       fendequ.write(code_file, instruction_number);
1456 
1457       // Get the current code_file position and jump if eval = true
1458       streampos pos1 = code_file.tellp();
1459       FJMPIFEVAL_ fjmp_if_eval(0);
1460       fjmp_if_eval.write(code_file, instruction_number);
1461       int prev_instruction_number = instruction_number;
1462       // The Jacobian if we have to solve the block determinsitic block
1463       if (simulation_type != EVALUATE_BACKWARD
1464           && simulation_type != EVALUATE_FORWARD)
1465         {
1466           switch (simulation_type)
1467             {
1468             case SOLVE_BACKWARD_SIMPLE:
1469             case SOLVE_FORWARD_SIMPLE:
1470               {
1471                 FNUMEXPR_ fnumexpr(FirstEndoDerivative, getBlockEquationID(block, 0), getBlockVariableID(block, 0), 0);
1472                 fnumexpr.write(code_file, instruction_number);
1473               }
1474               compileDerivative(code_file, instruction_number, getBlockEquationID(block, 0), getBlockVariableID(block, 0), 0, map_idx);
1475               {
1476                 FSTPG_ fstpg(0);
1477                 fstpg.write(code_file, instruction_number);
1478               }
1479               break;
1480 
1481             case SOLVE_BACKWARD_COMPLETE:
1482             case SOLVE_FORWARD_COMPLETE:
1483             case SOLVE_TWO_BOUNDARIES_COMPLETE:
1484             case SOLVE_TWO_BOUNDARIES_SIMPLE:
1485               count_u = feedback_variables.size();
1486               for (const auto &it : blocks_derivatives[block])
1487                 {
1488                   unsigned int eq, var;
1489                   int lag;
1490                   tie(eq, var, lag, ignore) = it;
1491                   unsigned int eqr = getBlockEquationID(block, eq);
1492                   unsigned int varr = getBlockVariableID(block, var);
1493                   if (eq >= block_recursive and var >= block_recursive)
1494                     {
1495                       if (lag != 0 && (simulation_type == SOLVE_FORWARD_COMPLETE || simulation_type == SOLVE_BACKWARD_COMPLETE))
1496                         continue;
1497                       if (!Uf[eqr].Ufl)
1498                         {
1499                           Uf[eqr].Ufl = static_cast<Uff_l *>(malloc(sizeof(Uff_l)));
1500                           Uf[eqr].Ufl_First = Uf[eqr].Ufl;
1501                         }
1502                       else
1503                         {
1504                           Uf[eqr].Ufl->pNext = static_cast<Uff_l *>(malloc(sizeof(Uff_l)));
1505                           Uf[eqr].Ufl = Uf[eqr].Ufl->pNext;
1506                         }
1507                       Uf[eqr].Ufl->pNext = nullptr;
1508                       Uf[eqr].Ufl->u = count_u;
1509                       Uf[eqr].Ufl->var = varr;
1510                       Uf[eqr].Ufl->lag = lag;
1511                       FNUMEXPR_ fnumexpr(FirstEndoDerivative, eqr, varr, lag);
1512                       fnumexpr.write(code_file, instruction_number);
1513                       compileChainRuleDerivative(code_file, instruction_number, eqr, varr, lag, map_idx);
1514                       FSTPU_ fstpu(count_u);
1515                       fstpu.write(code_file, instruction_number);
1516                       count_u++;
1517                     }
1518                 }
1519               for (i = 0; i < static_cast<int>(block_size); i++)
1520                 {
1521                   if (i >= static_cast<int>(block_recursive))
1522                     {
1523                       FLDR_ fldr(i-block_recursive);
1524                       fldr.write(code_file, instruction_number);
1525 
1526                       FLDZ_ fldz;
1527                       fldz.write(code_file, instruction_number);
1528 
1529                       v = getBlockEquationID(block, i);
1530                       for (Uf[v].Ufl = Uf[v].Ufl_First; Uf[v].Ufl; Uf[v].Ufl = Uf[v].Ufl->pNext)
1531                         {
1532                           FLDU_ fldu(Uf[v].Ufl->u);
1533                           fldu.write(code_file, instruction_number);
1534                           FLDV_ fldv{static_cast<int>(SymbolType::endogenous), static_cast<unsigned int>(Uf[v].Ufl->var), Uf[v].Ufl->lag};
1535                           fldv.write(code_file, instruction_number);
1536 
1537                           FBINARY_ fbinary{static_cast<int>(BinaryOpcode::times)};
1538                           fbinary.write(code_file, instruction_number);
1539 
1540                           FCUML_ fcuml;
1541                           fcuml.write(code_file, instruction_number);
1542                         }
1543                       Uf[v].Ufl = Uf[v].Ufl_First;
1544                       while (Uf[v].Ufl)
1545                         {
1546                           Uf[v].Ufl_First = Uf[v].Ufl->pNext;
1547                           free(Uf[v].Ufl);
1548                           Uf[v].Ufl = Uf[v].Ufl_First;
1549                         }
1550                       FBINARY_ fbinary{static_cast<int>(BinaryOpcode::minus)};
1551                       fbinary.write(code_file, instruction_number);
1552 
1553                       FSTPU_ fstpu(i - block_recursive);
1554                       fstpu.write(code_file, instruction_number);
1555                     }
1556                 }
1557               break;
1558             default:
1559               break;
1560             }
1561         }
1562       // Get the current code_file position and jump = true
1563       streampos pos2 = code_file.tellp();
1564       FJMP_ fjmp(0);
1565       fjmp.write(code_file, instruction_number);
1566       // Set code_file position to previous JMPIFEVAL_ and set the number of instructions to jump
1567       streampos pos3 = code_file.tellp();
1568       code_file.seekp(pos1);
1569       FJMPIFEVAL_ fjmp_if_eval1(instruction_number - prev_instruction_number);
1570       fjmp_if_eval1.write(code_file, instruction_number);
1571       code_file.seekp(pos3);
1572       prev_instruction_number = instruction_number;
1573       // The Jacobian if we have to solve the block determinsitic block
1574 
1575       prev_var = -1;
1576       prev_lag = -999999999;
1577       count_col_endo = 0;
1578       for (const auto &it : tmp_block_endo_derivative)
1579         {
1580           int lag, var;
1581           unsigned int eq;
1582           tie(lag, var, eq) = it.first;
1583           unsigned int eqr = getBlockEquationID(block, eq);
1584           unsigned int varr = getBlockVariableID(block, var);
1585           if (prev_var != var || prev_lag != lag)
1586             {
1587               prev_var = var;
1588               prev_lag = lag;
1589               count_col_endo++;
1590             }
1591           FNUMEXPR_ fnumexpr(FirstEndoDerivative, eqr, varr, lag);
1592           fnumexpr.write(code_file, instruction_number);
1593           compileDerivative(code_file, instruction_number, eqr, varr, lag, map_idx);
1594           FSTPG3_ fstpg3(eq, var, lag, count_col_endo-1);
1595           fstpg3.write(code_file, instruction_number);
1596         }
1597       prev_var = -1;
1598       prev_lag = -999999999;
1599       count_col_exo = 0;
1600       for (const auto &it : tmp_exo_derivative)
1601         {
1602           int lag, eq, var;
1603           tie(lag, var, eq) = it.first;
1604           int eqr = getBlockInitialEquationID(block, eq);
1605           int varr = getBlockInitialExogenousID(block, var);
1606           if (prev_var != var || prev_lag != lag)
1607             {
1608               prev_var = var;
1609               prev_lag = lag;
1610               count_col_exo++;
1611             }
1612           expr_t id = it.second;
1613 
1614           FNUMEXPR_ fnumexpr(FirstExoDerivative, eqr, varr, lag);
1615           fnumexpr.write(code_file, instruction_number);
1616           id->compile(code_file, instruction_number, false, temporary_terms, map_idx, true, false);
1617           FSTPG3_ fstpg3(eq, var, lag, count_col_exo-1);
1618           fstpg3.write(code_file, instruction_number);
1619         }
1620       prev_var = -1;
1621       prev_lag = -999999999;
1622       int count_col_exo_det = 0;
1623       for (const auto &it : tmp_exo_det_derivative)
1624         {
1625           int lag, eq, var;
1626           tie(lag, var, eq) = it.first;
1627           int eqr = getBlockInitialEquationID(block, eq);
1628           int varr = getBlockInitialDetExogenousID(block, var);
1629           if (prev_var != var || prev_lag != lag)
1630             {
1631               prev_var = var;
1632               prev_lag = lag;
1633               count_col_exo_det++;
1634             }
1635           expr_t id = it.second;
1636 
1637           FNUMEXPR_ fnumexpr(FirstExodetDerivative, eqr, varr, lag);
1638           fnumexpr.write(code_file, instruction_number);
1639           id->compile(code_file, instruction_number, false, temporary_terms, map_idx, true, false);
1640           FSTPG3_ fstpg3(eq, var, lag, count_col_exo_det-1);
1641           fstpg3.write(code_file, instruction_number);
1642         }
1643       prev_var = -1;
1644       prev_lag = -999999999;
1645       count_col_other_endo = 0;
1646       for (const auto &it : tmp_other_endo_derivative)
1647         {
1648           int lag, eq, var;
1649           tie(lag, var, eq) = it.first;
1650           int eqr = getBlockInitialEquationID(block, eq);
1651           int varr = getBlockInitialOtherEndogenousID(block, var);;
1652           if (prev_var != var || prev_lag != lag)
1653             {
1654               prev_var = var;
1655               prev_lag = lag;
1656               count_col_other_endo++;
1657             }
1658           expr_t id = it.second;
1659 
1660           FNUMEXPR_ fnumexpr(FirstOtherEndoDerivative, eqr, varr, lag);
1661           fnumexpr.write(code_file, instruction_number);
1662           id->compile(code_file, instruction_number, false, temporary_terms, map_idx, true, false);
1663           FSTPG3_ fstpg3(eq, var, lag, count_col_other_endo-1);
1664           fstpg3.write(code_file, instruction_number);
1665         }
1666 
1667       // Set codefile position to previous JMP_ and set the number of instructions to jump
1668       pos1 = code_file.tellp();
1669       code_file.seekp(pos2);
1670       FJMP_ fjmp1(instruction_number - prev_instruction_number);
1671       fjmp1.write(code_file, instruction_number);
1672       code_file.seekp(pos1);
1673     }
1674   FENDBLOCK_ fendblock;
1675   fendblock.write(code_file, instruction_number);
1676   FEND_ fend;
1677   fend.write(code_file, instruction_number);
1678   code_file.close();
1679 }
1680 
1681 void
writeDynamicMFile(const string & basename) const1682 DynamicModel::writeDynamicMFile(const string &basename) const
1683 {
1684   writeDynamicModel(basename, false, false);
1685 }
1686 
1687 void
writeDynamicJuliaFile(const string & basename) const1688 DynamicModel::writeDynamicJuliaFile(const string &basename) const
1689 {
1690   writeDynamicModel(basename, false, true);
1691 }
1692 
1693 void
writeDynamicCFile(const string & basename) const1694 DynamicModel::writeDynamicCFile(const string &basename) const
1695 {
1696   filesystem::create_directories(basename + "/model/src");
1697   string filename = basename + "/model/src/dynamic.c";
1698   string filename_mex = basename + "/model/src/dynamic_mex.c";
1699   ofstream mDynamicModelFile, mDynamicMexFile;
1700 
1701   int ntt = temporary_terms_mlv.size() + temporary_terms_derivatives[0].size() + temporary_terms_derivatives[1].size() + temporary_terms_derivatives[2].size() + temporary_terms_derivatives[3].size();
1702 
1703   mDynamicModelFile.open(filename, ios::out | ios::binary);
1704   if (!mDynamicModelFile.is_open())
1705     {
1706       cerr << "Error: Can't open file " << filename << " for writing" << endl;
1707       exit(EXIT_FAILURE);
1708     }
1709   mDynamicModelFile << "/*" << endl
1710                     << " * " << filename << " : Computes dynamic model for Dynare" << endl
1711                     << " *" << endl
1712                     << " * Warning : this file is generated automatically by Dynare" << endl
1713                     << " *           from model file (.mod)" << endl
1714                     << " */" << endl
1715                     << "#include <math.h>" << endl;
1716 
1717   mDynamicModelFile << "#include <stdlib.h>" << endl;
1718 
1719   if (external_functions_table.get_total_number_of_unique_model_block_external_functions())
1720     // External Matlab function, implies Dynamic function will call mex
1721     mDynamicModelFile
1722 #ifndef __APPLE__
1723       << "#include <uchar.h>" << endl // For MATLAB ≤ R2011a
1724 #else
1725       << "typedef uint_least16_t char16_t;" << endl
1726       << "typedef uint_least32_t char32_t;" << endl // uchar.h does not exist on macOS
1727 #endif
1728       << R"(#include "mex.h")" << endl;
1729 
1730   mDynamicModelFile << "#define max(a, b) (((a) > (b)) ? (a) : (b))" << endl
1731                     << "#define min(a, b) (((a) > (b)) ? (b) : (a))" << endl;
1732 
1733   // Write function definition if BinaryOpcode::powerDeriv is used
1734   writePowerDerivCHeader(mDynamicModelFile);
1735 
1736   mDynamicModelFile << endl;
1737 
1738   // Writing the function body
1739   writeDynamicModel(mDynamicModelFile, true, false);
1740 
1741   mDynamicModelFile << endl;
1742 
1743   writePowerDeriv(mDynamicModelFile);
1744   mDynamicModelFile.close();
1745 
1746   mDynamicMexFile.open(filename_mex, ios::out | ios::binary);
1747   if (!mDynamicMexFile.is_open())
1748     {
1749       cerr << "Error: Can't open file " << filename_mex << " for writing" << endl;
1750       exit(EXIT_FAILURE);
1751     }
1752 
1753   // Writing the gateway routine
1754   mDynamicMexFile << "/*" << endl
1755                   << " * " << filename_mex << " : The gateway routine used to call the Dynamic function "
1756                   << "located in " << filename << endl
1757                   << " *" << endl
1758                   << " * Warning : this file is generated automatically by Dynare" << endl
1759                   << " *           from model file (.mod)" << endl
1760                   << endl
1761                   << " */" << endl
1762                   << endl
1763                   << "#include <stdlib.h>" << endl
1764 #ifndef __APPLE__
1765                   << "#include <uchar.h>" << endl // For MATLAB ≤ R2011a
1766 #else
1767                   << "typedef uint_least16_t char16_t;" << endl
1768                   << "typedef uint_least32_t char32_t;" << endl // uchar.h does not exist on macOS
1769 #endif
1770                   << R"(#include "mex.h")" << endl
1771                   << endl
1772                   << "void dynamic_resid_tt(const double *y, const double *x, int nb_row_x, const double *params, const double *steady_state, int it_, double *T);" << endl
1773                   << "void dynamic_resid(const double *y, const double *x, int nb_row_x, const double *params, const double *steady_state, int it_, const double *T, double *residual);" << endl
1774                   << "void dynamic_g1_tt(const double *y, const double *x, int nb_row_x, const double *params, const double *steady_state, int it_, double *T);" << endl
1775                   << "void dynamic_g1(const double *y, const double *x, int nb_row_x, const double *params, const double *steady_state, int it_, const double *T, double *g1);" << endl
1776                   << "void dynamic_g2_tt(const double *y, const double *x, int nb_row_x, const double *params, const double *steady_state, int it_, double *T);" << endl
1777                   << "void dynamic_g2(const double *y, const double *x, int nb_row_x, const double *params, const double *steady_state, int it_, const double *T, double *v2);" << endl
1778                   << "void dynamic_g3_tt(const double *y, const double *x, int nb_row_x, const double *params, const double *steady_state, int it_, double *T);" << endl
1779                   << "void dynamic_g3(const double *y, const double *x, int nb_row_x, const double *params, const double *steady_state, int it_, const double *T, double *v3);" << endl
1780                   << "void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])" << endl
1781                   << "{" << endl
1782                   << "  /* Check that no derivatives of higher order than computed are being requested */" << endl
1783                   << "  if (nlhs > " << computed_derivs_order + 1 << ")" << endl
1784                   << R"(    mexErrMsgTxt("Derivatives of higher order than computed have been requested");)" << endl
1785                   << "  /* Create a pointer to the input matrix y. */" << endl
1786                   << "  double *y = mxGetPr(prhs[0]);" << endl
1787                   << endl
1788                   << "  /* Create a pointer to the input matrix x. */" << endl
1789                   << "  double *x = mxGetPr(prhs[1]);" << endl
1790                   << endl
1791                   << "  /* Create a pointer to the input matrix params. */" << endl
1792                   << "  double *params = mxGetPr(prhs[2]);" << endl
1793                   << endl
1794                   << "  /* Create a pointer to the input matrix steady_state. */" << endl
1795                   << "  double *steady_state = mxGetPr(prhs[3]);" << endl
1796                   << endl
1797                   << "  /* Fetch time index */" << endl
1798                   << "  int it_ = (int) mxGetScalar(prhs[4]) - 1;" << endl
1799                   << endl
1800                   << "  /* Gets number of rows of matrix x. */" << endl
1801                   << "  int nb_row_x = mxGetM(prhs[1]);" << endl
1802                   << endl
1803                   << "  double *T = (double *) malloc(sizeof(double)*" << ntt << ");"
1804                   << endl
1805                   << "  if (nlhs >= 1)" << endl
1806                   << "  {" << endl
1807                   << "     /* Set the output pointer to the output matrix residual. */" << endl
1808                   << "     plhs[0] = mxCreateDoubleMatrix(" << equations.size() << ",1, mxREAL);" << endl
1809                   << "     double *residual = mxGetPr(plhs[0]);" << endl
1810                   << "     dynamic_resid_tt(y, x, nb_row_x, params, steady_state, it_, T);" << endl
1811                   << "     dynamic_resid(y, x, nb_row_x, params, steady_state, it_, T, residual);" << endl
1812                   << "  }" << endl
1813                   << endl
1814                   << "  if (nlhs >= 2)" << endl
1815                   << "  {" << endl
1816                   << "     /* Set the output pointer to the output matrix g1. */" << endl
1817                   << "     plhs[1] = mxCreateDoubleMatrix(" << equations.size() << ", " << dynJacobianColsNbr << ", mxREAL);" << endl
1818                   << "     double *g1 = mxGetPr(plhs[1]);" << endl
1819                   << "     dynamic_g1_tt(y, x, nb_row_x, params, steady_state, it_, T);" << endl
1820                   << "     dynamic_g1(y, x, nb_row_x, params, steady_state, it_, T, g1);" << endl
1821                   << "  }" << endl
1822                   << endl
1823                   << " if (nlhs >= 3)" << endl
1824                   << "  {" << endl
1825                   << "     /* Set the output pointer to the output matrix v2. */" << endl
1826                   << "     plhs[2] = mxCreateDoubleMatrix(" << NNZDerivatives[2] << ", " << 3
1827                   << ", mxREAL);" << endl
1828                   << "     double *v2 = mxGetPr(plhs[2]);" << endl
1829                   << "     dynamic_g2_tt(y, x, nb_row_x, params, steady_state, it_, T);" << endl
1830                   << "     dynamic_g2(y, x, nb_row_x, params, steady_state, it_, T, v2);" << endl
1831                   << "  }" << endl
1832                   << endl
1833                   << " if (nlhs >= 4)" << endl
1834                   << "  {" << endl
1835                   << "     /* Set the output pointer to the output matrix v3. */" << endl
1836                   << "     plhs[3] = mxCreateDoubleMatrix(" << NNZDerivatives[3] << ", " << 3 << ", mxREAL);" << endl
1837                   << "     double *v3 = mxGetPr(plhs[3]);" << endl
1838                   << "     dynamic_g3_tt(y, x, nb_row_x, params, steady_state, it_, T);" << endl
1839                   << "     dynamic_g3(y, x, nb_row_x, params, steady_state, it_, T, v3);" << endl
1840                   << "  }" << endl
1841                   << endl
1842                   << " free(T);"
1843                   << "}" << endl;
1844   mDynamicMexFile.close();
1845 }
1846 
1847 string
reform(const string & name1) const1848 DynamicModel::reform(const string &name1) const
1849 {
1850   string name = name1;
1851   int pos = name.find(R"(\)", 0);
1852   while (pos >= 0)
1853     {
1854       if (name.substr(pos + 1, 1) != R"(\)")
1855         {
1856           name = name.insert(pos, R"(\)");
1857           pos++;
1858         }
1859       pos++;
1860       pos = name.find(R"(\)", pos);
1861     }
1862   return name;
1863 }
1864 
1865 void
printNonZeroHessianEquations(ostream & output) const1866 DynamicModel::printNonZeroHessianEquations(ostream &output) const
1867 {
1868   if (nonzero_hessian_eqs.size() != 1)
1869     output << "[";
1870   for (auto it = nonzero_hessian_eqs.begin();
1871        it != nonzero_hessian_eqs.end(); ++it)
1872     {
1873       if (it != nonzero_hessian_eqs.begin())
1874         output << " ";
1875       output << *it + 1;
1876     }
1877   if (nonzero_hessian_eqs.size() != 1)
1878     output << "]";
1879 }
1880 
1881 void
Write_Inf_To_Bin_File_Block(const string & basename,int num,int & u_count_int,bool & file_open,bool is_two_boundaries,bool linear_decomposition) const1882 DynamicModel::Write_Inf_To_Bin_File_Block(const string &basename, int num,
1883                                           int &u_count_int, bool &file_open, bool is_two_boundaries, bool linear_decomposition) const
1884 {
1885   int j;
1886   std::ofstream SaveCode;
1887   string filename;
1888 
1889   if (!linear_decomposition)
1890     filename = basename + "/model/bytecode/dynamic.bin";
1891   else
1892     filename = basename + "/model/bytecode/non_linear.bin";
1893 
1894   if (file_open)
1895     SaveCode.open(filename, ios::out | ios::in | ios::binary | ios::ate);
1896   else
1897     SaveCode.open(filename, ios::out | ios::binary);
1898   if (!SaveCode.is_open())
1899     {
1900       cerr << R"(Error : Can't open file ")" << filename << R"(" for writing)" << endl;
1901       exit(EXIT_FAILURE);
1902     }
1903   u_count_int = 0;
1904   unsigned int block_size = getBlockSize(num);
1905   unsigned int block_mfs = getBlockMfs(num);
1906   unsigned int block_recursive = block_size - block_mfs;
1907   for (const auto &it : blocks_derivatives[num])
1908     {
1909       unsigned int eq, var;
1910       int lag;
1911       tie(eq, var, lag, ignore) = it;
1912       if (lag != 0 && !is_two_boundaries)
1913         continue;
1914       if (eq >= block_recursive && var >= block_recursive)
1915         {
1916           int v = eq - block_recursive;
1917           SaveCode.write(reinterpret_cast<char *>(&v), sizeof(v));
1918           int varr = var - block_recursive + lag * block_mfs;
1919           SaveCode.write(reinterpret_cast<char *>(&varr), sizeof(varr));
1920           SaveCode.write(reinterpret_cast<char *>(&lag), sizeof(lag));
1921           int u = u_count_int + block_mfs;
1922           SaveCode.write(reinterpret_cast<char *>(&u), sizeof(u));
1923           u_count_int++;
1924         }
1925     }
1926 
1927   if (is_two_boundaries)
1928     u_count_int += block_mfs;
1929   for (j = block_recursive; j < static_cast<int>(block_size); j++)
1930     {
1931       unsigned int varr = getBlockVariableID(num, j);
1932       SaveCode.write(reinterpret_cast<char *>(&varr), sizeof(varr));
1933     }
1934   for (j = block_recursive; j < static_cast<int>(block_size); j++)
1935     {
1936       unsigned int eqr = getBlockEquationID(num, j);
1937       SaveCode.write(reinterpret_cast<char *>(&eqr), sizeof(eqr));
1938     }
1939   SaveCode.close();
1940 }
1941 
1942 void
writeSparseDynamicMFile(const string & basename) const1943 DynamicModel::writeSparseDynamicMFile(const string &basename) const
1944 {
1945   string sp;
1946   ofstream mDynamicModelFile;
1947   ostringstream tmp, tmp1, tmp_eq;
1948   string filename = packageDir(basename) + "/dynamic.m";
1949   mDynamicModelFile.open(filename, ios::out | ios::binary);
1950   if (!mDynamicModelFile.is_open())
1951     {
1952       cerr << "Error: Can't open file " << filename << " for writing" << endl;
1953       exit(EXIT_FAILURE);
1954     }
1955   mDynamicModelFile << "%" << endl
1956                     << "% " << filename << " : Computes dynamic model for Dynare" << endl
1957                     << "%" << endl
1958                     << "% Warning : this file is generated automatically by Dynare" << endl
1959                     << "%           from model file (.mod)" << endl << endl
1960                     << "%/" << endl;
1961 
1962   int Nb_SGE = 0;
1963   bool open_par = false;
1964 
1965   mDynamicModelFile << "function [varargout] = dynamic(options_, M_, oo_, varargin)" << endl
1966                     << "  g2=[];g3=[];" << endl;
1967   //Temporary variables declaration
1968   bool OK = true;
1969   ostringstream tmp_output;
1970   for (auto temporary_term : temporary_terms)
1971     {
1972       if (OK)
1973         OK = false;
1974       else
1975         tmp_output << " ";
1976       // In the following, "Static" is used to avoid getting the "(it_)" subscripting
1977       temporary_term->writeOutput(tmp_output, ExprNodeOutputType::matlabStaticModelSparse, temporary_terms, {});
1978     }
1979   if (tmp_output.str().length() > 0)
1980     mDynamicModelFile << "  global " << tmp_output.str() << ";" << endl;
1981 
1982   mDynamicModelFile << "  T_init=zeros(1,options_.periods+M_.maximum_lag+M_.maximum_lead);" << endl;
1983   tmp_output.str("");
1984   for (auto temporary_term : temporary_terms)
1985     {
1986       tmp_output << "  ";
1987       // In the following, "Static" is used to avoid getting the "(it_)" subscripting
1988       temporary_term->writeOutput(tmp_output, ExprNodeOutputType::matlabStaticModelSparse, temporary_terms, {});
1989       tmp_output << "=T_init;" << endl;
1990     }
1991   if (tmp_output.str().length() > 0)
1992     mDynamicModelFile << tmp_output.str();
1993 
1994   mDynamicModelFile << "  y_kmin=M_.maximum_lag;" << endl
1995                     << "  y_kmax=M_.maximum_lead;" << endl
1996                     << "  y_size=M_.endo_nbr;" << endl
1997                     << "  if(length(varargin)>0)" << endl
1998                     << "    %it is a simple evaluation of the dynamic model for time _it" << endl
1999                     << "    y=varargin{1};" << endl
2000                     << "    x=varargin{2};" << endl
2001                     << "    params=varargin{3};" << endl
2002                     << "    steady_state=varargin{4};" << endl
2003                     << "    it_=varargin{5};" << endl
2004                     << "    dr=varargin{6};" << endl
2005                     << "    Per_u_=0;" << endl
2006                     << "    Per_y_=it_*y_size;" << endl
2007                     << "    ys=y(it_,:);" << endl;
2008   tmp.str("");
2009   tmp_eq.str("");
2010   unsigned int nb_blocks = getNbBlocks();
2011   unsigned int block = 0;
2012   for (int count_call = 1; block < nb_blocks; block++, count_call++)
2013     {
2014       unsigned int block_size = getBlockSize(block),
2015         block_mfs = getBlockMfs(block),
2016         block_recursive = block_size - block_mfs;
2017       BlockSimulationType simulation_type = getBlockSimulationType(block);
2018 
2019       if (simulation_type == EVALUATE_FORWARD || simulation_type == EVALUATE_BACKWARD)
2020         {
2021           for (unsigned int ik = 0; ik < block_size; ik++)
2022             {
2023               tmp << " " << getBlockVariableID(block, ik)+1;
2024               tmp_eq << " " << getBlockEquationID(block, ik)+1;
2025             }
2026         }
2027       else
2028         {
2029           for (unsigned int ik = block_recursive; ik < block_size; ik++)
2030             {
2031               tmp << " " << getBlockVariableID(block, ik)+1;
2032               tmp_eq << " " << getBlockEquationID(block, ik)+1;
2033             }
2034         }
2035       mDynamicModelFile << "    y_index_eq=[" << tmp_eq.str() << "];" << endl
2036                         << "    y_index=[" << tmp.str() << "];" << endl;
2037 
2038       switch (simulation_type)
2039         {
2040         case EVALUATE_FORWARD:
2041         case EVALUATE_BACKWARD:
2042           mDynamicModelFile << "    [y, dr(" << count_call << ").g1, dr(" << count_call << ").g2, dr(" << count_call << ").g3, dr(" << count_call << ").g1_x, dr(" << count_call << ").g1_xd, dr(" << count_call << ").g1_o]=" << basename << ".block.dynamic_" << block + 1 << "(y, x, params, steady_state, 1, it_-1, 1);" << endl
2043                             << "    residual(y_index_eq)=ys(y_index)-y(it_, y_index);" << endl;
2044           break;
2045         case SOLVE_FORWARD_SIMPLE:
2046         case SOLVE_BACKWARD_SIMPLE:
2047           mDynamicModelFile << "    [r, y, dr(" << count_call << ").g1, dr(" << count_call << ").g2, dr(" << count_call << ").g3, dr(" << count_call << ").g1_x, dr(" << count_call << ").g1_xd, dr(" << count_call << ").g1_o]=" << basename << ".block.dynamic_" << block + 1 << "(y, x, params, steady_state, it_, 1);" << endl
2048                             << "    residual(y_index_eq)=r;" << endl;
2049           break;
2050         case SOLVE_FORWARD_COMPLETE:
2051         case SOLVE_BACKWARD_COMPLETE:
2052           mDynamicModelFile << "    [r, y, dr(" << count_call << ").g1, dr(" << count_call << ").g2, dr(" << count_call << ").g3, dr(" << count_call << ").g1_x, dr(" << count_call << ").g1_xd, dr(" << count_call << ").g1_o]=" << basename << ".block.dynamic_" << block + 1 << "(y, x, params, steady_state, it_, 1);" << endl
2053                             << "    residual(y_index_eq)=r;" << endl;
2054           break;
2055         case SOLVE_TWO_BOUNDARIES_COMPLETE:
2056         case SOLVE_TWO_BOUNDARIES_SIMPLE:
2057           mDynamicModelFile << "    [r, y, dr(" << count_call << ").g1, dr(" << count_call << ").g2, dr(" << count_call << ").g3, b, dr(" << count_call << ").g1_x, dr(" << count_call << ").g1_xd, dr(" << count_call << ").g1_o]=" << basename << ".block.dynamic_" <<  block + 1 << "(y, x, params, steady_state, it_-" << max_lag << ", 1, " << max_lag << ", " << block_recursive << "," << "options_.periods" << ");" << endl
2058                             << "    residual(y_index_eq)=r(:,M_.maximum_lag+1);" << endl;
2059           break;
2060         default:
2061           break;
2062         }
2063       tmp_eq.str("");
2064       tmp.str("");
2065     }
2066   if (tmp1.str().length())
2067     {
2068       mDynamicModelFile << tmp1.str();
2069       tmp1.str("");
2070     }
2071   mDynamicModelFile << "    varargout{1}=residual;" << endl
2072                     << "    varargout{2}=dr;" << endl
2073                     << "    return;" << endl
2074                     << "  end;" << endl
2075                     << "  %it is the deterministic simulation of the block decomposed dynamic model" << endl
2076                     << "  if(options_.stack_solve_algo==0)" << endl
2077                     << "    mthd='Sparse LU';" << endl
2078                     << "  elseif(options_.stack_solve_algo==1)" << endl
2079                     << "    mthd='Relaxation';" << endl
2080                     << "  elseif(options_.stack_solve_algo==2)" << endl
2081                     << "    mthd='GMRES';" << endl
2082                     << "  elseif(options_.stack_solve_algo==3)" << endl
2083                     << "    mthd='BICGSTAB';" << endl
2084                     << "  elseif(options_.stack_solve_algo==4)" << endl
2085                     << "    mthd='OPTIMPATH';" << endl
2086                     << "  else" << endl
2087                     << "    mthd='UNKNOWN';" << endl
2088                     << "  end;" << endl
2089                     << "  if options_.verbosity" << endl
2090                     << "    printline(41)" << endl
2091                     << "    disp(sprintf('MODEL SIMULATION (method=%s):',mthd))" << endl
2092                     << "    skipline()" << endl
2093                     << "  end" << endl
2094                     << "  periods=options_.periods;" << endl
2095                     << "  maxit_=options_.simul.maxit;" << endl
2096                     << "  solve_tolf=options_.solve_tolf;" << endl
2097                     << "  y=oo_.endo_simul';" << endl
2098                     << "  x=oo_.exo_simul;" << endl
2099                     << "  params=M_.params;" << endl
2100                     << "  steady_state=oo_.steady_state;" << endl
2101                     << "  oo_.deterministic_simulation.status = 0;" << endl;
2102   for (block = 0; block < nb_blocks; block++)
2103     {
2104       unsigned int block_size = getBlockSize(block);
2105       unsigned int block_mfs = getBlockMfs(block);
2106       unsigned int block_recursive = block_size - block_mfs;
2107       BlockSimulationType simulation_type = getBlockSimulationType(block);
2108 
2109       if (simulation_type == EVALUATE_FORWARD && block_size)
2110         {
2111           if (open_par)
2112             mDynamicModelFile << "  end" << endl;
2113           mDynamicModelFile << "  oo_.deterministic_simulation.status = 1;" << endl
2114                             << "  oo_.deterministic_simulation.error = 0;" << endl
2115                             << "  oo_.deterministic_simulation.iterations = 0;" << endl
2116                             << "  if(isfield(oo_.deterministic_simulation,'block'))" << endl
2117                             << "    blck_num = length(oo_.deterministic_simulation.block)+1;" << endl
2118                             << "  else" << endl
2119                             << "    blck_num = 1;" << endl
2120                             << "  end;" << endl
2121                             << "  oo_.deterministic_simulation.block(blck_num).status = 1;" << endl
2122                             << "  oo_.deterministic_simulation.block(blck_num).error = 0;" << endl
2123                             << "  oo_.deterministic_simulation.block(blck_num).iterations = 0;" << endl
2124                             << "  g1=[];g2=[];g3=[];" << endl
2125                             << "  y=" << basename << ".block.dynamic_" << block + 1 << "(y, x, params, steady_state, 0, y_kmin, periods);" << endl
2126                             << "  tmp = y(:,M_.block_structure.block(" << block + 1 << ").variable);" << endl
2127                             << "  if any(isnan(tmp) | isinf(tmp))" << endl
2128                             << "    disp(['Inf or Nan value during the evaluation of block " << block <<"']);" << endl
2129                             << "    oo_.deterministic_simulation.status = 0;" << endl
2130                             << "    oo_.deterministic_simulation.error = 100;" << endl
2131                             << "    varargout{1} = oo_;" << endl
2132                             << "    return;" << endl
2133                             << "  end;" << endl;
2134         }
2135       else if (simulation_type == EVALUATE_BACKWARD && block_size)
2136         {
2137           if (open_par)
2138             mDynamicModelFile << "  end" << endl;
2139           mDynamicModelFile << "  oo_.deterministic_simulation.status = 1;" << endl
2140                             << "  oo_.deterministic_simulation.error = 0;" << endl
2141                             << "  oo_.deterministic_simulation.iterations = 0;" << endl
2142                             << "  if(isfield(oo_.deterministic_simulation,'block'))" << endl
2143                             << "    blck_num = length(oo_.deterministic_simulation.block)+1;" << endl
2144                             << "  else" << endl
2145                             << "    blck_num = 1;" << endl
2146                             << "  end;" << endl
2147                             << "  oo_.deterministic_simulation.block(blck_num).status = 1;" << endl
2148                             << "  oo_.deterministic_simulation.block(blck_num).error = 0;" << endl
2149                             << "  oo_.deterministic_simulation.block(blck_num).iterations = 0;" << endl
2150                             << "  g1=[];g2=[];g3=[];" << endl
2151                             << "  y = " << basename << ".block.dynamic_" << block + 1 << "(y, x, params, steady_state, 0, y_kmin, periods);" << endl
2152                             << "  tmp = y(:,M_.block_structure.block(" << block + 1 << ").variable);" << endl
2153                             << "  if any(isnan(tmp) | isinf(tmp))" << endl
2154                             << "    disp(['Inf or Nan value during the evaluation of block " << block <<"']);" << endl
2155                             << "    oo_.deterministic_simulation.status = 0;" << endl
2156                             << "    oo_.deterministic_simulation.error = 100;" << endl
2157                             << "    varargout{1} = oo_;" << endl
2158                             << "    return;" << endl
2159                             << "  end;" << endl;
2160         }
2161       else if ((simulation_type == SOLVE_FORWARD_COMPLETE || simulation_type == SOLVE_FORWARD_SIMPLE) && block_size)
2162         {
2163           if (open_par)
2164             mDynamicModelFile << "  end" << endl;
2165           open_par = false;
2166           mDynamicModelFile << "  g1=0;" << endl
2167                             << "  r=0;" << endl;
2168           tmp.str("");
2169           for (unsigned int ik = block_recursive; ik < block_size; ik++)
2170             tmp << " " << getBlockVariableID(block, ik)+1;
2171           mDynamicModelFile << "  y_index = [" << tmp.str() << "];" << endl;
2172           int nze = blocks_derivatives[block].size();
2173           mDynamicModelFile << "  if(isfield(oo_.deterministic_simulation,'block'))" << endl
2174                             << "    blck_num = length(oo_.deterministic_simulation.block)+1;" << endl
2175                             << "  else" << endl
2176                             << "    blck_num = 1;" << endl
2177                             << "  end;" << endl
2178                             << "  y = solve_one_boundary('" << basename << ".block.dynamic_" <<  block + 1 << "'"
2179                             << ", y, x, params, steady_state, y_index, " << nze
2180                             << ", options_.periods, " << blocks_linear[block]
2181                             << ", blck_num, y_kmin, options_.simul.maxit, options_.solve_tolf, options_.slowc, " << cutoff << ", options_.stack_solve_algo, 1, 1, 0, M_, options_, oo_);" << endl
2182                             << "  tmp = y(:,M_.block_structure.block(" << block + 1 << ").variable);" << endl
2183                             << "  if any(isnan(tmp) | isinf(tmp))" << endl
2184                             << "    disp(['Inf or Nan value during the resolution of block " << block <<"']);" << endl
2185                             << "    oo_.deterministic_simulation.status = 0;" << endl
2186                             << "    oo_.deterministic_simulation.error = 100;" << endl
2187                             << "    varargout{1} = oo_;" << endl
2188                             << "    return;" << endl
2189                             << "  end;" << endl;
2190         }
2191       else if ((simulation_type == SOLVE_BACKWARD_COMPLETE || simulation_type == SOLVE_BACKWARD_SIMPLE) && block_size)
2192         {
2193           if (open_par)
2194             mDynamicModelFile << "  end" << endl;
2195           open_par = false;
2196           mDynamicModelFile << "  g1=0;" << endl
2197                             << "  r=0;" << endl;
2198           tmp.str("");
2199           for (unsigned int ik = block_recursive; ik < block_size; ik++)
2200             tmp << " " << getBlockVariableID(block, ik)+1;
2201           mDynamicModelFile << "  y_index = [" << tmp.str() << "];" << endl;
2202           int nze = blocks_derivatives[block].size();
2203 
2204           mDynamicModelFile << "  if(isfield(oo_.deterministic_simulation,'block'))" << endl
2205                             << "    blck_num = length(oo_.deterministic_simulation.block)+1;" << endl
2206                             << "  else" << endl
2207                             << "    blck_num = 1;" << endl
2208                             << "  end;" << endl
2209                             << "  y = solve_one_boundary('" << basename << ".block.dynamic_" <<  block + 1 << "'"
2210                             <<", y, x, params, steady_state, y_index, " << nze
2211                             <<", options_.periods, " << blocks_linear[block]
2212                             <<", blck_num, y_kmin, options_.simul.maxit, options_.solve_tolf, options_.slowc, " << cutoff << ", options_.stack_solve_algo, 1, 1, 0, M_, options_, oo_);" << endl
2213                             << "  tmp = y(:,M_.block_structure.block(" << block + 1 << ").variable);" << endl
2214                             << "  if any(isnan(tmp) | isinf(tmp))" << endl
2215                             << "    disp(['Inf or Nan value during the resolution of block " << block <<"']);" << endl
2216                             << "    oo_.deterministic_simulation.status = 0;" << endl
2217                             << "    oo_.deterministic_simulation.error = 100;" << endl
2218                             << "    varargout{1} = oo_;" << endl
2219                             << "    return;" << endl
2220                             << "  end;" << endl;
2221         }
2222       else if ((simulation_type == SOLVE_TWO_BOUNDARIES_COMPLETE || simulation_type == SOLVE_TWO_BOUNDARIES_SIMPLE) && block_size)
2223         {
2224           if (open_par)
2225             mDynamicModelFile << "  end" << endl;
2226           open_par = false;
2227           Nb_SGE++;
2228           int nze = blocks_derivatives[block].size();
2229           mDynamicModelFile << "  y_index=[";
2230           for (unsigned int ik = block_recursive; ik < block_size; ik++)
2231             mDynamicModelFile << " " << getBlockVariableID(block, ik)+1;
2232           mDynamicModelFile << "  ];" << endl
2233                             << "  if(isfield(oo_.deterministic_simulation,'block'))" << endl
2234                             << "    blck_num = length(oo_.deterministic_simulation.block)+1;" << endl
2235                             << "  else" << endl
2236                             << "    blck_num = 1;" << endl
2237                             << "  end;" << endl
2238                             << "  [y oo_] = solve_two_boundaries('" << basename << ".block.dynamic_" <<  block + 1 << "'"
2239                             <<", y, x, params, steady_state, y_index, " << nze
2240                             <<", options_.periods, " << max_leadlag_block[block].first
2241                             <<", " << max_leadlag_block[block].second
2242                             <<", " << blocks_linear[block]
2243                             <<", blck_num, y_kmin, options_.simul.maxit, options_.solve_tolf, options_.slowc, " << cutoff << ", options_.stack_solve_algo, options_, M_, oo_);" << endl
2244                             << "  tmp = y(:,M_.block_structure.block(" << block + 1 << ").variable);" << endl
2245                             << "  if any(isnan(tmp) | isinf(tmp))" << endl
2246                             << "    disp(['Inf or Nan value during the resolution of block " << block <<"']);" << endl
2247                             << "    oo_.deterministic_simulation.status = 0;" << endl
2248                             << "    oo_.deterministic_simulation.error = 100;" << endl
2249                             << "    varargout{1} = oo_;" << endl
2250                             << "    return;" << endl
2251                             << "  end;" << endl;
2252         }
2253     }
2254   if (open_par)
2255     mDynamicModelFile << "  end;" << endl;
2256   open_par = false;
2257   mDynamicModelFile << "  oo_.endo_simul = y';" << endl
2258                     << "  varargout{1} = oo_;" << endl
2259                     << "return;" << endl
2260                     << "end" << endl;
2261 
2262   mDynamicModelFile.close();
2263 
2264   writeModelEquationsOrdered_M(basename);
2265 }
2266 
2267 void
writeWrapperFunctions(const string & basename,const string & ending) const2268 DynamicModel::writeWrapperFunctions(const string &basename, const string &ending) const
2269 {
2270   string name;
2271   if (ending == "g1")
2272     name = "dynamic_resid_g1";
2273   else if (ending == "g2")
2274     name = "dynamic_resid_g1_g2";
2275   else if (ending == "g3")
2276     name = "dynamic_resid_g1_g2_g3";
2277 
2278   string filename = packageDir(basename) + "/" + name + ".m";
2279   ofstream output;
2280   output.open(filename, ios::out | ios::binary);
2281   if (!output.is_open())
2282     {
2283       cerr << "Error: Can't open file " << filename << " for writing" << endl;
2284       exit(EXIT_FAILURE);
2285     }
2286 
2287   if (ending == "g1")
2288     output << "function [residual, g1] = " << name << "(T, y, x, params, steady_state, it_, T_flag)" << endl
2289            << "% function [residual, g1] = " << name << "(T, y, x, params, steady_state, it_, T_flag)" << endl;
2290   else if (ending == "g2")
2291     output << "function [residual, g1, g2] = " << name << "(T, y, x, params, steady_state, it_, T_flag)" << endl
2292            << "% function [residual, g1, g2] = " << name << "(T, y, x, params, steady_state, it_, T_flag)" << endl;
2293   else if (ending == "g3")
2294     output << "function [residual, g1, g2, g3] = " << name << "(T, y, x, params, steady_state, it_, T_flag)" << endl
2295            << "% function [residual, g1, g2, g3] = " << name << "(T, y, x, params, steady_state, it_, T_flag)" << endl;
2296 
2297   output << "%" << endl
2298          << "% Wrapper function automatically created by Dynare" << endl
2299          << "%" << endl
2300          << endl
2301          << "    if T_flag" << endl
2302          << "        T = " << basename << ".dynamic_" << ending << "_tt(T, y, x, params, steady_state, it_);" << endl
2303          << "    end" << endl;
2304 
2305   if (ending == "g1")
2306     output << "    residual = " << basename << ".dynamic_resid(T, y, x, params, steady_state, it_, false);" << endl
2307            << "    g1       = " << basename << ".dynamic_g1(T, y, x, params, steady_state, it_, false);" << endl;
2308   else if (ending == "g2")
2309     output << "    [residual, g1] = " << basename << ".dynamic_resid_g1(T, y, x, params, steady_state, it_, false);" << endl
2310            << "    g2       = " << basename << ".dynamic_g2(T, y, x, params, steady_state, it_, false);" << endl;
2311   else if (ending == "g3")
2312     output << "    [residual, g1, g2] = " << basename << ".dynamic_resid_g1_g2(T, y, x, params, steady_state, it_, false);" << endl
2313            << "    g3       = " << basename << ".dynamic_g3(T, y, x, params, steady_state, it_, false);" << endl;
2314 
2315   output << endl << "end" << endl;
2316   output.close();
2317 }
2318 
2319 void
writeDynamicModelHelper(const string & basename,const string & name,const string & retvalname,const string & name_tt,size_t ttlen,const string & previous_tt_name,const ostringstream & init_s,const ostringstream & end_s,const ostringstream & s,const ostringstream & s_tt) const2320 DynamicModel::writeDynamicModelHelper(const string &basename,
2321                                       const string &name, const string &retvalname,
2322                                       const string &name_tt, size_t ttlen,
2323                                       const string &previous_tt_name,
2324                                       const ostringstream &init_s,
2325                                       const ostringstream &end_s,
2326                                       const ostringstream &s, const ostringstream &s_tt) const
2327 {
2328   string filename = packageDir(basename) + "/" + name_tt + ".m";
2329   ofstream output;
2330   output.open(filename, ios::out | ios::binary);
2331   if (!output.is_open())
2332     {
2333       cerr << "Error: Can't open file " << filename << " for writing" << endl;
2334       exit(EXIT_FAILURE);
2335     }
2336 
2337   output << "function T = " << name_tt << "(T, y, x, params, steady_state, it_)" << endl
2338          << "% function T = " << name_tt << "(T, y, x, params, steady_state, it_)" << endl
2339          << "%" << endl
2340          << "% File created by Dynare Preprocessor from .mod file" << endl
2341          << "%" << endl
2342          << "% Inputs:" << endl
2343          << "%   T             [#temp variables by 1]     double  vector of temporary terms to be filled by function" << endl
2344          << "%   y             [#dynamic variables by 1]  double  vector of endogenous variables in the order stored" << endl
2345          << "%                                                    in M_.lead_lag_incidence; see the Manual" << endl
2346          << "%   x             [nperiods by M_.exo_nbr]   double  matrix of exogenous variables (in declaration order)" << endl
2347          << "%                                                    for all simulation periods" << endl
2348          << "%   steady_state  [M_.endo_nbr by 1]         double  vector of steady state values" << endl
2349          << "%   params        [M_.param_nbr by 1]        double  vector of parameter values in declaration order" << endl
2350          << "%   it_           scalar                     double  time period for exogenous variables for which" << endl
2351          << "%                                                    to evaluate the model" << endl
2352          << "%" << endl
2353          << "% Output:" << endl
2354          << "%   T           [#temp variables by 1]       double  vector of temporary terms" << endl
2355          << "%" << endl << endl
2356          << "assert(length(T) >= " << ttlen << ");" << endl
2357          << endl;
2358 
2359   if (!previous_tt_name.empty())
2360     output << "T = " << basename << "." << previous_tt_name << "(T, y, x, params, steady_state, it_);" << endl << endl;
2361 
2362   output << s_tt.str() << endl
2363          << "end" << endl;
2364   output.close();
2365 
2366   filename = packageDir(basename) + "/" + name + ".m";
2367   output.open(filename, ios::out | ios::binary);
2368   if (!output.is_open())
2369     {
2370       cerr << "Error: Can't open file " << filename << " for writing" << endl;
2371       exit(EXIT_FAILURE);
2372     }
2373 
2374   output << "function " << retvalname << " = " << name << "(T, y, x, params, steady_state, it_, T_flag)" << endl
2375          << "% function " << retvalname << " = " << name << "(T, y, x, params, steady_state, it_, T_flag)" << endl
2376          << "%" << endl
2377          << "% File created by Dynare Preprocessor from .mod file" << endl
2378          << "%" << endl
2379          << "% Inputs:" << endl
2380          << "%   T             [#temp variables by 1]     double   vector of temporary terms to be filled by function" << endl
2381          << "%   y             [#dynamic variables by 1]  double   vector of endogenous variables in the order stored" << endl
2382          << "%                                                     in M_.lead_lag_incidence; see the Manual" << endl
2383          << "%   x             [nperiods by M_.exo_nbr]   double   matrix of exogenous variables (in declaration order)" << endl
2384          << "%                                                     for all simulation periods" << endl
2385          << "%   steady_state  [M_.endo_nbr by 1]         double   vector of steady state values" << endl
2386          << "%   params        [M_.param_nbr by 1]        double   vector of parameter values in declaration order" << endl
2387          << "%   it_           scalar                     double   time period for exogenous variables for which" << endl
2388          << "%                                                     to evaluate the model" << endl
2389          << "%   T_flag        boolean                    boolean  flag saying whether or not to calculate temporary terms" << endl
2390          << "%" << endl
2391          << "% Output:" << endl
2392          << "%   " << retvalname << endl
2393          << "%" << endl << endl;
2394 
2395   if (!name_tt.empty())
2396     output << "if T_flag" << endl
2397            << "    T = " << basename << "." << name_tt << "(T, y, x, params, steady_state, it_);" << endl
2398            << "end" << endl;
2399 
2400   output << init_s.str() << endl
2401          << s.str()
2402          << end_s.str() << endl
2403          << "end" << endl;
2404   output.close();
2405 }
2406 
2407 void
writeDynamicMatlabCompatLayer(const string & basename) const2408 DynamicModel::writeDynamicMatlabCompatLayer(const string &basename) const
2409 {
2410   string filename = packageDir(basename) + "/dynamic.m";
2411   ofstream output;
2412   output.open(filename, ios::out | ios::binary);
2413   if (!output.is_open())
2414     {
2415       cerr << "Error: Can't open file " << filename << " for writing" << endl;
2416       exit(EXIT_FAILURE);
2417     }
2418   int ntt = temporary_terms_mlv.size() + temporary_terms_derivatives[0].size() + temporary_terms_derivatives[1].size() + temporary_terms_derivatives[2].size() + temporary_terms_derivatives[3].size();
2419 
2420   output << "function [residual, g1, g2, g3] = dynamic(y, x, params, steady_state, it_)" << endl
2421          << "    T = NaN(" << ntt << ", 1);" << endl
2422          << "    if nargout <= 1" << endl
2423          << "        residual = " << basename << ".dynamic_resid(T, y, x, params, steady_state, it_, true);" << endl
2424          << "    elseif nargout == 2" << endl
2425          << "        [residual, g1] = " << basename << ".dynamic_resid_g1(T, y, x, params, steady_state, it_, true);" << endl
2426          << "    elseif nargout == 3" << endl
2427          << "        [residual, g1, g2] = " << basename << ".dynamic_resid_g1_g2(T, y, x, params, steady_state, it_, true);" << endl
2428          << "    else" << endl
2429          << "        [residual, g1, g2, g3] = " << basename << ".dynamic_resid_g1_g2_g3(T, y, x, params, steady_state, it_, true);" << endl
2430          << "    end" << endl
2431          << "end" << endl;
2432 
2433   output.close();
2434 }
2435 
2436 void
writeDynamicModel(ostream & DynamicOutput,bool use_dll,bool julia) const2437 DynamicModel::writeDynamicModel(ostream &DynamicOutput, bool use_dll, bool julia) const
2438 {
2439   writeDynamicModel("", DynamicOutput, use_dll, julia);
2440 }
2441 
2442 void
writeDynamicModel(const string & basename,bool use_dll,bool julia) const2443 DynamicModel::writeDynamicModel(const string &basename, bool use_dll, bool julia) const
2444 {
2445   ofstream DynamicOutput;
2446   writeDynamicModel(basename, DynamicOutput, use_dll, julia);
2447 }
2448 
2449 void
writeDynamicModel(const string & basename,ostream & DynamicOutput,bool use_dll,bool julia) const2450 DynamicModel::writeDynamicModel(const string &basename, ostream &DynamicOutput, bool use_dll, bool julia) const
2451 {
2452   vector<ostringstream> d_output(derivatives.size()); // Derivatives output (at all orders, including 0=residual)
2453   vector<ostringstream> tt_output(derivatives.size()); // Temp terms output (at all orders)
2454 
2455   ExprNodeOutputType output_type = (use_dll ? ExprNodeOutputType::CDynamicModel :
2456                                     julia ? ExprNodeOutputType::juliaDynamicModel : ExprNodeOutputType::matlabDynamicModel);
2457 
2458   deriv_node_temp_terms_t tef_terms;
2459   temporary_terms_t temp_term_union;
2460 
2461   writeModelLocalVariableTemporaryTerms(temp_term_union, temporary_terms_idxs,
2462                                         tt_output[0], output_type, tef_terms);
2463 
2464   writeTemporaryTerms(temporary_terms_derivatives[0],
2465                       temp_term_union,
2466                       temporary_terms_idxs,
2467                       tt_output[0], output_type, tef_terms);
2468 
2469   writeModelEquations(d_output[0], output_type, temp_term_union);
2470 
2471   int nrows = equations.size();
2472   int hessianColsNbr = dynJacobianColsNbr * dynJacobianColsNbr;
2473 
2474   // Writing Jacobian
2475   if (!derivatives[1].empty())
2476     {
2477       writeTemporaryTerms(temporary_terms_derivatives[1],
2478                           temp_term_union,
2479                           temporary_terms_idxs,
2480                           tt_output[1], output_type, tef_terms);
2481 
2482       for (const auto &first_derivative : derivatives[1])
2483         {
2484           auto [eq, var] = vectorToTuple<2>(first_derivative.first);
2485           expr_t d1 = first_derivative.second;
2486 
2487           jacobianHelper(d_output[1], eq, getDynJacobianCol(var), output_type);
2488           d_output[1] << "=";
2489           d1->writeOutput(d_output[1], output_type,
2490                           temp_term_union, temporary_terms_idxs, tef_terms);
2491           d_output[1] << ";" << endl;
2492         }
2493     }
2494 
2495   // Write derivatives for order ≥ 2
2496   for (size_t i = 2; i < derivatives.size(); i++)
2497     if (!derivatives[i].empty())
2498       {
2499         writeTemporaryTerms(temporary_terms_derivatives[i],
2500                             temp_term_union,
2501                             temporary_terms_idxs,
2502                             tt_output[i], output_type, tef_terms);
2503 
2504         /* When creating the sparse matrix (in MATLAB or C mode), since storage
2505            is in column-major order, output the first column, then the second,
2506            then the third. This gives a significant performance boost in use_dll
2507            mode (at both compilation and runtime), because it facilitates memory
2508            accesses and expression reusage. */
2509         ostringstream col0_output, col1_output, col2_output;
2510 
2511         int k = 0; // Current line index in the 3-column matrix
2512         for (const auto &[vidx, d] : derivatives[i])
2513           {
2514             int eq = vidx[0];
2515 
2516             int col_idx = 0;
2517             for (size_t j = 1; j < vidx.size(); j++)
2518               {
2519                 col_idx *= dynJacobianColsNbr;
2520                 col_idx += getDynJacobianCol(vidx[j]);
2521               }
2522 
2523             if (output_type == ExprNodeOutputType::juliaDynamicModel)
2524               {
2525                 d_output[i] << "    @inbounds " << "g" << i << "[" << eq + 1 << "," << col_idx + 1 << "] = ";
2526                 d->writeOutput(d_output[i], output_type, temp_term_union, temporary_terms_idxs, tef_terms);
2527                 d_output[i] << endl;
2528               }
2529             else
2530               {
2531                 sparseHelper(i, col0_output, k, 0, output_type);
2532                 col0_output << "=" << eq + 1 << ";" << endl;
2533 
2534                 sparseHelper(i, col1_output, k, 1, output_type);
2535                 col1_output << "=" << col_idx + 1 << ";" << endl;
2536 
2537                 sparseHelper(i, col2_output, k, 2, output_type);
2538                 col2_output << "=";
2539                 d->writeOutput(col2_output, output_type, temp_term_union, temporary_terms_idxs, tef_terms);
2540                 col2_output << ";" << endl;
2541 
2542                 k++;
2543               }
2544 
2545             // Output symetric elements at order 2
2546             if (i == 2 && vidx[1] != vidx[2])
2547               {
2548                 int col_idx_sym = getDynJacobianCol(vidx[2]) * dynJacobianColsNbr + getDynJacobianCol(vidx[1]);
2549 
2550                 if (output_type == ExprNodeOutputType::juliaDynamicModel)
2551                   d_output[2] << "    @inbounds g2[" << eq + 1 << "," << col_idx_sym + 1 << "] = "
2552                               << "g2[" << eq + 1 << "," << col_idx + 1 << "]" << endl;
2553                 else
2554                   {
2555                     sparseHelper(2, col0_output, k, 0, output_type);
2556                     col0_output << "=" << eq + 1 << ";" << endl;
2557 
2558                     sparseHelper(2, col1_output, k, 1, output_type);
2559                     col1_output << "=" << col_idx_sym + 1 << ";" << endl;
2560 
2561                     sparseHelper(2, col2_output, k, 2, output_type);
2562                     col2_output << "=";
2563                     sparseHelper(2, col2_output, k-1, 2, output_type);
2564                     col2_output << ";" << endl;
2565 
2566                     k++;
2567                   }
2568               }
2569           }
2570         if (output_type != ExprNodeOutputType::juliaDynamicModel)
2571           d_output[i] << col0_output.str() << col1_output.str() << col2_output.str();
2572       }
2573 
2574   if (output_type == ExprNodeOutputType::matlabDynamicModel)
2575     {
2576       // Check that we don't have more than 32 nested parenthesis because Matlab does not suppor this. See Issue #1201
2577       map<string, string> tmp_paren_vars;
2578       bool message_printed = false;
2579       for (auto &it : tt_output)
2580         fixNestedParenthesis(it, tmp_paren_vars, message_printed);
2581       for (auto &it : d_output)
2582         fixNestedParenthesis(it, tmp_paren_vars, message_printed);
2583 
2584       ostringstream init_output, end_output;
2585       init_output << "residual = zeros(" << nrows << ", 1);";
2586       writeDynamicModelHelper(basename, "dynamic_resid", "residual",
2587                               "dynamic_resid_tt",
2588                               temporary_terms_mlv.size() + temporary_terms_derivatives[0].size(),
2589                               "", init_output, end_output,
2590                               d_output[0], tt_output[0]);
2591 
2592       init_output.str("");
2593       init_output << "g1 = zeros(" << nrows << ", " << dynJacobianColsNbr << ");";
2594       writeDynamicModelHelper(basename, "dynamic_g1", "g1",
2595                               "dynamic_g1_tt",
2596                               temporary_terms_mlv.size() + temporary_terms_derivatives[0].size() + temporary_terms_derivatives[1].size(),
2597                               "dynamic_resid_tt",
2598                               init_output, end_output,
2599                               d_output[1], tt_output[1]);
2600       writeWrapperFunctions(basename, "g1");
2601 
2602       // For order ≥ 2
2603       int ncols = dynJacobianColsNbr;
2604       int ntt = temporary_terms_mlv.size() + temporary_terms_derivatives[0].size() + temporary_terms_derivatives[1].size();
2605       for (size_t i = 2; i < derivatives.size(); i++)
2606         {
2607           ncols *= dynJacobianColsNbr;
2608           ntt += temporary_terms_derivatives[i].size();
2609           string gname = "g" + to_string(i);
2610           string vname = "v" + to_string(i);
2611           string gprevname = "g" + to_string(i-1);
2612 
2613           init_output.str("");
2614           end_output.str("");
2615           if (derivatives[i].size())
2616             {
2617               init_output << vname << " = zeros(" << NNZDerivatives[i] << ",3);";
2618               end_output << gname << " = sparse("
2619                          << vname << "(:,1),"
2620                          << vname << "(:,2),"
2621                          << vname << "(:,3),"
2622                          << nrows << "," << ncols << ");";
2623             }
2624           else
2625             init_output << gname << " = sparse([],[],[]," << nrows << "," << ncols << ");";
2626           writeDynamicModelHelper(basename, "dynamic_" + gname, gname,
2627                                   "dynamic_" + gname + "_tt",
2628                                   ntt,
2629                                   "dynamic_" + gprevname + "_tt",
2630                                   init_output, end_output,
2631                                   d_output[i], tt_output[i]);
2632           if (i <= 3)
2633             writeWrapperFunctions(basename, gname);
2634         }
2635 
2636       writeDynamicMatlabCompatLayer(basename);
2637     }
2638   else if (output_type == ExprNodeOutputType::CDynamicModel)
2639     {
2640       for (size_t i = 0; i < d_output.size(); i++)
2641         {
2642           string funcname = i == 0 ? "resid" : "g" + to_string(i);
2643           string argname = i == 0 ? "residual" : i == 1 ? "g1" : "v" + to_string(i);
2644           DynamicOutput << "void dynamic_" << funcname << "_tt(const double *y, const double *x, int nb_row_x, const double *params, const double *steady_state, int it_, double *T)" << endl
2645                         << "{" << endl
2646                         << tt_output[i].str()
2647                         << "}" << endl
2648                         << endl
2649                         << "void dynamic_" << funcname << "(const double *y, const double *x, int nb_row_x, const double *params, const double *steady_state, int it_, const double *T, double *" << argname << ")" << endl
2650                         << "{" << endl;
2651           if (i == 0)
2652             DynamicOutput << "  double lhs, rhs;" << endl;
2653           DynamicOutput << d_output[i].str()
2654                         << "}" << endl
2655                         << endl;
2656         }
2657     }
2658   else
2659     {
2660       string filename = basename + "Dynamic.jl";
2661       ofstream output;
2662       output.open(filename, ios::out | ios::binary);
2663       if (!output.is_open())
2664         {
2665           cerr << "Error: Can't open file " << filename << " for writing" << endl;
2666           exit(EXIT_FAILURE);
2667         }
2668 
2669       output << "module " << basename << "Dynamic" << endl
2670              << "#" << endl
2671              << "# NB: this file was automatically generated by Dynare" << endl
2672              << "#     from " << basename << ".mod" << endl
2673              << "#" << endl
2674              << "using Utils" << endl << endl
2675              << "export tmp_nbr, dynamic!, dynamicResid!, dynamicG1!, dynamicG2!, dynamicG3!" << endl << endl
2676              << "#=" << endl
2677              << "# The comments below apply to all functions contained in this module #" << endl
2678              << "  NB: The arguments contained on the first line of the function" << endl
2679              << "      definition are those that are modified in place" << endl << endl
2680              << "## Exported Functions ##" << endl
2681              << "  dynamic!      : Wrapper function; computes residuals, Jacobian, Hessian," << endl
2682              << "                  and third derivatives depending on the arguments provided" << endl
2683              << "  dynamicResid! : Computes the dynamic model residuals" << endl
2684              << "  dynamicG1!    : Computes the dynamic model Jacobian" << endl
2685              << "  dynamicG2!    : Computes the dynamic model Hessian" << endl
2686              << "  dynamicG3!    : Computes the dynamic model third derivatives" << endl << endl
2687              << "## Exported Variables ##" << endl
2688              << "  tmp_nbr       : Vector{Int}(4) respectively the number of temporary variables" << endl
2689              << "                  for the residuals, g1, g2 and g3." << endl << endl
2690              << "## Local Functions ##" << endl
2691              << "  dynamicResidTT! : Computes the dynamic model temporary terms for the residuals" << endl
2692              << "  dynamicG1TT!    : Computes the dynamic model temporary terms for the Jacobian" << endl
2693              << "  dynamicG2TT!    : Computes the dynamic model temporary terms for the Hessian" << endl
2694              << "  dynamicG3TT!    : Computes the dynamic model temporary terms for the third derivatives" << endl << endl
2695              << "## Function Arguments ##" << endl
2696              << "  T            : Vector{Float64}(num_temp_terms), temporary terms" << endl
2697              << "  y            : Vector{Float64}(num_dynamic_vars), endogenous variables in the order stored model_.lead_lag_incidence; see the manual" << endl
2698              << "  x            : Matrix{Float64}(nperiods,model_.exo_nbr), exogenous variables (in declaration order) for all simulation periods" << endl
2699              << "  params       : Vector{Float64}(model_.param_nbr), parameter values in declaration order" << endl
2700              << "  steady_state : Vector{Float64}(model_endo_nbr)" << endl
2701              << "  it_          : Int, time period for exogenous variables for which to evaluate the model" << endl
2702              << "  residual     : Vector{Float64}(model_.eq_nbr), residuals of the dynamic model equations in order of declaration of the equations." << endl
2703              << "  g1           : Matrix{Float64}(model_.eq_nbr, num_dynamic_vars), Jacobian matrix of the dynamic model equations" << endl
2704              << "                 The rows and columns respectively correspond to equations in order of declaration and variables in order" << endl
2705              << "                 stored in model_.lead_lag_incidence" << endl
2706              << "  g2           : spzeros(model_.eq_nbr, (num_dynamic_vars)^2) Hessian matrix of the dynamic model equations" << endl
2707              << "                 The rows and columns respectively correspond to equations in order of declaration and variables in order" << endl
2708              << "                 stored in model_.lead_lag_incidence" << endl
2709              << "  g3           : spzeros(model_.eq_nbr, (num_dynamic_vars)^3) Third order derivative matrix of the dynamic model equations;" << endl
2710              << "                 The rows and columns respectively correspond to equations in order of declaration and variables in order" << endl
2711              << "                 stored in model_.lead_lag_incidence" << endl << endl
2712              << "## Remarks ##" << endl
2713              << "  [1] `num_dynamic_vars` is the number of non zero entries in the lead lag incidence matrix, `model_.lead_lag_incidence.`" << endl
2714              << "  [2] The size of `T`, ie the value of `num_temp_terms`, depends on the version of the dynamic model called. The number of temporary variables" << endl
2715              << "      used for the different returned objects (residuals, jacobian, hessian or third order derivatives) is given by the elements in `tmp_nbr`" << endl
2716              << "      exported vector. The first element is the number of temporaries used for the computation of the residuals, the second element is the" << endl
2717              << "      number of temporaries used for the evaluation of the jacobian matrix, etc. If one calls the version of the dynamic model computing the" << endl
2718              << "      residuals, the jacobian and hessian matrices, then `T` must have at least `sum(tmp_nbr[1:3])` elements." << endl
2719              << "=#" << endl << endl;
2720 
2721       // Write the number of temporary terms
2722       output << "tmp_nbr = zeros(Int,4)" << endl
2723              << "tmp_nbr[1] = " << temporary_terms_mlv.size() + temporary_terms_derivatives[0].size() << "# Number of temporary terms for the residuals" << endl
2724              << "tmp_nbr[2] = " << temporary_terms_derivatives[1].size() << "# Number of temporary terms for g1 (jacobian)" << endl
2725              << "tmp_nbr[3] = " << temporary_terms_derivatives[2].size() << "# Number of temporary terms for g2 (hessian)" << endl
2726              << "tmp_nbr[4] = " << temporary_terms_derivatives[3].size() << "# Number of temporary terms for g3 (third order derivates)" << endl << endl;
2727 
2728       // dynamicResidTT!
2729       output << "function dynamicResidTT!(T::Vector{Float64}," << endl
2730              << "                         y::Vector{Float64}, x::Matrix{Float64}, "
2731              << "params::Vector{Float64}, steady_state::Vector{Float64}, it_::Int)" << endl
2732              << tt_output[0].str()
2733              << "    return nothing" << endl
2734              << "end" << endl << endl;
2735 
2736       // dynamic!
2737       output << "function dynamicResid!(T::Vector{Float64}, residual::Vector{Float64}," << endl
2738              << "                       y::Vector{Float64}, x::Matrix{Float64}, "
2739              << "params::Vector{Float64}, steady_state::Vector{Float64}, it_::Int, T_flag::Bool)" << endl
2740              << "    @assert length(T) >= " << temporary_terms_mlv.size() + temporary_terms_derivatives[0].size() << endl
2741              << "    @assert length(residual) == " << nrows << endl
2742              << "    @assert length(y)+size(x, 2) == " << dynJacobianColsNbr << endl
2743              << "    @assert length(params) == " << symbol_table.param_nbr() << endl
2744              << "    if T_flag" << endl
2745              << "        dynamicResidTT!(T, y, x, params, steady_state, it_)" << endl
2746              << "    end" << endl
2747              << d_output[0].str()
2748              << "    return nothing" << endl
2749              << "end" << endl << endl;
2750 
2751       // dynamicG1TT!
2752       output << "function dynamicG1TT!(T::Vector{Float64}," << endl
2753              << "                      y::Vector{Float64}, x::Matrix{Float64}, "
2754              << "params::Vector{Float64}, steady_state::Vector{Float64}, it_::Int)" << endl
2755              << "    dynamicResidTT!(T, y, x, params, steady_state, it_)" << endl
2756              << tt_output[1].str()
2757              << "    return nothing" << endl
2758              << "end" << endl << endl;
2759 
2760       // dynamicG1!
2761       output << "function dynamicG1!(T::Vector{Float64}, g1::Matrix{Float64}," << endl
2762              << "                    y::Vector{Float64}, x::Matrix{Float64}, "
2763              << "params::Vector{Float64}, steady_state::Vector{Float64}, it_::Int, T_flag::Bool)" << endl
2764              << "    @assert length(T) >= "
2765              << temporary_terms_mlv.size() + temporary_terms_derivatives[0].size() + temporary_terms_derivatives[1].size() << endl
2766              << "    @assert size(g1) == (" << nrows << ", " << dynJacobianColsNbr << ")" << endl
2767              << "    @assert length(y)+size(x, 2) == " << dynJacobianColsNbr << endl
2768              << "    @assert length(params) == " << symbol_table.param_nbr() << endl
2769              << "    if T_flag" << endl
2770              << "        dynamicG1TT!(T, y, x, params, steady_state, it_)" << endl
2771              << "    end" << endl
2772              << "    fill!(g1, 0.0)" << endl
2773              << d_output[1].str()
2774              << "    return nothing" << endl
2775              << "end" << endl << endl;
2776 
2777       // dynamicG2TT!
2778       output << "function dynamicG2TT!(T::Vector{Float64}," << endl
2779              << "                      y::Vector{Float64}, x::Matrix{Float64}, "
2780              << "params::Vector{Float64}, steady_state::Vector{Float64}, it_::Int)" << endl
2781              << "    dynamicG1TT!(T, y, x, params, steady_state, it_)" << endl
2782              << tt_output[2].str()
2783              << "    return nothing" << endl
2784              << "end" << endl << endl;
2785 
2786       // dynamicG2!
2787       output << "function dynamicG2!(T::Vector{Float64}, g2::Matrix{Float64}," << endl
2788              << "                    y::Vector{Float64}, x::Matrix{Float64}, "
2789              << "params::Vector{Float64}, steady_state::Vector{Float64}, it_::Int, T_flag::Bool)" << endl
2790              << "    @assert length(T) >= " << temporary_terms_mlv.size() + temporary_terms_derivatives[0].size() + temporary_terms_derivatives[1].size() + temporary_terms_derivatives[2].size() << endl
2791              << "    @assert size(g2) == (" << nrows << ", " << hessianColsNbr << ")" << endl
2792              << "    @assert length(y)+size(x, 2) == " << dynJacobianColsNbr << endl
2793              << "    @assert length(params) == " << symbol_table.param_nbr() << endl
2794              << "    if T_flag" << endl
2795              << "        dynamicG2TT!(T, y, x, params, steady_state, it_)" << endl
2796              << "    end" << endl
2797              << "    fill!(g2, 0.0)" << endl
2798              << d_output[2].str()
2799              << "    return nothing" << endl
2800              << "end" << endl << endl;
2801 
2802       // dynamicG3TT!
2803       output << "function dynamicG3TT!(T::Vector{Float64}," << endl
2804              << "                      y::Vector{Float64}, x::Matrix{Float64}, "
2805              << "params::Vector{Float64}, steady_state::Vector{Float64}, it_::Int)" << endl
2806              << "    dynamicG2TT!(T, y, x, params, steady_state, it_)" << endl
2807              << tt_output[3].str()
2808              << "    return nothing" << endl
2809              << "end" << endl << endl;
2810 
2811       // dynamicG3!
2812       int ncols = hessianColsNbr * dynJacobianColsNbr;
2813       output << "function dynamicG3!(T::Vector{Float64}, g3::Matrix{Float64}," << endl
2814              << "                    y::Vector{Float64}, x::Matrix{Float64}, "
2815              << "params::Vector{Float64}, steady_state::Vector{Float64}, it_::Int, T_flag::Bool)" << endl
2816              << "    @assert length(T) >= "
2817              << temporary_terms_mlv.size() + temporary_terms_derivatives[0].size() + temporary_terms_derivatives[1].size() + temporary_terms_derivatives[2].size() + temporary_terms_derivatives[3].size() << endl
2818              << "    @assert size(g3) == (" << nrows << ", " << ncols << ")" << endl
2819              << "    @assert length(y)+size(x, 2) == " << dynJacobianColsNbr << endl
2820              << "    @assert length(params) == " << symbol_table.param_nbr() << endl
2821              << "    if T_flag" << endl
2822              << "      dynamicG3TT!(T, y, x, params, steady_state, it_)" << endl
2823              << "    end" << endl
2824              << "    fill!(g3, 0.0)" << endl
2825              << d_output[3].str()
2826              << "    return nothing" << endl
2827              << "end" << endl << endl;
2828 
2829       // dynamic!
2830       output << "function dynamic!(T::Vector{Float64}, residual::Vector{Float64}," << endl
2831              << "                  y::Vector{Float64}, x::Matrix{Float64}, "
2832              << "params::Vector{Float64}, steady_state::Vector{Float64}, it_::Int)" << endl
2833              << "    dynamicResid!(T, residual, y, x, params, steady_state, it_, true)" << endl
2834              << "    return nothing" << endl
2835              << "end" << endl
2836              << endl
2837              << "function dynamic!(T::Vector{Float64}, residual::Vector{Float64}, g1::Matrix{Float64}," << endl
2838              << "                  y::Vector{Float64}, x::Matrix{Float64}, "
2839              << "params::Vector{Float64}, steady_state::Vector{Float64}, it_::Int)" << endl
2840              << "    dynamicG1!(T, g1, y, x, params, steady_state, it_, true)" << endl
2841              << "    dynamicResid!(T, residual, y, x, params, steady_state, it_, false)" << endl
2842              << "    return nothing" << endl
2843              << "end" << endl
2844              << endl
2845              << "function dynamic!(T::Vector{Float64}, residual::Vector{Float64}, g1::Matrix{Float64}, g2::Matrix{Float64}," << endl
2846              << "                  y::Vector{Float64}, x::Matrix{Float64}, "
2847              << "params::Vector{Float64}, steady_state::Vector{Float64}, it_::Int)" << endl
2848              << "    dynamicG2!(T, g2, y, x, params, steady_state, it_, true)" << endl
2849              << "    dynamicG1!(T, g1, y, x, params, steady_state, it_, false)" << endl
2850              << "    dynamicResid!(T, residual, y, x, params, steady_state, it_, false)" << endl
2851              << "    return nothing" << endl
2852              << "end" << endl
2853              << endl
2854              << "function dynamic!(T::Vector{Float64}, residual::Vector{Float64}, g1::Matrix{Float64}, g2::Matrix{Float64}, g3::Matrix{Float64}," << endl
2855              << "                  y::Vector{Float64}, x::Matrix{Float64}, "
2856              << "params::Vector{Float64}, steady_state::Vector{Float64}, it_::Int)" << endl
2857              << "    dynamicG3!(T, g3, y, x, params, steady_state, it_, true)" << endl
2858              << "    dynamicG2!(T, g2, y, x, params, steady_state, it_, false)" << endl
2859              << "    dynamicG1!(T, g1, y, x, params, steady_state, it_, false)" << endl
2860              << "    dynamicResid!(T, residual, y, x, params, steady_state, it_, false)" << endl
2861              << "    return nothing" << endl
2862              << "end" << endl
2863              << "end" << endl;
2864       output.close();
2865     }
2866 }
2867 
2868 void
writeDynamicJacobianNonZeroElts(const string & basename) const2869 DynamicModel::writeDynamicJacobianNonZeroElts(const string &basename) const
2870 {
2871   vector<pair<int, int>> nzij_pred, nzij_current, nzij_fwrd; // pairs (tsid, equation)
2872   for (const auto &[indices, d1] : derivatives[1])
2873     {
2874       if (symbol_table.getType(getSymbIDByDerivID(indices[1])) != SymbolType::endogenous)
2875         continue;
2876       int tsid = symbol_table.getTypeSpecificID(getSymbIDByDerivID(indices[1]));
2877       int lag = getLagByDerivID(indices[1]);
2878       if (lag == -1)
2879         nzij_pred.emplace_back(tsid, indices[0]);
2880       else if (lag == 0)
2881         nzij_current.emplace_back(tsid, indices[0]);
2882       else
2883         nzij_fwrd.emplace_back(tsid, indices[0]);
2884     }
2885   sort(nzij_pred.begin(), nzij_pred.end());
2886   sort(nzij_current.begin(), nzij_current.end());
2887   sort(nzij_fwrd.begin(), nzij_fwrd.end());
2888 
2889   ofstream output{"+" + basename + "/dynamic_g1_nz.m", ios::out | ios::binary};
2890   output << "function [nzij_pred, nzij_current, nzij_fwrd] = dynamic_g1_nz()" << endl
2891          << "% Returns the coordinates of non-zero elements in the Jacobian, in column-major order, for each lead/lag (only for endogenous)" << endl;
2892   auto print_nzij = [&output](const vector<pair<int, int>> &nzij, const string &name) {
2893                       output << "  " << name << " = zeros(" << nzij.size() << ", 2, 'int32');" << endl;
2894                       int idx = 1;
2895                       for (const auto &it : nzij)
2896                         {
2897                           output << "  " << name << "(" << idx << ",1)=" << it.second+1 << ';'
2898                                  << " " << name << "(" << idx << ",2)=" << it.first+1 << ';' << endl;
2899                           idx++;
2900                         }
2901                     };
2902   print_nzij(nzij_pred, "nzij_pred");
2903   print_nzij(nzij_current, "nzij_current");
2904   print_nzij(nzij_fwrd, "nzij_fwrd");
2905   output << "end" << endl;
2906   output.close();
2907 }
2908 
2909 void
parseIncludeExcludeEquations(const string & inc_exc_eq_tags,set<pair<string,string>> & eq_tag_set,bool exclude_eqs)2910 DynamicModel::parseIncludeExcludeEquations(const string &inc_exc_eq_tags, set<pair<string, string>> &eq_tag_set, bool exclude_eqs)
2911 {
2912   string tags;
2913   if (filesystem::exists(inc_exc_eq_tags))
2914     {
2915       ifstream exclude_file;
2916       exclude_file.open(inc_exc_eq_tags, ifstream::in);
2917       if (!exclude_file.is_open())
2918         {
2919           cerr << "ERROR: Could not open " << inc_exc_eq_tags << endl;
2920           exit(EXIT_FAILURE);
2921         }
2922 
2923       string line;
2924       bool tagname_on_first_line = false;
2925       while (getline(exclude_file, line))
2926         {
2927           removeLeadingTrailingWhitespace(line);
2928           if (!line.empty())
2929             if (tags.empty() && line.find("=") != string::npos)
2930               {
2931                 tagname_on_first_line = true;
2932                 tags += line + "(";
2933               }
2934             else
2935               if (line.find("'") != string::npos)
2936                 tags += line + ",";
2937               else
2938                 tags += "'" + line + "',";
2939         }
2940 
2941       if (!tags.empty())
2942         {
2943           tags = tags.substr(0, tags.size()-1);
2944           if (tagname_on_first_line)
2945             tags += ")";
2946         }
2947     }
2948   else
2949     tags = inc_exc_eq_tags;
2950   removeLeadingTrailingWhitespace(tags);
2951 
2952   if (tags.front() == '[' && tags.back() != ']')
2953     {
2954       cerr << "Error: " << (exclude_eqs ? "exclude_eqs" : "include_eqs")
2955            << ": if the first character is '[' the last must be ']'" << endl;
2956       exit(EXIT_FAILURE);
2957     }
2958 
2959   if (tags.front() == '[' && tags.back() == ']')
2960     tags = tags.substr(1, tags.length() - 2);
2961   removeLeadingTrailingWhitespace(tags);
2962 
2963   regex q(R"(^\w+\s*=)");
2964   smatch matches;
2965   string tagname = "name";
2966   if (regex_search(tags, matches, q))
2967     {
2968       tagname = matches[0].str();
2969       tags = tags.substr(tagname.size(), tags.length() - tagname.size() + 1);
2970       removeLeadingTrailingWhitespace(tags);
2971       if (tags.front() == '(' && tags.back() == ')')
2972         {
2973           tags = tags.substr(1, tags.length() - 2);
2974           removeLeadingTrailingWhitespace(tags);
2975         }
2976       tagname = tagname.substr(0, tagname.size()-1);
2977       removeLeadingTrailingWhitespace(tagname);
2978     }
2979 
2980   string quote_regex = "'[^']+'";
2981   string non_quote_regex = R"([^,\s]+)";
2982   regex r(R"((\s*)" + quote_regex + "|" + non_quote_regex + R"(\s*)(,\s*()" + quote_regex + "|" + non_quote_regex + R"()\s*)*)");
2983   if (!regex_match(tags, r))
2984     {
2985       cerr << "Error: " << (exclude_eqs ? "exclude_eqs" : "include_eqs")
2986            << ": argument is of incorrect format." << endl;
2987       exit(EXIT_FAILURE);
2988     }
2989 
2990   regex s(quote_regex + "|" + non_quote_regex);
2991   for (auto it = sregex_iterator(tags.begin(), tags.end(), s);
2992        it != sregex_iterator(); ++it)
2993     {
2994       auto str = it->str();
2995       if (str[0] == '\'' && str[str.size()-1] == '\'')
2996         str = str.substr(1, str.size()-2);
2997       eq_tag_set.insert({tagname, str});
2998     }
2999 }
3000 
3001 void
includeExcludeEquations(const string & eqs,bool exclude_eqs)3002 DynamicModel::includeExcludeEquations(const string &eqs, bool exclude_eqs)
3003 {
3004   if (eqs.empty())
3005     return;
3006 
3007   set<pair<string, string>> eq_tag_set;
3008   parseIncludeExcludeEquations(eqs, eq_tag_set, exclude_eqs);
3009 
3010   vector<int> excluded_vars
3011     = ModelTree::includeExcludeEquations(eq_tag_set, exclude_eqs,
3012                                          equations, equations_lineno,
3013                                          equation_tags, equation_tags_xref, false);
3014 
3015   // `static_only_equation_tags` is `vector<vector<pair<string, string>>>`
3016   // while `equation_tags` is `vector<pair<int, pair<string, string>>>`
3017   // so convert former structure to latter to conform with function call
3018   int n = 0;
3019   vector<pair<int, pair<string, string>>> tmp_static_only_equation_tags;
3020   for (auto &eqn_tags : static_only_equations_equation_tags)
3021     {
3022       for (auto &eqn_tag : eqn_tags)
3023         tmp_static_only_equation_tags.emplace_back(make_pair(n, eqn_tag));
3024       n++;
3025     }
3026   // Ignore output because variables are not excluded when equations marked 'static' are excluded
3027   ModelTree::includeExcludeEquations(eq_tag_set, exclude_eqs,
3028                                      static_only_equations, static_only_equations_lineno,
3029                                      tmp_static_only_equation_tags,
3030                                      static_only_equation_tags_xref, true);
3031   if (!eq_tag_set.empty())
3032     {
3033       cerr << "ERROR: " << (exclude_eqs ? "exclude_eqs" : "include_eqs") << ": The equations specified by `";
3034       cerr << eq_tag_set.begin()->first << "= ";
3035       for (auto &it : eq_tag_set)
3036         cerr << it.second << ", ";
3037       cerr << "` were not found." << endl;
3038       exit(EXIT_FAILURE);
3039     }
3040 
3041   if (staticOnlyEquationsNbr() != dynamicOnlyEquationsNbr())
3042     {
3043       cerr << "ERROR: " << (exclude_eqs ? "exclude_eqs" : "include_eqs")
3044            << ": You must remove the same number of equations marked `static` as equations marked `dynamic`." << endl;
3045       exit(EXIT_FAILURE);
3046     }
3047 
3048   // convert back static equation info
3049   if (static_only_equations.empty())
3050     static_only_equations_equation_tags.clear();
3051   else
3052     {
3053       static_only_equations_equation_tags.resize(static_only_equations.size());
3054       fill(static_only_equations_equation_tags.begin(), static_only_equations_equation_tags.end(), vector<pair<string, string>>());
3055       for (auto &it : tmp_static_only_equation_tags)
3056         static_only_equations_equation_tags.at(it.first).emplace_back(it.second);
3057     }
3058 
3059   // Collect list of used variables in updated list of equations
3060   set<pair<int, int>> eqn_vars;
3061   for (const auto &eqn : equations)
3062     eqn->collectDynamicVariables(SymbolType::endogenous, eqn_vars);
3063   for (const auto &eqn : static_only_equations)
3064     eqn->collectDynamicVariables(SymbolType::endogenous, eqn_vars);
3065 
3066   // Change LHS variable type of excluded equation if it is used in an eqution that has been kept
3067   for (auto ev : excluded_vars)
3068     {
3069       bool found = false;
3070       for (const auto &it : eqn_vars)
3071         if (it.first == ev)
3072           {
3073             symbol_table.changeType(ev, SymbolType::exogenous);
3074             found = true;
3075             break;
3076           }
3077       if (!found)
3078         symbol_table.changeType(ev, SymbolType::excludedVariable);
3079     }
3080 }
3081 
3082 void
writeOutput(ostream & output,const string & basename,bool block_decomposition,bool linear_decomposition,bool byte_code,bool use_dll,bool estimation_present,bool compute_xrefs,bool julia) const3083 DynamicModel::writeOutput(ostream &output, const string &basename, bool block_decomposition, bool linear_decomposition, bool byte_code, bool use_dll, bool estimation_present, bool compute_xrefs, bool julia) const
3084 {
3085   /* Writing initialisation for M_.lead_lag_incidence matrix
3086      M_.lead_lag_incidence is a matrix with as many columns as there are
3087      endogenous variables and as many rows as there are periods in the
3088      models (nbr of rows = M_.max_lag+M_.max_lead+1)
3089 
3090      The matrix elements are equal to zero if a variable isn't present in the
3091      model at a given period.
3092   */
3093 
3094   string modstruct, outstruct;
3095   if (julia)
3096     {
3097       modstruct = "model_.";
3098       outstruct = "oo_.";
3099     }
3100   else
3101     {
3102       modstruct = "M_.";
3103       outstruct = "oo_.";
3104     }
3105 
3106   output << modstruct << "orig_maximum_endo_lag = " << max_endo_lag_orig << ";" << endl
3107          << modstruct << "orig_maximum_endo_lead = " << max_endo_lead_orig << ";" << endl
3108          << modstruct << "orig_maximum_exo_lag = " << max_exo_lag_orig << ";" << endl
3109          << modstruct << "orig_maximum_exo_lead = " << max_exo_lead_orig << ";" << endl
3110          << modstruct << "orig_maximum_exo_det_lag = " << max_exo_det_lag_orig << ";" << endl
3111          << modstruct << "orig_maximum_exo_det_lead = " << max_exo_det_lead_orig << ";" << endl
3112          << modstruct << "orig_maximum_lag = " << max_lag_orig << ";" << endl
3113          << modstruct << "orig_maximum_lead = " << max_lead_orig << ";" << endl
3114          << modstruct << "orig_maximum_lag_with_diffs_expanded = " << max_lag_with_diffs_expanded_orig << ";" << endl
3115          << modstruct << "lead_lag_incidence = [";
3116   // Loop on endogenous variables
3117   int nstatic = 0,
3118     nfwrd = 0,
3119     npred = 0,
3120     nboth = 0;
3121   for (int endoID = 0; endoID < symbol_table.endo_nbr(); endoID++)
3122     {
3123       output << endl;
3124       int sstatic = 1,
3125         sfwrd = 0,
3126         spred = 0,
3127         sboth = 0;
3128       // Loop on periods
3129       for (int lag = -max_endo_lag; lag <= max_endo_lead; lag++)
3130         {
3131           // Print variableID if exists with current period, otherwise print 0
3132           try
3133             {
3134               int varID = getDerivID(symbol_table.getID(SymbolType::endogenous, endoID), lag);
3135               output << " " << getDynJacobianCol(varID) + 1;
3136               if (lag == -1)
3137                 {
3138                   sstatic = 0;
3139                   spred = 1;
3140                 }
3141               else if (lag == 1)
3142                 {
3143                   if (spred == 1)
3144                     {
3145                       sboth = 1;
3146                       spred = 0;
3147                     }
3148                   else
3149                     {
3150                       sstatic = 0;
3151                       sfwrd = 1;
3152                     }
3153                 }
3154             }
3155           catch (UnknownDerivIDException &e)
3156             {
3157               output << " 0";
3158             }
3159         }
3160       nstatic += sstatic;
3161       nfwrd += sfwrd;
3162       npred += spred;
3163       nboth += sboth;
3164       output << ";";
3165     }
3166   output << "]';" << endl;
3167   output << modstruct << "nstatic = " << nstatic << ";" << endl
3168          << modstruct << "nfwrd   = " << nfwrd   << ";" << endl
3169          << modstruct << "npred   = " << npred   << ";" << endl
3170          << modstruct << "nboth   = " << nboth   << ";" << endl
3171          << modstruct << "nsfwrd   = " << nfwrd+nboth   << ";" << endl
3172          << modstruct << "nspred   = " << npred+nboth   << ";" << endl
3173          << modstruct << "ndynamic   = " << npred+nboth+nfwrd << ";" << endl;
3174   if (!julia)
3175     {
3176       output << modstruct << "dynamic_tmp_nbr = [";
3177       for (size_t i = 0; i < temporary_terms_derivatives.size(); i++)
3178         output << temporary_terms_derivatives[i].size() + (i == 0 ? temporary_terms_mlv.size() : 0) << "; ";
3179       output << "];" << endl;
3180 
3181       /* Write mapping between model local variables and indices in the temporary
3182          terms vector (dynare#1722) */
3183       output << modstruct << "model_local_variables_dynamic_tt_idxs = {" << endl;
3184       for (auto [mlv, value] : temporary_terms_mlv)
3185         output << "  '" << symbol_table.getName(mlv->symb_id) << "', "
3186                << temporary_terms_idxs.at(mlv)+1 << ';' << endl;
3187       output << "};" << endl;
3188     }
3189 
3190   // Write equation tags
3191   if (julia)
3192     {
3193       output << modstruct << "equation_tags = [" << endl;
3194       for (const auto &equation_tag : equation_tags)
3195         output << "                       EquationTag("
3196                << equation_tag.first + 1 << R"( , ")"
3197                << equation_tag.second.first << R"(" , ")"
3198                << equation_tag.second.second << R"("))" << endl;
3199       output << "                      ]" << endl;
3200     }
3201   else
3202     {
3203       output << modstruct << "equations_tags = {" << endl;
3204       for (const auto &equation_tag : equation_tags)
3205         output << "  " << equation_tag.first + 1 << " , '"
3206                << equation_tag.second.first << "' , '"
3207                << equation_tag.second.second << "' ;" << endl;
3208       output << "};" << endl;
3209     }
3210 
3211   // Write mapping for variables and equations they are present in
3212   for (const auto &variable : variableMapping)
3213     {
3214       output << modstruct << "mapping." << symbol_table.getName(variable.first) << ".eqidx = [";
3215       for (auto equation : variable.second)
3216         output << equation + 1 << " ";
3217       output << "];" << endl;
3218     }
3219 
3220   /* Say if static and dynamic models differ (because of [static] and [dynamic]
3221      equation tags) */
3222   output << modstruct << "static_and_dynamic_models_differ = "
3223          << (static_only_equations.size() > 0 ? "true" : "false")
3224          << ";" << endl;
3225 
3226   // Say if model contains an external function call
3227   bool has_external_function = false;
3228   for (auto equation : equations)
3229     if (equation->containsExternalFunction())
3230       {
3231         has_external_function = true;
3232         break;
3233       }
3234   output << modstruct << "has_external_function = "
3235          << (has_external_function ? "true" : "false")
3236          << ';' << endl;
3237 
3238   vector<int> state_var;
3239   for (int endoID = 0; endoID < symbol_table.endo_nbr(); endoID++)
3240     // Loop on periods
3241     for (int lag = -max_endo_lag; lag < 0; lag++)
3242       try
3243         {
3244           getDerivID(symbol_table.getID(SymbolType::endogenous, variable_reordered[endoID]), lag);
3245           if (lag < 0 && find(state_var.begin(), state_var.end(), variable_reordered[endoID]+1) == state_var.end())
3246             state_var.push_back(variable_reordered[endoID]+1);
3247         }
3248       catch (UnknownDerivIDException &e)
3249         {
3250         }
3251 
3252   //In case of sparse model, writes the block_decomposition structure of the model
3253   if (block_decomposition || linear_decomposition)
3254     {
3255       vector<int> state_equ;
3256       int count_lead_lag_incidence = 0;
3257       int max_lead, max_lag, max_lag_endo, max_lead_endo, max_lag_exo, max_lead_exo, max_lag_exo_det, max_lead_exo_det;
3258       unsigned int nb_blocks = getNbBlocks();
3259       for (unsigned int block = 0; block < nb_blocks; block++)
3260         {
3261           //For a block composed of a single equation determines wether we have to evaluate or to solve the equation
3262           count_lead_lag_incidence = 0;
3263           BlockSimulationType simulation_type = getBlockSimulationType(block);
3264           int block_size = getBlockSize(block);
3265           max_lag = max_leadlag_block[block].first;
3266           max_lead = max_leadlag_block[block].second;
3267           max_lag_endo = endo_max_leadlag_block[block].first;
3268           max_lead_endo = endo_max_leadlag_block[block].second;
3269           max_lag_exo = exo_max_leadlag_block[block].first;
3270           max_lead_exo = exo_max_leadlag_block[block].second;
3271           max_lag_exo_det = exo_det_max_leadlag_block[block].first;
3272           max_lead_exo_det = exo_det_max_leadlag_block[block].second;
3273           ostringstream tmp_s, tmp_s_eq;
3274           tmp_s.str("");
3275           tmp_s_eq.str("");
3276           for (int i = 0; i < block_size; i++)
3277             {
3278               tmp_s << " " << getBlockVariableID(block, i)+1;
3279               tmp_s_eq << " " << getBlockEquationID(block, i)+1;
3280             }
3281           set<int> exogenous;
3282           for (const auto &it : exo_block[block])
3283             for (int it1 : it.second)
3284               exogenous.insert(it1);
3285           set<int> exogenous_det;
3286           for (const auto &it : exo_det_block[block])
3287             for (int it1 : it.second)
3288               exogenous_det.insert(it1);
3289           set<int> other_endogenous;
3290           for (const auto &it : other_endo_block[block])
3291             for (int it1 : it.second)
3292               other_endogenous.insert(it1);
3293           output << "block_structure.block(" << block+1 << ").Simulation_Type = " << simulation_type << ";" << endl
3294                  << "block_structure.block(" << block+1 << ").maximum_lag = " << max_lag << ";" << endl
3295                  << "block_structure.block(" << block+1 << ").maximum_lead = " << max_lead << ";" << endl
3296                  << "block_structure.block(" << block+1 << ").maximum_endo_lag = " << max_lag_endo << ";" << endl
3297                  << "block_structure.block(" << block+1 << ").maximum_endo_lead = " << max_lead_endo << ";" << endl
3298                  << "block_structure.block(" << block+1 << ").maximum_exo_lag = " << max_lag_exo << ";" << endl
3299                  << "block_structure.block(" << block+1 << ").maximum_exo_lead = " << max_lead_exo << ";" << endl
3300                  << "block_structure.block(" << block+1 << ").maximum_exo_det_lag = " << max_lag_exo_det << ";" << endl
3301                  << "block_structure.block(" << block+1 << ").maximum_exo_det_lead = " << max_lead_exo_det << ";" << endl
3302                  << "block_structure.block(" << block+1 << ").endo_nbr = " << block_size << ";" << endl
3303                  << "block_structure.block(" << block+1 << ").mfs = " << getBlockMfs(block) << ";" << endl
3304                  << "block_structure.block(" << block+1 << ").equation = [" << tmp_s_eq.str() << "];" << endl
3305                  << "block_structure.block(" << block+1 << ").variable = [" << tmp_s.str() << "];" << endl
3306                  << "block_structure.block(" << block+1 << ").exo_nbr = " << getBlockExoSize(block) << ";" << endl
3307                  << "block_structure.block(" << block+1 << ").exogenous = [";
3308           int i = 0;
3309           for (int exo : exogenous)
3310             if (exo >= 0)
3311               {
3312                 output << " " << exo+1;
3313                 i++;
3314               }
3315           output << "];" << endl
3316                  << "block_structure.block(" << block+1 << ").exogenous_det = [";
3317           i = 0;
3318           for (int exo_det : exogenous_det)
3319             if (exo_det >= 0)
3320               {
3321                 output << " " << exo_det+1;
3322                 i++;
3323               }
3324           output << "];" << endl
3325                  << "block_structure.block(" << block+1 << ").exo_det_nbr = " << i << ";" << endl
3326                  << "block_structure.block(" << block+1 << ").other_endogenous = [";
3327           i = 0;
3328           for (int other_endo : other_endogenous)
3329             if (other_endo >= 0)
3330               {
3331                 output << " " << other_endo+1;
3332                 i++;
3333               }
3334           output << "];" << endl
3335                  << "block_structure.block(" << block+1 << ").other_endogenous_block = [";
3336           i = 0;
3337           for (int other_endo : other_endogenous)
3338             if (other_endo >= 0)
3339               {
3340                 bool OK = true;
3341                 unsigned int j;
3342                 for (j = 0; j < block && OK; j++)
3343                   for (unsigned int k = 0; k < getBlockSize(j) && OK; k++)
3344                     OK = other_endo != getBlockVariableID(j, k);
3345                 if (!OK)
3346                   output << " " << j;
3347                 i++;
3348               }
3349           output << "];" << endl;
3350 
3351           output << "block_structure.block(" << block+1 << ").tm1 = zeros(" << i << ", " << state_var.size() << ");" << endl;
3352           int count_other_endogenous = 1;
3353           for (int other_endo : other_endogenous)
3354             {
3355               for (auto it = state_var.begin(); it != state_var.end(); ++it)
3356                 if (*it == other_endo + 1)
3357                   output << "block_structure.block(" << block+1 << ").tm1("
3358                          << count_other_endogenous << ", "
3359                          << it - state_var.begin()+1 << ") = 1;" << endl;
3360               count_other_endogenous++;
3361             }
3362 
3363           output << "block_structure.block(" << block+1 << ").other_endo_nbr = " << i << ";" << endl;
3364 
3365           tmp_s.str("");
3366           count_lead_lag_incidence = 0;
3367           dynamic_jacob_map_t reordered_dynamic_jacobian;
3368           for (const auto &it : blocks_derivatives[block])
3369             reordered_dynamic_jacobian[{ get<2>(it), get<1>(it), get<0>(it) }] = get<3>(it);
3370           output << "block_structure.block(" << block+1 << ").lead_lag_incidence = [];" << endl;
3371           int last_var = -1;
3372           vector<int> local_state_var;
3373           vector<int> local_stat_var;
3374           int n_static = 0, n_backward = 0, n_forward = 0, n_mixed = 0;
3375           for (int lag = -1; lag < 1+1; lag++)
3376             {
3377               last_var = -1;
3378               for (const auto &it : reordered_dynamic_jacobian)
3379                 {
3380                   if (lag == get<0>(it.first) && last_var != get<1>(it.first))
3381                     {
3382                       if (lag == -1)
3383                         {
3384                           local_state_var.push_back(getBlockVariableID(block, get<1>(it.first))+1);
3385                           n_backward++;
3386                         }
3387                       else if (lag == 0)
3388                         {
3389                           if (find(local_state_var.begin(), local_state_var.end(), getBlockVariableID(block, get<1>(it.first))+1) == local_state_var.end())
3390                             {
3391                               local_stat_var.push_back(getBlockVariableID(block, get<1>(it.first))+1);
3392                               n_static++;
3393                             }
3394                         }
3395                       else
3396                         {
3397                           if (find(local_state_var.begin(), local_state_var.end(), getBlockVariableID(block, get<1>(it.first))+1) != local_state_var.end())
3398                             {
3399                               n_backward--;
3400                               n_mixed++;
3401                             }
3402                           else
3403                             {
3404                               if (find(local_stat_var.begin(), local_stat_var.end(), getBlockVariableID(block, get<1>(it.first))+1) != local_stat_var.end())
3405                                 n_static--;
3406                               n_forward++;
3407                             }
3408                         }
3409                       count_lead_lag_incidence++;
3410                       for (int i = last_var; i < get<1>(it.first)-1; i++)
3411                         tmp_s << " 0";
3412                       if (tmp_s.str().length())
3413                         tmp_s << " ";
3414                       tmp_s << count_lead_lag_incidence;
3415                       last_var = get<1>(it.first);
3416                     }
3417                 }
3418               for (int i = last_var + 1; i < block_size; i++)
3419                 tmp_s << " 0";
3420               output << "block_structure.block(" << block+1 << ").lead_lag_incidence = [ block_structure.block(" << block+1 << ").lead_lag_incidence; " << tmp_s.str() << "]; %lag = " << lag << endl;
3421               tmp_s.str("");
3422             }
3423           vector<int> inter_state_var;
3424           for (int &it_l : local_state_var)
3425             for (auto it = state_var.begin(); it != state_var.end(); ++it)
3426               if (*it == it_l)
3427                 inter_state_var.push_back(it - state_var.begin()+1);
3428           output << "block_structure.block(" << block+1 << ").sorted_col_dr_ghx = [";
3429           for (int it : inter_state_var)
3430             output << it << " ";
3431           output << "];" << endl;
3432           count_lead_lag_incidence = 0;
3433           output << "block_structure.block(" << block+1 << ").lead_lag_incidence_other = [];" << endl;
3434           for (int lag = -1; lag <= 1; lag++)
3435             {
3436               tmp_s.str("");
3437               for (int other_endo : other_endogenous)
3438                 {
3439                   bool done = false;
3440                   for (int i = 0; i < block_size; i++)
3441                     {
3442                       unsigned int eq = getBlockEquationID(block, i);
3443                       if (derivative_other_endo[block].find({ lag, eq, other_endo })
3444                           != derivative_other_endo[block].end())
3445                         {
3446                           count_lead_lag_incidence++;
3447                           tmp_s << " " << count_lead_lag_incidence;
3448                           done = true;
3449                           break;
3450                         }
3451                     }
3452                   if (!done)
3453                     tmp_s << " 0";
3454                 }
3455               output << "block_structure.block(" << block+1 << ").lead_lag_incidence_other = [ block_structure.block(" << block+1 << ").lead_lag_incidence_other; " << tmp_s.str() << "]; %lag = " << lag << endl;
3456             }
3457           output << "block_structure.block(" << block+1 << ").n_static = " << n_static << ";" << endl
3458                  << "block_structure.block(" << block+1 << ").n_forward = " << n_forward << ";" << endl
3459                  << "block_structure.block(" << block+1 << ").n_backward = " << n_backward << ";" << endl
3460                  << "block_structure.block(" << block+1 << ").n_mixed = " << n_mixed << ";" << endl;
3461         }
3462       output << modstruct << "block_structure.block = block_structure.block;" << endl;
3463       string cst_s;
3464       int nb_endo = symbol_table.endo_nbr();
3465       output << modstruct << "block_structure.variable_reordered = [";
3466       for (int i = 0; i < nb_endo; i++)
3467         output << " " << variable_reordered[i]+1;
3468       output << "];" << endl;
3469       output << modstruct << "block_structure.equation_reordered = [";
3470       for (int i = 0; i < nb_endo; i++)
3471         output << " " << equation_reordered[i]+1;
3472       output << "];" << endl;
3473       vector<int> variable_inv_reordered(nb_endo);
3474 
3475       for (int i = 0; i < nb_endo; i++)
3476         variable_inv_reordered[variable_reordered[i]] = i;
3477 
3478       for (int it : state_var)
3479         state_equ.push_back(equation_reordered[variable_inv_reordered[it - 1]]+1);
3480 
3481       map<tuple<int, int, int>, int> lag_row_incidence;
3482       for (const auto & [indices, d1] : derivatives[1])
3483         {
3484           int deriv_id = indices[1];
3485           if (getTypeByDerivID(deriv_id) == SymbolType::endogenous)
3486             {
3487               int eq = indices[0];
3488               int symb = getSymbIDByDerivID(deriv_id);
3489               int var = symbol_table.getTypeSpecificID(symb);
3490               int lag = getLagByDerivID(deriv_id);
3491               lag_row_incidence[{ lag, eq, var }] = 1;
3492             }
3493         }
3494       int prev_lag = -1000000;
3495       for (const auto &it : lag_row_incidence)
3496         {
3497           if (prev_lag != get<0>(it.first))
3498             {
3499               if (prev_lag != -1000000)
3500                 output << "];" << endl;
3501               prev_lag = get<0>(it.first);
3502               output << modstruct << "block_structure.incidence(" << max_endo_lag+get<0>(it.first)+1 << ").lead_lag = " << prev_lag << ";" << endl
3503                      << modstruct << "block_structure.incidence(" << max_endo_lag+get<0>(it.first)+1 << ").sparse_IM = [";
3504             }
3505           output << get<1>(it.first)+1 << " " << get<2>(it.first)+1 << ";" << endl;
3506         }
3507       output << "];" << endl;
3508       if (estimation_present)
3509         {
3510           ofstream KF_index_file;
3511           filesystem::create_directories(basename + "/model/bytecode");
3512           string main_name = basename + "/model/bytecode/kfi";
3513           KF_index_file.open(main_name, ios::out | ios::binary | ios::ate);
3514           int n_obs = symbol_table.observedVariablesNbr();
3515           int n_state = state_var.size();
3516           for (int it : state_var)
3517             if (symbol_table.isObservedVariable(symbol_table.getID(SymbolType::endogenous, it-1)))
3518               n_obs--;
3519 
3520           int n = n_obs + n_state;
3521           output << modstruct << "nobs_non_statevar = " << n_obs << ";" << endl;
3522           int nb_diag = 0;
3523 
3524           vector<int> i_nz_state_var(n);
3525           for (int i = 0; i < n_obs; i++)
3526             i_nz_state_var[i] = n;
3527           unsigned int lp = n_obs;
3528 
3529           for (unsigned int block = 0; block < nb_blocks; block++)
3530             {
3531               int block_size = getBlockSize(block);
3532               int nze = 0;
3533 
3534               for (int i = 0; i < block_size; i++)
3535                 {
3536                   int var = getBlockVariableID(block, i);
3537                   if (find(state_var.begin(), state_var.end(), var+1) != state_var.end())
3538                     nze++;
3539                 }
3540               if (block == 0)
3541                 {
3542                   set<pair<int, int>> row_state_var_incidence;
3543                   for (const auto &it : blocks_derivatives[block])
3544                     if (auto it_state_var = find(state_var.begin(), state_var.end(), getBlockVariableID(block, get<1>(it))+1);
3545                         it_state_var != state_var.end())
3546                       if (auto it_state_equ = find(state_equ.begin(), state_equ.end(), getBlockEquationID(block, get<0>(it))+1);
3547                           it_state_equ != state_equ.end())
3548                         row_state_var_incidence.emplace(it_state_equ - state_equ.begin(), it_state_var - state_var.begin());
3549                   auto row_state_var_incidence_it = row_state_var_incidence.begin();
3550                   bool diag = true;
3551                   int nb_diag_r = 0;
3552                   while (row_state_var_incidence_it != row_state_var_incidence.end() && diag)
3553                     {
3554                       diag = (row_state_var_incidence_it->first == row_state_var_incidence_it->second);
3555                       if (diag)
3556                         {
3557                           int equ = row_state_var_incidence_it->first;
3558                           row_state_var_incidence_it++;
3559                           if (equ != row_state_var_incidence_it->first)
3560                             nb_diag_r++;
3561                         }
3562 
3563                     }
3564                   set<pair<int, int>> col_state_var_incidence;
3565                   for (const auto &it : row_state_var_incidence)
3566                     col_state_var_incidence.emplace(it.second, it.first);
3567                   auto col_state_var_incidence_it = col_state_var_incidence.begin();
3568                   diag = true;
3569                   int nb_diag_c = 0;
3570                   while (col_state_var_incidence_it != col_state_var_incidence.end() && diag)
3571                     {
3572                       diag = (col_state_var_incidence_it->first == col_state_var_incidence_it->second);
3573                       if (diag)
3574                         {
3575                           int var = col_state_var_incidence_it->first;
3576                           col_state_var_incidence_it++;
3577                           if (var != col_state_var_incidence_it->first)
3578                             nb_diag_c++;
3579                         }
3580                     }
3581                   nb_diag = min(nb_diag_r, nb_diag_c);
3582                   row_state_var_incidence.clear();
3583                   col_state_var_incidence.clear();
3584                 }
3585               for (int i = 0; i < nze; i++)
3586                 i_nz_state_var[lp + i] = lp + nze;
3587               lp += nze;
3588             }
3589           output << modstruct << "nz_state_var = [";
3590           for (unsigned int i = 0; i < lp; i++)
3591             output << i_nz_state_var[i] << " ";
3592           output << "];" << endl;
3593           output << modstruct << "n_diag = " << nb_diag << ";" << endl;
3594           KF_index_file.write(reinterpret_cast<char *>(&nb_diag), sizeof(nb_diag));
3595 
3596           using index_KF = pair<int, pair<int, int >>;
3597           vector<index_KF> v_index_KF;
3598           for (int i = 0; i < n; i++)
3599             for (int j = n_obs; j < n; j++)
3600               {
3601                 int j1 = j - n_obs;
3602                 int j1_n_state = j1 * n_state - n_obs;
3603                 if ((i < n_obs) || (i >= nb_diag + n_obs) || (j1 >= nb_diag))
3604                   for (int k = n_obs; k < i_nz_state_var[i]; k++)
3605                     v_index_KF.emplace_back(i + j1 * n, pair(i + k * n, k + j1_n_state));
3606               }
3607           int size_v_index_KF = v_index_KF.size();
3608 
3609           KF_index_file.write(reinterpret_cast<char *>(&size_v_index_KF), sizeof(size_v_index_KF));
3610           for (auto &it : v_index_KF)
3611             KF_index_file.write(reinterpret_cast<char *>(&it), sizeof(index_KF));
3612 
3613           vector<index_KF> v_index_KF_2;
3614           int n_n_obs = n * n_obs;
3615           for (int i = 0; i < n; i++)
3616             for (int j = i; j < n; j++)
3617               {
3618                 if ((i < n_obs) || (i >= nb_diag + n_obs) || (j < n_obs) || (j >= nb_diag + n_obs))
3619                   for (int k = n_obs; k < i_nz_state_var[j]; k++)
3620                     {
3621                       int k_n = k * n;
3622                       v_index_KF_2.emplace_back(i * n + j, pair(i + k_n - n_n_obs, j + k_n));
3623                     }
3624               }
3625           int size_v_index_KF_2 = v_index_KF_2.size();
3626 
3627           KF_index_file.write(reinterpret_cast<char *>(&size_v_index_KF_2), sizeof(size_v_index_KF_2));
3628           for (auto &it : v_index_KF_2)
3629             KF_index_file.write(reinterpret_cast<char *>(&it), sizeof(index_KF));
3630           KF_index_file.close();
3631         }
3632     }
3633 
3634   output << modstruct << "state_var = [";
3635   for (int it : state_var)
3636     output << it << (julia ? "," : " ");
3637   output << "];" << endl;
3638 
3639   // Writing initialization for some other variables
3640   if (!julia)
3641     output << modstruct << "exo_names_orig_ord = [1:" << symbol_table.exo_nbr() << "];" << endl;
3642   else
3643     output << modstruct << "exo_names_orig_ord = collect(1:" << symbol_table.exo_nbr() << ");" << endl;
3644 
3645   output << modstruct << "maximum_lag = " << max_lag << ";" << endl
3646          << modstruct << "maximum_lead = " << max_lead << ";" << endl;
3647 
3648   output << modstruct << "maximum_endo_lag = " << max_endo_lag << ";" << endl
3649          << modstruct << "maximum_endo_lead = " << max_endo_lead << ";" << endl
3650          << outstruct << "steady_state = zeros(" << symbol_table.endo_nbr() << (julia ? ")" : ", 1);") << endl;
3651 
3652   output << modstruct << "maximum_exo_lag = " << max_exo_lag << ";" << endl
3653          << modstruct << "maximum_exo_lead = " << max_exo_lead << ";" << endl
3654          << outstruct << "exo_steady_state = zeros(" << symbol_table.exo_nbr() <<  (julia ? ")" : ", 1);")   << endl;
3655 
3656   if (symbol_table.exo_det_nbr())
3657     {
3658       output << modstruct << "maximum_exo_det_lag = " << max_exo_det_lag << ";" << endl
3659              << modstruct << "maximum_exo_det_lead = " << max_exo_det_lead << ";" << endl
3660              << outstruct << "exo_det_steady_state = zeros(" << symbol_table.exo_det_nbr() << (julia ? ")" : ", 1);") << endl;
3661     }
3662 
3663   output << modstruct << "params = " << (julia ? "fill(NaN, " : "NaN(")
3664          << symbol_table.param_nbr() << (julia ? ")" : ", 1);") << endl;
3665 
3666   // FIXME: implement this for Julia
3667   if (!julia)
3668     {
3669       string empty_cell = "cell(" + to_string(symbol_table.endo_nbr()) + ", 1)";
3670       output << modstruct << "endo_trends = struct('deflator', " << empty_cell
3671              << ", 'log_deflator', " << empty_cell << ", 'growth_factor', " << empty_cell
3672              << ", 'log_growth_factor', " << empty_cell << ");" << endl;
3673       for (int i = 0; i < symbol_table.endo_nbr(); i++)
3674         {
3675           int symb_id = symbol_table.getID(SymbolType::endogenous, i);
3676           if (auto it = nonstationary_symbols_map.find(symb_id); it != nonstationary_symbols_map.end())
3677             {
3678               auto [is_log, deflator] = it->second;
3679               output << modstruct << "endo_trends(" << i << ")."
3680                      << (is_log ? "log_deflator" : "deflator") << " = '";
3681               deflator->writeJsonOutput(output, {}, {});
3682               output << "';" << endl;
3683 
3684               auto growth_factor = const_cast<DynamicModel *>(this)->AddDivide(deflator, deflator->decreaseLeadsLags(1))->removeTrendLeadLag(trend_symbols_map)->replaceTrendVar();
3685               output << modstruct << "endo_trends(" << i << ")."
3686                      << (is_log ? "log_growth_factor" : "growth_factor") << " = '";
3687               growth_factor->writeJsonOutput(output, {}, {});
3688               output << "';" << endl;
3689             }
3690         }
3691     }
3692 
3693   if (compute_xrefs)
3694     writeXrefs(output);
3695 
3696   // Write number of non-zero derivatives
3697   // Use -1 if the derivatives have not been computed
3698   output << modstruct << (julia ? "nnzderivatives" : "NNZDerivatives") << " = [";
3699   for (int i = 1; i < static_cast<int>(NNZDerivatives.size()); i++)
3700     output << (i > computed_derivs_order ? -1 : NNZDerivatives[i]) << "; ";
3701   output << "];" << endl;
3702 
3703   // Write Pac Model Consistent Expectation parameter info
3704   for (auto &it : pac_mce_alpha_symb_ids)
3705     {
3706       output << modstruct << "pac." << it.first.first << ".equations." << it.first.second << ".mce.alpha = [";
3707       for (auto it : it.second)
3708         output << symbol_table.getTypeSpecificID(it) + 1 << " ";
3709       output << "];" << endl;
3710     }
3711 
3712   // Write Pac Model Consistent Expectation Z1 info
3713   for (auto &it : pac_mce_z1_symb_ids)
3714     output << modstruct << "pac." << it.first.first << ".equations." << it.first.second << ".mce.z1 = "
3715            << symbol_table.getTypeSpecificID(it.second) + 1 << ";" << endl;
3716 
3717   // Write Pac lag info
3718   for (auto &it : pac_eqtag_and_lag)
3719     output << modstruct << "pac." << it.first.first << ".equations." << it.second.first << ".max_lag = " << it.second.second << ";" << endl;
3720 
3721   // Write Pac equation tag info
3722   map<string, vector<pair<string, string>>> for_writing;
3723   for (auto &it : pac_eqtag_and_lag)
3724     for_writing[it.first.first].emplace_back(it.first.second, it.second.first);
3725 
3726   for (auto &it : for_writing)
3727     {
3728       output << modstruct << "pac." << it.first << ".tag_map = [";
3729       for (auto &it1 : it.second)
3730         output << "{'" << it1.first << "', '" << it1.second << "'};";
3731       output << "];" << endl;
3732     }
3733 
3734   for (auto &it : pac_model_info)
3735     {
3736       vector<int> lhs = get<0>(it.second);
3737       output << modstruct << "pac." << it.first << ".lhs = [";
3738       for (auto it : lhs)
3739         output << it + 1 << " ";
3740       output << "];" << endl;
3741 
3742       if (int growth_param_index = get<1>(it.second);
3743           growth_param_index >= 0)
3744         output << modstruct << "pac." << it.first << ".growth_neutrality_param_index = "
3745                << symbol_table.getTypeSpecificID(growth_param_index) + 1 << ";" << endl;
3746 
3747       output << modstruct << "pac." << it.first << ".auxiliary_model_type = '" << get<2>(it.second) << "';" << endl;
3748     }
3749 
3750   for (auto &pit : pac_equation_info)
3751     {
3752       auto [lhs_pac_var, optim_share_index, ar_params_and_vars, ec_params_and_vars, non_optim_vars_params_and_constants, additive_vars_params_and_constants, optim_additive_vars_params_and_constants] = pit.second;
3753       string substruct = pit.first.first + ".equations." + pit.first.second + ".";
3754 
3755       output << modstruct << "pac." << substruct << "lhs_var = "
3756              << symbol_table.getTypeSpecificID(lhs_pac_var.first) + 1 << ";" << endl;
3757 
3758       if (optim_share_index >= 0)
3759         output << modstruct << "pac." << substruct << "share_of_optimizing_agents_index = "
3760                << symbol_table.getTypeSpecificID(optim_share_index) + 1 << ";" << endl;
3761 
3762       output << modstruct << "pac." << substruct << "ec.params = "
3763              << symbol_table.getTypeSpecificID(ec_params_and_vars.first) + 1 << ";" << endl
3764              << modstruct << "pac." << substruct << "ec.vars = [";
3765       for (auto it : ec_params_and_vars.second)
3766         output << symbol_table.getTypeSpecificID(get<0>(it)) + 1 << " ";
3767       output << "];" << endl
3768              << modstruct << "pac." << substruct << "ec.istarget = [";
3769       for (auto it : ec_params_and_vars.second)
3770         output << (get<1>(it) ? "true " : "false ");
3771       output << "];" << endl
3772              << modstruct << "pac." << substruct << "ec.scale = [";
3773       for (auto it : ec_params_and_vars.second)
3774         output << get<2>(it) << " ";
3775       output << "];" << endl
3776              << modstruct << "pac." << substruct << "ec.isendo = [";
3777       for (auto it : ec_params_and_vars.second)
3778         switch (symbol_table.getType(get<0>(it)))
3779           {
3780           case SymbolType::endogenous:
3781             output << "true ";
3782             break;
3783           case SymbolType::exogenous:
3784             output << "false ";
3785             break;
3786           default:
3787             cerr << "expecting endogenous or exogenous" << endl;
3788             exit(EXIT_FAILURE);
3789           }
3790       output << "];" << endl
3791              << modstruct << "pac." << substruct << "ar.params = [";
3792       for (auto &it : ar_params_and_vars)
3793         output << symbol_table.getTypeSpecificID(it.first) + 1 << " ";
3794       output << "];" << endl
3795              << modstruct << "pac." << substruct << "ar.vars = [";
3796       for (auto &it : ar_params_and_vars)
3797         output << symbol_table.getTypeSpecificID(it.second.first) + 1 << " ";
3798       output << "];" << endl
3799              << modstruct << "pac." << substruct << "ar.lags = [";
3800       for (auto &it : ar_params_and_vars)
3801         output << it.second.second << " ";
3802       output << "];" << endl;
3803       if (!non_optim_vars_params_and_constants.empty())
3804         {
3805           output << modstruct << "pac." << substruct << "non_optimizing_behaviour.params = [";
3806           for (auto &it : non_optim_vars_params_and_constants)
3807             if (get<2>(it) >= 0)
3808               output << symbol_table.getTypeSpecificID(get<2>(it)) + 1 << " ";
3809             else
3810               output << "NaN ";
3811           output << "];" << endl
3812                  << modstruct << "pac." << substruct << "non_optimizing_behaviour.vars = [";
3813           for (auto &it : non_optim_vars_params_and_constants)
3814             output << symbol_table.getTypeSpecificID(get<0>(it)) + 1 << " ";
3815           output << "];" << endl
3816                  << modstruct << "pac." << substruct << "non_optimizing_behaviour.isendo = [";
3817           for (auto &it : non_optim_vars_params_and_constants)
3818             switch (symbol_table.getType(get<0>(it)))
3819               {
3820               case SymbolType::endogenous:
3821                 output << "true ";
3822                 break;
3823               case SymbolType::exogenous:
3824                 output << "false ";
3825                 break;
3826               default:
3827                 cerr << "expecting endogenous or exogenous" << endl;
3828                 exit(EXIT_FAILURE);
3829               }
3830           output << "];" << endl
3831                  << modstruct << "pac." << substruct << "non_optimizing_behaviour.lags = [";
3832           for (auto &it : non_optim_vars_params_and_constants)
3833             output << get<1>(it) << " ";
3834           output << "];" << endl
3835                  << modstruct << "pac." << substruct << "non_optimizing_behaviour.scaling_factor = [";
3836           for (auto &it : non_optim_vars_params_and_constants)
3837             output << get<3>(it) << " ";
3838           output << "];" << endl;
3839         }
3840       if (!additive_vars_params_and_constants.empty())
3841         {
3842           output << modstruct << "pac." << substruct << "additive.params = [";
3843           for (auto &it : additive_vars_params_and_constants)
3844             if (get<2>(it) >= 0)
3845               output << symbol_table.getTypeSpecificID(get<2>(it)) + 1 << " ";
3846             else
3847               output << "NaN ";
3848           output << "];" << endl
3849                  << modstruct << "pac." << substruct << "additive.vars = [";
3850           for (auto &it : additive_vars_params_and_constants)
3851             output << symbol_table.getTypeSpecificID(get<0>(it)) + 1 << " ";
3852           output << "];" << endl
3853                  << modstruct << "pac." << substruct << "additive.isendo = [";
3854           for (auto &it : additive_vars_params_and_constants)
3855             switch (symbol_table.getType(get<0>(it)))
3856               {
3857               case SymbolType::endogenous:
3858                 output << "true ";
3859                 break;
3860               case SymbolType::exogenous:
3861                 output << "false ";
3862                 break;
3863               default:
3864                 cerr << "expecting endogenous or exogenous" << endl;
3865                 exit(EXIT_FAILURE);
3866               }
3867           output << "];" << endl
3868                  << modstruct << "pac." << substruct << "additive.lags = [";
3869           for (auto &it : additive_vars_params_and_constants)
3870             output << get<1>(it) << " ";
3871           output << "];" << endl
3872                  << modstruct << "pac." << substruct << "additive.scaling_factor = [";
3873           for (auto &it : additive_vars_params_and_constants)
3874             output << get<3>(it) << " ";
3875           output << "];" << endl;
3876         }
3877       if (!optim_additive_vars_params_and_constants.empty())
3878         {
3879           output << modstruct << "pac." << substruct << "optim_additive.params = [";
3880           for (auto &it : optim_additive_vars_params_and_constants)
3881             if (get<2>(it) >= 0)
3882               output << symbol_table.getTypeSpecificID(get<2>(it)) + 1 << " ";
3883             else
3884               output << "NaN ";
3885           output << "];" << endl
3886                  << modstruct << "pac." << substruct << "optim_additive.vars = [";
3887           for (auto &it : optim_additive_vars_params_and_constants)
3888             output << symbol_table.getTypeSpecificID(get<0>(it)) + 1 << " ";
3889           output << "];" << endl
3890                  << modstruct << "pac." << substruct << "optim_additive.isendo = [";
3891           for (auto &it : optim_additive_vars_params_and_constants)
3892             switch (symbol_table.getType(get<0>(it)))
3893               {
3894               case SymbolType::endogenous:
3895                 output << "true ";
3896                 break;
3897               case SymbolType::exogenous:
3898                 output << "false ";
3899                 break;
3900               default:
3901                 cerr << "expecting endogenous or exogenous" << endl;
3902                 exit(EXIT_FAILURE);
3903               }
3904           output << "];" << endl
3905                  << modstruct << "pac." << substruct << "optim_additive.lags = [";
3906           for (auto &it : optim_additive_vars_params_and_constants)
3907             output << get<1>(it) << " ";
3908           output << "];" << endl
3909                  << modstruct << "pac." << substruct << "optim_additive.scaling_factor = [";
3910           for (auto &it : optim_additive_vars_params_and_constants)
3911             output << get<3>(it) << " ";
3912           output << "];" << endl;
3913         }
3914       // Create empty h0 and h1 substructures that will be overwritten later if not empty
3915       output << modstruct << "pac." << substruct << "h0_param_indices = [];" << endl
3916              << modstruct << "pac." << substruct << "h1_param_indices = [];" << endl;
3917     }
3918 
3919   for (auto &it : pac_h0_indices)
3920     {
3921       output << modstruct << "pac." << it.first.first << ".equations." << it.first.second << ".h0_param_indices = [";
3922       for (auto it1 : it.second)
3923         output << symbol_table.getTypeSpecificID(it1) + 1 << " ";
3924       output << "];" << endl;
3925     }
3926 
3927   for (auto &it : pac_h1_indices)
3928     {
3929       output << modstruct << "pac." << it.first.first << ".equations." << it.first.second << ".h1_param_indices = [";
3930       for (auto it1 : it.second)
3931         output << symbol_table.getTypeSpecificID(it1) + 1 << " ";
3932       output << "];" << endl;
3933     }
3934 }
3935 
3936 map<tuple<int, int, int>, expr_t>
collect_first_order_derivatives_endogenous()3937 DynamicModel::collect_first_order_derivatives_endogenous()
3938 {
3939   map<tuple<int, int, int>, expr_t> endo_derivatives;
3940   for (auto & [indices, d1] : derivatives[1])
3941     if (getTypeByDerivID(indices[1]) == SymbolType::endogenous)
3942       {
3943         int eq = indices[0];
3944         int var = symbol_table.getTypeSpecificID(getSymbIDByDerivID(indices[1]));
3945         int lag = getLagByDerivID(indices[1]);
3946         endo_derivatives[{ eq, var, lag }] = d1;
3947       }
3948   return endo_derivatives;
3949 }
3950 
3951 void
runTrendTest(const eval_context_t & eval_context)3952 DynamicModel::runTrendTest(const eval_context_t &eval_context)
3953 {
3954   computeDerivIDs();
3955   testTrendDerivativesEqualToZero(eval_context);
3956 }
3957 
3958 void
updateVarAndTrendModel() const3959 DynamicModel::updateVarAndTrendModel() const
3960 {
3961   for (int i = 0; i < 2; i++)
3962     {
3963       map<string, vector<int>> eqnums, trend_eqnums;
3964       if (i == 0)
3965         eqnums = var_model_table.getEqNums();
3966       else if (i == 1)
3967         {
3968           eqnums = trend_component_model_table.getEqNums();
3969           trend_eqnums = trend_component_model_table.getTargetEqNums();
3970         }
3971 
3972       map<string, vector<int>> trend_varr;
3973       map<string, vector<set<pair<int, int>>>> rhsr;
3974       for (const auto &it : eqnums)
3975         {
3976           vector<int> lhs, trend_var, trend_lhs;
3977           vector<set<pair<int, int>>> rhs;
3978 
3979           if (i == 1)
3980             {
3981               lhs = trend_component_model_table.getLhs(it.first);
3982               for (auto teqn : trend_eqnums.at(it.first))
3983                 {
3984                   int eqnidx = 0;
3985                   for (auto eqn : it.second)
3986                     {
3987                       if (eqn == teqn)
3988                         trend_lhs.push_back(lhs[eqnidx]);
3989                       eqnidx++;
3990                     }
3991                 }
3992             }
3993 
3994           int lhs_idx = 0;
3995           for (auto eqn : it.second)
3996             {
3997               set<pair<int, int>> rhs_set;
3998               equations[eqn]->arg2->collectDynamicVariables(SymbolType::endogenous, rhs_set);
3999               rhs.push_back(rhs_set);
4000 
4001               if (i == 1)
4002                 {
4003                   int lhs_symb_id = lhs[lhs_idx++];
4004                   if (symbol_table.isAuxiliaryVariable(lhs_symb_id))
4005                     try
4006                       {
4007                         lhs_symb_id = symbol_table.getOrigSymbIdForAuxVar(lhs_symb_id);
4008                       }
4009                     catch (...)
4010                       {
4011                       }
4012                   int trend_var_symb_id = equations[eqn]->arg2->findTargetVariable(lhs_symb_id);
4013                   if (trend_var_symb_id >= 0)
4014                     {
4015                       if (symbol_table.isAuxiliaryVariable(trend_var_symb_id))
4016                         try
4017                           {
4018                             trend_var_symb_id = symbol_table.getOrigSymbIdForAuxVar(trend_var_symb_id);
4019                           }
4020                         catch (...)
4021                           {
4022                           }
4023                       if (find(trend_lhs.begin(), trend_lhs.end(), trend_var_symb_id) == trend_lhs.end())
4024                         {
4025                           cerr << "ERROR: trend found in trend_component equation #" << eqn << " ("
4026                                << symbol_table.getName(trend_var_symb_id) << ") does not correspond to a trend equation" << endl;
4027                           exit(EXIT_FAILURE);
4028                         }
4029                     }
4030                   trend_var.push_back(trend_var_symb_id);
4031                 }
4032             }
4033 
4034           rhsr[it.first] = rhs;
4035           if (i == 1)
4036             trend_varr[it.first] = trend_var;
4037         }
4038 
4039       if (i == 0)
4040         var_model_table.setRhs(rhsr);
4041       else if (i == 1)
4042         {
4043           trend_component_model_table.setRhs(rhsr);
4044           trend_component_model_table.setTargetVar(trend_varr);
4045         }
4046     }
4047 }
4048 
4049 void
fillVarModelTable() const4050 DynamicModel::fillVarModelTable() const
4051 {
4052   map<string, vector<int>> eqnums, lhsr;
4053   map<string, vector<expr_t>> lhs_expr_tr;
4054   map<string, vector<set<pair<int, int>>>> rhsr;
4055   map<string, vector<string>> eqtags = var_model_table.getEqTags();
4056 
4057   for (const auto &it : eqtags)
4058     {
4059       vector<int> eqnumber, lhs;
4060       vector<expr_t> lhs_expr_t;
4061       vector<set<pair<int, int>>> rhs;
4062 
4063       for (const auto &eqtag : it.second)
4064         {
4065           int eqn = -1;
4066           set<pair<int, int>> lhs_set, lhs_tmp_set, rhs_set;
4067           for (const auto &equation_tag : equation_tags)
4068             if (equation_tag.second.first == "name"
4069                 && equation_tag.second.second == eqtag)
4070               {
4071                 eqn = equation_tag.first;
4072                 break;
4073               }
4074 
4075           if (eqn == -1)
4076             {
4077               cerr << "ERROR: equation tag '" << eqtag << "' not found" << endl;
4078               exit(EXIT_FAILURE);
4079             }
4080 
4081           equations[eqn]->arg1->collectDynamicVariables(SymbolType::endogenous, lhs_set);
4082           equations[eqn]->arg1->collectDynamicVariables(SymbolType::exogenous, lhs_tmp_set);
4083           equations[eqn]->arg1->collectDynamicVariables(SymbolType::parameter, lhs_tmp_set);
4084 
4085           if (lhs_set.size() != 1 || !lhs_tmp_set.empty())
4086             {
4087               cerr << "ERROR: in Equation " << eqtag
4088                    << ". A VAR may only have one endogenous variable on the LHS. " << endl;
4089               exit(EXIT_FAILURE);
4090             }
4091 
4092           auto itlhs = lhs_set.begin();
4093           if (itlhs->second != 0)
4094             {
4095               cerr << "ERROR: in Equation " << eqtag
4096                    << ". The variable on the LHS of a VAR may not appear with a lead or a lag. "
4097                    << endl;
4098               exit(EXIT_FAILURE);
4099             }
4100 
4101           eqnumber.push_back(eqn);
4102           lhs.push_back(itlhs->first);
4103           lhs_set.clear();
4104           set<expr_t> lhs_expr_t_set;
4105           equations[eqn]->arg1->collectVARLHSVariable(lhs_expr_t_set);
4106           lhs_expr_t.push_back(*(lhs_expr_t_set.begin()));
4107 
4108           equations[eqn]->arg2->collectDynamicVariables(SymbolType::endogenous, rhs_set);
4109           for (const auto &itrhs : rhs_set)
4110             if (itrhs.second > 0)
4111               {
4112                 cerr << "ERROR: in Equation " << eqtag
4113                      << ". A VAR may not have leaded or contemporaneous variables on the RHS. " << endl;
4114                 exit(EXIT_FAILURE);
4115               }
4116           rhs.push_back(rhs_set);
4117         }
4118       eqnums[it.first] = eqnumber;
4119       lhsr[it.first] = lhs;
4120       lhs_expr_tr[it.first] = lhs_expr_t;
4121       rhsr[it.first] = rhs;
4122     }
4123   var_model_table.setEqNums(eqnums);
4124   var_model_table.setLhs(lhsr);
4125   var_model_table.setRhs(rhsr);
4126   var_model_table.setLhsExprT(lhs_expr_tr);
4127 
4128   // Fill AR Matrix
4129   var_model_table.setAR(fillAutoregressiveMatrix(true));
4130 }
4131 
4132 void
fillVarModelTableFromOrigModel() const4133 DynamicModel::fillVarModelTableFromOrigModel() const
4134 {
4135   map<string, vector<int>> lags, orig_diff_var;
4136   map<string, vector<bool>> diff;
4137   for (const auto &it : var_model_table.getEqNums())
4138     {
4139       set<expr_t> lhs;
4140       vector<int> orig_diff_var_vec;
4141       vector<bool> diff_vec;
4142       for (auto eqn : it.second)
4143         {
4144           // ensure no leads in equations
4145           if (equations[eqn]->arg2->VarMinLag() <= 0)
4146             {
4147               cerr << "ERROR in VAR model Equation (#" << eqn << "). "
4148                    << "Leaded exogenous variables "
4149                    << "and leaded or contemporaneous endogenous variables not allowed in VAR"
4150                    << endl;
4151               exit(EXIT_FAILURE);
4152             }
4153 
4154           // save lhs variables
4155           equations[eqn]->arg1->collectVARLHSVariable(lhs);
4156 
4157           equations[eqn]->arg1->countDiffs() > 0 ?
4158             diff_vec.push_back(true) : diff_vec.push_back(false);
4159           if (diff_vec.back())
4160             {
4161               set<pair<int, int>> diff_set;
4162               equations[eqn]->arg1->collectDynamicVariables(SymbolType::endogenous, diff_set);
4163 
4164               if (diff_set.size() != 1)
4165                 {
4166                   cerr << "ERROR: problem getting variable for LHS diff operator in equation "
4167                        << eqn << endl;
4168                   exit(EXIT_FAILURE);
4169                 }
4170               orig_diff_var_vec.push_back(diff_set.begin()->first);
4171             }
4172           else
4173             orig_diff_var_vec.push_back(-1);
4174 
4175         }
4176 
4177       if (it.second.size() != lhs.size())
4178         {
4179           cerr << "ERROR: The LHS variables of the VAR model are not unique" << endl;
4180           exit(EXIT_FAILURE);
4181         }
4182 
4183       set<expr_t> lhs_lag_equiv;
4184       for (const auto &lh : lhs)
4185         {
4186           auto [lag_equiv_repr, index] = lh->getLagEquivalenceClass();
4187           lhs_lag_equiv.insert(lag_equiv_repr);
4188         }
4189 
4190       vector<int> max_lag;
4191       for (auto eqn : it.second)
4192         max_lag.push_back(equations[eqn]->arg2->VarMaxLag(lhs_lag_equiv));
4193       lags[it.first] = max_lag;
4194       diff[it.first] = diff_vec;
4195       orig_diff_var[it.first] = orig_diff_var_vec;
4196     }
4197   var_model_table.setDiff(diff);
4198   var_model_table.setMaxLags(lags);
4199   var_model_table.setOrigDiffVar(orig_diff_var);
4200 }
4201 
4202 map<string, map<tuple<int, int, int>, expr_t>>
fillAutoregressiveMatrix(bool is_var) const4203 DynamicModel::fillAutoregressiveMatrix(bool is_var) const
4204 {
4205   map<string, map<tuple<int, int, int>, expr_t>> ARr;
4206   auto eqnums = is_var ?
4207     var_model_table.getEqNums() : trend_component_model_table.getNonTargetEqNums();
4208   for (const auto &it : eqnums)
4209     {
4210       int i = 0;
4211       map<tuple<int, int, int>, expr_t> AR;
4212       vector<int> lhs = is_var ?
4213         var_model_table.getLhsOrigIds(it.first) : trend_component_model_table.getNonTargetLhs(it.first);
4214       for (auto eqn : it.second)
4215         {
4216           auto bopn = dynamic_cast<BinaryOpNode *>(equations[eqn]->arg2);
4217           bopn->fillAutoregressiveRow(i++, lhs, AR);
4218         }
4219       ARr[it.first] = AR;
4220     }
4221   return ARr;
4222 }
4223 
4224 void
fillTrendComponentModelTable() const4225 DynamicModel::fillTrendComponentModelTable() const
4226 {
4227   map<string, vector<int>> eqnums, trend_eqnums, lhsr;
4228   map<string, vector<expr_t>> lhs_expr_tr;
4229   map<string, vector<set<pair<int, int>>>> rhsr;
4230   map<string, vector<string>> eqtags = trend_component_model_table.getEqTags();
4231   map<string, vector<string>> trend_eqtags = trend_component_model_table.getTargetEqTags();
4232   for (const auto &it : trend_eqtags)
4233     {
4234       vector<int> trend_eqnumber;
4235       for (const auto &eqtag : it.second)
4236         {
4237           int eqn = -1;
4238           for (const auto &equation_tag : equation_tags)
4239             if (equation_tag.second.first == "name"
4240                 && equation_tag.second.second == eqtag)
4241               {
4242                 eqn = equation_tag.first;
4243                 break;
4244               }
4245 
4246           if (eqn == -1)
4247             {
4248               cerr << "ERROR: trend equation tag '" << eqtag << "' not found" << endl;
4249               exit(EXIT_FAILURE);
4250             }
4251           trend_eqnumber.push_back(eqn);
4252         }
4253       trend_eqnums[it.first] = trend_eqnumber;
4254     }
4255 
4256   for (const auto &it : eqtags)
4257     {
4258       vector<int> eqnumber, lhs;
4259       vector<expr_t> lhs_expr_t;
4260       vector<set<pair<int, int>>> rhs;
4261 
4262       for (const auto &eqtag : it.second)
4263         {
4264           int eqn = -1;
4265           set<pair<int, int>> lhs_set, lhs_tmp_set, rhs_set;
4266           for (const auto &equation_tag : equation_tags)
4267             if (equation_tag.second.first == "name"
4268                 && equation_tag.second.second == eqtag)
4269               {
4270                 eqn = equation_tag.first;
4271                 break;
4272               }
4273 
4274           if (eqn == -1)
4275             {
4276               cerr << "ERROR: equation tag '" << eqtag << "' not found" << endl;
4277               exit(EXIT_FAILURE);
4278             }
4279 
4280           equations[eqn]->arg1->collectDynamicVariables(SymbolType::endogenous, lhs_set);
4281           equations[eqn]->arg1->collectDynamicVariables(SymbolType::exogenous, lhs_tmp_set);
4282           equations[eqn]->arg1->collectDynamicVariables(SymbolType::parameter, lhs_tmp_set);
4283 
4284           if (lhs_set.size() != 1 || !lhs_tmp_set.empty())
4285             {
4286               cerr << "ERROR: in Equation " << eqtag
4287                    << ". A trend component model may only have one endogenous variable on the LHS. " << endl;
4288               exit(EXIT_FAILURE);
4289             }
4290 
4291           auto itlhs = lhs_set.begin();
4292           if (itlhs->second != 0)
4293             {
4294               cerr << "ERROR: in Equation " << eqtag
4295                    << ". The variable on the LHS of a trend component model may not appear with a lead or a lag. "
4296                    << endl;
4297               exit(EXIT_FAILURE);
4298             }
4299 
4300           eqnumber.push_back(eqn);
4301           lhs.push_back(itlhs->first);
4302           lhs_set.clear();
4303           set<expr_t> lhs_expr_t_set;
4304           equations[eqn]->arg1->collectVARLHSVariable(lhs_expr_t_set);
4305           lhs_expr_t.push_back(*(lhs_expr_t_set.begin()));
4306 
4307           equations[eqn]->arg2->collectDynamicVariables(SymbolType::endogenous, rhs_set);
4308           for (const auto &itrhs : rhs_set)
4309             if (itrhs.second > 0)
4310               {
4311                 cerr << "ERROR: in Equation " << eqtag
4312                      << ". A trend component model may not have leaded or contemporaneous variables on the RHS. " << endl;
4313                 exit(EXIT_FAILURE);
4314               }
4315           rhs.push_back(rhs_set);
4316         }
4317       eqnums[it.first] = eqnumber;
4318       lhsr[it.first] = lhs;
4319       lhs_expr_tr[it.first] = lhs_expr_t;
4320       rhsr[it.first] = rhs;
4321     }
4322   trend_component_model_table.setRhs(rhsr);
4323   trend_component_model_table.setVals(eqnums, trend_eqnums, lhsr, lhs_expr_tr);
4324 }
4325 
4326 pair<map<string, map<tuple<int, int, int>, expr_t>>, map<string, map<tuple<int, int, int>, expr_t>>>
fillErrorComponentMatrix(const ExprNode::subst_table_t & diff_subst_table) const4327 DynamicModel::fillErrorComponentMatrix(const ExprNode::subst_table_t &diff_subst_table) const
4328 {
4329   map<string, map<tuple<int, int, int>, expr_t>> A0r, A0starr;
4330 
4331   for (const auto &it : trend_component_model_table.getEqNums())
4332     {
4333       int i = 0;
4334       map<tuple<int, int, int>, expr_t> A0, A0star;
4335       vector<int> target_lhs = trend_component_model_table.getTargetLhs(it.first);
4336       vector<int> nontarget_eqnums = trend_component_model_table.getNonTargetEqNums(it.first);
4337       vector<int> undiff_nontarget_lhs = getUndiffLHSForPac(it.first, diff_subst_table);
4338       vector<int> parsed_undiff_nontarget_lhs;
4339 
4340       for (auto eqn : it.second)
4341         {
4342           if (find(nontarget_eqnums.begin(), nontarget_eqnums.end(), eqn) != nontarget_eqnums.end())
4343             parsed_undiff_nontarget_lhs.push_back(undiff_nontarget_lhs.at(i));
4344           i++;
4345         }
4346 
4347       i = 0;
4348       for (auto eqn : it.second)
4349         if (find(nontarget_eqnums.begin(), nontarget_eqnums.end(), eqn) != nontarget_eqnums.end())
4350           equations[eqn]->arg2->fillErrorCorrectionRow(i++, parsed_undiff_nontarget_lhs, target_lhs, A0, A0star);
4351       A0r[it.first] = A0;
4352       A0starr[it.first] = A0star;
4353     }
4354 
4355   return { A0r, A0starr };
4356 }
4357 
4358 void
fillTrendComponentModelTableFromOrigModel() const4359 DynamicModel::fillTrendComponentModelTableFromOrigModel() const
4360 {
4361   map<string, vector<int>> lags, orig_diff_var;
4362   map<string, vector<bool>> diff;
4363   for (const auto &it : trend_component_model_table.getEqNums())
4364     {
4365       set<expr_t> lhs;
4366       vector<int> orig_diff_var_vec;
4367       vector<bool> diff_vec;
4368       for (auto eqn : it.second)
4369         {
4370           // ensure no leads in equations
4371           if (equations[eqn]->arg2->VarMinLag() <= 0)
4372             {
4373               cerr << "ERROR in trend component model Equation (#" << eqn << "). "
4374                    << "Leaded exogenous variables "
4375                    << "and leaded or contemporaneous endogenous variables not allowed in VAR"
4376                    << endl;
4377               exit(EXIT_FAILURE);
4378             }
4379 
4380           // save lhs variables
4381           equations[eqn]->arg1->collectVARLHSVariable(lhs);
4382 
4383           if (equations[eqn]->arg1->countDiffs() > 0)
4384             diff_vec.push_back(true);
4385           else
4386             diff_vec.push_back(false);
4387           if (diff_vec.back())
4388             {
4389               set<pair<int, int>> diff_set;
4390               equations[eqn]->arg1->collectDynamicVariables(SymbolType::endogenous, diff_set);
4391 
4392               if (diff_set.size() != 1)
4393                 {
4394                   cerr << "ERROR: problem getting variable for LHS diff operator in equation "
4395                        << eqn << endl;
4396                   exit(EXIT_FAILURE);
4397                 }
4398               orig_diff_var_vec.push_back(diff_set.begin()->first);
4399             }
4400           else
4401             orig_diff_var_vec.push_back(-1);
4402 
4403         }
4404 
4405       if (it.second.size() != lhs.size())
4406         {
4407           cerr << "ERROR: The LHS variables of the trend component model are not unique" << endl;
4408           exit(EXIT_FAILURE);
4409         }
4410 
4411       set<expr_t> lhs_lag_equiv;
4412       for (const auto &lh : lhs)
4413         {
4414           auto [lag_equiv_repr, index] = lh->getLagEquivalenceClass();
4415           lhs_lag_equiv.insert(lag_equiv_repr);
4416         }
4417 
4418       vector<int> max_lag;
4419       for (auto eqn : it.second)
4420         max_lag.push_back(equations[eqn]->arg2->VarMaxLag(lhs_lag_equiv));
4421       lags[it.first] = max_lag;
4422       diff[it.first] = diff_vec;
4423       orig_diff_var[it.first] = orig_diff_var_vec;
4424     }
4425   trend_component_model_table.setDiff(diff);
4426   trend_component_model_table.setMaxLags(lags);
4427   trend_component_model_table.setOrigDiffVar(orig_diff_var);
4428 }
4429 
4430 void
fillTrendComponentmodelTableAREC(const ExprNode::subst_table_t & diff_subst_table) const4431 DynamicModel::fillTrendComponentmodelTableAREC(const ExprNode::subst_table_t &diff_subst_table) const
4432 {
4433   auto ARr = fillAutoregressiveMatrix(false);
4434   trend_component_model_table.setAR(ARr);
4435   auto [A0r, A0starr] = fillErrorComponentMatrix(diff_subst_table);
4436   trend_component_model_table.setA0(A0r, A0starr);
4437 }
4438 
4439 void
addEquationsForVar()4440 DynamicModel::addEquationsForVar()
4441 {
4442   if (var_model_table.empty())
4443     return;
4444   auto var_symbol_list_and_order = var_model_table.getSymbolListAndOrder();
4445 
4446   // List of endogenous variables and the minimum lag value that must exist in the model equations
4447   map<string, int> var_endos_and_lags, model_endos_and_lags;
4448   for (const auto &it : var_symbol_list_and_order)
4449     for (auto &equation : equations)
4450       if (equation->isVarModelReferenced(it.first))
4451         {
4452           vector<string> symbol_list = it.second.first.get_symbols();
4453           int order = it.second.second;
4454           for (auto &it1 : symbol_list)
4455             if (order > 2)
4456               if (var_endos_and_lags.find(it1) != var_endos_and_lags.end())
4457                 var_endos_and_lags[it1] = min(var_endos_and_lags[it1], -order);
4458               else
4459                 var_endos_and_lags[it1] = -order;
4460           break;
4461         }
4462 
4463   if (var_endos_and_lags.empty())
4464     return;
4465 
4466   // Ensure that the minimum lag value exists in the model equations.
4467   // If not, add an equation for it
4468   for (auto &equation : equations)
4469     equation->getEndosAndMaxLags(model_endos_and_lags);
4470 
4471   int count = 0;
4472   for (auto &it : var_endos_and_lags)
4473     if (auto it2 = model_endos_and_lags.find(it.first);
4474         it2 == model_endos_and_lags.end())
4475       cerr << "WARNING: Variable used in VAR that is not used in the model: " << it.first << endl;
4476     else
4477       if (it.second < it2->second)
4478         {
4479           int symb_id = symbol_table.getID(it.first);
4480           expr_t newvar = AddVariable(symb_id, it.second);
4481           expr_t auxvar = AddVariable(symbol_table.addVarModelEndoLagAuxiliaryVar(symb_id, it.second, newvar), 0);
4482           addEquation(AddEqual(newvar, auxvar), -1);
4483           addAuxEquation(AddEqual(newvar, auxvar));
4484           count++;
4485         }
4486 
4487   if (count > 0)
4488     cout << "Accounting for var_model lags not in model block: added "
4489          << count << " auxiliary variables and equations." << endl;
4490 }
4491 
4492 vector<int>
getUndiffLHSForPac(const string & aux_model_name,const ExprNode::subst_table_t & diff_subst_table) const4493 DynamicModel::getUndiffLHSForPac(const string &aux_model_name,
4494                                  const ExprNode::subst_table_t &diff_subst_table) const
4495 {
4496   vector<expr_t> lhs_expr_t = trend_component_model_table.getLhsExprT(aux_model_name);
4497   vector<int> lhs = trend_component_model_table.getLhs(aux_model_name);
4498   vector<bool> diff = trend_component_model_table.getDiff(aux_model_name);
4499   vector<int> orig_diff_var = trend_component_model_table.getOrigDiffVar(aux_model_name);
4500   vector<int> eqnumber = trend_component_model_table.getEqNums(aux_model_name);
4501   vector<int> nontrend_eqnums = trend_component_model_table.getNonTargetEqNums(aux_model_name);
4502 
4503   for (auto eqn : nontrend_eqnums)
4504     {
4505       int i = 0;
4506       for (auto it1 = eqnumber.begin(); it1 != eqnumber.end(); ++it1, i++)
4507         if (*it1 == eqn)
4508           break;
4509 
4510       if (eqnumber[i] != eqn)
4511         {
4512           cerr << "ERROR: equation " << eqn << " not found in VAR" << endl;
4513           exit(EXIT_FAILURE);
4514         }
4515 
4516       if (diff.at(i) != true)
4517         {
4518           cerr << "ERROR: the variable on the LHS of equation #" << eqn
4519                << " does not have the diff operator applied to it yet you are trying to undiff it."
4520                << endl;
4521           exit(EXIT_FAILURE);
4522         }
4523 
4524       bool printerr = false;
4525       expr_t node = nullptr;
4526       expr_t aux_var = lhs_expr_t.at(i);
4527       for (const auto &it : diff_subst_table)
4528         if (it.second == aux_var)
4529           {
4530             node = const_cast<expr_t>(it.first);
4531             break;
4532           }
4533 
4534       if (!node)
4535         {
4536           cerr << "Unexpected error encountered." << endl;
4537           exit(EXIT_FAILURE);
4538         }
4539 
4540       node = node->undiff();
4541       auto it1 = diff_subst_table.find(node);
4542       if (it1 == diff_subst_table.end())
4543         printerr = true;
4544 
4545       if (printerr)
4546         { // we have undiffed something like diff(x), hence x is not in diff_subst_table
4547           lhs_expr_t.at(i) = node;
4548           lhs.at(i) = dynamic_cast<VariableNode *>(node)->symb_id;
4549         }
4550       else
4551         {
4552           lhs_expr_t.at(i) = const_cast<expr_t>(it1->first);
4553           lhs.at(i) = const_cast<VariableNode *>(it1->second)->symb_id;
4554         }
4555     }
4556   return lhs;
4557 }
4558 
4559 map<pair<string, string>, pair<string, int>>
walkPacParameters(const string & name)4560 DynamicModel::walkPacParameters(const string &name)
4561 {
4562   map<pair<string, string>, pair<string, int>> eqtag_and_lag;
4563 
4564   int i = 0;
4565   for (auto &equation : equations)
4566     {
4567       pair<int, int> lhs(-1, -1);
4568       pair<int, vector<tuple<int, bool, int>>> ec_params_and_vars;
4569       set<pair<int, pair<int, int>>> ar_params_and_vars;
4570       vector<tuple<int, int, int, double>> non_optim_vars_params_and_constants, optim_additive_vars_params_and_constants, additive_vars_params_and_constants;
4571 
4572       if (equation->containsPacExpectation())
4573         {
4574           set<pair<int, int>> lhss;
4575           equation->arg1->collectDynamicVariables(SymbolType::endogenous, lhss);
4576           lhs = *lhss.begin();
4577           int lhs_symb_id = lhs.first;
4578           int lhs_orig_symb_id = lhs_symb_id;
4579           if (symbol_table.isAuxiliaryVariable(lhs_orig_symb_id))
4580             try
4581               {
4582                 lhs_orig_symb_id = symbol_table.getOrigSymbIdForAuxVar(lhs_orig_symb_id);
4583               }
4584             catch (...)
4585               {
4586               }
4587 
4588           auto arg2 = dynamic_cast<BinaryOpNode *>(equation->arg2);
4589           if (!arg2)
4590             {
4591               cerr << "Pac equation in incorrect format" << endl;
4592               exit(EXIT_FAILURE);
4593             }
4594           auto [optim_share_index, optim_part, non_optim_part, additive_part]
4595             = arg2->getPacOptimizingShareAndExprNodes(lhs_symb_id, lhs_orig_symb_id);
4596 
4597           if (!optim_part)
4598             {
4599               auto bopn = dynamic_cast<BinaryOpNode *>(equation->arg2);
4600               if (!bopn)
4601                 {
4602                   cerr << "Error in PAC equation" << endl;
4603                   exit(EXIT_FAILURE);
4604                 }
4605               bopn->getPacAREC(lhs_symb_id, lhs_orig_symb_id, ec_params_and_vars, ar_params_and_vars, additive_vars_params_and_constants);
4606             }
4607           else
4608             {
4609               auto bopn = dynamic_cast<BinaryOpNode *>(optim_part);
4610               if (!bopn)
4611                 {
4612                   cerr << "Error in PAC equation" << endl;
4613                   exit(EXIT_FAILURE);
4614                 }
4615               bopn->getPacAREC(lhs_symb_id, lhs_orig_symb_id, ec_params_and_vars, ar_params_and_vars, optim_additive_vars_params_and_constants);
4616               try
4617                 {
4618                   non_optim_vars_params_and_constants = non_optim_part->matchLinearCombinationOfVariables();
4619                   if (additive_part)
4620                     additive_vars_params_and_constants = additive_part->matchLinearCombinationOfVariables();
4621                 }
4622               catch (ExprNode::MatchFailureException &e)
4623                 {
4624                   cerr << "Error in parsing non-optimizing agents or additive part of PAC equation: "
4625                        << e.message << endl;
4626                   exit(EXIT_FAILURE);
4627                 }
4628             }
4629 
4630           string eqtag;
4631           for (auto &tag : equation_tags)
4632             if (tag.first == (&equation - &equations[0]))
4633               if (tag.second.first == "name")
4634                 {
4635                   eqtag = tag.second.second;
4636                   break;
4637                 }
4638           if (eqtag.empty())
4639             {
4640               cerr << "Every equation with a pac expectation must have been assigned an equation tag name" << endl;
4641               exit(EXIT_FAILURE);
4642             }
4643           if (lhs.first == -1)
4644             {
4645               cerr << "walkPacParameters: error obtaining LHS variable." << endl;
4646               exit(EXIT_FAILURE);
4647             }
4648           if (ec_params_and_vars.second.empty() || ar_params_and_vars.empty())
4649             {
4650               cerr << "walkPacParameters: error obtaining RHS parameters." << endl;
4651               exit(EXIT_FAILURE);
4652             }
4653           string eq = "eq" + to_string(i++);
4654           pac_equation_info[{name, eq}] = {lhs, optim_share_index,
4655                                            ar_params_and_vars, ec_params_and_vars,
4656                                            non_optim_vars_params_and_constants,
4657                                            additive_vars_params_and_constants,
4658                                            optim_additive_vars_params_and_constants};
4659           eqtag_and_lag[{name, eqtag}] = {eq, 0};
4660         }
4661     }
4662   return eqtag_and_lag;
4663 }
4664 
4665 void
getPacMaxLag(const string & pac_model_name,map<pair<string,string>,pair<string,int>> & eqtag_and_lag) const4666 DynamicModel::getPacMaxLag(const string &pac_model_name, map<pair<string, string>, pair<string, int>> &eqtag_and_lag) const
4667 {
4668   for (auto &equation : equations)
4669     if (equation->containsPacExpectation(pac_model_name))
4670       {
4671         set<pair<int, int>> endogs;
4672         equation->arg1->collectDynamicVariables(SymbolType::endogenous, endogs);
4673         if (endogs.size() != 1)
4674           {
4675             cerr << "The LHS of the PAC equation may only be comprised of one endogenous variable"
4676                  << endl;
4677             exit(EXIT_FAILURE);
4678           }
4679 
4680         string eqtag;
4681         for (auto &tag : equation_tags)
4682           if (tag.first == (&equation - &equations[0]))
4683             if (tag.second.first == "name")
4684               {
4685                 eqtag = tag.second.second;
4686                 break;
4687               }
4688         string eq = eqtag_and_lag[{pac_model_name, eqtag}].first;
4689         eqtag_and_lag[{pac_model_name, eqtag}] = {eq, equation->PacMaxLag(endogs.begin()->first)};
4690       }
4691 }
4692 
4693 int
getPacTargetSymbId(const string & pac_model_name) const4694 DynamicModel::getPacTargetSymbId(const string &pac_model_name) const
4695 {
4696   for (auto &equation : equations)
4697     if (equation->containsPacExpectation(pac_model_name))
4698       {
4699         pair<int, int> lhs(-1, -1);
4700         set<pair<int, int>> lhss;
4701         equation->arg1->collectDynamicVariables(SymbolType::endogenous, lhss);
4702         lhs = *lhss.begin();
4703         int lhs_symb_id = lhs.first;
4704         int lhs_orig_symb_id = lhs_symb_id;
4705         if (symbol_table.isAuxiliaryVariable(lhs_symb_id))
4706           try
4707             {
4708               lhs_orig_symb_id = symbol_table.getOrigSymbIdForAuxVar(lhs_symb_id);
4709             }
4710           catch (...)
4711             {
4712             }
4713         return equation->arg2->getPacTargetSymbId(lhs_symb_id, lhs_orig_symb_id);
4714       }
4715   return -1;
4716 }
4717 
4718 void
declarePacModelConsistentExpectationEndogs(const string & name)4719 DynamicModel::declarePacModelConsistentExpectationEndogs(const string &name)
4720 {
4721   int i = 0;
4722   for (auto &equation : equations)
4723     if (equation->containsPacExpectation())
4724       {
4725         string eqtag;
4726         for (auto &tag : equation_tags)
4727           if (tag.first == (&equation - &equations[0]))
4728             if (tag.second.first == "name")
4729               {
4730                 eqtag = tag.second.second;
4731                 break;
4732               }
4733         if (eqtag.empty())
4734           {
4735             cerr << "Every equation with a pac expectation must have been assigned an equation tag name" << endl;
4736             exit(EXIT_FAILURE);
4737           }
4738         string standard_eqtag = "eq" + to_string(i++);
4739         try
4740           {
4741             pac_mce_z1_symb_ids[{name, standard_eqtag}]
4742               = symbol_table.addSymbol("mce_Z1_" + name + "_" + standard_eqtag, SymbolType::endogenous);
4743           }
4744         catch (SymbolTable::AlreadyDeclaredException &e)
4745           {
4746             cerr << "Variable name needed by PAC (mce_Z1_" << name << "_" << standard_eqtag << endl;
4747             exit(EXIT_FAILURE);
4748           }
4749       }
4750 }
4751 
4752 void
addPacModelConsistentExpectationEquation(const string & name,int discount_symb_id,const map<pair<string,string>,pair<string,int>> & eqtag_and_lag,ExprNode::subst_table_t & diff_subst_table)4753 DynamicModel::addPacModelConsistentExpectationEquation(const string &name, int discount_symb_id,
4754                                                        const map<pair<string, string>, pair<string, int>> &eqtag_and_lag,
4755                                                        ExprNode::subst_table_t &diff_subst_table)
4756 {
4757   int pac_target_symb_id = getPacTargetSymbId(name);
4758   pac_eqtag_and_lag.insert(eqtag_and_lag.begin(), eqtag_and_lag.end());
4759   int neqs = 0;
4760   for (auto &it : eqtag_and_lag)
4761     {
4762       string eqtag = it.first.second;
4763       string standard_eqtag = it.second.first;
4764       int pac_max_lag_m = it.second.second + 1;
4765       string append_to_name = name + "_" + standard_eqtag;
4766       if (pac_mce_z1_symb_ids.find({name, standard_eqtag}) == pac_mce_z1_symb_ids.end())
4767         {
4768           cerr << "Error finding pac MCE Z1 symb id" << endl;
4769           exit(EXIT_FAILURE);
4770         }
4771       int mce_z1_symb_id = pac_mce_z1_symb_ids[{name, standard_eqtag}];
4772 
4773       expr_t A = One;
4774       expr_t fp = Zero;
4775       expr_t beta = AddVariable(discount_symb_id);
4776       for (int i = 1; i <= pac_max_lag_m; i++)
4777         try
4778           {
4779             int alpha_i_symb_id = symbol_table.addSymbol("mce_alpha_" + append_to_name + "_" + to_string(i),
4780                                                          SymbolType::parameter);
4781             pac_mce_alpha_symb_ids[{name, standard_eqtag}].push_back(alpha_i_symb_id);
4782             A = AddPlus(A, AddVariable(alpha_i_symb_id));
4783             fp = AddPlus(fp,
4784                          AddTimes(AddTimes(AddVariable(alpha_i_symb_id),
4785                                            AddPower(beta, AddPossiblyNegativeConstant(i))),
4786                                   AddVariable(mce_z1_symb_id, i)));
4787 
4788           }
4789         catch (SymbolTable::AlreadyDeclaredException &e)
4790           {
4791             cerr << "Variable name needed by PAC (mce_alpha_" << append_to_name << "_" << i << ")" << endl;
4792             exit(EXIT_FAILURE);
4793           }
4794 
4795       // Add diff nodes and eqs for pac_target_symb_id
4796       const VariableNode *target_base_diff_node;
4797       expr_t diff_node_to_search = AddDiff(AddVariable(pac_target_symb_id));
4798       if (auto sit = diff_subst_table.find(diff_node_to_search);
4799           sit != diff_subst_table.end())
4800         target_base_diff_node = sit->second;
4801       else
4802         {
4803           int symb_id = symbol_table.addDiffAuxiliaryVar(diff_node_to_search->idx, diff_node_to_search);
4804           target_base_diff_node = AddVariable(symb_id);
4805           addEquation(dynamic_cast<BinaryOpNode *>(AddEqual(const_cast<VariableNode *>(target_base_diff_node),
4806                                                             AddMinus(AddVariable(pac_target_symb_id),
4807                                                                      AddVariable(pac_target_symb_id, -1)))), -1);
4808           neqs++;
4809         }
4810 
4811       map<int, VariableNode *> target_aux_var_to_add;
4812       const VariableNode *last_aux_var = target_base_diff_node;
4813       for (int i = 1; i <= pac_max_lag_m - 1; i++, neqs++)
4814         {
4815           expr_t this_diff_node = AddDiff(AddVariable(pac_target_symb_id, i));
4816           int symb_id = symbol_table.addDiffLeadAuxiliaryVar(this_diff_node->idx, this_diff_node,
4817                                                              last_aux_var->symb_id, last_aux_var->lag);
4818           VariableNode *current_aux_var = AddVariable(symb_id);
4819           addEquation(dynamic_cast<BinaryOpNode *>(AddEqual(current_aux_var,
4820                                                             AddVariable(last_aux_var->symb_id, 1))), -1);
4821           last_aux_var = current_aux_var;
4822           target_aux_var_to_add[i] = current_aux_var;
4823         }
4824 
4825       expr_t fs = Zero;
4826       for (int k = 1; k <= pac_max_lag_m - 1; k++)
4827         {
4828           expr_t ssum = Zero;
4829           for (int j = k+1; j <= pac_max_lag_m; j++)
4830             {
4831               int alpha_j_symb_id = -1;
4832               string varname = "mce_alpha_" + append_to_name + "_" + to_string(j);
4833               try
4834                 {
4835                   alpha_j_symb_id = symbol_table.getID(varname);
4836                 }
4837               catch (SymbolTable::UnknownSymbolNameException &e)
4838                 {
4839                   alpha_j_symb_id = symbol_table.addSymbol(varname, SymbolType::parameter);
4840                 }
4841               ssum = AddPlus(ssum,
4842                              AddTimes(AddVariable(alpha_j_symb_id), AddPower(beta, AddPossiblyNegativeConstant(j))));
4843             }
4844           fs = AddPlus(fs, AddTimes(ssum, target_aux_var_to_add[k]));
4845         }
4846       addEquation(AddEqual(AddVariable(mce_z1_symb_id),
4847                            AddMinus(AddTimes(A, AddMinus(const_cast<VariableNode *>(target_base_diff_node), fs)), fp)), -1);
4848       neqs++;
4849       pac_expectation_substitution[{name, eqtag}] = AddVariable(mce_z1_symb_id);
4850     }
4851   cout << "Pac Model Consistent Expectation: added " << neqs << " auxiliary variables and equations." << endl;
4852 }
4853 
4854 void
fillPacModelInfo(const string & pac_model_name,vector<int> lhs,int max_lag,string aux_model_type,const map<pair<string,string>,pair<string,int>> & eqtag_and_lag,const vector<bool> & nonstationary,expr_t growth)4855 DynamicModel::fillPacModelInfo(const string &pac_model_name,
4856                                vector<int> lhs,
4857                                int max_lag,
4858                                string aux_model_type,
4859                                const map<pair<string, string>, pair<string, int>> &eqtag_and_lag,
4860                                const vector<bool> &nonstationary,
4861                                expr_t growth)
4862 {
4863   pac_eqtag_and_lag.insert(eqtag_and_lag.begin(), eqtag_and_lag.end());
4864 
4865   bool stationary_vars_present = any_of(nonstationary.begin(), nonstationary.end(), logical_not<bool>());
4866   bool nonstationary_vars_present = any_of(nonstationary.begin(), nonstationary.end(), [](bool b) { return b; }); // FIXME: use std::identity instead of an anonymous function when we upgrade to C++20
4867 
4868   int growth_param_index = -1;
4869   if (growth)
4870     growth_param_index = symbol_table.addSymbol(pac_model_name
4871                                                 +"_pac_growth_neutrality_correction",
4872                                                 SymbolType::parameter);
4873 
4874   for (auto pac_models_and_eqtags : pac_eqtag_and_lag)
4875     {
4876       if (pac_models_and_eqtags.first.first != pac_model_name)
4877         continue;
4878       string eqtag = pac_models_and_eqtags.first.second;
4879       string standard_eqtag = pac_models_and_eqtags.second.first;
4880       expr_t subExpr = Zero;
4881       if (stationary_vars_present)
4882         for (int i = 1; i < max_lag + 1; i++)
4883           for (auto lhsit : lhs)
4884             {
4885               stringstream param_name_h0;
4886               param_name_h0 << "h0_" << pac_model_name
4887                             << "_" << standard_eqtag
4888                             << "_var_" << symbol_table.getName(lhsit)
4889                             << "_lag_" << i;
4890               int new_param_symb_id = symbol_table.addSymbol(param_name_h0.str(), SymbolType::parameter);
4891               pac_h0_indices[{pac_model_name, standard_eqtag}].push_back(new_param_symb_id);
4892               subExpr = AddPlus(subExpr,
4893                                 AddTimes(AddVariable(new_param_symb_id),
4894                                          AddVariable(lhsit, -i)));
4895             }
4896 
4897       if (nonstationary_vars_present)
4898         for (int i = 1; i < max_lag + 1; i++)
4899           for (auto lhsit : lhs)
4900             {
4901               stringstream param_name_h1;
4902               param_name_h1 << "h1_" << pac_model_name
4903                             << "_" << standard_eqtag
4904                             << "_var_" << symbol_table.getName(lhsit)
4905                             << "_lag_" << i;
4906               int new_param_symb_id = symbol_table.addSymbol(param_name_h1.str(), SymbolType::parameter);
4907               pac_h1_indices[{pac_model_name, standard_eqtag}].push_back(new_param_symb_id);
4908               subExpr = AddPlus(subExpr,
4909                                 AddTimes(AddVariable(new_param_symb_id),
4910                                          AddVariable(lhsit, -i)));
4911             }
4912 
4913       if (growth)
4914         subExpr = AddPlus(subExpr,
4915                           AddTimes(AddVariable(growth_param_index), growth));
4916 
4917       pac_expectation_substitution[{pac_model_name, eqtag}] = subExpr;
4918     }
4919   pac_model_info[pac_model_name] = {move(lhs), growth_param_index, move(aux_model_type)};
4920 }
4921 
4922 void
substitutePacExpectation(const string & pac_model_name)4923 DynamicModel::substitutePacExpectation(const string &pac_model_name)
4924 {
4925   for (auto &it : pac_expectation_substitution)
4926     if (it.first.first == pac_model_name)
4927       for (auto &equation : equations)
4928         for (auto & [tagged_eq, tag_pair] : equation_tags)
4929           if (tagged_eq == (&equation - &equations[0])
4930               && tag_pair.first == "name" && tag_pair.second == it.first.second)
4931             {
4932               auto substeq = dynamic_cast<BinaryOpNode *>(equation->substitutePacExpectation(pac_model_name, it.second));
4933               assert(substeq);
4934               equation = substeq;
4935               break;
4936             }
4937 }
4938 
4939 void
computingPass(bool jacobianExo,int derivsOrder,int paramsDerivsOrder,const eval_context_t & eval_context,bool no_tmp_terms,bool block,bool use_dll,bool bytecode,bool linear_decomposition)4940 DynamicModel::computingPass(bool jacobianExo, int derivsOrder, int paramsDerivsOrder,
4941                             const eval_context_t &eval_context, bool no_tmp_terms, bool block, bool use_dll,
4942                             bool bytecode, bool linear_decomposition)
4943 {
4944   assert(jacobianExo || (derivsOrder < 2 && paramsDerivsOrder == 0));
4945 
4946   initializeVariablesAndEquations();
4947 
4948   // Prepare for derivation
4949   computeDerivIDs();
4950 
4951   // Computes dynamic jacobian columns, must be done after computeDerivIDs()
4952   computeDynJacobianCols(jacobianExo);
4953 
4954   // Compute derivatives w.r. to all endogenous, and possibly exogenous and exogenous deterministic
4955   set<int> vars;
4956   for (auto &it : deriv_id_table)
4957     {
4958       SymbolType type = symbol_table.getType(it.first.first);
4959       if (type == SymbolType::endogenous || (jacobianExo && (type == SymbolType::exogenous || type == SymbolType::exogenousDet)))
4960         vars.insert(it.second);
4961     }
4962 
4963   // Launch computations
4964   cout << "Computing " << (linear_decomposition ? "nonlinear " : "")
4965        << "dynamic model derivatives (order " << derivsOrder << ")." << endl;
4966 
4967   computeDerivatives(derivsOrder, vars);
4968 
4969   if (derivsOrder > 1)
4970     for (const auto &[indices, d2] : derivatives[2])
4971       nonzero_hessian_eqs.insert(indices[0]);
4972 
4973   if (paramsDerivsOrder > 0)
4974     {
4975       cout << "Computing dynamic model derivatives w.r.t. parameters (order " << paramsDerivsOrder << ")." << endl;
4976       computeParamsDerivatives(paramsDerivsOrder);
4977     }
4978 
4979   jacob_map_t contemporaneous_jacobian, static_jacobian;
4980   map<tuple<int, int, int>, expr_t> first_order_endo_derivatives;
4981   // for each block contains pair<Size, Feddback_variable>
4982   vector<pair<int, int>> blocks;
4983   vector<unsigned int> n_static, n_forward, n_backward, n_mixed;
4984 
4985   if (linear_decomposition)
4986     {
4987       first_order_endo_derivatives = collect_first_order_derivatives_endogenous();
4988       is_equation_linear = equationLinear(first_order_endo_derivatives);
4989 
4990       evaluateAndReduceJacobian(eval_context, contemporaneous_jacobian, static_jacobian, dynamic_jacobian, cutoff, false);
4991 
4992       if (!computeNaturalNormalization())
4993         computeNonSingularNormalization(contemporaneous_jacobian, cutoff, static_jacobian, dynamic_jacobian);
4994 
4995       lag_lead_vector_t equation_lag_lead, variable_lag_lead;
4996 
4997       blocks = select_non_linear_equations_and_variables(is_equation_linear, dynamic_jacobian, equation_reordered, variable_reordered,
4998                                                          inv_equation_reordered, inv_variable_reordered,
4999                                                          equation_lag_lead, variable_lag_lead,
5000                                                          n_static, n_forward, n_backward, n_mixed);
5001 
5002       equation_type_and_normalized_equation = equationTypeDetermination(first_order_endo_derivatives, variable_reordered, equation_reordered, 0);
5003       prologue = 0;
5004       epilogue = 0;
5005 
5006       block_type_firstequation_size_mfs = reduceBlocksAndTypeDetermination(dynamic_jacobian, blocks, equation_type_and_normalized_equation, variable_reordered, equation_reordered, n_static, n_forward, n_backward, n_mixed, block_col_type, linear_decomposition);
5007 
5008       computeChainRuleJacobian(blocks_derivatives);
5009 
5010       blocks_linear = BlockLinear(blocks_derivatives, variable_reordered);
5011 
5012       collect_block_first_order_derivatives();
5013 
5014       collectBlockVariables();
5015 
5016       global_temporary_terms = true;
5017       if (!no_tmp_terms)
5018         computeTemporaryTermsOrdered();
5019     }
5020 
5021   if (block)
5022     {
5023       evaluateAndReduceJacobian(eval_context, contemporaneous_jacobian, static_jacobian, dynamic_jacobian, cutoff, false);
5024 
5025       computeNonSingularNormalization(contemporaneous_jacobian, cutoff, static_jacobian, dynamic_jacobian);
5026 
5027       computePrologueAndEpilogue(static_jacobian, equation_reordered, variable_reordered);
5028 
5029       first_order_endo_derivatives = collect_first_order_derivatives_endogenous();
5030 
5031       equation_type_and_normalized_equation = equationTypeDetermination(first_order_endo_derivatives, variable_reordered, equation_reordered, mfs);
5032 
5033       cout << "Finding the optimal block decomposition of the model ..." << endl;
5034 
5035       lag_lead_vector_t equation_lag_lead, variable_lag_lead;
5036 
5037       computeBlockDecompositionAndFeedbackVariablesForEachBlock(static_jacobian, dynamic_jacobian, equation_reordered, variable_reordered, blocks, equation_type_and_normalized_equation, false, true, mfs, inv_equation_reordered, inv_variable_reordered, equation_lag_lead, variable_lag_lead, n_static, n_forward, n_backward, n_mixed);
5038 
5039       block_type_firstequation_size_mfs = reduceBlocksAndTypeDetermination(dynamic_jacobian, blocks, equation_type_and_normalized_equation, variable_reordered, equation_reordered, n_static, n_forward, n_backward, n_mixed, block_col_type, linear_decomposition);
5040 
5041       printBlockDecomposition(blocks);
5042 
5043       computeChainRuleJacobian(blocks_derivatives);
5044 
5045       blocks_linear = BlockLinear(blocks_derivatives, variable_reordered);
5046 
5047       collect_block_first_order_derivatives();
5048 
5049       collectBlockVariables();
5050 
5051       global_temporary_terms = true;
5052       if (!no_tmp_terms)
5053         computeTemporaryTermsOrdered();
5054       int k = 0;
5055       equation_block.resize(equations.size());
5056       variable_block_lead_lag = vector<tuple<int, int, int>>(equations.size());
5057       for (unsigned int i = 0; i < getNbBlocks(); i++)
5058         {
5059           for (unsigned int j = 0; j < getBlockSize(i); j++)
5060             {
5061               equation_block[equation_reordered[k]] = i;
5062               int l = variable_reordered[k];
5063               variable_block_lead_lag[l] = { i, variable_lag_lead[l].first, variable_lag_lead[l].second };
5064               k++;
5065             }
5066         }
5067     }
5068   else
5069     {
5070       computeTemporaryTerms(!use_dll, no_tmp_terms);
5071       if (bytecode && !no_tmp_terms)
5072         computeTemporaryTermsMapping();
5073 
5074       /* Must be called after computeTemporaryTerms(), because it depends on
5075          temporary_terms_mlv to be filled */
5076       if (paramsDerivsOrder > 0 && !no_tmp_terms)
5077         computeParamsDerivativesTemporaryTerms();
5078     }
5079 }
5080 
5081 void
computeXrefs()5082 DynamicModel::computeXrefs()
5083 {
5084   int i = 0;
5085   for (auto &equation : equations)
5086     {
5087       ExprNode::EquationInfo ei;
5088       equation->computeXrefs(ei);
5089       xrefs[i++] = ei;
5090     }
5091 
5092   i = 0;
5093   for (auto it = xrefs.begin(); it != xrefs.end(); ++it, i++)
5094     {
5095       computeRevXref(xref_param, it->second.param, i);
5096       computeRevXref(xref_endo, it->second.endo, i);
5097       computeRevXref(xref_exo, it->second.exo, i);
5098       computeRevXref(xref_exo_det, it->second.exo_det, i);
5099     }
5100 }
5101 
5102 void
computeRevXref(map<pair<int,int>,set<int>> & xrefset,const set<pair<int,int>> & eiref,int eqn)5103 DynamicModel::computeRevXref(map<pair<int, int>, set<int>> &xrefset, const set<pair<int, int>> &eiref, int eqn)
5104 {
5105   for (const auto &it : eiref)
5106     {
5107       set<int> eq;
5108       if (xrefset.find(it) != xrefset.end())
5109         eq = xrefset[it];
5110       eq.insert(eqn);
5111       xrefset[it] = eq;
5112     }
5113 }
5114 
5115 void
writeXrefs(ostream & output) const5116 DynamicModel::writeXrefs(ostream &output) const
5117 {
5118   output << "M_.xref1.param = cell(1, M_.eq_nbr);" << endl
5119          << "M_.xref1.endo = cell(1, M_.eq_nbr);" << endl
5120          << "M_.xref1.exo = cell(1, M_.eq_nbr);" << endl
5121          << "M_.xref1.exo_det = cell(1, M_.eq_nbr);" << endl;
5122   int i = 1;
5123   for (auto it = xrefs.begin(); it != xrefs.end(); ++it, i++)
5124     {
5125       output << "M_.xref1.param{" << i << "} = [ ";
5126       for (const auto &it1 : it->second.param)
5127         output << symbol_table.getTypeSpecificID(it1.first) + 1 << " ";
5128       output << "];" << endl;
5129 
5130       output << "M_.xref1.endo{" << i << "} = [ ";
5131       for (const auto &it1 : it->second.endo)
5132         output << "struct('id', " << symbol_table.getTypeSpecificID(it1.first) + 1 << ", 'shift', " << it1.second << ");";
5133       output << "];" << endl;
5134 
5135       output << "M_.xref1.exo{" << i << "} = [ ";
5136       for (const auto &it1 : it->second.exo)
5137         output << "struct('id', " << symbol_table.getTypeSpecificID(it1.first) + 1 << ", 'shift', " << it1.second << ");";
5138       output << "];" << endl;
5139 
5140       output << "M_.xref1.exo_det{" << i << "} = [ ";
5141       for (const auto &it1 : it->second.exo_det)
5142         output << "struct('id', " << symbol_table.getTypeSpecificID(it1.first) + 1 << ", 'shift', " << it1.second << ");";
5143       output << "];" << endl;
5144     }
5145 
5146   output << "M_.xref2.param = cell(1, M_.param_nbr);" << endl
5147          << "M_.xref2.endo = cell(1, M_.endo_nbr);" << endl
5148          << "M_.xref2.exo = cell(1, M_.exo_nbr);" << endl
5149          << "M_.xref2.exo_det = cell(1, M_.exo_det_nbr);" << endl;
5150   writeRevXrefs(output, xref_param, "param");
5151   writeRevXrefs(output, xref_endo, "endo");
5152   writeRevXrefs(output, xref_exo, "exo");
5153   writeRevXrefs(output, xref_exo_det, "exo_det");
5154 }
5155 
5156 void
writeRevXrefs(ostream & output,const map<pair<int,int>,set<int>> & xrefmap,const string & type) const5157 DynamicModel::writeRevXrefs(ostream &output, const map<pair<int, int>, set<int>> &xrefmap, const string &type) const
5158 {
5159   int last_tsid = -1;
5160   for (const auto &it : xrefmap)
5161     {
5162       int tsid = symbol_table.getTypeSpecificID(it.first.first) + 1;
5163       output << "M_.xref2." << type << "{" << tsid << "} = [ ";
5164       if (last_tsid == tsid)
5165         output << "M_.xref2." << type << "{" << tsid << "}; ";
5166       else
5167         last_tsid = tsid;
5168 
5169       for (const auto &it1 : it.second)
5170         if (type == "param")
5171           output << it1 + 1 << " ";
5172         else
5173           output << "struct('shift', " << it.first.second << ", 'eq', " << it1+1 << ");";
5174       output << "];" << endl;
5175     }
5176 }
5177 
5178 map<tuple<int, int, int, int, int>, int>
get_Derivatives(int block)5179 DynamicModel::get_Derivatives(int block)
5180 {
5181   int max_lag, max_lead;
5182   map<tuple<int, int, int, int, int>, int> Derivatives;
5183   BlockSimulationType simulation_type = getBlockSimulationType(block);
5184   if (simulation_type == EVALUATE_BACKWARD || simulation_type == EVALUATE_FORWARD)
5185     {
5186       max_lag = 1;
5187       max_lead = 1;
5188       setBlockLeadLag(block, max_lag, max_lead);
5189     }
5190   else
5191     {
5192       max_lag = getBlockMaxLag(block);
5193       max_lead = getBlockMaxLead(block);
5194     }
5195   int block_size = getBlockSize(block);
5196   int block_nb_recursive = block_size - getBlockMfs(block);
5197   for (int lag = -max_lag; lag <= max_lead; lag++)
5198     {
5199       for (int eq = 0; eq < block_size; eq++)
5200         {
5201           int eqr = getBlockEquationID(block, eq);
5202           for (int var = 0; var < block_size; var++)
5203             {
5204               int varr = getBlockVariableID(block, var);
5205               if (dynamic_jacobian.find({ lag, eqr, varr }) != dynamic_jacobian.end())
5206                 {
5207                   bool OK = true;
5208                   if (auto its = Derivatives.find({ lag, eq, var, eqr, varr });
5209                       its != Derivatives.end() && its->second == 2)
5210                     OK = false;
5211 
5212                   if (OK)
5213                     {
5214                       if (getBlockEquationType(block, eq) == E_EVALUATE_S && eq < block_nb_recursive)
5215                         //It's a normalized equation, we have to recompute the derivative using chain rule derivative function
5216                         Derivatives[{ lag, eq, var, eqr, varr }] = 1;
5217                       else
5218                         //It's a feedback equation we can use the derivatives
5219                         Derivatives[{ lag, eq, var, eqr, varr }] = 0;
5220                     }
5221                   if (var < block_nb_recursive)
5222                     {
5223                       int eqs = getBlockEquationID(block, var);
5224                       for (int vars = block_nb_recursive; vars < block_size; vars++)
5225                         {
5226                           int varrs = getBlockVariableID(block, vars);
5227                           //A new derivative needs to be computed using the chain rule derivative function (a feedback variable appears in a recursive equation)
5228                           if (Derivatives.find({ lag, var, vars, eqs, varrs }) != Derivatives.end())
5229                             Derivatives[{ lag, eq, vars, eqr, varrs }] = 2;
5230                         }
5231                     }
5232                 }
5233             }
5234         }
5235     }
5236   return Derivatives;
5237 }
5238 
5239 void
computeChainRuleJacobian(blocks_derivatives_t & blocks_endo_derivatives)5240 DynamicModel::computeChainRuleJacobian(blocks_derivatives_t &blocks_endo_derivatives)
5241 {
5242   map<int, expr_t> recursive_variables;
5243   unsigned int nb_blocks = getNbBlocks();
5244   blocks_endo_derivatives = blocks_derivatives_t(nb_blocks);
5245   for (unsigned int block = 0; block < nb_blocks; block++)
5246     {
5247       block_derivatives_equation_variable_laglead_nodeid_t tmp_derivatives;
5248       recursive_variables.clear();
5249       int block_size = getBlockSize(block);
5250       int block_nb_mfs = getBlockMfs(block);
5251       int block_nb_recursives = block_size - block_nb_mfs;
5252       blocks_endo_derivatives.push_back(block_derivatives_equation_variable_laglead_nodeid_t(0));
5253       for (int i = 0; i < block_nb_recursives; i++)
5254         {
5255           if (getBlockEquationType(block, i) == E_EVALUATE_S)
5256             recursive_variables[getDerivID(symbol_table.getID(SymbolType::endogenous, getBlockVariableID(block, i)), 0)] = getBlockEquationRenormalizedExpr(block, i);
5257           else
5258             recursive_variables[getDerivID(symbol_table.getID(SymbolType::endogenous, getBlockVariableID(block, i)), 0)] = getBlockEquationExpr(block, i);
5259         }
5260       auto Derivatives = get_Derivatives(block);
5261       for (const auto &it : Derivatives)
5262         {
5263           int Deriv_type = it.second;
5264           auto [lag, eq, var, eqr, varr] = it.first;
5265           if (Deriv_type == 0)
5266             first_chain_rule_derivatives[{ eqr, varr, lag }] = derivatives[1][{ eqr, getDerivID(symbol_table.getID(SymbolType::endogenous, varr), lag) }];
5267           else if (Deriv_type == 1)
5268             first_chain_rule_derivatives[{ eqr, varr, lag }] = (equation_type_and_normalized_equation[eqr].second)->getChainRuleDerivative(getDerivID(symbol_table.getID(SymbolType::endogenous, varr), lag), recursive_variables);
5269           else if (Deriv_type == 2)
5270             {
5271               if (getBlockEquationType(block, eq) == E_EVALUATE_S && eq < block_nb_recursives)
5272                 first_chain_rule_derivatives[{ eqr, varr, lag }] = (equation_type_and_normalized_equation[eqr].second)->getChainRuleDerivative(getDerivID(symbol_table.getID(SymbolType::endogenous, varr), lag), recursive_variables);
5273               else
5274                 first_chain_rule_derivatives[{ eqr, varr, lag }] = equations[eqr]->getChainRuleDerivative(getDerivID(symbol_table.getID(SymbolType::endogenous, varr), lag), recursive_variables);
5275             }
5276           tmp_derivatives.emplace_back(eq, var, lag, first_chain_rule_derivatives[{ eqr, varr, lag }]);
5277         }
5278       blocks_endo_derivatives[block] = tmp_derivatives;
5279     }
5280 }
5281 
5282 void
collect_block_first_order_derivatives()5283 DynamicModel::collect_block_first_order_derivatives()
5284 {
5285   //! vector for an equation or a variable indicates the block number
5286   vector<int> equation_2_block(equation_reordered.size()), variable_2_block(variable_reordered.size());
5287   unsigned int nb_blocks = getNbBlocks();
5288   for (unsigned int block = 0; block < nb_blocks; block++)
5289     {
5290       unsigned int block_size = getBlockSize(block);
5291       for (unsigned int i = 0; i < block_size; i++)
5292         {
5293           equation_2_block[getBlockEquationID(block, i)] = block;
5294           variable_2_block[getBlockVariableID(block, i)] = block;
5295         }
5296     }
5297   other_endo_block = vector<lag_var_t>(nb_blocks);
5298   exo_block = vector<lag_var_t>(nb_blocks);
5299   exo_det_block = vector<lag_var_t>(nb_blocks);
5300   derivative_endo = vector<derivative_t>(nb_blocks);
5301   derivative_other_endo = vector<derivative_t>(nb_blocks);
5302   derivative_exo = vector<derivative_t>(nb_blocks);
5303   derivative_exo_det = vector<derivative_t>(nb_blocks);
5304   endo_max_leadlag_block = vector<pair<int, int>>(nb_blocks, { 0, 0 });
5305   other_endo_max_leadlag_block = vector<pair<int, int>>(nb_blocks, { 0, 0 });
5306   exo_max_leadlag_block = vector<pair<int, int>>(nb_blocks, { 0, 0 });
5307   exo_det_max_leadlag_block = vector<pair<int, int>>(nb_blocks, { 0, 0 });
5308   max_leadlag_block = vector<pair<int, int>>(nb_blocks, { 0, 0 });
5309   for (auto & [indices, d1] : derivatives[1])
5310     {
5311       int eq = indices[0];
5312       int var = symbol_table.getTypeSpecificID(getSymbIDByDerivID(indices[1]));
5313       int lag = getLagByDerivID(indices[1]);
5314       int block_eq = equation_2_block[eq];
5315       int block_var = 0;
5316       derivative_t tmp_derivative;
5317       lag_var_t lag_var;
5318       switch (getTypeByDerivID(indices[1]))
5319         {
5320         case SymbolType::endogenous:
5321           block_var = variable_2_block[var];
5322           if (block_eq == block_var)
5323             {
5324               if (lag < 0 && lag < -endo_max_leadlag_block[block_eq].first)
5325                 endo_max_leadlag_block[block_eq] = { -lag, endo_max_leadlag_block[block_eq].second };
5326               if (lag > 0 && lag > endo_max_leadlag_block[block_eq].second)
5327                 endo_max_leadlag_block[block_eq] = { endo_max_leadlag_block[block_eq].first, lag };
5328               tmp_derivative = derivative_endo[block_eq];
5329               tmp_derivative[{ lag, eq, var }] = derivatives[1][{ eq, getDerivID(symbol_table.getID(SymbolType::endogenous, var), lag) }];
5330               derivative_endo[block_eq] = tmp_derivative;
5331             }
5332           else
5333             {
5334               if (lag < 0 && lag < -other_endo_max_leadlag_block[block_eq].first)
5335                 other_endo_max_leadlag_block[block_eq] = { -lag, other_endo_max_leadlag_block[block_eq].second };
5336               if (lag > 0 && lag > other_endo_max_leadlag_block[block_eq].second)
5337                 other_endo_max_leadlag_block[block_eq] = { other_endo_max_leadlag_block[block_eq].first, lag };
5338               tmp_derivative = derivative_other_endo[block_eq];
5339 
5340               if (auto it = block_other_endo_index.find(block_eq);
5341                   it == block_other_endo_index.end())
5342                 block_other_endo_index[block_eq][var] = 0;
5343               else
5344                 if (auto it1 = it->second.find(var);
5345                     it1 == it->second.end())
5346                   {
5347                     int size = block_other_endo_index[block_eq].size();
5348                     block_other_endo_index[block_eq][var] = size;
5349                   }
5350 
5351               tmp_derivative[{ lag, eq, var }] = derivatives[1][{ eq, getDerivID(symbol_table.getID(SymbolType::endogenous, var), lag) }];
5352               derivative_other_endo[block_eq] = tmp_derivative;
5353               lag_var = other_endo_block[block_eq];
5354               if (lag_var.find(lag) == lag_var.end())
5355                 lag_var[lag].clear();
5356               lag_var[lag].insert(var);
5357               other_endo_block[block_eq] = lag_var;
5358             }
5359           break;
5360         case SymbolType::exogenous:
5361           if (lag < 0 && lag < -exo_max_leadlag_block[block_eq].first)
5362             exo_max_leadlag_block[block_eq] = { -lag, exo_max_leadlag_block[block_eq].second };
5363           if (lag > 0 && lag > exo_max_leadlag_block[block_eq].second)
5364             exo_max_leadlag_block[block_eq] = { exo_max_leadlag_block[block_eq].first, lag };
5365           tmp_derivative = derivative_exo[block_eq];
5366 
5367           if (auto it = block_exo_index.find(block_eq);
5368               it == block_exo_index.end())
5369             block_exo_index[block_eq][var] = 0;
5370           else
5371             if (auto it1 = it->second.find(var);
5372                 it1 == it->second.end())
5373               {
5374                 int size = block_exo_index[block_eq].size();
5375                 block_exo_index[block_eq][var] = size;
5376               }
5377 
5378           tmp_derivative[{ lag, eq, var }] = derivatives[1][{ eq, getDerivID(symbol_table.getID(SymbolType::exogenous, var), lag) }];
5379           derivative_exo[block_eq] = tmp_derivative;
5380           lag_var = exo_block[block_eq];
5381           if (lag_var.find(lag) == lag_var.end())
5382             lag_var[lag].clear();
5383           lag_var[lag].insert(var);
5384           exo_block[block_eq] = lag_var;
5385           break;
5386         case SymbolType::exogenousDet:
5387           if (lag < 0 && lag < -exo_det_max_leadlag_block[block_eq].first)
5388             exo_det_max_leadlag_block[block_eq] = { -lag, exo_det_max_leadlag_block[block_eq].second };
5389           if (lag > 0 && lag > exo_det_max_leadlag_block[block_eq].second)
5390             exo_det_max_leadlag_block[block_eq] = { exo_det_max_leadlag_block[block_eq].first, lag };
5391           tmp_derivative = derivative_exo_det[block_eq];
5392 
5393           if (auto it = block_det_exo_index.find(block_eq);
5394               it == block_det_exo_index.end())
5395             block_det_exo_index[block_eq][var] = 0;
5396           else
5397             if (auto it1 = it->second.find(var);
5398                 it1 == it->second.end())
5399               {
5400                 int size = block_det_exo_index[block_eq].size();
5401                 block_det_exo_index[block_eq][var] = size;
5402               }
5403 
5404           tmp_derivative[{ lag, eq, var }] = derivatives[1][{ eq, getDerivID(symbol_table.getID(SymbolType::exogenous, var), lag) }];
5405           derivative_exo_det[block_eq] = tmp_derivative;
5406           lag_var = exo_det_block[block_eq];
5407           if (lag_var.find(lag) == lag_var.end())
5408             lag_var[lag].clear();
5409           lag_var[lag].insert(var);
5410           exo_det_block[block_eq] = lag_var;
5411           break;
5412         default:
5413           break;
5414         }
5415       if (lag < 0 && lag < -max_leadlag_block[block_eq].first)
5416         max_leadlag_block[block_eq] = { -lag, max_leadlag_block[block_eq].second };
5417       if (lag > 0 && lag > max_leadlag_block[block_eq].second)
5418         max_leadlag_block[block_eq] = { max_leadlag_block[block_eq].first, lag };
5419     }
5420 }
5421 
5422 void
collectBlockVariables()5423 DynamicModel::collectBlockVariables()
5424 {
5425   for (unsigned int block = 0; block < getNbBlocks(); block++)
5426     {
5427       int prev_var = -1;
5428       int prev_lag = -999999999;
5429       int count_col_exo = 0;
5430       var_t tmp_var_exo;
5431       for (const auto &it : exo_block[block])
5432         {
5433           int lag = it.first;
5434           for (int var : it.second)
5435             {
5436               tmp_var_exo.insert(var);
5437               if (prev_var != var || prev_lag != lag)
5438                 {
5439                   prev_var = var;
5440                   prev_lag = lag;
5441                   count_col_exo++;
5442                 }
5443             }
5444         }
5445       block_var_exo.emplace_back(tmp_var_exo, count_col_exo);
5446     }
5447 }
5448 
5449 void
writeDynamicFile(const string & basename,bool block,bool linear_decomposition,bool bytecode,bool use_dll,const string & mexext,const filesystem::path & matlabroot,const filesystem::path & dynareroot,bool julia) const5450 DynamicModel::writeDynamicFile(const string &basename, bool block, bool linear_decomposition, bool bytecode, bool use_dll, const string &mexext, const filesystem::path &matlabroot, const filesystem::path &dynareroot, bool julia) const
5451 {
5452   if (block && bytecode)
5453     writeModelEquationsCode_Block(basename, map_idx, linear_decomposition);
5454   else if (!block && bytecode)
5455     {
5456       if (linear_decomposition)
5457         writeModelEquationsCode_Block(basename, map_idx, linear_decomposition);
5458       writeModelEquationsCode(basename, map_idx);
5459     }
5460   else if (block && !bytecode)
5461     writeSparseDynamicMFile(basename);
5462   else if (use_dll)
5463     {
5464       writeDynamicCFile(basename);
5465       compileDll(basename, "dynamic", mexext, matlabroot, dynareroot);
5466     }
5467   else if (julia)
5468     writeDynamicJuliaFile(basename);
5469   else
5470     writeDynamicMFile(basename);
5471   writeSetAuxiliaryVariables(basename, julia);
5472 }
5473 
5474 void
writeSetAuxiliaryVariables(const string & basename,bool julia) const5475 DynamicModel::writeSetAuxiliaryVariables(const string &basename, bool julia) const
5476 {
5477   ostringstream output_func_body;
5478   writeAuxVarRecursiveDefinitions(output_func_body, ExprNodeOutputType::matlabDseries);
5479 
5480   if (output_func_body.str().empty())
5481     return;
5482 
5483   string func_name = julia ? basename + "_dynamic_set_auxiliary_series" : "dynamic_set_auxiliary_series";
5484   string filename = julia ? func_name + ".jl" : packageDir(basename) + "/" + func_name + ".m";
5485   string comment = julia ? "#" : "%";
5486 
5487   ofstream output;
5488   output.open(filename, ios::out | ios::binary);
5489   if (!output.is_open())
5490     {
5491       cerr << "ERROR: Can't open file " << filename << " for writing" << endl;
5492       exit(EXIT_FAILURE);
5493     }
5494 
5495   output << "function ds = " << func_name + "(ds, params)" << endl
5496          << comment << endl
5497          << comment << " Status : Computes Auxiliary variables of the dynamic model and returns a dseries" << endl
5498          << comment << endl
5499          << comment << " Warning : this file is generated automatically by Dynare" << endl
5500          << comment << "           from model file (.mod)" << endl << endl
5501          << output_func_body.str();
5502 
5503   output.close();
5504 }
5505 
5506 void
writeAuxVarRecursiveDefinitions(ostream & output,ExprNodeOutputType output_type) const5507 DynamicModel::writeAuxVarRecursiveDefinitions(ostream &output, ExprNodeOutputType output_type) const
5508 {
5509   deriv_node_temp_terms_t tef_terms;
5510   temporary_terms_t temporary_terms;
5511   temporary_terms_idxs_t temporary_terms_idxs;
5512   for (auto aux_eq : aux_equations)
5513     if (auto aux_eq2 = dynamic_cast<ExprNode *>(aux_eq);
5514         aux_eq2->containsExternalFunction())
5515       aux_eq2->writeExternalFunctionOutput(output, output_type, temporary_terms,
5516                                            temporary_terms_idxs, tef_terms);
5517   for (auto aux_eq : aux_equations)
5518     {
5519       dynamic_cast<ExprNode *>(aux_eq)->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms);
5520       output << ";" << endl;
5521     }
5522 }
5523 
5524 void
clearEquations()5525 DynamicModel::clearEquations()
5526 {
5527   equations.clear();
5528   equations_lineno.clear();
5529   equation_tags.clear();
5530   equation_tags_xref.clear();
5531 }
5532 
5533 void
replaceMyEquations(DynamicModel & dynamic_model) const5534 DynamicModel::replaceMyEquations(DynamicModel &dynamic_model) const
5535 {
5536   dynamic_model.clearEquations();
5537 
5538   for (size_t i = 0; i < equations.size(); i++)
5539     dynamic_model.addEquation(equations[i]->clone(dynamic_model), equations_lineno[i]);
5540 
5541   dynamic_model.equation_tags = equation_tags;
5542   dynamic_model.equation_tags_xref = equation_tags_xref;
5543 }
5544 
5545 void
computeRamseyPolicyFOCs(const StaticModel & static_model)5546 DynamicModel::computeRamseyPolicyFOCs(const StaticModel &static_model)
5547 {
5548   // Add aux LM to constraints in equations
5549   // equation[i]->lhs = rhs becomes equation[i]->MULT_(i+1)*(lhs-rhs) = 0
5550   int i;
5551   for (i = 0; i < static_cast<int>(equations.size()); i++)
5552     {
5553       auto substeq = dynamic_cast<BinaryOpNode *>(equations[i]->addMultipliersToConstraints(i));
5554       assert(substeq);
5555       equations[i] = substeq;
5556     }
5557   cout << "Ramsey Problem: added " << i << " Multipliers." << endl;
5558 
5559   // Add Planner Objective to equations so that it appears in Lagrangian
5560   assert(static_model.equations.size() == 1);
5561   addEquation(static_model.equations[0]->clone(*this), -1);
5562 
5563   // Get max endo lead and max endo lag
5564   set<pair<int, int>> dynvars;
5565   int max_eq_lead = 0;
5566   int max_eq_lag = 0;
5567   for (auto &equation : equations)
5568     equation->collectDynamicVariables(SymbolType::endogenous, dynvars);
5569 
5570   for (const auto &[symb_id, lag] : dynvars)
5571     {
5572       if (max_eq_lead < lag)
5573         max_eq_lead = lag;
5574       else if (-max_eq_lag > lag)
5575         max_eq_lag = -lag;
5576     }
5577 
5578   // Get Discount Factor
5579   assert(symbol_table.exists("optimal_policy_discount_factor"));
5580   int symb_id = symbol_table.getID("optimal_policy_discount_factor");
5581   assert(symbol_table.getType(symb_id) == SymbolType::parameter);
5582   expr_t discount_factor_node = AddVariable(symb_id, 0);
5583 
5584   // Create (modified) Lagrangian (so that we can take the derivative once at time t)
5585   expr_t lagrangian = Zero;
5586   for (i = 0; i < static_cast<int>(equations.size()); i++)
5587     for (int lag = -max_eq_lag; lag <= max_eq_lead; lag++)
5588       {
5589         expr_t dfpower = nullptr;
5590         stringstream lagstream;
5591         lagstream << abs(lag);
5592         if (lag < 0)
5593           dfpower = AddNonNegativeConstant(lagstream.str());
5594         else if (lag == 0)
5595           dfpower = Zero;
5596         else
5597           dfpower = AddMinus(Zero, AddNonNegativeConstant(lagstream.str()));
5598 
5599         lagrangian = AddPlus(AddTimes(AddPower(discount_factor_node, dfpower),
5600                                       equations[i]->getNonZeroPartofEquation()->decreaseLeadsLags(lag)), lagrangian);
5601       }
5602 
5603   // Save line numbers and tags, see below
5604   auto old_equations_lineno = equations_lineno;
5605   auto old_equation_tags = equation_tags;
5606 
5607   // Prepare derivation of the Lagrangian
5608   clearEquations();
5609   addEquation(AddEqual(lagrangian, Zero), -1);
5610   computeDerivIDs();
5611 
5612   /* Compute Lagrangian derivatives.
5613      Also restore line numbers and tags for FOCs w.r.t. a Lagrange multiplier
5614      (i.e. a FOC identical to an equation of the original model) */
5615   vector<expr_t> neweqs;
5616   vector<int> neweqs_lineno;
5617   map<int, vector<pair<string, string>>> neweqs_tags;
5618   for (auto &[symb_id_and_lag, deriv_id] : deriv_id_table)
5619     {
5620       auto &[symb_id, lag] = symb_id_and_lag;
5621       if (symbol_table.getType(symb_id) == SymbolType::endogenous && lag == 0)
5622         {
5623           neweqs.push_back(AddEqual(equations[0]->getNonZeroPartofEquation()->getDerivative(deriv_id), Zero));
5624           if (int i = symbol_table.getEquationNumberForMultiplier(symb_id);
5625               i != -1)
5626             {
5627               // This is a derivative w.r.t. a Lagrange multiplier
5628               neweqs_lineno.push_back(old_equations_lineno[i]);
5629               vector<pair<string, string>> tags;
5630               for (auto &[j, tagpair] : old_equation_tags)
5631                 if (j == i)
5632                   tags.emplace_back(tagpair);
5633               neweqs_tags[neweqs.size()-1] = tags;
5634             }
5635           else
5636             neweqs_lineno.push_back(-1);
5637         }
5638     }
5639 
5640   // Overwrite equations with the Lagrangian derivatives
5641   clearEquations();
5642   for (size_t i = 0; i < neweqs.size(); i++)
5643     addEquation(neweqs[i], neweqs_lineno[i], neweqs_tags[i]);
5644 }
5645 
5646 void
toNonlinearPart(DynamicModel & non_linear_equations_dynamic_model) const5647 DynamicModel::toNonlinearPart(DynamicModel &non_linear_equations_dynamic_model) const
5648 {
5649   // Convert model local variables (need to be done first)
5650   for (const auto &it : local_variables_table)
5651     non_linear_equations_dynamic_model.AddLocalVariable(it.first, it.second);
5652 }
5653 
5654 bool
ParamUsedWithLeadLag() const5655 DynamicModel::ParamUsedWithLeadLag() const
5656 {
5657   return ParamUsedWithLeadLagInternal();
5658 }
5659 
5660 void
createVariableMapping(int orig_eq_nbr)5661 DynamicModel::createVariableMapping(int orig_eq_nbr)
5662 {
5663   for (int ii = 0; ii < orig_eq_nbr; ii++)
5664     {
5665       set<int> eqvars;
5666       equations[ii]->collectVariables(SymbolType::endogenous, eqvars);
5667       equations[ii]->collectVariables(SymbolType::exogenous, eqvars);
5668       for (auto eqvar : eqvars)
5669         {
5670           eqvar = symbol_table.getUltimateOrigSymbID(eqvar);
5671           if (eqvar >= 0 && !symbol_table.isAuxiliaryVariable(eqvar))
5672             variableMapping[eqvar].emplace(ii);
5673         }
5674     }
5675 }
5676 
5677 void
expandEqTags()5678 DynamicModel::expandEqTags()
5679 {
5680   set<int> existing_tags;
5681   for (const auto &eqn : equation_tags)
5682     if (eqn.second.first == "name")
5683       existing_tags.insert(eqn.first);
5684 
5685   for (int eq = 0; eq < static_cast<int>(equations.size()); eq++)
5686     if (existing_tags.find(eq) == existing_tags.end())
5687       if (auto lhs_expr = dynamic_cast<VariableNode *>(equations[eq]->arg1); lhs_expr && equation_tags_xref.find({ "name", symbol_table.getName(lhs_expr->symb_id)}) == equation_tags_xref.end())
5688         {
5689           equation_tags.emplace_back(eq, pair("name", symbol_table.getName(lhs_expr->symb_id)));
5690           equation_tags_xref.emplace(pair("name", symbol_table.getName(lhs_expr->symb_id)), eq);
5691         }
5692       else if (equation_tags_xref.find({ "name", to_string(eq+1) }) == equation_tags_xref.end())
5693         {
5694           equation_tags.emplace_back(eq, pair("name", to_string(eq+1)));
5695           equation_tags_xref.emplace(pair("name", to_string(eq+1)), eq);
5696         }
5697       else
5698         {
5699           cerr << "Error creating default equation tag: cannot assign default tag to equation number " << eq+1 << " because it is already in use" << endl;
5700           exit(EXIT_FAILURE);
5701         }
5702 
5703   sort(equation_tags.begin(), equation_tags.end());
5704 }
5705 
5706 set<int>
findUnusedEndogenous()5707 DynamicModel::findUnusedEndogenous()
5708 {
5709   set<int> usedEndo, unusedEndo;
5710   for (auto &equation : equations)
5711     equation->collectVariables(SymbolType::endogenous, usedEndo);
5712   set<int> allEndo = symbol_table.getEndogenous();
5713   set_difference(allEndo.begin(), allEndo.end(),
5714                  usedEndo.begin(), usedEndo.end(),
5715                  inserter(unusedEndo, unusedEndo.begin()));
5716   return unusedEndo;
5717 }
5718 
5719 set<int>
findUnusedExogenous()5720 DynamicModel::findUnusedExogenous()
5721 {
5722   set<int> usedExo, unusedExo, unobservedExo;
5723   for (auto &equation : equations)
5724     equation->collectVariables(SymbolType::exogenous, usedExo);
5725   set<int> observedExo = symbol_table.getObservedExogenous();
5726   set<int> allExo = symbol_table.getExogenous();
5727   set_difference(allExo.begin(), allExo.end(),
5728                  observedExo.begin(), observedExo.end(),
5729                  inserter(unobservedExo, unobservedExo.begin()));
5730   set_difference(unobservedExo.begin(), unobservedExo.end(),
5731                  usedExo.begin(), usedExo.end(),
5732                  inserter(unusedExo, unusedExo.begin()));
5733   return unusedExo;
5734 }
5735 
5736 void
setLeadsLagsOrig()5737 DynamicModel::setLeadsLagsOrig()
5738 {
5739   set<pair<int, int>> dynvars;
5740 
5741   for (auto &equation : equations)
5742     {
5743       equation->collectDynamicVariables(SymbolType::endogenous, dynvars);
5744       equation->collectDynamicVariables(SymbolType::exogenous, dynvars);
5745       equation->collectDynamicVariables(SymbolType::exogenousDet, dynvars);
5746 
5747       max_lag_with_diffs_expanded_orig = max(equation->maxLagWithDiffsExpanded(),
5748                                              max_lag_with_diffs_expanded_orig);
5749     }
5750 
5751   for (const auto &dynvar : dynvars)
5752     {
5753       int lag = dynvar.second;
5754       SymbolType type = symbol_table.getType(dynvar.first);
5755 
5756       max_lead_orig = max(lag, max_lead_orig);
5757       max_lag_orig = max(-lag, max_lag_orig);
5758 
5759       switch (type)
5760         {
5761         case SymbolType::endogenous:
5762           max_endo_lead_orig = max(lag, max_endo_lead_orig);
5763           max_endo_lag_orig = max(-lag, max_endo_lag_orig);
5764           break;
5765         case SymbolType::exogenous:
5766           max_exo_lead_orig = max(lag, max_exo_lead_orig);
5767           max_exo_lag_orig = max(-lag, max_exo_lag_orig);
5768           break;
5769         case SymbolType::exogenousDet:
5770           max_exo_det_lead_orig = max(lag, max_exo_det_lead_orig);
5771           max_exo_det_lag_orig = max(-lag, max_exo_det_lag_orig);
5772           break;
5773         default:
5774           break;
5775         }
5776     }
5777 }
5778 
5779 void
computeDerivIDs()5780 DynamicModel::computeDerivIDs()
5781 {
5782   set<pair<int, int>> dynvars;
5783 
5784   for (auto &equation : equations)
5785     equation->collectDynamicVariables(SymbolType::endogenous, dynvars);
5786 
5787   dynJacobianColsNbr = dynvars.size();
5788 
5789   for (auto &equation : equations)
5790     {
5791       equation->collectDynamicVariables(SymbolType::exogenous, dynvars);
5792       equation->collectDynamicVariables(SymbolType::exogenousDet, dynvars);
5793       equation->collectDynamicVariables(SymbolType::parameter, dynvars);
5794       equation->collectDynamicVariables(SymbolType::trend, dynvars);
5795       equation->collectDynamicVariables(SymbolType::logTrend, dynvars);
5796     }
5797 
5798   for (const auto &dynvar : dynvars)
5799     {
5800       int lag = dynvar.second;
5801       SymbolType type = symbol_table.getType(dynvar.first);
5802 
5803       /* Setting maximum and minimum lags.
5804 
5805          We don't want these to be affected by lead/lags on parameters: they
5806          are accepted for facilitating variable flipping, but are simply
5807          ignored. */
5808       if (type != SymbolType::parameter)
5809         {
5810           max_lead = max(lag, max_lead);
5811           max_lag = max(-lag, max_lag);
5812         }
5813 
5814       switch (type)
5815         {
5816         case SymbolType::endogenous:
5817           max_endo_lead = max(lag, max_endo_lead);
5818           max_endo_lag = max(-lag, max_endo_lag);
5819           break;
5820         case SymbolType::exogenous:
5821           max_exo_lead = max(lag, max_exo_lead);
5822           max_exo_lag = max(-lag, max_exo_lag);
5823           break;
5824         case SymbolType::exogenousDet:
5825           max_exo_det_lead = max(lag, max_exo_det_lead);
5826           max_exo_det_lag = max(-lag, max_exo_det_lag);
5827           break;
5828         default:
5829           break;
5830         }
5831 
5832       // Create a new deriv_id
5833       int deriv_id = deriv_id_table.size();
5834 
5835       deriv_id_table[dynvar] = deriv_id;
5836       inv_deriv_id_table.push_back(dynvar);
5837     }
5838 }
5839 
5840 SymbolType
getTypeByDerivID(int deriv_id) const5841 DynamicModel::getTypeByDerivID(int deriv_id) const noexcept(false)
5842 {
5843   return symbol_table.getType(getSymbIDByDerivID(deriv_id));
5844 }
5845 
5846 int
getLagByDerivID(int deriv_id) const5847 DynamicModel::getLagByDerivID(int deriv_id) const noexcept(false)
5848 {
5849   if (deriv_id < 0 || deriv_id >= static_cast<int>(inv_deriv_id_table.size()))
5850     throw UnknownDerivIDException();
5851 
5852   return inv_deriv_id_table[deriv_id].second;
5853 }
5854 
5855 int
getSymbIDByDerivID(int deriv_id) const5856 DynamicModel::getSymbIDByDerivID(int deriv_id) const noexcept(false)
5857 {
5858   if (deriv_id < 0 || deriv_id >= static_cast<int>(inv_deriv_id_table.size()))
5859     throw UnknownDerivIDException();
5860 
5861   return inv_deriv_id_table[deriv_id].first;
5862 }
5863 
5864 int
getDerivID(int symb_id,int lag) const5865 DynamicModel::getDerivID(int symb_id, int lag) const noexcept(false)
5866 {
5867   auto it = deriv_id_table.find({ symb_id, lag });
5868   if (it == deriv_id_table.end())
5869     throw UnknownDerivIDException();
5870   else
5871     return it->second;
5872 }
5873 
5874 void
addAllParamDerivId(set<int> & deriv_id_set)5875 DynamicModel::addAllParamDerivId(set<int> &deriv_id_set)
5876 {
5877   for (size_t i = 0; i < inv_deriv_id_table.size(); i++)
5878     if (symbol_table.getType(inv_deriv_id_table[i].first) == SymbolType::parameter)
5879       deriv_id_set.insert(i);
5880 }
5881 
5882 void
computeDynJacobianCols(bool jacobianExo)5883 DynamicModel::computeDynJacobianCols(bool jacobianExo)
5884 {
5885   /* Sort the dynamic endogenous variables by lexicographic order over (lag, type_specific_symbol_id)
5886      and fill the dynamic columns for exogenous and exogenous deterministic */
5887   map<pair<int, int>, int> ordered_dyn_endo;
5888 
5889   for (auto &it : deriv_id_table)
5890     {
5891       int symb_id = it.first.first;
5892       int lag = it.first.second;
5893       int deriv_id = it.second;
5894       SymbolType type = symbol_table.getType(symb_id);
5895       int tsid = symbol_table.getTypeSpecificID(symb_id);
5896 
5897       switch (type)
5898         {
5899         case SymbolType::endogenous:
5900           ordered_dyn_endo[{ lag, tsid }] = deriv_id;
5901           break;
5902         case SymbolType::exogenous:
5903           // At this point, dynJacobianColsNbr contains the number of dynamic endogenous
5904           if (jacobianExo)
5905             dyn_jacobian_cols_table[deriv_id] = dynJacobianColsNbr + tsid;
5906           break;
5907         case SymbolType::exogenousDet:
5908           // At this point, dynJacobianColsNbr contains the number of dynamic endogenous
5909           if (jacobianExo)
5910             dyn_jacobian_cols_table[deriv_id] = dynJacobianColsNbr + symbol_table.exo_nbr() + tsid;
5911           break;
5912         case SymbolType::parameter:
5913         case SymbolType::trend:
5914         case SymbolType::logTrend:
5915           // We don't assign a dynamic jacobian column to parameters or trend variables
5916           break;
5917         default:
5918           // Shut up GCC
5919           cerr << "DynamicModel::computeDynJacobianCols: impossible case" << endl;
5920           exit(EXIT_FAILURE);
5921         }
5922     }
5923 
5924   // Fill in dynamic jacobian columns for endogenous
5925   int sorted_id = 0;
5926   for (auto &it : ordered_dyn_endo)
5927     dyn_jacobian_cols_table[it.second] = sorted_id++;
5928 
5929   // Set final value for dynJacobianColsNbr
5930   if (jacobianExo)
5931     dynJacobianColsNbr += symbol_table.exo_nbr() + symbol_table.exo_det_nbr();
5932 }
5933 
5934 int
getDynJacobianCol(int deriv_id) const5935 DynamicModel::getDynJacobianCol(int deriv_id) const noexcept(false)
5936 {
5937   if (auto it = dyn_jacobian_cols_table.find(deriv_id);
5938       it == dyn_jacobian_cols_table.end())
5939     throw UnknownDerivIDException();
5940   else
5941     return it->second;
5942 }
5943 
5944 void
testTrendDerivativesEqualToZero(const eval_context_t & eval_context)5945 DynamicModel::testTrendDerivativesEqualToZero(const eval_context_t &eval_context)
5946 {
5947   for (auto &it : deriv_id_table)
5948     if (symbol_table.getType(it.first.first) == SymbolType::trend
5949         || symbol_table.getType(it.first.first) == SymbolType::logTrend)
5950       for (int eq = 0; eq < static_cast<int>(equations.size()); eq++)
5951         {
5952           expr_t homogeneq = AddMinus(equations[eq]->arg1,
5953                                       equations[eq]->arg2);
5954 
5955           // Do not run the test if the term inside the log is zero
5956           if (fabs(homogeneq->eval(eval_context)) > zero_band)
5957             {
5958               expr_t testeq = AddLog(homogeneq); // F = log(lhs-rhs)
5959               testeq = testeq->getDerivative(it.second); // d F / d Trend
5960               for (auto &endogit : deriv_id_table)
5961                 if (symbol_table.getType(endogit.first.first) == SymbolType::endogenous)
5962                   {
5963                     double nearZero = testeq->getDerivative(endogit.second)->eval(eval_context); // eval d F / d Trend d Endog
5964                     if (fabs(nearZero) > balanced_growth_test_tol)
5965                       {
5966                         cerr << "ERROR: trends not compatible with balanced growth path; the second-order cross partial of equation " << eq + 1 << " (line "
5967                              << equations_lineno[eq] << ") w.r.t. trend variable "
5968                              << symbol_table.getName(it.first.first) << " and endogenous variable "
5969                              << symbol_table.getName(endogit.first.first) << " is not null (abs. value = "
5970                              << fabs(nearZero) << "). If you are confident that your trends are correctly specified, you can raise the value of option 'balanced_growth_test_tol' in the 'model' block." << endl;
5971                         exit(EXIT_FAILURE);
5972                       }
5973                   }
5974             }
5975         }
5976 }
5977 
5978 void
writeParamsDerivativesFile(const string & basename,bool julia) const5979 DynamicModel::writeParamsDerivativesFile(const string &basename, bool julia) const
5980 {
5981   if (!params_derivatives.size())
5982     return;
5983 
5984   ExprNodeOutputType output_type = (julia ? ExprNodeOutputType::juliaDynamicModel : ExprNodeOutputType::matlabDynamicModel);
5985   ostringstream tt_output; // Used for storing model temp vars and equations
5986   ostringstream rp_output; // 1st deriv. of residuals w.r.t. parameters
5987   ostringstream gp_output; // 1st deriv. of Jacobian w.r.t. parameters
5988   ostringstream rpp_output; // 2nd deriv of residuals w.r.t. parameters
5989   ostringstream gpp_output; // 2nd deriv of Jacobian w.r.t. parameters
5990   ostringstream hp_output; // 1st deriv. of Hessian w.r.t. parameters
5991   ostringstream g3p_output; // 1st deriv. of 3rd deriv. matrix w.r.t. parameters
5992 
5993   temporary_terms_t temp_term_union;
5994   deriv_node_temp_terms_t tef_terms;
5995 
5996   writeModelLocalVariableTemporaryTerms(temp_term_union, params_derivs_temporary_terms_idxs, tt_output, output_type, tef_terms);
5997   for (const auto &it : params_derivs_temporary_terms)
5998     writeTemporaryTerms(it.second, temp_term_union, params_derivs_temporary_terms_idxs, tt_output, output_type, tef_terms);
5999 
6000   for (const auto & [indices, d1] : params_derivatives.find({ 0, 1 })->second)
6001     {
6002       auto [eq, param] = vectorToTuple<2>(indices);
6003 
6004       int param_col = symbol_table.getTypeSpecificID(getSymbIDByDerivID(param)) + 1;
6005 
6006       rp_output << "rp" << LEFT_ARRAY_SUBSCRIPT(output_type) << eq+1 << ", " << param_col
6007                 << RIGHT_ARRAY_SUBSCRIPT(output_type) << " = ";
6008       d1->writeOutput(rp_output, output_type, temp_term_union, params_derivs_temporary_terms_idxs, tef_terms);
6009       rp_output << ";" << endl;
6010     }
6011 
6012   for (const auto & [indices, d2] : params_derivatives.find({ 1, 1 })->second)
6013     {
6014       auto [eq, var, param] = vectorToTuple<3>(indices);
6015 
6016       int var_col = getDynJacobianCol(var) + 1;
6017       int param_col = symbol_table.getTypeSpecificID(getSymbIDByDerivID(param)) + 1;
6018 
6019       gp_output << "gp" << LEFT_ARRAY_SUBSCRIPT(output_type) << eq+1 << ", " << var_col
6020                 << ", " << param_col << RIGHT_ARRAY_SUBSCRIPT(output_type) << " = ";
6021       d2->writeOutput(gp_output, output_type, temp_term_union, params_derivs_temporary_terms_idxs, tef_terms);
6022       gp_output << ";" << endl;
6023     }
6024 
6025   int i = 1;
6026   for (const auto &[indices, d2] : params_derivatives.find({ 0, 2 })->second)
6027     {
6028       auto [eq, param1, param2] = vectorToTuple<3>(indices);
6029 
6030       int param1_col = symbol_table.getTypeSpecificID(getSymbIDByDerivID(param1)) + 1;
6031       int param2_col = symbol_table.getTypeSpecificID(getSymbIDByDerivID(param2)) + 1;
6032 
6033       rpp_output << "rpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",1"
6034                  << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << eq+1 << ";" << endl
6035                  << "rpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",2"
6036                  << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << param1_col << ";" << endl
6037                  << "rpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",3"
6038                  << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << param2_col << ";" << endl
6039                  << "rpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",4"
6040                  << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=";
6041       d2->writeOutput(rpp_output, output_type, temp_term_union, params_derivs_temporary_terms_idxs, tef_terms);
6042       rpp_output << ";" << endl;
6043 
6044       i++;
6045 
6046       if (param1 != param2)
6047         {
6048           // Treat symmetric elements
6049           rpp_output << "rpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",1"
6050                      << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << eq+1 << ";" << endl
6051                      << "rpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",2"
6052                      << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << param2_col << ";" << endl
6053                      << "rpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",3"
6054                      << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << param1_col << ";" << endl
6055                      << "rpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",4"
6056                      << RIGHT_ARRAY_SUBSCRIPT(output_type)
6057                      << "=rpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i-1 << ",4"
6058                      << RIGHT_ARRAY_SUBSCRIPT(output_type) << ";" << endl;
6059           i++;
6060         }
6061     }
6062 
6063   i = 1;
6064   for (const auto &[indices, d2] : params_derivatives.find({ 1, 2 })->second)
6065     {
6066       auto [eq, var, param1, param2] = vectorToTuple<4>(indices);
6067 
6068       int var_col = getDynJacobianCol(var) + 1;
6069       int param1_col = symbol_table.getTypeSpecificID(getSymbIDByDerivID(param1)) + 1;
6070       int param2_col = symbol_table.getTypeSpecificID(getSymbIDByDerivID(param2)) + 1;
6071 
6072       gpp_output << "gpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",1"
6073                  << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << eq+1 << ";" << endl
6074                  << "gpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",2"
6075                  << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << var_col << ";" << endl
6076                  << "gpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",3"
6077                  << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << param1_col << ";" << endl
6078                  << "gpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",4"
6079                  << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << param2_col << ";" << endl
6080                  << "gpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",5"
6081                  << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=";
6082       d2->writeOutput(gpp_output, output_type, temp_term_union, params_derivs_temporary_terms_idxs, tef_terms);
6083       gpp_output << ";" << endl;
6084 
6085       i++;
6086 
6087       if (param1 != param2)
6088         {
6089           // Treat symmetric elements
6090           gpp_output << "gpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",1"
6091                      << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << eq+1 << ";" << endl
6092                      << "gpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",2"
6093                      << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << var_col << ";" << endl
6094                      << "gpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",3"
6095                      << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << param2_col << ";" << endl
6096                      << "gpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",4"
6097                      << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << param1_col << ";" << endl
6098                      << "gpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",5"
6099                      << RIGHT_ARRAY_SUBSCRIPT(output_type)
6100                      << "=gpp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i-1 << ",5"
6101                      << RIGHT_ARRAY_SUBSCRIPT(output_type) << ";" << endl;
6102           i++;
6103         }
6104     }
6105 
6106   i = 1;
6107   for (const auto &[indices, d2] : params_derivatives.find({ 2, 1 })->second)
6108     {
6109       auto [eq, var1, var2, param] = vectorToTuple<4>(indices);
6110 
6111       int var1_col = getDynJacobianCol(var1) + 1;
6112       int var2_col = getDynJacobianCol(var2) + 1;
6113       int param_col = symbol_table.getTypeSpecificID(getSymbIDByDerivID(param)) + 1;
6114 
6115       hp_output << "hp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",1"
6116                 << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << eq+1 << ";" << endl
6117                 << "hp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",2"
6118                 << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << var1_col << ";" << endl
6119                 << "hp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",3"
6120                 << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << var2_col << ";" << endl
6121                 << "hp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",4"
6122                 << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << param_col << ";" << endl
6123                 << "hp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",5"
6124                 << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=";
6125       d2->writeOutput(hp_output, output_type, temp_term_union, params_derivs_temporary_terms_idxs, tef_terms);
6126       hp_output << ";" << endl;
6127 
6128       i++;
6129 
6130       if (var1 != var2)
6131         {
6132           // Treat symmetric elements
6133           hp_output << "hp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",1"
6134                     << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << eq+1 << ";" << endl
6135                     << "hp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",2"
6136                     << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << var2_col << ";" << endl
6137                     << "hp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",3"
6138                     << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << var1_col << ";" << endl
6139                     << "hp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",4"
6140                     << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << param_col << ";" << endl
6141                     << "hp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",5"
6142                     << RIGHT_ARRAY_SUBSCRIPT(output_type)
6143                     << "=hp" << LEFT_ARRAY_SUBSCRIPT(output_type) << i-1 << ",5"
6144                     << RIGHT_ARRAY_SUBSCRIPT(output_type) << ";" << endl;
6145           i++;
6146         }
6147     }
6148 
6149   i = 1;
6150   for (const auto &[indices, d2] : params_derivatives.find({ 3, 1 })->second)
6151     {
6152       auto [eq, var1, var2, var3, param] = vectorToTuple<5>(indices);
6153 
6154       int var1_col = getDynJacobianCol(var1) + 1;
6155       int var2_col = getDynJacobianCol(var2) + 1;
6156       int var3_col = getDynJacobianCol(var3) + 1;
6157       int param_col = symbol_table.getTypeSpecificID(getSymbIDByDerivID(param)) + 1;
6158 
6159       g3p_output << "g3p" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",1"
6160                  << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << eq+1 << ";" << endl
6161                  << "g3p" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",2"
6162                  << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << var1_col << ";" << endl
6163                  << "g3p" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",3"
6164                  << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << var2_col << ";" << endl
6165                  << "g3p" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",4"
6166                  << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << var3_col << ";" << endl
6167                  << "g3p" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",5"
6168                  << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=" << param_col << ";" << endl
6169                  << "g3p" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << ",6"
6170                  << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=";
6171       d2->writeOutput(g3p_output, output_type, temp_term_union, params_derivs_temporary_terms_idxs, tef_terms);
6172       g3p_output << ";" << endl;
6173 
6174       i++;
6175     }
6176 
6177   string filename = julia ? basename + "DynamicParamsDerivs.jl" : packageDir(basename) + "/dynamic_params_derivs.m";
6178   ofstream paramsDerivsFile;
6179   paramsDerivsFile.open(filename, ios::out | ios::binary);
6180   if (!paramsDerivsFile.is_open())
6181     {
6182       cerr << "ERROR: Can't open file " << filename << " for writing" << endl;
6183       exit(EXIT_FAILURE);
6184     }
6185 
6186   if (!julia)
6187     {
6188       // Check that we don't have more than 32 nested parenthesis because Matlab does not suppor this. See Issue #1201
6189       map<string, string> tmp_paren_vars;
6190       bool message_printed = false;
6191       fixNestedParenthesis(tt_output, tmp_paren_vars, message_printed);
6192       fixNestedParenthesis(rp_output, tmp_paren_vars, message_printed);
6193       fixNestedParenthesis(gp_output, tmp_paren_vars, message_printed);
6194       fixNestedParenthesis(rpp_output, tmp_paren_vars, message_printed);
6195       fixNestedParenthesis(gpp_output, tmp_paren_vars, message_printed);
6196       fixNestedParenthesis(hp_output, tmp_paren_vars, message_printed);
6197       fixNestedParenthesis(g3p_output, tmp_paren_vars, message_printed);
6198       paramsDerivsFile << "function [rp, gp, rpp, gpp, hp, g3p] = dynamic_params_derivs(y, x, params, steady_state, it_, ss_param_deriv, ss_param_2nd_deriv)" << endl
6199                        << "%" << endl
6200                        << "% Compute the derivatives of the dynamic model with respect to the parameters" << endl
6201                        << "% Inputs :" << endl
6202                        << "%   y         [#dynamic variables by 1] double    vector of endogenous variables in the order stored" << endl
6203                        << "%                                                 in M_.lead_lag_incidence; see the Manual" << endl
6204                        << "%   x         [nperiods by M_.exo_nbr] double     matrix of exogenous variables (in declaration order)" << endl
6205                        << "%                                                 for all simulation periods" << endl
6206                        << "%   params    [M_.param_nbr by 1] double          vector of parameter values in declaration order" << endl
6207                        << "%   steady_state  [M_.endo_nbr by 1] double       vector of steady state values" << endl
6208                        << "%   it_       scalar double                       time period for exogenous variables for which to evaluate the model" << endl
6209                        << "%   ss_param_deriv     [M_.eq_nbr by #params]     Jacobian matrix of the steady states values with respect to the parameters" << endl
6210                        << "%   ss_param_2nd_deriv [M_.eq_nbr by #params by #params] Hessian matrix of the steady states values with respect to the parameters" << endl
6211                        << "%" << endl
6212                        << "% Outputs:" << endl
6213                        << "%   rp        [M_.eq_nbr by #params] double    Jacobian matrix of dynamic model equations with respect to parameters " << endl
6214                        << "%                                              Dynare may prepend or append auxiliary equations, see M_.aux_vars" << endl
6215                        << "%   gp        [M_.endo_nbr by #dynamic variables by #params] double    Derivative of the Jacobian matrix of the dynamic model equations with respect to the parameters" << endl
6216                        << "%                                                           rows: equations in order of declaration" << endl
6217                        << "%                                                           columns: variables in order stored in M_.lead_lag_incidence" << endl
6218                        << "%   rpp       [#second_order_residual_terms by 4] double   Hessian matrix of second derivatives of residuals with respect to parameters;" << endl
6219                        << "%                                                              rows: respective derivative term" << endl
6220                        << "%                                                              1st column: equation number of the term appearing" << endl
6221                        << "%                                                              2nd column: number of the first parameter in derivative" << endl
6222                        << "%                                                              3rd column: number of the second parameter in derivative" << endl
6223                        << "%                                                              4th column: value of the Hessian term" << endl
6224                        << "%   gpp      [#second_order_Jacobian_terms by 5] double   Hessian matrix of second derivatives of the Jacobian with respect to the parameters;" << endl
6225                        << "%                                                              rows: respective derivative term" << endl
6226                        << "%                                                              1st column: equation number of the term appearing" << endl
6227                        << "%                                                              2nd column: column number of variable in Jacobian of the dynamic model" << endl
6228                        << "%                                                              3rd column: number of the first parameter in derivative" << endl
6229                        << "%                                                              4th column: number of the second parameter in derivative" << endl
6230                        << "%                                                              5th column: value of the Hessian term" << endl
6231                        << "%   hp      [#first_order_Hessian_terms by 5] double   Jacobian matrix of derivatives of the dynamic Hessian with respect to the parameters;" << endl
6232                        << "%                                                              rows: respective derivative term" << endl
6233                        << "%                                                              1st column: equation number of the term appearing" << endl
6234                        << "%                                                              2nd column: column number of first variable in Hessian of the dynamic model" << endl
6235                        << "%                                                              3rd column: column number of second variable in Hessian of the dynamic model" << endl
6236                        << "%                                                              4th column: number of the parameter in derivative" << endl
6237                        << "%                                                              5th column: value of the Hessian term" << endl
6238                        << "%   g3p      [#first_order_g3_terms by 6] double   Jacobian matrix of derivatives of g3 (dynamic 3rd derivs) with respect to the parameters;" << endl
6239                        << "%                                                              rows: respective derivative term" << endl
6240                        << "%                                                              1st column: equation number of the term appearing" << endl
6241                        << "%                                                              2nd column: column number of first variable in g3 of the dynamic model" << endl
6242                        << "%                                                              3rd column: column number of second variable in g3 of the dynamic model" << endl
6243                        << "%                                                              4th column: column number of third variable in g3 of the dynamic model" << endl
6244                        << "%                                                              5th column: number of the parameter in derivative" << endl
6245                        << "%                                                              6th column: value of the Hessian term" << endl
6246                        << "%" << endl
6247                        << "%" << endl
6248                        << "% Warning : this file is generated automatically by Dynare" << endl
6249                        << "%           from model file (.mod)" << endl << endl
6250                        << "T = NaN(" << params_derivs_temporary_terms_idxs.size() << ",1);" << endl
6251                        << tt_output.str()
6252                        << "rp = zeros(" << equations.size() << ", "
6253                        << symbol_table.param_nbr() << ");" << endl
6254                        << rp_output.str()
6255                        << "gp = zeros(" << equations.size() << ", " << dynJacobianColsNbr << ", " << symbol_table.param_nbr() << ");" << endl
6256                        << gp_output.str()
6257                        << "if nargout >= 3" << endl
6258                        << "rpp = zeros(" << params_derivatives.find({ 0, 2 })->second.size() << ",4);" << endl
6259                        << rpp_output.str()
6260                        << "gpp = zeros(" << params_derivatives.find({ 1, 2 })->second.size() << ",5);" << endl
6261                        << gpp_output.str()
6262                        << "end" << endl
6263                        << "if nargout >= 5" << endl
6264                        << "hp = zeros(" << params_derivatives.find({ 2, 1 })->second.size() << ",5);" << endl
6265                        << hp_output.str()
6266                        << "end" << endl
6267                        << "if nargout >= 6" << endl
6268                        << "g3p = zeros(" << params_derivatives.find({ 3, 1 })->second.size() << ",6);" << endl
6269                        << g3p_output.str()
6270                        << "end" << endl
6271                        << "end" << endl;
6272     }
6273   else
6274     paramsDerivsFile << "module " << basename << "DynamicParamsDerivs" << endl
6275                      << "#" << endl
6276                      << "# NB: this file was automatically generated by Dynare" << endl
6277                      << "#     from " << basename << ".mod" << endl
6278                      << "#" << endl
6279                      << "export params_derivs" << endl << endl
6280                      << "function params_derivs(y, x, paramssteady_state, it_, "
6281                      << "ss_param_deriv, ss_param_2nd_deriv)" << endl
6282                      << tt_output.str()
6283                      << "rp = zeros(" << equations.size() << ", "
6284                      << symbol_table.param_nbr() << ");" << endl
6285                      << rp_output.str()
6286                      << "gp = zeros(" << equations.size() << ", " << dynJacobianColsNbr << ", " << symbol_table.param_nbr() << ");" << endl
6287                      << gp_output.str()
6288                      << "rpp = zeros(" << params_derivatives.find({ 0, 2 })->second.size() << ",4);" << endl
6289                      << rpp_output.str()
6290                      << "gpp = zeros(" << params_derivatives.find({ 1, 2 })->second.size() << ",5);" << endl
6291                      << gpp_output.str()
6292                      << "hp = zeros(" << params_derivatives.find({ 2, 1 })->second.size() << ",5);" << endl
6293                      << hp_output.str()
6294                      << "g3p = zeros(" << params_derivatives.find({ 3, 1 })->second.size() << ",6);" << endl
6295                      << g3p_output.str()
6296                      << "(rp, gp, rpp, gpp, hp, g3p)" << endl
6297                      << "end" << endl
6298                      << "end" << endl;
6299 
6300   paramsDerivsFile.close();
6301 }
6302 
6303 void
writeLatexFile(const string & basename,bool write_equation_tags) const6304 DynamicModel::writeLatexFile(const string &basename, bool write_equation_tags) const
6305 {
6306   writeLatexModelFile(basename, "dynamic", ExprNodeOutputType::latexDynamicModel, write_equation_tags);
6307 }
6308 
6309 void
writeLatexOriginalFile(const string & basename,bool write_equation_tags) const6310 DynamicModel::writeLatexOriginalFile(const string &basename, bool write_equation_tags) const
6311 {
6312   writeLatexModelFile(basename, "original", ExprNodeOutputType::latexDynamicModel, write_equation_tags);
6313 }
6314 
6315 void
substituteEndoLeadGreaterThanTwo(bool deterministic_model)6316 DynamicModel::substituteEndoLeadGreaterThanTwo(bool deterministic_model)
6317 {
6318   substituteLeadLagInternal(AuxVarType::endoLead, deterministic_model, {});
6319 }
6320 
6321 void
substituteEndoLagGreaterThanTwo(bool deterministic_model)6322 DynamicModel::substituteEndoLagGreaterThanTwo(bool deterministic_model)
6323 {
6324   substituteLeadLagInternal(AuxVarType::endoLag, deterministic_model, {});
6325 }
6326 
6327 void
substituteExoLead(bool deterministic_model)6328 DynamicModel::substituteExoLead(bool deterministic_model)
6329 {
6330   substituteLeadLagInternal(AuxVarType::exoLead, deterministic_model, {});
6331 }
6332 
6333 void
substituteExoLag(bool deterministic_model)6334 DynamicModel::substituteExoLag(bool deterministic_model)
6335 {
6336   substituteLeadLagInternal(AuxVarType::exoLag, deterministic_model, {});
6337 }
6338 
6339 void
substituteLeadLagInternal(AuxVarType type,bool deterministic_model,const vector<string> & subset)6340 DynamicModel::substituteLeadLagInternal(AuxVarType type, bool deterministic_model, const vector<string> &subset)
6341 {
6342   ExprNode::subst_table_t subst_table;
6343   vector<BinaryOpNode *> neweqs;
6344 
6345   // Substitute in used model local variables
6346   set<int> used_local_vars;
6347   for (auto &equation : equations)
6348     equation->collectVariables(SymbolType::modelLocalVariable, used_local_vars);
6349 
6350   for (int used_local_var : used_local_vars)
6351     {
6352       const expr_t value = local_variables_table.find(used_local_var)->second;
6353       expr_t subst;
6354       switch (type)
6355         {
6356         case AuxVarType::endoLead:
6357           subst = value->substituteEndoLeadGreaterThanTwo(subst_table, neweqs, deterministic_model);
6358           break;
6359         case AuxVarType::endoLag:
6360           subst = value->substituteEndoLagGreaterThanTwo(subst_table, neweqs);
6361           break;
6362         case AuxVarType::exoLead:
6363           subst = value->substituteExoLead(subst_table, neweqs, deterministic_model);
6364           break;
6365         case AuxVarType::exoLag:
6366           subst = value->substituteExoLag(subst_table, neweqs);
6367           break;
6368         case AuxVarType::diffForward:
6369           subst = value->differentiateForwardVars(subset, subst_table, neweqs);
6370           break;
6371         default:
6372           cerr << "DynamicModel::substituteLeadLagInternal: impossible case" << endl;
6373           exit(EXIT_FAILURE);
6374         }
6375       local_variables_table[used_local_var] = subst;
6376     }
6377 
6378   // Substitute in equations
6379   for (auto &equation : equations)
6380     {
6381       expr_t subst;
6382       switch (type)
6383         {
6384         case AuxVarType::endoLead:
6385           subst = equation->substituteEndoLeadGreaterThanTwo(subst_table, neweqs, deterministic_model);
6386           break;
6387         case AuxVarType::endoLag:
6388           subst = equation->substituteEndoLagGreaterThanTwo(subst_table, neweqs);
6389           break;
6390         case AuxVarType::exoLead:
6391           subst = equation->substituteExoLead(subst_table, neweqs, deterministic_model);
6392           break;
6393         case AuxVarType::exoLag:
6394           subst = equation->substituteExoLag(subst_table, neweqs);
6395           break;
6396         case AuxVarType::diffForward:
6397           subst = equation->differentiateForwardVars(subset, subst_table, neweqs);
6398           break;
6399         default:
6400           cerr << "DynamicModel::substituteLeadLagInternal: impossible case" << endl;
6401           exit(EXIT_FAILURE);
6402         }
6403       auto substeq = dynamic_cast<BinaryOpNode *>(subst);
6404       assert(substeq);
6405       equation = substeq;
6406     }
6407 
6408   // Add new equations
6409   for (auto &neweq : neweqs)
6410     {
6411       addEquation(neweq, -1);
6412       aux_equations.push_back(neweq);
6413     }
6414 
6415   if (neweqs.size() > 0)
6416     {
6417       cout << "Substitution of ";
6418       switch (type)
6419         {
6420         case AuxVarType::endoLead:
6421           cout << "endo leads >= 2";
6422           break;
6423         case AuxVarType::endoLag:
6424           cout << "endo lags >= 2";
6425           break;
6426         case AuxVarType::exoLead:
6427           cout << "exo leads";
6428           break;
6429         case AuxVarType::exoLag:
6430           cout << "exo lags";
6431           break;
6432         case AuxVarType::expectation:
6433           cout << "expectation";
6434           break;
6435         case AuxVarType::diffForward:
6436           cout << "forward vars";
6437           break;
6438         default:
6439           cerr << "DynamicModel::substituteLeadLagInternal: impossible case" << endl;
6440           exit(EXIT_FAILURE);
6441         }
6442       cout << ": added " << neweqs.size() << " auxiliary variables and equations." << endl;
6443     }
6444 }
6445 
6446 void
substituteAdl()6447 DynamicModel::substituteAdl()
6448 {
6449   /* Contrary to other substitution methods, we do the substitution in MLV
6450      definitions here, instead of doing it at the ExprNode method level,
6451      because otherwise this would substitute MLV in the original model (see
6452      #65). */
6453   for (auto &[id, definition] : local_variables_table)
6454     definition = definition->substituteAdl();
6455 
6456   for (auto &equation : equations)
6457     equation = dynamic_cast<BinaryOpNode *>(equation->substituteAdl());
6458 }
6459 
6460 vector<int>
getEquationNumbersFromTags(const set<string> & eqtags) const6461 DynamicModel::getEquationNumbersFromTags(const set<string> &eqtags) const
6462 {
6463   vector<int> eqnumbers;
6464   for (auto &eqtag : eqtags)
6465     {
6466       bool found = false;
6467       for (const auto &equation_tag : equation_tags)
6468         if (equation_tag.second.first == "name"
6469             && equation_tag.second.second == eqtag)
6470           {
6471             found = true;
6472             eqnumbers.push_back(equation_tag.first);
6473             break;
6474           }
6475       if (!found)
6476         {
6477           cerr << "ERROR: looking for equation tag " << eqtag << " failed." << endl;
6478           exit(EXIT_FAILURE);
6479         }
6480     }
6481   return eqnumbers;
6482 }
6483 
6484 void
findPacExpectationEquationNumbers(vector<int> & eqnumbers) const6485 DynamicModel::findPacExpectationEquationNumbers(vector<int> &eqnumbers) const
6486 {
6487   int i = 0;
6488   for (auto &equation : equations)
6489     {
6490       if (equation->containsPacExpectation()
6491           && find(eqnumbers.begin(), eqnumbers.end(), i) == eqnumbers.end())
6492         eqnumbers.push_back(i);
6493       i++;
6494     }
6495 }
6496 
6497 pair<lag_equivalence_table_t, ExprNode::subst_table_t>
substituteUnaryOps()6498 DynamicModel::substituteUnaryOps()
6499 {
6500   vector<int> eqnumbers(equations.size());
6501   iota(eqnumbers.begin(), eqnumbers.end(), 0);
6502   return substituteUnaryOps(eqnumbers);
6503 }
6504 
6505 pair<lag_equivalence_table_t, ExprNode::subst_table_t>
substituteUnaryOps(const set<string> & var_model_eqtags)6506 DynamicModel::substituteUnaryOps(const set<string> &var_model_eqtags)
6507 {
6508   vector<int> eqnumbers = getEquationNumbersFromTags(var_model_eqtags);
6509   findPacExpectationEquationNumbers(eqnumbers);
6510   return substituteUnaryOps(eqnumbers);
6511 }
6512 
6513 pair<lag_equivalence_table_t, ExprNode::subst_table_t>
substituteUnaryOps(const vector<int> & eqnumbers)6514 DynamicModel::substituteUnaryOps(const vector<int> &eqnumbers)
6515 {
6516   lag_equivalence_table_t nodes;
6517   ExprNode::subst_table_t subst_table;
6518 
6519   // Mark unary ops to be substituted in model local variables that appear in selected equations
6520   set<int> used_local_vars;
6521   for (int eqnumber : eqnumbers)
6522     equations[eqnumber]->collectVariables(SymbolType::modelLocalVariable, used_local_vars);
6523   for (auto &it : local_variables_table)
6524     if (used_local_vars.find(it.first) != used_local_vars.end())
6525       it.second->findUnaryOpNodesForAuxVarCreation(nodes);
6526 
6527   // Mark unary ops to be substituted in selected equations
6528   for (int eqnumber : eqnumbers)
6529     equations[eqnumber]->findUnaryOpNodesForAuxVarCreation(nodes);
6530 
6531   // Substitute in model local variables
6532   vector<BinaryOpNode *> neweqs;
6533   for (auto &it : local_variables_table)
6534     it.second = it.second->substituteUnaryOpNodes(nodes, subst_table, neweqs);
6535 
6536   // Substitute in equations
6537   for (auto &equation : equations)
6538     {
6539       auto substeq = dynamic_cast<BinaryOpNode *>(equation->
6540                                                   substituteUnaryOpNodes(nodes, subst_table, neweqs));
6541       assert(substeq);
6542       equation = substeq;
6543     }
6544 
6545   // Add new equations
6546   for (auto &neweq : neweqs)
6547     {
6548       addEquation(neweq, -1);
6549       aux_equations.push_back(neweq);
6550     }
6551 
6552   if (subst_table.size() > 0)
6553     cout << "Substitution of Unary Ops: added " << neweqs.size() << " auxiliary variables and equations." << endl;
6554 
6555   return { nodes, subst_table };
6556 }
6557 
6558 pair<lag_equivalence_table_t, ExprNode::subst_table_t>
substituteDiff(vector<expr_t> & pac_growth)6559 DynamicModel::substituteDiff(vector<expr_t> &pac_growth)
6560 {
6561   /* Note: at this point, we know that there is no diff operator with a lead,
6562      because they have been expanded by DataTree::AddDiff().
6563      Hence we can go forward with the substitution without worrying about the
6564      expectation operator. */
6565 
6566   lag_equivalence_table_t diff_nodes;
6567   ExprNode::subst_table_t diff_subst_table;
6568 
6569   // Mark diff operators to be substituted in model local variables
6570   set<int> used_local_vars;
6571   for (const auto &equation : equations)
6572     equation->collectVariables(SymbolType::modelLocalVariable, used_local_vars);
6573   for (auto &it : local_variables_table)
6574     if (used_local_vars.find(it.first) != used_local_vars.end())
6575       it.second->findDiffNodes(diff_nodes);
6576 
6577   // Mark diff operators to be substituted in equations
6578   for (const auto &equation : equations)
6579     equation->findDiffNodes(diff_nodes);
6580 
6581   for (const auto &gv : pac_growth)
6582     if (gv)
6583       gv->findDiffNodes(diff_nodes);
6584 
6585   // Substitute in model local variables
6586   vector<BinaryOpNode *> neweqs;
6587   for (auto &it : local_variables_table)
6588     it.second = it.second->substituteDiff(diff_nodes, diff_subst_table, neweqs);
6589 
6590   // Substitute in equations
6591   for (auto &equation : equations)
6592     {
6593       auto substeq = dynamic_cast<BinaryOpNode *>(equation->
6594                                                   substituteDiff(diff_nodes, diff_subst_table, neweqs));
6595       assert(substeq);
6596       equation = substeq;
6597     }
6598 
6599   for (auto &it : pac_growth)
6600     if (it)
6601       it = it->substituteDiff(diff_nodes, diff_subst_table, neweqs);
6602 
6603   // Add new equations
6604   for (auto &neweq : neweqs)
6605     {
6606       addEquation(neweq, -1);
6607       aux_equations.push_back(neweq);
6608     }
6609 
6610   if (diff_subst_table.size() > 0)
6611     cout << "Substitution of Diff operator: added " << neweqs.size() << " auxiliary variables and equations." << endl;
6612 
6613   return { diff_nodes, diff_subst_table };
6614 }
6615 
6616 void
substituteExpectation(bool partial_information_model)6617 DynamicModel::substituteExpectation(bool partial_information_model)
6618 {
6619   ExprNode::subst_table_t subst_table;
6620   vector<BinaryOpNode *> neweqs;
6621 
6622   // Substitute in model local variables
6623   for (auto &it : local_variables_table)
6624     it.second = it.second->substituteExpectation(subst_table, neweqs, partial_information_model);
6625 
6626   // Substitute in equations
6627   for (auto &equation : equations)
6628     {
6629       auto substeq = dynamic_cast<BinaryOpNode *>(equation->substituteExpectation(subst_table, neweqs, partial_information_model));
6630       assert(substeq);
6631       equation = substeq;
6632     }
6633 
6634   // Add new equations
6635   for (auto &neweq : neweqs)
6636     {
6637       addEquation(neweq, -1);
6638       aux_equations.push_back(neweq);
6639     }
6640 
6641   if (subst_table.size() > 0)
6642     {
6643       if (partial_information_model)
6644         cout << "Substitution of Expectation operator: added " << subst_table.size() << " auxiliary variables and " << neweqs.size() << " auxiliary equations." << endl;
6645       else
6646         cout << "Substitution of Expectation operator: added " << neweqs.size() << " auxiliary variables and equations." << endl;
6647     }
6648 }
6649 
6650 void
transformPredeterminedVariables()6651 DynamicModel::transformPredeterminedVariables()
6652 {
6653   for (auto &it : local_variables_table)
6654     it.second = it.second->decreaseLeadsLagsPredeterminedVariables();
6655 
6656   for (auto &equation : equations)
6657     {
6658       auto substeq = dynamic_cast<BinaryOpNode *>(equation->decreaseLeadsLagsPredeterminedVariables());
6659       assert(substeq);
6660       equation = substeq;
6661     }
6662 }
6663 
6664 void
detrendEquations()6665 DynamicModel::detrendEquations()
6666 {
6667   // We go backwards in the list of trend_vars, to deal correctly with I(2) processes
6668   for (auto it = nonstationary_symbols_map.crbegin();
6669        it != nonstationary_symbols_map.crend(); ++it)
6670     for (auto &equation : equations)
6671       {
6672         auto substeq = dynamic_cast<BinaryOpNode *>(equation->detrend(it->first, it->second.first, it->second.second));
6673         assert(substeq);
6674         equation = dynamic_cast<BinaryOpNode *>(substeq);
6675       }
6676 
6677   for (auto &equation : equations)
6678     {
6679       auto substeq = dynamic_cast<BinaryOpNode *>(equation->removeTrendLeadLag(trend_symbols_map));
6680       assert(substeq);
6681       equation = dynamic_cast<BinaryOpNode *>(substeq);
6682     }
6683 }
6684 
6685 void
removeTrendVariableFromEquations()6686 DynamicModel::removeTrendVariableFromEquations()
6687 {
6688   for (auto &equation : equations)
6689     {
6690       auto substeq = dynamic_cast<BinaryOpNode *>(equation->replaceTrendVar());
6691       assert(substeq);
6692       equation = dynamic_cast<BinaryOpNode *>(substeq);
6693     }
6694 }
6695 
6696 void
differentiateForwardVars(const vector<string> & subset)6697 DynamicModel::differentiateForwardVars(const vector<string> &subset)
6698 {
6699   substituteLeadLagInternal(AuxVarType::diffForward, true, subset);
6700 }
6701 
6702 void
fillEvalContext(eval_context_t & eval_context) const6703 DynamicModel::fillEvalContext(eval_context_t &eval_context) const
6704 {
6705   // First, auxiliary variables
6706   for (auto aux_equation : aux_equations)
6707     {
6708       assert(aux_equation->op_code == BinaryOpcode::equal);
6709       auto auxvar = dynamic_cast<VariableNode *>(aux_equation->arg1);
6710       assert(auxvar);
6711       try
6712         {
6713           double val = aux_equation->arg2->eval(eval_context);
6714           eval_context[auxvar->symb_id] = val;
6715         }
6716       catch (ExprNode::EvalException &e)
6717         {
6718           // Do nothing
6719         }
6720     }
6721 
6722   // Second, model local variables
6723   for (auto it : local_variables_table)
6724     {
6725       try
6726         {
6727           const expr_t expression = it.second;
6728           double val = expression->eval(eval_context);
6729           eval_context[it.first] = val;
6730         }
6731       catch (ExprNode::EvalException &e)
6732         {
6733           // Do nothing
6734         }
6735     }
6736 
6737   //Third, trend variables
6738   vector <int> trendVars = symbol_table.getTrendVarIds();
6739   for (int &trendVar : trendVars)
6740     eval_context[trendVar] = 2; //not <= 0 bc of log, not 1 bc of powers
6741 }
6742 
6743 bool
isModelLocalVariableUsed() const6744 DynamicModel::isModelLocalVariableUsed() const
6745 {
6746   set<int> used_local_vars;
6747   size_t i = 0;
6748   while (i < equations.size() && used_local_vars.size() == 0)
6749     {
6750       equations[i]->collectVariables(SymbolType::modelLocalVariable, used_local_vars);
6751       i++;
6752     }
6753   return used_local_vars.size() > 0;
6754 }
6755 
6756 void
addStaticOnlyEquation(expr_t eq,int lineno,const vector<pair<string,string>> & eq_tags)6757 DynamicModel::addStaticOnlyEquation(expr_t eq, int lineno, const vector<pair<string, string>> &eq_tags)
6758 {
6759   auto beq = dynamic_cast<BinaryOpNode *>(eq);
6760   assert(beq && beq->op_code == BinaryOpcode::equal);
6761 
6762   vector<pair<string, string>> soe_eq_tags;
6763   for (const auto &eq_tag : eq_tags)
6764     soe_eq_tags.push_back(eq_tag);
6765 
6766   int n = static_only_equations.size();
6767   static_only_equations.push_back(beq);
6768   static_only_equations_lineno.push_back(lineno);
6769   static_only_equations_equation_tags.push_back(soe_eq_tags);
6770   for (auto &it : soe_eq_tags)
6771     static_only_equation_tags_xref.emplace(it, n);
6772 }
6773 
6774 size_t
staticOnlyEquationsNbr() const6775 DynamicModel::staticOnlyEquationsNbr() const
6776 {
6777   return static_only_equations.size();
6778 }
6779 
6780 size_t
dynamicOnlyEquationsNbr() const6781 DynamicModel::dynamicOnlyEquationsNbr() const
6782 {
6783   set<int> eqs;
6784 
6785   for (const auto &equation_tag : equation_tags)
6786     if (equation_tag.second.first == "dynamic")
6787       eqs.insert(equation_tag.first);
6788 
6789   return eqs.size();
6790 }
6791 
6792 bool
isChecksumMatching(const string & basename,bool block) const6793 DynamicModel::isChecksumMatching(const string &basename, bool block) const
6794 {
6795   stringstream buffer;
6796 
6797   // Write equation tags
6798   for (const auto &equation_tag : equation_tags)
6799     buffer << "  " << equation_tag.first + 1
6800            << equation_tag.second.first
6801            << equation_tag.second.second << endl;
6802 
6803   ExprNodeOutputType buffer_type = block ? ExprNodeOutputType::matlabDynamicModelSparse : ExprNodeOutputType::CDynamicModel;
6804 
6805   deriv_node_temp_terms_t tef_terms;
6806   temporary_terms_t temp_term_union;
6807   writeModelLocalVariableTemporaryTerms(temp_term_union, temporary_terms_idxs,
6808                                         buffer, buffer_type, tef_terms);
6809 
6810   writeTemporaryTerms(temporary_terms_derivatives[0],
6811                       temp_term_union, temporary_terms_idxs,
6812                       buffer, buffer_type, tef_terms);
6813 
6814   writeModelEquations(buffer, buffer_type, temp_term_union);
6815 
6816   size_t result = hash<string>{}(buffer.str());
6817 
6818   // check whether basename directory exist. If not, create it.
6819   // If it does, read old checksum if it exists, return if equal to result
6820   fstream checksum_file;
6821   auto filename = filesystem::path{basename} / "checksum";
6822   if (!filesystem::create_directory(basename))
6823     {
6824       checksum_file.open(filename, ios::in | ios::binary);
6825       if (checksum_file.is_open())
6826         {
6827           size_t old_checksum;
6828           checksum_file >> old_checksum;
6829           checksum_file.close();
6830           if (old_checksum == result)
6831             return true;
6832         }
6833     }
6834 
6835   // write new checksum file if none or different from old checksum
6836   checksum_file.open(filename, ios::out | ios::binary);
6837   if (!checksum_file.is_open())
6838     {
6839       cerr << "ERROR: Can't open file " << filename << endl;
6840       exit(EXIT_FAILURE);
6841     }
6842   checksum_file << result;
6843   checksum_file.close();
6844   return false;
6845 }
6846 
6847 void
writeJsonOutput(ostream & output) const6848 DynamicModel::writeJsonOutput(ostream &output) const
6849 {
6850   deriv_node_temp_terms_t tef_terms;
6851   writeJsonModelLocalVariables(output, false, tef_terms);
6852   output << ", ";
6853   writeJsonModelEquations(output, false);
6854   output << ", ";
6855   writeJsonXrefs(output);
6856   output << ", ";
6857   writeJsonAST(output);
6858   output << ", ";
6859   writeJsonVariableMapping(output);
6860 }
6861 
6862 void
writeJsonAST(ostream & output) const6863 DynamicModel::writeJsonAST(ostream &output) const
6864 {
6865   vector<pair<string, string>> eqtags;
6866   output << R"("abstract_syntax_tree":[)" << endl;
6867   for (int eq = 0; eq < static_cast<int>(equations.size()); eq++)
6868     {
6869       if (eq != 0)
6870         output << ", ";
6871 
6872       output << R"({ "number":)" << eq
6873              << R"(, "line":)" << equations_lineno[eq];
6874 
6875       for (const auto &equation_tag : equation_tags)
6876         if (equation_tag.first == eq)
6877           eqtags.push_back(equation_tag.second);
6878 
6879       if (!eqtags.empty())
6880         {
6881           output << R"(, "tags": {)";
6882           int i = 0;
6883           for (const auto &[name, value] : eqtags)
6884             {
6885               if (i != 0)
6886                 output << ", ";
6887               output << R"(")" << name << R"(": ")" << value << R"(")";
6888               i++;
6889             }
6890           output << "}";
6891           eqtags.clear();
6892         }
6893 
6894       output << R"(, "AST": )";
6895       equations[eq]->writeJsonAST(output);
6896       output << "}";
6897     }
6898   output << "]";
6899 }
6900 
6901 void
writeJsonVariableMapping(ostream & output) const6902 DynamicModel::writeJsonVariableMapping(ostream &output) const
6903 {
6904   output << R"("variable_mapping":[)" << endl;
6905   for (auto it = variableMapping.begin(); it != variableMapping.end(); ++it)
6906     {
6907       if (it != variableMapping.begin())
6908         output << ", ";
6909       auto [var, eqs] = *it;
6910       output << R"({"name": ")" << symbol_table.getName(var) << R"(", "equations":[)";
6911       bool first_eq = true;
6912       for (auto it2 = eqs.begin(); it2 != eqs.end(); ++it2)
6913         for (const auto &equation_tag : equation_tags)
6914           if (equation_tag.first == *it2 && equation_tag.second.first == "name")
6915             {
6916               if (first_eq)
6917                 first_eq = false;
6918               else
6919                 output << ", ";
6920               output << '"' << equation_tag.second.second << '"';
6921             }
6922       output << "]}" << endl;
6923     }
6924   output << "]";
6925 }
6926 
6927 void
writeJsonXrefsHelper(ostream & output,const map<pair<int,int>,set<int>> & xrefs) const6928 DynamicModel::writeJsonXrefsHelper(ostream &output, const map<pair<int, int>, set<int>> &xrefs) const
6929 {
6930   for (auto it = xrefs.begin(); it != xrefs.end(); ++it)
6931     {
6932       if (it != xrefs.begin())
6933         output << ", ";
6934       output << R"({"name": ")" << symbol_table.getName(it->first.first) << R"(")"
6935              << R"(, "shift": )" << it->first.second
6936              << R"(, "equations": [)";
6937       for (auto it1 = it->second.begin(); it1 != it->second.end(); ++it1)
6938         {
6939           if (it1 != it->second.begin())
6940             output << ", ";
6941           output << *it1 + 1;
6942         }
6943       output << "]}";
6944     }
6945 }
6946 
6947 void
writeJsonXrefs(ostream & output) const6948 DynamicModel::writeJsonXrefs(ostream &output) const
6949 {
6950   output << R"("xrefs": {)"
6951          << R"("parameters": [)";
6952   writeJsonXrefsHelper(output, xref_param);
6953   output << "]"
6954          << R"(, "endogenous": [)";
6955   writeJsonXrefsHelper(output, xref_endo);
6956   output << "]"
6957          << R"(, "exogenous": [)";
6958   writeJsonXrefsHelper(output, xref_exo);
6959   output << "]"
6960          << R"(, "exogenous_deterministic": [)";
6961   writeJsonXrefsHelper(output, xref_exo_det);
6962   output << "]}" << endl;
6963 }
6964 
6965 void
writeJsonOriginalModelOutput(ostream & output) const6966 DynamicModel::writeJsonOriginalModelOutput(ostream &output) const
6967 {
6968   writeJsonModelEquations(output, false);
6969   output << ", ";
6970   writeJsonAST(output);
6971 }
6972 
6973 void
writeJsonDynamicModelInfo(ostream & output) const6974 DynamicModel::writeJsonDynamicModelInfo(ostream &output) const
6975 {
6976   output << R"("model_info": {)"
6977          << R"("lead_lag_incidence": [)";
6978   // Loop on endogenous variables
6979   int nstatic = 0,
6980     nfwrd = 0,
6981     npred = 0,
6982     nboth = 0;
6983   for (int endoID = 0; endoID < symbol_table.endo_nbr(); endoID++)
6984     {
6985       if (endoID != 0)
6986         output << ",";
6987       output << "[";
6988       int sstatic = 1,
6989         sfwrd = 0,
6990         spred = 0,
6991         sboth = 0;
6992       // Loop on periods
6993       for (int lag = -max_endo_lag; lag <= max_endo_lead; lag++)
6994         {
6995           // Print variableID if exists with current period, otherwise print 0
6996           try
6997             {
6998               if (lag != -max_endo_lag)
6999                 output << ",";
7000               int varID = getDerivID(symbol_table.getID(SymbolType::endogenous, endoID), lag);
7001               output << " " << getDynJacobianCol(varID) + 1;
7002               if (lag == -1)
7003                 {
7004                   sstatic = 0;
7005                   spred = 1;
7006                 }
7007               else if (lag == 1)
7008                 {
7009                   if (spred == 1)
7010                     {
7011                       sboth = 1;
7012                       spred = 0;
7013                     }
7014                   else
7015                     {
7016                       sstatic = 0;
7017                       sfwrd = 1;
7018                     }
7019                 }
7020             }
7021           catch (UnknownDerivIDException &e)
7022             {
7023               output << " 0";
7024             }
7025         }
7026       nstatic += sstatic;
7027       nfwrd += sfwrd;
7028       npred += spred;
7029       nboth += sboth;
7030       output << "]";
7031     }
7032   output << "], "
7033          << R"("nstatic": )" << nstatic << ", "
7034          << R"("nfwrd": )" << nfwrd << ", "
7035          << R"("npred": )" << npred << ", "
7036          << R"("nboth": )" << nboth << ", "
7037          << R"("nsfwrd": )" << nfwrd+nboth << ", "
7038          << R"("nspred": )" << npred+nboth << ", "
7039          << R"("ndynamic": )" << npred+nboth+nfwrd << endl;
7040   output << "}";
7041 }
7042 
7043 void
writeJsonComputingPassOutput(ostream & output,bool writeDetails) const7044 DynamicModel::writeJsonComputingPassOutput(ostream &output, bool writeDetails) const
7045 {
7046   ostringstream model_local_vars_output; // Used for storing model local vars
7047   vector<ostringstream> d_output(derivatives.size()); // Derivatives output (at all orders, including 0=residual)
7048 
7049   deriv_node_temp_terms_t tef_terms;
7050   temporary_terms_t temp_term_union;
7051 
7052   writeJsonModelLocalVariables(model_local_vars_output, true, tef_terms);
7053 
7054   writeJsonTemporaryTerms(temporary_terms_derivatives[0], temp_term_union, d_output[0], tef_terms, "");
7055   d_output[0] << ", ";
7056   writeJsonModelEquations(d_output[0], true);
7057 
7058   int ncols = dynJacobianColsNbr;
7059   for (size_t i = 1; i < derivatives.size(); i++)
7060     {
7061       string matrix_name = i == 1 ? "jacobian" : i == 2 ? "hessian" : i == 3 ? "third_derivative" : to_string(i) + "th_derivative";
7062       writeJsonTemporaryTerms(temporary_terms_derivatives[i], temp_term_union, d_output[i], tef_terms, matrix_name);
7063       temp_term_union.insert(temporary_terms_derivatives[i].begin(), temporary_terms_derivatives[i].end());
7064       d_output[i] << R"(, ")" << matrix_name  << R"(": {)"
7065                   << R"(  "nrows": )" << equations.size()
7066                   << R"(, "ncols": )" << ncols
7067                   << R"(, "entries": [)";
7068 
7069       for (auto it = derivatives[i].begin(); it != derivatives[i].end(); ++it)
7070         {
7071           if (it != derivatives[i].begin())
7072             d_output[i] << ", ";
7073 
7074           const vector<int> &vidx = it->first;
7075           expr_t d = it->second;
7076           int eq = vidx[0];
7077 
7078           int col_idx = 0;
7079           for (size_t j = 1; j < vidx.size(); j++)
7080             {
7081               col_idx *= dynJacobianColsNbr;
7082               col_idx += getDynJacobianCol(vidx[j]);
7083             }
7084 
7085           if (writeDetails)
7086             d_output[i] << R"({"eq": )" << eq + 1;
7087           else
7088             d_output[i] << R"({"row": )" << eq + 1;
7089 
7090           d_output[i] << R"(, "col": )" << (i > 1 ? "[" : "") << col_idx + 1;
7091 
7092           if (i == 2 && vidx[1] != vidx[2]) // Symmetric elements in hessian
7093             {
7094               int col_idx_sym = getDynJacobianCol(vidx[2]) * dynJacobianColsNbr + getDynJacobianCol(vidx[1]);
7095               d_output[i] << ", " << col_idx_sym + 1;
7096             }
7097           if (i > 1)
7098             d_output[i] << "]";
7099 
7100           if (writeDetails)
7101             for (size_t j = 1; j < vidx.size(); j++)
7102               d_output[i] << R"(, "var)" << (i > 1 ? to_string(j) : "") << R"(": ")" << symbol_table.getName(getSymbIDByDerivID(vidx[j])) << R"(")"
7103                           << R"(, "shift)" << (i > 1 ? to_string(j) : "") << R"(": )" << getLagByDerivID(vidx[j]);
7104 
7105           d_output[i] << R"(, "val": ")";
7106           d->writeJsonOutput(d_output[i], temp_term_union, tef_terms);
7107           d_output[i] << R"("})" << endl;
7108         }
7109       d_output[i] << "]}";
7110 
7111       ncols *= dynJacobianColsNbr;
7112     }
7113 
7114   if (writeDetails)
7115     output << R"("dynamic_model": {)";
7116   else
7117     output << R"("dynamic_model_simple": {)";
7118   output << model_local_vars_output.str();
7119   for (const auto &it : d_output)
7120     output << ", " << it.str();
7121   output << "}";
7122 }
7123 
7124 void
writeJsonParamsDerivativesFile(ostream & output,bool writeDetails) const7125 DynamicModel::writeJsonParamsDerivativesFile(ostream &output, bool writeDetails) const
7126 {
7127   if (!params_derivatives.size())
7128     return;
7129 
7130   ostringstream model_local_vars_output; // Used for storing model local vars
7131   ostringstream model_output; // Used for storing model temp vars and equations
7132   ostringstream rp_output; // 1st deriv. of residuals w.r.t. parameters
7133   ostringstream gp_output; // 1st deriv. of Jacobian w.r.t. parameters
7134   ostringstream rpp_output; // 2nd deriv of residuals w.r.t. parameters
7135   ostringstream gpp_output; // 2nd deriv of Jacobian w.r.t. parameters
7136   ostringstream hp_output; // 1st deriv. of Hessian w.r.t. parameters
7137   ostringstream g3p_output; // 1st deriv. of 3rd deriv. matrix w.r.t. parameters
7138 
7139   deriv_node_temp_terms_t tef_terms;
7140   writeJsonModelLocalVariables(model_local_vars_output, true, tef_terms);
7141 
7142   temporary_terms_t temp_term_union;
7143   for (const auto &it : params_derivs_temporary_terms)
7144     writeJsonTemporaryTerms(it.second, temp_term_union, model_output, tef_terms, "all");
7145 
7146   rp_output << R"("deriv_wrt_params": {)"
7147             << R"(  "neqs": )" << equations.size()
7148             << R"(, "nparamcols": )" << symbol_table.param_nbr()
7149             << R"(, "entries": [)";
7150   auto &rp = params_derivatives.find({ 0, 1 })->second;
7151   for (auto it = rp.begin(); it != rp.end(); ++it)
7152     {
7153       if (it != rp.begin())
7154         rp_output << ", ";
7155 
7156       auto [eq, param] = vectorToTuple<2>(it->first);
7157       expr_t d1 = it->second;
7158 
7159       int param_col = symbol_table.getTypeSpecificID(getSymbIDByDerivID(param)) + 1;
7160 
7161       if (writeDetails)
7162         rp_output << R"({"eq": )" << eq + 1;
7163       else
7164         rp_output << R"({"row": )" << eq + 1;
7165 
7166       rp_output << R"(, "param_col": )" << param_col + 1;
7167 
7168       if (writeDetails)
7169         rp_output << R"(, "param": ")" << symbol_table.getName(getSymbIDByDerivID(param)) << R"(")";
7170 
7171       rp_output << R"(, "val": ")";
7172       d1->writeJsonOutput(rp_output, temp_term_union, tef_terms);
7173       rp_output << R"("})" << endl;
7174     }
7175   rp_output << "]}";
7176 
7177   gp_output << R"("deriv_jacobian_wrt_params": {)"
7178             << R"(  "neqs": )" << equations.size()
7179             << R"(, "nvarcols": )" << dynJacobianColsNbr
7180             << R"(, "nparamcols": )" << symbol_table.param_nbr()
7181             << R"(, "entries": [)";
7182   auto &gp = params_derivatives.find({ 1, 1 })->second;
7183   for (auto it = gp.begin(); it != gp.end(); ++it)
7184     {
7185       if (it != gp.begin())
7186         gp_output << ", ";
7187 
7188       auto [eq, var, param] = vectorToTuple<3>(it->first);
7189       expr_t d2 = it->second;
7190 
7191       int var_col = getDynJacobianCol(var) + 1;
7192       int param_col = symbol_table.getTypeSpecificID(getSymbIDByDerivID(param)) + 1;
7193 
7194       if (writeDetails)
7195         gp_output << R"({"eq": )" << eq + 1;
7196       else
7197         gp_output << R"({"row": )" << eq + 1;
7198 
7199       gp_output << R"(, "var_col": )" << var_col + 1
7200                 << R"(, "param_col": )" << param_col + 1;
7201 
7202       if (writeDetails)
7203         gp_output << R"(, "var": ")" << symbol_table.getName(getSymbIDByDerivID(var)) << R"(")"
7204                   << R"(, "lag": )" << getLagByDerivID(var)
7205                   << R"(, "param": ")" << symbol_table.getName(getSymbIDByDerivID(param)) << R"(")";
7206 
7207       gp_output << R"(, "val": ")";
7208       d2->writeJsonOutput(gp_output, temp_term_union, tef_terms);
7209       gp_output << R"("})" << endl;
7210     }
7211   gp_output << "]}";
7212 
7213   rpp_output << R"("second_deriv_residuals_wrt_params": {)"
7214              << R"(  "nrows": )" << equations.size()
7215              << R"(, "nparam1cols": )" << symbol_table.param_nbr()
7216              << R"(, "nparam2cols": )" << symbol_table.param_nbr()
7217              << R"(, "entries": [)";
7218   auto &rpp = params_derivatives.find({ 0, 2 })->second;
7219   for (auto it = rpp.begin(); it != rpp.end(); ++it)
7220     {
7221       if (it != rpp.begin())
7222         rpp_output << ", ";
7223 
7224       auto [eq, param1, param2] = vectorToTuple<3>(it->first);
7225       expr_t d2 = it->second;
7226 
7227       int param1_col = symbol_table.getTypeSpecificID(getSymbIDByDerivID(param1)) + 1;
7228       int param2_col = symbol_table.getTypeSpecificID(getSymbIDByDerivID(param2)) + 1;
7229 
7230       if (writeDetails)
7231         rpp_output << R"({"eq": )" << eq + 1;
7232       else
7233         rpp_output << R"({"row": )" << eq + 1;
7234       rpp_output << R"(, "param1_col": )" << param1_col + 1
7235                  << R"(, "param2_col": )" << param2_col + 1;
7236 
7237       if (writeDetails)
7238         rpp_output << R"(, "param1": ")" << symbol_table.getName(getSymbIDByDerivID(param1)) << R"(")"
7239                    << R"(, "param2": ")" << symbol_table.getName(getSymbIDByDerivID(param2)) << R"(")";
7240 
7241       rpp_output << R"(, "val": ")";
7242       d2->writeJsonOutput(rpp_output, temp_term_union, tef_terms);
7243       rpp_output << R"("})" << endl;
7244     }
7245   rpp_output << "]}";
7246 
7247   gpp_output << R"("second_deriv_jacobian_wrt_params": {)"
7248              << R"(  "neqs": )" << equations.size()
7249              << R"(, "nvarcols": )" << dynJacobianColsNbr
7250              << R"(, "nparam1cols": )" << symbol_table.param_nbr()
7251              << R"(, "nparam2cols": )" << symbol_table.param_nbr()
7252              << R"(, "entries": [)";
7253   auto &gpp = params_derivatives.find({ 1, 2 })->second;
7254   for (auto it = gpp.begin(); it != gpp.end(); ++it)
7255     {
7256       if (it != gpp.begin())
7257         gpp_output << ", ";
7258 
7259       auto [eq, var, param1, param2] = vectorToTuple<4>(it->first);
7260       expr_t d2 = it->second;
7261 
7262       int var_col = getDynJacobianCol(var) + 1;
7263       int param1_col = symbol_table.getTypeSpecificID(getSymbIDByDerivID(param1)) + 1;
7264       int param2_col = symbol_table.getTypeSpecificID(getSymbIDByDerivID(param2)) + 1;
7265 
7266       if (writeDetails)
7267         gpp_output << R"({"eq": )" << eq + 1;
7268       else
7269         gpp_output << R"({"row": )" << eq + 1;
7270 
7271       gpp_output << R"(, "var_col": )" << var_col + 1
7272                  << R"(, "param1_col": )" << param1_col + 1
7273                  << R"(, "param2_col": )" << param2_col + 1;
7274 
7275       if (writeDetails)
7276         gpp_output << R"(, "var": ")" << symbol_table.getName(var) << R"(")"
7277                    << R"(, "lag": )" << getLagByDerivID(var)
7278                    << R"(, "param1": ")" << symbol_table.getName(getSymbIDByDerivID(param1)) << R"(")"
7279                    << R"(, "param2": ")" << symbol_table.getName(getSymbIDByDerivID(param2)) << R"(")";
7280 
7281       gpp_output << R"(, "val": ")";
7282       d2->writeJsonOutput(gpp_output, temp_term_union, tef_terms);
7283       gpp_output << R"("})" << endl;
7284     }
7285   gpp_output << "]}" << endl;
7286 
7287   hp_output << R"("derivative_hessian_wrt_params": {)"
7288             << R"(  "neqs": )" << equations.size()
7289             << R"(, "nvar1cols": )" << dynJacobianColsNbr
7290             << R"(, "nvar2cols": )" << dynJacobianColsNbr
7291             << R"(, "nparamcols": )" << symbol_table.param_nbr()
7292             << R"(, "entries": [)";
7293   auto &hp = params_derivatives.find({ 2, 1 })->second;
7294   for (auto it = hp.begin(); it != hp.end(); ++it)
7295     {
7296       if (it != hp.begin())
7297         hp_output << ", ";
7298 
7299       auto [eq, var1, var2, param] = vectorToTuple<4>(it->first);
7300       expr_t d2 = it->second;
7301 
7302       int var1_col = getDynJacobianCol(var1) + 1;
7303       int var2_col = getDynJacobianCol(var2) + 1;
7304       int param_col = symbol_table.getTypeSpecificID(getSymbIDByDerivID(param)) + 1;
7305 
7306       if (writeDetails)
7307         hp_output << R"({"eq": )" << eq + 1;
7308       else
7309         hp_output << R"({"row": )" << eq + 1;
7310 
7311       hp_output << R"(, "var1_col": )" << var1_col + 1
7312                 << R"(, "var2_col": )" << var2_col + 1
7313                 << R"(, "param_col": )" << param_col + 1;
7314 
7315       if (writeDetails)
7316         hp_output << R"(, "var1": ")" << symbol_table.getName(getSymbIDByDerivID(var1)) << R"(")"
7317                   << R"(, "lag1": )" << getLagByDerivID(var1)
7318                   << R"(, "var2": ")" << symbol_table.getName(getSymbIDByDerivID(var2)) << R"(")"
7319                   << R"(, "lag2": )" << getLagByDerivID(var2)
7320                   << R"(, "param": ")" << symbol_table.getName(getSymbIDByDerivID(param)) << R"(")";
7321 
7322       hp_output << R"(, "val": ")";
7323       d2->writeJsonOutput(hp_output, temp_term_union, tef_terms);
7324       hp_output << R"("})" << endl;
7325     }
7326   hp_output << "]}" << endl;
7327 
7328   g3p_output << R"("derivative_g3_wrt_params": {)"
7329              << R"(  "neqs": )" << equations.size()
7330              << R"(, "nvar1cols": )" << dynJacobianColsNbr
7331              << R"(, "nvar2cols": )" << dynJacobianColsNbr
7332              << R"(, "nvar3cols": )" << dynJacobianColsNbr
7333              << R"(, "nparamcols": )" << symbol_table.param_nbr()
7334              << R"(, "entries": [)";
7335   auto &g3p = params_derivatives.find({ 3, 1 })->second;
7336   for (auto it = g3p.begin(); it != g3p.end(); ++it)
7337     {
7338       if (it != g3p.begin())
7339         g3p_output << ", ";
7340 
7341       auto [eq, var1, var2, var3, param] = vectorToTuple<5>(it->first);
7342       expr_t d2 = it->second;
7343 
7344       int var1_col = getDynJacobianCol(var1) + 1;
7345       int var2_col = getDynJacobianCol(var2) + 1;
7346       int var3_col = getDynJacobianCol(var3) + 1;
7347       int param_col = symbol_table.getTypeSpecificID(getSymbIDByDerivID(param)) + 1;
7348 
7349       if (writeDetails)
7350         g3p_output << R"({"eq": )" << eq + 1;
7351       else
7352         g3p_output << R"({"row": )" << eq + 1;
7353 
7354       g3p_output << R"(, "var1_col": )" << var1_col + 1
7355                  << R"(, "var2_col": )" << var2_col + 1
7356                  << R"(, "var3_col": )" << var3_col + 1
7357                  << R"(, "param_col": )" << param_col + 1;
7358 
7359       if (writeDetails)
7360         g3p_output << R"(, "var1": ")" << symbol_table.getName(getSymbIDByDerivID(var1)) << R"(")"
7361                    << R"(, "lag1": )" << getLagByDerivID(var1)
7362                    << R"(, "var2": ")" << symbol_table.getName(getSymbIDByDerivID(var2)) << R"(")"
7363                    << R"(, "lag2": )" << getLagByDerivID(var2)
7364                    << R"(, "var3": ")" << symbol_table.getName(getSymbIDByDerivID(var3)) << R"(")"
7365                    << R"(, "lag3": )" << getLagByDerivID(var3)
7366                    << R"(, "param": ")" << symbol_table.getName(getSymbIDByDerivID(param)) << R"(")";
7367 
7368       g3p_output << R"(, "val": ")";
7369       d2->writeJsonOutput(g3p_output, temp_term_union, tef_terms);
7370       g3p_output << R"("})" << endl;
7371     }
7372   g3p_output << "]}" << endl;
7373 
7374   if (writeDetails)
7375     output << R"("dynamic_model_params_derivative": {)";
7376   else
7377     output << R"("dynamic_model_params_derivatives_simple": {)";
7378   output << model_local_vars_output.str()
7379          << ", " << model_output.str()
7380          << ", " << rp_output.str()
7381          << ", " << gp_output.str()
7382          << ", " << rpp_output.str()
7383          << ", " << gpp_output.str()
7384          << ", " << hp_output.str()
7385          << ", " << g3p_output.str()
7386          << "}";
7387 }
7388 
7389 void
substituteVarExpectation(const map<string,expr_t> & subst_table)7390 DynamicModel::substituteVarExpectation(const map<string, expr_t> &subst_table)
7391 {
7392   for (auto &equation : equations)
7393     equation = dynamic_cast<BinaryOpNode *>(equation->substituteVarExpectation(subst_table));
7394 }
7395