1 /*
2  * Copyright © 2006-2020 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 <cstdlib>
21 #include <iostream>
22 #include <fstream>
23 #include <typeinfo>
24 #include <cassert>
25 #include <random>
26 
27 #include <filesystem>
28 
29 #include "ModFile.hh"
30 #include "ConfigFile.hh"
31 #include "ComputingTasks.hh"
32 #include "Shocks.hh"
33 
ModFile(WarningConsolidation & warnings_arg)34 ModFile::ModFile(WarningConsolidation &warnings_arg)
35   : var_model_table{symbol_table},
36     trend_component_model_table{symbol_table},
37     expressions_tree{symbol_table, num_constants, external_functions_table},
38     original_model{symbol_table, num_constants, external_functions_table,
39                    trend_component_model_table, var_model_table},
40     dynamic_model{symbol_table, num_constants, external_functions_table,
41                   trend_component_model_table, var_model_table},
42     trend_dynamic_model{symbol_table, num_constants, external_functions_table,
43                         trend_component_model_table, var_model_table},
44     ramsey_FOC_equations_dynamic_model{symbol_table, num_constants, external_functions_table,
45                                        trend_component_model_table, var_model_table},
46     orig_ramsey_dynamic_model{symbol_table, num_constants, external_functions_table,
47                               trend_component_model_table, var_model_table},
48     non_linear_equations_dynamic_model{symbol_table, num_constants, external_functions_table,
49                                        trend_component_model_table, var_model_table},
50     epilogue{symbol_table, num_constants, external_functions_table,
51              trend_component_model_table, var_model_table},
52     static_model{symbol_table, num_constants, external_functions_table},
53     steady_state_model{symbol_table, num_constants, external_functions_table, static_model},
54     warnings{warnings_arg}
55 {
56 }
57 
58 void
evalAllExpressions(bool warn_uninit)59 ModFile::evalAllExpressions(bool warn_uninit)
60 {
61   cout << "Evaluating expressions...";
62 
63   // Loop over all statements, and fill global eval context if relevant
64   for (auto &st : statements)
65     {
66       if (auto ips = dynamic_cast<InitParamStatement *>(st.get()); ips)
67         ips->fillEvalContext(global_eval_context);
68 
69       if (auto ies = dynamic_cast<InitOrEndValStatement *>(st.get()); ies)
70         ies->fillEvalContext(global_eval_context);
71 
72       if (auto lpass = dynamic_cast<LoadParamsAndSteadyStateStatement *>(st.get()); lpass)
73         lpass->fillEvalContext(global_eval_context);
74     }
75 
76   // Evaluate model local variables
77   dynamic_model.fillEvalContext(global_eval_context);
78 
79   cout << "done" << endl;
80 
81   // Check if some symbols are not initialized, and give them a zero value then
82   for (int id = 0; id <= symbol_table.maxID(); id++)
83     if (auto type = symbol_table.getType(id);
84         (type == SymbolType::endogenous || type == SymbolType::exogenous || type == SymbolType::exogenousDet
85          || type == SymbolType::parameter || type == SymbolType::modelLocalVariable)
86         && global_eval_context.find(id) == global_eval_context.end())
87       {
88         if (warn_uninit)
89           warnings << "WARNING: Can't find a numeric initial value for "
90                    << symbol_table.getName(id) << ", using zero" << endl;
91         global_eval_context[id] = 0;
92       }
93 }
94 
95 void
addStatement(unique_ptr<Statement> st)96 ModFile::addStatement(unique_ptr<Statement> st)
97 {
98   statements.push_back(move(st));
99 }
100 
101 void
addStatementAtFront(unique_ptr<Statement> st)102 ModFile::addStatementAtFront(unique_ptr<Statement> st)
103 {
104   statements.insert(statements.begin(), move(st));
105 }
106 
107 void
checkPass(bool nostrict,bool stochastic)108 ModFile::checkPass(bool nostrict, bool stochastic)
109 {
110   for (auto &statement : statements)
111     statement->checkPass(mod_file_struct, warnings);
112 
113   // Check the steady state block
114   steady_state_model.checkPass(mod_file_struct, warnings);
115 
116   // Check epilogue block
117   epilogue.checkPass(mod_file_struct, warnings);
118 
119   if (mod_file_struct.write_latex_steady_state_model_present
120       && !mod_file_struct.steady_state_model_present)
121     {
122       cerr << "ERROR: You cannot have a write_latex_steady_state_model statement without a steady_state_model block." << endl;
123       exit(EXIT_FAILURE);
124     }
125 
126   // If order option has not been set, default to 2
127   if (!mod_file_struct.order_option)
128     mod_file_struct.order_option = 2;
129 
130   param_used_with_lead_lag = dynamic_model.ParamUsedWithLeadLag();
131   if (param_used_with_lead_lag)
132     warnings << "WARNING: A parameter was used with a lead or a lag in the model block" << endl;
133 
134   bool stochastic_statement_present = mod_file_struct.stoch_simul_present
135     || mod_file_struct.estimation_present
136     || mod_file_struct.osr_present
137     || mod_file_struct.ramsey_policy_present
138     || mod_file_struct.discretionary_policy_present
139     || mod_file_struct.calib_smoother_present
140     || mod_file_struct.identification_present
141     || mod_file_struct.sensitivity_present
142     || stochastic;
143 
144   // Allow empty model only when doing a standalone BVAR estimation
145   if (dynamic_model.equation_number() == 0
146       && (mod_file_struct.check_present
147           || mod_file_struct.perfect_foresight_solver_present
148           || stochastic_statement_present))
149     {
150       cerr << "ERROR: At least one model equation must be declared!" << endl;
151       exit(EXIT_FAILURE);
152     }
153 
154   if ((mod_file_struct.ramsey_model_present || mod_file_struct.ramsey_policy_present)
155       && mod_file_struct.discretionary_policy_present)
156     {
157       cerr << "ERROR: You cannot use the discretionary_policy command when you use either ramsey_model or ramsey_policy and vice versa" << endl;
158       exit(EXIT_FAILURE);
159     }
160 
161   if (((mod_file_struct.ramsey_model_present || mod_file_struct.discretionary_policy_present)
162        && !mod_file_struct.planner_objective_present)
163       || (!(mod_file_struct.ramsey_model_present || mod_file_struct.discretionary_policy_present)
164           && mod_file_struct.planner_objective_present))
165     {
166       cerr << "ERROR: A planner_objective statement must be used with a ramsey_model, a ramsey_policy or a discretionary_policy statement and vice versa." << endl;
167       exit(EXIT_FAILURE);
168     }
169 
170   if ((mod_file_struct.osr_present && (!mod_file_struct.osr_params_present || !mod_file_struct.optim_weights_present))
171       || ((!mod_file_struct.osr_present || !mod_file_struct.osr_params_present) && mod_file_struct.optim_weights_present)
172       || ((!mod_file_struct.osr_present || !mod_file_struct.optim_weights_present) && mod_file_struct.osr_params_present))
173     {
174       cerr << "ERROR: The osr statement must be used with osr_params and optim_weights." << endl;
175       exit(EXIT_FAILURE);
176     }
177 
178   if (mod_file_struct.perfect_foresight_solver_present && stochastic_statement_present)
179     {
180       cerr << "ERROR: A .mod file cannot contain both one of {perfect_foresight_solver,simul} and one of {stoch_simul, estimation, osr, ramsey_policy, discretionary_policy}. This is not possible: one cannot mix perfect foresight context with stochastic context in the same file." << endl;
181       exit(EXIT_FAILURE);
182     }
183 
184   if (mod_file_struct.k_order_solver && byte_code)
185     {
186       cerr << "ERROR: 'k_order_solver' (which is implicit if order >= 3), is not yet compatible with 'bytecode'." << endl;
187       exit(EXIT_FAILURE);
188     }
189 
190   if (use_dll && (block || byte_code || linear_decomposition))
191     {
192       cerr << "ERROR: In 'model' block, 'use_dll' option is not compatible with 'block', 'bytecode' or 'linear_decomposition'" << endl;
193       exit(EXIT_FAILURE);
194     }
195   if (block && linear_decomposition)
196     {
197       cerr << "ERROR: In 'model' block, 'block' option is not compatible with 'linear_decomposition'" << endl;
198       exit(EXIT_FAILURE);
199     }
200 
201   if (!byte_code && linear_decomposition)
202     {
203       cerr << "ERROR: For the moment in 'model' block, 'linear_decomposition' option is compatible only with 'bytecode' option" << endl;
204       exit(EXIT_FAILURE);
205     }
206 
207   if ((block || byte_code) && dynamic_model.isModelLocalVariableUsed())
208     {
209       cerr << "ERROR: In 'model' block, 'block' or 'bytecode' options are not yet compatible with pound expressions" << endl;
210       exit(EXIT_FAILURE);
211     }
212 
213   if ((stochastic_statement_present || mod_file_struct.check_present || mod_file_struct.steady_present) && no_static)
214     {
215       cerr << "ERROR: no_static option is incompatible with stoch_simul, estimation, osr, ramsey_policy, discretionary_policy, steady and check commands" << endl;
216       exit(EXIT_FAILURE);
217     }
218 
219   if (mod_file_struct.dsge_var_estimated && !mod_file_struct.dsge_prior_weight_in_estimated_params)
220     {
221       cerr << "ERROR: When estimating a DSGE-VAR model and estimating the weight of the prior, dsge_prior_weight must "
222            << "be referenced in the estimated_params block." << endl;
223       exit(EXIT_FAILURE);
224     }
225 
226   if (symbol_table.exists("dsge_prior_weight"))
227     {
228       if (symbol_table.getType("dsge_prior_weight") != SymbolType::parameter)
229         {
230           cerr << "ERROR: dsge_prior_weight may only be used as a parameter." << endl;
231           exit(EXIT_FAILURE);
232         }
233       else
234         warnings << "WARNING: When estimating a DSGE-Var, declaring dsge_prior_weight as a "
235                  << "parameter is deprecated. The preferred method is to do this via "
236                  << "the dsge_var option in the estimation statement." << endl;
237 
238       if (mod_file_struct.dsge_var_estimated || !mod_file_struct.dsge_var_calibrated.empty())
239         {
240           cerr << "ERROR: dsge_prior_weight can either be declared as a parameter (deprecated) or via the dsge_var option "
241                << "to the estimation statement (preferred), but not both." << endl;
242           exit(EXIT_FAILURE);
243         }
244 
245       if (!mod_file_struct.dsge_prior_weight_initialized && !mod_file_struct.dsge_prior_weight_in_estimated_params)
246         {
247           cerr << "ERROR: If dsge_prior_weight is declared as a parameter, it must either be initialized or placed in the "
248                << "estimated_params block." << endl;
249           exit(EXIT_FAILURE);
250         }
251 
252       if (mod_file_struct.dsge_prior_weight_initialized && mod_file_struct.dsge_prior_weight_in_estimated_params)
253         {
254           cerr << "ERROR: dsge_prior_weight cannot be both initialized and estimated." << endl;
255           exit(EXIT_FAILURE);
256         }
257     }
258 
259   if (mod_file_struct.dsge_prior_weight_in_estimated_params)
260     if (!mod_file_struct.dsge_var_estimated && !mod_file_struct.dsge_var_calibrated.empty())
261       {
262         cerr << "ERROR: If dsge_prior_weight is in the estimated_params block, the prior weight cannot be calibrated "
263              << "via the dsge_var option in the estimation statement." << endl;
264         exit(EXIT_FAILURE);
265       }
266     else if (!mod_file_struct.dsge_var_estimated && !symbol_table.exists("dsge_prior_weight"))
267       {
268         cerr << "ERROR: If dsge_prior_weight is in the estimated_params block, it must either be declared as a parameter "
269              << "(deprecated) or the dsge_var option must be passed to the estimation statement (preferred)." << endl;
270         exit(EXIT_FAILURE);
271       }
272 
273   if (dynamic_model.staticOnlyEquationsNbr() != dynamic_model.dynamicOnlyEquationsNbr())
274     {
275       cerr << "ERROR: the number of equations marked [static] must be equal to the number of equations marked [dynamic]" << endl;
276       exit(EXIT_FAILURE);
277     }
278 
279   if (dynamic_model.staticOnlyEquationsNbr() > 0
280       && (mod_file_struct.ramsey_model_present || mod_file_struct.discretionary_policy_present))
281     {
282       cerr << "ERROR: marking equations as [static] or [dynamic] is not possible with ramsey_model, ramsey_policy or discretionary_policy" << endl;
283       exit(EXIT_FAILURE);
284     }
285 
286   if (stochastic_statement_present
287       && (dynamic_model.isUnaryOpUsed(UnaryOpcode::sign)
288           || dynamic_model.isUnaryOpUsed(UnaryOpcode::abs)
289           || dynamic_model.isBinaryOpUsed(BinaryOpcode::max)
290           || dynamic_model.isBinaryOpUsed(BinaryOpcode::min)
291           || dynamic_model.isBinaryOpUsed(BinaryOpcode::greater)
292           || dynamic_model.isBinaryOpUsed(BinaryOpcode::less)
293           || dynamic_model.isBinaryOpUsed(BinaryOpcode::greaterEqual)
294           || dynamic_model.isBinaryOpUsed(BinaryOpcode::lessEqual)
295           || dynamic_model.isBinaryOpUsed(BinaryOpcode::equalEqual)
296           || dynamic_model.isBinaryOpUsed(BinaryOpcode::different)))
297     warnings << R"(WARNING: you are using a function (max, min, abs, sign) or an operator (<, >, <=, >=, ==, !=) which is unsuitable for a stochastic context; see the reference manual, section about "Expressions", for more details.)" << endl;
298 
299   if (linear
300       && (dynamic_model.isUnaryOpUsedOnType(SymbolType::endogenous, UnaryOpcode::sign)
301           || dynamic_model.isUnaryOpUsedOnType(SymbolType::endogenous, UnaryOpcode::abs)
302           || dynamic_model.isBinaryOpUsedOnType(SymbolType::endogenous, BinaryOpcode::max)
303           || dynamic_model.isBinaryOpUsedOnType(SymbolType::endogenous, BinaryOpcode::min)
304           || dynamic_model.isBinaryOpUsedOnType(SymbolType::endogenous, BinaryOpcode::greater)
305           || dynamic_model.isBinaryOpUsedOnType(SymbolType::endogenous, BinaryOpcode::less)
306           || dynamic_model.isBinaryOpUsedOnType(SymbolType::endogenous, BinaryOpcode::greaterEqual)
307           || dynamic_model.isBinaryOpUsedOnType(SymbolType::endogenous, BinaryOpcode::lessEqual)
308           || dynamic_model.isBinaryOpUsedOnType(SymbolType::endogenous, BinaryOpcode::equalEqual)
309           || dynamic_model.isBinaryOpUsedOnType(SymbolType::endogenous, BinaryOpcode::different)))
310     {
311       cerr << "ERROR: you have declared your model 'linear' but you are using a function "
312            << "(max, min, abs, sign) or an operator (<, >, <=, >=, ==, !=) on an "
313            << "endogenous variable." << endl;
314       exit(EXIT_FAILURE);
315     }
316 
317   if (linear
318       && !mod_file_struct.perfect_foresight_solver_present
319       && (dynamic_model.isUnaryOpUsedOnType(SymbolType::exogenous, UnaryOpcode::sign)
320           || dynamic_model.isUnaryOpUsedOnType(SymbolType::exogenous, UnaryOpcode::abs)
321           || dynamic_model.isBinaryOpUsedOnType(SymbolType::exogenous, BinaryOpcode::max)
322           || dynamic_model.isBinaryOpUsedOnType(SymbolType::exogenous, BinaryOpcode::min)
323           || dynamic_model.isBinaryOpUsedOnType(SymbolType::exogenous, BinaryOpcode::greater)
324           || dynamic_model.isBinaryOpUsedOnType(SymbolType::exogenous, BinaryOpcode::less)
325           || dynamic_model.isBinaryOpUsedOnType(SymbolType::exogenous, BinaryOpcode::greaterEqual)
326           || dynamic_model.isBinaryOpUsedOnType(SymbolType::exogenous, BinaryOpcode::lessEqual)
327           || dynamic_model.isBinaryOpUsedOnType(SymbolType::exogenous, BinaryOpcode::equalEqual)
328           || dynamic_model.isBinaryOpUsedOnType(SymbolType::exogenous, BinaryOpcode::different)))
329     {
330       cerr << "ERROR: you have declared your model 'linear' but you are using a function "
331            << "(max, min, abs, sign) or an operator (<, >, <=, >=, ==, !=) on an "
332            << "exogenous variable in a non-perfect-foresight context." << endl;
333       exit(EXIT_FAILURE);
334     }
335 
336   // Test if some estimated parameters are used within the values of shocks
337   // statements (see issue #469)
338   set<int> parameters_intersect;
339   set_intersection(mod_file_struct.parameters_within_shocks_values.begin(),
340                    mod_file_struct.parameters_within_shocks_values.end(),
341                    mod_file_struct.estimated_parameters.begin(),
342                    mod_file_struct.estimated_parameters.end(),
343                    inserter(parameters_intersect, parameters_intersect.begin()));
344   if (parameters_intersect.size() > 0)
345     {
346       cerr << "ERROR: some estimated parameters (";
347       for (auto it = parameters_intersect.begin();
348            it != parameters_intersect.end();)
349         {
350           cerr << symbol_table.getName(*it);
351           if (++it != parameters_intersect.end())
352             cerr << ", ";
353         }
354       cerr << ") also appear in the expressions defining the variance/covariance matrix of shocks; this is not allowed." << endl;
355       exit(EXIT_FAILURE);
356     }
357 
358   // Check if some exogenous is not used in the model block, Issue #841
359   set<int> unusedExo0 = dynamic_model.findUnusedExogenous();
360   set<int> unusedExo;
361   set_difference(unusedExo0.begin(), unusedExo0.end(),
362                  mod_file_struct.pac_params.begin(), mod_file_struct.pac_params.end(),
363                  inserter(unusedExo, unusedExo.begin()));
364   if (unusedExo.size() > 0)
365     {
366       ostringstream unused_exos;
367       for (int it : unusedExo)
368         unused_exos << symbol_table.getName(it) << " ";
369 
370       if (nostrict)
371         warnings << "WARNING: " << unused_exos.str()
372                  << "not used in model block, removed by nostrict command-line option" << endl;
373       else
374         {
375           cerr << "ERROR: " << unused_exos.str() << "not used in model block. To bypass this error, use the `nostrict` option. This may lead to crashes or unexpected behavior." << endl;
376           exit(EXIT_FAILURE);
377         }
378     }
379 }
380 
381 void
transformPass(bool nostrict,bool stochastic,bool compute_xrefs,bool transform_unary_ops,const string & exclude_eqs,const string & include_eqs)382 ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, bool transform_unary_ops,
383                        const string &exclude_eqs, const string &include_eqs)
384 {
385   /* Save the original model (must be done before any model transformations by preprocessor)
386      — except substituting out variables which we know are constant (they
387        appear in an equation of the form: X = constant)
388      — except adl operators which we always want expanded
389      — except predetermined variables (which must be handled before the call to
390        setLeadsLagsOrig(), see preprocessor#47)
391      — except diff operators with a lead which have been expanded by
392        DataTree:AddDiff()
393   */
394   dynamic_model.includeExcludeEquations(exclude_eqs, true);
395   dynamic_model.includeExcludeEquations(include_eqs, false);
396   dynamic_model.simplifyEquations();
397   dynamic_model.substituteAdl();
398   if (symbol_table.predeterminedNbr() > 0)
399     dynamic_model.transformPredeterminedVariables();
400   dynamic_model.setLeadsLagsOrig();
401   original_model = dynamic_model;
402   dynamic_model.expandEqTags();
403 
404   // Check that all declared endogenous are used in equations
405   set<int> unusedEndogs = dynamic_model.findUnusedEndogenous();
406   bool unusedEndogsIsErr = !nostrict && !mod_file_struct.bvar_present && unusedEndogs.size();
407   for (int unusedEndog : unusedEndogs)
408     if (nostrict)
409       {
410         symbol_table.changeType(unusedEndog, SymbolType::unusedEndogenous);
411         warnings << "WARNING: '" << symbol_table.getName(unusedEndog)
412                  << "' not used in model block, removed by nostrict command-line option" << endl;
413       }
414     else if (unusedEndogsIsErr)
415       cerr << "Error: " << symbol_table.getName(unusedEndog) << " not used in the model block"<< endl;
416 
417   if (unusedEndogsIsErr)
418     exit(EXIT_FAILURE);
419 
420   // Declare endogenous used for PAC model-consistent expectations
421   for (auto &statement : statements)
422     if (auto pms = dynamic_cast<PacModelStatement *>(statement.get());
423         pms)
424       {
425         if (pms->growth)
426           pac_growth.push_back(pms->growth);
427         if (pms->aux_model_name.empty())
428           dynamic_model.declarePacModelConsistentExpectationEndogs(pms->name);
429       }
430 
431   // Get all equation tags associated with VARs and Trend Component Models
432   set<string> eqtags;
433   for (const auto &it : trend_component_model_table.getEqTags())
434     for (auto &it1 : it.second)
435       eqtags.insert(it1);
436 
437   for (const auto &it : var_model_table.getEqTags())
438     for (auto &it1 : it.second)
439       eqtags.insert(it1);
440 
441   // Create auxiliary variables and equations for unary ops
442   lag_equivalence_table_t unary_ops_nodes;
443   ExprNode::subst_table_t unary_ops_subst_table;
444   if (transform_unary_ops)
445     tie(unary_ops_nodes, unary_ops_subst_table) = dynamic_model.substituteUnaryOps();
446   else
447     // substitute only those unary ops that appear in auxiliary model equations
448     tie(unary_ops_nodes, unary_ops_subst_table) = dynamic_model.substituteUnaryOps(eqtags);
449 
450   // Create auxiliary variable and equations for Diff operators
451   auto [diff_nodes, diff_subst_table] = dynamic_model.substituteDiff(pac_growth);
452 
453   // Fill Trend Component Model Table
454   dynamic_model.fillTrendComponentModelTable();
455   original_model.fillTrendComponentModelTableFromOrigModel();
456   dynamic_model.fillTrendComponentmodelTableAREC(diff_subst_table);
457   dynamic_model.fillVarModelTable();
458   original_model.fillVarModelTableFromOrigModel();
459 
460   // Pac Model
461   int i = 0;
462   for (auto &statement : statements)
463     if (auto pms = dynamic_cast<PacModelStatement *>(statement.get()); pms)
464       {
465         if (pms->growth)
466           pms->overwriteGrowth(pac_growth.at(i++));
467 
468         int max_lag;
469         vector<int> lhs;
470         vector<bool> nonstationary;
471         string aux_model_type;
472         if (trend_component_model_table.isExistingTrendComponentModelName(pms->aux_model_name))
473           {
474             aux_model_type = "trend_component";
475             max_lag = trend_component_model_table.getMaxLag(pms->aux_model_name) + 1;
476             lhs = dynamic_model.getUndiffLHSForPac(pms->aux_model_name, diff_subst_table);
477             // All lhs variables in a trend component model are nonstationary
478             nonstationary.insert(nonstationary.end(), trend_component_model_table.getDiff(pms->aux_model_name).size(), true);
479           }
480         else if (var_model_table.isExistingVarModelName(pms->aux_model_name))
481           {
482             aux_model_type = "var";
483             max_lag = var_model_table.getMaxLag(pms->aux_model_name);
484             lhs = var_model_table.getLhs(pms->aux_model_name);
485             // nonstationary variables in a VAR are those that are in diff
486             nonstationary = var_model_table.getDiff(pms->aux_model_name);
487           }
488         else if (pms->aux_model_name.empty())
489           max_lag = 0;
490         else
491           {
492             cerr << "Error: aux_model_name not recognized as VAR model or Trend Component model" << endl;
493             exit(EXIT_FAILURE);
494           }
495         auto eqtag_and_lag = dynamic_model.walkPacParameters(pms->name);
496         original_model.getPacMaxLag(pms->name, eqtag_and_lag);
497         if (pms->aux_model_name.empty())
498           dynamic_model.addPacModelConsistentExpectationEquation(pms->name, symbol_table.getID(pms->discount),
499                                                                  eqtag_and_lag, diff_subst_table);
500         else
501           dynamic_model.fillPacModelInfo(pms->name, lhs, max_lag, aux_model_type,
502                                          eqtag_and_lag, nonstationary, pms->growth);
503         dynamic_model.substitutePacExpectation(pms->name);
504       }
505 
506   dynamic_model.addEquationsForVar();
507 
508   // Create auxiliary vars for Expectation operator
509   dynamic_model.substituteExpectation(mod_file_struct.partial_information);
510 
511   if (nonstationary_variables)
512     {
513       dynamic_model.detrendEquations();
514       trend_dynamic_model = dynamic_model;
515       dynamic_model.removeTrendVariableFromEquations();
516       const auto &trend_symbols = dynamic_model.getTrendSymbolsMap();
517       const auto &nonstationary_symbols = dynamic_model.getNonstationarySymbolsMap();
518       epilogue.detrend(trend_symbols, nonstationary_symbols);
519     }
520 
521   epilogue.toStatic();
522 
523   mod_file_struct.orig_eq_nbr = dynamic_model.equation_number();
524   if (mod_file_struct.ramsey_model_present)
525     {
526       PlannerObjectiveStatement *pos = nullptr;
527       for (auto &statement : statements)
528         if (auto pos2 = dynamic_cast<PlannerObjectiveStatement *>(statement.get()); pos2)
529           if (pos)
530             {
531               cerr << "ERROR: there can only be one planner_objective statement" << endl;
532               exit(EXIT_FAILURE);
533             }
534           else
535             pos = pos2;
536       assert(pos);
537       const StaticModel &planner_objective = pos->getPlannerObjective();
538 
539       /*
540         clone the model then clone the new equations back to the original because
541         we have to call computeDerivIDs (in computeRamseyPolicyFOCs and computingPass)
542       */
543       if (linear)
544         orig_ramsey_dynamic_model = dynamic_model;
545       ramsey_FOC_equations_dynamic_model = dynamic_model;
546       ramsey_FOC_equations_dynamic_model.computeRamseyPolicyFOCs(planner_objective);
547       ramsey_FOC_equations_dynamic_model.replaceMyEquations(dynamic_model);
548       mod_file_struct.ramsey_eq_nbr = dynamic_model.equation_number() - mod_file_struct.orig_eq_nbr;
549     }
550 
551   /* Handle var_expectation_model statements: collect information about them,
552      create the new corresponding parameters, and the expressions to replace
553      the var_expectation statements.
554      TODO: move information collection to checkPass(), within a new
555      VarModelTable class */
556   map<string, expr_t> var_expectation_subst_table;
557   for (auto &statement : statements)
558     {
559       auto vems = dynamic_cast<VarExpectationModelStatement *>(statement.get());
560       if (!vems)
561         continue;
562 
563       int max_lag;
564       vector<int> lhs;
565       auto &model_name = vems->model_name;
566       if (var_model_table.isExistingVarModelName(vems->aux_model_name))
567         {
568           max_lag = var_model_table.getMaxLag(vems->aux_model_name);
569           lhs = var_model_table.getLhs(vems->aux_model_name);
570         }
571       else if (trend_component_model_table.isExistingTrendComponentModelName(vems->aux_model_name))
572         {
573           max_lag = trend_component_model_table.getMaxLag(vems->aux_model_name) + 1;
574           lhs = dynamic_model.getUndiffLHSForPac(vems->aux_model_name, diff_subst_table);
575         }
576       else
577         {
578           cerr << "ERROR: var_expectation_model " << model_name
579                << " refers to nonexistent auxiliary model " << vems->aux_model_name << endl;
580           exit(EXIT_FAILURE);
581         }
582 
583       /* Substitute unary and diff operators in the 'expression' option, then
584          match the linear combination in the expression option */
585       vems->substituteUnaryOpNodes(unary_ops_nodes, unary_ops_subst_table);
586       vems->substituteDiff(diff_nodes, diff_subst_table);
587       vems->matchExpression();
588 
589       /* Create auxiliary parameters and the expression to be substituted into
590          the var_expectations statement */
591       auto subst_expr = dynamic_model.Zero;
592       for (int lag = 0; lag < max_lag; lag++)
593         for (auto variable : lhs)
594           {
595             string param_name = "var_expectation_model_" + model_name + '_' + symbol_table.getName(variable) + '_' + to_string(lag);
596             int new_param_id = symbol_table.addSymbol(param_name, SymbolType::parameter);
597             vems->aux_params_ids.push_back(new_param_id);
598 
599             subst_expr = dynamic_model.AddPlus(subst_expr,
600                                                dynamic_model.AddTimes(dynamic_model.AddVariable(new_param_id),
601                                                                       dynamic_model.AddVariable(variable, -lag)));
602           }
603 
604       if (var_expectation_subst_table.find(model_name) != var_expectation_subst_table.end())
605         {
606           cerr << "ERROR: model name '" << model_name << "' is used by several var_expectation_model statements" << endl;
607           exit(EXIT_FAILURE);
608         }
609       var_expectation_subst_table[model_name] = subst_expr;
610     }
611   // And finally perform the substitutions
612   dynamic_model.substituteVarExpectation(var_expectation_subst_table);
613   dynamic_model.createVariableMapping(mod_file_struct.orig_eq_nbr +
614                                       (mod_file_struct.ramsey_model_present ?
615                                        mod_file_struct.ramsey_eq_nbr : 0));
616 
617   if (mod_file_struct.stoch_simul_present
618       || mod_file_struct.estimation_present
619       || mod_file_struct.osr_present
620       || mod_file_struct.ramsey_policy_present
621       || mod_file_struct.discretionary_policy_present
622       || mod_file_struct.calib_smoother_present
623       || mod_file_struct.identification_present
624       || mod_file_struct.sensitivity_present
625       || stochastic)
626     {
627       // In stochastic models, create auxiliary vars for leads and lags greater than 2, on both endos and exos
628       dynamic_model.substituteEndoLeadGreaterThanTwo(false);
629       dynamic_model.substituteExoLead(false);
630       dynamic_model.substituteEndoLagGreaterThanTwo(false);
631       dynamic_model.substituteExoLag(false);
632     }
633   else
634     {
635       // In deterministic models, create auxiliary vars for leads and lags endogenous greater than 2, only on endos (useless on exos)
636       dynamic_model.substituteEndoLeadGreaterThanTwo(true);
637       dynamic_model.substituteEndoLagGreaterThanTwo(true);
638     }
639 
640   dynamic_model.updateVarAndTrendModel();
641 
642   if (differentiate_forward_vars)
643     dynamic_model.differentiateForwardVars(differentiate_forward_vars_subset);
644 
645   if (mod_file_struct.dsge_var_estimated || !mod_file_struct.dsge_var_calibrated.empty())
646     try
647       {
648         int sid = symbol_table.addSymbol("dsge_prior_weight", SymbolType::parameter);
649         if (!mod_file_struct.dsge_var_calibrated.empty())
650           addStatementAtFront(make_unique<InitParamStatement>(sid,
651                                                               expressions_tree.AddNonNegativeConstant(mod_file_struct.dsge_var_calibrated),
652                                                               symbol_table));
653       }
654     catch (SymbolTable::AlreadyDeclaredException &e)
655       {
656         cerr << "ERROR: dsge_prior_weight should not be declared as a model variable / parameter "
657              << "when the dsge_var option is passed to the estimation statement." << endl;
658         exit(EXIT_FAILURE);
659       }
660 
661   dynamic_model.reorderAuxiliaryEquations();
662 
663   // Freeze the symbol table
664   symbol_table.freeze();
665 
666   if (compute_xrefs)
667     dynamic_model.computeXrefs();
668 
669   /*
670     Enforce the same number of equations and endogenous, except in three cases:
671     - ramsey_model, ramsey_policy or discretionary_policy is used
672     - a BVAR command is used and there is no equation (standalone BVAR estimation)
673     - nostrict option is passed and there are more endogs than equations (dealt with before freeze)
674   */
675   if (!(mod_file_struct.ramsey_model_present || mod_file_struct.discretionary_policy_present)
676       && !(mod_file_struct.bvar_present && dynamic_model.equation_number() == 0)
677       && !(mod_file_struct.occbin_option)
678       && (dynamic_model.equation_number() != symbol_table.endo_nbr()))
679     {
680       cerr << "ERROR: There are " << dynamic_model.equation_number() << " equations but " << symbol_table.endo_nbr() << " endogenous variables!" << endl;
681       exit(EXIT_FAILURE);
682     }
683 
684   if (symbol_table.exo_det_nbr() > 0 && mod_file_struct.perfect_foresight_solver_present)
685     {
686       cerr << "ERROR: A .mod file cannot contain both one of {perfect_foresight_solver, simul} and varexo_det declaration (all exogenous variables are deterministic in this case)" << endl;
687       exit(EXIT_FAILURE);
688     }
689 
690   if (mod_file_struct.ramsey_policy_present && symbol_table.exo_det_nbr() > 0)
691     {
692       cerr << "ERROR: ramsey_policy is incompatible with deterministic exogenous variables" << endl;
693       exit(EXIT_FAILURE);
694     }
695 
696   if (mod_file_struct.identification_present && symbol_table.exo_det_nbr() > 0)
697     {
698       cerr << "ERROR: identification is incompatible with deterministic exogenous variables" << endl;
699       exit(EXIT_FAILURE);
700     }
701 
702   if (!mod_file_struct.ramsey_model_present)
703     cout << "Found " << dynamic_model.equation_number() << " equation(s)." << endl;
704   else
705     {
706       cout << "Found " << mod_file_struct.orig_eq_nbr  << " equation(s)." << endl;
707       cout << "Found " << dynamic_model.equation_number() << " FOC equation(s) for Ramsey Problem." << endl;
708     }
709 
710   if (symbol_table.exists("dsge_prior_weight"))
711     if (mod_file_struct.bayesian_irf_present)
712       {
713         if (symbol_table.exo_nbr() != symbol_table.observedVariablesNbr())
714           {
715             cerr << "ERROR: When estimating a DSGE-Var and the bayesian_irf option is passed to the estimation "
716                  << "statement, the number of shocks must equal the number of observed variables." << endl;
717             exit(EXIT_FAILURE);
718           }
719       }
720     else
721       if (symbol_table.exo_nbr() < symbol_table.observedVariablesNbr())
722         {
723           cerr << "ERROR: When estimating a DSGE-Var, the number of shocks must be "
724                << "greater than or equal to the number of observed variables." << endl;
725           exit(EXIT_FAILURE);
726         }
727 }
728 
729 void
computingPass(bool no_tmp_terms,FileOutputType output,int params_derivs_order)730 ModFile::computingPass(bool no_tmp_terms, FileOutputType output, int params_derivs_order)
731 {
732   // Mod file may have no equation (for example in a standalone BVAR estimation)
733   if (dynamic_model.equation_number() > 0)
734     {
735       if (nonstationary_variables)
736         trend_dynamic_model.runTrendTest(global_eval_context);
737 
738       // Compute static model and its derivatives
739       static_model = static_cast<StaticModel>(dynamic_model);
740       if (linear_decomposition)
741         {
742           non_linear_equations_dynamic_model = dynamic_model;
743           non_linear_equations_dynamic_model.set_cutoff_to_zero();
744           non_linear_equations_dynamic_model.computingPass(true, 1, 0, global_eval_context, no_tmp_terms, block, use_dll, byte_code, linear_decomposition);
745         }
746       if (!no_static)
747         {
748           if (mod_file_struct.stoch_simul_present
749               || mod_file_struct.estimation_present || mod_file_struct.osr_present
750               || mod_file_struct.ramsey_model_present || mod_file_struct.identification_present
751               || mod_file_struct.calib_smoother_present)
752             static_model.set_cutoff_to_zero();
753 
754           int derivsOrder = 1;
755           int paramsDerivsOrder = 0;
756           if (mod_file_struct.identification_present || mod_file_struct.estimation_analytic_derivation)
757             {
758               derivsOrder = 2;
759               paramsDerivsOrder = params_derivs_order;
760             }
761           static_model.computingPass(derivsOrder, paramsDerivsOrder, global_eval_context, no_tmp_terms, block, byte_code);
762         }
763       // Set things to compute for dynamic model
764       if (mod_file_struct.perfect_foresight_solver_present || mod_file_struct.check_present
765           || mod_file_struct.stoch_simul_present
766           || mod_file_struct.estimation_present || mod_file_struct.osr_present
767           || mod_file_struct.ramsey_model_present || mod_file_struct.identification_present
768           || mod_file_struct.calib_smoother_present)
769         {
770           if (mod_file_struct.perfect_foresight_solver_present)
771             dynamic_model.computingPass(true, 1, 0, global_eval_context, no_tmp_terms, block, use_dll, byte_code, linear_decomposition);
772           else
773             {
774               if (mod_file_struct.stoch_simul_present
775                   || mod_file_struct.estimation_present || mod_file_struct.osr_present
776                   || mod_file_struct.ramsey_model_present || mod_file_struct.identification_present
777                   || mod_file_struct.calib_smoother_present)
778                 dynamic_model.set_cutoff_to_zero();
779               if (mod_file_struct.order_option < 1)
780                 {
781                   cerr << "ERROR: Incorrect order option..." << endl;
782                   exit(EXIT_FAILURE);
783                 }
784               int derivsOrder = max(mod_file_struct.order_option,
785                                     mod_file_struct.identification_order + 1); // See preprocessor#40
786               if (mod_file_struct.sensitivity_present || linear || output == FileOutputType::second)
787                 derivsOrder = max(derivsOrder, 2);
788               if (mod_file_struct.estimation_analytic_derivation || output == FileOutputType::third)
789                 derivsOrder = max(derivsOrder, 3);
790               int paramsDerivsOrder = 0;
791               if (mod_file_struct.identification_present || mod_file_struct.estimation_analytic_derivation)
792                 paramsDerivsOrder = params_derivs_order;
793               dynamic_model.computingPass(true, derivsOrder, paramsDerivsOrder, global_eval_context, no_tmp_terms, block, use_dll, byte_code, linear_decomposition);
794               if (linear && mod_file_struct.ramsey_model_present)
795                 orig_ramsey_dynamic_model.computingPass(true, 2, paramsDerivsOrder, global_eval_context, no_tmp_terms, block, use_dll, byte_code, linear_decomposition);
796             }
797         }
798       else // No computing task requested, compute derivatives up to 2nd order by default
799         dynamic_model.computingPass(true, 2, 0, global_eval_context, no_tmp_terms, block, use_dll, byte_code, linear_decomposition);
800 
801       /* Check that the model is linear.
802          FIXME: this check always passes if derivsOrder = 1, i.e. for a perfect
803          foresight model, because the Hessian is not computed in that case. */
804       if (linear)
805         {
806           set<int> eqs;
807           if (mod_file_struct.ramsey_model_present)
808             eqs = orig_ramsey_dynamic_model.getNonZeroHessianEquations();
809           else
810             eqs = dynamic_model.getNonZeroHessianEquations();
811 
812           if (!eqs.empty())
813             {
814               cerr << "ERROR: If the model is declared linear the second derivatives must be equal to zero." << endl
815                    << "       The following equations have non-zero second derivatives:" << endl;
816               for (const auto &it : eqs)
817                 {
818                   cerr << "       * Eq # " << it+1;
819                   auto tags = dynamic_model.getEquationTags(it);
820                   if (auto it2 = tags.find("name"); it2 != tags.end())
821                     cerr << " [" << it2->second << "]";
822                   cerr << endl;
823                 }
824               exit(EXIT_FAILURE);
825             }
826         }
827     }
828 
829   for (auto &statement : statements)
830     statement->computingPass();
831 
832   // Compute epilogue derivatives (but silence standard output)
833   streambuf *oldcout = cout.rdbuf();
834   cout.rdbuf(nullptr);
835   epilogue.computingPass(true, 2, 0, global_eval_context, true, false, false, false, false);
836   cout.rdbuf(oldcout);
837 }
838 
839 void
writeOutputFiles(const string & basename,bool clear_all,bool clear_global,bool no_log,bool no_warn,bool console,bool nograph,bool nointeractive,const ConfigFile & config_file,bool check_model_changes,bool minimal_workspace,bool compute_xrefs,const string & mexext,const filesystem::path & matlabroot,const filesystem::path & dynareroot,bool onlymodel,bool gui) const840 ModFile::writeOutputFiles(const string &basename, bool clear_all, bool clear_global, bool no_log, bool no_warn,
841                           bool console, bool nograph, bool nointeractive, const ConfigFile &config_file,
842                           bool check_model_changes, bool minimal_workspace, bool compute_xrefs,
843                           const string &mexext,
844                           const filesystem::path &matlabroot,
845                           const filesystem::path &dynareroot, bool onlymodel, bool gui) const
846 {
847   bool hasModelChanged = !dynamic_model.isChecksumMatching(basename, block) || !check_model_changes;
848   if (hasModelChanged)
849     {
850       // Erase possible remnants of previous runs
851       /* Under MATLAB+Windows (but not under Octave nor under GNU/Linux or
852          macOS), if we directly remove the "+" subdirectory, then the
853          preprocessor is not able to recreate it afterwards (presumably because
854          MATLAB maintains some sort of lock on it). The workaround is to rename
855          it before deleting it (the renaming must occur in the same directory,
856          otherwise it may file if the destination is not on the same
857          filesystem). */
858       if (filesystem::path plusfolder{"+" + basename}; filesystem::exists(plusfolder))
859         {
860           if (filesystem::exists(plusfolder / "+objective"))
861             {
862               // Do it recursively for the +objective folder, created by ramsey_policy
863               auto tmp2 = unique_path();
864               filesystem::rename(plusfolder / "+objective", tmp2);
865               filesystem::remove_all(tmp2);
866             }
867 
868           auto tmp = unique_path();
869           filesystem::rename(plusfolder, tmp);
870           filesystem::remove_all(tmp);
871         }
872       filesystem::remove_all(basename + "/model/src");
873       filesystem::remove_all(basename + "/model/bytecode");
874     }
875 
876   ofstream mOutputFile;
877 
878   if (basename.size())
879     {
880       filesystem::create_directory("+" + basename);
881       string fname = "+" + basename + "/driver.m";
882       mOutputFile.open(fname, ios::out | ios::binary);
883       if (!mOutputFile.is_open())
884         {
885           cerr << "ERROR: Can't open file " << fname << " for writing" << endl;
886           exit(EXIT_FAILURE);
887         }
888     }
889   else
890     {
891       cerr << "ERROR: Missing file name" << endl;
892       exit(EXIT_FAILURE);
893     }
894 
895   mOutputFile << "%" << endl
896               << "% Status : main Dynare file" << endl
897               << "%" << endl
898               << "% Warning : this file is generated automatically by Dynare" << endl
899               << "%           from model file (.mod)" << endl << endl;
900 
901   if (no_warn)
902     mOutputFile << "warning off" << endl; // This will be executed *after* function warning_config()
903 
904   if (clear_all)
905     mOutputFile << "if isoctave || matlab_ver_less_than('8.6')" << endl
906                 << "    clear all" << endl
907                 << "else" << endl
908                 << "    clearvars -global" << endl
909                 << "    clear_persistent_variables(fileparts(which('dynare')), false)" << endl
910                 << "end" << endl;
911   else if (clear_global)
912     mOutputFile << "clear M_ options_ oo_ estim_params_ bayestopt_ dataset_ dataset_info estimation_info ys0_ ex0_;" << endl;
913 
914   mOutputFile << "tic0 = tic;" << endl
915               << "% Define global variables." << endl
916               << "global M_ options_ oo_ estim_params_ bayestopt_ dataset_ dataset_info estimation_info ys0_ ex0_" << endl
917               << "options_ = [];" << endl
918               << "M_.fname = '" << basename << "';" << endl
919               << "M_.dynare_version = '" << PACKAGE_VERSION << "';" << endl
920               << "oo_.dynare_version = '" << PACKAGE_VERSION << "';" << endl
921               << "options_.dynare_version = '" << PACKAGE_VERSION << "';" << endl
922               << "%" << endl
923               << "% Some global variables initialization" << endl
924               << "%" << endl;
925   if (!onlymodel)
926     config_file.writeHooks(mOutputFile);
927   mOutputFile << "global_initialization;" << endl
928               << "diary off;" << endl;
929   if (!no_log)
930     mOutputFile << "diary('" << basename << ".log');" << endl;
931 
932   if (minimal_workspace)
933     mOutputFile << "options_.minimal_workspace = true;" << endl;
934 
935   if (console)
936     mOutputFile << "options_.console_mode = true;" << endl
937                 << "options_.nodisplay = true;" << endl;
938   if (nograph)
939     mOutputFile << "options_.nograph = true;" << endl;
940 
941   if (nointeractive)
942     mOutputFile << "options_.nointeractive = true;" << endl;
943 
944   if (param_used_with_lead_lag)
945     mOutputFile << "M_.parameter_used_with_lead_lag = true;" << endl;
946 
947   cout << "Processing outputs ..." << endl;
948 
949   symbol_table.writeOutput(mOutputFile);
950 
951   var_model_table.writeOutput(basename, mOutputFile);
952   trend_component_model_table.writeOutput(basename, mOutputFile);
953 
954   // Initialize M_.Sigma_e, M_.Correlation_matrix, M_.H, and M_.Correlation_matrix_ME
955   mOutputFile << "M_.Sigma_e = zeros(" << symbol_table.exo_nbr() << ", "
956               << symbol_table.exo_nbr() << ");" << endl
957               << "M_.Correlation_matrix = eye(" << symbol_table.exo_nbr() << ", "
958               << symbol_table.exo_nbr() << ");" << endl;
959 
960   if (mod_file_struct.calibrated_measurement_errors)
961     mOutputFile << "M_.H = zeros(" << symbol_table.observedVariablesNbr() << ", "
962                 << symbol_table.observedVariablesNbr() << ");" << endl
963                 << "M_.Correlation_matrix_ME = eye(" << symbol_table.observedVariablesNbr() << ", "
964                 << symbol_table.observedVariablesNbr() << ");" << endl;
965   else
966     mOutputFile << "M_.H = 0;" << endl
967                 << "M_.Correlation_matrix_ME = 1;" << endl;
968 
969   // May be later modified by a shocks block
970   mOutputFile << "M_.sigma_e_is_diagonal = true;" << endl;
971 
972   // Initialize M_.det_shocks
973   mOutputFile << "M_.det_shocks = [];" << endl;
974 
975   auto to_matlab_logical = [](bool m) { return m ? "true" : "false"; };
976 
977   mOutputFile << "options_.linear = " << to_matlab_logical(linear) << ";" << endl
978               << "options_.block = " << to_matlab_logical(block) << ";" << endl
979               << "options_.bytecode = " << to_matlab_logical(byte_code) << ";" << endl
980               << "options_.use_dll = " << to_matlab_logical(use_dll) << ";" << endl
981               << "options_.linear_decomposition = " << to_matlab_logical(linear_decomposition) << ";" << endl;
982 
983   if (parallel_local_files.size() > 0)
984     {
985       mOutputFile << "options_.parallel_info.local_files = {" << endl;
986       for (const auto &parallel_local_file : parallel_local_files)
987         {
988           size_t j = parallel_local_file.find_last_of(R"(/\)");
989           if (j == string::npos)
990             mOutputFile << "'', '" << parallel_local_file << "';" << endl;
991           else
992             mOutputFile << "'" << parallel_local_file.substr(0, j+1) << "', '"
993                         << parallel_local_file.substr(j+1, string::npos) << "';" << endl;
994         }
995       mOutputFile << "};" << endl;
996     }
997 
998   if (dynamic_model.isHessianComputed())
999     {
1000       mOutputFile << "M_.nonzero_hessian_eqs = ";
1001       dynamic_model.printNonZeroHessianEquations(mOutputFile);
1002       mOutputFile << ";" << endl
1003                   << "M_.hessian_eq_zero = isempty(M_.nonzero_hessian_eqs);" << endl;
1004     }
1005 
1006   if (!onlymodel)
1007     config_file.writeCluster(mOutputFile);
1008 
1009   if (byte_code)
1010     mOutputFile << "if exist('bytecode') ~= 3" << endl
1011                 << "  error('DYNARE: Can''t find bytecode DLL. Please compile it or remove the ''bytecode'' option.')" << endl
1012                 << "end" << endl;
1013 
1014   mOutputFile << "M_.orig_eq_nbr = " << mod_file_struct.orig_eq_nbr << ";" << endl
1015               << "M_.eq_nbr = " << dynamic_model.equation_number() << ";" << endl
1016               << "M_.ramsey_eq_nbr = " << mod_file_struct.ramsey_eq_nbr << ";" << endl
1017               << "M_.set_auxiliary_variables = exist(['./+' M_.fname '/set_auxiliary_variables.m'], 'file') == 2;" << endl;
1018 
1019   epilogue.writeOutput(mOutputFile);
1020 
1021   if (dynamic_model.equation_number() > 0)
1022     {
1023       if (linear_decomposition)
1024         non_linear_equations_dynamic_model.writeOutput(mOutputFile, basename, block, true, byte_code, use_dll, mod_file_struct.estimation_present, compute_xrefs, false);
1025       dynamic_model.writeOutput(mOutputFile, basename, block, false, byte_code, use_dll, mod_file_struct.estimation_present, compute_xrefs, false);
1026       if (!no_static)
1027         static_model.writeOutput(mOutputFile, block);
1028     }
1029 
1030   if (onlymodel || gui)
1031     for (auto &statement : statements)
1032       {
1033         /* Special treatment for initval block: insert initial values for the
1034            auxiliary variables and initialize exo det */
1035         if (auto ivs = dynamic_cast<InitValStatement *>(statement.get()); ivs)
1036           {
1037             ivs->writeOutput(mOutputFile, basename, minimal_workspace);
1038             static_model.writeAuxVarInitval(mOutputFile, ExprNodeOutputType::matlabOutsideModel);
1039             ivs->writeOutputPostInit(mOutputFile);
1040           }
1041 
1042         // Special treatment for endval block: insert initial values for the auxiliary variables
1043         if (auto evs = dynamic_cast<EndValStatement *>(statement.get()); evs)
1044           {
1045             evs->writeOutput(mOutputFile, basename, minimal_workspace);
1046             static_model.writeAuxVarInitval(mOutputFile, ExprNodeOutputType::matlabOutsideModel);
1047           }
1048 
1049         if (auto ips = dynamic_cast<InitParamStatement *>(statement.get()); ips)
1050           ips->writeOutput(mOutputFile, basename, minimal_workspace);
1051 
1052         if (auto ss = dynamic_cast<ShocksStatement *>(statement.get()); ss)
1053           ss->writeOutput(mOutputFile, basename, minimal_workspace);
1054 
1055         if (auto eps = dynamic_cast<EstimatedParamsStatement *>(statement.get()); eps)
1056           eps->writeOutput(mOutputFile, basename, minimal_workspace);
1057 
1058         if (auto sgs = dynamic_cast<ShockGroupsStatement *>(statement.get()); sgs)
1059           sgs->writeOutput(mOutputFile, basename, minimal_workspace);
1060 
1061         if (gui)
1062           if (auto it = dynamic_cast<NativeStatement *>(statement.get()); it)
1063             it->writeOutput(mOutputFile, basename, minimal_workspace);
1064       }
1065   else
1066     {
1067       for (auto &statement : statements)
1068         {
1069           statement->writeOutput(mOutputFile, basename, minimal_workspace);
1070 
1071           /* Special treatment for initval block: insert initial values for the
1072              auxiliary variables and initialize exo det */
1073           if (auto ivs = dynamic_cast<InitValStatement *>(statement.get()); ivs)
1074             {
1075               static_model.writeAuxVarInitval(mOutputFile, ExprNodeOutputType::matlabOutsideModel);
1076               ivs->writeOutputPostInit(mOutputFile);
1077             }
1078 
1079           // Special treatment for endval block: insert initial values for the auxiliary variables
1080           if (auto evs = dynamic_cast<EndValStatement *>(statement.get()); evs)
1081             static_model.writeAuxVarInitval(mOutputFile, ExprNodeOutputType::matlabOutsideModel);
1082 
1083           // Special treatment for load params and steady state statement: insert initial values for the auxiliary variables
1084           if (auto lpass = dynamic_cast<LoadParamsAndSteadyStateStatement *>(statement.get()); lpass && !no_static)
1085             static_model.writeAuxVarInitval(mOutputFile, ExprNodeOutputType::matlabOutsideModel);
1086         }
1087 
1088       mOutputFile << "save('" << basename << "_results.mat', 'oo_', 'M_', 'options_');" << endl
1089                   << "if exist('estim_params_', 'var') == 1" << endl
1090                   << "  save('" << basename << "_results.mat', 'estim_params_', '-append');" << endl << "end" << endl
1091                   << "if exist('bayestopt_', 'var') == 1" << endl
1092                   << "  save('" << basename << "_results.mat', 'bayestopt_', '-append');" << endl << "end" << endl
1093                   << "if exist('dataset_', 'var') == 1" << endl
1094                   << "  save('" << basename << "_results.mat', 'dataset_', '-append');" << endl << "end" << endl
1095                   << "if exist('estimation_info', 'var') == 1" << endl
1096                   << "  save('" << basename << "_results.mat', 'estimation_info', '-append');" << endl << "end" << endl
1097                   << "if exist('dataset_info', 'var') == 1" << endl
1098                   << "  save('" << basename << "_results.mat', 'dataset_info', '-append');" << endl << "end" << endl
1099                   << "if exist('oo_recursive_', 'var') == 1" << endl
1100                   << "  save('" << basename << "_results.mat', 'oo_recursive_', '-append');" << endl << "end" << endl;
1101 
1102       config_file.writeEndParallel(mOutputFile);
1103 
1104       mOutputFile << endl << endl
1105                   << "disp(['Total computing time : ' dynsec2hms(toc(tic0)) ]);" << endl;
1106 
1107       if (!no_warn)
1108         {
1109           if (warnings.countWarnings() > 0)
1110             mOutputFile << "disp('Note: " << warnings.countWarnings() << " warning(s) encountered in the preprocessor')" << endl;
1111 
1112           mOutputFile << "if ~isempty(lastwarn)" << endl
1113                       << "  disp('Note: warning(s) encountered in MATLAB/Octave code')" << endl
1114                       << "end" << endl;
1115         }
1116     }
1117 
1118   if (!no_log)
1119     mOutputFile << "diary off" << endl;
1120 
1121   mOutputFile.close();
1122 
1123   if (hasModelChanged)
1124     {
1125       // Create static and dynamic files
1126       if (dynamic_model.equation_number() > 0)
1127         {
1128           if (!no_static)
1129             {
1130               static_model.writeStaticFile(basename, block, byte_code, use_dll, mexext, matlabroot, dynareroot, false);
1131               static_model.writeParamsDerivativesFile(basename, false);
1132             }
1133 
1134           if (linear_decomposition)
1135             {
1136               non_linear_equations_dynamic_model.writeDynamicFile(basename, block, linear_decomposition, byte_code, use_dll, mexext, matlabroot, dynareroot, false);
1137               non_linear_equations_dynamic_model.writeParamsDerivativesFile(basename, false);
1138             }
1139 
1140           dynamic_model.writeDynamicFile(basename, block, false, byte_code, use_dll, mexext, matlabroot, dynareroot, false);
1141 
1142           dynamic_model.writeParamsDerivativesFile(basename, false);
1143 
1144           dynamic_model.writeDynamicJacobianNonZeroElts(basename);
1145         }
1146 
1147       // Create steady state file
1148       steady_state_model.writeSteadyStateFile(basename, mod_file_struct.ramsey_model_present, false);
1149 
1150       // Create epilogue file
1151       epilogue.writeEpilogueFile(basename);
1152     }
1153 
1154   cout << "done" << endl;
1155 }
1156 
1157 void
writeExternalFiles(const string & basename,LanguageOutputType language) const1158 ModFile::writeExternalFiles(const string &basename, LanguageOutputType language) const
1159 {
1160   switch (language)
1161     {
1162     case LanguageOutputType::julia:
1163       writeExternalFilesJulia(basename);
1164       break;
1165     case LanguageOutputType::matlab:
1166       cerr << "The 'output' option cannot be used when language=matlab" << endl;
1167       exit(EXIT_FAILURE);
1168     }
1169 }
1170 
1171 void
writeExternalFilesJulia(const string & basename) const1172 ModFile::writeExternalFilesJulia(const string &basename) const
1173 {
1174   ofstream jlOutputFile;
1175   if (basename.size())
1176     {
1177       string fname(basename);
1178       fname += ".jl";
1179       jlOutputFile.open(fname, ios::out | ios::binary);
1180       if (!jlOutputFile.is_open())
1181         {
1182           cerr << "ERROR: Can't open file " << fname
1183                << " for writing" << endl;
1184           exit(EXIT_FAILURE);
1185         }
1186     }
1187   else
1188     {
1189       cerr << "ERROR: Missing file name" << endl;
1190       exit(EXIT_FAILURE);
1191     }
1192 
1193   jlOutputFile << "module " << basename << endl
1194                << "#" << endl
1195                << "# NB: this file was automatically generated by Dynare" << endl
1196                << "#     from " << basename << ".mod" << endl
1197                << "#" << endl << endl
1198                << "using DynareModel" << endl
1199                << "using DynareOptions" << endl
1200                << "using DynareOutput" << endl << endl
1201                << "using Utils" << endl
1202                << "using SteadyState" << endl << endl
1203                << "using " << basename << "Static" << endl
1204                << "using " << basename << "Dynamic" << endl
1205                << R"(if isfile(")" << basename << R"(SteadyState.jl"))" << endl
1206                << "    using " << basename << "SteadyState" << endl
1207                << "end" << endl
1208                << R"(if isfile(")" << basename << R"(SteadyState2.jl"))" << endl
1209                << "    using " << basename << "SteadyState2" << endl
1210                << "end" << endl << endl
1211                << "export model_, options_, oo_" << endl;
1212 
1213   // Write Output
1214   jlOutputFile << endl
1215                << "oo_ = dynare_output()" << endl
1216                << R"(oo_.dynare_version = ")" << PACKAGE_VERSION << R"(")" << endl;
1217 
1218   // Write Options
1219   jlOutputFile << endl
1220                << "options_ = dynare_options()" << endl
1221                << R"(options_.dynare_version = ")" << PACKAGE_VERSION << R"(")" << endl;
1222   if (linear)
1223     jlOutputFile << "options_.linear = true" << endl;
1224 
1225   // Write Model
1226   jlOutputFile << endl
1227                << "model_ = dynare_model()" << endl
1228                << R"(model_.fname = ")" << basename << R"(")" << endl
1229                << R"(model_.dynare_version = ")" << PACKAGE_VERSION << R"(")" << endl
1230                << "model_.sigma_e = zeros(Float64, " << symbol_table.exo_nbr() << ", "
1231                << symbol_table.exo_nbr() << ")" << endl
1232                << "model_.correlation_matrix = ones(Float64, " << symbol_table.exo_nbr() << ", "
1233                << symbol_table.exo_nbr() << ")" << endl
1234                << "model_.orig_eq_nbr = " << mod_file_struct.orig_eq_nbr << endl
1235                << "model_.eq_nbr = " << dynamic_model.equation_number() << endl
1236                << "model_.ramsey_eq_nbr = " << mod_file_struct.ramsey_eq_nbr << endl;
1237 
1238   if (mod_file_struct.calibrated_measurement_errors)
1239     jlOutputFile << "model_.h = zeros(Float64,"
1240                  << symbol_table.observedVariablesNbr() << ", "
1241                  << symbol_table.observedVariablesNbr() << ");" << endl
1242                  << "model_.correlation_matrix_me = ones(Float64, "
1243                  << symbol_table.observedVariablesNbr() << ", "
1244                  << symbol_table.observedVariablesNbr() << ");" << endl;
1245   else
1246     jlOutputFile << "model_.h = zeros(Float64, 1, 1)" << endl
1247                  << "model_.correlation_matrix_me = ones(Float64, 1, 1)" << endl;
1248 
1249   cout << "Processing outputs ..." << endl;
1250   symbol_table.writeJuliaOutput(jlOutputFile);
1251 
1252   if (dynamic_model.equation_number() > 0)
1253     {
1254       dynamic_model.writeOutput(jlOutputFile, basename, false, false, false, false,
1255                                 mod_file_struct.estimation_present, false, true);
1256       if (!no_static)
1257         {
1258           static_model.writeStaticFile(basename, false, false, false, "", {}, {}, true);
1259           static_model.writeParamsDerivativesFile(basename, true);
1260         }
1261       dynamic_model.writeDynamicFile(basename, block, linear_decomposition, byte_code, use_dll,
1262                                      "", {}, {}, true);
1263       dynamic_model.writeParamsDerivativesFile(basename, true);
1264     }
1265   steady_state_model.writeSteadyStateFile(basename, mod_file_struct.ramsey_model_present, true);
1266 
1267   // Print statements (includes parameter values)
1268   for (auto &statement : statements)
1269     statement->writeJuliaOutput(jlOutputFile, basename);
1270 
1271   jlOutputFile << "model_.static = " << basename << "Static.static!" << endl
1272                << "model_.dynamic = " << basename << "Dynamic.dynamic!" << endl
1273                << "model_.temporaries.static = " << basename << "Static.tmp_nbr" << endl
1274                << "model_.temporaries.dynamic = " << basename << "Dynamic.tmp_nbr" << endl
1275                << R"(if isfile(")" << basename << R"(SteadyState.jl"))" << endl
1276                << "    model_.user_written_analytical_steady_state = true" << endl
1277                << "    model_.steady_state = " << basename << "SteadyState.steady_state!" << endl
1278                << "end" << endl
1279                << R"(if isfile(")" << basename << R"(SteadyState2.jl"))" << endl
1280                << "    model_.analytical_steady_state = true" << endl
1281                << "    model_.steady_state = " << basename << "SteadyState2.steady_state!" << endl
1282                << "end" << endl
1283                << R"(if isfile(")" << basename << R"(StaticParamsDerivs.jl"))" << endl
1284                << "    using " << basename << "StaticParamsDerivs" << endl
1285                << "    model_.static_params_derivs = " << basename << "StaticParamsDerivs.params_derivs" << endl
1286                << "end" << endl
1287                << R"(if isfile(")" << basename << R"(DynamicParamsDerivs.jl"))" << endl
1288                << "    using " << basename << "DynamicParamsDerivs" << endl
1289                << "    model_.dynamic_params_derivs = " << basename << "DynamicParamsDerivs.params_derivs" << endl
1290                << "end" << endl
1291                << "end" << endl;
1292   jlOutputFile.close();
1293   cout << "done" << endl;
1294 }
1295 
1296 void
writeJsonOutput(const string & basename,JsonOutputPointType json,JsonFileOutputType json_output_mode,bool onlyjson,bool jsonderivsimple)1297 ModFile::writeJsonOutput(const string &basename, JsonOutputPointType json, JsonFileOutputType json_output_mode, bool onlyjson, bool jsonderivsimple)
1298 {
1299   if (json == JsonOutputPointType::nojson)
1300     return;
1301 
1302   if (json == JsonOutputPointType::parsing || json == JsonOutputPointType::checkpass)
1303     symbol_table.freeze();
1304 
1305   if (json_output_mode == JsonFileOutputType::standardout)
1306     cout << "//-- BEGIN JSON --// " << endl
1307          << "{" << endl;
1308 
1309   writeJsonOutputParsingCheck(basename, json_output_mode, json == JsonOutputPointType::transformpass, json == JsonOutputPointType::computingpass);
1310 
1311   if (json == JsonOutputPointType::parsing || json == JsonOutputPointType::checkpass)
1312     symbol_table.unfreeze();
1313 
1314   if (json == JsonOutputPointType::computingpass)
1315     writeJsonComputingPassOutput(basename, json_output_mode, jsonderivsimple);
1316 
1317   if (json_output_mode == JsonFileOutputType::standardout)
1318     cout << "}" << endl
1319          << "//-- END JSON --// " << endl;
1320 
1321   switch (json)
1322     {
1323     case JsonOutputPointType::parsing:
1324       cout << "JSON written after Parsing step." << endl;
1325       break;
1326     case JsonOutputPointType::checkpass:
1327       cout << "JSON written after Check step." << endl;
1328       break;
1329     case JsonOutputPointType::transformpass:
1330       cout << "JSON written after Transform step." << endl;
1331       break;
1332     case JsonOutputPointType::computingpass:
1333       cout << "JSON written after Computing step." << endl;
1334       break;
1335     case JsonOutputPointType::nojson:
1336       cerr << "ModFile::writeJsonOutput: should not arrive here." << endl;
1337       exit(EXIT_FAILURE);
1338     }
1339 
1340   if (onlyjson)
1341     exit(EXIT_SUCCESS);
1342 }
1343 
1344 void
writeJsonOutputParsingCheck(const string & basename,JsonFileOutputType json_output_mode,bool transformpass,bool computingpass) const1345 ModFile::writeJsonOutputParsingCheck(const string &basename, JsonFileOutputType json_output_mode, bool transformpass, bool computingpass) const
1346 {
1347   ostringstream output;
1348   output << "{" << endl;
1349 
1350   symbol_table.writeJsonOutput(output);
1351   output << ", ";
1352   dynamic_model.writeJsonOutput(output);
1353 
1354   if (!statements.empty()
1355       || !var_model_table.empty()
1356       || !trend_component_model_table.empty())
1357     {
1358       output << R"(, "statements": [)";
1359       if (!var_model_table.empty())
1360         {
1361           var_model_table.writeJsonOutput(output);
1362           output << ", ";
1363         }
1364 
1365       if (!trend_component_model_table.empty())
1366         {
1367           trend_component_model_table.writeJsonOutput(output);
1368           output << ", ";
1369         }
1370 
1371       for (auto it = statements.begin(); it != statements.end(); ++it)
1372         {
1373           if (it != statements.begin())
1374             output << ", " << endl;
1375           (*it)->writeJsonOutput(output);
1376         }
1377       output << "]" << endl;
1378     }
1379 
1380   if (computingpass)
1381     {
1382       output << ",";
1383       dynamic_model.writeJsonDynamicModelInfo(output);
1384     }
1385   output << "}" << endl;
1386 
1387   ostringstream original_model_output;
1388   original_model_output << "";
1389   if (transformpass || computingpass)
1390     {
1391       original_model_output << "{";
1392       original_model.writeJsonOriginalModelOutput(original_model_output);
1393       if (!statements.empty() || !var_model_table.empty() || !trend_component_model_table.empty())
1394         {
1395           original_model_output << endl << R"(, "statements": [)";
1396           if (!var_model_table.empty())
1397             {
1398               var_model_table.writeJsonOutput(original_model_output);
1399               original_model_output << ", ";
1400             }
1401           if (!trend_component_model_table.empty())
1402             {
1403               trend_component_model_table.writeJsonOutput(original_model_output);
1404               original_model_output << ", ";
1405             }
1406           int i = 0;
1407           for (const auto &it : statements)
1408             {
1409               original_model_output << (i++ > 0 ? "," : "") << endl;
1410               it->writeJsonOutput(original_model_output);
1411             }
1412           original_model_output << "]" << endl;
1413         }
1414       original_model_output << "}" << endl;
1415     }
1416 
1417   ostringstream steady_state_model_output;
1418   steady_state_model_output << "";
1419   if (dynamic_model.equation_number() > 0)
1420     steady_state_model.writeJsonSteadyStateFile(steady_state_model_output,
1421                                                 transformpass || computingpass);
1422 
1423   if (json_output_mode == JsonFileOutputType::standardout)
1424     {
1425       if (transformpass || computingpass)
1426         cout << R"("transformed_modfile": )";
1427       else
1428         cout << R"("modfile": )";
1429       cout << output.str();
1430       if (!original_model_output.str().empty())
1431         cout << R"(, "original_model": )" << original_model_output.str();
1432       if (!steady_state_model_output.str().empty())
1433         cout << R"(, "steady_state_model": )" << steady_state_model_output.str();
1434     }
1435   else
1436     {
1437       ofstream jsonOutputFile;
1438 
1439       if (basename.size())
1440         {
1441           filesystem::create_directories(basename + "/model/json");
1442           string fname{basename + "/model/json/modfile.json"};
1443           jsonOutputFile.open(fname, ios::out | ios::binary);
1444           if (!jsonOutputFile.is_open())
1445             {
1446               cerr << "ERROR: Can't open file " << fname << " for writing" << endl;
1447               exit(EXIT_FAILURE);
1448             }
1449         }
1450       else
1451         {
1452           cerr << "ERROR: Missing file name" << endl;
1453           exit(EXIT_FAILURE);
1454         }
1455 
1456       jsonOutputFile << output.str();
1457       jsonOutputFile.close();
1458 
1459       if (!original_model_output.str().empty())
1460         {
1461           if (basename.size())
1462             {
1463               string fname{basename + "/model/json/modfile-original.json"};
1464               jsonOutputFile.open(fname, ios::out | ios::binary);
1465               if (!jsonOutputFile.is_open())
1466                 {
1467                   cerr << "ERROR: Can't open file " << fname << " for writing" << endl;
1468                   exit(EXIT_FAILURE);
1469                 }
1470             }
1471           else
1472             {
1473               cerr << "ERROR: Missing file name" << endl;
1474               exit(EXIT_FAILURE);
1475             }
1476 
1477           jsonOutputFile << original_model_output.str();
1478           jsonOutputFile.close();
1479         }
1480       if (!steady_state_model_output.str().empty())
1481         {
1482           if (basename.size())
1483             {
1484               string fname{basename + "/model/json/steady_state_model.json"};
1485               jsonOutputFile.open(fname, ios::out | ios::binary);
1486               if (!jsonOutputFile.is_open())
1487                 {
1488                   cerr << "ERROR: Can't open file " << fname << " for writing" << endl;
1489                   exit(EXIT_FAILURE);
1490                 }
1491             }
1492           else
1493             {
1494               cerr << "ERROR: Missing file name" << endl;
1495               exit(EXIT_FAILURE);
1496             }
1497 
1498           jsonOutputFile << steady_state_model_output.str();
1499           jsonOutputFile.close();
1500         }
1501     }
1502 }
1503 
1504 void
writeJsonComputingPassOutput(const string & basename,JsonFileOutputType json_output_mode,bool jsonderivsimple) const1505 ModFile::writeJsonComputingPassOutput(const string &basename, JsonFileOutputType json_output_mode, bool jsonderivsimple) const
1506 {
1507   if (basename.empty() && json_output_mode != JsonFileOutputType::standardout)
1508     {
1509       cerr << "ERROR: Missing file name" << endl;
1510       exit(EXIT_FAILURE);
1511     }
1512 
1513   ostringstream tmp_out, static_output, dynamic_output, static_paramsd_output, dynamic_paramsd_output;
1514 
1515   static_output << "{";
1516   static_model.writeJsonComputingPassOutput(static_output, !jsonderivsimple);
1517   static_output << "}";
1518 
1519   dynamic_output << "{";
1520   dynamic_model.writeJsonComputingPassOutput(dynamic_output, !jsonderivsimple);
1521   dynamic_output << "}";
1522 
1523   static_model.writeJsonParamsDerivativesFile(tmp_out, !jsonderivsimple);
1524   if (!tmp_out.str().empty())
1525     static_paramsd_output << "{" << tmp_out.str() << "}" << endl;
1526 
1527   tmp_out.str("");
1528   dynamic_model.writeJsonParamsDerivativesFile(tmp_out, !jsonderivsimple);
1529   if (!tmp_out.str().empty())
1530     dynamic_paramsd_output << "{" << tmp_out.str() << "}" << endl;
1531 
1532   if (json_output_mode == JsonFileOutputType::standardout)
1533     {
1534       cout << R"(, "static_model": )" << static_output.str() << endl
1535            << R"(, "dynamic_model": )" << dynamic_output.str() << endl;
1536 
1537       if (!static_paramsd_output.str().empty())
1538         cout << R"(, "static_params_deriv": )" << static_paramsd_output.str() << endl;
1539 
1540       if (!dynamic_paramsd_output.str().empty())
1541         cout << R"(, "dynamic_params_deriv": )" << dynamic_paramsd_output.str() << endl;
1542     }
1543   else
1544     {
1545       filesystem::create_directories(basename + "/model/json");
1546 
1547       writeJsonFileHelper(basename + "/model/json/static.json", static_output);
1548       writeJsonFileHelper(basename + "/model/json/dynamic.json", dynamic_output);
1549 
1550       if (!static_paramsd_output.str().empty())
1551         writeJsonFileHelper(basename + "/model/json/static_params_derivs.json", static_paramsd_output);
1552 
1553       if (!dynamic_paramsd_output.str().empty())
1554         writeJsonFileHelper(basename + "/model/json/params_derivs.json", dynamic_paramsd_output);
1555     }
1556 }
1557 
1558 void
writeJsonFileHelper(const string & fname,ostringstream & output) const1559 ModFile::writeJsonFileHelper(const string &fname, ostringstream &output) const
1560 {
1561   ofstream jsonOutput;
1562   jsonOutput.open(fname, ios::out | ios::binary);
1563   if (!jsonOutput.is_open())
1564     {
1565       cerr << "ERROR: Can't open file " << fname << " for writing" << endl;
1566       exit(EXIT_FAILURE);
1567     }
1568   jsonOutput << output.str();
1569   jsonOutput.close();
1570 }
1571 
1572 filesystem::path
unique_path()1573 ModFile::unique_path()
1574 {
1575   filesystem::path path;
1576   string possible_characters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
1577   random_device rd;
1578   mt19937 generator(rd());
1579   uniform_int_distribution<int> distribution{0, static_cast<int>(possible_characters.size())-1};
1580   do
1581     {
1582       constexpr int rand_length = 10;
1583       string rand_str(rand_length, '\0');
1584       for (auto &dis : rand_str)
1585         dis = possible_characters[distribution(generator)];
1586       path = rand_str;
1587     }
1588   while (filesystem::exists(path));
1589 
1590   return path;
1591 }
1592