Commit 2f1382fa authored by sebastien's avatar sebastien

preprocessor:

* In stochastic mode, now transforms the model by removing leads and lags greater or equal to 2 (creating auxiliary variables and equations in the process)
* Information about these variables is in structure M_.aux_vars
* Automatically add the necessary initialization for auxiliary vars after the initval block or load_params_and_steady_state


git-svn-id: https://www.dynare.org/svn/dynare/trunk@3002 ac1d8469-bf42-47a9-8791-bf33cf982152
parent b051e0b5
......@@ -59,23 +59,28 @@ DataTree::AddNumConstant(const string &value)
return new NumConstNode(*this, id);
}
NodeID
DataTree::AddVariableInternal(const string &name, int lag)
VariableNode *
DataTree::AddVariableInternal(int symb_id, int lag)
{
int symb_id = symbol_table.getID(name);
variable_node_map_type::iterator it = variable_node_map.find(make_pair(symb_id, lag));
if (it != variable_node_map.end())
return it->second;
else
return new VariableNode(*this, symb_id, lag, computeDerivID(symb_id, lag));
return new VariableNode(*this, symb_id, lag);
}
NodeID
VariableNode *
DataTree::AddVariable(const string &name, int lag)
{
int symb_id = symbol_table.getID(name);
return AddVariable(symb_id, lag);
}
VariableNode *
DataTree::AddVariable(int symb_id, int lag)
{
assert(lag == 0);
return AddVariableInternal(name, lag);
return AddVariableInternal(symb_id, lag);
}
NodeID
......@@ -445,25 +450,6 @@ DataTree::AddUnknownFunction(const string &function_name, const vector<NodeID> &
return new UnknownFunctionNode(*this, id, arguments);
}
void
DataTree::fillEvalContext(eval_context_type &eval_context) const
{
for(map<int, NodeID>::const_iterator it = local_variables_table.begin();
it != local_variables_table.end(); it++)
{
try
{
const NodeID expression = it->second;
double val = expression->eval(eval_context);
eval_context[it->first] = val;
}
catch(ExprNode::EvalException &e)
{
// Do nothing
}
}
}
bool
DataTree::isSymbolUsed(int symb_id) const
{
......@@ -478,12 +464,6 @@ DataTree::isSymbolUsed(int symb_id) const
return false;
}
int
DataTree::computeDerivID(int symb_id, int lag)
{
return -1;
}
int
DataTree::getDerivID(int symb_id, int lag) const throw (UnknownDerivIDException)
{
......
......@@ -49,26 +49,24 @@ protected:
//! Reference to numerical constants table
NumericalConstants &num_constants;
typedef map<int, NodeID> num_const_node_map_type;
typedef map<int, NumConstNode *> num_const_node_map_type;
num_const_node_map_type num_const_node_map;
//! Pair (symbol_id, lag) used as key
typedef map<pair<int, int>, NodeID> variable_node_map_type;
typedef map<pair<int, int>, VariableNode *> variable_node_map_type;
variable_node_map_type variable_node_map;
typedef map<pair<NodeID, int>, NodeID> unary_op_node_map_type;
typedef map<pair<NodeID, int>, UnaryOpNode *> unary_op_node_map_type;
unary_op_node_map_type unary_op_node_map;
typedef map<pair<pair<NodeID, NodeID>, int>, NodeID> binary_op_node_map_type;
typedef map<pair<pair<NodeID, NodeID>, int>, BinaryOpNode *> binary_op_node_map_type;
binary_op_node_map_type binary_op_node_map;
typedef map<pair<pair<pair<NodeID, NodeID>,NodeID>, int>, NodeID> trinary_op_node_map_type;
typedef map<pair<pair<pair<NodeID, NodeID>,NodeID>, int>, TrinaryOpNode *> trinary_op_node_map_type;
trinary_op_node_map_type trinary_op_node_map;
//! Stores local variables value (maps symbol ID to corresponding node)
map<int, NodeID> local_variables_table;
//! Internal implementation of AddVariable(), without the check on the lag
NodeID AddVariableInternal(const string &name, int lag);
VariableNode *AddVariableInternal(int symb_id, int lag);
//! Computes a new deriv_id, or returns -1 if the variable is not one w.r. to which to derive
virtual int computeDerivID(int symb_id, int lag);
private:
typedef list<NodeID> node_list_type;
//! The list of nodes
......@@ -100,7 +98,9 @@ public:
NodeID AddNumConstant(const string &value);
//! Adds a variable
/*! The default implementation of the method refuses any lag != 0 */
virtual NodeID AddVariable(const string &name, int lag = 0);
virtual VariableNode *AddVariable(int symb_id, int lag = 0);
//! Adds a variable, using its symbol name
VariableNode *AddVariable(const string &name, int lag = 0);
//! Adds "arg1+arg2" to model tree
NodeID AddPlus(NodeID iArg1, NodeID iArg2);
//! Adds "arg1-arg2" to model tree
......@@ -172,8 +172,6 @@ public:
//! Adds an unknown function node
/*! \todo Use a map to share identical nodes */
NodeID AddUnknownFunction(const string &function_name, const vector<NodeID> &arguments);
//! Fill eval context with values of local variables
void fillEvalContext(eval_context_type &eval_context) const;
//! Checks if a given symbol is used somewhere in the data tree
bool isSymbolUsed(int symb_id) const;
//! Thrown when trying to access an unknown variable by deriv_id
......
......@@ -48,10 +48,10 @@ DynamicModel::DynamicModel(SymbolTable &symbol_table_arg,
{
}
NodeID
DynamicModel::AddVariable(const string &name, int lag)
VariableNode *
DynamicModel::AddVariable(int symb_id, int lag)
{
return AddVariableInternal(name, lag);
return AddVariableInternal(symb_id, lag);
}
void
......@@ -2292,7 +2292,10 @@ DynamicModel::computingPass(bool jacobianExo, bool hessian, bool thirdDerivative
{
assert(jacobianExo || !(hessian || thirdDerivatives || paramsDerivatives));
// Computes dynamic jacobian columns
// Prepare for derivation
computeDerivIDs();
// Computes dynamic jacobian columns, must be done after computeDerivIDs()
computeDynJacobianCols(jacobianExo);
// Compute derivatives w.r. to all endogenous, and possibly exogenous and exogenous deterministic
......@@ -2408,6 +2411,11 @@ DynamicModel::toStatic(StaticModel &static_model) const
for (vector<BinaryOpNode *>::const_iterator it = equations.begin();
it != equations.end(); it++)
static_model.addEquation((*it)->toStatic(static_model));
// Convert auxiliary equations
for (deque<BinaryOpNode *>::const_iterator it = aux_equations.begin();
it != aux_equations.end(); it++)
static_model.addAuxEquation((*it)->toStatic(static_model));
}
void
......@@ -2424,60 +2432,65 @@ DynamicModel::toStaticDll(StaticDllModel &static_model) const
static_model.addEquation((*it)->toStatic(static_model));
}
int
DynamicModel::computeDerivID(int symb_id, int lag)
void
DynamicModel::computeDerivIDs()
{
// Setting maximum and minimum lags
if (max_lead < lag)
max_lead = lag;
else if (-max_lag > lag)
max_lag = -lag;
set<pair<int, int> > dynvars;
SymbolType type = symbol_table.getType(symb_id);
for(int i = 0; i < (int) equations.size(); i++)
equations[i]->collectVariables(eEndogenous, dynvars);
switch (type)
dynJacobianColsNbr = dynvars.size();
for(int i = 0; i < (int) equations.size(); i++)
{
case eEndogenous:
if (max_endo_lead < lag)
max_endo_lead = lag;
else if (-max_endo_lag > lag)
max_endo_lag = -lag;
break;
case eExogenous:
if (max_exo_lead < lag)
max_exo_lead = lag;
else if (-max_exo_lag > lag)
max_exo_lag = -lag;
break;
case eExogenousDet:
if (max_exo_det_lead < lag)
max_exo_det_lead = lag;
else if (-max_exo_det_lag > lag)
max_exo_det_lag = -lag;
break;
case eParameter:
// We wan't to compute a derivation ID for parameters
break;
default:
return -1;
equations[i]->collectVariables(eExogenous, dynvars);
equations[i]->collectVariables(eExogenousDet, dynvars);
equations[i]->collectVariables(eParameter, dynvars);
}
// Check if dynamic variable already has a deriv_id
pair<int, int> key = make_pair(symb_id, lag);
deriv_id_table_t::const_iterator it = deriv_id_table.find(key);
if (it != deriv_id_table.end())
return it->second;
for(set<pair<int, int> >::const_iterator it = dynvars.begin();
it != dynvars.end(); it++)
{
int lag = it->second;
SymbolType type = symbol_table.getType(it->first);
// Create a new deriv_id
int deriv_id = deriv_id_table.size();
// Setting maximum and minimum lags
if (max_lead < lag)
max_lead = lag;
else if (-max_lag > lag)
max_lag = -lag;
deriv_id_table[key] = deriv_id;
inv_deriv_id_table.push_back(key);
switch (type)
{
case eEndogenous:
if (max_endo_lead < lag)
max_endo_lead = lag;
else if (-max_endo_lag > lag)
max_endo_lag = -lag;
break;
case eExogenous:
if (max_exo_lead < lag)
max_exo_lead = lag;
else if (-max_exo_lag > lag)
max_exo_lag = -lag;
break;
case eExogenousDet:
if (max_exo_det_lead < lag)
max_exo_det_lead = lag;
else if (-max_exo_det_lag > lag)
max_exo_det_lag = -lag;
break;
default:
break;
}
if (type == eEndogenous)
dynJacobianColsNbr++;
// Create a new deriv_id
int deriv_id = deriv_id_table.size();
return deriv_id;
deriv_id_table[*it] = deriv_id;
inv_deriv_id_table.push_back(*it);
}
}
SymbolType
......@@ -2814,4 +2827,100 @@ DynamicModel::hessianHelper(ostream &output, int row_nb, int col_nb, ExprNodeOut
output << RIGHT_ARRAY_SUBSCRIPT(output_type);
}
void
DynamicModel::substituteLeadGreaterThanTwo()
{
ExprNode::subst_table_t subst_table;
vector<BinaryOpNode *> neweqs;
// Substitute in model local variables
for(map<int, NodeID>::iterator it = local_variables_table.begin();
it != local_variables_table.end(); it++)
it->second = it->second->substituteLeadGreaterThanTwo(subst_table, neweqs);
// Substitute in equations
for(int i = 0; i < (int) equations.size(); i++)
{
BinaryOpNode *substeq = dynamic_cast<BinaryOpNode *>(equations[i]->substituteLeadGreaterThanTwo(subst_table, neweqs));
assert(substeq != NULL);
equations[i] = substeq;
}
// Add new equations
for(int i = 0; i < (int) neweqs.size(); i++)
addEquation(neweqs[i]);
// Add the new set of equations at the *beginning* of aux_equations
copy(neweqs.rbegin(), neweqs.rend(), front_inserter(aux_equations));
if (neweqs.size() > 0)
cout << "Substitution of leads >= 2: added " << neweqs.size() << " auxiliary variables and equations." << endl;
}
void
DynamicModel::substituteLagGreaterThanTwo()
{
ExprNode::subst_table_t subst_table;
vector<BinaryOpNode *> neweqs;
// Substitute in model local variables
for(map<int, NodeID>::iterator it = local_variables_table.begin();
it != local_variables_table.end(); it++)
it->second = it->second->substituteLagGreaterThanTwo(subst_table, neweqs);
// Substitute in equations
for(int i = 0; i < (int) equations.size(); i++)
{
BinaryOpNode *substeq = dynamic_cast<BinaryOpNode *>(equations[i]->substituteLagGreaterThanTwo(subst_table, neweqs));
assert(substeq != NULL);
equations[i] = substeq;
}
// Add new equations
for(int i = 0; i < (int) neweqs.size(); i++)
addEquation(neweqs[i]);
// Add the new set of equations at the *beginning* of aux_equations
copy(neweqs.rbegin(), neweqs.rend(), front_inserter(aux_equations));
if (neweqs.size() > 0)
cout << "Substitution of lags >= 2: added " << neweqs.size() << " auxiliary variables and equations." << endl;
}
void
DynamicModel::fillEvalContext(eval_context_type &eval_context) const
{
// First, auxiliary variables
for(deque<BinaryOpNode *>::const_iterator it = aux_equations.begin();
it != aux_equations.end(); it++)
{
assert((*it)->get_op_code() == oEqual);
VariableNode *auxvar = dynamic_cast<VariableNode *>((*it)->get_arg1());
assert(auxvar != NULL);
try
{
double val = (*it)->get_arg2()->eval(eval_context);
eval_context[auxvar->get_symb_id()] = val;
}
catch(ExprNode::EvalException &e)
{
// Do nothing
}
}
// Second, model local variables
for(map<int, NodeID>::const_iterator it = local_variables_table.begin();
it != local_variables_table.end(); it++)
{
try
{
const NodeID expression = it->second;
double val = expression->eval(eval_context);
eval_context[it->first] = val;
}
catch(ExprNode::EvalException &e)
{
// Do nothing
}
}
}
......@@ -31,7 +31,6 @@ using namespace std;
//! Stores a dynamic model
class DynamicModel : public ModelTree
{
public:
private:
typedef map<pair<int, int>, int> deriv_id_table_t;
//! Maps a pair (symbol_id, lag) to a deriv ID
......@@ -44,20 +43,20 @@ private:
map<int, int> dyn_jacobian_cols_table;
//! Maximum lag and lead over all types of variables (positive values)
/*! Set by computeDerivID() */
/*! Set by computeDerivIDs() */
int max_lag, max_lead;
//! Maximum lag and lead over endogenous variables (positive values)
/*! Set by computeDerivID() */
/*! Set by computeDerivIDs() */
int max_endo_lag, max_endo_lead;
//! Maximum lag and lead over exogenous variables (positive values)
/*! Set by computeDerivID() */
/*! Set by computeDerivIDs() */
int max_exo_lag, max_exo_lead;
//! Maximum lag and lead over deterministic exogenous variables (positive values)
/*! Set by computeDerivID() */
/*! Set by computeDerivIDs() */
int max_exo_det_lag, max_exo_det_lead;
//! Number of columns of dynamic jacobian
/*! Set by computeDerivID() and computeDynJacobianCols() */
/*! Set by computeDerivID()s and computeDynJacobianCols() */
int dynJacobianColsNbr;
//! Derivatives of the residuals w.r. to parameters
......@@ -113,7 +112,6 @@ private:
//! Write chain rule derivative code of an equation w.r. to a variable
void compileChainRuleDerivative(ofstream &code_file, int eq, int var, int lag, map_idx_type &map_idx) const;
virtual int computeDerivID(int symb_id, int lag);
//! Get the type corresponding to a derivation ID
SymbolType getTypeByDerivID(int deriv_id) const throw (UnknownDerivIDException);
//! Get the lag corresponding to a derivation ID
......@@ -131,6 +129,10 @@ private:
//! Collect only the first derivatives
map<pair<int, pair<int, int> >, NodeID> collect_first_order_derivatives_endogenous();
//! Allocates the derivation IDs for all dynamic variables of the model
/*! Also computes max_{endo,exo}_{lead_lag}, and initializes dynJacobianColsNbr to the number of dynamic endos */
void computeDerivIDs();
//! Helper for writing the Jacobian elements in MATLAB and C
/*! Writes either (i+1,j+1) or [i+j*no_eq] */
void jacobianHelper(ostream &output, int eq_nb, int col_nb, ExprNodeOutputType output_type) const;
......@@ -147,7 +149,7 @@ public:
DynamicModel(SymbolTable &symbol_table_arg, NumericalConstants &num_constants);
//! Adds a variable node
/*! This implementation allows for non-zero lag */
virtual NodeID AddVariable(const string &name, int lag = 0);
virtual VariableNode *AddVariable(int symb_id, int lag = 0);
//! Absolute value under which a number is considered to be zero
double cutoff;
//! Compute the minimum feedback set in the dynamic model:
......@@ -196,6 +198,15 @@ public:
//! Returns true indicating that this is a dynamic model
virtual bool isDynamic() const { return true; };
//! Transforms the model by removing all leads greater or equal than 2
void substituteLeadGreaterThanTwo();
//! Transforms the model by removing all lags greater or equal than 2
void substituteLagGreaterThanTwo();
//! Fills eval context with values of model local variables and auxiliary variables
void fillEvalContext(eval_context_type &eval_context) const;
};
#endif
......@@ -35,6 +35,9 @@ main2(stringstream &in, string &basename, bool debug, bool clear_all, bool no_tm
// Run checking pass
mod_file->checkPass();
// Perform transformations on the model (creation of auxiliary vars and equations)
mod_file->transformPass();
// Evaluate parameters initialization, initval, endval and pounds
mod_file->evalAllExpressions();
......
......@@ -34,7 +34,7 @@ using namespace __gnu_cxx;
#include "DataTree.hh"
#include "BlockTriangular.hh"
ExprNode::ExprNode(DataTree &datatree_arg) : datatree(datatree_arg)
ExprNode::ExprNode(DataTree &datatree_arg) : datatree(datatree_arg), preparedForDerivation(false)
{
// Add myself to datatree
datatree.node_list.push_back(this);
......@@ -50,6 +50,9 @@ ExprNode::~ExprNode()
NodeID
ExprNode::getDerivative(int deriv_id)
{
if (!preparedForDerivation)
prepareForDerivation();
// Return zero if derivative is necessarily null (using symbolic a priori)
set<int>::const_iterator it = non_null_derivatives.find(deriv_id);
if (it == non_null_derivatives.end())
......@@ -144,6 +147,42 @@ ExprNode::writeOutput(ostream &output)
writeOutput(output, oMatlabOutsideModel, temporary_terms_type());
}
VariableNode *
ExprNode::createLeadAuxiliaryVarForMyself(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const
{
int n = maxEndoLead();
assert(n >= 2);
subst_table_t::const_iterator it = subst_table.find(this);
if (it != subst_table.end())
return const_cast<VariableNode *>(it->second);
NodeID substexpr = decreaseLeadsLags(n-1);
int lag = n-2;
// Each iteration tries to create an auxvar such that auxvar(+1)=expr(-lag)
// At the beginning (resp. end) of each iteration, substexpr is an expression (possibly an auxvar) equivalent to expr(-lag-1) (resp. expr(-lag))
while(lag >= 0)
{
NodeID orig_expr = decreaseLeadsLags(lag);
it = subst_table.find(orig_expr);
if (it == subst_table.end())
{
int symb_id = datatree.symbol_table.addLeadAuxiliaryVar(orig_expr->idx);
neweqs.push_back(dynamic_cast<BinaryOpNode *>(datatree.AddEqual(datatree.AddVariable(symb_id, 0), substexpr)));
substexpr = datatree.AddVariable(symb_id, +1);
assert(dynamic_cast<VariableNode *>(substexpr) != NULL);
subst_table[orig_expr] = dynamic_cast<VariableNode *>(substexpr);
}
else
substexpr = const_cast<VariableNode *>(it->second);
lag--;
}
return dynamic_cast<VariableNode *>(substexpr);
}
NumConstNode::NumConstNode(DataTree &datatree_arg, int id_arg) :
ExprNode(datatree_arg),
......@@ -151,7 +190,12 @@ NumConstNode::NumConstNode(DataTree &datatree_arg, int id_arg) :
{
// Add myself to the num const map
datatree.num_const_node_map[id] = this;
}
void
NumConstNode::prepareForDerivation()
{
preparedForDerivation = true;
// All derivatives are null, so non_null_derivatives is left empty
}
......@@ -221,19 +265,51 @@ NumConstNode::toStatic(DataTree &static_datatree) const
return static_datatree.AddNumConstant(datatree.num_constants.get(id));
}
int
NumConstNode::maxEndoLead() const
{
return 0;
}
VariableNode::VariableNode(DataTree &datatree_arg, int symb_id_arg, int lag_arg, int deriv_id_arg) :
NodeID
NumConstNode::decreaseLeadsLags(int n) const
{
return const_cast<NumConstNode *>(this);
}
NodeID
NumConstNode::substituteLeadGreaterThanTwo(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const
{
return const_cast<NumConstNode *>(this);
}
NodeID
NumConstNode::substituteLagGreaterThanTwo(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const
{
return const_cast<NumConstNode *>(this);
}
VariableNode::VariableNode(DataTree &datatree_arg, int symb_id_arg, int lag_arg) :
ExprNode(datatree_arg),
symb_id(symb_id_arg),
type(datatree.symbol_table.getType(symb_id_arg)),
lag(lag_arg),
deriv_id(deriv_id_arg)
lag(lag_arg)
{
// Add myself to the variable map
datatree.variable_node_map[make_pair(symb_id, lag)] = this;
// It makes sense to allow a lead/lag on parameters: during steady state calibration, endogenous and parameters can be swapped
assert(lag == 0 || (type != eModelLocalVariable && type != eModFileLocalVariable && type != eUnknownFunction));
assert(type != eUnknownFunction
&& (lag == 0 || (type != eModelLocalVariable && type != eModFileLocalVariable)));
}
void
VariableNode::prepareForDerivation()
{
if (preparedForDerivation)
return;
preparedForDerivation = true;
// Fill in non_null_derivatives
switch (type)
......@@ -243,9 +319,10 @@ VariableNode::VariableNode(DataTree &datatree_arg, int symb_id_arg, int lag_arg,
case eExogenousDet:
case eParameter:
// For a variable or a parameter, the only non-null derivative is with respect to itself
non_null_derivatives.insert(deriv_id);
non_null_derivatives.insert(datatree.getDerivID(symb_id, lag));
break;
case eModelLocalVariable:
datatree.local_variables_table[symb_id]->prepareForDerivation();
// Non null derivatives are those of the value of the local parameter
non_null_derivatives = datatree.local_variables_table[symb_id]->non_null_derivatives;
break;
......@@ -253,13 +330,13 @@ VariableNode::VariableNode(DataTree &datatree_arg, int symb_id_arg, int lag_arg,
// Such a variable is never derived
break;
case eUnknownFunction:
cerr << "Attempt to construct a VariableNode with an unknown function name" << endl;
cerr << "VariableNode::prepareForDerivation: impossible case" << endl;
exit(EXIT_FAILURE);
}
}
NodeID
VariableNode::computeDerivative(int deriv_id_arg)
VariableNode::computeDerivative(int deriv_id)
{