Fix interaction of temporary terms with steady_state operator

When the same complex expression appears outside and inside a steady_state()
operator, the same temporary term would be used for both cases, which was
obviously wrong.

The fix consists in never substituting temporary terms for expressions inside
the steady_state operator().

Incidentally, this implies that external functions can no longer be used inside
steady_state operators (since their computed values are stored inside temporary
terms).
master
Sébastien Villemot 2022-07-05 15:04:36 +02:00
parent db0d9290b5
commit c27342cfeb
No known key found for this signature in database
GPG Key ID: 2CECE9350ECEBE4A
2 changed files with 72 additions and 12 deletions

View File

@ -99,6 +99,13 @@ ExprNode::checkIfTemporaryTermThenWrite(ostream &output, ExprNodeOutputType outp
if (!temporary_terms.contains(const_cast<ExprNode *>(this))) if (!temporary_terms.contains(const_cast<ExprNode *>(this)))
return false; return false;
/* If we are inside a steady_state() operator, the temporary terms do not
apply, since those refer to the dynamic model (assuming that writeOutput()
was initially not called with a steady state output type, which is
typically the case). */
if (isSteadyStateOperatorOutput(output_type))
return false;
auto it2 = temporary_terms_idxs.find(const_cast<ExprNode *>(this)); auto it2 = temporary_terms_idxs.find(const_cast<ExprNode *>(this));
// It is the responsibility of the caller to ensure that all temporary terms have their index // It is the responsibility of the caller to ensure that all temporary terms have their index
assert(it2 != temporary_terms_idxs.end()); assert(it2 != temporary_terms_idxs.end());
@ -113,7 +120,7 @@ bool
ExprNode::checkIfTemporaryTermThenWriteBytecode(BytecodeWriter &code_file, ExprNode::checkIfTemporaryTermThenWriteBytecode(BytecodeWriter &code_file,
const temporary_terms_t &temporary_terms, const temporary_terms_t &temporary_terms,
const temporary_terms_idxs_t &temporary_terms_idxs, const temporary_terms_idxs_t &temporary_terms_idxs,
bool dynamic) const bool dynamic, bool steady_dynamic) const
{ {
if (!temporary_terms.contains(const_cast<ExprNode *>(this))) if (!temporary_terms.contains(const_cast<ExprNode *>(this)))
return false; return false;
@ -122,6 +129,12 @@ ExprNode::checkIfTemporaryTermThenWriteBytecode(BytecodeWriter &code_file,
// It is the responsibility of the caller to ensure that all temporary terms have their index // It is the responsibility of the caller to ensure that all temporary terms have their index
assert(it2 != temporary_terms_idxs.end()); assert(it2 != temporary_terms_idxs.end());
/* If we are inside a steady_state() operator, the temporary terms do not
apply, since those refer to the dynamic model (assuming that writeBytecodeOutput()
was initially not called with steady_dynamic=true). */
if (steady_dynamic)
return false;
if (dynamic) if (dynamic)
code_file << FLDT_{it2->second}; code_file << FLDT_{it2->second};
else else
@ -497,7 +510,7 @@ NumConstNode::writeBytecodeOutput(BytecodeWriter &code_file, [[maybe_unused]] bo
[[maybe_unused]] bool steady_dynamic, [[maybe_unused]] bool steady_dynamic,
[[maybe_unused]] const deriv_node_temp_terms_t &tef_terms) const [[maybe_unused]] const deriv_node_temp_terms_t &tef_terms) const
{ {
if (!checkIfTemporaryTermThenWriteBytecode(code_file, temporary_terms, temporary_terms_idxs, dynamic)) if (!checkIfTemporaryTermThenWriteBytecode(code_file, temporary_terms, temporary_terms_idxs, dynamic, steady_dynamic))
code_file << FLDC_{datatree.num_constants.getDouble(id)}; code_file << FLDC_{datatree.num_constants.getDouble(id)};
} }
@ -1290,7 +1303,7 @@ VariableNode::writeBytecodeOutput(BytecodeWriter &code_file, bool assignment_lhs
const temporary_terms_idxs_t &temporary_terms_idxs, bool dynamic, bool steady_dynamic, const temporary_terms_idxs_t &temporary_terms_idxs, bool dynamic, bool steady_dynamic,
const deriv_node_temp_terms_t &tef_terms) const const deriv_node_temp_terms_t &tef_terms) const
{ {
if (checkIfTemporaryTermThenWriteBytecode(code_file, temporary_terms, temporary_terms_idxs, dynamic)) if (checkIfTemporaryTermThenWriteBytecode(code_file, temporary_terms, temporary_terms_idxs, dynamic, steady_dynamic))
return; return;
auto type = get_type(); auto type = get_type();
@ -2404,7 +2417,8 @@ UnaryOpNode::computeTemporaryTerms(const pair<int, int> &derivOrder,
it == reference_count.end()) it == reference_count.end())
{ {
reference_count[this2] = { 1, derivOrder }; reference_count[this2] = { 1, derivOrder };
arg->computeTemporaryTerms(derivOrder, temp_terms_map, reference_count, is_matlab); if (op_code != UnaryOpcode::steadyState) // See comment in checkIfTemporaryTermThenWrite{,Bytecode}()
arg->computeTemporaryTerms(derivOrder, temp_terms_map, reference_count, is_matlab);
} }
else else
{ {
@ -2424,7 +2438,8 @@ UnaryOpNode::computeBlockTemporaryTerms(int blk, int eq, vector<vector<temporary
it == reference_count.end()) it == reference_count.end())
{ {
reference_count[this2] = { 1, blk, eq }; reference_count[this2] = { 1, blk, eq };
arg->computeBlockTemporaryTerms(blk, eq, blocks_temporary_terms, reference_count); if (op_code != UnaryOpcode::steadyState) // See comment in checkIfTemporaryTermThenWrite{,Bytecode}()
arg->computeBlockTemporaryTerms(blk, eq, blocks_temporary_terms, reference_count);
} }
else else
{ {
@ -3056,7 +3071,7 @@ UnaryOpNode::writeBytecodeOutput(BytecodeWriter &code_file, bool assignment_lhs,
const temporary_terms_idxs_t &temporary_terms_idxs, bool dynamic, bool steady_dynamic, const temporary_terms_idxs_t &temporary_terms_idxs, bool dynamic, bool steady_dynamic,
const deriv_node_temp_terms_t &tef_terms) const const deriv_node_temp_terms_t &tef_terms) const
{ {
if (checkIfTemporaryTermThenWriteBytecode(code_file, temporary_terms, temporary_terms_idxs, dynamic)) if (checkIfTemporaryTermThenWriteBytecode(code_file, temporary_terms, temporary_terms_idxs, dynamic, steady_dynamic))
return; return;
if (op_code == UnaryOpcode::steadyState) if (op_code == UnaryOpcode::steadyState)
@ -4305,7 +4320,7 @@ BinaryOpNode::writeBytecodeOutput(BytecodeWriter &code_file, bool assignment_lhs
const temporary_terms_idxs_t &temporary_terms_idxs, bool dynamic, bool steady_dynamic, const temporary_terms_idxs_t &temporary_terms_idxs, bool dynamic, bool steady_dynamic,
const deriv_node_temp_terms_t &tef_terms) const const deriv_node_temp_terms_t &tef_terms) const
{ {
if (checkIfTemporaryTermThenWriteBytecode(code_file, temporary_terms, temporary_terms_idxs, dynamic)) if (checkIfTemporaryTermThenWriteBytecode(code_file, temporary_terms, temporary_terms_idxs, dynamic, steady_dynamic))
return; return;
if (op_code == BinaryOpcode::powerDeriv) if (op_code == BinaryOpcode::powerDeriv)
@ -5991,7 +6006,7 @@ TrinaryOpNode::writeBytecodeOutput(BytecodeWriter &code_file, bool assignment_lh
const temporary_terms_idxs_t &temporary_terms_idxs, bool dynamic, bool steady_dynamic, const temporary_terms_idxs_t &temporary_terms_idxs, bool dynamic, bool steady_dynamic,
const deriv_node_temp_terms_t &tef_terms) const const deriv_node_temp_terms_t &tef_terms) const
{ {
if (checkIfTemporaryTermThenWriteBytecode(code_file, temporary_terms, temporary_terms_idxs, dynamic)) if (checkIfTemporaryTermThenWriteBytecode(code_file, temporary_terms, temporary_terms_idxs, dynamic, steady_dynamic))
return; return;
arg1->writeBytecodeOutput(code_file, assignment_lhs, temporary_terms, temporary_terms_idxs, dynamic, steady_dynamic, tef_terms); arg1->writeBytecodeOutput(code_file, assignment_lhs, temporary_terms, temporary_terms_idxs, dynamic, steady_dynamic, tef_terms);
@ -7219,7 +7234,13 @@ ExternalFunctionNode::writeBytecodeOutput(BytecodeWriter &code_file, bool assign
[[maybe_unused]] bool steady_dynamic, [[maybe_unused]] bool steady_dynamic,
const deriv_node_temp_terms_t &tef_terms) const const deriv_node_temp_terms_t &tef_terms) const
{ {
if (checkIfTemporaryTermThenWriteBytecode(code_file, temporary_terms, temporary_terms_idxs, dynamic)) if (steady_dynamic)
{
cerr << "ERROR: The expression inside a steady_state operator cannot contain external functions" << endl;
exit(EXIT_FAILURE);
}
if (checkIfTemporaryTermThenWriteBytecode(code_file, temporary_terms, temporary_terms_idxs, dynamic, steady_dynamic))
return; return;
if (!assignment_lhs) if (!assignment_lhs)
@ -7331,6 +7352,12 @@ ExternalFunctionNode::writeOutput(ostream &output, ExprNodeOutputType output_typ
return; return;
} }
if (isSteadyStateOperatorOutput(output_type))
{
cerr << "ERROR: The expression inside a steady_state operator cannot contain external functions" << endl;
exit(EXIT_FAILURE);
}
if (checkIfTemporaryTermThenWrite(output, output_type, temporary_terms, temporary_terms_idxs)) if (checkIfTemporaryTermThenWrite(output, output_type, temporary_terms, temporary_terms_idxs))
return; return;
@ -7563,6 +7590,12 @@ FirstDerivExternalFunctionNode::writeOutput(ostream &output, ExprNodeOutputType
return; return;
} }
if (isSteadyStateOperatorOutput(output_type))
{
cerr << "ERROR: The expression inside a steady_state operator cannot contain external functions" << endl;
exit(EXIT_FAILURE);
}
if (checkIfTemporaryTermThenWrite(output, output_type, temporary_terms, temporary_terms_idxs)) if (checkIfTemporaryTermThenWrite(output, output_type, temporary_terms, temporary_terms_idxs))
return; return;
@ -7592,7 +7625,13 @@ FirstDerivExternalFunctionNode::writeBytecodeOutput(BytecodeWriter &code_file, b
[[maybe_unused]] bool steady_dynamic, [[maybe_unused]] bool steady_dynamic,
const deriv_node_temp_terms_t &tef_terms) const const deriv_node_temp_terms_t &tef_terms) const
{ {
if (checkIfTemporaryTermThenWriteBytecode(code_file, temporary_terms, temporary_terms_idxs, dynamic)) if (steady_dynamic)
{
cerr << "ERROR: The expression inside a steady_state operator cannot contain external functions" << endl;
exit(EXIT_FAILURE);
}
if (checkIfTemporaryTermThenWriteBytecode(code_file, temporary_terms, temporary_terms_idxs, dynamic, steady_dynamic))
return; return;
int first_deriv_symb_id = datatree.external_functions_table.getFirstDerivSymbID(symb_id); int first_deriv_symb_id = datatree.external_functions_table.getFirstDerivSymbID(symb_id);
@ -7906,6 +7945,12 @@ SecondDerivExternalFunctionNode::writeOutput(ostream &output, ExprNodeOutputType
return; return;
} }
if (isSteadyStateOperatorOutput(output_type))
{
cerr << "ERROR: The expression inside a steady_state operator cannot contain external functions" << endl;
exit(EXIT_FAILURE);
}
if (checkIfTemporaryTermThenWrite(output, output_type, temporary_terms, temporary_terms_idxs)) if (checkIfTemporaryTermThenWrite(output, output_type, temporary_terms, temporary_terms_idxs))
return; return;
@ -8110,7 +8155,13 @@ SecondDerivExternalFunctionNode::writeBytecodeOutput(BytecodeWriter &code_file,
[[maybe_unused]] bool steady_dynamic, [[maybe_unused]] bool steady_dynamic,
const deriv_node_temp_terms_t &tef_terms) const const deriv_node_temp_terms_t &tef_terms) const
{ {
if (checkIfTemporaryTermThenWriteBytecode(code_file, temporary_terms, temporary_terms_idxs, dynamic)) if (steady_dynamic)
{
cerr << "ERROR: The expression inside a steady_state operator cannot contain external functions" << endl;
exit(EXIT_FAILURE);
}
if (checkIfTemporaryTermThenWriteBytecode(code_file, temporary_terms, temporary_terms_idxs, dynamic, steady_dynamic))
return; return;
int second_deriv_symb_id = datatree.external_functions_table.getSecondDerivSymbID(symb_id); int second_deriv_symb_id = datatree.external_functions_table.getSecondDerivSymbID(symb_id);

View File

@ -143,6 +143,15 @@ isLatexOutput(ExprNodeOutputType output_type)
|| output_type == ExprNodeOutputType::latexDynamicSteadyStateOperator; || output_type == ExprNodeOutputType::latexDynamicSteadyStateOperator;
} }
constexpr bool
isSteadyStateOperatorOutput(ExprNodeOutputType output_type)
{
return output_type == ExprNodeOutputType::latexDynamicSteadyStateOperator
|| output_type == ExprNodeOutputType::matlabDynamicSteadyStateOperator
|| output_type == ExprNodeOutputType::CDynamicSteadyStateOperator
|| output_type == ExprNodeOutputType::juliaDynamicSteadyStateOperator;
}
/* Equal to 1 for Matlab langage or Julia, or to 0 for C language. Not defined for LaTeX. /* Equal to 1 for Matlab langage or Julia, or to 0 for C language. Not defined for LaTeX.
In Matlab and Julia, array indexes begin at 1, while they begin at 0 in C */ In Matlab and Julia, array indexes begin at 1, while they begin at 0 in C */
constexpr int constexpr int
@ -245,7 +254,7 @@ protected:
bool checkIfTemporaryTermThenWriteBytecode(BytecodeWriter &code_file, bool checkIfTemporaryTermThenWriteBytecode(BytecodeWriter &code_file,
const temporary_terms_t &temporary_terms, const temporary_terms_t &temporary_terms,
const temporary_terms_idxs_t &temporary_terms_idxs, const temporary_terms_idxs_t &temporary_terms_idxs,
bool dynamic) const; bool dynamic, bool steady_dynamic) const;
// Internal helper for matchVariableTimesConstantTimesParam() // Internal helper for matchVariableTimesConstantTimesParam()
virtual void matchVTCTPHelper(optional<int> &var_id, int &lag, optional<int> &param_id, double &constant, bool at_denominator) const; virtual void matchVTCTPHelper(optional<int> &var_id, int &lag, optional<int> &param_id, double &constant, bool at_denominator) const;