Occbin: generate +<basename>/occbin_difference.m

In particular, this implies that steady state values of endogenous in the
“occbin_constraints” block must now be specified using the STEADY_STATE()
operator (and not with a “_ss” suffix).

Moreover:
– make various simplifications to the fields generated under M_
– in the driver file, replace the call to occbin.initialize() by a few explicit operations

Ref. #68
var-models
Sébastien Villemot 2021-07-20 12:20:19 +02:00
parent 90b1235a64
commit 81d4fd5d83
No known key found for this signature in database
GPG Key ID: 2CECE9350ECEBE4A
7 changed files with 112 additions and 24 deletions

View File

@ -5235,7 +5235,7 @@ MatchedMomentsStatement::writeJsonOutput(ostream &output) const
}
OccbinConstraintsStatement::OccbinConstraintsStatement(const SymbolTable &symbol_table_arg,
const vector<tuple<string, expr_t, expr_t, expr_t, expr_t>> constraints_arg)
const vector<tuple<string, BinaryOpNode *, BinaryOpNode *, expr_t, expr_t>> constraints_arg)
: symbol_table{symbol_table_arg}, constraints{constraints_arg}
{
}
@ -5248,30 +5248,90 @@ OccbinConstraintsStatement::checkPass(ModFileStructure &mod_file_struct, Warning
cerr << "ERROR: Multiple 'occbin_constraints' blocks are not allowed" << endl;
exit(EXIT_FAILURE);
}
if (constraints.size() > 2)
{
cerr << "ERROR: only up to two constraints are supported in 'occbin_constraints' block" << endl;
exit(EXIT_FAILURE);
}
mod_file_struct.occbin_constraints_present = true;
}
void
OccbinConstraintsStatement::writeOutput(ostream &output, const string &basename, bool minimal_workspace) const
{
output << "M_.occbin.constraint = [" << endl;
output << "M_.occbin.constraint_nbr = " << constraints.size() << ';' << endl
<< "M_.occbin.pswitch = [" << endl;
for (const auto &[name, bind, relax, error_bind, error_relax] : constraints)
output << symbol_table.getTypeSpecificID(name) + 1 << ' ';
output << "];" << endl
<< "options_.occbin = struct();" << endl
<< "options_.occbin = occbin.set_default_options(options_.occbin, M_);" << endl
<< "oo_.dr=set_state_space(oo_.dr,M_,options_);" << endl;
string filename = "+" + basename + "/occbin_difference.m";
ofstream diff_output;
diff_output.open(filename, ios::out | ios::binary);
if (!diff_output.is_open())
{
cerr << "Error: Can't open file " << filename << " for writing" << endl;
exit(EXIT_FAILURE);
}
diff_output << "function [binding, relax, err] = occbin_difference(zdatalinear, params, steady_state)" << endl;
int idx = 1;
for (const auto &[name, bind, relax, error_bind, error_relax] : constraints)
{
output << "struct('pswitch','" << name << "','bind','";
bind->writeJsonOutput(output, {}, {});
output << "','relax','";
diff_output << "binding.constraint_" << idx << " = ";
dynamic_cast<ExprNode *>(bind)->writeOutput(diff_output, ExprNodeOutputType::occbinDifferenceFile);
diff_output << ';' << endl
<< "relax.constraint_" << idx << " = ";
if (relax)
relax->writeJsonOutput(output, {}, {});
output << "','error_bind','";
dynamic_cast<ExprNode *>(relax)->writeOutput(diff_output, ExprNodeOutputType::occbinDifferenceFile);
else
diff_output << "~binding.constraint_" << idx;
diff_output << ';' << endl
<< "err.binding_constraint_" << idx << " = ";
if (error_bind)
error_bind->writeJsonOutput(output, {}, {});
output << "','error_relax','";
error_bind->writeOutput(diff_output, ExprNodeOutputType::occbinDifferenceFile);
else
{
diff_output << "abs((";
bind->arg1->writeOutput(diff_output, ExprNodeOutputType::occbinDifferenceFile);
diff_output << ")-(";
bind->arg2->writeOutput(diff_output, ExprNodeOutputType::occbinDifferenceFile);
diff_output << "))";
}
diff_output << ';' << endl
<< "err.relax_constraint_" << idx << " = ";
if (error_relax)
error_relax->writeJsonOutput(output, {}, {});
output << "');" << endl;
error_relax->writeOutput(diff_output, ExprNodeOutputType::occbinDifferenceFile);
else if (relax)
{
diff_output << "abs((";
relax->arg1->writeOutput(diff_output, ExprNodeOutputType::occbinDifferenceFile);
diff_output << ")-(";
relax->arg2->writeOutput(diff_output, ExprNodeOutputType::occbinDifferenceFile);
diff_output << "))";
}
else if (!error_bind)
/* If relax, error_relax and error_bind have not been specified, then
error_bind and error_relax have the same default value. */
diff_output << "err.binding_constraint_" << idx;
else
{
/* If relax and error_relax have not been specified, but error_bind
has been specified, then we need to compute the default value for
error_relax since it is different from error_bind. */
diff_output << "abs((";
bind->arg1->writeOutput(diff_output, ExprNodeOutputType::occbinDifferenceFile);
diff_output << ")-(";
bind->arg2->writeOutput(diff_output, ExprNodeOutputType::occbinDifferenceFile);
diff_output << "))";
}
diff_output << ';' << endl;
idx++;
}
output << "];" << endl
<< "[M_, oo_, options_] = occbin.initialize(M_, oo_, options_);" << endl;
diff_output << "end" << endl;
diff_output.close();
}
void
@ -5283,10 +5343,10 @@ OccbinConstraintsStatement::writeJsonOutput(ostream &output) const
auto [name, bind, relax, error_bind, error_relax] = *it;
output << R"({ "name": ")" << name << R"(", "bind": ")";
bind->writeJsonOutput(output, {}, {});
dynamic_cast<ExprNode *>(bind)->writeJsonOutput(output, {}, {});
output << R"(", "relax": ")";
if (relax)
relax->writeJsonOutput(output, {}, {});
dynamic_cast<ExprNode *>(relax)->writeJsonOutput(output, {}, {});
output << R"(", "error_bind": ")";
if (error_bind)
error_bind->writeJsonOutput(output, {}, {});

View File

@ -1260,9 +1260,9 @@ private:
const SymbolTable &symbol_table;
public:
// The tuple is (name, bind, relax, error_bind, error_relax) (where relax and error_{bind,relax} can be nullptr)
const vector<tuple<string, expr_t, expr_t, expr_t, expr_t>> constraints;
const vector<tuple<string, BinaryOpNode *, BinaryOpNode *, expr_t, expr_t>> constraints;
OccbinConstraintsStatement(const SymbolTable &symbol_table_arg,
const vector<tuple<string, expr_t, expr_t, expr_t, expr_t>> constraints_arg);
const vector<tuple<string, BinaryOpNode *, BinaryOpNode *, expr_t, expr_t>> constraints_arg);
void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
void writeJsonOutput(ostream &output) const override;

View File

@ -208,8 +208,8 @@ class ParsingDriver;
%type <tuple<string,string,string,string>> prior_eq_opt options_eq_opt
%type <vector<pair<int, int>>> period_list
%type <vector<expr_t>> matched_moments_list value_list
%type <tuple<string, expr_t, expr_t, expr_t, expr_t>> occbin_constraints_regime
%type <vector<tuple<string, expr_t, expr_t, expr_t, expr_t>>> occbin_constraints_regimes_list
%type <tuple<string, BinaryOpNode *, BinaryOpNode *, expr_t, expr_t>> occbin_constraints_regime
%type <vector<tuple<string, BinaryOpNode *, BinaryOpNode *, expr_t, expr_t>>> occbin_constraints_regimes_list
%type <map<string, expr_t>> occbin_constraints_regime_options_list
%type <pair<string, expr_t>> occbin_constraints_regime_option
%%
@ -887,7 +887,21 @@ occbin_constraints_regimes_list : occbin_constraints_regime
;
occbin_constraints_regime : NAME QUOTED_STRING ';' occbin_constraints_regime_options_list
{ $$ = { $2, $4["bind"], $4["relax"], $4["error_bind"], $4["error_relax"] }; }
{
BinaryOpNode *bind = dynamic_cast<BinaryOpNode *>($4["bind"]);
if (bind && !(bind->op_code == BinaryOpcode::less
|| bind->op_code == BinaryOpcode::greater
|| bind->op_code == BinaryOpcode::lessEqual
|| bind->op_code == BinaryOpcode::greaterEqual))
driver.error("The 'bind' expression must be an inequality constraint");
BinaryOpNode *relax = dynamic_cast<BinaryOpNode *>($4["relax"]);
if (relax && !(relax->op_code == BinaryOpcode::less
|| relax->op_code == BinaryOpcode::greater
|| relax->op_code == BinaryOpcode::lessEqual
|| relax->op_code == BinaryOpcode::greaterEqual))
driver.error("The 'relax' expression must be an inequality constraint");
$$ = { $2, bind, relax, $4["error_bind"], $4["error_relax"] };
}
;
occbin_constraints_regime_options_list : occbin_constraints_regime_option

View File

@ -1001,6 +1001,9 @@ VariableNode::writeOutput(ostream &output, ExprNodeOutputType output_type,
output << lag;
output << RIGHT_ARRAY_SUBSCRIPT(output_type);
break;
case ExprNodeOutputType::occbinDifferenceFile:
output << "zdatalinear(:," << tsid + 1 << ")";
break;
default:
cerr << "VariableNode::writeOutput: should not reach this point" << endl;
exit(EXIT_FAILURE);
@ -2702,6 +2705,7 @@ UnaryOpNode::writeOutput(ostream &output, ExprNodeOutputType output_type,
switch (output_type)
{
case ExprNodeOutputType::matlabDynamicModel:
case ExprNodeOutputType::occbinDifferenceFile:
new_output_type = ExprNodeOutputType::matlabDynamicSteadyStateOperator;
break;
case ExprNodeOutputType::latexDynamicModel:

View File

@ -97,7 +97,8 @@ enum class ExprNodeOutputType
steadyStateFile, //!< Matlab code, in the generated steady state file
juliaSteadyStateFile, //!< Julia code, in the generated steady state file
matlabDseries, //!< Matlab code for dseries
epilogueFile //!< Matlab code, in the generated epilogue file
epilogueFile, //!< Matlab code, in the generated epilogue file
occbinDifferenceFile //!< MATLAB, in the generated occbin_difference file
};
inline bool
@ -109,7 +110,8 @@ isMatlabOutput(ExprNodeOutputType output_type)
|| output_type == ExprNodeOutputType::matlabDynamicSteadyStateOperator
|| output_type == ExprNodeOutputType::steadyStateFile
|| output_type == ExprNodeOutputType::matlabDseries
|| output_type == ExprNodeOutputType::epilogueFile;
|| output_type == ExprNodeOutputType::epilogueFile
|| output_type == ExprNodeOutputType::occbinDifferenceFile;
}
inline bool

View File

@ -3420,7 +3420,7 @@ ParsingDriver::begin_occbin_constraints()
}
void
ParsingDriver::end_occbin_constraints(const vector<tuple<string, expr_t, expr_t, expr_t, expr_t>> &constraints)
ParsingDriver::end_occbin_constraints(const vector<tuple<string, BinaryOpNode *, BinaryOpNode *, expr_t, expr_t>> &constraints)
{
// Perform a few checks
for (const auto &[name, bind, relax, error_bind, error_relax] : constraints)
@ -3428,6 +3428,14 @@ ParsingDriver::end_occbin_constraints(const vector<tuple<string, expr_t, expr_t,
check_symbol_is_parameter(name);
if (!bind)
error("The 'bind' expression is missing in constraint '" + name + "'");
if (bind->hasExogenous())
error("Exogenous variables are not allowed in the context of the 'bind' expression");
if (relax && relax->hasExogenous())
error("Exogenous variables are not allowed in the context of the 'relax' expression");
if (error_bind && error_bind->hasExogenous())
error("Exogenous variables are not allowed in the context of the 'error_bind' expression");
if (error_relax && error_relax->hasExogenous())
error("Exogenous variables are not allowed in the context of the 'error_relax' expression");
}
mod_file->addStatement(make_unique<OccbinConstraintsStatement>(mod_file->symbol_table, constraints));

View File

@ -887,7 +887,7 @@ public:
//! Start parsing an occbin_constraints block
void begin_occbin_constraints();
//! Add an occbin_constraints block
void end_occbin_constraints(const vector<tuple<string, expr_t, expr_t, expr_t, expr_t>> &constraints);
void end_occbin_constraints(const vector<tuple<string, BinaryOpNode *, BinaryOpNode *, expr_t, expr_t>> &constraints);
};
#endif // ! PARSING_DRIVER_HH