ModelEquationBlock.cc 13.8 KB
Newer Older
1
/*
Houtan Bastani's avatar
Houtan Bastani committed
2
 * Copyright (C) 2010-2018 Dynare Team
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * This file is part of Dynare.
 *
 * Dynare is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Dynare is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Dynare.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <cassert>
#include <algorithm>

Houtan Bastani's avatar
Houtan Bastani committed
23
#include "ModelEquationBlock.hh"
24

25 26 27 28
SteadyStateModel::SteadyStateModel(SymbolTable &symbol_table_arg,
                                   NumericalConstants &num_constants_arg,
                                   ExternalFunctionsTable &external_functions_table_arg,
                                   const StaticModel &static_model_arg) :
29 30
  DataTree{symbol_table_arg, num_constants_arg, external_functions_table_arg},
  static_model{static_model_arg}
31 32 33
{
}

34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
SteadyStateModel::SteadyStateModel(const SteadyStateModel &m) :
  DataTree {m},
  static_model {m.static_model}
{
  for (const auto &it : m.def_table)
    def_table.push_back(make_pair(it.first, it.second->cloneDynamic(*this)));
}

SteadyStateModel &
SteadyStateModel::operator=(const SteadyStateModel &m)
{
  DataTree::operator=(m);

  assert(&static_model == &m.static_model);

  def_table.clear();
  for (const auto &it : m.def_table)
    def_table.push_back(make_pair(it.first, it.second->cloneDynamic(*this)));

  return *this;
}

56
void
57
SteadyStateModel::addDefinition(int symb_id, expr_t expr)
58
{
59 60
  AddVariable(symb_id); // Create the variable node to be used in write method

61 62 63
  assert(symbol_table.getType(symb_id) == SymbolType::endogenous
         || symbol_table.getType(symb_id) == SymbolType::modFileLocalVariable
         || symbol_table.getType(symb_id) == SymbolType::parameter);
64 65

  // Add the variable
66 67
  vector<int> v;
  v.push_back(symb_id);
68
  def_table.emplace_back(v, expr);
69 70 71 72 73
}

void
SteadyStateModel::addMultipleDefinitions(const vector<int> &symb_ids, expr_t expr)
{
74
  for (int symb_id : symb_ids)
75
    {
76
      AddVariable(symb_id); // Create the variable nodes to be used in write method
77 78 79
      assert(symbol_table.getType(symb_id) == SymbolType::endogenous
             || symbol_table.getType(symb_id) == SymbolType::modFileLocalVariable
             || symbol_table.getType(symb_id) == SymbolType::parameter);
80
    }
81
  def_table.emplace_back(symb_ids, expr);
82 83 84
}

void
85
SteadyStateModel::checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) const
86
{
87
  if (def_table.size() == 0)
88 89
    return;

90
  mod_file_struct.steady_state_model_present = true;
91 92
  vector<int> so_far_defined;

93
  for (const auto & i : def_table)
94
    {
95
      const vector<int> &symb_ids = i.first;
96 97

      // Check that symbols are not already defined
98 99
      for (int symb_id : symb_ids)
        if (find(so_far_defined.begin(), so_far_defined.end(), symb_id)
100
            != so_far_defined.end())
101
          warnings << "WARNING: in the 'steady_state_model' block, variable '" << symbol_table.getName(symb_id) << "' is declared twice" << endl;
102

103
      // Check that expression has no undefined symbol
104
      if (!mod_file_struct.ramsey_model_present)
105
        {
106
          set<int> used_symbols;
107
          const expr_t &expr = i.second;
108 109
          expr->collectVariables(SymbolType::endogenous, used_symbols);
          expr->collectVariables(SymbolType::modFileLocalVariable, used_symbols);
110 111
          for (int used_symbol : used_symbols)
            if (find(so_far_defined.begin(), so_far_defined.end(), used_symbol)
112
                == so_far_defined.end())
113
              {
114
                cerr << "ERROR: in the 'steady_state_model' block, variable '" << symbol_table.getName(used_symbol)
115
                     << "' is undefined in the declaration of variable '" << symbol_table.getName(symb_ids[0]) << "'" << endl;
116 117 118
                exit(EXIT_FAILURE);
              }
        }
119 120

      copy(symb_ids.begin(), symb_ids.end(), back_inserter(so_far_defined));
121
    }
122 123

  set<int> orig_endogs = symbol_table.getOrigEndogenous();
124
  for (int orig_endog : orig_endogs)
125
    {
126
      if (find(so_far_defined.begin(), so_far_defined.end(), orig_endog)
127
          == so_far_defined.end())
128
        warnings << "WARNING: in the 'steady_state_model' block, variable '" << symbol_table.getName(orig_endog) << "' is not assigned a value" << endl;
129
    }
130 131
}

132 133 134 135 136 137 138 139
void
SteadyStateModel::writeLatexSteadyStateFile(const string &basename) const
{
  ofstream output, content_output;
  string filename = basename + "_steady_state.tex";
  string content_basename = basename + "_steady_state_content";
  string content_filename = content_basename + ".tex";

140
  output.open(filename, ios::out | ios::binary);
141 142 143 144 145 146
  if (!output.is_open())
    {
      cerr << "ERROR: Can't open file " << filename << " for writing" << endl;
      exit(EXIT_FAILURE);
    }

147
  content_output.open(content_filename, ios::out | ios::binary);
148 149 150 151 152 153 154 155 156 157 158 159 160 161
  if (!content_output.is_open())
    {
      cerr << "ERROR: Can't open file " << content_filename << " for writing" << endl;
      exit(EXIT_FAILURE);
    }

  output << "\\documentclass[10pt,a4paper]{article}" << endl
         << "\\usepackage[landscape]{geometry}" << endl
         << "\\usepackage{fullpage}" << endl
         << "\\usepackage{amsfonts}" << endl
         << "\\usepackage{breqn}" << endl
         << "\\begin{document}" << endl
         << "\\footnotesize" << endl;

162
  for (const auto & it : def_table)
163
    for (auto it1 = it.first.begin(); it1 != it.first.end(); it1++)
164 165
      {
        int id = *it1;
166
        expr_t value = it.second;
167 168
        content_output << "\\begin{dmath}" << endl
                       << symbol_table.getTeXName(id) << " = ";
169
        value->writeOutput(content_output, ExprNodeOutputType::latexStaticModel);
170 171 172 173 174 175 176 177 178 179 180 181
        content_output << endl << "\\end{dmath}" << endl;
      }

  static_model.writeLatexAuxVarRecursiveDefinitions(content_output);

  output << "\\include{" << content_basename << "}" << endl
         << "\\end{document}" << endl;

  output.close();
  content_output.close();
}

182
void
Houtan Bastani's avatar
Houtan Bastani committed
183
SteadyStateModel::writeSteadyStateFile(const string &basename, bool ramsey_model, bool julia) const
184
{
185
  if (def_table.size() == 0)
186 187
    return;

188
  string filename = julia ? basename + "SteadyState2.jl" : packageDir(basename) + "/steadystate.m";
189
  ofstream output;
190
  output.open(filename, ios::out | ios::binary);
191 192 193 194 195 196
  if (!output.is_open())
    {
      cerr << "ERROR: Can't open file " << filename << " for writing" << endl;
      exit(EXIT_FAILURE);
    }

197
  ExprNodeOutputType output_type = (julia ? ExprNodeOutputType::juliaSteadyStateFile : ExprNodeOutputType::steadyStateFile);
Houtan Bastani's avatar
Houtan Bastani committed
198 199

  if (!julia)
200
    output << "function [ys_, params, info] = steadystate("
Houtan Bastani's avatar
Houtan Bastani committed
201 202 203 204
           << "ys_, exo_, params)" << endl
           << "% Steady state generated by Dynare preprocessor" << endl
           << "    info = 0;" << endl;
  else
205 206 207 208 209
    output << "module " << basename << "SteadyState2" << endl
           << "#" << endl
           << "# NB: this file was automatically generated by Dynare" << endl
           << "#     from " << basename << ".mod" << endl
           << "#" << endl
210
           << "export steady_state!" << endl << endl
211 212
           << "function steady_state!(ys_::Vector{Float64}, exo_::Vector{Float64}, "
           << "params::Vector{Float64})" << endl;
213

214
  for (const auto & i : def_table)
215
    {
216
      const vector<int> &symb_ids = i.first;
217
      output << "    ";
218 219 220 221
      if (symb_ids.size() > 1)
        output << "[";
      for (size_t j = 0; j < symb_ids.size(); j++)
        {
222
          auto it = variable_node_map.find({ symb_ids[j], 0 });
223
          assert(it != variable_node_map.end());
Houtan Bastani's avatar
Houtan Bastani committed
224
          dynamic_cast<ExprNode *>(it->second)->writeOutput(output, output_type);
225 226 227 228 229 230 231
          if (j < symb_ids.size()-1)
            output << ",";
        }
      if (symb_ids.size() > 1)
        output << "]";

      output << "=";
232
      i.second->writeOutput(output, output_type);
233 234
      output << ";" << endl;
    }
Houtan Bastani's avatar
Houtan Bastani committed
235 236 237 238
  if (!julia)
    output << "    % Auxiliary equations" << endl;
  else
    output << "    # Auxiliary equations" << endl;
239
  static_model.writeAuxVarRecursiveDefinitions(output, output_type);
Houtan Bastani's avatar
Houtan Bastani committed
240

241 242 243
  if (!julia)
    output << "    check_=0;" << endl;

Houtan Bastani's avatar
Houtan Bastani committed
244 245 246
  output << "end" << endl;
  if (julia)
    output << "end" << endl;
247
  output.close();
248 249
}

250 251 252 253 254 255
void
SteadyStateModel::writeJsonSteadyStateFile(ostream &output, bool transformComputingPass) const
{
  if (def_table.size() == 0)
    return;

256
  vector<pair<string, string>> eqtags;
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271

  output << "{\"steady_state_model\": [";

  for (size_t i = 0; i < def_table.size(); i++)
    {
      const vector<int> &symb_ids = def_table[i].first;
      if (i != 0)
        output << ",";
      output << "{\"lhs\": ";
      if (symb_ids.size() > 1)
        output << "[";
      for (size_t j = 0; j < symb_ids.size(); j++)
        {
          if (j != 0)
            output << ",";
272
          auto it =
273
            variable_node_map.find({ symb_ids[j], 0 });
274 275
          assert(it != variable_node_map.end());
          output << "\"";
276
          dynamic_cast<ExprNode *>(it->second)->writeJsonOutput(output, {}, {}, false);
277 278 279 280 281
          output << "\"";
        }
      if (symb_ids.size() > 1)
        output << "]";
      output << ", \"rhs\":\"";
282
      def_table[i].second->writeJsonOutput(output, {}, {}, false);
283 284 285 286 287 288 289 290
      output << "\"}" << endl;
    }

  if (transformComputingPass)
    static_model.writeJsonAuxVarRecursiveDefinitions(output);

  output << "]}";
}
Houtan Bastani's avatar
Houtan Bastani committed
291 292 293 294 295 296

Epilogue::Epilogue(SymbolTable &symbol_table_arg,
                   NumericalConstants &num_constants_arg,
                   ExternalFunctionsTable &external_functions_table_arg,
                   TrendComponentModelTable &trend_component_model_table_arg,
                   VarModelTable &var_model_table_arg) :
297 298
  DynamicModel{symbol_table_arg, num_constants_arg, external_functions_table_arg,
               trend_component_model_table_arg, var_model_table_arg}
Houtan Bastani's avatar
Houtan Bastani committed
299 300 301
{
}

302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
Epilogue::Epilogue(const Epilogue &m) :
  DynamicModel {m},
  endogs {m.endogs},
  exogs {m.exogs}
{
  for (const auto &it : m.def_table)
    def_table.push_back(make_pair(it.first, it.second->cloneDynamic(*this)));
}

Epilogue &
Epilogue::operator=(const Epilogue &m)
{
  DynamicModel::operator=(m);

  endogs = m.endogs;
  exogs = m.exogs;

  def_table.clear();
  for (const auto &it : m.def_table)
    def_table.push_back(make_pair(it.first, it.second->cloneDynamic(*this)));

  return *this;
}

Houtan Bastani's avatar
Houtan Bastani committed
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
void
Epilogue::addDefinition(int symb_id, expr_t expr)
{
  AddVariable(symb_id); // Create the variable node to be used in write method
  def_table.emplace_back(symb_id, expr);
  endogs.emplace(symb_id);
  expr->collectVariables(SymbolType::endogenous, exogs);
  expr->collectVariables(SymbolType::exogenous, exogs);
  expr->collectVariables(SymbolType::endogenousEpilogue, exogs);
  expr->collectVariables(SymbolType::exogenousEpilogue, exogs);
  for (auto it : endogs)
    exogs.erase(it);
}

void
Epilogue::checkPass(WarningConsolidation &warnings) const
{
  if (def_table.size() == 0)
    return;

  vector<int> so_far_defined;
  for (const auto & it : def_table)
    {
      if (find(so_far_defined.begin(), so_far_defined.end(), it.first) != so_far_defined.end())
        {
          cerr << "WARNING: in the 'epilogue' block, variable '" << symbol_table.getName(it.first)
               << "' is declared twice" << endl;
          exit(EXIT_FAILURE);
        }
      so_far_defined.push_back(it.first);
    }
}

void
Epilogue::writeEpilogueFile(const string &basename) const
{
  if (def_table.size() == 0)
    return;

  string filename = packageDir(basename) + "/epilogue.m";
  ofstream output;
  output.open(filename, ios::out | ios::binary);
  if (!output.is_open())
    {
      cerr << "ERROR: Can't open file " << filename << " for writing" << endl;
      exit(EXIT_FAILURE);
    }

374
  ExprNodeOutputType output_type = ExprNodeOutputType::epilogueFile;
Houtan Bastani's avatar
Houtan Bastani committed
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420
  output << "function ds = epilogue(params, ds)" << endl
         << "% function ds = epilogue(params, ds)" << endl
         << "% Epilogue file generated by Dynare preprocessor" << endl << endl
         << "epilogue_ds_first_date__ = ds.firstdate;" << endl
         << "epilogue_loop_begin_idx__ = lastdate(ds) - ds.lastobservedperiod;" << endl
         << "epilogue_loop_end_idx__ = lastdate(ds) - firstdate(ds) + 1;" << endl << endl;

  output << "% endogenous" << endl;
  for (auto symb_id : endogs)
    output << symbol_table.getName(symb_id) << " = ds." << symbol_table.getName(symb_id) << ".data;" << endl;
  output << endl
         << "% exogenous" << endl;
  for (auto symb_id : exogs)
    output << symbol_table.getName(symb_id) << " = ds." << symbol_table.getName(symb_id) << ".data;" << endl;
  output << endl
         << "for epilogue_it__ = epilogue_loop_begin_idx__::epilogue_loop_end_idx__" << endl;

  deriv_node_temp_terms_t tef_terms;
  temporary_terms_t temporary_terms;
  temporary_terms_idxs_t temporary_terms_idxs;
  for (const auto & it : def_table)
    if (it.second->containsExternalFunction())
      {
        output << "    ";
        it.second->writeExternalFunctionOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms);
      }
  output << endl;
  for (const auto & it : def_table)
    {
      auto node = variable_node_map.find({ it.first, 0 });
      assert(node != variable_node_map.end());

      output << "    ";
      dynamic_cast<ExprNode *>(node->second)->writeOutput(output, output_type);
      output << " = ";
      it.second->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms);
      output << ";" << endl;
    }
  output << "end" << endl << endl;
  for (auto symb_id : endogs)
    output << "ds." << symbol_table.getName(symb_id) << " = dseries(" << symbol_table.getName(symb_id)
           << ", epilogue_ds_first_date__);" << endl;
  output << endl
         << "end" << endl;
  output.close();
}