Skip to content
Snippets Groups Projects
Select Git revision
  • ha_mcp
  • master default protected
  • pac_composite_target_mce
  • ramsey_k_order
  • 4.6
  • occbin
  • uop
  • rework_pac
  • aux_vars_fix
  • created_preprocessor_repo
10 results

SymbolTable.cc

Blame
  • Forked from Dynare / preprocessor
    320 commits behind the upstream repository.
    • Sébastien Villemot's avatar
      0169240f
      Ramsey: write derivatives of static model w.r.t. Lagrange multipliers in a new file · 0169240f
      Sébastien Villemot authored
      The computing of the Ramsey steady state relies on the fact that Lagrange
      multipliers appear linearly in the system to be solved. Instead of directly
      solving for the Lagrange multipliers along with the other variables,
      dyn_ramsey_static.m reduces the size of the problem by always computing the
      value of the multipliers that minimizes the residuals, given the other
      variables (using a minimum norm solution, easy to compute because of the
      linearity property). That function thus needs the derivatives of the optimality
      FOCs with respect to the multipliers. The problem is that, when multipliers
      appear in an auxiliary variable related to a lead/lag, then those derivatives
      need to be retrieved by a chain rule derivation, which cannot be easily done
      with the regular static file.
      
      This commit implements the creation of a new file,
      ramsey_multipliers_static_g1.{m,mex}, that provides exactly the needed
      derivatives w.r.t. Lagrange multipliers through chain rule derivation.
      
      Ref. dynare#633, dynare#1119, dynare#1133
      0169240f
      History
      Ramsey: write derivatives of static model w.r.t. Lagrange multipliers in a new file
      Sébastien Villemot authored
      The computing of the Ramsey steady state relies on the fact that Lagrange
      multipliers appear linearly in the system to be solved. Instead of directly
      solving for the Lagrange multipliers along with the other variables,
      dyn_ramsey_static.m reduces the size of the problem by always computing the
      value of the multipliers that minimizes the residuals, given the other
      variables (using a minimum norm solution, easy to compute because of the
      linearity property). That function thus needs the derivatives of the optimality
      FOCs with respect to the multipliers. The problem is that, when multipliers
      appear in an auxiliary variable related to a lead/lag, then those derivatives
      need to be retrieved by a chain rule derivation, which cannot be easily done
      with the regular static file.
      
      This commit implements the creation of a new file,
      ramsey_multipliers_static_g1.{m,mex}, that provides exactly the needed
      derivatives w.r.t. Lagrange multipliers through chain rule derivation.
      
      Ref. dynare#633, dynare#1119, dynare#1133
    SymbolTable.cc 31.65 KiB
    /*
     * Copyright © 2003-2023 Dynare Team
     *
     * This file is part of Dynare.
     *
     * Dynare is free software: you can redistribute it and/or modify
     * it under the terms of the GNU General Public License as published by
     * the Free Software Foundation, either version 3 of the License, or
     * (at your option) any later version.
     *
     * Dynare is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     * GNU General Public License for more details.
     *
     * You should have received a copy of the GNU General Public License
     * along with Dynare.  If not, see <https://www.gnu.org/licenses/>.
     */
    
    #include <algorithm>
    #include <sstream>
    #include <iostream>
    #include <cassert>
    #pragma GCC diagnostic push
    #pragma GCC diagnostic ignored "-Wold-style-cast"
    #include <boost/algorithm/string/replace.hpp>
    #pragma GCC diagnostic pop
    #include <utility>
    
    #include "SymbolTable.hh"
    
    int
    SymbolTable::addSymbol(const string &name, SymbolType type, const string &tex_name, const vector<pair<string, string>> &partition_value) noexcept(false)
    {
      if (frozen)
        throw FrozenException();
    
      if (exists(name))
        {
          if (type_table[getID(name)] == type)
            throw AlreadyDeclaredException(name, true);
          else
            throw AlreadyDeclaredException(name, false);
        }
    
      string final_tex_name = tex_name;
      if (final_tex_name.empty())
        {
          final_tex_name = name;
          size_t pos = 0;
          while ((pos = final_tex_name.find('_', pos)) != string::npos)
            {
              final_tex_name.insert(pos, R"(\)");
              pos += 2;
            }
        }
    
      string final_long_name = name;
      bool non_long_name_partition_exists = false;
      for (const auto &it : partition_value)
        if (it.first == "long_name")
          final_long_name = it.second;
        else
          non_long_name_partition_exists = true;
    
      int id = symbol_table.size();
    
      symbol_table[name] = id;
      type_table.push_back(type);
      name_table.push_back(name);
      tex_name_table.push_back(final_tex_name);
      long_name_table.push_back(final_long_name);
      if (non_long_name_partition_exists)
        {
          map<string, string> pmv;
          for (const auto &it : partition_value)
            pmv[it.first] = it.second;
          partition_value_map[id] = pmv;
        }
      return id;
    }
    
    int
    SymbolTable::addSymbol(const string &name, SymbolType type) noexcept(false)
    {
      return addSymbol(name, type, "", {});
    }
    
    void
    SymbolTable::freeze() noexcept(false)
    {
      if (frozen)
        throw FrozenException();
    
      frozen = true;
    
      for (int i = 0; i < static_cast<int>(symbol_table.size()); i++)
        {
          int tsi;
          switch (getType(i))
            {
            case SymbolType::endogenous:
              tsi = endo_ids.size();
              endo_ids.push_back(i);
              break;
            case SymbolType::exogenous:
              tsi = exo_ids.size();
              exo_ids.push_back(i);
              break;
            case SymbolType::exogenousDet:
              tsi = exo_det_ids.size();
              exo_det_ids.push_back(i);
              break;
            case SymbolType::parameter:
              tsi = param_ids.size();
              param_ids.push_back(i);
              break;
            default:
              continue;
            }
          type_specific_ids[i] = tsi;
        }
    }
    
    void
    SymbolTable::unfreeze()
    {
      frozen = false;
      endo_ids.clear();
      exo_ids.clear();
      exo_det_ids.clear();
      param_ids.clear();
      type_specific_ids.clear();
    }
    
    void
    SymbolTable::changeType(int id, SymbolType newtype) noexcept(false)
    {
      if (frozen)
        throw FrozenException();
    
      validateSymbID(id);
    
      type_table[id] = newtype;
    }
    
    int
    SymbolTable::getID(SymbolType type, int tsid) const noexcept(false)
    {
      if (!frozen)
        throw NotYetFrozenException();
    
      switch (type)
        {
        case SymbolType::endogenous:
          if (tsid < 0 || tsid >= static_cast<int>(endo_ids.size()))
            throw UnknownTypeSpecificIDException(tsid, type);
          else
            return endo_ids[tsid];
        case SymbolType::exogenous:
          if (tsid < 0 || tsid >= static_cast<int>(exo_ids.size()))
            throw UnknownTypeSpecificIDException(tsid, type);
          else
            return exo_ids[tsid];
        case SymbolType::exogenousDet:
          if (tsid < 0 || tsid >= static_cast<int>(exo_det_ids.size()))
            throw UnknownTypeSpecificIDException(tsid, type);
          else
            return exo_det_ids[tsid];
        case SymbolType::parameter:
          if (tsid < 0 || tsid >= static_cast<int>(param_ids.size()))
            throw UnknownTypeSpecificIDException(tsid, type);
          else
            return param_ids[tsid];
        default:
          throw UnknownTypeSpecificIDException(tsid, type);
        }
    }
    
    map<string, map<int, string>>
    SymbolTable::getPartitionsForType(SymbolType st) const noexcept(false)
    {
      map<string, map<int, string>> partitions;
      for (const auto &it : partition_value_map)
        if (getType(it.first) == st)
          for (const auto &it1 : it.second)
            partitions[it1.first][it.first] = it1.second;
      return partitions;
    }
    
    void
    SymbolTable::writeOutput(ostream &output) const noexcept(false)
    {
      if (!frozen)
        throw NotYetFrozenException();
    
      if (exo_nbr() > 0)
        {
          output << "M_.exo_names = cell(" << exo_nbr() << ",1);" << endl;
          output << "M_.exo_names_tex = cell(" << exo_nbr() << ",1);" << endl;
          output << "M_.exo_names_long = cell(" << exo_nbr() << ",1);" << endl;
          for (int id = 0; id < exo_nbr(); id++)
            output << "M_.exo_names(" << id+1 << ") = {'" << getName(exo_ids[id]) << "'};" << endl
                   << "M_.exo_names_tex(" << id+1 << ") = {'" << getTeXName(exo_ids[id]) << "'};" << endl
                   << "M_.exo_names_long(" << id+1 << ") = {'" << getLongName(exo_ids[id]) << "'};" << endl;
          for (auto &partition : getPartitionsForType(SymbolType::exogenous))
            if (partition.first != "long_name")
              {
                output << "M_.exo_partitions." << partition.first << " = { ";
                for (int id = 0; id < exo_nbr(); id++)
                  {
                    output << "'";
                    if (auto it1 = partition.second.find(exo_ids[id]);
                        it1 != partition.second.end())
                      output << it1->second;
                    output << "' ";
                  }
                output << "};" << endl;
                if (partition.first == "status")
                  output << "M_ = set_observed_exogenous_variables(M_);" << endl;
                if (partition.first == "used")
                  output << "M_ = set_exogenous_variables_for_simulation(M_);" << endl;
              }
        }
      else
        {
          output << "M_.exo_names = {};" << endl;
          output << "M_.exo_names_tex = {};" << endl;
          output << "M_.exo_names_long = {};" << endl;
        }
    
      if (exo_det_nbr() > 0)
        {
          output << "M_.exo_det_names = cell(" << exo_det_nbr() << ",1);" << endl;
          output << "M_.exo_det_names_tex = cell(" << exo_det_nbr() << ",1);" << endl;
          output << "M_.exo_det_names_long = cell(" << exo_det_nbr() << ",1);" << endl;
          for (int id = 0; id < exo_det_nbr(); id++)
            output << "M_.exo_det_names(" << id+1 << ") = {'" << getName(exo_det_ids[id]) << "'};" << endl
                   << "M_.exo_det_names_tex(" << id+1 << ") = {'" << getTeXName(exo_det_ids[id]) << "'};" << endl
                   << "M_.exo_det_names_long(" << id+1 << ") = {'" << getLongName(exo_det_ids[id]) << "'};" << endl;
          output << "M_.exo_det_partitions = struct();" << endl;
          for (auto &partition : getPartitionsForType(SymbolType::exogenousDet))
            if (partition.first != "long_name")
              {
                output << "M_.exo_det_partitions." << partition.first << " = { ";
                for (int id = 0; id < exo_det_nbr(); id++)
                  {
                    output << "'";
                    if (auto it1 = partition.second.find(exo_det_ids[id]);
                        it1 != partition.second.end())
                      output << it1->second;
                    output << "' ";
                  }
                output << "};" << endl;
              }
        }
    
      if (endo_nbr() > 0)
        {
          output << "M_.endo_names = cell(" << endo_nbr() << ",1);" << endl;
          output << "M_.endo_names_tex = cell(" << endo_nbr() << ",1);" << endl;
          output << "M_.endo_names_long = cell(" << endo_nbr() << ",1);" << endl;
          for (int id = 0; id < endo_nbr(); id++)
            output << "M_.endo_names(" << id+1 << ") = {'" << getName(endo_ids[id]) << "'};" << endl
                   << "M_.endo_names_tex(" << id+1 << ") = {'" << getTeXName(endo_ids[id]) << "'};" << endl
                   << "M_.endo_names_long(" << id+1 << ") = {'" << getLongName(endo_ids[id]) << "'};" << endl;
          output << "M_.endo_partitions = struct();" << endl;
          for (auto &partition : getPartitionsForType(SymbolType::endogenous))
            if (partition.first != "long_name")
              {
                output << "M_.endo_partitions." << partition.first << " = { ";
                for (int id = 0; id < endo_nbr(); id++)
                  {
                    output << "'";
                    if (auto it1 = partition.second.find(endo_ids[id]);
                        it1 != partition.second.end())
                      output << it1->second;
                    output << "' ";
                  }
                output << "};" << endl;
              }
        }
    
      if (param_nbr() > 0)
        {
          output << "M_.param_names = cell(" << param_nbr() << ",1);" << endl;
          output << "M_.param_names_tex = cell(" << param_nbr() << ",1);" << endl;
          output << "M_.param_names_long = cell(" << param_nbr() << ",1);" << endl;
          for (int id = 0; id < param_nbr(); id++)
            {
              output << "M_.param_names(" << id+1 << ") = {'" << getName(param_ids[id]) << "'};" << endl
                     << "M_.param_names_tex(" << id+1 << ") = {'" << getTeXName(param_ids[id]) << "'};" << endl
                     << "M_.param_names_long(" << id+1 << ") = {'" << getLongName(param_ids[id]) << "'};" << endl;
              if (getName(param_ids[id]) == "dsge_prior_weight")
                output << "options_.dsge_var = 1;" << endl;
            }
          output << "M_.param_partitions = struct();" << endl;
          for (auto &partition : getPartitionsForType(SymbolType::parameter))
            if (partition.first != "long_name")
              {
                output << "M_.param_partitions." << partition.first << " = { ";
                for (int id = 0; id < param_nbr(); id++)
                  {
                    output << "'";
                    if (auto it1 = partition.second.find(param_ids[id]);
                        it1 != partition.second.end())
                      output << it1->second;
                    output << "' ";
                  }
                output << "};" << endl;
              }
        }
      else
        {
          output << "M_.param_names = {};" << endl;
          output << "M_.param_names_tex = {};" << endl;
          output << "M_.param_names_long = {};" << endl;
        }
    
      output << "M_.exo_det_nbr = " << exo_det_nbr() << ";" << endl
             << "M_.exo_nbr = " << exo_nbr() << ";" << endl
             << "M_.endo_nbr = " << endo_nbr() << ";" << endl
             << "M_.param_nbr = " << param_nbr() << ";" << endl;
    
      // Write the auxiliary variable table
      output << "M_.orig_endo_nbr = " << orig_endo_nbr() << ";" << endl;
      if (aux_vars.size() == 0)
        output << "M_.aux_vars = [];" << endl;
      else
        for (int i = 0; i < static_cast<int>(aux_vars.size()); i++)
          {
            output << "M_.aux_vars(" << i+1 << ").endo_index = " << getTypeSpecificID(aux_vars[i].symb_id)+1 << ";" << endl
                   << "M_.aux_vars(" << i+1 << ").type = " << aux_vars[i].get_type_id() << ";" << endl;
            switch (aux_vars[i].type)
              {
              case AuxVarType::endoLead:
              case AuxVarType::exoLead:
              case AuxVarType::expectation:
              case AuxVarType::pacExpectation:
              case AuxVarType::pacTargetNonstationary:
                break;
              case AuxVarType::endoLag:
              case AuxVarType::exoLag:
              case AuxVarType::logTransform:
              case AuxVarType::diffLag:
              case AuxVarType::diffLead:
              case AuxVarType::diffForward:
                output << "M_.aux_vars(" << i+1 << ").orig_index = " << getTypeSpecificID(aux_vars[i].orig_symb_id.value())+1 << ";" << endl
                       << "M_.aux_vars(" << i+1 << ").orig_lead_lag = " << aux_vars[i].orig_lead_lag.value() << ";" << endl;
                break;
              case AuxVarType::unaryOp:
                output << "M_.aux_vars(" << i+1 << ").unary_op = '" << aux_vars[i].unary_op << "';" << endl;
                [[fallthrough]];
              case AuxVarType::diff:
                if (aux_vars[i].orig_symb_id)
                  output << "M_.aux_vars(" << i+1 << ").orig_index = " << getTypeSpecificID(*aux_vars[i].orig_symb_id)+1 << ";" << endl
                         << "M_.aux_vars(" << i+1 << ").orig_lead_lag = " << aux_vars[i].orig_lead_lag.value() << ";" << endl;
                break;
              case AuxVarType::multiplier:
                output << "M_.aux_vars(" << i+1 << ").eq_nbr = " << aux_vars[i].equation_number_for_multiplier + 1 << ";" << endl;
                break;
              }
    
            if (expr_t orig_expr = aux_vars[i].expr_node;
                orig_expr)
              {
                output << "M_.aux_vars(" << i+1 << ").orig_expr = '";
                orig_expr->writeJsonOutput(output, {}, {});
                output << "';" << endl;
              }
          }
    
      if (predeterminedNbr() > 0)
        {
          output << "M_.predetermined_variables = [ ";
          for (int predetermined_variable : predetermined_variables)
            output << getTypeSpecificID(predetermined_variable)+1 << " ";
          output << "];" << endl;
        }
    
      if (observedVariablesNbr() > 0)
        {
          output << "options_.varobs = cell(" << observedVariablesNbr() << ", 1);" << endl;
          for (int ic{1};
               int it : varobs)
            output << "options_.varobs(" << ic++ << ")  = {'" << getName(it) << "'};" << endl;
    
          output << "options_.varobs_id = [ ";
          for (int varob : varobs)
            output << getTypeSpecificID(varob)+1 << " ";
          output << " ];"  << endl;
        }
    
      if (observedExogenousVariablesNbr() > 0)
        {
          output << "options_.varexobs = cell(1);" << endl;
          for (int ic{1};
               int it : varexobs)
            output << "options_.varexobs(" << ic++ << ")  = {'" << getName(it) << "'};" << endl;
    
          output << "options_.varexobs_id = [ ";
          for (int varexob : varexobs)
            output << getTypeSpecificID(varexob)+1 << " ";
          output << " ];"  << endl;
        }
    }
    
    int
    SymbolTable::addLeadAuxiliaryVarInternal(bool endo, int index, expr_t expr_arg) noexcept(false)
    {
      string varname{(endo ? "AUX_ENDO_LEAD_" : "AUX_EXO_LEAD_") + to_string(index)};
      int symb_id;
      try
        {
          symb_id = addSymbol(varname, SymbolType::endogenous);
        }
      catch (AlreadyDeclaredException &e)
        {
          cerr << "ERROR: you should rename your variable called " << varname << ", this name is internally used by Dynare" << endl;
          exit(EXIT_FAILURE);
        }
    
      aux_vars.emplace_back(symb_id, (endo ? AuxVarType::endoLead : AuxVarType::exoLead), 0, 0, 0, 0, expr_arg, "");
    
      return symb_id;
    }
    
    int
    SymbolTable::addLagAuxiliaryVarInternal(bool endo, int orig_symb_id, int orig_lead_lag, expr_t expr_arg) noexcept(false)
    {
      string varname{(endo ? "AUX_ENDO_LAG_" : "AUX_EXO_LAG_") + to_string(orig_symb_id) + "_" + to_string(-orig_lead_lag)};
      int symb_id;
      try
        {
          symb_id = addSymbol(varname, SymbolType::endogenous);
        }
      catch (AlreadyDeclaredException &e)
        {
          cerr << "ERROR: you should rename your variable called " << varname << ", this name is internally used by Dynare" << endl;
          exit(EXIT_FAILURE);
        }
    
      aux_vars.emplace_back(symb_id, (endo ? AuxVarType::endoLag : AuxVarType::exoLag), orig_symb_id, orig_lead_lag, 0, 0, expr_arg, "");
    
      return symb_id;
    }
    
    int
    SymbolTable::addEndoLeadAuxiliaryVar(int index, expr_t expr_arg) noexcept(false)
    {
      return addLeadAuxiliaryVarInternal(true, index, expr_arg);
    }
    
    int
    SymbolTable::addEndoLagAuxiliaryVar(int orig_symb_id, int orig_lead_lag, expr_t expr_arg) noexcept(false)
    {
      return addLagAuxiliaryVarInternal(true, orig_symb_id, orig_lead_lag, expr_arg);
    }
    
    int
    SymbolTable::addExoLeadAuxiliaryVar(int index, expr_t expr_arg) noexcept(false)
    {
      return addLeadAuxiliaryVarInternal(false, index, expr_arg);
    }
    
    int
    SymbolTable::addExoLagAuxiliaryVar(int orig_symb_id, int orig_lead_lag, expr_t expr_arg) noexcept(false)
    {
      return addLagAuxiliaryVarInternal(false, orig_symb_id, orig_lead_lag, expr_arg);
    }
    
    int
    SymbolTable::addExpectationAuxiliaryVar(int information_set, int index, expr_t expr_arg) noexcept(false)
    {
      string varname{"AUX_EXPECT_"s + (information_set < 0 ? "LAG" : "LEAD") + "_"
        + to_string(abs(information_set)) + "_" + to_string(index)};
      int symb_id;
      try
        {
          symb_id = addSymbol(varname, SymbolType::endogenous);
        }
      catch (AlreadyDeclaredException &e)
        {
          cerr << "ERROR: you should rename your variable called " << varname << ", this name is internally used by Dynare" << endl;
          exit(EXIT_FAILURE);
        }
    
      aux_vars.emplace_back(symb_id, AuxVarType::expectation, 0, 0, 0, information_set, expr_arg, "");
    
      return symb_id;
    }
    
    int
    SymbolTable::addLogTransformAuxiliaryVar(int orig_symb_id, int orig_lead_lag, expr_t expr_arg) noexcept(false)
    {
      string varname = "LOG_" + getName(orig_symb_id);
      int symb_id;
      try
        {
          symb_id = addSymbol(varname, SymbolType::endogenous);
        }
      catch (AlreadyDeclaredException &e)
        {
          cerr << "ERROR: you should rename your variable called " << varname << ", it conflicts with the auxiliary variable created for representing the log of " << getName(orig_symb_id) << endl;
          exit(EXIT_FAILURE);
        }
    
      aux_vars.emplace_back(symb_id, AuxVarType::logTransform, orig_symb_id, orig_lead_lag, 0, 0, expr_arg, "");
    
      return symb_id;
    }
    
    int
    SymbolTable::addDiffLagAuxiliaryVar(int index, expr_t expr_arg, int orig_symb_id, int orig_lag) noexcept(false)
    {
      string varname{"AUX_DIFF_LAG_" + to_string(index)};
      int symb_id;
      try
        {
          symb_id = addSymbol(varname, SymbolType::endogenous);
        }
      catch (AlreadyDeclaredException &e)
        {
          cerr << "ERROR: you should rename your variable called " << varname << ", this name is internally used by Dynare" << endl;
          exit(EXIT_FAILURE);
        }
    
      aux_vars.emplace_back(symb_id, AuxVarType::diffLag, orig_symb_id, orig_lag, 0, 0, expr_arg, "");
    
      return symb_id;
    }
    
    int
    SymbolTable::addDiffLeadAuxiliaryVar(int index, expr_t expr_arg, int orig_symb_id, int orig_lead) noexcept(false)
    {
      string varname{"AUX_DIFF_LEAD_" + to_string(index)};
      int symb_id;
      try
        {
          symb_id = addSymbol(varname, SymbolType::endogenous);
        }
      catch (AlreadyDeclaredException &e)
        {
          cerr << "ERROR: you should rename your variable called " << varname << ", this name is internally used by Dynare" << endl;
          exit(EXIT_FAILURE);
        }
    
      aux_vars.emplace_back(symb_id, AuxVarType::diffLead, orig_symb_id, orig_lead, 0, 0, expr_arg, "");
    
      return symb_id;
    }
    
    int
    SymbolTable::addDiffAuxiliaryVar(int index, expr_t expr_arg, optional<int> orig_symb_id, optional<int> orig_lag) noexcept(false)
    {
      string varname{"AUX_DIFF_" + to_string(index)};
      int symb_id;
      try
        {
          symb_id = addSymbol(varname, SymbolType::endogenous);
        }
      catch (AlreadyDeclaredException &e)
        {
          cerr << "ERROR: you should rename your variable called " << varname << ", this name is internally used by Dynare" << endl;
          exit(EXIT_FAILURE);
        }
    
      aux_vars.emplace_back(symb_id, AuxVarType::diff, move(orig_symb_id), move(orig_lag), 0, 0, expr_arg, "");
    
      return symb_id;
    }
    
    int
    SymbolTable::addUnaryOpAuxiliaryVar(int index, expr_t expr_arg, string unary_op, optional<int> orig_symb_id, optional<int> orig_lag) noexcept(false)
    {
      string varname{"AUX_UOP_" + to_string(index)};
      int symb_id;
      try
        {
          symb_id = addSymbol(varname, SymbolType::endogenous);
        }
      catch (AlreadyDeclaredException &e)
        {
          cerr << "ERROR: you should rename your variable called " << varname << ", this name is internally used by Dynare" << endl;
          exit(EXIT_FAILURE);
        }
    
      aux_vars.emplace_back(symb_id, AuxVarType::unaryOp, move(orig_symb_id), move(orig_lag), 0, 0, expr_arg, unary_op);
    
      return symb_id;
    }
    
    int
    SymbolTable::addMultiplierAuxiliaryVar(int index) noexcept(false)
    {
      string varname{"MULT_" + to_string(index+1)};
      int symb_id;
      try
        {
          symb_id = addSymbol(varname, SymbolType::endogenous);
        }
      catch (AlreadyDeclaredException &e)
        {
          cerr << "ERROR: you should rename your variable called " << varname << ", this name is internally used by Dynare" << endl;
          exit(EXIT_FAILURE);
        }
    
      aux_vars.emplace_back(symb_id, AuxVarType::multiplier, 0, 0, index, 0, nullptr, "");
      return symb_id;
    }
    
    int
    SymbolTable::addDiffForwardAuxiliaryVar(int orig_symb_id, int orig_lead_lag, expr_t expr_arg) noexcept(false)
    {
      string varname{"AUX_DIFF_FWRD_" + to_string(orig_symb_id+1)};
      int symb_id;
      try
        {
          symb_id = addSymbol(varname, SymbolType::endogenous);
        }
      catch (AlreadyDeclaredException &e)
        {
          cerr << "ERROR: you should rename your variable called " << varname << ", this name is internally used by Dynare" << endl;
          exit(EXIT_FAILURE);
        }
    
      aux_vars.emplace_back(symb_id, AuxVarType::diffForward, orig_symb_id, orig_lead_lag, 0, 0, expr_arg, "");
      return symb_id;
    }
    
    int
    SymbolTable::addPacExpectationAuxiliaryVar(const string &name, expr_t expr_arg)
    {
      int symb_id;
      try
        {
          symb_id = addSymbol(name, SymbolType::endogenous);
        }
      catch (AlreadyDeclaredException &e)
        {
          cerr << "ERROR: the variable/parameter '" << name << "' conflicts with a variable that will be generated for a 'pac_expectation' expression. Please rename it." << endl;
          exit(EXIT_FAILURE);
        }
    
      aux_vars.emplace_back(symb_id, AuxVarType::pacExpectation, 0, 0, 0, 0, expr_arg, "");
      return symb_id;
    }
    
    int
    SymbolTable::addPacTargetNonstationaryAuxiliaryVar(const string &name, expr_t expr_arg)
    {
      int symb_id;
      try
        {
          symb_id = addSymbol(name, SymbolType::endogenous);
        }
      catch (AlreadyDeclaredException &e)
        {
          cerr << "ERROR: the variable/parameter '" << name << "' conflicts with a variable that will be generated for a 'pac_target_nonstationary' expression. Please rename it." << endl;
          exit(EXIT_FAILURE);
        }
    
      aux_vars.emplace_back(symb_id, AuxVarType::pacTargetNonstationary, 0, 0, 0, 0, expr_arg, "");
      return symb_id;
    }
    
    int
    SymbolTable::searchAuxiliaryVars(int orig_symb_id, int orig_lead_lag) const noexcept(false)
    {
      for (const auto &aux_var : aux_vars)
        if ((aux_var.type == AuxVarType::endoLag || aux_var.type == AuxVarType::exoLag)
            && aux_var.orig_symb_id == orig_symb_id && aux_var.orig_lead_lag == orig_lead_lag)
          return aux_var.symb_id;
      throw SearchFailedException(orig_symb_id, orig_lead_lag);
    }
    
    int
    SymbolTable::getOrigSymbIdForAuxVar(int aux_var_symb_id_arg) const noexcept(false)
    {
      for (const auto &aux_var : aux_vars)
        if ((aux_var.type == AuxVarType::endoLag
             || aux_var.type == AuxVarType::exoLag
             || aux_var.type == AuxVarType::diff
             || aux_var.type == AuxVarType::diffLag
             || aux_var.type == AuxVarType::diffLead
             || aux_var.type == AuxVarType::diffForward
             || aux_var.type == AuxVarType::unaryOp)
            && aux_var.symb_id == aux_var_symb_id_arg)
          if (optional<int> r = aux_var.orig_symb_id; r)
            return *r;
          else
            throw UnknownSymbolIDException(aux_var_symb_id_arg); // Some diff and unaryOp auxvars have orig_symb_id unset
      throw UnknownSymbolIDException(aux_var_symb_id_arg);
    }
    
    pair<int, int>
    SymbolTable::unrollDiffLeadLagChain(int symb_id, int lag) const noexcept(false)
    {
      for (const auto &aux_var : aux_vars)
        if (aux_var.symb_id == symb_id)
          if (aux_var.type == AuxVarType::diffLag || aux_var.type == AuxVarType::diffLead)
            {
              auto [orig_symb_id, orig_lag] = unrollDiffLeadLagChain(aux_var.orig_symb_id.value(), lag);
              return { orig_symb_id, orig_lag + aux_var.orig_lead_lag.value() };
            }
      return { symb_id, lag };
    }
    
    void
    SymbolTable::markPredetermined(int symb_id) noexcept(false)
    {
      validateSymbID(symb_id);
    
      if (frozen)
        throw FrozenException();
    
      assert(getType(symb_id) == SymbolType::endogenous);
    
      predetermined_variables.insert(symb_id);
    }
    
    void
    SymbolTable::markWithLogTransform(int symb_id) noexcept(false)
    {
      validateSymbID(symb_id);
    
      if (frozen)
        throw FrozenException();
    
      assert(getType(symb_id) == SymbolType::endogenous);
    
      with_log_transform.insert(symb_id);
    }
    
    bool
    SymbolTable::isPredetermined(int symb_id) const noexcept(false)
    {
      validateSymbID(symb_id);
      return predetermined_variables.contains(symb_id);
    }
    
    int
    SymbolTable::predeterminedNbr() const
    {
      return predetermined_variables.size();
    }
    
    void
    SymbolTable::addObservedVariable(int symb_id) noexcept(false)
    {
      validateSymbID(symb_id);
      assert(getType(symb_id) == SymbolType::endogenous);
      varobs.push_back(symb_id);
    }
    
    int
    SymbolTable::observedVariablesNbr() const
    {
      return static_cast<int>(varobs.size());
    }
    
    bool
    SymbolTable::isObservedVariable(int symb_id) const
    {
      return find(varobs.begin(), varobs.end(), symb_id) != varobs.end();
    }
    
    int
    SymbolTable::getObservedVariableIndex(int symb_id) const
    {
      auto it = find(varobs.begin(), varobs.end(), symb_id);
      assert(it != varobs.end());
      return static_cast<int>(it - varobs.begin());
    }
    
    void
    SymbolTable::addObservedExogenousVariable(int symb_id) noexcept(false)
    {
      validateSymbID(symb_id);
      assert(getType(symb_id) != SymbolType::endogenous);
      varexobs.push_back(symb_id);
    }
    
    int
    SymbolTable::observedExogenousVariablesNbr() const
    {
      return static_cast<int>(varexobs.size());
    }
    
    bool
    SymbolTable::isObservedExogenousVariable(int symb_id) const
    {
      return find(varexobs.begin(), varexobs.end(), symb_id) != varexobs.end();
    }
    
    int
    SymbolTable::getObservedExogenousVariableIndex(int symb_id) const
    {
      auto it = find(varexobs.begin(), varexobs.end(), symb_id);
      assert(it != varexobs.end());
      return static_cast<int>(it - varexobs.begin());
    }
    
    vector <int>
    SymbolTable::getTrendVarIds() const
    {
      vector <int> trendVars;
      for (const auto &it : symbol_table)
        if (getType(it.second) == SymbolType::trend || getType(it.second) == SymbolType::logTrend)
          trendVars.push_back(it.second);
      return trendVars;
    }
    
    set<int>
    SymbolTable::getExogenous() const
    {
      set <int> exogs;
      for (const auto &it : symbol_table)
        if (getType(it.second) == SymbolType::exogenous)
          exogs.insert(it.second);
      return exogs;
    }
    
    set<int>
    SymbolTable::getObservedExogenous() const
    {
      set <int> oexogs;
      for (const auto &it : symbol_table)
        if (getType(it.second) == SymbolType::exogenous)
          if (isObservedExogenousVariable(it.second))
            oexogs.insert(it.second);
      return oexogs;
    }
    
    set<int>
    SymbolTable::getEndogenous() const
    {
      set <int> endogs;
      for (const auto &it : symbol_table)
        if (getType(it.second) == SymbolType::endogenous)
          endogs.insert(it.second);
      return endogs;
    }
    
    bool
    SymbolTable::isAuxiliaryVariable(int symb_id) const
    {
      return any_of(aux_vars.begin(), aux_vars.end(), [=](const auto &av) { return av.symb_id == symb_id; });
    }
    
    bool
    SymbolTable::isDiffAuxiliaryVariable(int symb_id) const
    {
      return any_of(aux_vars.begin(), aux_vars.end(),
                    [=](const auto &av) { return av.symb_id == symb_id
                        && (av.type == AuxVarType::diff
                            || av.type == AuxVarType::diffLag
                            || av.type == AuxVarType::diffLead); });
    }
    
    set<int>
    SymbolTable::getOrigEndogenous() const
    {
      set <int> origendogs;
      for (const auto &it : symbol_table)
        if (getType(it.second) == SymbolType::endogenous && !isAuxiliaryVariable(it.second))
          origendogs.insert(it.second);
      return origendogs;
    }
    
    void
    SymbolTable::writeJsonOutput(ostream &output) const
    {
      output << R"("endogenous": )";
      writeJsonVarVector(output, endo_ids);
    
      output << R"(, "exogenous":)";
      writeJsonVarVector(output, exo_ids);
    
      output << R"(, "exogenous_deterministic": )";
      writeJsonVarVector(output, exo_det_ids);
    
      output << R"(, "parameters": )";
      writeJsonVarVector(output, param_ids);
    
      if (observedVariablesNbr() > 0)
        {
          output << R"(, "varobs": [)";
          for (size_t i = 0; i < varobs.size(); i++)
    	{
    	  if (i != 0)
    	    output << ", ";
    	  output << R"(")" << getName(varobs[i]) << R"(")";
    	}
          output << "]" << endl;
          
          output << R"(, "varobs_ids": [)";
          for (size_t i = 0; i < varobs.size(); i++)
    	{
    	  if (i != 0)
    	    output << ", ";
    	  output << getTypeSpecificID(varobs[i])+1;
    	}
          output << "]" << endl;
        }
    
      if (observedExogenousVariablesNbr() > 0)
        {
          output << R"(, "varexobs": [)";
          for (size_t i = 0; i < varexobs.size(); i++)
    	{
    	  if (i != 0)
    	    output << ", ";
    	  output << R"(")" << getName(varexobs[i]) << R"(")";
    	}
          output << "]" << endl;
          
          output << R"(, "varexobs_ids": [)";
          for (size_t i = 0; i < varexobs.size(); i++)
    	{
    	  if (i != 0)
    	    output << ", ";
    	  output << getTypeSpecificID(varexobs[i])+1;
    	}
          output << "]" << endl;
        }
      // Write the auxiliary variable table
      output << R"(, "orig_endo_nbr": )" << orig_endo_nbr() << endl;
      if (aux_vars.size() == 0)
        output << R"(, "aux_vars": [])";
      else
        {
          output << R"(, "aux_vars": [)" << endl;
          for (int i = 0; i < static_cast<int>(aux_vars.size()); i++)
    	{
    	  if (i != 0)
    	    output << ", ";
    	  output << R"({"endo_index": )" << getTypeSpecificID(aux_vars[i].symb_id)+1
    		 << R"(, "type": )" << aux_vars[i].get_type_id();
    	  switch (aux_vars[i].type)
    	    {
    	    case AuxVarType::endoLead:
    	    case AuxVarType::exoLead:
    	    case AuxVarType::expectation:
    	    case AuxVarType::pacExpectation:
                case AuxVarType::pacTargetNonstationary:
    	      break;
    	    case AuxVarType::endoLag:
    	    case AuxVarType::exoLag:
                case AuxVarType::logTransform:
    	    case AuxVarType::diffLag:
    	    case AuxVarType::diffLead:
    	    case AuxVarType::diffForward:
    	      output << R"(, "orig_index": )" << getTypeSpecificID(aux_vars[i].orig_symb_id.value())+1
    		     << R"(, "orig_lead_lag": )" << aux_vars[i].orig_lead_lag.value();
    	      break;
    	    case AuxVarType::unaryOp:
                  output << R"(, "unary_op": ")" << aux_vars[i].unary_op << R"(")";
                  [[fallthrough]];
                case AuxVarType::diff:
    	      if (aux_vars[i].orig_symb_id)
    		output << R"(, "orig_index": )" << getTypeSpecificID(*aux_vars[i].orig_symb_id)+1
    		       << R"(, "orig_lead_lag": )" << aux_vars[i].orig_lead_lag.value();
    	      break;
    	    case AuxVarType::multiplier:
    	      output << R"(, "eq_nbr": )" << aux_vars[i].equation_number_for_multiplier + 1;
    	      break;
    	    }
    
    	  if (expr_t orig_expr = aux_vars[i].expr_node;
    	      orig_expr)
    	    {
    	      output << R"(, "orig_expr": ")";
    	      orig_expr->writeJsonOutput(output, {}, {});
    	      output << R"(")";
    	    }
    	  output << '}' << endl;
    	}
          output << "]" << endl;
        }
    }
    
    void
    SymbolTable::writeJsonVarVector(ostream &output, const vector<int> &varvec) const
    {
      output << "[";
      for (size_t i = 0; i < varvec.size(); i++)
        {
          if (i != 0)
            output << ", ";
          output << "{"
                 << R"("name":")" << getName(varvec[i]) << R"(", )"
                 << R"("texName":")" << boost::replace_all_copy(getTeXName(varvec[i]), R"(\)", R"(\\)") << R"(", )"
                 << R"("longName":")" << boost::replace_all_copy(getLongName(varvec[i]), R"(\)", R"(\\)") << R"("})"
                 << endl;
        }
      output << "]" << endl;
    }
    
    int
    SymbolTable::getUltimateOrigSymbID(int symb_id) const
    {
      while (isAuxiliaryVariable(symb_id))
        try
          {
            symb_id = getOrigSymbIdForAuxVar(symb_id);
          }
        catch (UnknownSymbolIDException &)
          {
            break;
          }
      return symb_id;
    }
    
    optional<int>
    SymbolTable::getEquationNumberForMultiplier(int symb_id) const
    {
      for (const auto &aux_var : aux_vars)
        if (aux_var.symb_id == symb_id && aux_var.type == AuxVarType::multiplier)
          return aux_var.equation_number_for_multiplier;
      return nullopt;
    }
    
    const set<int> &
    SymbolTable::getVariablesWithLogTransform() const
    {
      return with_log_transform;
    }
    
    set<int>
    SymbolTable::getLagrangeMultipliers() const
    {
      set<int> r;
      for (const auto &aux_var : aux_vars)
        if (aux_var.type == AuxVarType::multiplier)
          r.insert(aux_var.symb_id);
      return r;
    }