Add new “log” option to “var” statement

When an endogenous is declared with “var(log)”, say “y”:
– creates an auxiliary named “LOG_y”
– replaces “y(±l)” everywhere by “exp(LOG_y(±l))”
– adds a new auxiliary equation “y=exp(LOG_y)”
– adds a new definition “LOG_y=log(y)” in set_auxiliary_variables.m and
  dynamic_set_auxiliary_series.m files

This option also works in conjunction with “deflator=…”, such as “var(log,
deflator=…)” (the “log” must appear befor “deflator”). There are no provisions
for combining “log” with “log_deflator”, because that would not make much sense
from an economic point of view (amounts to taking the log two times).

Ref. dynare#349
fix-tolerance-parameters
Sébastien Villemot 2022-03-30 17:40:01 +02:00
parent ee14027e1b
commit 71edfd05e4
No known key found for this signature in database
GPG Key ID: 2CECE9350ECEBE4A
10 changed files with 217 additions and 14 deletions

View File

@ -5768,6 +5768,65 @@ DynamicModel::transformPredeterminedVariables()
// No need to handle static_only_equations, since there are no leads/lags there
}
void
DynamicModel::substituteLogTransform()
{
for (int symb_id : symbol_table.getVariablesWithLogTransform())
{
expr_t aux_def = AddLog(AddVariable(symb_id));
int aux_symb_id = symbol_table.addLogTransformAuxiliaryVar(symb_id, 0, aux_def);
for (auto &[id, definition] : local_variables_table)
definition = definition->substituteLogTransform(symb_id, aux_symb_id);
for (auto &equation : equations)
equation = dynamic_cast<BinaryOpNode *>(equation->substituteLogTransform(symb_id, aux_symb_id));
for (auto &equation : static_only_equations)
equation = dynamic_cast<BinaryOpNode *>(equation->substituteLogTransform(symb_id, aux_symb_id));
/*
We add the following new equations:
+ X=exp(log_X) to the model
+ log_X=log(X) to the list of auxiliary equations
In this way:
+ statements like X=1 in initval/endval blocks will be correctly
handled (i.e. log_X will be initialized to 0 in this case), through
the set_auxiliary_variables.m and dynamic_set_auxiliary_series.m files
+ computation of X in perfect foresight simulations will be done by
simple evaluation when using block decomposition (X will belong to an
block of type evaluate, or maybe even the epilogue)
*/
addAuxEquation(AddEqual(AddVariable(aux_symb_id), aux_def));
addEquation(AddEqual(AddVariable(symb_id), AddExp(AddVariable(aux_symb_id))),
-1, {});
}
}
void
DynamicModel::checkNoWithLogTransform(const set<int> &eqnumbers)
{
set<int> endos;
for (int eq : eqnumbers)
equations[eq]->collectVariables(SymbolType::endogenous, endos);
const set<int> &with_log_transform = symbol_table.getVariablesWithLogTransform();
vector<int> intersect;
set_intersection(endos.begin(), endos.end(),
with_log_transform.begin(), with_log_transform.end(),
back_inserter(intersect));
if (!intersect.empty())
{
cerr << "ERROR: the following variables are declared with var(log) and therefore cannot appear in a VAR/TCM/PAC equation: ";
for (int symb_id : intersect)
cerr << symbol_table.getName(symb_id) << " ";
cerr << endl;
exit(EXIT_FAILURE);
}
}
void
DynamicModel::detrendEquations()
{

View File

@ -499,6 +499,12 @@ public:
//! Transforms the model by decreasing the lead/lag of predetermined variables in model equations by one
void transformPredeterminedVariables();
// Performs the transformations associated to variables declared with “var(log)”
void substituteLogTransform();
// Check that no variable was declared with “var(log)” in the given equations
void checkNoWithLogTransform(const set<int> &eqnumbers);
//! Transforms the model by removing trends specified by the user
void detrendEquations();

View File

@ -491,11 +491,17 @@ log_trend_var : LOG_TREND_VAR '(' LOG_GROWTH_FACTOR EQUAL { driver.begin_model()
;
var : VAR symbol_list_with_tex_and_partition ';'
{ driver.var($2); }
{ driver.var($2, false); }
| VAR '(' LOG ')' symbol_list_with_tex_and_partition ';'
{ driver.var($5, true); }
| VAR '(' DEFLATOR EQUAL { driver.begin_model(); } hand_side ')' symbol_list_with_tex_and_partition ';'
{ driver.end_nonstationary_var(false, $6, $8); }
{ driver.end_nonstationary_var(false, $6, $8, false); }
| VAR '(' LOG COMMA DEFLATOR EQUAL { driver.begin_model(); } hand_side ')' symbol_list_with_tex_and_partition ';'
{ driver.end_nonstationary_var(false, $8, $10, true); }
| VAR '(' LOG_DEFLATOR EQUAL { driver.begin_model(); } hand_side ')' symbol_list_with_tex_and_partition ';'
{ driver.end_nonstationary_var(true, $6, $8); }
{ driver.end_nonstationary_var(true, $6, $8, false); }
/* The case LOG + LOG_DEFLATOR is omitted, because it does not make much sense
from an economic point of view (amounts to taking the log two times) */
;
var_remove : VAR_REMOVE symbol_list ';' { driver.var_remove($2); };

View File

@ -748,6 +748,12 @@ NumConstNode::replaceVarsInEquation(map<VariableNode *, NumConstNode *> &table)
return const_cast<NumConstNode *>(this);
}
expr_t
NumConstNode::substituteLogTransform(int orig_symb_id, int aux_symb_id) const
{
return const_cast<NumConstNode *>(this);
}
VariableNode::VariableNode(DataTree &datatree_arg, int idx_arg, int symb_id_arg, int lag_arg) :
ExprNode{datatree_arg, idx_arg},
symb_id{symb_id_arg},
@ -1990,6 +1996,18 @@ VariableNode::matchMatchedMoment(vector<int> &symb_ids, vector<int> &lags, vecto
powers.push_back(1);
}
expr_t
VariableNode::substituteLogTransform(int orig_symb_id, int aux_symb_id) const
{
if (get_type() == SymbolType::modelLocalVariable)
return datatree.getLocalVariable(symb_id)->substituteLogTransform(orig_symb_id, aux_symb_id);
if (symb_id == orig_symb_id)
return datatree.AddExp(datatree.AddVariable(aux_symb_id, lag));
else
return const_cast<VariableNode *>(this);
}
UnaryOpNode::UnaryOpNode(DataTree &datatree_arg, int idx_arg, UnaryOpcode op_code_arg, const expr_t arg_arg, int expectation_information_set_arg, int param1_symb_id_arg, int param2_symb_id_arg, string adl_param_name_arg, vector<int> adl_lags_arg) :
ExprNode{datatree_arg, idx_arg},
arg{arg_arg},
@ -3768,6 +3786,13 @@ UnaryOpNode::replaceVarsInEquation(map<VariableNode *, NumConstNode *> &table) c
return buildSimilarUnaryOpNode(argsubst, datatree);
}
expr_t
UnaryOpNode::substituteLogTransform(int orig_symb_id, int aux_symb_id) const
{
expr_t argsubst = arg->substituteLogTransform(orig_symb_id, aux_symb_id);
return buildSimilarUnaryOpNode(argsubst, datatree);
}
BinaryOpNode::BinaryOpNode(DataTree &datatree_arg, int idx_arg, const expr_t arg1_arg,
BinaryOpcode op_code_arg, const expr_t arg2_arg, int powerDerivOrder_arg) :
ExprNode{datatree_arg, idx_arg},
@ -5620,6 +5645,13 @@ BinaryOpNode::matchMatchedMoment(vector<int> &symb_ids, vector<int> &lags, vecto
throw MatchFailureException("Unsupported binary operator");
}
expr_t
BinaryOpNode::substituteLogTransform(int orig_symb_id, int aux_symb_id) const
{
expr_t arg1subst = arg1->substituteLogTransform(orig_symb_id, aux_symb_id);
expr_t arg2subst = arg2->substituteLogTransform(orig_symb_id, aux_symb_id);
return buildSimilarBinaryOpNode(arg1subst, arg2subst, datatree);
}
TrinaryOpNode::TrinaryOpNode(DataTree &datatree_arg, int idx_arg, const expr_t arg1_arg,
TrinaryOpcode op_code_arg, const expr_t arg2_arg, const expr_t arg3_arg) :
@ -6499,6 +6531,15 @@ TrinaryOpNode::replaceVarsInEquation(map<VariableNode *, NumConstNode *> &table)
return buildSimilarTrinaryOpNode(arg1subst, arg2subst, arg3subst, datatree);
}
expr_t
TrinaryOpNode::substituteLogTransform(int orig_symb_id, int aux_symb_id) const
{
expr_t arg1subst = arg1->substituteLogTransform(orig_symb_id, aux_symb_id);
expr_t arg2subst = arg2->substituteLogTransform(orig_symb_id, aux_symb_id);
expr_t arg3subst = arg3->substituteLogTransform(orig_symb_id, aux_symb_id);
return buildSimilarTrinaryOpNode(arg1subst, arg2subst, arg3subst, datatree);
}
AbstractExternalFunctionNode::AbstractExternalFunctionNode(DataTree &datatree_arg,
int idx_arg,
int symb_id_arg,
@ -7079,6 +7120,15 @@ ExternalFunctionNode::ExternalFunctionNode(DataTree &datatree_arg,
{
}
expr_t
AbstractExternalFunctionNode::substituteLogTransform(int orig_symb_id, int aux_symb_id) const
{
vector<expr_t> arguments_subst;
for (auto argument : arguments)
arguments_subst.push_back(argument->substituteLogTransform(orig_symb_id, aux_symb_id));
return buildSimilarExternalFunctionNode(arguments_subst, datatree);
}
expr_t
ExternalFunctionNode::composeDerivatives(const vector<expr_t> &dargs)
{
@ -8371,6 +8421,12 @@ SubModelNode::removeTrendLeadLag(const map<int, expr_t> &trend_symbols_map) cons
exit(EXIT_FAILURE);
}
expr_t
SubModelNode::substituteLogTransform(int orig_symb_id, int aux_symb_id) const
{
return const_cast<SubModelNode *>(this);
}
VarExpectationNode::VarExpectationNode(DataTree &datatree_arg,
int idx_arg,
string model_name_arg) :

View File

@ -744,6 +744,9 @@ public:
// Returns true if the expression contains an exogenous or an exogenous deterministic
bool hasExogenous() const;
// Substitutes orig_symb_id(±l) with exp(aux_symb_id(±l)) (used for “var(log)”)
virtual expr_t substituteLogTransform(int orig_symb_id, int aux_symb_id) const = 0;
};
//! Object used to compare two nodes (using their indexes)
@ -826,6 +829,7 @@ public:
bool containsPacTargetNonstationary(const string &pac_model_name = "") const override;
bool isParamTimesEndogExpr() const override;
expr_t substituteStaticAuxiliaryVariable() const override;
expr_t substituteLogTransform(int orig_symb_id, int aux_symb_id) const override;
};
//! Symbol or variable node
@ -901,6 +905,7 @@ public:
expr_t substituteStaticAuxiliaryVariable() const override;
void matchMatchedMoment(vector<int> &symb_ids, vector<int> &lags, vector<int> &powers) const override;
pair<int, expr_t> matchEndogenousTimesConstant() const override;
expr_t substituteLogTransform(int orig_symb_id, int aux_symb_id) const override;
};
//! Unary operator node
@ -1004,6 +1009,7 @@ public:
//! Substitute auxiliary variables by their expression in static model
expr_t substituteStaticAuxiliaryVariable() const override;
void decomposeAdditiveTerms(vector<pair<expr_t, int>> &terms, int current_sign) const override;
expr_t substituteLogTransform(int orig_symb_id, int aux_symb_id) const override;
};
//! Binary operator node
@ -1148,6 +1154,7 @@ public:
void decomposeMultiplicativeFactors(vector<pair<expr_t, int>> &factors, int current_exponent = 1) const override;
void matchMatchedMoment(vector<int> &symb_ids, vector<int> &lags, vector<int> &powers) const override;
pair<int, expr_t> matchEndogenousTimesConstant() const override;
expr_t substituteLogTransform(int orig_symb_id, int aux_symb_id) const override;
};
//! Trinary operator node
@ -1244,6 +1251,7 @@ public:
bool isParamTimesEndogExpr() const override;
//! Substitute auxiliary variables by their expression in static model
expr_t substituteStaticAuxiliaryVariable() const override;
expr_t substituteLogTransform(int orig_symb_id, int aux_symb_id) const override;
};
//! External function node
@ -1355,6 +1363,7 @@ public:
bool isParamTimesEndogExpr() const override;
//! Substitute auxiliary variables by their expression in static model
expr_t substituteStaticAuxiliaryVariable() const override;
expr_t substituteLogTransform(int orig_symb_id, int aux_symb_id) const override;
};
class ExternalFunctionNode : public AbstractExternalFunctionNode
@ -1531,6 +1540,7 @@ public:
expr_t replaceTrendVar() const override;
expr_t detrend(int symb_id, bool log_trend, expr_t trend) const override;
expr_t removeTrendLeadLag(const map<int, expr_t> &trend_symbols_map) const override;
expr_t substituteLogTransform(int orig_symb_id, int aux_symb_id) const override;
};
class VarExpectationNode : public SubModelNode

View File

@ -428,6 +428,9 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, bool
set<int> unary_ops_eqs = dynamic_model.getEquationNumbersFromTags(var_tcm_eqtags);
unary_ops_eqs.merge(dynamic_model.findPacExpectationEquationNumbers());
// Check that no variable in VAR/TCM/PAC equations was declared with “var(log)”
dynamic_model.checkNoWithLogTransform(unary_ops_eqs);
// Create auxiliary variables and equations for unary ops
lag_equivalence_table_t unary_ops_nodes;
ExprNode::subst_table_t unary_ops_subst_table;
@ -502,6 +505,9 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, bool
dynamic_model.createVariableMapping();
// Must come after detrending of variables and Ramsey policy transformation
dynamic_model.substituteLogTransform();
/* Create auxiliary vars for leads and lags greater than 2, on both endos and
exos. The transformation is not exactly the same on stochastic and
deterministic models, because there is no need to take into account the

View File

@ -205,10 +205,15 @@ ParsingDriver::declare_endogenous(const string &name, const string &tex_name, co
}
void
ParsingDriver::var(const vector<tuple<string, string, vector<pair<string, string>>>> &symbol_list)
ParsingDriver::var(const vector<tuple<string, string, vector<pair<string, string>>>> &symbol_list,
bool log_option)
{
for (auto &[name, tex_name, partition] : symbol_list)
declare_endogenous(name, tex_name, partition);
{
int symb_id = declare_endogenous(name, tex_name, partition);
if (log_option)
mod_file->symbol_table.markWithLogTransform(symb_id);
}
}
int
@ -469,7 +474,7 @@ ParsingDriver::add_expression_variable(const string &name)
}
void
ParsingDriver::end_nonstationary_var(bool log_deflator, expr_t deflator, const vector<tuple<string, string, vector<pair<string, string>>>> &symbol_list)
ParsingDriver::end_nonstationary_var(bool log_deflator, expr_t deflator, const vector<tuple<string, string, vector<pair<string, string>>>> &symbol_list, bool log_option)
{
mod_file->nonstationary_variables = true;
@ -478,6 +483,8 @@ ParsingDriver::end_nonstationary_var(bool log_deflator, expr_t deflator, const v
{
int symb_id = declare_endogenous(name, tex_name, partition);
declared_nonstationary_vars.push_back(symb_id);
if (log_option)
mod_file->symbol_table.markWithLogTransform(symb_id);
}
try

View File

@ -354,8 +354,8 @@ public:
void initval_file();
//! Declares an endogenous variable (and returns its symbol ID)
int declare_endogenous(const string &name, const string &tex_name = "", const vector<pair<string, string>> &partition_value = {});
// Handles a “var” statement (without “deflator” or “log_deflator” options)
void var(const vector<tuple<string, string, vector<pair<string, string>>>> &symbol_list);
// Handles a “var” or “var(log)” statement (without “deflator” or “log_deflator” options)
void var(const vector<tuple<string, string, vector<pair<string, string>>>> &symbol_list, bool log_option);
//! Declares an exogenous variable (and returns its symbol ID)
int declare_exogenous(const string &name, const string &tex_name = "", const vector<pair<string, string>> &partition_value = {});
// Handles a “varexo” statement
@ -844,8 +844,8 @@ public:
void add_steady_state_model_equal_multiple(const vector<string> &symbol_list, expr_t expr);
//! Ends declaration of trend variable
void end_trend_var(bool log_trend, expr_t growth_factor, const vector<pair<string, string>> &symbol_list);
//! Ends declaration of nonstationary variable
void end_nonstationary_var(bool log_deflator, expr_t deflator, const vector<tuple<string, string, vector<pair<string, string>>>> &symbol_list);
//! Handles a “var(deflator=…)”, “var(log, deflator=…)” or “var(log_deflator=…)” statement
void end_nonstationary_var(bool log_deflator, expr_t deflator, const vector<tuple<string, string, vector<pair<string, string>>>> &symbol_list, bool log_option);
//! Add a graph format to the list of formats requested
void add_graph_format(string name);
//! Add the graph_format option to the OptionsList structure

View File

@ -363,6 +363,7 @@ SymbolTable::writeOutput(ostream &output) const noexcept(false)
break;
case AuxVarType::endoLag:
case AuxVarType::exoLag:
case AuxVarType::logTransform:
case AuxVarType::diffLag:
case AuxVarType::diffLead:
case AuxVarType::diffForward:
@ -525,6 +526,26 @@ SymbolTable::addExpectationAuxiliaryVar(int information_set, int index, expr_t e
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)
{
@ -763,6 +784,19 @@ SymbolTable::markPredetermined(int symb_id) noexcept(false)
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)
{
@ -992,6 +1026,7 @@ SymbolTable::writeJsonOutput(ostream &output) const
break;
case AuxVarType::endoLag:
case AuxVarType::exoLag:
case AuxVarType::logTransform:
case AuxVarType::diffLag:
case AuxVarType::diffLead:
case AuxVarType::diffForward:
@ -1064,3 +1099,9 @@ SymbolTable::getEquationNumberForMultiplier(int symb_id) const
return aux_var.get_equation_number_for_multiplier();
return -1;
}
const set<int> &
SymbolTable::getVariablesWithLogTransform() const
{
return with_log_transform;
}

View File

@ -46,9 +46,7 @@ enum class AuxVarType
for the differentiate_forward_vars option.
N.B.: nothing to do with the diff() operator! */
multiplier = 6, //!< Multipliers for FOC of Ramsey Problem
// Value 7 is unused for the time being (but could be reused)
logTransform = 7, //!< Log-transformation of a variable declared with “var(log)”
diff = 8, //!< Variable for Diff operator
diffLag = 9, //!< Variable for timing between Diff operators (lag)
unaryOp = 10, //!< Variable for allowing the undiff operator to work when diff was taken of unary op, eg diff(log(x))
@ -65,7 +63,7 @@ private:
AuxVarType type; //!< Its type
int orig_symb_id; /* Symbol ID of the (only) endo that appears on the RHS of
the definition of this auxvar.
Used by endoLag, exoLag, diffForward, diff, diffLag,
Used by endoLag, exoLag, diffForward, logTransform, diff, diffLag,
diffLead and unaryOp.
For diff and unaryOp, if the argument expression is more complex
than than a simple variable, this value is equal to -1. */
@ -182,6 +180,9 @@ private:
//! Stores the list of observed exogenous variables
vector<int> varexobs;
//! Stores the endogenous variables declared with “var(log)”
set<int> with_log_transform;
public:
//! Thrown when trying to access an unknown symbol (by name)
class UnknownSymbolNameException
@ -308,6 +309,13 @@ public:
\return the symbol ID of the new symbol
*/
int addMultiplierAuxiliaryVar(int index) noexcept(false);
/* Adds an auxiliary variable associated to an endogenous declared with
var(log).
orig_symb_id is the symbol ID of the original variable
orig_lead_lag is typically 0
expr_arg is typically log(orig_symb_id)
*/
int addLogTransformAuxiliaryVar(int orig_symb_id, int orig_lead_lag, expr_t expr_arg) noexcept(false);
//! Adds an auxiliary variable for the (time) differentiate of a forward var
/*!
\param[in] orig_symb_id The symb_id of the forward variable
@ -409,6 +417,8 @@ public:
void writeJsonOutput(ostream &output) const;
//! Mark a symbol as predetermined variable
void markPredetermined(int symb_id) noexcept(false);
//! Mark an endogenous as having been declared with “var(log)”
void markWithLogTransform(int symb_id) noexcept(false);
//! Test if a given symbol is a predetermined variable
bool isPredetermined(int symb_id) const noexcept(false);
//! Return the number of predetermined variables
@ -456,6 +466,8 @@ public:
/* Return all the information about a given auxiliary variable. Throws
UnknownSymbolIDException if it is not an aux var */
const AuxVarInfo &getAuxVarInfo(int symb_id) const;
// Returns the set of all endogenous declared with “var(log)”
const set<int> &getVariablesWithLogTransform() const;
};
inline void