/* * Copyright © 2007-2021 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 . */ #include #include #include #include #include #include #include "ExprNode.hh" #include "DataTree.hh" #include "ModFile.hh" ExprNode::ExprNode(DataTree &datatree_arg, int idx_arg) : datatree{datatree_arg}, idx{idx_arg} { } expr_t ExprNode::getDerivative(int deriv_id) { if (!preparedForDerivation) prepareForDerivation(); // Return zero if derivative is necessarily null (using symbolic a priori) if (auto it = non_null_derivatives.find(deriv_id); it == non_null_derivatives.end()) return datatree.Zero; // If derivative is stored in cache, use the cached value, otherwise compute it (and cache it) if (auto it2 = derivatives.find(deriv_id); it2 != derivatives.end()) return it2->second; else { expr_t d = computeDerivative(deriv_id); derivatives[deriv_id] = d; return d; } } int ExprNode::precedence(ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms) const { // For a constant, a variable, or a unary op, the precedence is maximal return 100; } int ExprNode::precedenceJson(const temporary_terms_t &temporary_terms) const { // For a constant, a variable, or a unary op, the precedence is maximal return 100; } int ExprNode::cost(int cost, bool is_matlab) const { // For a terminal node, the cost is null return 0; } int ExprNode::cost(const vector> &blocks_temporary_terms, bool is_matlab) const { // For a terminal node, the cost is null return 0; } int ExprNode::cost(const map, temporary_terms_t> &temp_terms_map, bool is_matlab) const { // For a terminal node, the cost is null return 0; } bool ExprNode::checkIfTemporaryTermThenWrite(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs) const { if (auto it = temporary_terms.find(const_cast(this)); it == temporary_terms.end()) return false; auto it2 = temporary_terms_idxs.find(const_cast(this)); // It is the responsibility of the caller to ensure that all temporary terms have their index assert(it2 != temporary_terms_idxs.end()); output << "T" << LEFT_ARRAY_SUBSCRIPT(output_type) << it2->second + ARRAY_SUBSCRIPT_OFFSET(output_type) << RIGHT_ARRAY_SUBSCRIPT(output_type); return true; } pair ExprNode::getLagEquivalenceClass() const { int index = maxLead(); if (index == numeric_limits::min()) index = 0; // If no variable in the expression, the equivalence class has size 1 return { decreaseLeadsLags(index), index }; } void ExprNode::collectVariables(SymbolType type, set &result) const { set> symbs_lags; collectDynamicVariables(type, symbs_lags); transform(symbs_lags.begin(), symbs_lags.end(), inserter(result, result.begin()), [](auto x) { return x.first; }); } void ExprNode::collectEndogenous(set> &result) const { set> symb_ids_and_lags; collectDynamicVariables(SymbolType::endogenous, symb_ids_and_lags); for (const auto &[symb_id, lag] : symb_ids_and_lags) result.emplace(datatree.symbol_table.getTypeSpecificID(symb_id), lag); } void ExprNode::computeTemporaryTerms(const pair &derivOrder, map, temporary_terms_t> &temp_terms_map, map>> &reference_count, bool is_matlab) const { // Nothing to do for a terminal node } void ExprNode::computeBlockTemporaryTerms(int blk, int eq, vector> &blocks_temporary_terms, map> &reference_count) const { // Nothing to do for a terminal node } void ExprNode::writeOutput(ostream &output) const { writeOutput(output, ExprNodeOutputType::matlabOutsideModel, {}, {}); } void ExprNode::writeOutput(ostream &output, ExprNodeOutputType output_type) const { writeOutput(output, output_type, {}, {}); } void ExprNode::writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs) const { writeOutput(output, output_type, temporary_terms, temporary_terms_idxs, {}); } void ExprNode::compile(ostream &CompileCode, unsigned int &instruction_number, bool lhs_rhs, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, bool dynamic, bool steady_dynamic) const { compile(CompileCode, instruction_number, lhs_rhs, temporary_terms, temporary_terms_idxs, dynamic, steady_dynamic, {}); } void ExprNode::writeExternalFunctionOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, deriv_node_temp_terms_t &tef_terms) const { // Nothing to do } void ExprNode::writeJsonExternalFunctionOutput(vector &efout, const temporary_terms_t &temporary_terms, deriv_node_temp_terms_t &tef_terms, bool isdynamic) const { // Nothing to do } void ExprNode::compileExternalFunctionOutput(ostream &CompileCode, unsigned int &instruction_number, bool lhs_rhs, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, bool dynamic, bool steady_dynamic, deriv_node_temp_terms_t &tef_terms) const { // Nothing to do } VariableNode * ExprNode::createEndoLeadAuxiliaryVarForMyself(subst_table_t &subst_table, vector &neweqs) const { int n = maxEndoLead(); assert(n >= 2); if (auto it = subst_table.find(this); it != subst_table.end()) return const_cast(it->second); expr_t 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) { expr_t orig_expr = decreaseLeadsLags(lag); if (auto it = subst_table.find(orig_expr); it == subst_table.end()) { int symb_id = datatree.symbol_table.addEndoLeadAuxiliaryVar(orig_expr->idx, substexpr); neweqs.push_back(datatree.AddEqual(datatree.AddVariable(symb_id, 0), substexpr)); substexpr = datatree.AddVariable(symb_id, +1); assert(dynamic_cast(substexpr)); subst_table[orig_expr] = dynamic_cast(substexpr); } else substexpr = const_cast(it->second); lag--; } return dynamic_cast(substexpr); } VariableNode * ExprNode::createExoLeadAuxiliaryVarForMyself(subst_table_t &subst_table, vector &neweqs) const { int n = maxExoLead(); assert(n >= 1); if (auto it = subst_table.find(this); it != subst_table.end()) return const_cast(it->second); expr_t substexpr = decreaseLeadsLags(n); int lag = n-1; // 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) { expr_t orig_expr = decreaseLeadsLags(lag); if (auto it = subst_table.find(orig_expr); it == subst_table.end()) { int symb_id = datatree.symbol_table.addExoLeadAuxiliaryVar(orig_expr->idx, substexpr); neweqs.push_back(datatree.AddEqual(datatree.AddVariable(symb_id, 0), substexpr)); substexpr = datatree.AddVariable(symb_id, +1); assert(dynamic_cast(substexpr)); subst_table[orig_expr] = dynamic_cast(substexpr); } else substexpr = const_cast(it->second); lag--; } return dynamic_cast(substexpr); } bool ExprNode::isNumConstNodeEqualTo(double value) const { return false; } bool ExprNode::isVariableNodeEqualTo(SymbolType type_arg, int variable_id, int lag_arg) const { return false; } void ExprNode::getEndosAndMaxLags(map &model_endos_and_lags) const { } void ExprNode::fillErrorCorrectionRow(int eqn, const vector &nontarget_lhs, const vector &target_lhs, map, expr_t> &A0, map, expr_t> &A0star) const { vector> terms; decomposeAdditiveTerms(terms, 1); for (const auto &it : terms) { pair>> m; try { m = it.first->matchParamTimesLinearCombinationOfVariables(); for (auto &t : m.second) get<3>(t) *= it.second; // Update sign of constants } catch (MatchFailureException &e) { /* FIXME: we should not just skip them, but rather verify that they are autoregressive terms or residuals (probably by merging the two "fill" procedures) */ continue; } // Helper function auto one_step_orig = [this](int symb_id) { return datatree.symbol_table.isAuxiliaryVariable(symb_id) ? datatree.symbol_table.getOrigSymbIdForDiffAuxVar(symb_id) : symb_id; }; /* Verify that all variables belong to the error-correction term. FIXME: same remark as above about skipping terms. */ bool not_ec = false; for (const auto &t : m.second) { int vid = one_step_orig(get<0>(t)); not_ec = not_ec || (find(target_lhs.begin(), target_lhs.end(), vid) == target_lhs.end() && find(nontarget_lhs.begin(), nontarget_lhs.end(), vid) == nontarget_lhs.end()); } if (not_ec) continue; // Now fill the matrices for (auto [var_id, lag, param_id, constant] : m.second) { int orig_vid = one_step_orig(var_id); int orig_lag = datatree.symbol_table.isAuxiliaryVariable(var_id) ? -datatree.symbol_table.getOrigLeadLagForDiffAuxVar(var_id) : lag; if (find(target_lhs.begin(), target_lhs.end(), orig_vid) == target_lhs.end()) { // This an LHS variable, so fill A0 if (constant != 1) { cerr << "ERROR in trend component model: LHS variable should not appear with a multiplicative constant in error correction term" << endl; exit(EXIT_FAILURE); } if (param_id != -1) { cerr << "ERROR in trend component model: spurious parameter in error correction term" << endl; exit(EXIT_FAILURE); } int colidx = static_cast(distance(nontarget_lhs.begin(), find(nontarget_lhs.begin(), nontarget_lhs.end(), orig_vid))); if (A0.find({eqn, -orig_lag, colidx}) != A0.end()) { cerr << "ExprNode::fillErrorCorrection: Error filling A0 matrix: " << "lag/symb_id encountered more than once in equation" << endl; exit(EXIT_FAILURE); } A0[{eqn, -orig_lag, colidx}] = datatree.AddVariable(m.first); } else { // This is a target, so fill A0star int colidx = static_cast(distance(target_lhs.begin(), find(target_lhs.begin(), target_lhs.end(), orig_vid))); expr_t e = datatree.AddTimes(datatree.AddVariable(m.first), datatree.AddPossiblyNegativeConstant(-constant)); if (param_id != -1) e = datatree.AddTimes(e, datatree.AddVariable(param_id)); if (auto coor = tuple(eqn, -orig_lag, colidx); A0star.find(coor) == A0star.end()) A0star[coor] = e; else A0star[coor] = datatree.AddPlus(e, A0star[coor]); } } } } void ExprNode::matchMatchedMoment(vector &symb_ids, vector &lags, vector &powers) const { throw MatchFailureException{"Unsupported expression"}; } NumConstNode::NumConstNode(DataTree &datatree_arg, int idx_arg, int id_arg) : ExprNode{datatree_arg, idx_arg}, id{id_arg} { } int NumConstNode::countDiffs() const { return 0; } void NumConstNode::prepareForDerivation() { preparedForDerivation = true; // All derivatives are null, so non_null_derivatives is left empty } expr_t NumConstNode::computeDerivative(int deriv_id) { return datatree.Zero; } void NumConstNode::writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, const deriv_node_temp_terms_t &tef_terms) const { if (!checkIfTemporaryTermThenWrite(output, output_type, temporary_terms, temporary_terms_idxs)) output << datatree.num_constants.get(id); } void NumConstNode::writeJsonAST(ostream &output) const { output << R"({"node_type" : "NumConstNode", "value" : )"; output << std::stof(datatree.num_constants.get(id)) << "}"; } void NumConstNode::writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, const deriv_node_temp_terms_t &tef_terms, bool isdynamic) const { output << datatree.num_constants.get(id); } bool NumConstNode::containsExternalFunction() const { return false; } double NumConstNode::eval(const eval_context_t &eval_context) const noexcept(false) { return datatree.num_constants.getDouble(id); } void NumConstNode::compile(ostream &CompileCode, unsigned int &instruction_number, bool lhs_rhs, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, bool dynamic, bool steady_dynamic, const deriv_node_temp_terms_t &tef_terms) const { FLDC_ fldc(datatree.num_constants.getDouble(id)); fldc.write(CompileCode, instruction_number); } void NumConstNode::collectVARLHSVariable(set &result) const { cerr << "ERROR: you can only have variables or unary ops on LHS of VAR" << endl; exit(EXIT_FAILURE); } void NumConstNode::collectDynamicVariables(SymbolType type_arg, set> &result) const { } void NumConstNode::computeSubExprContainingVariable(int symb_id, int lag, set &contain_var) const { } BinaryOpNode * NumConstNode::normalizeEquationHelper(const set &contain_var, expr_t rhs) const { cerr << "NumConstNode::normalizeEquation: this should not happen" << endl; exit(EXIT_FAILURE); } expr_t NumConstNode::getChainRuleDerivative(int deriv_id, const map &recursive_variables) { return datatree.Zero; } expr_t NumConstNode::toStatic(DataTree &static_datatree) const { return static_datatree.AddNonNegativeConstant(datatree.num_constants.get(id)); } void NumConstNode::computeXrefs(EquationInfo &ei) const { } expr_t NumConstNode::clone(DataTree &datatree) const { return datatree.AddNonNegativeConstant(datatree.num_constants.get(id)); } int NumConstNode::maxEndoLead() const { return 0; } int NumConstNode::maxExoLead() const { return 0; } int NumConstNode::maxEndoLag() const { return 0; } int NumConstNode::maxExoLag() const { return 0; } int NumConstNode::maxLead() const { return numeric_limits::min(); } int NumConstNode::maxLag() const { return numeric_limits::min(); } int NumConstNode::maxLagWithDiffsExpanded() const { return numeric_limits::min(); } expr_t NumConstNode::undiff() const { return const_cast(this); } int NumConstNode::VarMinLag() const { return 1; } int NumConstNode::VarMaxLag(const set &lhs_lag_equiv) const { return 0; } expr_t NumConstNode::decreaseLeadsLags(int n) const { return const_cast(this); } expr_t NumConstNode::decreaseLeadsLagsPredeterminedVariables() const { return const_cast(this); } expr_t NumConstNode::substituteEndoLeadGreaterThanTwo(subst_table_t &subst_table, vector &neweqs, bool deterministic_model) const { return const_cast(this); } expr_t NumConstNode::substituteEndoLagGreaterThanTwo(subst_table_t &subst_table, vector &neweqs) const { return const_cast(this); } expr_t NumConstNode::substituteExoLead(subst_table_t &subst_table, vector &neweqs, bool deterministic_model) const { return const_cast(this); } expr_t NumConstNode::substituteExoLag(subst_table_t &subst_table, vector &neweqs) const { return const_cast(this); } expr_t NumConstNode::substituteExpectation(subst_table_t &subst_table, vector &neweqs, bool partial_information_model) const { return const_cast(this); } expr_t NumConstNode::substituteAdl() const { return const_cast(this); } expr_t NumConstNode::substituteModelLocalVariables() const { return const_cast(this); } expr_t NumConstNode::substituteVarExpectation(const map &subst_table) const { return const_cast(this); } void NumConstNode::findDiffNodes(lag_equivalence_table_t &nodes) const { } void NumConstNode::findUnaryOpNodesForAuxVarCreation(lag_equivalence_table_t &nodes) const { } int NumConstNode::findTargetVariable(int lhs_symb_id) const { return -1; } expr_t NumConstNode::substituteDiff(const lag_equivalence_table_t &nodes, subst_table_t &subst_table, vector &neweqs) const { return const_cast(this); } expr_t NumConstNode::substituteUnaryOpNodes(const lag_equivalence_table_t &nodes, subst_table_t &subst_table, vector &neweqs) const { return const_cast(this); } expr_t NumConstNode::substitutePacExpectation(const string &name, expr_t subexpr) { return const_cast(this); } expr_t NumConstNode::differentiateForwardVars(const vector &subset, subst_table_t &subst_table, vector &neweqs) const { return const_cast(this); } bool NumConstNode::isNumConstNodeEqualTo(double value) const { if (datatree.num_constants.getDouble(id) == value) return true; else return false; } bool NumConstNode::isVariableNodeEqualTo(SymbolType type_arg, int variable_id, int lag_arg) const { return false; } void NumConstNode::getEndosAndMaxLags(map &model_endos_and_lags) const { } bool NumConstNode::containsPacExpectation(const string &pac_model_name) const { return false; } expr_t NumConstNode::replaceTrendVar() const { return const_cast(this); } expr_t NumConstNode::detrend(int symb_id, bool log_trend, expr_t trend) const { return const_cast(this); } expr_t NumConstNode::removeTrendLeadLag(const map &trend_symbols_map) const { return const_cast(this); } bool NumConstNode::isInStaticForm() const { return true; } bool NumConstNode::isParamTimesEndogExpr() const { return false; } bool NumConstNode::isVarModelReferenced(const string &model_info_name) const { return false; } expr_t NumConstNode::substituteStaticAuxiliaryVariable() const { return const_cast(this); } expr_t NumConstNode::replaceVarsInEquation(map &table) const { return const_cast(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}, lag{lag_arg} { // It makes sense to allow a lead/lag on parameters: during steady state calibration, endogenous and parameters can be swapped assert(get_type() != SymbolType::externalFunction && (lag == 0 || (get_type() != SymbolType::modelLocalVariable && get_type() != SymbolType::modFileLocalVariable))); } void VariableNode::prepareForDerivation() { if (preparedForDerivation) return; preparedForDerivation = true; // Fill in non_null_derivatives switch (get_type()) { case SymbolType::endogenous: case SymbolType::exogenous: case SymbolType::exogenousDet: case SymbolType::parameter: case SymbolType::trend: case SymbolType::logTrend: // For a variable or a parameter, the only non-null derivative is with respect to itself non_null_derivatives.insert(datatree.getDerivID(symb_id, lag)); break; case SymbolType::modelLocalVariable: datatree.getLocalVariable(symb_id)->prepareForDerivation(); // Non null derivatives are those of the value of the local parameter non_null_derivatives = datatree.getLocalVariable(symb_id)->non_null_derivatives; break; case SymbolType::modFileLocalVariable: case SymbolType::statementDeclaredVariable: case SymbolType::unusedEndogenous: // Such a variable is never derived break; case SymbolType::externalFunction: case SymbolType::endogenousVAR: case SymbolType::epilogue: cerr << "VariableNode::prepareForDerivation: impossible case" << endl; exit(EXIT_FAILURE); case SymbolType::excludedVariable: cerr << "VariableNode::prepareForDerivation: impossible case: " << "You are trying to derive a variable that has been excluded via include_eqs/exclude_eqs: " << datatree.symbol_table.getName(symb_id) << endl; exit(EXIT_FAILURE); } } expr_t VariableNode::computeDerivative(int deriv_id) { switch (get_type()) { case SymbolType::endogenous: case SymbolType::exogenous: case SymbolType::exogenousDet: case SymbolType::parameter: case SymbolType::trend: case SymbolType::logTrend: if (deriv_id == datatree.getDerivID(symb_id, lag)) return datatree.One; else return datatree.Zero; case SymbolType::modelLocalVariable: return datatree.getLocalVariable(symb_id)->getDerivative(deriv_id); case SymbolType::modFileLocalVariable: cerr << "modFileLocalVariable is not derivable" << endl; exit(EXIT_FAILURE); case SymbolType::statementDeclaredVariable: cerr << "statementDeclaredVariable is not derivable" << endl; exit(EXIT_FAILURE); case SymbolType::unusedEndogenous: cerr << "unusedEndogenous is not derivable" << endl; exit(EXIT_FAILURE); case SymbolType::externalFunction: case SymbolType::endogenousVAR: case SymbolType::epilogue: case SymbolType::excludedVariable: cerr << "VariableNode::computeDerivative: Impossible case!" << endl; exit(EXIT_FAILURE); } // Suppress GCC warning exit(EXIT_FAILURE); } bool VariableNode::containsExternalFunction() const { if (get_type() == SymbolType::modelLocalVariable) return datatree.getLocalVariable(symb_id)->containsExternalFunction(); return false; } void VariableNode::writeJsonAST(ostream &output) const { output << R"({"node_type" : "VariableNode", )" << R"("name" : ")" << datatree.symbol_table.getName(symb_id) << R"(", "type" : ")"; switch (get_type()) { case SymbolType::endogenous: output << "endogenous"; break; case SymbolType::exogenous: output << "exogenous"; break; case SymbolType::exogenousDet: output << "exogenousDet"; break; case SymbolType::parameter: output << "parameter"; break; case SymbolType::modelLocalVariable: output << "modelLocalVariable"; break; case SymbolType::modFileLocalVariable: output << "modFileLocalVariable"; break; case SymbolType::externalFunction: output << "externalFunction"; break; case SymbolType::trend: output << "trend"; break; case SymbolType::statementDeclaredVariable: output << "statementDeclaredVariable"; break; case SymbolType::logTrend: output << "logTrend:"; break; case SymbolType::unusedEndogenous: output << "unusedEndogenous"; break; case SymbolType::endogenousVAR: output << "endogenousVAR"; break; case SymbolType::epilogue: output << "epilogue"; break; case SymbolType::excludedVariable: cerr << "VariableNode::computeDerivative: Impossible case!" << endl; exit(EXIT_FAILURE); } output << R"(", "lag" : )" << lag << "}"; } void VariableNode::writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, const deriv_node_temp_terms_t &tef_terms, bool isdynamic) const { if (temporary_terms.find(const_cast(this)) != temporary_terms.end()) { output << "T" << idx; return; } output << datatree.symbol_table.getName(symb_id); if (isdynamic && lag != 0) output << "(" << lag << ")"; } void VariableNode::writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, const deriv_node_temp_terms_t &tef_terms) const { auto type = get_type(); if (checkIfTemporaryTermThenWrite(output, output_type, temporary_terms, temporary_terms_idxs)) return; if (isLatexOutput(output_type)) { if (output_type == ExprNodeOutputType::latexDynamicSteadyStateOperator) output << R"(\bar)"; output << "{" << datatree.symbol_table.getTeXName(symb_id) << "}"; if (output_type == ExprNodeOutputType::latexDynamicModel && (type == SymbolType::endogenous || type == SymbolType::exogenous || type == SymbolType::exogenousDet || type == SymbolType::trend || type == SymbolType::logTrend)) { output << "_{t"; if (lag != 0) { if (lag > 0) output << "+"; output << lag; } output << "}"; } return; } int i; switch (type) { case SymbolType::parameter: if (int tsid = datatree.symbol_table.getTypeSpecificID(symb_id); output_type == ExprNodeOutputType::matlabOutsideModel) output << "M_.params" << "(" << tsid + 1 << ")"; else output << "params" << LEFT_ARRAY_SUBSCRIPT(output_type) << tsid + ARRAY_SUBSCRIPT_OFFSET(output_type) << RIGHT_ARRAY_SUBSCRIPT(output_type); break; case SymbolType::modelLocalVariable: if (output_type == ExprNodeOutputType::matlabDynamicSteadyStateOperator || output_type == ExprNodeOutputType::CDynamicSteadyStateOperator) { output << "("; datatree.getLocalVariable(symb_id)->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); output << ")"; } else /* We append underscores to avoid name clashes with "g1" or "oo_". But we probably never arrive here because MLV are temporary terms… */ output << datatree.symbol_table.getName(symb_id) << "__"; break; case SymbolType::modFileLocalVariable: output << datatree.symbol_table.getName(symb_id); break; case SymbolType::endogenous: switch (int tsid = datatree.symbol_table.getTypeSpecificID(symb_id); output_type) { case ExprNodeOutputType::juliaDynamicModel: case ExprNodeOutputType::matlabDynamicModel: case ExprNodeOutputType::CDynamicModel: i = datatree.getDynJacobianCol(datatree.getDerivID(symb_id, lag)) + ARRAY_SUBSCRIPT_OFFSET(output_type); output << "y" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << RIGHT_ARRAY_SUBSCRIPT(output_type); break; case ExprNodeOutputType::CStaticModel: case ExprNodeOutputType::juliaStaticModel: case ExprNodeOutputType::matlabStaticModel: i = tsid + ARRAY_SUBSCRIPT_OFFSET(output_type); output << "y" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << RIGHT_ARRAY_SUBSCRIPT(output_type); break; case ExprNodeOutputType::matlabOutsideModel: output << "oo_.steady_state(" << tsid + 1 << ")"; break; case ExprNodeOutputType::juliaDynamicSteadyStateOperator: case ExprNodeOutputType::matlabDynamicSteadyStateOperator: output << "steady_state" << LEFT_ARRAY_SUBSCRIPT(output_type) << tsid + 1 << RIGHT_ARRAY_SUBSCRIPT(output_type); break; case ExprNodeOutputType::CDynamicSteadyStateOperator: output << "steady_state[" << tsid << "]"; break; case ExprNodeOutputType::juliaSteadyStateFile: case ExprNodeOutputType::steadyStateFile: output << "ys_" << LEFT_ARRAY_SUBSCRIPT(output_type) << tsid + 1 << RIGHT_ARRAY_SUBSCRIPT(output_type); break; case ExprNodeOutputType::matlabDseries: output << "ds." << datatree.symbol_table.getName(symb_id); if (lag != 0) output << LEFT_ARRAY_SUBSCRIPT(output_type) << lag << RIGHT_ARRAY_SUBSCRIPT(output_type); break; case ExprNodeOutputType::epilogueFile: output << "ds." << datatree.symbol_table.getName(symb_id); output << LEFT_ARRAY_SUBSCRIPT(output_type) << "t"; if (lag != 0) output << lag; output << RIGHT_ARRAY_SUBSCRIPT(output_type); break; default: cerr << "VariableNode::writeOutput: should not reach this point" << endl; exit(EXIT_FAILURE); } break; case SymbolType::exogenous: i = datatree.symbol_table.getTypeSpecificID(symb_id) + ARRAY_SUBSCRIPT_OFFSET(output_type); switch (output_type) { case ExprNodeOutputType::juliaDynamicModel: case ExprNodeOutputType::matlabDynamicModel: if (lag > 0) output << "x" << LEFT_ARRAY_SUBSCRIPT(output_type) << "it_+" << lag << ", " << i << RIGHT_ARRAY_SUBSCRIPT(output_type); else if (lag < 0) output << "x" << LEFT_ARRAY_SUBSCRIPT(output_type) << "it_" << lag << ", " << i << RIGHT_ARRAY_SUBSCRIPT(output_type); else output << "x" << LEFT_ARRAY_SUBSCRIPT(output_type) << "it_, " << i << RIGHT_ARRAY_SUBSCRIPT(output_type); break; case ExprNodeOutputType::CDynamicModel: if (lag == 0) output << "x[it_+" << i << "*nb_row_x]"; else if (lag > 0) output << "x[it_+" << lag << "+" << i << "*nb_row_x]"; else output << "x[it_" << lag << "+" << i << "*nb_row_x]"; break; case ExprNodeOutputType::CStaticModel: case ExprNodeOutputType::juliaStaticModel: case ExprNodeOutputType::matlabStaticModel: output << "x" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << RIGHT_ARRAY_SUBSCRIPT(output_type); break; case ExprNodeOutputType::matlabOutsideModel: assert(lag == 0); output << "oo_.exo_steady_state(" << i << ")"; break; case ExprNodeOutputType::matlabDynamicSteadyStateOperator: output << "oo_.exo_steady_state(" << i << ")"; break; case ExprNodeOutputType::juliaSteadyStateFile: case ExprNodeOutputType::steadyStateFile: output << "exo_" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << RIGHT_ARRAY_SUBSCRIPT(output_type); break; case ExprNodeOutputType::matlabDseries: output << "ds." << datatree.symbol_table.getName(symb_id); if (lag != 0) output << LEFT_ARRAY_SUBSCRIPT(output_type) << lag << RIGHT_ARRAY_SUBSCRIPT(output_type); break; case ExprNodeOutputType::epilogueFile: output << "ds." << datatree.symbol_table.getName(symb_id); output << LEFT_ARRAY_SUBSCRIPT(output_type) << "t"; if (lag != 0) output << lag; output << RIGHT_ARRAY_SUBSCRIPT(output_type); break; default: cerr << "VariableNode::writeOutput: should not reach this point" << endl; exit(EXIT_FAILURE); } break; case SymbolType::exogenousDet: i = datatree.symbol_table.getTypeSpecificID(symb_id) + datatree.symbol_table.exo_nbr() + ARRAY_SUBSCRIPT_OFFSET(output_type); switch (output_type) { case ExprNodeOutputType::juliaDynamicModel: case ExprNodeOutputType::matlabDynamicModel: if (lag > 0) output << "x" << LEFT_ARRAY_SUBSCRIPT(output_type) << "it_+" << lag << ", " << i << RIGHT_ARRAY_SUBSCRIPT(output_type); else if (lag < 0) output << "x" << LEFT_ARRAY_SUBSCRIPT(output_type) << "it_" << lag << ", " << i << RIGHT_ARRAY_SUBSCRIPT(output_type); else output << "x" << LEFT_ARRAY_SUBSCRIPT(output_type) << "it_, " << i << RIGHT_ARRAY_SUBSCRIPT(output_type); break; case ExprNodeOutputType::CDynamicModel: if (lag == 0) output << "x[it_+" << i << "*nb_row_x]"; else if (lag > 0) output << "x[it_+" << lag << "+" << i << "*nb_row_x]"; else output << "x[it_" << lag << "+" << i << "*nb_row_x]"; break; case ExprNodeOutputType::CStaticModel: case ExprNodeOutputType::juliaStaticModel: case ExprNodeOutputType::matlabStaticModel: output << "x" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << RIGHT_ARRAY_SUBSCRIPT(output_type); break; case ExprNodeOutputType::matlabOutsideModel: assert(lag == 0); output << "oo_.exo_det_steady_state(" << datatree.symbol_table.getTypeSpecificID(symb_id) + 1 << ")"; break; case ExprNodeOutputType::matlabDynamicSteadyStateOperator: output << "oo_.exo_det_steady_state(" << datatree.symbol_table.getTypeSpecificID(symb_id) + 1 << ")"; break; case ExprNodeOutputType::juliaSteadyStateFile: case ExprNodeOutputType::steadyStateFile: output << "exo_" << LEFT_ARRAY_SUBSCRIPT(output_type) << i << RIGHT_ARRAY_SUBSCRIPT(output_type); break; case ExprNodeOutputType::matlabDseries: output << "ds." << datatree.symbol_table.getName(symb_id); if (lag != 0) output << LEFT_ARRAY_SUBSCRIPT(output_type) << lag << RIGHT_ARRAY_SUBSCRIPT(output_type); break; case ExprNodeOutputType::epilogueFile: output << "ds." << datatree.symbol_table.getName(symb_id); output << LEFT_ARRAY_SUBSCRIPT(output_type) << "t"; if (lag != 0) output << lag; output << RIGHT_ARRAY_SUBSCRIPT(output_type); break; default: cerr << "VariableNode::writeOutput: should not reach this point" << endl; exit(EXIT_FAILURE); } break; case SymbolType::epilogue: if (output_type == ExprNodeOutputType::epilogueFile) { output << "ds." << datatree.symbol_table.getName(symb_id); output << LEFT_ARRAY_SUBSCRIPT(output_type) << "t"; if (lag != 0) output << lag; output << RIGHT_ARRAY_SUBSCRIPT(output_type); } else if (output_type == ExprNodeOutputType::matlabDseries) // Only writing dseries for epilogue_static, hence no need to check lag output << "ds." << datatree.symbol_table.getName(symb_id); else { cerr << "VariableNode::writeOutput: Impossible case" << endl; exit(EXIT_FAILURE); } break; case SymbolType::unusedEndogenous: cerr << "ERROR: You cannot use an endogenous variable in an expression if that variable has not been used in the model block." << endl; exit(EXIT_FAILURE); case SymbolType::externalFunction: case SymbolType::trend: case SymbolType::logTrend: case SymbolType::statementDeclaredVariable: case SymbolType::endogenousVAR: case SymbolType::excludedVariable: cerr << "VariableNode::writeOutput: Impossible case" << endl; exit(EXIT_FAILURE); } } expr_t VariableNode::substituteStaticAuxiliaryVariable() const { if (get_type() == SymbolType::endogenous) try { return datatree.symbol_table.getAuxiliaryVarsExprNode(symb_id)->substituteStaticAuxiliaryVariable(); } catch (SymbolTable::SearchFailedException &e) { } return const_cast(this); } double VariableNode::eval(const eval_context_t &eval_context) const noexcept(false) { if (get_type() == SymbolType::modelLocalVariable) return datatree.getLocalVariable(symb_id)->eval(eval_context); auto it = eval_context.find(symb_id); if (it == eval_context.end()) throw EvalException(); return it->second; } void VariableNode::compile(ostream &CompileCode, unsigned int &instruction_number, bool lhs_rhs, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, bool dynamic, bool steady_dynamic, const deriv_node_temp_terms_t &tef_terms) const { auto type = get_type(); if (type == SymbolType::modelLocalVariable || type == SymbolType::modFileLocalVariable) datatree.getLocalVariable(symb_id)->compile(CompileCode, instruction_number, lhs_rhs, temporary_terms, temporary_terms_idxs, dynamic, steady_dynamic, tef_terms); else { int tsid = datatree.symbol_table.getTypeSpecificID(symb_id); if (type == SymbolType::exogenousDet) tsid += datatree.symbol_table.exo_nbr(); if (!lhs_rhs) { if (dynamic) { if (steady_dynamic) // steady state values in a dynamic model { FLDVS_ fldvs{static_cast(type), static_cast(tsid)}; fldvs.write(CompileCode, instruction_number); } else { if (type == SymbolType::parameter) { FLDV_ fldv{static_cast(type), static_cast(tsid)}; fldv.write(CompileCode, instruction_number); } else { FLDV_ fldv{static_cast(type), static_cast(tsid), lag}; fldv.write(CompileCode, instruction_number); } } } else { FLDSV_ fldsv{static_cast(type), static_cast(tsid)}; fldsv.write(CompileCode, instruction_number); } } else { if (dynamic) { if (steady_dynamic) // steady state values in a dynamic model { cerr << "Impossible case: steady_state in rhs of equation" << endl; exit(EXIT_FAILURE); } else { if (type == SymbolType::parameter) { FSTPV_ fstpv{static_cast(type), static_cast(tsid)}; fstpv.write(CompileCode, instruction_number); } else { FSTPV_ fstpv{static_cast(type), static_cast(tsid), lag}; fstpv.write(CompileCode, instruction_number); } } } else { FSTPSV_ fstpsv{static_cast(type), static_cast(tsid)}; fstpsv.write(CompileCode, instruction_number); } } } } void VariableNode::collectVARLHSVariable(set &result) const { if (get_type() == SymbolType::endogenous && lag == 0) result.insert(const_cast(this)); else { cerr << "ERROR: you can only have endogenous variables or unary ops on LHS of VAR" << endl; exit(EXIT_FAILURE); } } void VariableNode::collectDynamicVariables(SymbolType type_arg, set> &result) const { if (get_type() == type_arg) result.emplace(symb_id, lag); if (get_type() == SymbolType::modelLocalVariable) datatree.getLocalVariable(symb_id)->collectDynamicVariables(type_arg, result); } void VariableNode::computeSubExprContainingVariable(int symb_id_arg, int lag_arg, set &contain_var) const { if (symb_id == symb_id_arg && lag == lag_arg) contain_var.insert(const_cast(this)); if (get_type() == SymbolType::modelLocalVariable) datatree.getLocalVariable(symb_id)->computeSubExprContainingVariable(symb_id_arg, lag_arg, contain_var); } BinaryOpNode * VariableNode::normalizeEquationHelper(const set &contain_var, expr_t rhs) const { assert(contain_var.count(const_cast(this)) > 0); if (get_type() == SymbolType::modelLocalVariable) return datatree.getLocalVariable(symb_id)->normalizeEquationHelper(contain_var, rhs); // This the LHS variable: we have finished the normalization return datatree.AddEqual(const_cast(this), rhs); } expr_t VariableNode::getChainRuleDerivative(int deriv_id, const map &recursive_variables) { switch (get_type()) { case SymbolType::endogenous: case SymbolType::exogenous: case SymbolType::exogenousDet: case SymbolType::parameter: case SymbolType::trend: case SymbolType::logTrend: if (deriv_id == datatree.getDerivID(symb_id, lag)) return datatree.One; // If there is in the equation a recursive variable we could use a chaine rule derivation else if (auto it = recursive_variables.find(datatree.getDerivID(symb_id, lag)); it != recursive_variables.end()) return it->second->arg2->getChainRuleDerivative(deriv_id, recursive_variables); else return datatree.Zero; case SymbolType::modelLocalVariable: return datatree.getLocalVariable(symb_id)->getChainRuleDerivative(deriv_id, recursive_variables); case SymbolType::modFileLocalVariable: cerr << "modFileLocalVariable is not derivable" << endl; exit(EXIT_FAILURE); case SymbolType::statementDeclaredVariable: cerr << "statementDeclaredVariable is not derivable" << endl; exit(EXIT_FAILURE); case SymbolType::unusedEndogenous: cerr << "unusedEndogenous is not derivable" << endl; exit(EXIT_FAILURE); case SymbolType::externalFunction: case SymbolType::endogenousVAR: case SymbolType::epilogue: case SymbolType::excludedVariable: cerr << "VariableNode::getChainRuleDerivative: Impossible case" << endl; exit(EXIT_FAILURE); } // Suppress GCC warning exit(EXIT_FAILURE); } expr_t VariableNode::toStatic(DataTree &static_datatree) const { return static_datatree.AddVariable(symb_id); } void VariableNode::computeXrefs(EquationInfo &ei) const { switch (get_type()) { case SymbolType::endogenous: ei.endo.emplace(symb_id, lag); break; case SymbolType::exogenous: ei.exo.emplace(symb_id, lag); break; case SymbolType::exogenousDet: ei.exo_det.emplace(symb_id, lag); break; case SymbolType::parameter: ei.param.emplace(symb_id, 0); break; case SymbolType::modFileLocalVariable: datatree.getLocalVariable(symb_id)->computeXrefs(ei); break; case SymbolType::trend: case SymbolType::logTrend: case SymbolType::modelLocalVariable: case SymbolType::statementDeclaredVariable: case SymbolType::unusedEndogenous: case SymbolType::externalFunction: case SymbolType::endogenousVAR: case SymbolType::epilogue: case SymbolType::excludedVariable: break; } } SymbolType VariableNode::get_type() const { return datatree.symbol_table.getType(symb_id); } expr_t VariableNode::clone(DataTree &datatree) const { return datatree.AddVariable(symb_id, lag); } int VariableNode::maxEndoLead() const { switch (get_type()) { case SymbolType::endogenous: return max(lag, 0); case SymbolType::modelLocalVariable: return datatree.getLocalVariable(symb_id)->maxEndoLead(); default: return 0; } } int VariableNode::maxExoLead() const { switch (get_type()) { case SymbolType::exogenous: return max(lag, 0); case SymbolType::modelLocalVariable: return datatree.getLocalVariable(symb_id)->maxExoLead(); default: return 0; } } int VariableNode::maxEndoLag() const { switch (get_type()) { case SymbolType::endogenous: return max(-lag, 0); case SymbolType::modelLocalVariable: return datatree.getLocalVariable(symb_id)->maxEndoLag(); default: return 0; } } int VariableNode::maxExoLag() const { switch (get_type()) { case SymbolType::exogenous: return max(-lag, 0); case SymbolType::modelLocalVariable: return datatree.getLocalVariable(symb_id)->maxExoLag(); default: return 0; } } int VariableNode::maxLead() const { switch (get_type()) { case SymbolType::endogenous: case SymbolType::exogenous: case SymbolType::exogenousDet: return lag; case SymbolType::modelLocalVariable: return datatree.getLocalVariable(symb_id)->maxLead(); default: return 0; } } int VariableNode::VarMinLag() const { switch (get_type()) { case SymbolType::endogenous: return -lag; case SymbolType::exogenous: if (lag > 0) return -lag; else return 1; // Can have contemporaneus exog in VAR case SymbolType::modelLocalVariable: return datatree.getLocalVariable(symb_id)->VarMinLag(); default: return 1; } } int VariableNode::maxLag() const { switch (get_type()) { case SymbolType::endogenous: case SymbolType::exogenous: case SymbolType::exogenousDet: return -lag; case SymbolType::modelLocalVariable: return datatree.getLocalVariable(symb_id)->maxLag(); default: return 0; } } int VariableNode::maxLagWithDiffsExpanded() const { switch (get_type()) { case SymbolType::endogenous: case SymbolType::exogenous: case SymbolType::exogenousDet: case SymbolType::epilogue: return -lag; case SymbolType::modelLocalVariable: return datatree.getLocalVariable(symb_id)->maxLagWithDiffsExpanded(); default: return 0; } } expr_t VariableNode::undiff() const { return const_cast(this); } int VariableNode::VarMaxLag(const set &lhs_lag_equiv) const { auto [lag_equiv_repr, index] = getLagEquivalenceClass(); if (lhs_lag_equiv.find(lag_equiv_repr) == lhs_lag_equiv.end()) return 0; return maxLag(); } expr_t VariableNode::substituteAdl() const { /* Do not recurse into model-local variables definition, rather do it at the DynamicModel method level (see the comment there) */ return const_cast(this); } expr_t VariableNode::substituteModelLocalVariables() const { if (get_type() == SymbolType::modelLocalVariable) return datatree.getLocalVariable(symb_id); return const_cast(this); } expr_t VariableNode::substituteVarExpectation(const map &subst_table) const { if (get_type() == SymbolType::modelLocalVariable) return datatree.getLocalVariable(symb_id)->substituteVarExpectation(subst_table); return const_cast(this); } void VariableNode::findDiffNodes(lag_equivalence_table_t &nodes) const { if (get_type() == SymbolType::modelLocalVariable) datatree.getLocalVariable(symb_id)->findDiffNodes(nodes); } void VariableNode::findUnaryOpNodesForAuxVarCreation(lag_equivalence_table_t &nodes) const { if (get_type() == SymbolType::modelLocalVariable) datatree.getLocalVariable(symb_id)->findUnaryOpNodesForAuxVarCreation(nodes); } int VariableNode::findTargetVariable(int lhs_symb_id) const { if (get_type() == SymbolType::modelLocalVariable) return datatree.getLocalVariable(symb_id)->findTargetVariable(lhs_symb_id); return -1; } expr_t VariableNode::substituteDiff(const lag_equivalence_table_t &nodes, subst_table_t &subst_table, vector &neweqs) const { if (get_type() == SymbolType::modelLocalVariable) return datatree.getLocalVariable(symb_id)->substituteDiff(nodes, subst_table, neweqs); return const_cast(this); } expr_t VariableNode::substituteUnaryOpNodes(const lag_equivalence_table_t &nodes, subst_table_t &subst_table, vector &neweqs) const { if (get_type() == SymbolType::modelLocalVariable) return datatree.getLocalVariable(symb_id)->substituteUnaryOpNodes(nodes, subst_table, neweqs); return const_cast(this); } expr_t VariableNode::substitutePacExpectation(const string &name, expr_t subexpr) { if (get_type() == SymbolType::modelLocalVariable) return datatree.getLocalVariable(symb_id)->substitutePacExpectation(name, subexpr); return const_cast(this); } expr_t VariableNode::decreaseLeadsLags(int n) const { switch (get_type()) { case SymbolType::endogenous: case SymbolType::exogenous: case SymbolType::exogenousDet: case SymbolType::trend: case SymbolType::logTrend: return datatree.AddVariable(symb_id, lag-n); case SymbolType::modelLocalVariable: return datatree.getLocalVariable(symb_id)->decreaseLeadsLags(n); default: return const_cast(this); } } expr_t VariableNode::decreaseLeadsLagsPredeterminedVariables() const { /* Do not recurse into model-local variables definitions, since MLVs are already handled by DynamicModel::transformPredeterminedVariables(). This is also necessary because of #65. */ if (datatree.symbol_table.isPredetermined(symb_id)) return decreaseLeadsLags(1); else return const_cast(this); } expr_t VariableNode::substituteEndoLeadGreaterThanTwo(subst_table_t &subst_table, vector &neweqs, bool deterministic_model) const { switch (get_type()) { case SymbolType::endogenous: if (lag <= 1) return const_cast(this); else return createEndoLeadAuxiliaryVarForMyself(subst_table, neweqs); case SymbolType::modelLocalVariable: if (expr_t value = datatree.getLocalVariable(symb_id); value->maxEndoLead() <= 1) return const_cast(this); else return value->substituteEndoLeadGreaterThanTwo(subst_table, neweqs, deterministic_model); default: return const_cast(this); } } expr_t VariableNode::substituteEndoLagGreaterThanTwo(subst_table_t &subst_table, vector &neweqs) const { VariableNode *substexpr; int cur_lag; switch (get_type()) { case SymbolType::endogenous: if (lag >= -1) return const_cast(this); if (auto it = subst_table.find(this); it != subst_table.end()) return const_cast(it->second); substexpr = datatree.AddVariable(symb_id, -1); cur_lag = -2; // Each iteration tries to create an auxvar such that auxvar(-1)=curvar(cur_lag) // At the beginning (resp. end) of each iteration, substexpr is an expression (possibly an auxvar) equivalent to curvar(cur_lag+1) (resp. curvar(cur_lag)) while (cur_lag >= lag) { VariableNode *orig_expr = datatree.AddVariable(symb_id, cur_lag); if (auto it = subst_table.find(orig_expr); it == subst_table.end()) { int aux_symb_id = datatree.symbol_table.addEndoLagAuxiliaryVar(symb_id, cur_lag+1, substexpr); neweqs.push_back(datatree.AddEqual(datatree.AddVariable(aux_symb_id, 0), substexpr)); substexpr = datatree.AddVariable(aux_symb_id, -1); subst_table[orig_expr] = substexpr; } else substexpr = const_cast(it->second); cur_lag--; } return substexpr; case SymbolType::modelLocalVariable: if (expr_t value = datatree.getLocalVariable(symb_id); value->maxEndoLag() <= 1) return const_cast(this); else return value->substituteEndoLagGreaterThanTwo(subst_table, neweqs); default: return const_cast(this); } } expr_t VariableNode::substituteExoLead(subst_table_t &subst_table, vector &neweqs, bool deterministic_model) const { switch (get_type()) { case SymbolType::exogenous: if (lag <= 0) return const_cast(this); else return createExoLeadAuxiliaryVarForMyself(subst_table, neweqs); case SymbolType::modelLocalVariable: if (expr_t value = datatree.getLocalVariable(symb_id); value->maxExoLead() == 0) return const_cast(this); else return value->substituteExoLead(subst_table, neweqs, deterministic_model); default: return const_cast(this); } } expr_t VariableNode::substituteExoLag(subst_table_t &subst_table, vector &neweqs) const { VariableNode *substexpr; int cur_lag; switch (get_type()) { case SymbolType::exogenous: if (lag >= 0) return const_cast(this); if (auto it = subst_table.find(this); it != subst_table.end()) return const_cast(it->second); substexpr = datatree.AddVariable(symb_id, 0); cur_lag = -1; // Each iteration tries to create an auxvar such that auxvar(-1)=curvar(cur_lag) // At the beginning (resp. end) of each iteration, substexpr is an expression (possibly an auxvar) equivalent to curvar(cur_lag+1) (resp. curvar(cur_lag)) while (cur_lag >= lag) { VariableNode *orig_expr = datatree.AddVariable(symb_id, cur_lag); if (auto it = subst_table.find(orig_expr); it == subst_table.end()) { int aux_symb_id = datatree.symbol_table.addExoLagAuxiliaryVar(symb_id, cur_lag+1, substexpr); neweqs.push_back(datatree.AddEqual(datatree.AddVariable(aux_symb_id, 0), substexpr)); substexpr = datatree.AddVariable(aux_symb_id, -1); subst_table[orig_expr] = substexpr; } else substexpr = const_cast(it->second); cur_lag--; } return substexpr; case SymbolType::modelLocalVariable: if (expr_t value = datatree.getLocalVariable(symb_id); value->maxExoLag() == 0) return const_cast(this); else return value->substituteExoLag(subst_table, neweqs); default: return const_cast(this); } } expr_t VariableNode::substituteExpectation(subst_table_t &subst_table, vector &neweqs, bool partial_information_model) const { if (get_type() == SymbolType::modelLocalVariable) return datatree.getLocalVariable(symb_id)->substituteExpectation(subst_table, neweqs, partial_information_model); return const_cast(this); } expr_t VariableNode::differentiateForwardVars(const vector &subset, subst_table_t &subst_table, vector &neweqs) const { switch (get_type()) { case SymbolType::endogenous: assert(lag <= 1); if (lag <= 0 || (subset.size() > 0 && find(subset.begin(), subset.end(), datatree.symbol_table.getName(symb_id)) == subset.end())) return const_cast(this); else { VariableNode *diffvar; if (auto it = subst_table.find(this); it != subst_table.end()) diffvar = const_cast(it->second); else { int aux_symb_id = datatree.symbol_table.addDiffForwardAuxiliaryVar(symb_id, datatree.AddMinus(datatree.AddVariable(symb_id, 0), datatree.AddVariable(symb_id, -1))); neweqs.push_back(datatree.AddEqual(datatree.AddVariable(aux_symb_id, 0), datatree.AddMinus(datatree.AddVariable(symb_id, 0), datatree.AddVariable(symb_id, -1)))); diffvar = datatree.AddVariable(aux_symb_id, 1); subst_table[this] = diffvar; } return datatree.AddPlus(datatree.AddVariable(symb_id, 0), diffvar); } case SymbolType::modelLocalVariable: if (expr_t value = datatree.getLocalVariable(symb_id); value->maxEndoLead() <= 0) return const_cast(this); else return value->differentiateForwardVars(subset, subst_table, neweqs); default: return const_cast(this); } } bool VariableNode::isNumConstNodeEqualTo(double value) const { return false; } bool VariableNode::isVariableNodeEqualTo(SymbolType type_arg, int variable_id, int lag_arg) const { if (get_type() == type_arg && datatree.symbol_table.getTypeSpecificID(symb_id) == variable_id && lag == lag_arg) return true; else return false; } bool VariableNode::containsPacExpectation(const string &pac_model_name) const { if (get_type() == SymbolType::modelLocalVariable) return datatree.getLocalVariable(symb_id)->containsPacExpectation(pac_model_name); return false; } expr_t VariableNode::replaceTrendVar() const { if (get_type() == SymbolType::modelLocalVariable) return datatree.getLocalVariable(symb_id)->replaceTrendVar(); if (get_type() == SymbolType::trend) return datatree.One; else if (get_type() == SymbolType::logTrend) return datatree.Zero; else return const_cast(this); } expr_t VariableNode::detrend(int symb_id, bool log_trend, expr_t trend) const { if (get_type() == SymbolType::modelLocalVariable) return datatree.getLocalVariable(symb_id)->detrend(symb_id, log_trend, trend); if (this->symb_id != symb_id) return const_cast(this); if (log_trend) { if (lag == 0) return datatree.AddPlus(const_cast(this), trend); else return datatree.AddPlus(const_cast(this), trend->decreaseLeadsLags(-lag)); } else { if (lag == 0) return datatree.AddTimes(const_cast(this), trend); else return datatree.AddTimes(const_cast(this), trend->decreaseLeadsLags(-lag)); } } int VariableNode::countDiffs() const { if (get_type() == SymbolType::modelLocalVariable) return datatree.getLocalVariable(symb_id)->countDiffs(); return 0; } expr_t VariableNode::removeTrendLeadLag(const map &trend_symbols_map) const { if (get_type() == SymbolType::modelLocalVariable) return datatree.getLocalVariable(symb_id)->removeTrendLeadLag(trend_symbols_map); if ((get_type() != SymbolType::trend && get_type() != SymbolType::logTrend) || lag == 0) return const_cast(this); auto it = trend_symbols_map.find(symb_id); expr_t noTrendLeadLagNode = datatree.AddVariable(it->first); bool log_trend = get_type() == SymbolType::logTrend; expr_t trend = it->second; if (lag > 0) { expr_t growthFactorSequence = trend->decreaseLeadsLags(-1); if (log_trend) { for (int i = 1; i < lag; i++) growthFactorSequence = datatree.AddPlus(growthFactorSequence, trend->decreaseLeadsLags(-1*(i+1))); return datatree.AddPlus(noTrendLeadLagNode, growthFactorSequence); } else { for (int i = 1; i < lag; i++) growthFactorSequence = datatree.AddTimes(growthFactorSequence, trend->decreaseLeadsLags(-1*(i+1))); return datatree.AddTimes(noTrendLeadLagNode, growthFactorSequence); } } else //get_lag < 0 { expr_t growthFactorSequence = trend; if (log_trend) { for (int i = 1; i < abs(lag); i++) growthFactorSequence = datatree.AddPlus(growthFactorSequence, trend->decreaseLeadsLags(i)); return datatree.AddMinus(noTrendLeadLagNode, growthFactorSequence); } else { for (int i = 1; i < abs(lag); i++) growthFactorSequence = datatree.AddTimes(growthFactorSequence, trend->decreaseLeadsLags(i)); return datatree.AddDivide(noTrendLeadLagNode, growthFactorSequence); } } } bool VariableNode::isInStaticForm() const { if (get_type() == SymbolType::modelLocalVariable) return datatree.getLocalVariable(symb_id)->isInStaticForm(); return lag == 0; } bool VariableNode::isParamTimesEndogExpr() const { if (get_type() == SymbolType::modelLocalVariable) return datatree.getLocalVariable(symb_id)->isParamTimesEndogExpr(); return false; } bool VariableNode::isVarModelReferenced(const string &model_info_name) const { if (get_type() == SymbolType::modelLocalVariable) return datatree.getLocalVariable(symb_id)->isVarModelReferenced(model_info_name); return false; } void VariableNode::getEndosAndMaxLags(map &model_endos_and_lags) const { if (get_type() == SymbolType::modelLocalVariable) return datatree.getLocalVariable(symb_id)->getEndosAndMaxLags(model_endos_and_lags); if (get_type() == SymbolType::endogenous) if (string varname = datatree.symbol_table.getName(symb_id); model_endos_and_lags.find(varname) == model_endos_and_lags.end()) model_endos_and_lags[varname] = min(model_endos_and_lags[varname], lag); else model_endos_and_lags[varname] = lag; } expr_t VariableNode::replaceVarsInEquation(map &table) const { /* Do not recurse into model-local variables definitions, since MLVs are already handled by ModelTree::simplifyEquations(). This is also necessary because of #65. */ for (auto &it : table) if (it.first->symb_id == symb_id) return it.second; return const_cast(this); } void VariableNode::matchMatchedMoment(vector &symb_ids, vector &lags, vector &powers) const { /* Used for simple expression outside model block, so no need to special-case model local variables */ if (get_type() != SymbolType::endogenous) throw MatchFailureException{"Variable " + datatree.symbol_table.getName(symb_id) + " is not an endogenous"}; symb_ids.push_back(symb_id); lags.push_back(lag); powers.push_back(1); } 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 adl_lags_arg) : ExprNode{datatree_arg, idx_arg}, arg{arg_arg}, expectation_information_set{expectation_information_set_arg}, param1_symb_id{param1_symb_id_arg}, param2_symb_id{param2_symb_id_arg}, op_code{op_code_arg}, adl_param_name{move(adl_param_name_arg)}, adl_lags{move(adl_lags_arg)} { } void UnaryOpNode::prepareForDerivation() { if (preparedForDerivation) return; preparedForDerivation = true; arg->prepareForDerivation(); // Non-null derivatives are those of the argument (except for STEADY_STATE) non_null_derivatives = arg->non_null_derivatives; if (op_code == UnaryOpcode::steadyState || op_code == UnaryOpcode::steadyStateParamDeriv || op_code == UnaryOpcode::steadyStateParam2ndDeriv) datatree.addAllParamDerivId(non_null_derivatives); } expr_t UnaryOpNode::composeDerivatives(expr_t darg, int deriv_id) { expr_t t11, t12, t13, t14; switch (op_code) { case UnaryOpcode::uminus: return datatree.AddUMinus(darg); case UnaryOpcode::exp: return datatree.AddTimes(darg, this); case UnaryOpcode::log: return datatree.AddDivide(darg, arg); case UnaryOpcode::log10: t11 = datatree.AddExp(datatree.One); t12 = datatree.AddLog10(t11); t13 = datatree.AddDivide(darg, arg); return datatree.AddTimes(t12, t13); case UnaryOpcode::cos: t11 = datatree.AddSin(arg); t12 = datatree.AddUMinus(t11); return datatree.AddTimes(darg, t12); case UnaryOpcode::sin: t11 = datatree.AddCos(arg); return datatree.AddTimes(darg, t11); case UnaryOpcode::tan: t11 = datatree.AddTimes(this, this); t12 = datatree.AddPlus(t11, datatree.One); return datatree.AddTimes(darg, t12); case UnaryOpcode::acos: t11 = datatree.AddSin(this); t12 = datatree.AddDivide(darg, t11); return datatree.AddUMinus(t12); case UnaryOpcode::asin: t11 = datatree.AddCos(this); return datatree.AddDivide(darg, t11); case UnaryOpcode::atan: t11 = datatree.AddTimes(arg, arg); t12 = datatree.AddPlus(datatree.One, t11); return datatree.AddDivide(darg, t12); case UnaryOpcode::cosh: t11 = datatree.AddSinh(arg); return datatree.AddTimes(darg, t11); case UnaryOpcode::sinh: t11 = datatree.AddCosh(arg); return datatree.AddTimes(darg, t11); case UnaryOpcode::tanh: t11 = datatree.AddTimes(this, this); t12 = datatree.AddMinus(datatree.One, t11); return datatree.AddTimes(darg, t12); case UnaryOpcode::acosh: t11 = datatree.AddSinh(this); return datatree.AddDivide(darg, t11); case UnaryOpcode::asinh: t11 = datatree.AddCosh(this); return datatree.AddDivide(darg, t11); case UnaryOpcode::atanh: t11 = datatree.AddTimes(arg, arg); t12 = datatree.AddMinus(datatree.One, t11); return datatree.AddTimes(darg, t12); case UnaryOpcode::sqrt: t11 = datatree.AddPlus(this, this); return datatree.AddDivide(darg, t11); case UnaryOpcode::cbrt: t11 = datatree.AddPower(arg, datatree.AddDivide(datatree.Two, datatree.Three)); t12 = datatree.AddTimes(datatree.Three, t11); return datatree.AddDivide(darg, t12); case UnaryOpcode::abs: t11 = datatree.AddSign(arg); return datatree.AddTimes(t11, darg); case UnaryOpcode::sign: return datatree.Zero; case UnaryOpcode::steadyState: if (datatree.isDynamic()) { if (datatree.getTypeByDerivID(deriv_id) == SymbolType::parameter) { auto varg = dynamic_cast(arg); if (!varg) { cerr << "UnaryOpNode::composeDerivatives: STEADY_STATE() should only be used on " << "standalone variables (like STEADY_STATE(y)) to be derivable w.r.t. parameters" << endl; exit(EXIT_FAILURE); } if (datatree.symbol_table.getType(varg->symb_id) == SymbolType::endogenous) return datatree.AddSteadyStateParamDeriv(arg, datatree.getSymbIDByDerivID(deriv_id)); else return datatree.Zero; } else return datatree.Zero; } else return darg; case UnaryOpcode::steadyStateParamDeriv: assert(datatree.isDynamic()); if (datatree.getTypeByDerivID(deriv_id) == SymbolType::parameter) { auto varg = dynamic_cast(arg); assert(varg); assert(datatree.symbol_table.getType(varg->symb_id) == SymbolType::endogenous); return datatree.AddSteadyStateParam2ndDeriv(arg, param1_symb_id, datatree.getSymbIDByDerivID(deriv_id)); } else return datatree.Zero; case UnaryOpcode::steadyStateParam2ndDeriv: assert(datatree.isDynamic()); if (datatree.getTypeByDerivID(deriv_id) == SymbolType::parameter) { cerr << "3rd derivative of STEADY_STATE node w.r.t. three parameters not implemented" << endl; exit(EXIT_FAILURE); } else return datatree.Zero; case UnaryOpcode::expectation: cerr << "UnaryOpNode::composeDerivatives: not implemented on UnaryOpcode::expectation" << endl; exit(EXIT_FAILURE); case UnaryOpcode::erf: // x^2 t11 = datatree.AddPower(arg, datatree.Two); // exp(x^2) t12 = datatree.AddExp(t11); // sqrt(pi) t11 = datatree.AddSqrt(datatree.Pi); // sqrt(pi)*exp(x^2) t13 = datatree.AddTimes(t11, t12); // 2/(sqrt(pi)*exp(x^2)); t14 = datatree.AddDivide(datatree.Two, t13); // (2/(sqrt(pi)*exp(x^2)))*dx; return datatree.AddTimes(t14, darg); case UnaryOpcode::diff: cerr << "UnaryOpNode::composeDerivatives: not implemented on UnaryOpcode::diff" << endl; exit(EXIT_FAILURE); case UnaryOpcode::adl: cerr << "UnaryOpNode::composeDerivatives: not implemented on UnaryOpcode::adl" << endl; exit(EXIT_FAILURE); } // Suppress GCC warning exit(EXIT_FAILURE); } expr_t UnaryOpNode::computeDerivative(int deriv_id) { expr_t darg = arg->getDerivative(deriv_id); return composeDerivatives(darg, deriv_id); } int UnaryOpNode::cost(const map, temporary_terms_t> &temp_terms_map, bool is_matlab) const { // For a temporary term, the cost is null for (const auto &it : temp_terms_map) if (it.second.find(const_cast(this)) != it.second.end()) return 0; return cost(arg->cost(temp_terms_map, is_matlab), is_matlab); } int UnaryOpNode::cost(const vector> &blocks_temporary_terms, bool is_matlab) const { // For a temporary term, the cost is null for (const auto &blk_tt : blocks_temporary_terms) for (const auto &eq_tt : blk_tt) if (eq_tt.find(const_cast(this)) != eq_tt.end()) return 0; return cost(arg->cost(blocks_temporary_terms, is_matlab), is_matlab); } int UnaryOpNode::cost(int cost, bool is_matlab) const { if (op_code == UnaryOpcode::uminus && dynamic_cast(arg)) return 0; // Cost is zero for a negative constant, as for a positive one if (is_matlab) // Cost for Matlab files switch (op_code) { case UnaryOpcode::uminus: case UnaryOpcode::sign: return cost + 70; case UnaryOpcode::exp: return cost + 160; case UnaryOpcode::log: return cost + 300; case UnaryOpcode::log10: case UnaryOpcode::erf: return cost + 16000; case UnaryOpcode::cos: case UnaryOpcode::sin: case UnaryOpcode::cosh: return cost + 210; case UnaryOpcode::tan: return cost + 230; case UnaryOpcode::acos: return cost + 300; case UnaryOpcode::asin: return cost + 310; case UnaryOpcode::atan: return cost + 140; case UnaryOpcode::sinh: return cost + 240; case UnaryOpcode::tanh: return cost + 190; case UnaryOpcode::acosh: return cost + 770; case UnaryOpcode::asinh: return cost + 460; case UnaryOpcode::atanh: return cost + 350; case UnaryOpcode::sqrt: case UnaryOpcode::cbrt: case UnaryOpcode::abs: return cost + 570; case UnaryOpcode::steadyState: case UnaryOpcode::steadyStateParamDeriv: case UnaryOpcode::steadyStateParam2ndDeriv: case UnaryOpcode::expectation: return cost; case UnaryOpcode::diff: cerr << "UnaryOpNode::cost: not implemented on UnaryOpcode::diff" << endl; exit(EXIT_FAILURE); case UnaryOpcode::adl: cerr << "UnaryOpNode::cost: not implemented on UnaryOpcode::adl" << endl; exit(EXIT_FAILURE); } else // Cost for C files switch (op_code) { case UnaryOpcode::uminus: case UnaryOpcode::sign: return cost + 3; case UnaryOpcode::exp: case UnaryOpcode::acosh: return cost + 210; case UnaryOpcode::log: return cost + 137; case UnaryOpcode::log10: return cost + 139; case UnaryOpcode::cos: case UnaryOpcode::sin: return cost + 160; case UnaryOpcode::tan: return cost + 170; case UnaryOpcode::acos: case UnaryOpcode::atan: return cost + 190; case UnaryOpcode::asin: return cost + 180; case UnaryOpcode::cosh: case UnaryOpcode::sinh: case UnaryOpcode::tanh: case UnaryOpcode::erf: return cost + 240; case UnaryOpcode::asinh: return cost + 220; case UnaryOpcode::atanh: return cost + 150; case UnaryOpcode::sqrt: case UnaryOpcode::cbrt: case UnaryOpcode::abs: return cost + 90; case UnaryOpcode::steadyState: case UnaryOpcode::steadyStateParamDeriv: case UnaryOpcode::steadyStateParam2ndDeriv: case UnaryOpcode::expectation: return cost; case UnaryOpcode::diff: cerr << "UnaryOpNode::cost: not implemented on UnaryOpcode::diff" << endl; exit(EXIT_FAILURE); case UnaryOpcode::adl: cerr << "UnaryOpNode::cost: not implemented on UnaryOpcode::adl" << endl; exit(EXIT_FAILURE); } exit(EXIT_FAILURE); } void UnaryOpNode::computeTemporaryTerms(const pair &derivOrder, map, temporary_terms_t> &temp_terms_map, map>> &reference_count, bool is_matlab) const { expr_t this2 = const_cast(this); if (auto it = reference_count.find(this2); it == reference_count.end()) { reference_count[this2] = { 1, derivOrder }; arg->computeTemporaryTerms(derivOrder, temp_terms_map, reference_count, is_matlab); } else { auto &[nref, min_order] = it->second; nref++; if (nref * cost(temp_terms_map, is_matlab) > min_cost(is_matlab)) temp_terms_map[min_order].insert(this2); } } void UnaryOpNode::computeBlockTemporaryTerms(int blk, int eq, vector> &blocks_temporary_terms, map> &reference_count) const { expr_t this2 = const_cast(this); if (auto it = reference_count.find(this2); it == reference_count.end()) { reference_count[this2] = { 1, blk, eq }; arg->computeBlockTemporaryTerms(blk, eq, blocks_temporary_terms, reference_count); } else { auto &[nref, first_blk, first_eq] = it->second; nref++; if (nref * cost(blocks_temporary_terms, false) > min_cost_c) blocks_temporary_terms[first_blk][first_eq].insert(this2); } } bool UnaryOpNode::containsExternalFunction() const { return arg->containsExternalFunction(); } void UnaryOpNode::writeJsonAST(ostream &output) const { output << R"({"node_type" : "UnaryOpNode", "op" : ")"; switch (op_code) { case UnaryOpcode::uminus: output << "uminus"; break; case UnaryOpcode::exp: output << "exp"; break; case UnaryOpcode::log: output << "log"; break; case UnaryOpcode::log10: output << "log10"; break; case UnaryOpcode::cos: output << "cos"; break; case UnaryOpcode::sin: output << "sin"; break; case UnaryOpcode::tan: output << "tan"; break; case UnaryOpcode::acos: output << "acos"; break; case UnaryOpcode::asin: output << "asin"; break; case UnaryOpcode::atan: output << "atan"; break; case UnaryOpcode::cosh: output << "cosh"; break; case UnaryOpcode::sinh: output << "sinh"; break; case UnaryOpcode::tanh: output << "tanh"; break; case UnaryOpcode::acosh: output << "acosh"; break; case UnaryOpcode::asinh: output << "asinh"; break; case UnaryOpcode::atanh: output << "atanh"; break; case UnaryOpcode::sqrt: output << "sqrt"; break; case UnaryOpcode::cbrt: output << "cbrt"; break; case UnaryOpcode::abs: output << "abs"; break; case UnaryOpcode::sign: output << "sign"; break; case UnaryOpcode::diff: output << "diff"; break; case UnaryOpcode::adl: output << "adl"; break; case UnaryOpcode::steadyState: output << "steady_state"; case UnaryOpcode::steadyStateParamDeriv: output << "steady_state_param_deriv"; break; case UnaryOpcode::steadyStateParam2ndDeriv: output << "steady_state_param_second_deriv"; break; case UnaryOpcode::expectation: output << "expectation"; break; case UnaryOpcode::erf: output << "erf"; break; } output << R"(", "arg" : )"; arg->writeJsonAST(output); switch (op_code) { case UnaryOpcode::adl: output << R"(, "adl_param_name" : ")" << adl_param_name << R"(")" << R"(, "lags" : [)"; for (auto it = adl_lags.begin(); it != adl_lags.end(); ++it) { if (it != adl_lags.begin()) output << ", "; output << *it; } output << "]"; break; default: break; } output << "}"; } void UnaryOpNode::writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, const deriv_node_temp_terms_t &tef_terms, bool isdynamic) const { if (temporary_terms.find(const_cast(this)) != temporary_terms.end()) { output << "T" << idx; return; } // Always put parenthesis around uminus nodes if (op_code == UnaryOpcode::uminus) output << "("; switch (op_code) { case UnaryOpcode::uminus: output << "-"; break; case UnaryOpcode::exp: output << "exp"; break; case UnaryOpcode::log: output << "log"; break; case UnaryOpcode::log10: output << "log10"; break; case UnaryOpcode::cos: output << "cos"; break; case UnaryOpcode::sin: output << "sin"; break; case UnaryOpcode::tan: output << "tan"; break; case UnaryOpcode::acos: output << "acos"; break; case UnaryOpcode::asin: output << "asin"; break; case UnaryOpcode::atan: output << "atan"; break; case UnaryOpcode::cosh: output << "cosh"; break; case UnaryOpcode::sinh: output << "sinh"; break; case UnaryOpcode::tanh: output << "tanh"; break; case UnaryOpcode::acosh: output << "acosh"; break; case UnaryOpcode::asinh: output << "asinh"; break; case UnaryOpcode::atanh: output << "atanh"; break; case UnaryOpcode::sqrt: output << "sqrt"; break; case UnaryOpcode::cbrt: output << "cbrt"; break; case UnaryOpcode::abs: output << "abs"; break; case UnaryOpcode::sign: output << "sign"; break; case UnaryOpcode::diff: output << "diff"; break; case UnaryOpcode::adl: output << "adl("; arg->writeJsonOutput(output, temporary_terms, tef_terms); output << ", '" << adl_param_name << "', ["; for (auto it = adl_lags.begin(); it != adl_lags.end(); ++it) { if (it != adl_lags.begin()) output << ", "; output << *it; } output << "])"; return; case UnaryOpcode::steadyState: output << "("; arg->writeJsonOutput(output, temporary_terms, tef_terms, isdynamic); output << ")"; return; case UnaryOpcode::steadyStateParamDeriv: { auto varg = dynamic_cast(arg); assert(varg); assert(datatree.symbol_table.getType(varg->symb_id) == SymbolType::endogenous); assert(datatree.symbol_table.getType(param1_symb_id) == SymbolType::parameter); int tsid_endo = datatree.symbol_table.getTypeSpecificID(varg->symb_id); int tsid_param = datatree.symbol_table.getTypeSpecificID(param1_symb_id); output << "ss_param_deriv(" << tsid_endo+1 << "," << tsid_param+1 << ")"; } return; case UnaryOpcode::steadyStateParam2ndDeriv: { auto varg = dynamic_cast(arg); assert(varg); assert(datatree.symbol_table.getType(varg->symb_id) == SymbolType::endogenous); assert(datatree.symbol_table.getType(param1_symb_id) == SymbolType::parameter); assert(datatree.symbol_table.getType(param2_symb_id) == SymbolType::parameter); int tsid_endo = datatree.symbol_table.getTypeSpecificID(varg->symb_id); int tsid_param1 = datatree.symbol_table.getTypeSpecificID(param1_symb_id); int tsid_param2 = datatree.symbol_table.getTypeSpecificID(param2_symb_id); output << "ss_param_2nd_deriv(" << tsid_endo+1 << "," << tsid_param1+1 << "," << tsid_param2+1 << ")"; } return; case UnaryOpcode::expectation: output << "EXPECTATION(" << expectation_information_set << ")"; break; case UnaryOpcode::erf: output << "erf"; break; } bool close_parenthesis = false; /* Enclose argument with parentheses if: - current opcode is not uminus, or - current opcode is uminus and argument has lowest precedence */ if (op_code != UnaryOpcode::uminus || (op_code == UnaryOpcode::uminus && arg->precedenceJson(temporary_terms) < precedenceJson(temporary_terms))) { output << "("; close_parenthesis = true; } // Write argument arg->writeJsonOutput(output, temporary_terms, tef_terms, isdynamic); if (close_parenthesis) output << ")"; // Close parenthesis for uminus if (op_code == UnaryOpcode::uminus) output << ")"; } void UnaryOpNode::writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, const deriv_node_temp_terms_t &tef_terms) const { if (checkIfTemporaryTermThenWrite(output, output_type, temporary_terms, temporary_terms_idxs)) return; // Always put parenthesis around uminus nodes if (op_code == UnaryOpcode::uminus) output << LEFT_PAR(output_type); switch (op_code) { case UnaryOpcode::uminus: output << "-"; break; case UnaryOpcode::exp: if (isLatexOutput(output_type)) output << R"(\exp)"; else output << "exp"; break; case UnaryOpcode::log: if (isLatexOutput(output_type)) output << R"(\log)"; else output << "log"; break; case UnaryOpcode::log10: if (isLatexOutput(output_type)) output << R"(\log_{10})"; else output << "log10"; break; case UnaryOpcode::cos: if (isLatexOutput(output_type)) output << R"(\cos)"; else output << "cos"; break; case UnaryOpcode::sin: if (isLatexOutput(output_type)) output << R"(\sin)"; else output << "sin"; break; case UnaryOpcode::tan: if (isLatexOutput(output_type)) output << R"(\tan)"; else output << "tan"; break; case UnaryOpcode::acos: output << "acos"; break; case UnaryOpcode::asin: output << "asin"; break; case UnaryOpcode::atan: output << "atan"; break; case UnaryOpcode::cosh: output << "cosh"; break; case UnaryOpcode::sinh: output << "sinh"; break; case UnaryOpcode::tanh: output << "tanh"; break; case UnaryOpcode::acosh: output << "acosh"; break; case UnaryOpcode::asinh: output << "asinh"; break; case UnaryOpcode::atanh: output << "atanh"; break; case UnaryOpcode::sqrt: if (isLatexOutput(output_type)) { output << R"(\sqrt{)"; arg->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); output << "}"; return; } output << "sqrt"; break; case UnaryOpcode::cbrt: if (isMatlabOutput(output_type)) { output << "nthroot("; arg->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); output << ", 3)"; return; } else if (isLatexOutput(output_type)) { output << R"(\sqrt[3]{)"; arg->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); output << "}"; return; } else output << "cbrt"; break; case UnaryOpcode::abs: output << "abs"; break; case UnaryOpcode::sign: if (output_type == ExprNodeOutputType::CDynamicModel || output_type == ExprNodeOutputType::CStaticModel) output << "copysign"; else output << "sign"; break; case UnaryOpcode::steadyState: ExprNodeOutputType new_output_type; switch (output_type) { case ExprNodeOutputType::matlabDynamicModel: new_output_type = ExprNodeOutputType::matlabDynamicSteadyStateOperator; break; case ExprNodeOutputType::latexDynamicModel: new_output_type = ExprNodeOutputType::latexDynamicSteadyStateOperator; break; case ExprNodeOutputType::CDynamicModel: new_output_type = ExprNodeOutputType::CDynamicSteadyStateOperator; break; case ExprNodeOutputType::juliaDynamicModel: new_output_type = ExprNodeOutputType::juliaDynamicSteadyStateOperator; break; default: new_output_type = output_type; break; } output << "("; arg->writeOutput(output, new_output_type, temporary_terms, temporary_terms_idxs, tef_terms); output << ")"; return; case UnaryOpcode::steadyStateParamDeriv: { auto varg = dynamic_cast(arg); assert(varg); assert(datatree.symbol_table.getType(varg->symb_id) == SymbolType::endogenous); assert(datatree.symbol_table.getType(param1_symb_id) == SymbolType::parameter); int tsid_endo = datatree.symbol_table.getTypeSpecificID(varg->symb_id); int tsid_param = datatree.symbol_table.getTypeSpecificID(param1_symb_id); assert(isMatlabOutput(output_type)); output << "ss_param_deriv(" << tsid_endo+1 << "," << tsid_param+1 << ")"; } return; case UnaryOpcode::steadyStateParam2ndDeriv: { auto varg = dynamic_cast(arg); assert(varg); assert(datatree.symbol_table.getType(varg->symb_id) == SymbolType::endogenous); assert(datatree.symbol_table.getType(param1_symb_id) == SymbolType::parameter); assert(datatree.symbol_table.getType(param2_symb_id) == SymbolType::parameter); int tsid_endo = datatree.symbol_table.getTypeSpecificID(varg->symb_id); int tsid_param1 = datatree.symbol_table.getTypeSpecificID(param1_symb_id); int tsid_param2 = datatree.symbol_table.getTypeSpecificID(param2_symb_id); assert(isMatlabOutput(output_type)); output << "ss_param_2nd_deriv(" << tsid_endo+1 << "," << tsid_param1+1 << "," << tsid_param2+1 << ")"; } return; case UnaryOpcode::expectation: if (!isLatexOutput(output_type)) { cerr << "UnaryOpNode::writeOutput: not implemented on UnaryOpcode::expectation" << endl; exit(EXIT_FAILURE); } output << R"(\mathbb{E}_{t)"; if (expectation_information_set != 0) { if (expectation_information_set > 0) output << "+"; output << expectation_information_set; } output << "}"; break; case UnaryOpcode::erf: output << "erf"; break; case UnaryOpcode::diff: output << "diff"; break; case UnaryOpcode::adl: output << "adl"; break; } bool close_parenthesis = false; /* Enclose argument with parentheses if: - current opcode is not uminus, or - current opcode is uminus and argument has lowest precedence */ if (op_code != UnaryOpcode::uminus || (op_code == UnaryOpcode::uminus && arg->precedence(output_type, temporary_terms) < precedence(output_type, temporary_terms))) { output << LEFT_PAR(output_type); if (op_code == UnaryOpcode::sign && (output_type == ExprNodeOutputType::CDynamicModel || output_type == ExprNodeOutputType::CStaticModel)) output << "1.0,"; close_parenthesis = true; } // Write argument arg->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); if (close_parenthesis) output << RIGHT_PAR(output_type); // Close parenthesis for uminus if (op_code == UnaryOpcode::uminus) output << RIGHT_PAR(output_type); } void UnaryOpNode::writeExternalFunctionOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, deriv_node_temp_terms_t &tef_terms) const { arg->writeExternalFunctionOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); } void UnaryOpNode::writeJsonExternalFunctionOutput(vector &efout, const temporary_terms_t &temporary_terms, deriv_node_temp_terms_t &tef_terms, bool isdynamic) const { arg->writeJsonExternalFunctionOutput(efout, temporary_terms, tef_terms, isdynamic); } void UnaryOpNode::compileExternalFunctionOutput(ostream &CompileCode, unsigned int &instruction_number, bool lhs_rhs, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, bool dynamic, bool steady_dynamic, deriv_node_temp_terms_t &tef_terms) const { arg->compileExternalFunctionOutput(CompileCode, instruction_number, lhs_rhs, temporary_terms, temporary_terms_idxs, dynamic, steady_dynamic, tef_terms); } double UnaryOpNode::eval_opcode(UnaryOpcode op_code, double v) noexcept(false) { switch (op_code) { case UnaryOpcode::uminus: return -v; case UnaryOpcode::exp: return exp(v); case UnaryOpcode::log: return log(v); case UnaryOpcode::log10: return log10(v); case UnaryOpcode::cos: return cos(v); case UnaryOpcode::sin: return sin(v); case UnaryOpcode::tan: return tan(v); case UnaryOpcode::acos: return acos(v); case UnaryOpcode::asin: return asin(v); case UnaryOpcode::atan: return atan(v); case UnaryOpcode::cosh: return cosh(v); case UnaryOpcode::sinh: return sinh(v); case UnaryOpcode::tanh: return tanh(v); case UnaryOpcode::acosh: return acosh(v); case UnaryOpcode::asinh: return asinh(v); case UnaryOpcode::atanh: return atanh(v); case UnaryOpcode::sqrt: return sqrt(v); case UnaryOpcode::cbrt: return cbrt(v); case UnaryOpcode::abs: return abs(v); case UnaryOpcode::sign: return (v > 0) ? 1 : ((v < 0) ? -1 : 0); case UnaryOpcode::steadyState: return v; case UnaryOpcode::steadyStateParamDeriv: cerr << "UnaryOpNode::eval_opcode: not implemented on UnaryOpcode::steadyStateParamDeriv" << endl; exit(EXIT_FAILURE); case UnaryOpcode::steadyStateParam2ndDeriv: cerr << "UnaryOpNode::eval_opcode: not implemented on UnaryOpcode::steadyStateParam2ndDeriv" << endl; exit(EXIT_FAILURE); case UnaryOpcode::expectation: cerr << "UnaryOpNode::eval_opcode: not implemented on UnaryOpcode::expectation" << endl; exit(EXIT_FAILURE); case UnaryOpcode::erf: return erf(v); case UnaryOpcode::diff: cerr << "UnaryOpNode::eval_opcode: not implemented on UnaryOpcode::diff" << endl; exit(EXIT_FAILURE); case UnaryOpcode::adl: cerr << "UnaryOpNode::eval_opcode: not implemented on UnaryOpcode::adl" << endl; exit(EXIT_FAILURE); } // Suppress GCC warning exit(EXIT_FAILURE); } double UnaryOpNode::eval(const eval_context_t &eval_context) const noexcept(false) { double v = arg->eval(eval_context); return eval_opcode(op_code, v); } void UnaryOpNode::compile(ostream &CompileCode, unsigned int &instruction_number, bool lhs_rhs, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, bool dynamic, bool steady_dynamic, const deriv_node_temp_terms_t &tef_terms) const { if (auto this2 = const_cast(this); temporary_terms.find(this2) != temporary_terms.end()) { if (dynamic) { FLDT_ fldt(temporary_terms_idxs.at(this2)); fldt.write(CompileCode, instruction_number); } else { FLDST_ fldst(temporary_terms_idxs.at(this2)); fldst.write(CompileCode, instruction_number); } return; } if (op_code == UnaryOpcode::steadyState) arg->compile(CompileCode, instruction_number, lhs_rhs, temporary_terms, temporary_terms_idxs, dynamic, true, tef_terms); else { arg->compile(CompileCode, instruction_number, lhs_rhs, temporary_terms, temporary_terms_idxs, dynamic, steady_dynamic, tef_terms); FUNARY_ funary{static_cast(op_code)}; funary.write(CompileCode, instruction_number); } } void UnaryOpNode::collectVARLHSVariable(set &result) const { if (op_code == UnaryOpcode::diff) result.insert(const_cast(this)); else arg->collectVARLHSVariable(result); } void UnaryOpNode::collectDynamicVariables(SymbolType type_arg, set> &result) const { arg->collectDynamicVariables(type_arg, result); } void UnaryOpNode::computeSubExprContainingVariable(int symb_id, int lag, set &contain_var) const { arg->computeSubExprContainingVariable(symb_id, lag, contain_var); if (contain_var.count(arg) > 0) contain_var.insert(const_cast(this)); } BinaryOpNode * UnaryOpNode::normalizeEquationHelper(const set &contain_var, expr_t rhs) const { assert(contain_var.count(const_cast(this)) > 0); switch (op_code) { case UnaryOpcode::uminus: rhs = datatree.AddUMinus(rhs); break; case UnaryOpcode::exp: rhs = datatree.AddLog(rhs); break; case UnaryOpcode::log: rhs = datatree.AddExp(rhs); break; case UnaryOpcode::log10: rhs = datatree.AddPower(datatree.AddNonNegativeConstant("10"), rhs); break; case UnaryOpcode::cos: rhs = datatree.AddAcos(rhs); break; case UnaryOpcode::sin: rhs = datatree.AddAsin(rhs); break; case UnaryOpcode::tan: rhs = datatree.AddAtan(rhs); break; case UnaryOpcode::acos: rhs = datatree.AddCos(rhs); break; case UnaryOpcode::asin: rhs = datatree.AddSin(rhs); break; case UnaryOpcode::atan: rhs = datatree.AddTan(rhs); break; case UnaryOpcode::cosh: rhs = datatree.AddAcosh(rhs); break; case UnaryOpcode::sinh: rhs = datatree.AddAsinh(rhs); break; case UnaryOpcode::tanh: rhs = datatree.AddAtanh(rhs); break; case UnaryOpcode::acosh: rhs = datatree.AddCosh(rhs); break; case UnaryOpcode::asinh: rhs = datatree.AddSinh(rhs); break; case UnaryOpcode::atanh: rhs = datatree.AddTanh(rhs); break; case UnaryOpcode::sqrt: rhs = datatree.AddPower(rhs, datatree.Two); break; case UnaryOpcode::cbrt: rhs = datatree.AddPower(rhs, datatree.Three); break; default: throw NormalizationFailed(); } return arg->normalizeEquationHelper(contain_var, rhs); } expr_t UnaryOpNode::getChainRuleDerivative(int deriv_id, const map &recursive_variables) { expr_t darg = arg->getChainRuleDerivative(deriv_id, recursive_variables); return composeDerivatives(darg, deriv_id); } expr_t UnaryOpNode::buildSimilarUnaryOpNode(expr_t alt_arg, DataTree &alt_datatree) const { switch (op_code) { case UnaryOpcode::uminus: return alt_datatree.AddUMinus(alt_arg); case UnaryOpcode::exp: return alt_datatree.AddExp(alt_arg); case UnaryOpcode::log: return alt_datatree.AddLog(alt_arg); case UnaryOpcode::log10: return alt_datatree.AddLog10(alt_arg); case UnaryOpcode::cos: return alt_datatree.AddCos(alt_arg); case UnaryOpcode::sin: return alt_datatree.AddSin(alt_arg); case UnaryOpcode::tan: return alt_datatree.AddTan(alt_arg); case UnaryOpcode::acos: return alt_datatree.AddAcos(alt_arg); case UnaryOpcode::asin: return alt_datatree.AddAsin(alt_arg); case UnaryOpcode::atan: return alt_datatree.AddAtan(alt_arg); case UnaryOpcode::cosh: return alt_datatree.AddCosh(alt_arg); case UnaryOpcode::sinh: return alt_datatree.AddSinh(alt_arg); case UnaryOpcode::tanh: return alt_datatree.AddTanh(alt_arg); case UnaryOpcode::acosh: return alt_datatree.AddAcosh(alt_arg); case UnaryOpcode::asinh: return alt_datatree.AddAsinh(alt_arg); case UnaryOpcode::atanh: return alt_datatree.AddAtanh(alt_arg); case UnaryOpcode::sqrt: return alt_datatree.AddSqrt(alt_arg); case UnaryOpcode::cbrt: return alt_datatree.AddCbrt(alt_arg); case UnaryOpcode::abs: return alt_datatree.AddAbs(alt_arg); case UnaryOpcode::sign: return alt_datatree.AddSign(alt_arg); case UnaryOpcode::steadyState: return alt_datatree.AddSteadyState(alt_arg); case UnaryOpcode::steadyStateParamDeriv: cerr << "UnaryOpNode::buildSimilarUnaryOpNode: UnaryOpcode::steadyStateParamDeriv can't be translated" << endl; exit(EXIT_FAILURE); case UnaryOpcode::steadyStateParam2ndDeriv: cerr << "UnaryOpNode::buildSimilarUnaryOpNode: UnaryOpcode::steadyStateParam2ndDeriv can't be translated" << endl; exit(EXIT_FAILURE); case UnaryOpcode::expectation: return alt_datatree.AddExpectation(expectation_information_set, alt_arg); case UnaryOpcode::erf: return alt_datatree.AddErf(alt_arg); case UnaryOpcode::diff: return alt_datatree.AddDiff(alt_arg); case UnaryOpcode::adl: return alt_datatree.AddAdl(alt_arg, adl_param_name, adl_lags); } // Suppress GCC warning exit(EXIT_FAILURE); } expr_t UnaryOpNode::toStatic(DataTree &static_datatree) const { expr_t sarg = arg->toStatic(static_datatree); return buildSimilarUnaryOpNode(sarg, static_datatree); } void UnaryOpNode::computeXrefs(EquationInfo &ei) const { arg->computeXrefs(ei); } expr_t UnaryOpNode::clone(DataTree &datatree) const { expr_t substarg = arg->clone(datatree); return buildSimilarUnaryOpNode(substarg, datatree); } int UnaryOpNode::maxEndoLead() const { return arg->maxEndoLead(); } int UnaryOpNode::maxExoLead() const { return arg->maxExoLead(); } int UnaryOpNode::maxEndoLag() const { return arg->maxEndoLag(); } int UnaryOpNode::maxExoLag() const { return arg->maxExoLag(); } int UnaryOpNode::maxLead() const { return arg->maxLead(); } int UnaryOpNode::maxLag() const { return arg->maxLag(); } int UnaryOpNode::maxLagWithDiffsExpanded() const { if (op_code == UnaryOpcode::diff) return arg->maxLagWithDiffsExpanded() + 1; return arg->maxLagWithDiffsExpanded(); } expr_t UnaryOpNode::undiff() const { if (op_code == UnaryOpcode::diff) return arg; return arg->undiff(); } int UnaryOpNode::VarMaxLag(const set &lhs_lag_equiv) const { auto [lag_equiv_repr, index] = getLagEquivalenceClass(); if (lhs_lag_equiv.find(lag_equiv_repr) == lhs_lag_equiv.end()) return 0; return arg->maxLag(); } int UnaryOpNode::VarMinLag() const { return arg->VarMinLag(); } expr_t UnaryOpNode::substituteAdl() const { if (op_code != UnaryOpcode::adl) { expr_t argsubst = arg->substituteAdl(); return buildSimilarUnaryOpNode(argsubst, datatree); } expr_t arg1subst = arg->substituteAdl(); expr_t retval = nullptr; ostringstream inttostr; for (auto it = adl_lags.begin(); it != adl_lags.end(); ++it) if (it == adl_lags.begin()) { inttostr << *it; retval = datatree.AddTimes(datatree.AddVariable(datatree.symbol_table.getID(adl_param_name + "_lag_" + inttostr.str()), 0), arg1subst->decreaseLeadsLags(*it)); } else { inttostr.clear(); inttostr.str(""); inttostr << *it; retval = datatree.AddPlus(retval, datatree.AddTimes(datatree.AddVariable(datatree.symbol_table.getID(adl_param_name + "_lag_" + inttostr.str()), 0), arg1subst->decreaseLeadsLags(*it))); } return retval; } expr_t UnaryOpNode::substituteModelLocalVariables() const { expr_t argsubst = arg->substituteModelLocalVariables(); return buildSimilarUnaryOpNode(argsubst, datatree); } expr_t UnaryOpNode::substituteVarExpectation(const map &subst_table) const { expr_t argsubst = arg->substituteVarExpectation(subst_table); return buildSimilarUnaryOpNode(argsubst, datatree); } int UnaryOpNode::countDiffs() const { if (op_code == UnaryOpcode::diff) return arg->countDiffs() + 1; return arg->countDiffs(); } bool UnaryOpNode::createAuxVarForUnaryOpNode() const { switch (op_code) { case UnaryOpcode::exp: case UnaryOpcode::log: case UnaryOpcode::log10: case UnaryOpcode::cos: case UnaryOpcode::sin: case UnaryOpcode::tan: case UnaryOpcode::acos: case UnaryOpcode::asin: case UnaryOpcode::atan: case UnaryOpcode::cosh: case UnaryOpcode::sinh: case UnaryOpcode::tanh: case UnaryOpcode::acosh: case UnaryOpcode::asinh: case UnaryOpcode::atanh: case UnaryOpcode::sqrt: case UnaryOpcode::cbrt: case UnaryOpcode::abs: case UnaryOpcode::sign: case UnaryOpcode::erf: return true; default: return false; } } void UnaryOpNode::findUnaryOpNodesForAuxVarCreation(lag_equivalence_table_t &nodes) const { arg->findUnaryOpNodesForAuxVarCreation(nodes); if (!this->createAuxVarForUnaryOpNode()) return; auto [lag_equiv_repr, index] = getLagEquivalenceClass(); nodes[lag_equiv_repr][index] = const_cast(this); } void UnaryOpNode::findDiffNodes(lag_equivalence_table_t &nodes) const { arg->findDiffNodes(nodes); if (op_code != UnaryOpcode::diff) return; auto [lag_equiv_repr, index] = getLagEquivalenceClass(); nodes[lag_equiv_repr][index] = const_cast(this); } int UnaryOpNode::findTargetVariable(int lhs_symb_id) const { return arg->findTargetVariable(lhs_symb_id); } expr_t UnaryOpNode::substituteDiff(const lag_equivalence_table_t &nodes, subst_table_t &subst_table, vector &neweqs) const { // If this is not a diff node, then substitute recursively and return expr_t argsubst = arg->substituteDiff(nodes, subst_table, neweqs); if (op_code != UnaryOpcode::diff) return buildSimilarUnaryOpNode(argsubst, datatree); if (auto sit = subst_table.find(this); sit != subst_table.end()) return const_cast(sit->second); auto [lag_equiv_repr, index] = getLagEquivalenceClass(); auto it = nodes.find(lag_equiv_repr); if (it == nodes.end() || it->second.find(index) == it->second.end() || it->second.at(index) != this) { /* diff does not appear in VAR equations, so simply create aux var and return. Once the comparison of expression nodes works, come back and remove this part, folding into the next loop. */ int symb_id = datatree.symbol_table.addDiffAuxiliaryVar(argsubst->idx, const_cast(this)); VariableNode *aux_var = datatree.AddVariable(symb_id, 0); neweqs.push_back(datatree.AddEqual(aux_var, datatree.AddMinus(argsubst, argsubst->decreaseLeadsLags(1)))); subst_table[this] = dynamic_cast(aux_var); return const_cast(subst_table[this]); } /* At this point, we know that this node (and its lagged/leaded brothers) must be substituted. We create the auxiliary variable and fill the substitution table for all those similar nodes, in an iteration going from leads to lags. */ int last_index = 0; VariableNode *last_aux_var = nullptr; for (auto rit = it->second.rbegin(); rit != it->second.rend(); ++rit) { expr_t argsubst = dynamic_cast(rit->second)-> arg->substituteDiff(nodes, subst_table, neweqs); auto vn = dynamic_cast(argsubst); int symb_id; if (rit == it->second.rbegin()) { if (vn) symb_id = datatree.symbol_table.addDiffAuxiliaryVar(argsubst->idx, rit->second, vn->symb_id, vn->lag); else symb_id = datatree.symbol_table.addDiffAuxiliaryVar(argsubst->idx, rit->second); // make originating aux var & equation last_index = rit->first; last_aux_var = datatree.AddVariable(symb_id, 0); //ORIG_AUX_DIFF = argsubst - argsubst(-1) neweqs.push_back(datatree.AddEqual(last_aux_var, datatree.AddMinus(argsubst, argsubst->decreaseLeadsLags(1)))); subst_table[rit->second] = dynamic_cast(last_aux_var); } else { // just add equation of form: AUX_DIFF = LAST_AUX_VAR(-1) VariableNode *new_aux_var = nullptr; for (int i = last_index; i > rit->first; i--) { if (i == last_index) symb_id = datatree.symbol_table.addDiffLagAuxiliaryVar(argsubst->idx, rit->second, last_aux_var->symb_id, last_aux_var->lag); else symb_id = datatree.symbol_table.addDiffLagAuxiliaryVar(new_aux_var->idx, rit->second, last_aux_var->symb_id, last_aux_var->lag); new_aux_var = datatree.AddVariable(symb_id, 0); neweqs.push_back(datatree.AddEqual(new_aux_var, last_aux_var->decreaseLeadsLags(1))); last_aux_var = new_aux_var; } subst_table[rit->second] = dynamic_cast(new_aux_var); last_index = rit->first; } } return const_cast(subst_table[this]); } expr_t UnaryOpNode::substituteUnaryOpNodes(const lag_equivalence_table_t &nodes, subst_table_t &subst_table, vector &neweqs) const { if (auto sit = subst_table.find(this); sit != subst_table.end()) return const_cast(sit->second); /* If the equivalence class of this node is not marked for substitution, then substitute recursively and return. */ auto [lag_equiv_repr, index] = getLagEquivalenceClass(); auto it = nodes.find(lag_equiv_repr); expr_t argsubst = arg->substituteUnaryOpNodes(nodes, subst_table, neweqs); if (it == nodes.end()) return buildSimilarUnaryOpNode(argsubst, datatree); string unary_op; switch (op_code) { case UnaryOpcode::exp: unary_op = "exp"; break; case UnaryOpcode::log: unary_op = "log"; break; case UnaryOpcode::log10: unary_op = "log10"; break; case UnaryOpcode::cos: unary_op = "cos"; break; case UnaryOpcode::sin: unary_op = "sin"; break; case UnaryOpcode::tan: unary_op = "tan"; break; case UnaryOpcode::acos: unary_op = "acos"; break; case UnaryOpcode::asin: unary_op = "asin"; break; case UnaryOpcode::atan: unary_op = "atan"; break; case UnaryOpcode::cosh: unary_op = "cosh"; break; case UnaryOpcode::sinh: unary_op = "sinh"; break; case UnaryOpcode::tanh: unary_op = "tanh"; break; case UnaryOpcode::acosh: unary_op = "acosh"; break; case UnaryOpcode::asinh: unary_op = "asinh"; break; case UnaryOpcode::atanh: unary_op = "atanh"; break; case UnaryOpcode::sqrt: unary_op = "sqrt"; break; case UnaryOpcode::cbrt: unary_op = "cbrt"; break; case UnaryOpcode::abs: unary_op = "abs"; break; case UnaryOpcode::sign: unary_op = "sign"; break; case UnaryOpcode::erf: unary_op = "erf"; break; default: cerr << "UnaryOpNode::substituteUnaryOpNodes: Shouldn't arrive here" << endl; exit(EXIT_FAILURE); } /* At this point, we know that this node (and its lagged/leaded brothers) must be substituted. We create the auxiliary variable and fill the substitution table for all those similar nodes, in an iteration going from leads to lags. */ int base_index = 0; VariableNode *aux_var = nullptr; for (auto rit = it->second.rbegin(); rit != it->second.rend(); ++rit) if (rit == it->second.rbegin()) { /* Verify that we’re not operating on a node with leads, since the transformation does take into account the expectation operator. We only need to do this for the first iteration of the loop, because we’re going from leads to lags. */ if (rit->second->maxLead() > 0) { cerr << "Cannot substitute unary operations that contain leads" << endl; exit(EXIT_FAILURE); } int symb_id; auto vn = dynamic_cast(argsubst); if (!vn) symb_id = datatree.symbol_table.addUnaryOpAuxiliaryVar(this->idx, dynamic_cast(rit->second), unary_op); else symb_id = datatree.symbol_table.addUnaryOpAuxiliaryVar(this->idx, dynamic_cast(rit->second), unary_op, vn->symb_id, vn->lag); aux_var = datatree.AddVariable(symb_id, 0); neweqs.push_back(datatree.AddEqual(aux_var, dynamic_cast(rit->second))); subst_table[rit->second] = dynamic_cast(aux_var); base_index = rit->first; } else subst_table[rit->second] = dynamic_cast(aux_var->decreaseLeadsLags(base_index - rit->first)); assert(subst_table.find(this) != subst_table.end()); return const_cast(subst_table.at(this)); } expr_t UnaryOpNode::substitutePacExpectation(const string &name, expr_t subexpr) { expr_t argsubst = arg->substitutePacExpectation(name, subexpr); return buildSimilarUnaryOpNode(argsubst, datatree); } expr_t UnaryOpNode::decreaseLeadsLags(int n) const { expr_t argsubst = arg->decreaseLeadsLags(n); return buildSimilarUnaryOpNode(argsubst, datatree); } expr_t UnaryOpNode::decreaseLeadsLagsPredeterminedVariables() const { expr_t argsubst = arg->decreaseLeadsLagsPredeterminedVariables(); return buildSimilarUnaryOpNode(argsubst, datatree); } expr_t UnaryOpNode::substituteEndoLeadGreaterThanTwo(subst_table_t &subst_table, vector &neweqs, bool deterministic_model) const { if (op_code == UnaryOpcode::uminus || deterministic_model) { expr_t argsubst = arg->substituteEndoLeadGreaterThanTwo(subst_table, neweqs, deterministic_model); return buildSimilarUnaryOpNode(argsubst, datatree); } else { if (maxEndoLead() >= 2) return createEndoLeadAuxiliaryVarForMyself(subst_table, neweqs); else return const_cast(this); } } expr_t UnaryOpNode::substituteEndoLagGreaterThanTwo(subst_table_t &subst_table, vector &neweqs) const { expr_t argsubst = arg->substituteEndoLagGreaterThanTwo(subst_table, neweqs); return buildSimilarUnaryOpNode(argsubst, datatree); } expr_t UnaryOpNode::substituteExoLead(subst_table_t &subst_table, vector &neweqs, bool deterministic_model) const { if (op_code == UnaryOpcode::uminus || deterministic_model) { expr_t argsubst = arg->substituteExoLead(subst_table, neweqs, deterministic_model); return buildSimilarUnaryOpNode(argsubst, datatree); } else { if (maxExoLead() >= 1) return createExoLeadAuxiliaryVarForMyself(subst_table, neweqs); else return const_cast(this); } } expr_t UnaryOpNode::substituteExoLag(subst_table_t &subst_table, vector &neweqs) const { expr_t argsubst = arg->substituteExoLag(subst_table, neweqs); return buildSimilarUnaryOpNode(argsubst, datatree); } expr_t UnaryOpNode::substituteExpectation(subst_table_t &subst_table, vector &neweqs, bool partial_information_model) const { if (op_code == UnaryOpcode::expectation) { if (auto it = subst_table.find(const_cast(this)); it != subst_table.end()) return const_cast(it->second); //Arriving here, we need to create an auxiliary variable for this Expectation Operator: //AUX_EXPECT_(LEAD/LAG)_(period)_(arg.idx) OR //AUX_EXPECT_(info_set_name)_(arg.idx) int symb_id = datatree.symbol_table.addExpectationAuxiliaryVar(expectation_information_set, arg->idx, const_cast(this)); expr_t newAuxE = datatree.AddVariable(symb_id, 0); if (partial_information_model && expectation_information_set == 0) if (!dynamic_cast(arg)) { cerr << "ERROR: In Partial Information models, EXPECTATION(0)(X) " << "can only be used when X is a single variable." << endl; exit(EXIT_FAILURE); } //take care of any nested expectation operators by calling arg->substituteExpectation(.), then decreaseLeadsLags for this UnaryOpcode::expectation operator //arg(lag-period) (holds entire subtree of arg(lag-period) expr_t substexpr = (arg->substituteExpectation(subst_table, neweqs, partial_information_model))->decreaseLeadsLags(expectation_information_set); assert(substexpr); neweqs.push_back(datatree.AddEqual(newAuxE, substexpr)); //AUXE_period_arg.idx = arg(lag-period) newAuxE = datatree.AddVariable(symb_id, expectation_information_set); assert(dynamic_cast(newAuxE)); subst_table[this] = dynamic_cast(newAuxE); return newAuxE; } else { expr_t argsubst = arg->substituteExpectation(subst_table, neweqs, partial_information_model); return buildSimilarUnaryOpNode(argsubst, datatree); } } expr_t UnaryOpNode::differentiateForwardVars(const vector &subset, subst_table_t &subst_table, vector &neweqs) const { expr_t argsubst = arg->differentiateForwardVars(subset, subst_table, neweqs); return buildSimilarUnaryOpNode(argsubst, datatree); } bool UnaryOpNode::isNumConstNodeEqualTo(double value) const { return false; } bool UnaryOpNode::isVariableNodeEqualTo(SymbolType type_arg, int variable_id, int lag_arg) const { return false; } bool UnaryOpNode::containsPacExpectation(const string &pac_model_name) const { return arg->containsPacExpectation(pac_model_name); } expr_t UnaryOpNode::replaceTrendVar() const { expr_t argsubst = arg->replaceTrendVar(); return buildSimilarUnaryOpNode(argsubst, datatree); } expr_t UnaryOpNode::detrend(int symb_id, bool log_trend, expr_t trend) const { expr_t argsubst = arg->detrend(symb_id, log_trend, trend); return buildSimilarUnaryOpNode(argsubst, datatree); } expr_t UnaryOpNode::removeTrendLeadLag(const map &trend_symbols_map) const { expr_t argsubst = arg->removeTrendLeadLag(trend_symbols_map); return buildSimilarUnaryOpNode(argsubst, datatree); } bool UnaryOpNode::isInStaticForm() const { if (op_code == UnaryOpcode::steadyState || op_code == UnaryOpcode::steadyStateParamDeriv || op_code == UnaryOpcode::steadyStateParam2ndDeriv || op_code == UnaryOpcode::expectation) return false; else return arg->isInStaticForm(); } bool UnaryOpNode::isParamTimesEndogExpr() const { return arg->isParamTimesEndogExpr(); } bool UnaryOpNode::isVarModelReferenced(const string &model_info_name) const { return arg->isVarModelReferenced(model_info_name); } void UnaryOpNode::getEndosAndMaxLags(map &model_endos_and_lags) const { arg->getEndosAndMaxLags(model_endos_and_lags); } expr_t UnaryOpNode::substituteStaticAuxiliaryVariable() const { if (op_code == UnaryOpcode::diff) return datatree.Zero; expr_t argsubst = arg->substituteStaticAuxiliaryVariable(); if (op_code == UnaryOpcode::expectation) return argsubst; else return buildSimilarUnaryOpNode(argsubst, datatree); } expr_t UnaryOpNode::replaceVarsInEquation(map &table) const { expr_t argsubst = arg->replaceVarsInEquation(table); 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}, arg1{arg1_arg}, arg2{arg2_arg}, op_code{op_code_arg}, powerDerivOrder{powerDerivOrder_arg} { assert(powerDerivOrder >= 0); } void BinaryOpNode::prepareForDerivation() { if (preparedForDerivation) return; preparedForDerivation = true; arg1->prepareForDerivation(); arg2->prepareForDerivation(); // Non-null derivatives are the union of those of the arguments // Compute set union of arg1->non_null_derivatives and arg2->non_null_derivatives set_union(arg1->non_null_derivatives.begin(), arg1->non_null_derivatives.end(), arg2->non_null_derivatives.begin(), arg2->non_null_derivatives.end(), inserter(non_null_derivatives, non_null_derivatives.begin())); } expr_t BinaryOpNode::getNonZeroPartofEquation() const { assert(arg1 == datatree.Zero || arg2 == datatree.Zero); if (arg1 == datatree.Zero) return arg2; return arg1; } expr_t BinaryOpNode::composeDerivatives(expr_t darg1, expr_t darg2) { expr_t t11, t12, t13, t14, t15; switch (op_code) { case BinaryOpcode::plus: return datatree.AddPlus(darg1, darg2); case BinaryOpcode::minus: case BinaryOpcode::equal: return datatree.AddMinus(darg1, darg2); case BinaryOpcode::times: t11 = datatree.AddTimes(darg1, arg2); t12 = datatree.AddTimes(darg2, arg1); return datatree.AddPlus(t11, t12); case BinaryOpcode::divide: if (darg2 != datatree.Zero) { t11 = datatree.AddTimes(darg1, arg2); t12 = datatree.AddTimes(darg2, arg1); t13 = datatree.AddMinus(t11, t12); t14 = datatree.AddTimes(arg2, arg2); return datatree.AddDivide(t13, t14); } else return datatree.AddDivide(darg1, arg2); case BinaryOpcode::less: case BinaryOpcode::greater: case BinaryOpcode::lessEqual: case BinaryOpcode::greaterEqual: case BinaryOpcode::equalEqual: case BinaryOpcode::different: return datatree.Zero; case BinaryOpcode::power: if (darg2 == datatree.Zero) if (darg1 == datatree.Zero) return datatree.Zero; else if (dynamic_cast(arg2)) { t11 = datatree.AddMinus(arg2, datatree.One); t12 = datatree.AddPower(arg1, t11); t13 = datatree.AddTimes(arg2, t12); return datatree.AddTimes(darg1, t13); } else return datatree.AddTimes(darg1, datatree.AddPowerDeriv(arg1, arg2, powerDerivOrder + 1)); else { t11 = datatree.AddLog(arg1); t12 = datatree.AddTimes(darg2, t11); t13 = datatree.AddTimes(darg1, arg2); t14 = datatree.AddDivide(t13, arg1); t15 = datatree.AddPlus(t12, t14); return datatree.AddTimes(t15, this); } case BinaryOpcode::powerDeriv: if (darg2 == datatree.Zero) return datatree.AddTimes(darg1, datatree.AddPowerDeriv(arg1, arg2, powerDerivOrder + 1)); else { t11 = datatree.AddTimes(darg2, datatree.AddLog(arg1)); t12 = datatree.AddMinus(arg2, datatree.AddPossiblyNegativeConstant(powerDerivOrder)); t13 = datatree.AddTimes(darg1, t12); t14 = datatree.AddDivide(t13, arg1); t15 = datatree.AddPlus(t11, t14); expr_t f = datatree.AddPower(arg1, t12); expr_t first_part = datatree.AddTimes(f, t15); for (int i = 0; i < powerDerivOrder; i++) first_part = datatree.AddTimes(first_part, datatree.AddMinus(arg2, datatree.AddPossiblyNegativeConstant(i))); t13 = datatree.Zero; for (int i = 0; i < powerDerivOrder; i++) { t11 = datatree.One; for (int j = 0; j < powerDerivOrder; j++) if (i != j) { t12 = datatree.AddMinus(arg2, datatree.AddPossiblyNegativeConstant(j)); t11 = datatree.AddTimes(t11, t12); } t13 = datatree.AddPlus(t13, t11); } t13 = datatree.AddTimes(darg2, t13); t14 = datatree.AddTimes(f, t13); return datatree.AddPlus(first_part, t14); } case BinaryOpcode::max: t11 = datatree.AddGreater(arg1, arg2); t12 = datatree.AddTimes(t11, darg1); t13 = datatree.AddMinus(datatree.One, t11); t14 = datatree.AddTimes(t13, darg2); return datatree.AddPlus(t14, t12); case BinaryOpcode::min: t11 = datatree.AddGreater(arg2, arg1); t12 = datatree.AddTimes(t11, darg1); t13 = datatree.AddMinus(datatree.One, t11); t14 = datatree.AddTimes(t13, darg2); return datatree.AddPlus(t14, t12); } // Suppress GCC warning exit(EXIT_FAILURE); } expr_t BinaryOpNode::unpackPowerDeriv() const { if (op_code != BinaryOpcode::powerDeriv) return const_cast(this); expr_t front = datatree.One; for (int i = 0; i < powerDerivOrder; i++) front = datatree.AddTimes(front, datatree.AddMinus(arg2, datatree.AddPossiblyNegativeConstant(i))); expr_t tmp = datatree.AddPower(arg1, datatree.AddMinus(arg2, datatree.AddPossiblyNegativeConstant(powerDerivOrder))); return datatree.AddTimes(front, tmp); } expr_t BinaryOpNode::computeDerivative(int deriv_id) { expr_t darg1 = arg1->getDerivative(deriv_id); expr_t darg2 = arg2->getDerivative(deriv_id); return composeDerivatives(darg1, darg2); } int BinaryOpNode::precedence(ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms) const { // A temporary term behaves as a variable if (temporary_terms.find(const_cast(this)) != temporary_terms.end()) return 100; switch (op_code) { case BinaryOpcode::equal: return 0; case BinaryOpcode::equalEqual: case BinaryOpcode::different: return 1; case BinaryOpcode::lessEqual: case BinaryOpcode::greaterEqual: case BinaryOpcode::less: case BinaryOpcode::greater: return 2; case BinaryOpcode::plus: case BinaryOpcode::minus: return 3; case BinaryOpcode::times: case BinaryOpcode::divide: return 4; case BinaryOpcode::power: case BinaryOpcode::powerDeriv: if (isCOutput(output_type)) // In C, power operator is of the form pow(a, b) return 100; else return 5; case BinaryOpcode::min: case BinaryOpcode::max: return 100; } // Suppress GCC warning exit(EXIT_FAILURE); } int BinaryOpNode::precedenceJson(const temporary_terms_t &temporary_terms) const { // A temporary term behaves as a variable if (temporary_terms.find(const_cast(this)) != temporary_terms.end()) return 100; switch (op_code) { case BinaryOpcode::equal: return 0; case BinaryOpcode::equalEqual: case BinaryOpcode::different: return 1; case BinaryOpcode::lessEqual: case BinaryOpcode::greaterEqual: case BinaryOpcode::less: case BinaryOpcode::greater: return 2; case BinaryOpcode::plus: case BinaryOpcode::minus: return 3; case BinaryOpcode::times: case BinaryOpcode::divide: return 4; case BinaryOpcode::power: case BinaryOpcode::powerDeriv: return 5; case BinaryOpcode::min: case BinaryOpcode::max: return 100; } // Suppress GCC warning exit(EXIT_FAILURE); } int BinaryOpNode::cost(const map, temporary_terms_t> &temp_terms_map, bool is_matlab) const { // For a temporary term, the cost is null for (const auto &it : temp_terms_map) if (it.second.find(const_cast(this)) != it.second.end()) return 0; int arg_cost = arg1->cost(temp_terms_map, is_matlab) + arg2->cost(temp_terms_map, is_matlab); return cost(arg_cost, is_matlab); } int BinaryOpNode::cost(const vector> &blocks_temporary_terms, bool is_matlab) const { // For a temporary term, the cost is null for (const auto &blk_tt : blocks_temporary_terms) for (const auto &eq_tt : blk_tt) if (eq_tt.find(const_cast(this)) != eq_tt.end()) return 0; int arg_cost = arg1->cost(blocks_temporary_terms, is_matlab) + arg2->cost(blocks_temporary_terms, is_matlab); return cost(arg_cost, is_matlab); } int BinaryOpNode::cost(int cost, bool is_matlab) const { if (is_matlab) // Cost for Matlab files switch (op_code) { case BinaryOpcode::less: case BinaryOpcode::greater: case BinaryOpcode::lessEqual: case BinaryOpcode::greaterEqual: case BinaryOpcode::equalEqual: case BinaryOpcode::different: return cost + 60; case BinaryOpcode::plus: case BinaryOpcode::minus: case BinaryOpcode::times: return cost + 90; case BinaryOpcode::max: case BinaryOpcode::min: return cost + 110; case BinaryOpcode::divide: return cost + 990; case BinaryOpcode::power: case BinaryOpcode::powerDeriv: return cost + (min_cost_matlab/2+1); case BinaryOpcode::equal: return cost; } else // Cost for C files switch (op_code) { case BinaryOpcode::less: case BinaryOpcode::greater: case BinaryOpcode::lessEqual: case BinaryOpcode::greaterEqual: case BinaryOpcode::equalEqual: case BinaryOpcode::different: return cost + 2; case BinaryOpcode::plus: case BinaryOpcode::minus: case BinaryOpcode::times: return cost + 4; case BinaryOpcode::max: case BinaryOpcode::min: return cost + 5; case BinaryOpcode::divide: return cost + 15; case BinaryOpcode::power: return cost + 520; case BinaryOpcode::powerDeriv: return cost + (min_cost_c/2+1); case BinaryOpcode::equal: return cost; } // Suppress GCC warning exit(EXIT_FAILURE); } void BinaryOpNode::computeTemporaryTerms(const pair &derivOrder, map, temporary_terms_t> &temp_terms_map, map>> &reference_count, bool is_matlab) const { expr_t this2 = const_cast(this); if (auto it = reference_count.find(this2); it == reference_count.end()) { // If this node has never been encountered, set its ref count to one, // and travel through its children reference_count[this2] = { 1, derivOrder }; arg1->computeTemporaryTerms(derivOrder, temp_terms_map, reference_count, is_matlab); arg2->computeTemporaryTerms(derivOrder, temp_terms_map, reference_count, is_matlab); } else { /* If the node has already been encountered, increment its ref count and declare it as a temporary term if it is too costly (except if it is an equal node: we don't want them as temporary terms) */ auto &[nref, min_order] = it->second; nref++; if (nref * cost(temp_terms_map, is_matlab) > min_cost(is_matlab) && op_code != BinaryOpcode::equal) temp_terms_map[min_order].insert(this2); } } void BinaryOpNode::computeBlockTemporaryTerms(int blk, int eq, vector> &blocks_temporary_terms, map> &reference_count) const { expr_t this2 = const_cast(this); if (auto it = reference_count.find(this2); it == reference_count.end()) { reference_count[this2] = { 1, blk, eq }; arg1->computeBlockTemporaryTerms(blk, eq, blocks_temporary_terms, reference_count); arg2->computeBlockTemporaryTerms(blk, eq, blocks_temporary_terms, reference_count); } else { auto &[nref, first_blk, first_eq] = it->second; nref++; if (nref * cost(blocks_temporary_terms, false) > min_cost_c && op_code != BinaryOpcode::equal) blocks_temporary_terms[first_blk][first_eq].insert(this2); } } double BinaryOpNode::eval_opcode(double v1, BinaryOpcode op_code, double v2, int derivOrder) noexcept(false) { switch (op_code) { case BinaryOpcode::plus: return v1 + v2; case BinaryOpcode::minus: return v1 - v2; case BinaryOpcode::times: return v1 * v2; case BinaryOpcode::divide: return v1 / v2; case BinaryOpcode::power: return pow(v1, v2); case BinaryOpcode::powerDeriv: if (fabs(v1) < near_zero && v2 > 0 && derivOrder > v2 && fabs(v2-nearbyint(v2)) < near_zero) return 0.0; else { double dxp = pow(v1, v2-derivOrder); for (int i = 0; i < derivOrder; i++) dxp *= v2--; return dxp; } case BinaryOpcode::max: if (v1 < v2) return v2; else return v1; case BinaryOpcode::min: if (v1 > v2) return v2; else return v1; case BinaryOpcode::less: return v1 < v2; case BinaryOpcode::greater: return v1 > v2; case BinaryOpcode::lessEqual: return v1 <= v2; case BinaryOpcode::greaterEqual: return v1 >= v2; case BinaryOpcode::equalEqual: return v1 == v2; case BinaryOpcode::different: return v1 != v2; case BinaryOpcode::equal: throw EvalException(); } // Suppress GCC warning exit(EXIT_FAILURE); } double BinaryOpNode::eval(const eval_context_t &eval_context) const noexcept(false) { double v1 = arg1->eval(eval_context); double v2 = arg2->eval(eval_context); return eval_opcode(v1, op_code, v2, powerDerivOrder); } void BinaryOpNode::compile(ostream &CompileCode, unsigned int &instruction_number, bool lhs_rhs, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, bool dynamic, bool steady_dynamic, const deriv_node_temp_terms_t &tef_terms) const { // If current node is a temporary term if (auto this2 = const_cast(this); temporary_terms.find(this2) != temporary_terms.end()) { if (dynamic) { FLDT_ fldt(temporary_terms_idxs.at(this2)); fldt.write(CompileCode, instruction_number); } else { FLDST_ fldst(temporary_terms_idxs.at(this2)); fldst.write(CompileCode, instruction_number); } return; } if (op_code == BinaryOpcode::powerDeriv) { FLDC_ fldc(powerDerivOrder); fldc.write(CompileCode, instruction_number); } arg1->compile(CompileCode, instruction_number, lhs_rhs, temporary_terms, temporary_terms_idxs, dynamic, steady_dynamic, tef_terms); arg2->compile(CompileCode, instruction_number, lhs_rhs, temporary_terms, temporary_terms_idxs, dynamic, steady_dynamic, tef_terms); FBINARY_ fbinary{static_cast(op_code)}; fbinary.write(CompileCode, instruction_number); } bool BinaryOpNode::containsExternalFunction() const { return arg1->containsExternalFunction() || arg2->containsExternalFunction(); } void BinaryOpNode::writeJsonAST(ostream &output) const { output << R"({"node_type" : "BinaryOpNode",)" << R"( "op" : ")"; switch (op_code) { case BinaryOpcode::plus: output << "+"; break; case BinaryOpcode::minus: output << "-"; break; case BinaryOpcode::times: output << "*"; break; case BinaryOpcode::divide: output << "/"; break; case BinaryOpcode::power: output << "^"; break; case BinaryOpcode::less: output << "<"; break; case BinaryOpcode::greater: output << ">"; break; case BinaryOpcode::lessEqual: output << "<="; break; case BinaryOpcode::greaterEqual: output << ">="; break; case BinaryOpcode::equalEqual: output << "=="; break; case BinaryOpcode::different: output << "!="; break; case BinaryOpcode::equal: output << "="; break; case BinaryOpcode::max: output << "max"; break; case BinaryOpcode::min: output << "min"; break; case BinaryOpcode::powerDeriv: output << "power_deriv"; break; } output << R"(", "arg1" : )"; arg1->writeJsonAST(output); output << R"(, "arg2" : )"; arg2->writeJsonAST(output); output << "}"; } void BinaryOpNode::writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, const deriv_node_temp_terms_t &tef_terms, bool isdynamic) const { // If current node is a temporary term if (temporary_terms.find(const_cast(this)) != temporary_terms.end()) { output << "T" << idx; return; } if (op_code == BinaryOpcode::max || op_code == BinaryOpcode::min) { switch (op_code) { case BinaryOpcode::max: output << "max("; break; case BinaryOpcode::min: output << "min("; break; default: ; } arg1->writeJsonOutput(output, temporary_terms, tef_terms, isdynamic); output << ","; arg2->writeJsonOutput(output, temporary_terms, tef_terms, isdynamic); output << ")"; return; } if (op_code == BinaryOpcode::powerDeriv) { output << "get_power_deriv("; arg1->writeJsonOutput(output, temporary_terms, tef_terms, isdynamic); output << ","; arg2->writeJsonOutput(output, temporary_terms, tef_terms, isdynamic); output << "," << powerDerivOrder << ")"; return; } int prec = precedenceJson(temporary_terms); bool close_parenthesis = false; // If left argument has a lower precedence, or if current and left argument are both power operators, // add parenthesis around left argument if (auto barg1 = dynamic_cast(arg1); arg1->precedenceJson(temporary_terms) < prec || (op_code == BinaryOpcode::power && barg1 && barg1->op_code == BinaryOpcode::power)) { output << "("; close_parenthesis = true; } // Write left argument arg1->writeJsonOutput(output, temporary_terms, tef_terms, isdynamic); if (close_parenthesis) output << ")"; // Write current operator symbol switch (op_code) { case BinaryOpcode::plus: output << "+"; break; case BinaryOpcode::minus: output << "-"; break; case BinaryOpcode::times: output << "*"; break; case BinaryOpcode::divide: output << "/"; break; case BinaryOpcode::power: output << "^"; break; case BinaryOpcode::less: output << "<"; break; case BinaryOpcode::greater: output << ">"; break; case BinaryOpcode::lessEqual: output << "<="; break; case BinaryOpcode::greaterEqual: output << ">="; break; case BinaryOpcode::equalEqual: output << "=="; break; case BinaryOpcode::different: output << "!="; break; case BinaryOpcode::equal: output << "="; break; default: ; } close_parenthesis = false; /* Add parenthesis around right argument if: - its precedence is lower than those of the current node - it is a power operator and current operator is also a power operator - it is a minus operator with same precedence than current operator - it is a divide operator with same precedence than current operator */ auto barg2 = dynamic_cast(arg2); if (int arg2_prec = arg2->precedenceJson(temporary_terms); arg2_prec < prec || (op_code == BinaryOpcode::power && barg2 && barg2->op_code == BinaryOpcode::power) || (op_code == BinaryOpcode::minus && arg2_prec == prec) || (op_code == BinaryOpcode::divide && arg2_prec == prec)) { output << "("; close_parenthesis = true; } // Write right argument arg2->writeJsonOutput(output, temporary_terms, tef_terms, isdynamic); if (close_parenthesis) output << ")"; } void BinaryOpNode::writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, const deriv_node_temp_terms_t &tef_terms) const { if (checkIfTemporaryTermThenWrite(output, output_type, temporary_terms, temporary_terms_idxs)) return; // Treat derivative of Power if (op_code == BinaryOpcode::powerDeriv) { if (isLatexOutput(output_type)) unpackPowerDeriv()->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); else { if (output_type == ExprNodeOutputType::juliaStaticModel || output_type == ExprNodeOutputType::juliaDynamicModel) output << "get_power_deriv("; else output << "getPowerDeriv("; arg1->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); output << ","; arg2->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); output << "," << powerDerivOrder << ")"; } return; } // Treat special case of power operator in C, and case of max and min operators if ((op_code == BinaryOpcode::power && isCOutput(output_type)) || op_code == BinaryOpcode::max || op_code == BinaryOpcode::min) { switch (op_code) { case BinaryOpcode::power: output << "pow("; break; case BinaryOpcode::max: if (isCOutput(output_type)) output << "fmax("; else output << "max("; break; case BinaryOpcode::min: if (isCOutput(output_type)) output << "fmin("; else output << "min("; break; default: ; } arg1->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); output << ","; arg2->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); output << ")"; return; } int prec = precedence(output_type, temporary_terms); bool close_parenthesis = false; if (isLatexOutput(output_type) && op_code == BinaryOpcode::divide) output << R"(\frac{)"; else { // If left argument has a lower precedence, or if current and left argument are both power operators, add parenthesis around left argument auto barg1 = dynamic_cast(arg1); if (arg1->precedence(output_type, temporary_terms) < prec || (op_code == BinaryOpcode::power && barg1 != nullptr && barg1->op_code == BinaryOpcode::power)) { output << LEFT_PAR(output_type); close_parenthesis = true; } } // Write left argument arg1->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); if (close_parenthesis) output << RIGHT_PAR(output_type); if (isLatexOutput(output_type) && op_code == BinaryOpcode::divide) output << "}"; // Write current operator symbol switch (op_code) { case BinaryOpcode::plus: output << "+"; break; case BinaryOpcode::minus: output << "-"; break; case BinaryOpcode::times: if (isLatexOutput(output_type)) output << R"(\, )"; else output << "*"; break; case BinaryOpcode::divide: if (!isLatexOutput(output_type)) output << "/"; break; case BinaryOpcode::power: output << "^"; break; case BinaryOpcode::less: output << "<"; break; case BinaryOpcode::greater: output << ">"; break; case BinaryOpcode::lessEqual: if (isLatexOutput(output_type)) output << R"(\leq )"; else output << "<="; break; case BinaryOpcode::greaterEqual: if (isLatexOutput(output_type)) output << R"(\geq )"; else output << ">="; break; case BinaryOpcode::equalEqual: output << "=="; break; case BinaryOpcode::different: if (isMatlabOutput(output_type)) output << "~="; else { if (isCOutput(output_type) || isJuliaOutput(output_type)) output << "!="; else output << R"(\neq )"; } break; case BinaryOpcode::equal: output << "="; break; default: ; } close_parenthesis = false; if (isLatexOutput(output_type) && (op_code == BinaryOpcode::power || op_code == BinaryOpcode::divide)) output << "{"; else { /* Add parenthesis around right argument if: - its precedence is lower than those of the current node - it is a power operator and current operator is also a power operator - it is a minus operator with same precedence than current operator - it is a divide operator with same precedence than current operator */ auto barg2 = dynamic_cast(arg2); if (int arg2_prec = arg2->precedence(output_type, temporary_terms); arg2_prec < prec || (op_code == BinaryOpcode::power && barg2 && barg2->op_code == BinaryOpcode::power && !isLatexOutput(output_type)) || (op_code == BinaryOpcode::minus && arg2_prec == prec) || (op_code == BinaryOpcode::divide && arg2_prec == prec && !isLatexOutput(output_type))) { output << LEFT_PAR(output_type); close_parenthesis = true; } } // Write right argument arg2->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); if (isLatexOutput(output_type) && (op_code == BinaryOpcode::power || op_code == BinaryOpcode::divide)) output << "}"; if (close_parenthesis) output << RIGHT_PAR(output_type); } void BinaryOpNode::writeExternalFunctionOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, deriv_node_temp_terms_t &tef_terms) const { arg1->writeExternalFunctionOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); arg2->writeExternalFunctionOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); } void BinaryOpNode::writeJsonExternalFunctionOutput(vector &efout, const temporary_terms_t &temporary_terms, deriv_node_temp_terms_t &tef_terms, bool isdynamic) const { arg1->writeJsonExternalFunctionOutput(efout, temporary_terms, tef_terms, isdynamic); arg2->writeJsonExternalFunctionOutput(efout, temporary_terms, tef_terms, isdynamic); } void BinaryOpNode::compileExternalFunctionOutput(ostream &CompileCode, unsigned int &instruction_number, bool lhs_rhs, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, bool dynamic, bool steady_dynamic, deriv_node_temp_terms_t &tef_terms) const { arg1->compileExternalFunctionOutput(CompileCode, instruction_number, lhs_rhs, temporary_terms, temporary_terms_idxs, dynamic, steady_dynamic, tef_terms); arg2->compileExternalFunctionOutput(CompileCode, instruction_number, lhs_rhs, temporary_terms, temporary_terms_idxs, dynamic, steady_dynamic, tef_terms); } int BinaryOpNode::VarMinLag() const { return min(arg1->VarMinLag(), arg2->VarMinLag()); } int BinaryOpNode::VarMaxLag(const set &lhs_lag_equiv) const { return max(arg1->VarMaxLag(lhs_lag_equiv), arg2->VarMaxLag(lhs_lag_equiv)); } void BinaryOpNode::collectVARLHSVariable(set &result) const { cerr << "ERROR: you can only have variables or unary ops on LHS of VAR" << endl; exit(EXIT_FAILURE); } void BinaryOpNode::collectDynamicVariables(SymbolType type_arg, set> &result) const { arg1->collectDynamicVariables(type_arg, result); arg2->collectDynamicVariables(type_arg, result); } expr_t BinaryOpNode::Compute_RHS(expr_t arg1, expr_t arg2, int op, int op_type) const { switch (op_type) { case 0: /*Unary Operator*/ switch (static_cast(op)) { case UnaryOpcode::uminus: return datatree.AddUMinus(arg1); case UnaryOpcode::exp: return datatree.AddExp(arg1); case UnaryOpcode::log: return datatree.AddLog(arg1); case UnaryOpcode::log10: return datatree.AddLog10(arg1); default: cerr << "BinaryOpNode::Compute_RHS: case not handled"; exit(EXIT_FAILURE); } break; case 1: /*Binary Operator*/ switch (static_cast(op)) { case BinaryOpcode::plus: return datatree.AddPlus(arg1, arg2); case BinaryOpcode::minus: return datatree.AddMinus(arg1, arg2); case BinaryOpcode::times: return datatree.AddTimes(arg1, arg2); case BinaryOpcode::divide: return datatree.AddDivide(arg1, arg2); case BinaryOpcode::power: return datatree.AddPower(arg1, arg2); default: cerr << "BinaryOpNode::Compute_RHS: case not handled"; exit(EXIT_FAILURE); } break; } return nullptr; } void BinaryOpNode::computeSubExprContainingVariable(int symb_id, int lag, set &contain_var) const { arg1->computeSubExprContainingVariable(symb_id, lag, contain_var); arg2->computeSubExprContainingVariable(symb_id, lag, contain_var); if (contain_var.count(arg1) > 0 || contain_var.count(arg2) > 0) contain_var.insert(const_cast(this)); } BinaryOpNode * BinaryOpNode::normalizeEquationHelper(const set &contain_var, expr_t rhs) const { assert(contain_var.count(const_cast(this)) > 0); bool arg1_contains_var = contain_var.count(arg1) > 0; bool arg2_contains_var = contain_var.count(arg2) > 0; assert(arg1_contains_var || arg2_contains_var); if (arg1_contains_var && arg2_contains_var) throw NormalizationFailed(); switch (op_code) { case BinaryOpcode::plus: if (arg1_contains_var) rhs = datatree.AddMinus(rhs, arg2); else rhs = datatree.AddMinus(rhs, arg1); break; case BinaryOpcode::minus: if (arg1_contains_var) rhs = datatree.AddPlus(rhs, arg2); else rhs = datatree.AddMinus(arg1, rhs); break; case BinaryOpcode::times: if (arg1_contains_var) rhs = datatree.AddDivide(rhs, arg2); else rhs = datatree.AddDivide(rhs, arg1); break; case BinaryOpcode::divide: if (arg1_contains_var) rhs = datatree.AddTimes(rhs, arg2); else rhs = datatree.AddDivide(arg1, rhs); break; case BinaryOpcode::power: if (arg1_contains_var) rhs = datatree.AddPower(rhs, datatree.AddDivide(datatree.One, arg2)); else // a^f(X)=rhs is normalized in f(X)=ln(rhs)/ln(a) rhs = datatree.AddDivide(datatree.AddLog(rhs), datatree.AddLog(arg1)); break; case BinaryOpcode::equal: cerr << "BinaryOpCode::normalizeEquationHelper: this case should not happen" << endl; exit(EXIT_FAILURE); default: throw NormalizationFailed(); } if (arg1_contains_var) return arg1->normalizeEquationHelper(contain_var, rhs); else return arg2->normalizeEquationHelper(contain_var, rhs); } BinaryOpNode * BinaryOpNode::normalizeEquation(int symb_id, int lag) const { assert(op_code == BinaryOpcode::equal); set contain_var; computeSubExprContainingVariable(symb_id, lag, contain_var); bool arg1_contains_var = contain_var.count(arg1) > 0; bool arg2_contains_var = contain_var.count(arg2) > 0; assert(arg1_contains_var || arg2_contains_var); if (arg1_contains_var && arg2_contains_var) throw NormalizationFailed(); return arg1_contains_var ? arg1->normalizeEquationHelper(contain_var, arg2) : arg2->normalizeEquationHelper(contain_var, arg1); } expr_t BinaryOpNode::getChainRuleDerivative(int deriv_id, const map &recursive_variables) { expr_t darg1 = arg1->getChainRuleDerivative(deriv_id, recursive_variables); expr_t darg2 = arg2->getChainRuleDerivative(deriv_id, recursive_variables); return composeDerivatives(darg1, darg2); } expr_t BinaryOpNode::buildSimilarBinaryOpNode(expr_t alt_arg1, expr_t alt_arg2, DataTree &alt_datatree) const { switch (op_code) { case BinaryOpcode::plus: return alt_datatree.AddPlus(alt_arg1, alt_arg2); case BinaryOpcode::minus: return alt_datatree.AddMinus(alt_arg1, alt_arg2); case BinaryOpcode::times: return alt_datatree.AddTimes(alt_arg1, alt_arg2); case BinaryOpcode::divide: return alt_datatree.AddDivide(alt_arg1, alt_arg2); case BinaryOpcode::power: return alt_datatree.AddPower(alt_arg1, alt_arg2); case BinaryOpcode::equal: return alt_datatree.AddEqual(alt_arg1, alt_arg2); case BinaryOpcode::max: return alt_datatree.AddMax(alt_arg1, alt_arg2); case BinaryOpcode::min: return alt_datatree.AddMin(alt_arg1, alt_arg2); case BinaryOpcode::less: return alt_datatree.AddLess(alt_arg1, alt_arg2); case BinaryOpcode::greater: return alt_datatree.AddGreater(alt_arg1, alt_arg2); case BinaryOpcode::lessEqual: return alt_datatree.AddLessEqual(alt_arg1, alt_arg2); case BinaryOpcode::greaterEqual: return alt_datatree.AddGreaterEqual(alt_arg1, alt_arg2); case BinaryOpcode::equalEqual: return alt_datatree.AddEqualEqual(alt_arg1, alt_arg2); case BinaryOpcode::different: return alt_datatree.AddDifferent(alt_arg1, alt_arg2); case BinaryOpcode::powerDeriv: return alt_datatree.AddPowerDeriv(alt_arg1, alt_arg2, powerDerivOrder); } // Suppress GCC warning exit(EXIT_FAILURE); } expr_t BinaryOpNode::toStatic(DataTree &static_datatree) const { expr_t sarg1 = arg1->toStatic(static_datatree); expr_t sarg2 = arg2->toStatic(static_datatree); return buildSimilarBinaryOpNode(sarg1, sarg2, static_datatree); } void BinaryOpNode::computeXrefs(EquationInfo &ei) const { arg1->computeXrefs(ei); arg2->computeXrefs(ei); } expr_t BinaryOpNode::clone(DataTree &datatree) const { expr_t substarg1 = arg1->clone(datatree); expr_t substarg2 = arg2->clone(datatree); return buildSimilarBinaryOpNode(substarg1, substarg2, datatree); } int BinaryOpNode::maxEndoLead() const { return max(arg1->maxEndoLead(), arg2->maxEndoLead()); } int BinaryOpNode::maxExoLead() const { return max(arg1->maxExoLead(), arg2->maxExoLead()); } int BinaryOpNode::maxEndoLag() const { return max(arg1->maxEndoLag(), arg2->maxEndoLag()); } int BinaryOpNode::maxExoLag() const { return max(arg1->maxExoLag(), arg2->maxExoLag()); } int BinaryOpNode::maxLead() const { return max(arg1->maxLead(), arg2->maxLead()); } int BinaryOpNode::maxLag() const { return max(arg1->maxLag(), arg2->maxLag()); } int BinaryOpNode::maxLagWithDiffsExpanded() const { return max(arg1->maxLagWithDiffsExpanded(), arg2->maxLagWithDiffsExpanded()); } expr_t BinaryOpNode::undiff() const { expr_t arg1subst = arg1->undiff(); expr_t arg2subst = arg2->undiff(); return buildSimilarBinaryOpNode(arg1subst, arg2subst, datatree); } expr_t BinaryOpNode::decreaseLeadsLags(int n) const { expr_t arg1subst = arg1->decreaseLeadsLags(n); expr_t arg2subst = arg2->decreaseLeadsLags(n); return buildSimilarBinaryOpNode(arg1subst, arg2subst, datatree); } expr_t BinaryOpNode::decreaseLeadsLagsPredeterminedVariables() const { expr_t arg1subst = arg1->decreaseLeadsLagsPredeterminedVariables(); expr_t arg2subst = arg2->decreaseLeadsLagsPredeterminedVariables(); return buildSimilarBinaryOpNode(arg1subst, arg2subst, datatree); } expr_t BinaryOpNode::substituteEndoLeadGreaterThanTwo(subst_table_t &subst_table, vector &neweqs, bool deterministic_model) const { expr_t arg1subst, arg2subst; int maxendolead1 = arg1->maxEndoLead(), maxendolead2 = arg2->maxEndoLead(); if (maxendolead1 < 2 && maxendolead2 < 2) return const_cast(this); if (deterministic_model) { arg1subst = maxendolead1 >= 2 ? arg1->substituteEndoLeadGreaterThanTwo(subst_table, neweqs, deterministic_model) : arg1; arg2subst = maxendolead2 >= 2 ? arg2->substituteEndoLeadGreaterThanTwo(subst_table, neweqs, deterministic_model) : arg2; return buildSimilarBinaryOpNode(arg1subst, arg2subst, datatree); } else switch (op_code) { case BinaryOpcode::plus: case BinaryOpcode::minus: case BinaryOpcode::equal: arg1subst = maxendolead1 >= 2 ? arg1->substituteEndoLeadGreaterThanTwo(subst_table, neweqs, deterministic_model) : arg1; arg2subst = maxendolead2 >= 2 ? arg2->substituteEndoLeadGreaterThanTwo(subst_table, neweqs, deterministic_model) : arg2; return buildSimilarBinaryOpNode(arg1subst, arg2subst, datatree); case BinaryOpcode::times: case BinaryOpcode::divide: if (maxendolead1 >= 2 && maxendolead2 == 0 && arg2->maxExoLead() == 0) { arg1subst = arg1->substituteEndoLeadGreaterThanTwo(subst_table, neweqs, deterministic_model); return buildSimilarBinaryOpNode(arg1subst, arg2, datatree); } if (maxendolead1 == 0 && arg1->maxExoLead() == 0 && maxendolead2 >= 2 && op_code == BinaryOpcode::times) { arg2subst = arg2->substituteEndoLeadGreaterThanTwo(subst_table, neweqs, deterministic_model); return buildSimilarBinaryOpNode(arg1, arg2subst, datatree); } return createEndoLeadAuxiliaryVarForMyself(subst_table, neweqs); default: return createEndoLeadAuxiliaryVarForMyself(subst_table, neweqs); } } expr_t BinaryOpNode::substituteEndoLagGreaterThanTwo(subst_table_t &subst_table, vector &neweqs) const { expr_t arg1subst = arg1->substituteEndoLagGreaterThanTwo(subst_table, neweqs); expr_t arg2subst = arg2->substituteEndoLagGreaterThanTwo(subst_table, neweqs); return buildSimilarBinaryOpNode(arg1subst, arg2subst, datatree); } expr_t BinaryOpNode::substituteExoLead(subst_table_t &subst_table, vector &neweqs, bool deterministic_model) const { expr_t arg1subst, arg2subst; int maxexolead1 = arg1->maxExoLead(), maxexolead2 = arg2->maxExoLead(); if (maxexolead1 < 1 && maxexolead2 < 1) return const_cast(this); if (deterministic_model) { arg1subst = maxexolead1 >= 1 ? arg1->substituteExoLead(subst_table, neweqs, deterministic_model) : arg1; arg2subst = maxexolead2 >= 1 ? arg2->substituteExoLead(subst_table, neweqs, deterministic_model) : arg2; return buildSimilarBinaryOpNode(arg1subst, arg2subst, datatree); } else switch (op_code) { case BinaryOpcode::plus: case BinaryOpcode::minus: case BinaryOpcode::equal: arg1subst = maxexolead1 >= 1 ? arg1->substituteExoLead(subst_table, neweqs, deterministic_model) : arg1; arg2subst = maxexolead2 >= 1 ? arg2->substituteExoLead(subst_table, neweqs, deterministic_model) : arg2; return buildSimilarBinaryOpNode(arg1subst, arg2subst, datatree); case BinaryOpcode::times: case BinaryOpcode::divide: if (maxexolead1 >= 1 && maxexolead2 == 0 && arg2->maxEndoLead() == 0) { arg1subst = arg1->substituteExoLead(subst_table, neweqs, deterministic_model); return buildSimilarBinaryOpNode(arg1subst, arg2, datatree); } if (maxexolead1 == 0 && arg1->maxEndoLead() == 0 && maxexolead2 >= 1 && op_code == BinaryOpcode::times) { arg2subst = arg2->substituteExoLead(subst_table, neweqs, deterministic_model); return buildSimilarBinaryOpNode(arg1, arg2subst, datatree); } return createExoLeadAuxiliaryVarForMyself(subst_table, neweqs); default: return createExoLeadAuxiliaryVarForMyself(subst_table, neweqs); } } expr_t BinaryOpNode::substituteExoLag(subst_table_t &subst_table, vector &neweqs) const { expr_t arg1subst = arg1->substituteExoLag(subst_table, neweqs); expr_t arg2subst = arg2->substituteExoLag(subst_table, neweqs); return buildSimilarBinaryOpNode(arg1subst, arg2subst, datatree); } expr_t BinaryOpNode::substituteExpectation(subst_table_t &subst_table, vector &neweqs, bool partial_information_model) const { expr_t arg1subst = arg1->substituteExpectation(subst_table, neweqs, partial_information_model); expr_t arg2subst = arg2->substituteExpectation(subst_table, neweqs, partial_information_model); return buildSimilarBinaryOpNode(arg1subst, arg2subst, datatree); } expr_t BinaryOpNode::substituteAdl() const { expr_t arg1subst = arg1->substituteAdl(); expr_t arg2subst = arg2->substituteAdl(); return buildSimilarBinaryOpNode(arg1subst, arg2subst, datatree); } expr_t BinaryOpNode::substituteModelLocalVariables() const { expr_t arg1subst = arg1->substituteModelLocalVariables(); expr_t arg2subst = arg2->substituteModelLocalVariables(); return buildSimilarBinaryOpNode(arg1subst, arg2subst, datatree); } expr_t BinaryOpNode::substituteVarExpectation(const map &subst_table) const { expr_t arg1subst = arg1->substituteVarExpectation(subst_table); expr_t arg2subst = arg2->substituteVarExpectation(subst_table); return buildSimilarBinaryOpNode(arg1subst, arg2subst, datatree); } void BinaryOpNode::findUnaryOpNodesForAuxVarCreation(lag_equivalence_table_t &nodes) const { arg1->findUnaryOpNodesForAuxVarCreation(nodes); arg2->findUnaryOpNodesForAuxVarCreation(nodes); } void BinaryOpNode::findDiffNodes(lag_equivalence_table_t &nodes) const { arg1->findDiffNodes(nodes); arg2->findDiffNodes(nodes); } expr_t BinaryOpNode::substituteDiff(const lag_equivalence_table_t &nodes, subst_table_t &subst_table, vector &neweqs) const { expr_t arg1subst = arg1->substituteDiff(nodes, subst_table, neweqs); expr_t arg2subst = arg2->substituteDiff(nodes, subst_table, neweqs); return buildSimilarBinaryOpNode(arg1subst, arg2subst, datatree); } expr_t BinaryOpNode::substituteUnaryOpNodes(const lag_equivalence_table_t &nodes, subst_table_t &subst_table, vector &neweqs) const { expr_t arg1subst = arg1->substituteUnaryOpNodes(nodes, subst_table, neweqs); expr_t arg2subst = arg2->substituteUnaryOpNodes(nodes, subst_table, neweqs); return buildSimilarBinaryOpNode(arg1subst, arg2subst, datatree); } int BinaryOpNode::countDiffs() const { return max(arg1->countDiffs(), arg2->countDiffs()); } expr_t BinaryOpNode::substitutePacExpectation(const string &name, expr_t subexpr) { expr_t arg1subst = arg1->substitutePacExpectation(name, subexpr); expr_t arg2subst = arg2->substitutePacExpectation(name, subexpr); return buildSimilarBinaryOpNode(arg1subst, arg2subst, datatree); } expr_t BinaryOpNode::differentiateForwardVars(const vector &subset, subst_table_t &subst_table, vector &neweqs) const { expr_t arg1subst = arg1->differentiateForwardVars(subset, subst_table, neweqs); expr_t arg2subst = arg2->differentiateForwardVars(subset, subst_table, neweqs); return buildSimilarBinaryOpNode(arg1subst, arg2subst, datatree); } expr_t BinaryOpNode::addMultipliersToConstraints(int i) { int symb_id = datatree.symbol_table.addMultiplierAuxiliaryVar(i); expr_t newAuxLM = datatree.AddVariable(symb_id, 0); return datatree.AddEqual(datatree.AddTimes(newAuxLM, datatree.AddMinus(arg1, arg2)), datatree.Zero); } bool BinaryOpNode::isNumConstNodeEqualTo(double value) const { return false; } bool BinaryOpNode::isVariableNodeEqualTo(SymbolType type_arg, int variable_id, int lag_arg) const { return false; } bool BinaryOpNode::containsPacExpectation(const string &pac_model_name) const { return arg1->containsPacExpectation(pac_model_name) || arg2->containsPacExpectation(pac_model_name); } expr_t BinaryOpNode::replaceTrendVar() const { expr_t arg1subst = arg1->replaceTrendVar(); expr_t arg2subst = arg2->replaceTrendVar(); return buildSimilarBinaryOpNode(arg1subst, arg2subst, datatree); } expr_t BinaryOpNode::detrend(int symb_id, bool log_trend, expr_t trend) const { expr_t arg1subst = arg1->detrend(symb_id, log_trend, trend); expr_t arg2subst = arg2->detrend(symb_id, log_trend, trend); return buildSimilarBinaryOpNode(arg1subst, arg2subst, datatree); } expr_t BinaryOpNode::removeTrendLeadLag(const map &trend_symbols_map) const { expr_t arg1subst = arg1->removeTrendLeadLag(trend_symbols_map); expr_t arg2subst = arg2->removeTrendLeadLag(trend_symbols_map); return buildSimilarBinaryOpNode(arg1subst, arg2subst, datatree); } bool BinaryOpNode::isInStaticForm() const { return arg1->isInStaticForm() && arg2->isInStaticForm(); } bool BinaryOpNode::findTargetVariableHelper1(int lhs_symb_id, int rhs_symb_id) const { if (lhs_symb_id == rhs_symb_id) return true; try { if (datatree.symbol_table.isAuxiliaryVariable(rhs_symb_id) && lhs_symb_id == datatree.symbol_table.getOrigSymbIdForAuxVar(rhs_symb_id)) return true; } catch (...) { } return false; } int BinaryOpNode::findTargetVariableHelper(const expr_t arg1, const expr_t arg2, int lhs_symb_id) const { set params; arg1->collectVariables(SymbolType::parameter, params); if (params.size() != 1) return -1; set> endogs; arg2->collectDynamicVariables(SymbolType::endogenous, endogs); if (auto testarg2 = dynamic_cast(arg2); endogs.size() == 2 && testarg2 && testarg2->op_code == BinaryOpcode::minus && dynamic_cast(testarg2->arg1) && dynamic_cast(testarg2->arg2)) { if (findTargetVariableHelper1(lhs_symb_id, endogs.begin()->first)) return endogs.rbegin()->first; else if (findTargetVariableHelper1(lhs_symb_id, endogs.rbegin()->first)) return endogs.begin()->first; } return -1; } int BinaryOpNode::findTargetVariable(int lhs_symb_id) const { int retval = findTargetVariableHelper(arg1, arg2, lhs_symb_id); if (retval < 0) retval = findTargetVariableHelper(arg2, arg1, lhs_symb_id); if (retval < 0) retval = arg1->findTargetVariable(lhs_symb_id); if (retval < 0) retval = arg2->findTargetVariable(lhs_symb_id); return retval; } void BinaryOpNode::getPacAREC(int lhs_symb_id, int lhs_orig_symb_id, pair>> &ec_params_and_vars, vector> &ar_params_and_vars, vector> &additive_vars_params_and_constants) const { ec_params_and_vars.first = -1; vector> terms; decomposeAdditiveTerms(terms, 1); for (auto it = terms.begin(); it != terms.end(); ++it) if (auto bopn = dynamic_cast(it->first); bopn) { try { auto [param_id, target_id] = bopn->matchParamTimesTargetMinusVariable(lhs_orig_symb_id); ec_params_and_vars = { param_id, { { target_id, true, 1 }, { lhs_orig_symb_id, false, -1 }}}; terms.erase(it); break; } catch (MatchFailureException &e) { } } if (ec_params_and_vars.first < 0) { cerr << "Error finding EC part of PAC equation" << endl; exit(EXIT_FAILURE); } for (const auto &it : terms) { if (dynamic_cast(it.first)) continue; pair>> m; try { m = {-1, {it.first->matchVariableTimesConstantTimesParam()}}; } catch (MatchFailureException &e) { try { m = it.first->matchParamTimesLinearCombinationOfVariables(); } catch (MatchFailureException &e) { cerr << "Unsupported expression in PAC equation" << endl; exit(EXIT_FAILURE); } } for (auto &t : m.second) get<3>(t) *= it.second; // Update sign of constants int pid = get<0>(m); for (auto [vid, vlag, pidtmp, constant] : m.second) { if (pid == -1) pid = pidtmp; else if (pidtmp >= 0) { cerr << "unexpected parameter found in PAC equation" << endl; exit(EXIT_FAILURE); } if (int vidorig = datatree.symbol_table.getUltimateOrigSymbID(vid); vidorig == lhs_symb_id || vidorig == lhs_orig_symb_id) { // This is an autoregressive term if (constant != 1 || pid == -1 || !datatree.symbol_table.isDiffAuxiliaryVariable(vid)) { cerr << "BinaryOpNode::getPacAREC: autoregressive terms must be of the form 'parameter*diff_lagged_variable" << endl; exit(EXIT_FAILURE); } int ar_lag = datatree.symbol_table.getOrigLeadLagForDiffAuxVar(vid); if (static_cast(ar_params_and_vars.size()) < ar_lag) ar_params_and_vars.resize(ar_lag, { -1, -1, 0 }); ar_params_and_vars[ar_lag-1] = { pid, vid, vlag }; } else // This is a residual additive term additive_vars_params_and_constants.emplace_back(vid, vlag, pid, constant); } } } bool BinaryOpNode::isParamTimesEndogExpr() const { if (op_code == BinaryOpcode::times) { set params; auto test_arg1 = dynamic_cast(arg1); auto test_arg2 = dynamic_cast(arg2); if (test_arg1) arg1->collectVariables(SymbolType::parameter, params); else if (test_arg2) arg2->collectVariables(SymbolType::parameter, params); else return false; if (params.size() != 1) return false; params.clear(); set> endogs, exogs; if (test_arg1) { arg2->collectDynamicVariables(SymbolType::endogenous, endogs); arg2->collectDynamicVariables(SymbolType::exogenous, exogs); arg2->collectVariables(SymbolType::parameter, params); if (params.size() == 0 && exogs.size() == 0 && endogs.size() >= 1) return true; } else { arg1->collectDynamicVariables(SymbolType::endogenous, endogs); arg1->collectDynamicVariables(SymbolType::exogenous, exogs); arg1->collectVariables(SymbolType::parameter, params); if (params.size() == 0 && exogs.size() == 0 && endogs.size() >= 1) return true; } } else if (op_code == BinaryOpcode::plus) return arg1->isParamTimesEndogExpr() || arg2->isParamTimesEndogExpr(); return false; } expr_t BinaryOpNode::getPacNonOptimizingPart(int optim_share_symb_id) const { vector> factors; decomposeMultiplicativeFactors(factors); // Search for a factor of the form 1-optim_share expr_t one_minus_optim_share = nullptr; for (auto [factor, exponent] : factors) { auto bopn = dynamic_cast(factor); if (exponent != 1 || !bopn || bopn->op_code != BinaryOpcode::minus) continue; auto arg1 = dynamic_cast(bopn->arg1); auto arg2 = dynamic_cast(bopn->arg2); if (arg1 && arg2 && arg1->eval({}) == 1 && arg2->symb_id == optim_share_symb_id) { one_minus_optim_share = factor; break; } } if (!one_minus_optim_share) return nullptr; // Construct the product formed by the other factors and return it expr_t non_optim_part = datatree.One; for (auto [factor, exponent] : factors) if (factor != one_minus_optim_share) if (exponent == 1) non_optim_part = datatree.AddTimes(non_optim_part, factor); else non_optim_part = datatree.AddDivide(non_optim_part, factor); return non_optim_part; } pair BinaryOpNode::getPacOptimizingShareAndExprNodesHelper(int lhs_symb_id, int lhs_orig_symb_id) const { int optim_param_symb_id = -1; expr_t optim_part = nullptr; set endogs; collectVariables(SymbolType::endogenous, endogs); // Test whether it contains the LHS in level if (endogs.count(lhs_orig_symb_id) > 0) { set params; if (arg1->isParamTimesEndogExpr() && !arg2->isParamTimesEndogExpr()) { optim_part = arg1; arg2->collectVariables(SymbolType::parameter, params); optim_param_symb_id = *(params.begin()); } else if (arg2->isParamTimesEndogExpr() && !arg1->isParamTimesEndogExpr()) { optim_part = arg2; arg1->collectVariables(SymbolType::parameter, params); optim_param_symb_id = *(params.begin()); } } return {optim_param_symb_id, optim_part}; } tuple BinaryOpNode::getPacOptimizingShareAndExprNodes(int lhs_symb_id, int lhs_orig_symb_id) const { vector> terms; decomposeAdditiveTerms(terms, 1); for (auto &it : terms) if (dynamic_cast(it.first)) // if the pac_expectation operator is additive in the expression // there are no optimizing shares return {-1, nullptr, nullptr, nullptr}; int optim_share; expr_t optim_part, non_optim_part, additive_part; optim_part = non_optim_part = additive_part = nullptr; for (auto it = terms.begin(); it != terms.end(); ++it) if (auto bopn = dynamic_cast(it->first); bopn) { tie(optim_share, optim_part) = bopn->getPacOptimizingShareAndExprNodesHelper(lhs_symb_id, lhs_orig_symb_id); if (optim_share >= 0 && optim_part) { terms.erase(it); break; } } if (!optim_part) return {-1, nullptr, nullptr, nullptr}; for (auto it = terms.begin(); it != terms.end(); ++it) if (auto bopn = dynamic_cast(it->first); bopn) { non_optim_part = bopn->getPacNonOptimizingPart(optim_share); if (non_optim_part) { terms.erase(it); break; } } if (!non_optim_part) return {-1, nullptr, nullptr, nullptr}; else { additive_part = datatree.Zero; for (auto it : terms) additive_part = datatree.AddPlus(additive_part, it.first); if (additive_part == datatree.Zero) additive_part = nullptr; } return {optim_share, optim_part, non_optim_part, additive_part}; } void BinaryOpNode::fillAutoregressiveRow(int eqn, const vector &lhs, map, expr_t> &AR) const { vector> terms; decomposeAdditiveTerms(terms, 1); for (const auto &it : terms) { int vid, lag, param_id; double constant; try { tie(vid, lag, param_id, constant) = it.first->matchVariableTimesConstantTimesParam(); constant *= it.second; } catch (MatchFailureException &e) { continue; } if (datatree.symbol_table.isDiffAuxiliaryVariable(vid)) { lag = -datatree.symbol_table.getOrigLeadLagForDiffAuxVar(vid); vid = datatree.symbol_table.getOrigSymbIdForDiffAuxVar(vid); } if (find(lhs.begin(), lhs.end(), vid) == lhs.end()) continue; if (AR.find({eqn, -lag, vid}) != AR.end()) { cerr << "BinaryOpNode::fillAutoregressiveRow: Error filling AR matrix: " << "lag/symb_id encountered more than once in equation" << endl; exit(EXIT_FAILURE); } if (constant != 1 || param_id == -1) { cerr << "BinaryOpNode::fillAutoregressiveRow: autoregressive terms must be of the form 'parameter*lagged_variable" << endl; exit(EXIT_FAILURE); } AR[{eqn, -lag, vid}] = datatree.AddVariable(param_id); } } void BinaryOpNode::findConstantEquations(map &table) const { if (op_code == BinaryOpcode::equal) { if (dynamic_cast(arg1) && dynamic_cast(arg2)) table[dynamic_cast(arg1)] = dynamic_cast(arg2); else if (dynamic_cast(arg2) && dynamic_cast(arg1)) table[dynamic_cast(arg2)] = dynamic_cast(arg1); } } expr_t BinaryOpNode::replaceVarsInEquation(map &table) const { if (op_code == BinaryOpcode::equal) for (auto &it : table) if ((it.first == arg1 && it.second == arg2) || (it.first == arg2 && it.second == arg1)) return const_cast(this); expr_t arg1subst = arg1->replaceVarsInEquation(table); expr_t arg2subst = arg2->replaceVarsInEquation(table); return buildSimilarBinaryOpNode(arg1subst, arg2subst, datatree); } bool BinaryOpNode::isVarModelReferenced(const string &model_info_name) const { return arg1->isVarModelReferenced(model_info_name) || arg2->isVarModelReferenced(model_info_name); } void BinaryOpNode::getEndosAndMaxLags(map &model_endos_and_lags) const { arg1->getEndosAndMaxLags(model_endos_and_lags); arg2->getEndosAndMaxLags(model_endos_and_lags); } expr_t BinaryOpNode::substituteStaticAuxiliaryVariable() const { expr_t arg1subst = arg1->substituteStaticAuxiliaryVariable(); expr_t arg2subst = arg2->substituteStaticAuxiliaryVariable(); return buildSimilarBinaryOpNode(arg1subst, arg2subst, datatree); } expr_t BinaryOpNode::substituteStaticAuxiliaryDefinition() const { expr_t arg2subst = arg2->substituteStaticAuxiliaryVariable(); return buildSimilarBinaryOpNode(arg1, arg2subst, datatree); } void BinaryOpNode::matchMatchedMoment(vector &symb_ids, vector &lags, vector &powers) const { if (op_code == BinaryOpcode::times) { arg1->matchMatchedMoment(symb_ids, lags, powers); arg2->matchMatchedMoment(symb_ids, lags, powers); } else if (op_code == BinaryOpcode::power) { if (!dynamic_cast(arg1)) throw MatchFailureException("First argument of power expression must be a variable"); auto ncn = dynamic_cast(arg2); if (!ncn) throw MatchFailureException("Second argument of power expression must be a positive integer"); double c = datatree.num_constants.getDouble(ncn->id); if (c <= 0 || round(c) != c) throw MatchFailureException("Second argument of power expression must be a positive integer"); arg1->matchMatchedMoment(symb_ids, lags, powers); powers.back() = static_cast(c); } else throw MatchFailureException("Unsupported binary operator"); } 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) : ExprNode{datatree_arg, idx_arg}, arg1{arg1_arg}, arg2{arg2_arg}, arg3{arg3_arg}, op_code{op_code_arg} { } void TrinaryOpNode::prepareForDerivation() { if (preparedForDerivation) return; preparedForDerivation = true; arg1->prepareForDerivation(); arg2->prepareForDerivation(); arg3->prepareForDerivation(); // Non-null derivatives are the union of those of the arguments // Compute set union of arg{1,2,3}->non_null_derivatives set non_null_derivatives_tmp; set_union(arg1->non_null_derivatives.begin(), arg1->non_null_derivatives.end(), arg2->non_null_derivatives.begin(), arg2->non_null_derivatives.end(), inserter(non_null_derivatives_tmp, non_null_derivatives_tmp.begin())); set_union(non_null_derivatives_tmp.begin(), non_null_derivatives_tmp.end(), arg3->non_null_derivatives.begin(), arg3->non_null_derivatives.end(), inserter(non_null_derivatives, non_null_derivatives.begin())); } expr_t TrinaryOpNode::composeDerivatives(expr_t darg1, expr_t darg2, expr_t darg3) { expr_t t11, t12, t13, t14, t15; switch (op_code) { case TrinaryOpcode::normcdf: // normal pdf is inlined in the tree expr_t y; // sqrt(2*pi) t14 = datatree.AddSqrt(datatree.AddTimes(datatree.Two, datatree.Pi)); // x - mu t12 = datatree.AddMinus(arg1, arg2); // y = (x-mu)/sigma y = datatree.AddDivide(t12, arg3); // (x-mu)^2/sigma^2 t12 = datatree.AddTimes(y, y); // -(x-mu)^2/sigma^2 t13 = datatree.AddUMinus(t12); // -((x-mu)^2/sigma^2)/2 t12 = datatree.AddDivide(t13, datatree.Two); // exp(-((x-mu)^2/sigma^2)/2) t13 = datatree.AddExp(t12); // derivative of a standardized normal // t15 = (1/sqrt(2*pi))*exp(-y^2/2) t15 = datatree.AddDivide(t13, t14); // derivatives thru x t11 = datatree.AddDivide(darg1, arg3); // derivatives thru mu t12 = datatree.AddDivide(darg2, arg3); // intermediary sum t14 = datatree.AddMinus(t11, t12); // derivatives thru sigma t11 = datatree.AddDivide(y, arg3); t12 = datatree.AddTimes(t11, darg3); //intermediary sum t11 = datatree.AddMinus(t14, t12); // total derivative: // (darg1/sigma - darg2/sigma - darg3*(x-mu)/sigma^2) * t15 // where t15 is the derivative of a standardized normal return datatree.AddTimes(t11, t15); case TrinaryOpcode::normpdf: // (x - mu) t11 = datatree.AddMinus(arg1, arg2); // (x - mu)/sigma t12 = datatree.AddDivide(t11, arg3); // darg3 * (x - mu)/sigma t11 = datatree.AddTimes(darg3, t12); // darg2 - darg1 t13 = datatree.AddMinus(darg2, darg1); // darg2 - darg1 + darg3 * (x - mu)/sigma t14 = datatree.AddPlus(t13, t11); // ((x - mu)/sigma) * (darg2 - darg1 + darg3 * (x - mu)/sigma) t11 = datatree.AddTimes(t12, t14); // ((x - mu)/sigma) * (darg2 - darg1 + darg3 * (x - mu)/sigma) - darg3 t12 = datatree.AddMinus(t11, darg3); // this / sigma t11 = datatree.AddDivide(this, arg3); // total derivative: // (this / sigma) * (((x - mu)/sigma) * (darg2 - darg1 + darg3 * (x - mu)/sigma) - darg3) return datatree.AddTimes(t11, t12); } // Suppress GCC warning exit(EXIT_FAILURE); } expr_t TrinaryOpNode::computeDerivative(int deriv_id) { expr_t darg1 = arg1->getDerivative(deriv_id); expr_t darg2 = arg2->getDerivative(deriv_id); expr_t darg3 = arg3->getDerivative(deriv_id); return composeDerivatives(darg1, darg2, darg3); } int TrinaryOpNode::precedence(ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms) const { // A temporary term behaves as a variable if (temporary_terms.find(const_cast(this)) != temporary_terms.end()) return 100; switch (op_code) { case TrinaryOpcode::normcdf: case TrinaryOpcode::normpdf: return 100; } // Suppress GCC warning exit(EXIT_FAILURE); } int TrinaryOpNode::cost(const map, temporary_terms_t> &temp_terms_map, bool is_matlab) const { // For a temporary term, the cost is null for (const auto &it : temp_terms_map) if (it.second.find(const_cast(this)) != it.second.end()) return 0; int arg_cost = arg1->cost(temp_terms_map, is_matlab) + arg2->cost(temp_terms_map, is_matlab) + arg3->cost(temp_terms_map, is_matlab); return cost(arg_cost, is_matlab); } int TrinaryOpNode::cost(const vector> &blocks_temporary_terms, bool is_matlab) const { // For a temporary term, the cost is null for (const auto &blk_tt : blocks_temporary_terms) for (const auto &eq_tt : blk_tt) if (eq_tt.find(const_cast(this)) != eq_tt.end()) return 0; int arg_cost = arg1->cost(blocks_temporary_terms, is_matlab) + arg2->cost(blocks_temporary_terms, is_matlab) + arg3->cost(blocks_temporary_terms, is_matlab); return cost(arg_cost, is_matlab); } int TrinaryOpNode::cost(int cost, bool is_matlab) const { if (is_matlab) // Cost for Matlab files switch (op_code) { case TrinaryOpcode::normcdf: case TrinaryOpcode::normpdf: return cost+1000; } else // Cost for C files switch (op_code) { case TrinaryOpcode::normcdf: case TrinaryOpcode::normpdf: return cost+1000; } // Suppress GCC warning exit(EXIT_FAILURE); } void TrinaryOpNode::computeTemporaryTerms(const pair &derivOrder, map, temporary_terms_t> &temp_terms_map, map>> &reference_count, bool is_matlab) const { expr_t this2 = const_cast(this); if (auto it = reference_count.find(this2); it == reference_count.end()) { // If this node has never been encountered, set its ref count to one, // and travel through its children reference_count[this2] = { 1, derivOrder }; arg1->computeTemporaryTerms(derivOrder, temp_terms_map, reference_count, is_matlab); arg2->computeTemporaryTerms(derivOrder, temp_terms_map, reference_count, is_matlab); arg3->computeTemporaryTerms(derivOrder, temp_terms_map, reference_count, is_matlab); } else { // If the node has already been encountered, increment its ref count // and declare it as a temporary term if it is too costly auto &[nref, min_order] = it->second; nref++; if (nref * cost(temp_terms_map, is_matlab) > min_cost(is_matlab)) temp_terms_map[min_order].insert(this2); } } void TrinaryOpNode::computeBlockTemporaryTerms(int blk, int eq, vector> &blocks_temporary_terms, map> &reference_count) const { expr_t this2 = const_cast(this); if (auto it = reference_count.find(this2); it == reference_count.end()) { reference_count[this2] = { 1, blk, eq }; arg1->computeBlockTemporaryTerms(blk, eq, blocks_temporary_terms, reference_count); arg2->computeBlockTemporaryTerms(blk, eq, blocks_temporary_terms, reference_count); arg3->computeBlockTemporaryTerms(blk, eq, blocks_temporary_terms, reference_count); } else { auto &[nref, first_blk, first_eq] = it->second; nref++; if (nref * cost(blocks_temporary_terms, false) > min_cost_c) blocks_temporary_terms[first_blk][first_eq].insert(this2); } } double TrinaryOpNode::eval_opcode(double v1, TrinaryOpcode op_code, double v2, double v3) noexcept(false) { switch (op_code) { case TrinaryOpcode::normcdf: return (0.5*(1+erf((v1-v2)/v3/M_SQRT2))); case TrinaryOpcode::normpdf: return (1/(v3*sqrt(2*M_PI)*exp(pow((v1-v2)/v3, 2)/2))); } // Suppress GCC warning exit(EXIT_FAILURE); } double TrinaryOpNode::eval(const eval_context_t &eval_context) const noexcept(false) { double v1 = arg1->eval(eval_context); double v2 = arg2->eval(eval_context); double v3 = arg3->eval(eval_context); return eval_opcode(v1, op_code, v2, v3); } void TrinaryOpNode::compile(ostream &CompileCode, unsigned int &instruction_number, bool lhs_rhs, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, bool dynamic, bool steady_dynamic, const deriv_node_temp_terms_t &tef_terms) const { // If current node is a temporary term if (auto this2 = const_cast(this); temporary_terms.find(this2) != temporary_terms.end()) { if (dynamic) { FLDT_ fldt(temporary_terms_idxs.at(this2)); fldt.write(CompileCode, instruction_number); } else { FLDST_ fldst(temporary_terms_idxs.at(this2)); fldst.write(CompileCode, instruction_number); } return; } arg1->compile(CompileCode, instruction_number, lhs_rhs, temporary_terms, temporary_terms_idxs, dynamic, steady_dynamic, tef_terms); arg2->compile(CompileCode, instruction_number, lhs_rhs, temporary_terms, temporary_terms_idxs, dynamic, steady_dynamic, tef_terms); arg3->compile(CompileCode, instruction_number, lhs_rhs, temporary_terms, temporary_terms_idxs, dynamic, steady_dynamic, tef_terms); FTRINARY_ ftrinary{static_cast(op_code)}; ftrinary.write(CompileCode, instruction_number); } bool TrinaryOpNode::containsExternalFunction() const { return arg1->containsExternalFunction() || arg2->containsExternalFunction() || arg3->containsExternalFunction(); } void TrinaryOpNode::writeJsonAST(ostream &output) const { output << R"({"node_type" : "TrinaryOpNode", )" << R"("op" : ")"; switch (op_code) { case TrinaryOpcode::normcdf: output << "normcdf"; break; case TrinaryOpcode::normpdf: output << "normpdf"; break; } output << R"(", "arg1" : )"; arg1->writeJsonAST(output); output << R"(, "arg2" : )"; arg2->writeJsonAST(output); output << R"(, "arg2" : )"; arg3->writeJsonAST(output); output << "}"; } void TrinaryOpNode::writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, const deriv_node_temp_terms_t &tef_terms, bool isdynamic) const { // If current node is a temporary term if (temporary_terms.find(const_cast(this)) != temporary_terms.end()) { output << "T" << idx; return; } switch (op_code) { case TrinaryOpcode::normcdf: output << "normcdf("; break; case TrinaryOpcode::normpdf: output << "normpdf("; break; } arg1->writeJsonOutput(output, temporary_terms, tef_terms, isdynamic); output << ","; arg2->writeJsonOutput(output, temporary_terms, tef_terms, isdynamic); output << ","; arg3->writeJsonOutput(output, temporary_terms, tef_terms, isdynamic); output << ")"; } void TrinaryOpNode::writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, const deriv_node_temp_terms_t &tef_terms) const { if (checkIfTemporaryTermThenWrite(output, output_type, temporary_terms, temporary_terms_idxs)) return; switch (op_code) { case TrinaryOpcode::normcdf: if (isCOutput(output_type)) { // In C, there is no normcdf() primitive, so use erf() output << "(0.5*(1+erf((("; arg1->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); output << ")-("; arg2->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); output << "))/("; arg3->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); output << ")/M_SQRT2)))"; } else { output << "normcdf("; arg1->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); output << ","; arg2->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); output << ","; arg3->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); output << ")"; } break; case TrinaryOpcode::normpdf: if (isCOutput(output_type)) { //(1/(v3*sqrt(2*M_PI)*exp(pow((v1-v2)/v3,2)/2))) output << "(1/("; arg3->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); output << "*sqrt(2*M_PI)*exp(pow(("; arg1->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); output << "-"; arg2->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); output << ")/"; arg3->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); output << ",2)/2)))"; } else { output << "normpdf("; arg1->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); output << ","; arg2->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); output << ","; arg3->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); output << ")"; } break; } } void TrinaryOpNode::writeExternalFunctionOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, deriv_node_temp_terms_t &tef_terms) const { arg1->writeExternalFunctionOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); arg2->writeExternalFunctionOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); arg3->writeExternalFunctionOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); } void TrinaryOpNode::writeJsonExternalFunctionOutput(vector &efout, const temporary_terms_t &temporary_terms, deriv_node_temp_terms_t &tef_terms, bool isdynamic) const { arg1->writeJsonExternalFunctionOutput(efout, temporary_terms, tef_terms, isdynamic); arg2->writeJsonExternalFunctionOutput(efout, temporary_terms, tef_terms, isdynamic); arg3->writeJsonExternalFunctionOutput(efout, temporary_terms, tef_terms, isdynamic); } void TrinaryOpNode::compileExternalFunctionOutput(ostream &CompileCode, unsigned int &instruction_number, bool lhs_rhs, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, bool dynamic, bool steady_dynamic, deriv_node_temp_terms_t &tef_terms) const { arg1->compileExternalFunctionOutput(CompileCode, instruction_number, lhs_rhs, temporary_terms, temporary_terms_idxs, dynamic, steady_dynamic, tef_terms); arg2->compileExternalFunctionOutput(CompileCode, instruction_number, lhs_rhs, temporary_terms, temporary_terms_idxs, dynamic, steady_dynamic, tef_terms); arg3->compileExternalFunctionOutput(CompileCode, instruction_number, lhs_rhs, temporary_terms, temporary_terms_idxs, dynamic, steady_dynamic, tef_terms); } void TrinaryOpNode::collectVARLHSVariable(set &result) const { cerr << "ERROR: you can only have variables or unary ops on LHS of VAR" << endl; exit(EXIT_FAILURE); } void TrinaryOpNode::collectDynamicVariables(SymbolType type_arg, set> &result) const { arg1->collectDynamicVariables(type_arg, result); arg2->collectDynamicVariables(type_arg, result); arg3->collectDynamicVariables(type_arg, result); } void TrinaryOpNode::computeSubExprContainingVariable(int symb_id, int lag, set &contain_var) const { arg1->computeSubExprContainingVariable(symb_id, lag, contain_var); arg2->computeSubExprContainingVariable(symb_id, lag, contain_var); arg3->computeSubExprContainingVariable(symb_id, lag, contain_var); if (contain_var.count(arg1) > 0 || contain_var.count(arg2) > 0 || contain_var.count(arg3) > 0) contain_var.insert(const_cast(this)); } BinaryOpNode * TrinaryOpNode::normalizeEquationHelper(const set &contain_var, expr_t rhs) const { throw NormalizationFailed(); } expr_t TrinaryOpNode::getChainRuleDerivative(int deriv_id, const map &recursive_variables) { expr_t darg1 = arg1->getChainRuleDerivative(deriv_id, recursive_variables); expr_t darg2 = arg2->getChainRuleDerivative(deriv_id, recursive_variables); expr_t darg3 = arg3->getChainRuleDerivative(deriv_id, recursive_variables); return composeDerivatives(darg1, darg2, darg3); } expr_t TrinaryOpNode::buildSimilarTrinaryOpNode(expr_t alt_arg1, expr_t alt_arg2, expr_t alt_arg3, DataTree &alt_datatree) const { switch (op_code) { case TrinaryOpcode::normcdf: return alt_datatree.AddNormcdf(alt_arg1, alt_arg2, alt_arg3); case TrinaryOpcode::normpdf: return alt_datatree.AddNormpdf(alt_arg1, alt_arg2, alt_arg3); } // Suppress GCC warning exit(EXIT_FAILURE); } expr_t TrinaryOpNode::toStatic(DataTree &static_datatree) const { expr_t sarg1 = arg1->toStatic(static_datatree); expr_t sarg2 = arg2->toStatic(static_datatree); expr_t sarg3 = arg3->toStatic(static_datatree); return buildSimilarTrinaryOpNode(sarg1, sarg2, sarg3, static_datatree); } void TrinaryOpNode::computeXrefs(EquationInfo &ei) const { arg1->computeXrefs(ei); arg2->computeXrefs(ei); arg3->computeXrefs(ei); } expr_t TrinaryOpNode::clone(DataTree &datatree) const { expr_t substarg1 = arg1->clone(datatree); expr_t substarg2 = arg2->clone(datatree); expr_t substarg3 = arg3->clone(datatree); return buildSimilarTrinaryOpNode(substarg1, substarg2, substarg3, datatree); } int TrinaryOpNode::maxEndoLead() const { return max(arg1->maxEndoLead(), max(arg2->maxEndoLead(), arg3->maxEndoLead())); } int TrinaryOpNode::maxExoLead() const { return max(arg1->maxExoLead(), max(arg2->maxExoLead(), arg3->maxExoLead())); } int TrinaryOpNode::maxEndoLag() const { return max(arg1->maxEndoLag(), max(arg2->maxEndoLag(), arg3->maxEndoLag())); } int TrinaryOpNode::maxExoLag() const { return max(arg1->maxExoLag(), max(arg2->maxExoLag(), arg3->maxExoLag())); } int TrinaryOpNode::maxLead() const { return max(arg1->maxLead(), max(arg2->maxLead(), arg3->maxLead())); } int TrinaryOpNode::maxLag() const { return max(arg1->maxLag(), max(arg2->maxLag(), arg3->maxLag())); } int TrinaryOpNode::maxLagWithDiffsExpanded() const { return max(arg1->maxLagWithDiffsExpanded(), max(arg2->maxLagWithDiffsExpanded(), arg3->maxLagWithDiffsExpanded())); } expr_t TrinaryOpNode::undiff() const { expr_t arg1subst = arg1->undiff(); expr_t arg2subst = arg2->undiff(); expr_t arg3subst = arg3->undiff(); return buildSimilarTrinaryOpNode(arg1subst, arg2subst, arg3subst, datatree); } int TrinaryOpNode::VarMinLag() const { return min(min(arg1->VarMinLag(), arg2->VarMinLag()), arg3->VarMinLag()); } int TrinaryOpNode::VarMaxLag(const set &lhs_lag_equiv) const { return max(arg1->VarMaxLag(lhs_lag_equiv), max(arg2->VarMaxLag(lhs_lag_equiv), arg3->VarMaxLag(lhs_lag_equiv))); } expr_t TrinaryOpNode::decreaseLeadsLags(int n) const { expr_t arg1subst = arg1->decreaseLeadsLags(n); expr_t arg2subst = arg2->decreaseLeadsLags(n); expr_t arg3subst = arg3->decreaseLeadsLags(n); return buildSimilarTrinaryOpNode(arg1subst, arg2subst, arg3subst, datatree); } expr_t TrinaryOpNode::decreaseLeadsLagsPredeterminedVariables() const { expr_t arg1subst = arg1->decreaseLeadsLagsPredeterminedVariables(); expr_t arg2subst = arg2->decreaseLeadsLagsPredeterminedVariables(); expr_t arg3subst = arg3->decreaseLeadsLagsPredeterminedVariables(); return buildSimilarTrinaryOpNode(arg1subst, arg2subst, arg3subst, datatree); } expr_t TrinaryOpNode::substituteEndoLeadGreaterThanTwo(subst_table_t &subst_table, vector &neweqs, bool deterministic_model) const { if (maxEndoLead() < 2) return const_cast(this); else if (deterministic_model) { expr_t arg1subst = arg1->substituteEndoLeadGreaterThanTwo(subst_table, neweqs, deterministic_model); expr_t arg2subst = arg2->substituteEndoLeadGreaterThanTwo(subst_table, neweqs, deterministic_model); expr_t arg3subst = arg3->substituteEndoLeadGreaterThanTwo(subst_table, neweqs, deterministic_model); return buildSimilarTrinaryOpNode(arg1subst, arg2subst, arg3subst, datatree); } else return createEndoLeadAuxiliaryVarForMyself(subst_table, neweqs); } expr_t TrinaryOpNode::substituteEndoLagGreaterThanTwo(subst_table_t &subst_table, vector &neweqs) const { expr_t arg1subst = arg1->substituteEndoLagGreaterThanTwo(subst_table, neweqs); expr_t arg2subst = arg2->substituteEndoLagGreaterThanTwo(subst_table, neweqs); expr_t arg3subst = arg3->substituteEndoLagGreaterThanTwo(subst_table, neweqs); return buildSimilarTrinaryOpNode(arg1subst, arg2subst, arg3subst, datatree); } expr_t TrinaryOpNode::substituteExoLead(subst_table_t &subst_table, vector &neweqs, bool deterministic_model) const { if (maxExoLead() == 0) return const_cast(this); else if (deterministic_model) { expr_t arg1subst = arg1->substituteExoLead(subst_table, neweqs, deterministic_model); expr_t arg2subst = arg2->substituteExoLead(subst_table, neweqs, deterministic_model); expr_t arg3subst = arg3->substituteExoLead(subst_table, neweqs, deterministic_model); return buildSimilarTrinaryOpNode(arg1subst, arg2subst, arg3subst, datatree); } else return createExoLeadAuxiliaryVarForMyself(subst_table, neweqs); } expr_t TrinaryOpNode::substituteExoLag(subst_table_t &subst_table, vector &neweqs) const { expr_t arg1subst = arg1->substituteExoLag(subst_table, neweqs); expr_t arg2subst = arg2->substituteExoLag(subst_table, neweqs); expr_t arg3subst = arg3->substituteExoLag(subst_table, neweqs); return buildSimilarTrinaryOpNode(arg1subst, arg2subst, arg3subst, datatree); } expr_t TrinaryOpNode::substituteExpectation(subst_table_t &subst_table, vector &neweqs, bool partial_information_model) const { expr_t arg1subst = arg1->substituteExpectation(subst_table, neweqs, partial_information_model); expr_t arg2subst = arg2->substituteExpectation(subst_table, neweqs, partial_information_model); expr_t arg3subst = arg3->substituteExpectation(subst_table, neweqs, partial_information_model); return buildSimilarTrinaryOpNode(arg1subst, arg2subst, arg3subst, datatree); } expr_t TrinaryOpNode::substituteAdl() const { expr_t arg1subst = arg1->substituteAdl(); expr_t arg2subst = arg2->substituteAdl(); expr_t arg3subst = arg3->substituteAdl(); return buildSimilarTrinaryOpNode(arg1subst, arg2subst, arg3subst, datatree); } expr_t TrinaryOpNode::substituteModelLocalVariables() const { expr_t arg1subst = arg1->substituteModelLocalVariables(); expr_t arg2subst = arg2->substituteModelLocalVariables(); expr_t arg3subst = arg3->substituteModelLocalVariables(); return buildSimilarTrinaryOpNode(arg1subst, arg2subst, arg3subst, datatree); } expr_t TrinaryOpNode::substituteVarExpectation(const map &subst_table) const { expr_t arg1subst = arg1->substituteVarExpectation(subst_table); expr_t arg2subst = arg2->substituteVarExpectation(subst_table); expr_t arg3subst = arg3->substituteVarExpectation(subst_table); return buildSimilarTrinaryOpNode(arg1subst, arg2subst, arg3subst, datatree); } void TrinaryOpNode::findDiffNodes(lag_equivalence_table_t &nodes) const { arg1->findDiffNodes(nodes); arg2->findDiffNodes(nodes); arg3->findDiffNodes(nodes); } void TrinaryOpNode::findUnaryOpNodesForAuxVarCreation(lag_equivalence_table_t &nodes) const { arg1->findUnaryOpNodesForAuxVarCreation(nodes); arg2->findUnaryOpNodesForAuxVarCreation(nodes); arg3->findUnaryOpNodesForAuxVarCreation(nodes); } int TrinaryOpNode::findTargetVariable(int lhs_symb_id) const { int retval = arg1->findTargetVariable(lhs_symb_id); if (retval < 0) retval = arg2->findTargetVariable(lhs_symb_id); if (retval < 0) retval = arg3->findTargetVariable(lhs_symb_id); return retval; } expr_t TrinaryOpNode::substituteDiff(const lag_equivalence_table_t &nodes, subst_table_t &subst_table, vector &neweqs) const { expr_t arg1subst = arg1->substituteDiff(nodes, subst_table, neweqs); expr_t arg2subst = arg2->substituteDiff(nodes, subst_table, neweqs); expr_t arg3subst = arg3->substituteDiff(nodes, subst_table, neweqs); return buildSimilarTrinaryOpNode(arg1subst, arg2subst, arg3subst, datatree); } expr_t TrinaryOpNode::substituteUnaryOpNodes(const lag_equivalence_table_t &nodes, subst_table_t &subst_table, vector &neweqs) const { expr_t arg1subst = arg1->substituteUnaryOpNodes(nodes, subst_table, neweqs); expr_t arg2subst = arg2->substituteUnaryOpNodes(nodes, subst_table, neweqs); expr_t arg3subst = arg3->substituteUnaryOpNodes(nodes, subst_table, neweqs); return buildSimilarTrinaryOpNode(arg1subst, arg2subst, arg3subst, datatree); } int TrinaryOpNode::countDiffs() const { return max(arg1->countDiffs(), max(arg2->countDiffs(), arg3->countDiffs())); } expr_t TrinaryOpNode::substitutePacExpectation(const string &name, expr_t subexpr) { expr_t arg1subst = arg1->substitutePacExpectation(name, subexpr); expr_t arg2subst = arg2->substitutePacExpectation(name, subexpr); expr_t arg3subst = arg3->substitutePacExpectation(name, subexpr); return buildSimilarTrinaryOpNode(arg1subst, arg2subst, arg3subst, datatree); } expr_t TrinaryOpNode::differentiateForwardVars(const vector &subset, subst_table_t &subst_table, vector &neweqs) const { expr_t arg1subst = arg1->differentiateForwardVars(subset, subst_table, neweqs); expr_t arg2subst = arg2->differentiateForwardVars(subset, subst_table, neweqs); expr_t arg3subst = arg3->differentiateForwardVars(subset, subst_table, neweqs); return buildSimilarTrinaryOpNode(arg1subst, arg2subst, arg3subst, datatree); } bool TrinaryOpNode::isNumConstNodeEqualTo(double value) const { return false; } bool TrinaryOpNode::isVariableNodeEqualTo(SymbolType type_arg, int variable_id, int lag_arg) const { return false; } bool TrinaryOpNode::containsPacExpectation(const string &pac_model_name) const { return (arg1->containsPacExpectation(pac_model_name) || arg2->containsPacExpectation(pac_model_name) || arg3->containsPacExpectation(pac_model_name)); } expr_t TrinaryOpNode::replaceTrendVar() const { expr_t arg1subst = arg1->replaceTrendVar(); expr_t arg2subst = arg2->replaceTrendVar(); expr_t arg3subst = arg3->replaceTrendVar(); return buildSimilarTrinaryOpNode(arg1subst, arg2subst, arg3subst, datatree); } expr_t TrinaryOpNode::detrend(int symb_id, bool log_trend, expr_t trend) const { expr_t arg1subst = arg1->detrend(symb_id, log_trend, trend); expr_t arg2subst = arg2->detrend(symb_id, log_trend, trend); expr_t arg3subst = arg3->detrend(symb_id, log_trend, trend); return buildSimilarTrinaryOpNode(arg1subst, arg2subst, arg3subst, datatree); } expr_t TrinaryOpNode::removeTrendLeadLag(const map &trend_symbols_map) const { expr_t arg1subst = arg1->removeTrendLeadLag(trend_symbols_map); expr_t arg2subst = arg2->removeTrendLeadLag(trend_symbols_map); expr_t arg3subst = arg3->removeTrendLeadLag(trend_symbols_map); return buildSimilarTrinaryOpNode(arg1subst, arg2subst, arg3subst, datatree); } bool TrinaryOpNode::isInStaticForm() const { return arg1->isInStaticForm() && arg2->isInStaticForm() && arg3->isInStaticForm(); } bool TrinaryOpNode::isParamTimesEndogExpr() const { return arg1->isParamTimesEndogExpr() || arg2->isParamTimesEndogExpr() || arg3->isParamTimesEndogExpr(); } bool TrinaryOpNode::isVarModelReferenced(const string &model_info_name) const { return arg1->isVarModelReferenced(model_info_name) || arg2->isVarModelReferenced(model_info_name) || arg3->isVarModelReferenced(model_info_name); } void TrinaryOpNode::getEndosAndMaxLags(map &model_endos_and_lags) const { arg1->getEndosAndMaxLags(model_endos_and_lags); arg2->getEndosAndMaxLags(model_endos_and_lags); arg3->getEndosAndMaxLags(model_endos_and_lags); } expr_t TrinaryOpNode::substituteStaticAuxiliaryVariable() const { expr_t arg1subst = arg1->substituteStaticAuxiliaryVariable(); expr_t arg2subst = arg2->substituteStaticAuxiliaryVariable(); expr_t arg3subst = arg3->substituteStaticAuxiliaryVariable(); return buildSimilarTrinaryOpNode(arg1subst, arg2subst, arg3subst, datatree); } expr_t TrinaryOpNode::replaceVarsInEquation(map &table) const { expr_t arg1subst = arg1->replaceVarsInEquation(table); expr_t arg2subst = arg2->replaceVarsInEquation(table); expr_t arg3subst = arg3->replaceVarsInEquation(table); return buildSimilarTrinaryOpNode(arg1subst, arg2subst, arg3subst, datatree); } AbstractExternalFunctionNode::AbstractExternalFunctionNode(DataTree &datatree_arg, int idx_arg, int symb_id_arg, vector arguments_arg) : ExprNode{datatree_arg, idx_arg}, symb_id{symb_id_arg}, arguments{move(arguments_arg)} { } void AbstractExternalFunctionNode::prepareForDerivation() { if (preparedForDerivation) return; for (auto argument : arguments) argument->prepareForDerivation(); non_null_derivatives = arguments.at(0)->non_null_derivatives; for (int i = 1; i < static_cast(arguments.size()); i++) set_union(non_null_derivatives.begin(), non_null_derivatives.end(), arguments.at(i)->non_null_derivatives.begin(), arguments.at(i)->non_null_derivatives.end(), inserter(non_null_derivatives, non_null_derivatives.begin())); preparedForDerivation = true; } expr_t AbstractExternalFunctionNode::computeDerivative(int deriv_id) { assert(datatree.external_functions_table.getNargs(symb_id) > 0); vector dargs; for (auto argument : arguments) dargs.push_back(argument->getDerivative(deriv_id)); return composeDerivatives(dargs); } expr_t AbstractExternalFunctionNode::getChainRuleDerivative(int deriv_id, const map &recursive_variables) { assert(datatree.external_functions_table.getNargs(symb_id) > 0); vector dargs; for (auto argument : arguments) dargs.push_back(argument->getChainRuleDerivative(deriv_id, recursive_variables)); return composeDerivatives(dargs); } unsigned int AbstractExternalFunctionNode::compileExternalFunctionArguments(ostream &CompileCode, unsigned int &instruction_number, bool lhs_rhs, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, bool dynamic, bool steady_dynamic, const deriv_node_temp_terms_t &tef_terms) const { for (auto argument : arguments) argument->compile(CompileCode, instruction_number, lhs_rhs, temporary_terms, temporary_terms_idxs, dynamic, steady_dynamic, tef_terms); return (arguments.size()); } void AbstractExternalFunctionNode::collectVARLHSVariable(set &result) const { cerr << "ERROR: you can only have variables or unary ops on LHS of VAR" << endl; exit(EXIT_FAILURE); } void AbstractExternalFunctionNode::collectDynamicVariables(SymbolType type_arg, set> &result) const { for (auto argument : arguments) argument->collectDynamicVariables(type_arg, result); } double AbstractExternalFunctionNode::eval(const eval_context_t &eval_context) const noexcept(false) { throw EvalExternalFunctionException(); } int AbstractExternalFunctionNode::maxEndoLead() const { int val = 0; for (auto argument : arguments) val = max(val, argument->maxEndoLead()); return val; } int AbstractExternalFunctionNode::maxExoLead() const { int val = 0; for (auto argument : arguments) val = max(val, argument->maxExoLead()); return val; } int AbstractExternalFunctionNode::maxEndoLag() const { int val = 0; for (auto argument : arguments) val = max(val, argument->maxEndoLag()); return val; } int AbstractExternalFunctionNode::maxExoLag() const { int val = 0; for (auto argument : arguments) val = max(val, argument->maxExoLag()); return val; } int AbstractExternalFunctionNode::maxLead() const { int val = 0; for (auto argument : arguments) val = max(val, argument->maxLead()); return val; } int AbstractExternalFunctionNode::maxLag() const { int val = 0; for (auto argument : arguments) val = max(val, argument->maxLag()); return val; } int AbstractExternalFunctionNode::maxLagWithDiffsExpanded() const { int val = 0; for (auto argument : arguments) val = max(val, argument->maxLagWithDiffsExpanded()); return val; } expr_t AbstractExternalFunctionNode::undiff() const { vector arguments_subst; for (auto argument : arguments) arguments_subst.push_back(argument->undiff()); return buildSimilarExternalFunctionNode(arguments_subst, datatree); } int AbstractExternalFunctionNode::VarMinLag() const { int val = 0; for (auto argument : arguments) val = min(val, argument->VarMinLag()); return val; } int AbstractExternalFunctionNode::VarMaxLag(const set &lhs_lag_equiv) const { int max_lag = 0; for (auto argument : arguments) max_lag = max(max_lag, argument->VarMaxLag(lhs_lag_equiv)); return max_lag; } expr_t AbstractExternalFunctionNode::decreaseLeadsLags(int n) const { vector arguments_subst; for (auto argument : arguments) arguments_subst.push_back(argument->decreaseLeadsLags(n)); return buildSimilarExternalFunctionNode(arguments_subst, datatree); } expr_t AbstractExternalFunctionNode::decreaseLeadsLagsPredeterminedVariables() const { vector arguments_subst; for (auto argument : arguments) arguments_subst.push_back(argument->decreaseLeadsLagsPredeterminedVariables()); return buildSimilarExternalFunctionNode(arguments_subst, datatree); } expr_t AbstractExternalFunctionNode::substituteEndoLeadGreaterThanTwo(subst_table_t &subst_table, vector &neweqs, bool deterministic_model) const { vector arguments_subst; for (auto argument : arguments) arguments_subst.push_back(argument->substituteEndoLeadGreaterThanTwo(subst_table, neweqs, deterministic_model)); return buildSimilarExternalFunctionNode(arguments_subst, datatree); } expr_t AbstractExternalFunctionNode::substituteEndoLagGreaterThanTwo(subst_table_t &subst_table, vector &neweqs) const { vector arguments_subst; for (auto argument : arguments) arguments_subst.push_back(argument->substituteEndoLagGreaterThanTwo(subst_table, neweqs)); return buildSimilarExternalFunctionNode(arguments_subst, datatree); } expr_t AbstractExternalFunctionNode::substituteExoLead(subst_table_t &subst_table, vector &neweqs, bool deterministic_model) const { vector arguments_subst; for (auto argument : arguments) arguments_subst.push_back(argument->substituteExoLead(subst_table, neweqs, deterministic_model)); return buildSimilarExternalFunctionNode(arguments_subst, datatree); } expr_t AbstractExternalFunctionNode::substituteExoLag(subst_table_t &subst_table, vector &neweqs) const { vector arguments_subst; for (auto argument : arguments) arguments_subst.push_back(argument->substituteExoLag(subst_table, neweqs)); return buildSimilarExternalFunctionNode(arguments_subst, datatree); } expr_t AbstractExternalFunctionNode::substituteExpectation(subst_table_t &subst_table, vector &neweqs, bool partial_information_model) const { vector arguments_subst; for (auto argument : arguments) arguments_subst.push_back(argument->substituteExpectation(subst_table, neweqs, partial_information_model)); return buildSimilarExternalFunctionNode(arguments_subst, datatree); } expr_t AbstractExternalFunctionNode::substituteAdl() const { vector arguments_subst; for (auto argument : arguments) arguments_subst.push_back(argument->substituteAdl()); return buildSimilarExternalFunctionNode(arguments_subst, datatree); } expr_t AbstractExternalFunctionNode::substituteModelLocalVariables() const { vector arguments_subst; for (auto argument : arguments) arguments_subst.push_back(argument->substituteModelLocalVariables()); return buildSimilarExternalFunctionNode(arguments_subst, datatree); } expr_t AbstractExternalFunctionNode::substituteVarExpectation(const map &subst_table) const { vector arguments_subst; for (auto argument : arguments) arguments_subst.push_back(argument->substituteVarExpectation(subst_table)); return buildSimilarExternalFunctionNode(arguments_subst, datatree); } void AbstractExternalFunctionNode::findDiffNodes(lag_equivalence_table_t &nodes) const { for (auto argument : arguments) argument->findDiffNodes(nodes); } void AbstractExternalFunctionNode::findUnaryOpNodesForAuxVarCreation(lag_equivalence_table_t &nodes) const { for (auto argument : arguments) argument->findUnaryOpNodesForAuxVarCreation(nodes); } int AbstractExternalFunctionNode::findTargetVariable(int lhs_symb_id) const { for (auto argument : arguments) if (int retval = argument->findTargetVariable(lhs_symb_id); retval >= 0) return retval; return -1; } expr_t AbstractExternalFunctionNode::substituteDiff(const lag_equivalence_table_t &nodes, subst_table_t &subst_table, vector &neweqs) const { vector arguments_subst; for (auto argument : arguments) arguments_subst.push_back(argument->substituteDiff(nodes, subst_table, neweqs)); return buildSimilarExternalFunctionNode(arguments_subst, datatree); } expr_t AbstractExternalFunctionNode::substituteUnaryOpNodes(const lag_equivalence_table_t &nodes, subst_table_t &subst_table, vector &neweqs) const { vector arguments_subst; for (auto argument : arguments) arguments_subst.push_back(argument->substituteUnaryOpNodes(nodes, subst_table, neweqs)); return buildSimilarExternalFunctionNode(arguments_subst, datatree); } int AbstractExternalFunctionNode::countDiffs() const { int ndiffs = 0; for (auto argument : arguments) ndiffs = max(ndiffs, argument->countDiffs()); return ndiffs; } expr_t AbstractExternalFunctionNode::substitutePacExpectation(const string &name, expr_t subexpr) { vector arguments_subst; for (auto argument : arguments) arguments_subst.push_back(argument->substitutePacExpectation(name, subexpr)); return buildSimilarExternalFunctionNode(arguments_subst, datatree); } expr_t AbstractExternalFunctionNode::differentiateForwardVars(const vector &subset, subst_table_t &subst_table, vector &neweqs) const { vector arguments_subst; for (auto argument : arguments) arguments_subst.push_back(argument->differentiateForwardVars(subset, subst_table, neweqs)); return buildSimilarExternalFunctionNode(arguments_subst, datatree); } bool AbstractExternalFunctionNode::alreadyWrittenAsTefTerm(int the_symb_id, const deriv_node_temp_terms_t &tef_terms) const { if (tef_terms.find({ the_symb_id, arguments }) != tef_terms.end()) return true; return false; } int AbstractExternalFunctionNode::getIndxInTefTerms(int the_symb_id, const deriv_node_temp_terms_t &tef_terms) const noexcept(false) { if (auto it = tef_terms.find({ the_symb_id, arguments }); it != tef_terms.end()) return it->second; throw UnknownFunctionNameAndArgs(); } void AbstractExternalFunctionNode::computeTemporaryTerms(const pair &derivOrder, map, temporary_terms_t> &temp_terms_map, map>> &reference_count, bool is_matlab) const { /* All external function nodes are declared as temporary terms. Given that temporary terms are separated in several functions (residuals, jacobian, …), we must make sure that all temporary terms derived from a given external function call are assigned just after that call. As a consequence, we need to “promote” some terms to a previous level (in the sense that residuals come before jacobian), if a temporary term corresponding to the same external function call is present in that previous level. */ expr_t this2 = const_cast(this); for (auto &tt : temp_terms_map) if (auto it = find_if(tt.second.cbegin(), tt.second.cend(), sameTefTermPredicate()); it != tt.second.cend()) { tt.second.insert(this2); return; } temp_terms_map[derivOrder].insert(this2); } void AbstractExternalFunctionNode::computeBlockTemporaryTerms(int blk, int eq, vector> &blocks_temporary_terms, map> &reference_count) const { // See comments in computeTemporaryTerms() for the logic expr_t this2 = const_cast(this); for (auto &btt : blocks_temporary_terms) for (auto &tt : btt) if (auto it = find_if(tt.cbegin(), tt.cend(), sameTefTermPredicate()); it != tt.cend()) { tt.insert(this2); return; } blocks_temporary_terms[blk][eq].insert(this2); } bool AbstractExternalFunctionNode::isNumConstNodeEqualTo(double value) const { return false; } bool AbstractExternalFunctionNode::isVariableNodeEqualTo(SymbolType type_arg, int variable_id, int lag_arg) const { return false; } bool AbstractExternalFunctionNode::containsPacExpectation(const string &pac_model_name) const { for (auto argument : arguments) if (argument->containsPacExpectation(pac_model_name)) return true; return false; } expr_t AbstractExternalFunctionNode::replaceTrendVar() const { vector arguments_subst; for (auto argument : arguments) arguments_subst.push_back(argument->replaceTrendVar()); return buildSimilarExternalFunctionNode(arguments_subst, datatree); } expr_t AbstractExternalFunctionNode::detrend(int symb_id, bool log_trend, expr_t trend) const { vector arguments_subst; for (auto argument : arguments) arguments_subst.push_back(argument->detrend(symb_id, log_trend, trend)); return buildSimilarExternalFunctionNode(arguments_subst, datatree); } expr_t AbstractExternalFunctionNode::removeTrendLeadLag(const map &trend_symbols_map) const { vector arguments_subst; for (auto argument : arguments) arguments_subst.push_back(argument->removeTrendLeadLag(trend_symbols_map)); return buildSimilarExternalFunctionNode(arguments_subst, datatree); } bool AbstractExternalFunctionNode::isInStaticForm() const { for (auto argument : arguments) if (!argument->isInStaticForm()) return false; return true; } bool AbstractExternalFunctionNode::isParamTimesEndogExpr() const { return false; } bool AbstractExternalFunctionNode::isVarModelReferenced(const string &model_info_name) const { for (auto argument : arguments) if (!argument->isVarModelReferenced(model_info_name)) return true; return false; } void AbstractExternalFunctionNode::getEndosAndMaxLags(map &model_endos_and_lags) const { for (auto argument : arguments) argument->getEndosAndMaxLags(model_endos_and_lags); } void AbstractExternalFunctionNode::computeSubExprContainingVariable(int symb_id, int lag, set &contain_var) const { bool var_present = false; for (auto arg : arguments) { arg->computeSubExprContainingVariable(symb_id, lag, contain_var); var_present = var_present || contain_var.count(arg) > 0; } if (var_present) contain_var.insert(const_cast(this)); } BinaryOpNode * AbstractExternalFunctionNode::normalizeEquationHelper(const set &contain_var, expr_t rhs) const { throw NormalizationFailed(); } void AbstractExternalFunctionNode::writeExternalFunctionArguments(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, const deriv_node_temp_terms_t &tef_terms) const { for (auto it = arguments.begin(); it != arguments.end(); ++it) { if (it != arguments.begin()) output << ","; (*it)->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); } } void AbstractExternalFunctionNode::writeJsonASTExternalFunctionArguments(ostream &output) const { int i = 0; output << "{"; for (auto it = arguments.begin(); it != arguments.end(); ++it, i++) { if (it != arguments.begin()) output << ","; output << R"("arg)" << i << R"(" : )"; (*it)->writeJsonAST(output); } output << "}"; } void AbstractExternalFunctionNode::writeJsonExternalFunctionArguments(ostream &output, const temporary_terms_t &temporary_terms, const deriv_node_temp_terms_t &tef_terms, bool isdynamic) const { for (auto it = arguments.begin(); it != arguments.end(); ++it) { if (it != arguments.begin()) output << ","; (*it)->writeJsonOutput(output, temporary_terms, tef_terms, isdynamic); } } void AbstractExternalFunctionNode::writePrhs(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, const deriv_node_temp_terms_t &tef_terms) const { int i = 0; for (auto argument : arguments) { output << " prhs[" << i++ << "] = mxCreateDoubleScalar("; // All external_function arguments are scalars argument->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); output << ");" << endl; } } bool AbstractExternalFunctionNode::containsExternalFunction() const { return true; } expr_t AbstractExternalFunctionNode::substituteStaticAuxiliaryVariable() const { vector arguments_subst; for (auto argument : arguments) arguments_subst.push_back(argument->substituteStaticAuxiliaryVariable()); return buildSimilarExternalFunctionNode(arguments_subst, datatree); } expr_t AbstractExternalFunctionNode::replaceVarsInEquation(map &table) const { vector arguments_subst; for (auto argument : arguments) arguments_subst.push_back(argument->replaceVarsInEquation(table)); return buildSimilarExternalFunctionNode(arguments_subst, datatree); } ExternalFunctionNode::ExternalFunctionNode(DataTree &datatree_arg, int idx_arg, int symb_id_arg, const vector &arguments_arg) : AbstractExternalFunctionNode{datatree_arg, idx_arg, symb_id_arg, arguments_arg} { } expr_t ExternalFunctionNode::composeDerivatives(const vector &dargs) { vector dNodes; for (int i = 0; i < static_cast(dargs.size()); i++) dNodes.push_back(datatree.AddTimes(dargs.at(i), datatree.AddFirstDerivExternalFunction(symb_id, arguments, i+1))); expr_t theDeriv = datatree.Zero; for (auto &dNode : dNodes) theDeriv = datatree.AddPlus(theDeriv, dNode); return theDeriv; } void ExternalFunctionNode::compile(ostream &CompileCode, unsigned int &instruction_number, bool lhs_rhs, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, bool dynamic, bool steady_dynamic, const deriv_node_temp_terms_t &tef_terms) const { if (auto this2 = const_cast(this); temporary_terms.find(this2) != temporary_terms.end()) { if (dynamic) { FLDT_ fldt(temporary_terms_idxs.at(this2)); fldt.write(CompileCode, instruction_number); } else { FLDST_ fldst(temporary_terms_idxs.at(this2)); fldst.write(CompileCode, instruction_number); } return; } if (!lhs_rhs) { FLDTEF_ fldtef(getIndxInTefTerms(symb_id, tef_terms)); fldtef.write(CompileCode, instruction_number); } else { FSTPTEF_ fstptef(getIndxInTefTerms(symb_id, tef_terms)); fstptef.write(CompileCode, instruction_number); } } void ExternalFunctionNode::compileExternalFunctionOutput(ostream &CompileCode, unsigned int &instruction_number, bool lhs_rhs, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, bool dynamic, bool steady_dynamic, deriv_node_temp_terms_t &tef_terms) const { int first_deriv_symb_id = datatree.external_functions_table.getFirstDerivSymbID(symb_id); assert(first_deriv_symb_id != ExternalFunctionsTable::IDSetButNoNameProvided); for (auto argument : arguments) argument->compileExternalFunctionOutput(CompileCode, instruction_number, lhs_rhs, temporary_terms, temporary_terms_idxs, dynamic, steady_dynamic, tef_terms); if (!alreadyWrittenAsTefTerm(symb_id, tef_terms)) { tef_terms[{ symb_id, arguments }] = static_cast(tef_terms.size()); int indx = getIndxInTefTerms(symb_id, tef_terms); int second_deriv_symb_id = datatree.external_functions_table.getSecondDerivSymbID(symb_id); assert(second_deriv_symb_id != ExternalFunctionsTable::IDSetButNoNameProvided); unsigned int nb_output_arguments = 0; if (symb_id == first_deriv_symb_id && symb_id == second_deriv_symb_id) nb_output_arguments = 3; else if (symb_id == first_deriv_symb_id) nb_output_arguments = 2; else nb_output_arguments = 1; unsigned int nb_input_arguments = compileExternalFunctionArguments(CompileCode, instruction_number, lhs_rhs, temporary_terms, temporary_terms_idxs, dynamic, steady_dynamic, tef_terms); FCALL_ fcall(nb_output_arguments, nb_input_arguments, datatree.symbol_table.getName(symb_id), indx); switch (nb_output_arguments) { case 1: fcall.set_function_type(ExternalFunctionType::withoutDerivative); break; case 2: fcall.set_function_type(ExternalFunctionType::withFirstDerivative); break; case 3: fcall.set_function_type(ExternalFunctionType::withFirstAndSecondDerivative); break; } fcall.write(CompileCode, instruction_number); FSTPTEF_ fstptef(indx); fstptef.write(CompileCode, instruction_number); } } void ExternalFunctionNode::writeJsonAST(ostream &output) const { output << R"({"node_type" : "ExternalFunctionNode", )" << R"("name" : ")" << datatree.symbol_table.getName(symb_id) << R"(", "args" : [)"; writeJsonASTExternalFunctionArguments(output); output << "]}"; } void ExternalFunctionNode::writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, const deriv_node_temp_terms_t &tef_terms, bool isdynamic) const { if (temporary_terms.find(const_cast(this)) != temporary_terms.end()) { output << "T" << idx; return; } try { int tef_idx = getIndxInTefTerms(symb_id, tef_terms); output << "TEF_" << tef_idx; } catch (UnknownFunctionNameAndArgs &) { // When writing the JSON output at parsing pass, we don’t use TEF terms output << datatree.symbol_table.getName(symb_id) << "("; writeJsonExternalFunctionArguments(output, temporary_terms, tef_terms, isdynamic); output << ")"; } } void ExternalFunctionNode::writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, const deriv_node_temp_terms_t &tef_terms) const { if (output_type == ExprNodeOutputType::matlabOutsideModel || output_type == ExprNodeOutputType::steadyStateFile || output_type == ExprNodeOutputType::juliaSteadyStateFile || output_type == ExprNodeOutputType::epilogueFile || isLatexOutput(output_type)) { string name = isLatexOutput(output_type) ? datatree.symbol_table.getTeXName(symb_id) : datatree.symbol_table.getName(symb_id); output << name << "("; writeExternalFunctionArguments(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); output << ")"; return; } if (checkIfTemporaryTermThenWrite(output, output_type, temporary_terms, temporary_terms_idxs)) return; if (isCOutput(output_type)) output << "*"; output << "TEF_" << getIndxInTefTerms(symb_id, tef_terms); } void ExternalFunctionNode::writeExternalFunctionOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, deriv_node_temp_terms_t &tef_terms) const { int first_deriv_symb_id = datatree.external_functions_table.getFirstDerivSymbID(symb_id); assert(first_deriv_symb_id != ExternalFunctionsTable::IDSetButNoNameProvided); for (auto argument : arguments) argument->writeExternalFunctionOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); if (!alreadyWrittenAsTefTerm(symb_id, tef_terms)) { tef_terms[{ symb_id, arguments }] = static_cast(tef_terms.size()); int indx = getIndxInTefTerms(symb_id, tef_terms); int second_deriv_symb_id = datatree.external_functions_table.getSecondDerivSymbID(symb_id); assert(second_deriv_symb_id != ExternalFunctionsTable::IDSetButNoNameProvided); if (isCOutput(output_type)) { output << "double *TEF_" << indx; if (symb_id == first_deriv_symb_id) output << ", *TEFD_" << indx; if (symb_id == second_deriv_symb_id) output << ", *TEFDD_" << indx; output << ";" << endl; if (symb_id == first_deriv_symb_id && symb_id == second_deriv_symb_id) output << "int TEFDD_" << indx << "_nrows;" << endl; int nlhs = symb_id == first_deriv_symb_id && symb_id == second_deriv_symb_id ? 3 : symb_id == first_deriv_symb_id ? 2 : 1; output << "{" << endl << " mxArray *plhs[" << nlhs << "], *prhs[" << arguments.size() << "];" << endl; writePrhs(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); output << " mexCallMATLAB(" << nlhs << ", plhs, " << arguments.size() << ", prhs, " << R"(")" << datatree.symbol_table.getName(symb_id) << R"(");)" << endl; output << " TEF_" << indx << " = mxGetPr(plhs[0]);" << endl; if (symb_id == first_deriv_symb_id) { output << " TEFD_" << indx << " = mxGetPr(plhs[1]);" << endl; if (symb_id == second_deriv_symb_id) output << " TEFDD_" << indx << " = mxGetPr(plhs[2]);" << endl << " TEFDD_" << indx << "_nrows = (int)mxGetM(plhs[2]);" << endl; } output << "}" << endl; } else { if (symb_id == first_deriv_symb_id && symb_id == second_deriv_symb_id) output << "[TEF_" << indx << ", TEFD_"<< indx << ", TEFDD_"<< indx << "] = "; else if (symb_id == first_deriv_symb_id) output << "[TEF_" << indx << ", TEFD_"<< indx << "] = "; else output << "TEF_" << indx << " = "; output << datatree.symbol_table.getName(symb_id) << "("; writeExternalFunctionArguments(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); output << ");" << endl; } } } void ExternalFunctionNode::writeJsonExternalFunctionOutput(vector &efout, const temporary_terms_t &temporary_terms, deriv_node_temp_terms_t &tef_terms, bool isdynamic) const { int first_deriv_symb_id = datatree.external_functions_table.getFirstDerivSymbID(symb_id); assert(first_deriv_symb_id != ExternalFunctionsTable::IDSetButNoNameProvided); for (auto argument : arguments) argument->writeJsonExternalFunctionOutput(efout, temporary_terms, tef_terms, isdynamic); if (!alreadyWrittenAsTefTerm(symb_id, tef_terms)) { tef_terms[{ symb_id, arguments }] = static_cast(tef_terms.size()); int indx = getIndxInTefTerms(symb_id, tef_terms); int second_deriv_symb_id = datatree.external_functions_table.getSecondDerivSymbID(symb_id); assert(second_deriv_symb_id != ExternalFunctionsTable::IDSetButNoNameProvided); stringstream ef; ef << R"({"external_function": {)" << R"("external_function_term": "TEF_)" << indx << R"(")"; if (symb_id == first_deriv_symb_id) ef << R"(, "external_function_term_d": "TEFD_)" << indx << R"(")"; if (symb_id == second_deriv_symb_id) ef << R"(, "external_function_term_dd": "TEFDD_)" << indx << R"(")"; ef << R"(, "value": ")" << datatree.symbol_table.getName(symb_id) << "("; writeJsonExternalFunctionArguments(ef, temporary_terms, tef_terms, isdynamic); ef << R"lit()"}})lit"; efout.push_back(ef.str()); } } expr_t ExternalFunctionNode::toStatic(DataTree &static_datatree) const { vector static_arguments; for (auto argument : arguments) static_arguments.push_back(argument->toStatic(static_datatree)); return static_datatree.AddExternalFunction(symb_id, static_arguments); } void ExternalFunctionNode::computeXrefs(EquationInfo &ei) const { vector dynamic_arguments; for (auto argument : arguments) argument->computeXrefs(ei); } expr_t ExternalFunctionNode::clone(DataTree &datatree) const { vector dynamic_arguments; for (auto argument : arguments) dynamic_arguments.push_back(argument->clone(datatree)); return datatree.AddExternalFunction(symb_id, dynamic_arguments); } expr_t ExternalFunctionNode::buildSimilarExternalFunctionNode(vector &alt_args, DataTree &alt_datatree) const { return alt_datatree.AddExternalFunction(symb_id, alt_args); } function ExternalFunctionNode::sameTefTermPredicate() const { return [this](expr_t e) { auto e2 = dynamic_cast(e); return (e2 != nullptr && e2->symb_id == symb_id); }; } FirstDerivExternalFunctionNode::FirstDerivExternalFunctionNode(DataTree &datatree_arg, int idx_arg, int top_level_symb_id_arg, const vector &arguments_arg, int inputIndex_arg) : AbstractExternalFunctionNode{datatree_arg, idx_arg, top_level_symb_id_arg, arguments_arg}, inputIndex{inputIndex_arg} { } expr_t FirstDerivExternalFunctionNode::composeDerivatives(const vector &dargs) { vector dNodes; for (int i = 0; i < static_cast(dargs.size()); i++) dNodes.push_back(datatree.AddTimes(dargs.at(i), datatree.AddSecondDerivExternalFunction(symb_id, arguments, inputIndex, i+1))); expr_t theDeriv = datatree.Zero; for (auto &dNode : dNodes) theDeriv = datatree.AddPlus(theDeriv, dNode); return theDeriv; } void FirstDerivExternalFunctionNode::writeJsonAST(ostream &output) const { output << R"({"node_type" : "FirstDerivExternalFunctionNode", )" << R"("name" : ")" << datatree.symbol_table.getName(symb_id) << R"(", "args" : [)"; writeJsonASTExternalFunctionArguments(output); output << "]}"; } void FirstDerivExternalFunctionNode::writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, const deriv_node_temp_terms_t &tef_terms, bool isdynamic) const { // If current node is a temporary term if (temporary_terms.find(const_cast(this)) != temporary_terms.end()) { output << "T" << idx; return; } const int first_deriv_symb_id = datatree.external_functions_table.getFirstDerivSymbID(symb_id); assert(first_deriv_symb_id != ExternalFunctionsTable::IDSetButNoNameProvided); const int tmpIndx = inputIndex - 1; if (first_deriv_symb_id == symb_id) output << "TEFD_" << getIndxInTefTerms(symb_id, tef_terms) << "[" << tmpIndx << "]"; else if (first_deriv_symb_id == ExternalFunctionsTable::IDNotSet) output << "TEFD_fdd_" << getIndxInTefTerms(symb_id, tef_terms) << "_" << inputIndex; else output << "TEFD_def_" << getIndxInTefTerms(first_deriv_symb_id, tef_terms) << "[" << tmpIndx << "]"; } void FirstDerivExternalFunctionNode::writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, const deriv_node_temp_terms_t &tef_terms) const { assert(output_type != ExprNodeOutputType::matlabOutsideModel); if (isLatexOutput(output_type)) { output << R"(\frac{\partial )" << datatree.symbol_table.getTeXName(symb_id) << R"(}{\partial )" << inputIndex << "}("; writeExternalFunctionArguments(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); output << ")"; return; } if (checkIfTemporaryTermThenWrite(output, output_type, temporary_terms, temporary_terms_idxs)) return; const int first_deriv_symb_id = datatree.external_functions_table.getFirstDerivSymbID(symb_id); assert(first_deriv_symb_id != ExternalFunctionsTable::IDSetButNoNameProvided); const int tmpIndx = inputIndex - 1 + ARRAY_SUBSCRIPT_OFFSET(output_type); if (first_deriv_symb_id == symb_id) output << "TEFD_" << getIndxInTefTerms(symb_id, tef_terms) << LEFT_ARRAY_SUBSCRIPT(output_type) << tmpIndx << RIGHT_ARRAY_SUBSCRIPT(output_type); else if (first_deriv_symb_id == ExternalFunctionsTable::IDNotSet) { if (isCOutput(output_type)) output << "*"; output << "TEFD_fdd_" << getIndxInTefTerms(symb_id, tef_terms) << "_" << inputIndex; } else output << "TEFD_def_" << getIndxInTefTerms(first_deriv_symb_id, tef_terms) << LEFT_ARRAY_SUBSCRIPT(output_type) << tmpIndx << RIGHT_ARRAY_SUBSCRIPT(output_type); } void FirstDerivExternalFunctionNode::compile(ostream &CompileCode, unsigned int &instruction_number, bool lhs_rhs, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, bool dynamic, bool steady_dynamic, const deriv_node_temp_terms_t &tef_terms) const { if (auto this2 = const_cast(this); temporary_terms.find(this2) != temporary_terms.end()) { if (dynamic) { FLDT_ fldt(temporary_terms_idxs.at(this2)); fldt.write(CompileCode, instruction_number); } else { FLDST_ fldst(temporary_terms_idxs.at(this2)); fldst.write(CompileCode, instruction_number); } return; } int first_deriv_symb_id = datatree.external_functions_table.getFirstDerivSymbID(symb_id); assert(first_deriv_symb_id != ExternalFunctionsTable::IDSetButNoNameProvided); if (!lhs_rhs) { FLDTEFD_ fldtefd(getIndxInTefTerms(symb_id, tef_terms), inputIndex); fldtefd.write(CompileCode, instruction_number); } else { FSTPTEFD_ fstptefd(getIndxInTefTerms(symb_id, tef_terms), inputIndex); fstptefd.write(CompileCode, instruction_number); } } void FirstDerivExternalFunctionNode::writeExternalFunctionOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, deriv_node_temp_terms_t &tef_terms) const { assert(output_type != ExprNodeOutputType::matlabOutsideModel); int first_deriv_symb_id = datatree.external_functions_table.getFirstDerivSymbID(symb_id); assert(first_deriv_symb_id != ExternalFunctionsTable::IDSetButNoNameProvided); /* For a node with derivs provided by the user function, call the method on the non-derived node */ if (first_deriv_symb_id == symb_id) { expr_t parent = datatree.AddExternalFunction(symb_id, arguments); parent->writeExternalFunctionOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); return; } if (alreadyWrittenAsTefTerm(first_deriv_symb_id, tef_terms)) return; if (isCOutput(output_type)) if (first_deriv_symb_id == ExternalFunctionsTable::IDNotSet) { output << "double *TEFD_fdd_" << getIndxInTefTerms(symb_id, tef_terms) << "_" << inputIndex << ";" << endl << "{" << endl << " const mwSize dims[2] = {1, " << arguments.size() << "};" << endl << " mxArray *plhs[1], *prhs[3];" << endl << R"( prhs[0] = mxCreateString(")" << datatree.symbol_table.getName(symb_id) << R"(");)" << endl << " prhs[1] = mxCreateDoubleScalar(" << inputIndex << ");"<< endl << " prhs[2] = mxCreateCellArray(2, dims);"<< endl; int i = 0; for (auto argument : arguments) { output << " mxSetCell(prhs[2], " << i++ << ", " << "mxCreateDoubleScalar("; // All external_function arguments are scalars argument->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); output << "));" << endl; } output << " mexCallMATLAB(1, plhs, 3, prhs," << R"("jacob_element");)" << endl << " TEFD_fdd_" << getIndxInTefTerms(symb_id, tef_terms) << "_" << inputIndex << " = mxGetPr(plhs[0]);" << endl << "}" << endl; } else { tef_terms[{ first_deriv_symb_id, arguments }] = static_cast(tef_terms.size()); int indx = getIndxInTefTerms(first_deriv_symb_id, tef_terms); output << "double *TEFD_def_" << indx << ";" << endl << "{" << endl << " mxArray *plhs[1], *prhs[" << arguments.size() << "];" << endl; writePrhs(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); output << " mexCallMATLAB(1, plhs, " << arguments.size() << ", prhs," << R"(")" << datatree.symbol_table.getName(first_deriv_symb_id) << R"(");)" << endl << " TEFD_def_" << indx << " = mxGetPr(plhs[0]);" << endl << "}" << endl; } else { if (first_deriv_symb_id == ExternalFunctionsTable::IDNotSet) output << "TEFD_fdd_" << getIndxInTefTerms(symb_id, tef_terms) << "_" << inputIndex << " = jacob_element('" << datatree.symbol_table.getName(symb_id) << "'," << inputIndex << ",{"; else { tef_terms[{ first_deriv_symb_id, arguments }] = static_cast(tef_terms.size()); output << "TEFD_def_" << getIndxInTefTerms(first_deriv_symb_id, tef_terms) << " = " << datatree.symbol_table.getName(first_deriv_symb_id) << "("; } writeExternalFunctionArguments(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); if (first_deriv_symb_id == ExternalFunctionsTable::IDNotSet) output << "}"; output << ");" << endl; } } void FirstDerivExternalFunctionNode::writeJsonExternalFunctionOutput(vector &efout, const temporary_terms_t &temporary_terms, deriv_node_temp_terms_t &tef_terms, bool isdynamic) const { int first_deriv_symb_id = datatree.external_functions_table.getFirstDerivSymbID(symb_id); assert(first_deriv_symb_id != ExternalFunctionsTable::IDSetButNoNameProvided); /* For a node with derivs provided by the user function, call the method on the non-derived node */ if (first_deriv_symb_id == symb_id) { expr_t parent = datatree.AddExternalFunction(symb_id, arguments); parent->writeJsonExternalFunctionOutput(efout, temporary_terms, tef_terms, isdynamic); return; } if (alreadyWrittenAsTefTerm(first_deriv_symb_id, tef_terms)) return; stringstream ef; if (first_deriv_symb_id == ExternalFunctionsTable::IDNotSet) ef << R"({"first_deriv_external_function": {)" << R"("external_function_term": "TEFD_fdd_)" << getIndxInTefTerms(symb_id, tef_terms) << "_" << inputIndex << R"(")" << R"(, "analytic_derivative": false)" << R"(, "wrt": )" << inputIndex << R"(, "value": ")" << datatree.symbol_table.getName(symb_id) << "("; else { tef_terms[{ first_deriv_symb_id, arguments }] = static_cast(tef_terms.size()); ef << R"({"first_deriv_external_function": {)" << R"("external_function_term": "TEFD_def_)" << getIndxInTefTerms(first_deriv_symb_id, tef_terms) << R"(")" << R"(, "analytic_derivative": true)" << R"(, "value": ")" << datatree.symbol_table.getName(first_deriv_symb_id) << "("; } writeJsonExternalFunctionArguments(ef, temporary_terms, tef_terms, isdynamic); ef << R"lit()"}})lit"; efout.push_back(ef.str()); } void FirstDerivExternalFunctionNode::compileExternalFunctionOutput(ostream &CompileCode, unsigned int &instruction_number, bool lhs_rhs, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, bool dynamic, bool steady_dynamic, deriv_node_temp_terms_t &tef_terms) const { int first_deriv_symb_id = datatree.external_functions_table.getFirstDerivSymbID(symb_id); assert(first_deriv_symb_id != ExternalFunctionsTable::IDSetButNoNameProvided); if (first_deriv_symb_id == symb_id || alreadyWrittenAsTefTerm(first_deriv_symb_id, tef_terms)) return; unsigned int nb_add_input_arguments = compileExternalFunctionArguments(CompileCode, instruction_number, lhs_rhs, temporary_terms, temporary_terms_idxs, dynamic, steady_dynamic, tef_terms); if (first_deriv_symb_id == ExternalFunctionsTable::IDNotSet) { unsigned int nb_input_arguments = 0; unsigned int nb_output_arguments = 1; int indx = getIndxInTefTerms(symb_id, tef_terms); FCALL_ fcall(nb_output_arguments, nb_input_arguments, "jacob_element", indx); fcall.set_arg_func_name(datatree.symbol_table.getName(symb_id)); fcall.set_row(inputIndex); fcall.set_nb_add_input_arguments(nb_add_input_arguments); fcall.set_function_type(ExternalFunctionType::numericalFirstDerivative); fcall.write(CompileCode, instruction_number); FSTPTEFD_ fstptefd(indx, inputIndex); fstptefd.write(CompileCode, instruction_number); } else { tef_terms[{ first_deriv_symb_id, arguments }] = static_cast(tef_terms.size()); int indx = getIndxInTefTerms(symb_id, tef_terms); int second_deriv_symb_id = datatree.external_functions_table.getSecondDerivSymbID(symb_id); assert(second_deriv_symb_id != ExternalFunctionsTable::IDSetButNoNameProvided); unsigned int nb_output_arguments = 1; FCALL_ fcall(nb_output_arguments, nb_add_input_arguments, datatree.symbol_table.getName(first_deriv_symb_id), indx); fcall.set_function_type(ExternalFunctionType::firstDerivative); fcall.write(CompileCode, instruction_number); FSTPTEFD_ fstptefd(indx, inputIndex); fstptefd.write(CompileCode, instruction_number); } } expr_t FirstDerivExternalFunctionNode::clone(DataTree &datatree) const { vector dynamic_arguments; for (auto argument : arguments) dynamic_arguments.push_back(argument->clone(datatree)); return datatree.AddFirstDerivExternalFunction(symb_id, dynamic_arguments, inputIndex); } expr_t FirstDerivExternalFunctionNode::buildSimilarExternalFunctionNode(vector &alt_args, DataTree &alt_datatree) const { return alt_datatree.AddFirstDerivExternalFunction(symb_id, alt_args, inputIndex); } expr_t FirstDerivExternalFunctionNode::toStatic(DataTree &static_datatree) const { vector static_arguments; for (auto argument : arguments) static_arguments.push_back(argument->toStatic(static_datatree)); return static_datatree.AddFirstDerivExternalFunction(symb_id, static_arguments, inputIndex); } void FirstDerivExternalFunctionNode::computeXrefs(EquationInfo &ei) const { vector dynamic_arguments; for (auto argument : arguments) argument->computeXrefs(ei); } function FirstDerivExternalFunctionNode::sameTefTermPredicate() const { int first_deriv_symb_id = datatree.external_functions_table.getFirstDerivSymbID(symb_id); if (first_deriv_symb_id == symb_id) return [this](expr_t e) { auto e2 = dynamic_cast(e); return (e2 && e2->symb_id == symb_id); }; else return [this](expr_t e) { auto e2 = dynamic_cast(e); return (e2 && e2->symb_id == symb_id); }; } SecondDerivExternalFunctionNode::SecondDerivExternalFunctionNode(DataTree &datatree_arg, int idx_arg, int top_level_symb_id_arg, const vector &arguments_arg, int inputIndex1_arg, int inputIndex2_arg) : AbstractExternalFunctionNode{datatree_arg, idx_arg, top_level_symb_id_arg, arguments_arg}, inputIndex1{inputIndex1_arg}, inputIndex2{inputIndex2_arg} { } expr_t SecondDerivExternalFunctionNode::composeDerivatives(const vector &dargs) { cerr << "ERROR: third order derivatives of external functions are not implemented" << endl; exit(EXIT_FAILURE); } void SecondDerivExternalFunctionNode::writeJsonAST(ostream &output) const { output << R"({"node_type" : "SecondDerivExternalFunctionNode", )" << R"("name" : ")" << datatree.symbol_table.getName(symb_id) << R"(", "args" : [)"; writeJsonASTExternalFunctionArguments(output); output << "]}"; } void SecondDerivExternalFunctionNode::writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, const deriv_node_temp_terms_t &tef_terms, bool isdynamic) const { // If current node is a temporary term if (temporary_terms.find(const_cast(this)) != temporary_terms.end()) { output << "T" << idx; return; } const int second_deriv_symb_id = datatree.external_functions_table.getSecondDerivSymbID(symb_id); assert(second_deriv_symb_id != ExternalFunctionsTable::IDSetButNoNameProvided); const int tmpIndex1 = inputIndex1 - 1; const int tmpIndex2 = inputIndex2 - 1; if (second_deriv_symb_id == symb_id) output << "TEFDD_" << getIndxInTefTerms(symb_id, tef_terms) << "[" << tmpIndex1 << "," << tmpIndex2 << "]"; else if (second_deriv_symb_id == ExternalFunctionsTable::IDNotSet) output << "TEFDD_fdd_" << getIndxInTefTerms(symb_id, tef_terms) << "_" << inputIndex1 << "_" << inputIndex2; else output << "TEFDD_def_" << getIndxInTefTerms(second_deriv_symb_id, tef_terms) << "[" << tmpIndex1 << "," << tmpIndex2 << "]"; } void SecondDerivExternalFunctionNode::writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, const deriv_node_temp_terms_t &tef_terms) const { assert(output_type != ExprNodeOutputType::matlabOutsideModel); if (isLatexOutput(output_type)) { output << R"(\frac{\partial^2 )" << datatree.symbol_table.getTeXName(symb_id) << R"(}{\partial )" << inputIndex1 << R"(\partial )" << inputIndex2 << "}("; writeExternalFunctionArguments(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); output << ")"; return; } if (checkIfTemporaryTermThenWrite(output, output_type, temporary_terms, temporary_terms_idxs)) return; const int second_deriv_symb_id = datatree.external_functions_table.getSecondDerivSymbID(symb_id); assert(second_deriv_symb_id != ExternalFunctionsTable::IDSetButNoNameProvided); const int tmpIndex1 = inputIndex1 - 1 + ARRAY_SUBSCRIPT_OFFSET(output_type); const int tmpIndex2 = inputIndex2 - 1 + ARRAY_SUBSCRIPT_OFFSET(output_type); int indx = getIndxInTefTerms(symb_id, tef_terms); if (second_deriv_symb_id == symb_id) if (isCOutput(output_type)) output << "TEFDD_" << indx << LEFT_ARRAY_SUBSCRIPT(output_type) << tmpIndex1 << " * TEFDD_" << indx << "_nrows + " << tmpIndex2 << RIGHT_ARRAY_SUBSCRIPT(output_type); else output << "TEFDD_" << getIndxInTefTerms(symb_id, tef_terms) << LEFT_ARRAY_SUBSCRIPT(output_type) << tmpIndex1 << "," << tmpIndex2 << RIGHT_ARRAY_SUBSCRIPT(output_type); else if (second_deriv_symb_id == ExternalFunctionsTable::IDNotSet) { if (isCOutput(output_type)) output << "*"; output << "TEFDD_fdd_" << getIndxInTefTerms(symb_id, tef_terms) << "_" << inputIndex1 << "_" << inputIndex2; } else if (isCOutput(output_type)) output << "TEFDD_def_" << getIndxInTefTerms(second_deriv_symb_id, tef_terms) << LEFT_ARRAY_SUBSCRIPT(output_type) << tmpIndex1 << " * PROBLEM_" << indx << "_nrows" << tmpIndex2 << RIGHT_ARRAY_SUBSCRIPT(output_type); else output << "TEFDD_def_" << getIndxInTefTerms(second_deriv_symb_id, tef_terms) << LEFT_ARRAY_SUBSCRIPT(output_type) << tmpIndex1 << "," << tmpIndex2 << RIGHT_ARRAY_SUBSCRIPT(output_type); } void SecondDerivExternalFunctionNode::writeExternalFunctionOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, deriv_node_temp_terms_t &tef_terms) const { assert(output_type != ExprNodeOutputType::matlabOutsideModel); int second_deriv_symb_id = datatree.external_functions_table.getSecondDerivSymbID(symb_id); assert(second_deriv_symb_id != ExternalFunctionsTable::IDSetButNoNameProvided); /* For a node with derivs provided by the user function, call the method on the non-derived node */ if (second_deriv_symb_id == symb_id) { expr_t parent = datatree.AddExternalFunction(symb_id, arguments); parent->writeExternalFunctionOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); return; } if (alreadyWrittenAsTefTerm(second_deriv_symb_id, tef_terms)) return; if (isCOutput(output_type)) if (second_deriv_symb_id == ExternalFunctionsTable::IDNotSet) { stringstream ending; output << "double *TEFDD_fdd_" << getIndxInTefTerms(symb_id, tef_terms) << "_" << inputIndex1 << "_" << inputIndex2 << ";" << endl << "{" << endl << " const mwSize dims[2]= {1, " << arguments.size() << "};" << endl << " mxArray *plhs[1], *prhs[4];" << endl << R"( prhs[0] = mxCreateString(")" << datatree.symbol_table.getName(symb_id) << R"(");)" << endl << " prhs[1] = mxCreateDoubleScalar(" << inputIndex1 << ");"<< endl << " prhs[2] = mxCreateDoubleScalar(" << inputIndex2 << ");"<< endl << " prhs[3] = mxCreateCellArray(2, dims);"<< endl; int i = 0; for (auto argument : arguments) { output << " mxSetCell(prhs[3], " << i++ << ", " << " mxCreateDoubleScalar("; // All external_function arguments are scalars argument->writeOutput(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); output << "));" << endl; } output << " mexCallMATLAB(1, plhs, 4, prhs, " << R"("hess_element");)" << endl << " TEFDD_fdd_" << getIndxInTefTerms(symb_id, tef_terms) << "_" << inputIndex1 << "_" << inputIndex2 << " = mxGetPr(plhs[0]);" << endl << "}" << endl; } else { tef_terms[{ second_deriv_symb_id, arguments }] = static_cast(tef_terms.size()); int indx = getIndxInTefTerms(second_deriv_symb_id, tef_terms); output << "double *TEFDD_def_" << indx << ";" << endl << "{" << endl << " mxArray *plhs[1], *prhs[" << arguments.size() << "];" << endl; writePrhs(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); output << " mexCallMATLAB(1, plhs, " << arguments.size() << ", prhs, " << R"(")" << datatree.symbol_table.getName(second_deriv_symb_id) << R"(");)" << endl << " TEFDD_def_" << indx << " = mxGetPr(plhs[0]);" << endl << "}" << endl; } else { if (second_deriv_symb_id == ExternalFunctionsTable::IDNotSet) output << "TEFDD_fdd_" << getIndxInTefTerms(symb_id, tef_terms) << "_" << inputIndex1 << "_" << inputIndex2 << " = hess_element('" << datatree.symbol_table.getName(symb_id) << "'," << inputIndex1 << "," << inputIndex2 << ",{"; else { tef_terms[{ second_deriv_symb_id, arguments }] = static_cast(tef_terms.size()); output << "TEFDD_def_" << getIndxInTefTerms(second_deriv_symb_id, tef_terms) << " = " << datatree.symbol_table.getName(second_deriv_symb_id) << "("; } writeExternalFunctionArguments(output, output_type, temporary_terms, temporary_terms_idxs, tef_terms); if (second_deriv_symb_id == ExternalFunctionsTable::IDNotSet) output << "}"; output << ");" << endl; } } void SecondDerivExternalFunctionNode::writeJsonExternalFunctionOutput(vector &efout, const temporary_terms_t &temporary_terms, deriv_node_temp_terms_t &tef_terms, bool isdynamic) const { int second_deriv_symb_id = datatree.external_functions_table.getSecondDerivSymbID(symb_id); assert(second_deriv_symb_id != ExternalFunctionsTable::IDSetButNoNameProvided); /* For a node with derivs provided by the user function, call the method on the non-derived node */ if (second_deriv_symb_id == symb_id) { expr_t parent = datatree.AddExternalFunction(symb_id, arguments); parent->writeJsonExternalFunctionOutput(efout, temporary_terms, tef_terms, isdynamic); return; } if (alreadyWrittenAsTefTerm(second_deriv_symb_id, tef_terms)) return; stringstream ef; if (second_deriv_symb_id == ExternalFunctionsTable::IDNotSet) ef << R"({"second_deriv_external_function": {)" << R"("external_function_term": "TEFDD_fdd_)" << getIndxInTefTerms(symb_id, tef_terms) << "_" << inputIndex1 << "_" << inputIndex2 << R"(")" << R"(, "analytic_derivative": false)" << R"(, "wrt1": )" << inputIndex1 << R"(, "wrt2": )" << inputIndex2 << R"(, "value": ")" << datatree.symbol_table.getName(symb_id) << "("; else { tef_terms[{ second_deriv_symb_id, arguments }] = static_cast(tef_terms.size()); ef << R"({"second_deriv_external_function": {)" << R"("external_function_term": "TEFDD_def_)" << getIndxInTefTerms(second_deriv_symb_id, tef_terms) << R"(")" << R"(, "analytic_derivative": true)" << R"(, "value": ")" << datatree.symbol_table.getName(second_deriv_symb_id) << "("; } writeJsonExternalFunctionArguments(ef, temporary_terms, tef_terms, isdynamic); ef << R"lit()"}})lit" << endl; efout.push_back(ef.str()); } expr_t SecondDerivExternalFunctionNode::clone(DataTree &datatree) const { vector dynamic_arguments; for (auto argument : arguments) dynamic_arguments.push_back(argument->clone(datatree)); return datatree.AddSecondDerivExternalFunction(symb_id, dynamic_arguments, inputIndex1, inputIndex2); } expr_t SecondDerivExternalFunctionNode::buildSimilarExternalFunctionNode(vector &alt_args, DataTree &alt_datatree) const { return alt_datatree.AddSecondDerivExternalFunction(symb_id, alt_args, inputIndex1, inputIndex2); } expr_t SecondDerivExternalFunctionNode::toStatic(DataTree &static_datatree) const { vector static_arguments; for (auto argument : arguments) static_arguments.push_back(argument->toStatic(static_datatree)); return static_datatree.AddSecondDerivExternalFunction(symb_id, static_arguments, inputIndex1, inputIndex2); } void SecondDerivExternalFunctionNode::computeXrefs(EquationInfo &ei) const { vector dynamic_arguments; for (auto argument : arguments) argument->computeXrefs(ei); } void SecondDerivExternalFunctionNode::compile(ostream &CompileCode, unsigned int &instruction_number, bool lhs_rhs, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, bool dynamic, bool steady_dynamic, const deriv_node_temp_terms_t &tef_terms) const { cerr << "SecondDerivExternalFunctionNode::compile: not implemented." << endl; exit(EXIT_FAILURE); } void SecondDerivExternalFunctionNode::compileExternalFunctionOutput(ostream &CompileCode, unsigned int &instruction_number, bool lhs_rhs, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, bool dynamic, bool steady_dynamic, deriv_node_temp_terms_t &tef_terms) const { cerr << "SecondDerivExternalFunctionNode::compileExternalFunctionOutput: not implemented." << endl; exit(EXIT_FAILURE); } function SecondDerivExternalFunctionNode::sameTefTermPredicate() const { int second_deriv_symb_id = datatree.external_functions_table.getSecondDerivSymbID(symb_id); if (second_deriv_symb_id == symb_id) return [this](expr_t e) { auto e2 = dynamic_cast(e); return (e2 && e2->symb_id == symb_id); }; else return [this](expr_t e) { auto e2 = dynamic_cast(e); return (e2 && e2->symb_id == symb_id); }; } VarExpectationNode::VarExpectationNode(DataTree &datatree_arg, int idx_arg, string model_name_arg) : ExprNode{datatree_arg, idx_arg}, model_name{move(model_name_arg)} { } void VarExpectationNode::computeTemporaryTerms(const pair &derivOrder, map, temporary_terms_t> &temp_terms_map, map>> &reference_count, bool is_matlab) const { cerr << "VarExpectationNode::computeTemporaryTerms not implemented." << endl; exit(EXIT_FAILURE); } void VarExpectationNode::computeBlockTemporaryTerms(int blk, int eq, vector> &blocks_temporary_terms, map> &reference_count) const { cerr << "VarExpectationNode::computeBlocksTemporaryTerms not implemented." << endl; exit(EXIT_FAILURE); } expr_t VarExpectationNode::toStatic(DataTree &static_datatree) const { cerr << "VarExpectationNode::toStatic not implemented." << endl; exit(EXIT_FAILURE); } expr_t VarExpectationNode::clone(DataTree &datatree) const { return datatree.AddVarExpectation(model_name); } void VarExpectationNode::writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, const deriv_node_temp_terms_t &tef_terms) const { assert(output_type != ExprNodeOutputType::matlabOutsideModel); if (isLatexOutput(output_type)) { output << "VAR_EXPECTATION(" << model_name << ')'; return; } cerr << "VarExpectationNode::writeOutput not implemented for non-LaTeX." << endl; exit(EXIT_FAILURE); } int VarExpectationNode::maxEndoLead() const { cerr << "VarExpectationNode::maxEndoLead not implemented." << endl; exit(EXIT_FAILURE); } int VarExpectationNode::maxExoLead() const { cerr << "VarExpectationNode::maxExoLead not implemented." << endl; exit(EXIT_FAILURE); } int VarExpectationNode::maxEndoLag() const { cerr << "VarExpectationNode::maxEndoLead not implemented." << endl; exit(EXIT_FAILURE); } int VarExpectationNode::maxExoLag() const { cerr << "VarExpectationNode::maxExoLead not implemented." << endl; exit(EXIT_FAILURE); } int VarExpectationNode::maxLead() const { cerr << "VarExpectationNode::maxLead not implemented." << endl; exit(EXIT_FAILURE); } int VarExpectationNode::maxLag() const { cerr << "VarExpectationNode::maxLag not implemented." << endl; exit(EXIT_FAILURE); } int VarExpectationNode::maxLagWithDiffsExpanded() const { /* This node will be substituted by lagged variables, so in theory we should return a strictly positive value. But from here this value is not easy to compute. We return 0, because currently this function is only called from DynamicModel::setLeadsLagsOrig(), and the maximum lag will nevertheless be correctly computed because the maximum lag of the VAR will be taken into account via the corresponding equations. */ return 0; } expr_t VarExpectationNode::undiff() const { cerr << "VarExpectationNode::undiff not implemented." << endl; exit(EXIT_FAILURE); } int VarExpectationNode::VarMinLag() const { cerr << "VarExpectationNode::VarMinLag not implemented." << endl; exit(EXIT_FAILURE); } int VarExpectationNode::VarMaxLag(const set &lhs_lag_equiv) const { cerr << "VarExpectationNode::VarMaxLag not implemented." << endl; exit(EXIT_FAILURE); } expr_t VarExpectationNode::decreaseLeadsLags(int n) const { cerr << "VarExpectationNode::decreaseLeadsLags not implemented." << endl; exit(EXIT_FAILURE); } void VarExpectationNode::prepareForDerivation() { cerr << "VarExpectationNode::prepareForDerivation not implemented." << endl; exit(EXIT_FAILURE); } expr_t VarExpectationNode::computeDerivative(int deriv_id) { cerr << "VarExpectationNode::computeDerivative not implemented." << endl; exit(EXIT_FAILURE); } expr_t VarExpectationNode::getChainRuleDerivative(int deriv_id, const map &recursive_variables) { cerr << "VarExpectationNode::getChainRuleDerivative not implemented." << endl; exit(EXIT_FAILURE); } bool VarExpectationNode::containsExternalFunction() const { return false; } double VarExpectationNode::eval(const eval_context_t &eval_context) const noexcept(false) { throw EvalException(); } int VarExpectationNode::countDiffs() const { cerr << "VarExpectationNode::countDiffs not implemented." << endl; exit(EXIT_FAILURE); } void VarExpectationNode::computeXrefs(EquationInfo &ei) const { } void VarExpectationNode::collectVARLHSVariable(set &result) const { cerr << "ERROR: you can only have variables or unary ops on LHS of VAR" << endl; exit(EXIT_FAILURE); } void VarExpectationNode::collectDynamicVariables(SymbolType type_arg, set> &result) const { } void VarExpectationNode::compile(ostream &CompileCode, unsigned int &instruction_number, bool lhs_rhs, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, bool dynamic, bool steady_dynamic, const deriv_node_temp_terms_t &tef_terms) const { cerr << "VarExpectationNode::compile not implemented." << endl; exit(EXIT_FAILURE); } void VarExpectationNode::computeSubExprContainingVariable(int symb_id, int lag, set &contain_var) const { } BinaryOpNode * VarExpectationNode::normalizeEquationHelper(const set &contain_var, expr_t rhs) const { throw NormalizationFailed(); } expr_t VarExpectationNode::substituteEndoLeadGreaterThanTwo(subst_table_t &subst_table, vector &neweqs, bool deterministic_model) const { cerr << "VarExpectationNode::substituteEndoLeadGreaterThanTwo not implemented." << endl; exit(EXIT_FAILURE); } expr_t VarExpectationNode::substituteEndoLagGreaterThanTwo(subst_table_t &subst_table, vector &neweqs) const { cerr << "VarExpectationNode::substituteEndoLagGreaterThanTwo not implemented." << endl; exit(EXIT_FAILURE); } expr_t VarExpectationNode::substituteExoLead(subst_table_t &subst_table, vector &neweqs, bool deterministic_model) const { cerr << "VarExpectationNode::substituteExoLead not implemented." << endl; exit(EXIT_FAILURE); } expr_t VarExpectationNode::substituteExoLag(subst_table_t &subst_table, vector &neweqs) const { cerr << "VarExpectationNode::substituteExoLag not implemented." << endl; exit(EXIT_FAILURE); } expr_t VarExpectationNode::substituteExpectation(subst_table_t &subst_table, vector &neweqs, bool partial_information_model) const { return const_cast(this); } expr_t VarExpectationNode::substituteAdl() const { return const_cast(this); } expr_t VarExpectationNode::substituteModelLocalVariables() const { return const_cast(this); } expr_t VarExpectationNode::substituteVarExpectation(const map &subst_table) const { auto it = subst_table.find(model_name); if (it == subst_table.end()) { cerr << "ERROR: unknown model '" << model_name << "' used in var_expectation expression" << endl; exit(EXIT_FAILURE); } return it->second; } void VarExpectationNode::findDiffNodes(lag_equivalence_table_t &nodes) const { } void VarExpectationNode::findUnaryOpNodesForAuxVarCreation(lag_equivalence_table_t &nodes) const { } int VarExpectationNode::findTargetVariable(int lhs_symb_id) const { return -1; } expr_t VarExpectationNode::substituteDiff(const lag_equivalence_table_t &nodes, subst_table_t &subst_table, vector &neweqs) const { return const_cast(this); } expr_t VarExpectationNode::substituteUnaryOpNodes(const lag_equivalence_table_t &nodes, subst_table_t &subst_table, vector &neweqs) const { return const_cast(this); } expr_t VarExpectationNode::substitutePacExpectation(const string &name, expr_t subexpr) { return const_cast(this); } expr_t VarExpectationNode::differentiateForwardVars(const vector &subset, subst_table_t &subst_table, vector &neweqs) const { cerr << "VarExpectationNode::differentiateForwardVars not implemented." << endl; exit(EXIT_FAILURE); } bool VarExpectationNode::containsPacExpectation(const string &pac_model_name) const { return false; } bool VarExpectationNode::isNumConstNodeEqualTo(double value) const { return false; } expr_t VarExpectationNode::decreaseLeadsLagsPredeterminedVariables() const { cerr << "VarExpectationNode::decreaseLeadsLagsPredeterminedVariables not implemented." << endl; exit(EXIT_FAILURE); } bool VarExpectationNode::isVariableNodeEqualTo(SymbolType type_arg, int variable_id, int lag_arg) const { return false; } expr_t VarExpectationNode::replaceTrendVar() const { cerr << "VarExpectationNode::replaceTrendVar not implemented." << endl; exit(EXIT_FAILURE); } expr_t VarExpectationNode::detrend(int symb_id, bool log_trend, expr_t trend) const { cerr << "VarExpectationNode::detrend not implemented." << endl; exit(EXIT_FAILURE); } expr_t VarExpectationNode::removeTrendLeadLag(const map &trend_symbols_map) const { cerr << "VarExpectationNode::removeTrendLeadLag not implemented." << endl; exit(EXIT_FAILURE); } bool VarExpectationNode::isInStaticForm() const { cerr << "VarExpectationNode::isInStaticForm not implemented." << endl; exit(EXIT_FAILURE); } bool VarExpectationNode::isVarModelReferenced(const string &model_info_name) const { /* TODO: should check here whether the var_expectation_model is equal to the argument; we probably need a VarModelTable class to do that elegantly */ return false; } void VarExpectationNode::getEndosAndMaxLags(map &model_endos_and_lags) const { } bool VarExpectationNode::isParamTimesEndogExpr() const { return false; } expr_t VarExpectationNode::substituteStaticAuxiliaryVariable() const { return const_cast(this); } expr_t VarExpectationNode::replaceVarsInEquation(map &table) const { return const_cast(this); } void VarExpectationNode::writeJsonAST(ostream &output) const { output << R"({"node_type" : "VarExpectationNode", )" << R"("name" : ")" << model_name << R"("})"; } void VarExpectationNode::writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, const deriv_node_temp_terms_t &tef_terms, bool isdynamic) const { output << "var_expectation(" << "model_name = " << model_name << ")"; } PacExpectationNode::PacExpectationNode(DataTree &datatree_arg, int idx_arg, string model_name_arg) : ExprNode{datatree_arg, idx_arg}, model_name{move(model_name_arg)} { } void PacExpectationNode::computeTemporaryTerms(const pair &derivOrder, map, temporary_terms_t> &temp_terms_map, map>> &reference_count, bool is_matlab) const { temp_terms_map[derivOrder].insert(const_cast(this)); } void PacExpectationNode::computeBlockTemporaryTerms(int blk, int eq, vector> &blocks_temporary_terms, map> &reference_count) const { blocks_temporary_terms[blk][eq].insert(const_cast(this)); } expr_t PacExpectationNode::toStatic(DataTree &static_datatree) const { return static_datatree.AddPacExpectation(string(model_name)); } expr_t PacExpectationNode::clone(DataTree &datatree) const { return datatree.AddPacExpectation(string(model_name)); } void PacExpectationNode::writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, const deriv_node_temp_terms_t &tef_terms) const { assert(output_type != ExprNodeOutputType::matlabOutsideModel); if (isLatexOutput(output_type)) { output << "PAC_EXPECTATION" << LEFT_PAR(output_type) << model_name << RIGHT_PAR(output_type); return; } } int PacExpectationNode::maxEndoLead() const { return 0; } int PacExpectationNode::maxExoLead() const { return 0; } int PacExpectationNode::maxEndoLag() const { return 0; } int PacExpectationNode::maxExoLag() const { return 0; } int PacExpectationNode::maxLead() const { return 0; } int PacExpectationNode::maxLag() const { return 0; } int PacExpectationNode::maxLagWithDiffsExpanded() const { // Same comment as in VarExpectationNode::maxLagWithDiffsExpanded() return 0; } expr_t PacExpectationNode::undiff() const { return const_cast(this); } int PacExpectationNode::VarMinLag() const { return 1; } int PacExpectationNode::VarMaxLag(const set &lhs_lag_equiv) const { return 0; } expr_t PacExpectationNode::decreaseLeadsLags(int n) const { return const_cast(this); } void PacExpectationNode::prepareForDerivation() { cerr << "PacExpectationNode::prepareForDerivation: shouldn't arrive here." << endl; exit(EXIT_FAILURE); } expr_t PacExpectationNode::computeDerivative(int deriv_id) { cerr << "PacExpectationNode::computeDerivative: shouldn't arrive here." << endl; exit(EXIT_FAILURE); } expr_t PacExpectationNode::getChainRuleDerivative(int deriv_id, const map &recursive_variables) { cerr << "PacExpectationNode::getChainRuleDerivative: shouldn't arrive here." << endl; exit(EXIT_FAILURE); } bool PacExpectationNode::containsExternalFunction() const { return false; } double PacExpectationNode::eval(const eval_context_t &eval_context) const noexcept(false) { throw EvalException(); } void PacExpectationNode::computeXrefs(EquationInfo &ei) const { } void PacExpectationNode::collectVARLHSVariable(set &result) const { cerr << "ERROR: you can only have variables or unary ops on LHS of VAR" << endl; exit(EXIT_FAILURE); } void PacExpectationNode::collectDynamicVariables(SymbolType type_arg, set> &result) const { } void PacExpectationNode::compile(ostream &CompileCode, unsigned int &instruction_number, bool lhs_rhs, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, bool dynamic, bool steady_dynamic, const deriv_node_temp_terms_t &tef_terms) const { cerr << "PacExpectationNode::compile not implemented." << endl; exit(EXIT_FAILURE); } int PacExpectationNode::countDiffs() const { return 0; } void PacExpectationNode::computeSubExprContainingVariable(int symb_id, int lag, set &contain_var) const { } BinaryOpNode * PacExpectationNode::normalizeEquationHelper(const set &contain_var, expr_t rhs) const { throw NormalizationFailed(); } expr_t PacExpectationNode::substituteEndoLeadGreaterThanTwo(subst_table_t &subst_table, vector &neweqs, bool deterministic_model) const { return const_cast(this); } expr_t PacExpectationNode::substituteEndoLagGreaterThanTwo(subst_table_t &subst_table, vector &neweqs) const { return const_cast(this); } expr_t PacExpectationNode::substituteExoLead(subst_table_t &subst_table, vector &neweqs, bool deterministic_model) const { return const_cast(this); } expr_t PacExpectationNode::substituteExoLag(subst_table_t &subst_table, vector &neweqs) const { return const_cast(this); } expr_t PacExpectationNode::substituteExpectation(subst_table_t &subst_table, vector &neweqs, bool partial_information_model) const { return const_cast(this); } expr_t PacExpectationNode::substituteAdl() const { return const_cast(this); } expr_t PacExpectationNode::substituteModelLocalVariables() const { return const_cast(this); } expr_t PacExpectationNode::substituteVarExpectation(const map &subst_table) const { return const_cast(this); } void PacExpectationNode::findDiffNodes(lag_equivalence_table_t &nodes) const { } void PacExpectationNode::findUnaryOpNodesForAuxVarCreation(lag_equivalence_table_t &nodes) const { } int PacExpectationNode::findTargetVariable(int lhs_symb_id) const { return -1; } expr_t PacExpectationNode::substituteDiff(const lag_equivalence_table_t &nodes, subst_table_t &subst_table, vector &neweqs) const { return const_cast(this); } expr_t PacExpectationNode::substituteUnaryOpNodes(const lag_equivalence_table_t &nodes, subst_table_t &subst_table, vector &neweqs) const { return const_cast(this); } expr_t PacExpectationNode::differentiateForwardVars(const vector &subset, subst_table_t &subst_table, vector &neweqs) const { return const_cast(this); } bool PacExpectationNode::containsPacExpectation(const string &pac_model_name) const { if (pac_model_name.empty()) return true; else return pac_model_name == model_name; } bool PacExpectationNode::isNumConstNodeEqualTo(double value) const { return false; } expr_t PacExpectationNode::decreaseLeadsLagsPredeterminedVariables() const { return const_cast(this); } bool PacExpectationNode::isVariableNodeEqualTo(SymbolType type_arg, int variable_id, int lag_arg) const { return false; } expr_t PacExpectationNode::replaceTrendVar() const { return const_cast(this); } expr_t PacExpectationNode::detrend(int symb_id, bool log_trend, expr_t trend) const { return const_cast(this); } expr_t PacExpectationNode::removeTrendLeadLag(const map &trend_symbols_map) const { return const_cast(this); } bool PacExpectationNode::isInStaticForm() const { return false; } bool PacExpectationNode::isVarModelReferenced(const string &model_info_name) const { return model_name == model_info_name; } void PacExpectationNode::getEndosAndMaxLags(map &model_endos_and_lags) const { } expr_t PacExpectationNode::substituteStaticAuxiliaryVariable() const { return const_cast(this); } expr_t PacExpectationNode::replaceVarsInEquation(map &table) const { return const_cast(this); } void PacExpectationNode::writeJsonAST(ostream &output) const { output << R"({"node_type" : "PacExpectationNode", )" << R"("name" : ")" << model_name << R"("})"; } void PacExpectationNode::writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, const deriv_node_temp_terms_t &tef_terms, bool isdynamic) const { output << "pac_expectation(" << "model_name = " << model_name << ")"; } bool PacExpectationNode::isParamTimesEndogExpr() const { return false; } expr_t PacExpectationNode::substitutePacExpectation(const string &name, expr_t subexpr) { if (model_name != name) return const_cast(this); return subexpr; } void ExprNode::decomposeAdditiveTerms(vector> &terms, int current_sign) const { terms.emplace_back(const_cast(this), current_sign); } void UnaryOpNode::decomposeAdditiveTerms(vector> &terms, int current_sign) const { if (op_code == UnaryOpcode::uminus) arg->decomposeAdditiveTerms(terms, -current_sign); else ExprNode::decomposeAdditiveTerms(terms, current_sign); } void BinaryOpNode::decomposeAdditiveTerms(vector> &terms, int current_sign) const { if (op_code == BinaryOpcode::plus || op_code == BinaryOpcode::minus) { arg1->decomposeAdditiveTerms(terms, current_sign); if (op_code == BinaryOpcode::plus) arg2->decomposeAdditiveTerms(terms, current_sign); else arg2->decomposeAdditiveTerms(terms, -current_sign); } else ExprNode::decomposeAdditiveTerms(terms, current_sign); } void ExprNode::decomposeMultiplicativeFactors(vector> &factors, int current_exponent) const { factors.emplace_back(const_cast(this), current_exponent); } void BinaryOpNode::decomposeMultiplicativeFactors(vector> &factors, int current_exponent) const { if (op_code == BinaryOpcode::times || op_code == BinaryOpcode::divide) { arg1->decomposeMultiplicativeFactors(factors, current_exponent); if (op_code == BinaryOpcode::times) arg2->decomposeMultiplicativeFactors(factors, current_exponent); else arg2->decomposeMultiplicativeFactors(factors, -current_exponent); } else ExprNode::decomposeMultiplicativeFactors(factors, current_exponent); } tuple ExprNode::matchVariableTimesConstantTimesParam(bool variable_obligatory) const { int variable_id = -1, lag = 0, param_id = -1; double constant = 1.0; matchVTCTPHelper(variable_id, lag, param_id, constant, false); if (variable_obligatory && variable_id == -1) throw MatchFailureException{"No variable in this expression"}; return {variable_id, lag, param_id, constant}; } void ExprNode::matchVTCTPHelper(int &var_id, int &lag, int ¶m_id, double &constant, bool at_denominator) const { throw MatchFailureException{"Expression not allowed in linear combination of variables"}; } void NumConstNode::matchVTCTPHelper(int &var_id, int &lag, int ¶m_id, double &constant, bool at_denominator) const { double myvalue = eval({}); if (at_denominator) constant /= myvalue; else constant *= myvalue; } void VariableNode::matchVTCTPHelper(int &var_id, int &lag, int ¶m_id, double &constant, bool at_denominator) const { if (at_denominator) throw MatchFailureException{"A variable or parameter cannot appear at denominator"}; SymbolType type = get_type(); if (type == SymbolType::endogenous || type == SymbolType::exogenous) { if (var_id != -1) throw MatchFailureException{"More than one variable in this expression"}; var_id = symb_id; lag = this->lag; } else if (type == SymbolType::parameter) { if (param_id != -1) throw MatchFailureException{"More than one parameter in this expression"}; param_id = symb_id; } else throw MatchFailureException{"Symbol " + datatree.symbol_table.getName(symb_id) + " not allowed here"}; } void UnaryOpNode::matchVTCTPHelper(int &var_id, int &lag, int ¶m_id, double &constant, bool at_denominator) const { if (op_code == UnaryOpcode::uminus) { constant = -constant; arg->matchVTCTPHelper(var_id, lag, param_id, constant, at_denominator); } else throw MatchFailureException{"Operator not allowed in this expression"}; } void BinaryOpNode::matchVTCTPHelper(int &var_id, int &lag, int ¶m_id, double &constant, bool at_denominator) const { if (op_code == BinaryOpcode::times || op_code == BinaryOpcode::divide) { arg1->matchVTCTPHelper(var_id, lag, param_id, constant, at_denominator); if (op_code == BinaryOpcode::times) arg2->matchVTCTPHelper(var_id, lag, param_id, constant, at_denominator); else arg2->matchVTCTPHelper(var_id, lag, param_id, constant, !at_denominator); } else throw MatchFailureException{"Operator not allowed in this expression"}; } vector> ExprNode::matchLinearCombinationOfVariables(bool variable_obligatory_in_each_term) const { vector> terms; decomposeAdditiveTerms(terms); vector> result; for (const auto &it : terms) { expr_t term = it.first; int sign = it.second; auto m = term->matchVariableTimesConstantTimesParam(variable_obligatory_in_each_term); get<3>(m) *= sign; result.push_back(m); } return result; } pair>> ExprNode::matchParamTimesLinearCombinationOfVariables() const { auto bopn = dynamic_cast(this); if (!bopn || bopn->op_code != BinaryOpcode::times) throw MatchFailureException{"Not a multiplicative expression"}; expr_t param = bopn->arg1, lincomb = bopn->arg2; auto is_param = [](expr_t e) { auto vn = dynamic_cast(e); return vn && vn->get_type() == SymbolType::parameter; }; if (!is_param(param)) { swap(param, lincomb); if (!is_param(param)) throw MatchFailureException{"No parameter on either side of the multiplication"}; } return { dynamic_cast(param)->symb_id, lincomb->matchLinearCombinationOfVariables() }; } pair ExprNode::matchParamTimesTargetMinusVariable(int symb_id) const { auto bopn = dynamic_cast(this); if (!bopn || bopn->op_code != BinaryOpcode::times) throw MatchFailureException{"Not a multiplicative expression"}; expr_t param = bopn->arg1, minus = bopn->arg2; auto is_param = [](expr_t e) { auto vn = dynamic_cast(e); return vn && vn->get_type() == SymbolType::parameter; }; if (!is_param(param)) { swap(param, minus); if (!is_param(param)) throw MatchFailureException{"No parameter on either side of the multiplication"}; } auto bminus = dynamic_cast(minus); if (!bminus || bminus->op_code != BinaryOpcode::minus) throw MatchFailureException{"Neither factor is a minus operator"}; auto lhs_level = dynamic_cast(bminus->arg2); auto target = dynamic_cast(bminus->arg1); auto check_target = [&]() { if (target->get_type() != SymbolType::endogenous && target->get_type() != SymbolType::exogenous) return false; if (datatree.symbol_table.isAuxiliaryVariable(target->symb_id)) { auto avi = datatree.symbol_table.getAuxVarInfo(target->symb_id); return (avi.get_type() == AuxVarType::unaryOp && avi.get_unary_op() == "log" && avi.get_orig_symb_id() != -1 && !datatree.symbol_table.isAuxiliaryVariable(avi.get_orig_symb_id()) && target->lag + avi.get_orig_lead_lag() == -1); } else return target->lag == -1; }; if (lhs_level && lhs_level->symb_id == symb_id && target && check_target()) return { dynamic_cast(param)->symb_id, target->symb_id }; else throw MatchFailureException{"Neither factor is of the form (target-variable) where target is endo or exo (possibly logged), and has one lag"}; }