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 ¶llel_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