From af16cf6e22bb3abf4a413b02b195872c815d9c03 Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Fri, 12 Dec 2025 13:26:19 +1300 Subject: [PATCH 01/44] Hardcoded installation of symengine and gmp --- src/CMakeLists.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 466fb2930..d971b4e33 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -178,6 +178,18 @@ add_library(cellml ${API_HEADER_FILES} ) +target_include_directories(cellml + PUBLIC + "C:/symengine/include" + "C:/gmp/include" +) + +target_link_libraries(cellml + PUBLIC + "C:/symengine/lib/symengine.lib" + "C:/gmp/lib/gmp.lib" +) + set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/xmldoc.cpp PROPERTIES COMPILE_DEFINITIONS XML_ERROR_CALLBACK_ARGUMENT_TYPE=${CONST_ERROR_STRUCTURED_ERROR_CALLBACK_TYPE}) From 9e2b0b3be0d71e3b7c244bedc46b71a0810bef7e Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Fri, 12 Dec 2025 13:26:37 +1300 Subject: [PATCH 02/44] Created basic rearrangement test case --- tests/analyser/analyser.cpp | 14 +++++++++ .../unarranged_algebraic_equation.cellml | 29 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 tests/resources/analyser/unarranged_algebraic_equation.cellml diff --git a/tests/analyser/analyser.cpp b/tests/analyser/analyser.cpp index ecc9db714..add9e0f43 100644 --- a/tests/analyser/analyser.cpp +++ b/tests/analyser/analyser.cpp @@ -1060,3 +1060,17 @@ TEST(Analyser, unsuitablyConstrainedNlaSystem) EXPECT_EQ(libcellml::AnalyserModel::Type::UNSUITABLY_CONSTRAINED, analyser->analyserModel()->type()); } + +TEST(Analyser, rearrangeAlgebraicEquation) +{ + auto parser = libcellml::Parser::create(); + auto model = parser->parseModel(fileContents("analyser/unarranged_algebraic_equation.cellml")); + + EXPECT_EQ(size_t(0), parser->issueCount()); + + auto analyser = libcellml::Analyser::create(); + + analyser->analyseModel(model); + + EXPECT_EQ(libcellml::AnalyserModel::Type::ALGEBRAIC, analyser->analyserModel()->type()); +} diff --git a/tests/resources/analyser/unarranged_algebraic_equation.cellml b/tests/resources/analyser/unarranged_algebraic_equation.cellml new file mode 100644 index 000000000..5840ba82b --- /dev/null +++ b/tests/resources/analyser/unarranged_algebraic_equation.cellml @@ -0,0 +1,29 @@ + + + + + + + + + + x + 1 + + + + z + 3 + + + + x + + + y + z + + + + + From dafb56e8cb531de59d27d9ca378f750dec319c8c Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Fri, 12 Dec 2025 13:26:58 +1300 Subject: [PATCH 03/44] Basic implementation of equation rearrangement --- src/analyser.cpp | 130 +++++++++++++++++++++++++++++++++++++++++++++++ src/analyser_p.h | 12 +++++ 2 files changed, 142 insertions(+) diff --git a/src/analyser.cpp b/src/analyser.cpp index e79550d27..f7bf7f00c 100644 --- a/src/analyser.cpp +++ b/src/analyser.cpp @@ -22,6 +22,7 @@ limitations under the License. #include #include +#include #include "libcellml/analyserequation.h" #include "libcellml/analyserexternalvariable.h" @@ -198,6 +199,127 @@ bool AnalyserInternalEquation::variableOnLhsOrRhs(const AnalyserInternalVariable || variableOnRhs(variable); } +SymEngine::RCP AnalyserInternalEquation::symEngineRepresentation(AnalyserEquationAstPtr ast, const std::map> &symbolMap) +{ + if (ast == nullptr) { + return SymEngine::null; + } + + AnalyserEquationAstPtr leftAst = ast->leftChild(); + AnalyserEquationAstPtr rightAst = ast->rightChild(); + + // Recursively call getConvertedAst on left and right children. + SymEngine::RCP left = symEngineRepresentation(leftAst, symbolMap); + SymEngine::RCP right = symEngineRepresentation(rightAst, symbolMap); + + // Analyse mAst current type and value. + switch (ast->type()) { + case AnalyserEquationAst::Type::EQUALITY: + return Eq(left, right); + case AnalyserEquationAst::Type::PLUS: + return add(left, right); + case AnalyserEquationAst::Type::CI: + // Seems like the voi doesn't exist in mAllVariables, so we don't have an easy means of access. + // For now we'll just throw an error if the symbol is not found. + if (symbolMap.find(ast->variable()->name()) == symbolMap.end()) { + throw std::runtime_error("Unsupported variable in symEngineRepresentation"); + } + return symbolMap.at(ast->variable()->name()); + default: + // Our parser is unable to handle this type, so we need to let the caller know by throwing an error. + throw std::runtime_error("Unsupported AST type in symEngineRepresentation"); + } +} + +AnalyserEquationAstPtr AnalyserInternalEquation::parseSymEngineExpression(SymEngine::RCP &seExpression, + std::map, AnalyserInternalVariablePtr, SymEngine::RCPBasicKeyLess> &astMap) +{ + auto children = seExpression->get_args(); + + AnalyserEquationAstPtr ast = AnalyserEquationAst::create(); + + switch (seExpression->get_type_code()) { + case SymEngine::SYMENGINE_EQUALITY: { + ast->setType(AnalyserEquationAst::Type::EQUALITY); + break; + } + case SymEngine::SYMENGINE_ADD: { + ast->setType(AnalyserEquationAst::Type::PLUS); + break; + } + case SymEngine::SYMENGINE_MUL: { + ast->setType(AnalyserEquationAst::Type::TIMES); + break; + } + case SymEngine::SYMENGINE_SYMBOL: { + SymEngine::RCP symbolExpr = SymEngine::rcp_dynamic_cast(seExpression); + ast->setType(AnalyserEquationAst::Type::CI); + ast->setVariable(astMap.at(symbolExpr)->mVariable); + break; + } + default: + break; + } + + // Assume two children max. + // This is likely wrong since SYMENGINE_ADD could have x + y + z (and thus 3 children), + // but it's sufficient for this very early implementation. + if (children.size() > 0) { + ast->setLeftChild(parseSymEngineExpression(children[0], astMap)); + if (children.size() > 1) { + ast->setRightChild(parseSymEngineExpression(children[1], astMap)); + } + } + + return ast; +} + +AnalyserEquationAstPtr AnalyserInternalEquation::rearrangeFor(const AnalyserInternalVariablePtr &variable) +{ + std::map> symbolMap; + std::map, AnalyserInternalVariablePtr, SymEngine::RCPBasicKeyLess> astMap; + + for (const auto &variable : mAllVariables) { + SymEngine::RCP symbol = SymEngine::symbol(variable->mVariable->name()); + symbolMap[variable->mVariable->name()] = symbol; + astMap[symbol] = variable; + } + + SymEngine::RCP equation; + try { + equation = symEngineRepresentation(mAst, symbolMap); + } catch (const std::runtime_error &e) { + // Our parser was unable to convert the AST to a SymEngine expression. + return nullptr; + } + + SymEngine::RCP solutionSet = solve(equation, symbolMap[variable->mVariable->name()]); + SymEngine::vec_basic solutions = solutionSet->get_args(); + + // Our system needs to be able to isolate a single solution. + if (solutions.size() != 1) { + return nullptr; + } + SymEngine::RCP answer = solutions.front(); + + // Rebuild the AST from the rearranged expression. + AnalyserEquationAstPtr ast = AnalyserEquationAst::create(); + AnalyserEquationAstPtr isolatedVariableAst = AnalyserEquationAst::create(); + AnalyserEquationAstPtr rearrangedEquationAst = parseSymEngineExpression(answer, astMap); + + ast->setType(AnalyserEquationAst::Type::EQUALITY); + ast->setLeftChild(isolatedVariableAst); + ast->setRightChild(rearrangedEquationAst); + + isolatedVariableAst->setType(AnalyserEquationAst::Type::CI); + isolatedVariableAst->setVariable(variable->mVariable); + isolatedVariableAst->setParent(ast); + + rearrangedEquationAst->setParent(ast); + + return ast; +} + bool AnalyserInternalEquation::check(const AnalyserModelPtr &analyserModel, bool checkNlaSystems) { // Nothing to check if the equation has a known type. @@ -278,6 +400,14 @@ bool AnalyserInternalEquation::check(const AnalyserModelPtr &analyserModel, bool mVariables.front() : nullptr; + // If we have one variable left, but it's not isolated, try to rearrange it. + if ((unknownVariableLeft != nullptr) && !variableOnLhsOrRhs(unknownVariableLeft)) { + auto newAst = rearrangeFor(unknownVariableLeft); + if (newAst != nullptr) { + mAst = newAst; + } + } + if (((unknownVariableLeft != nullptr) && (checkNlaSystems || variableOnLhsOrRhs(unknownVariableLeft))) || !initialisedVariables.empty()) { diff --git a/src/analyser_p.h b/src/analyser_p.h index f888861de..b27818673 100644 --- a/src/analyser_p.h +++ b/src/analyser_p.h @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +#include + #include "libcellml/generatorprofile.h" #include "libcellml/issue.h" @@ -22,6 +24,12 @@ limitations under the License. #include "logger_p.h" #include "utilities.h" +namespace SymEngine { +template class RCP; +class Basic; +class Symbol; +} // namespace SymEngine + namespace libcellml { struct AnalyserInternalEquation; @@ -128,6 +136,10 @@ struct AnalyserInternalEquation bool variableOnRhs(const AnalyserInternalVariablePtr &variable); bool variableOnLhsOrRhs(const AnalyserInternalVariablePtr &variable); + SymEngine::RCP symEngineRepresentation(AnalyserEquationAstPtr ast, const std::map> &symbolMap); + AnalyserEquationAstPtr parseSymEngineExpression(SymEngine::RCP &seExpression, std::map, AnalyserInternalVariablePtr, SymEngine::RCPBasicKeyLess> &astMap); + AnalyserEquationAstPtr rearrangeFor(const AnalyserInternalVariablePtr &variable); + bool check(const AnalyserModelPtr &analyserModel, bool checkNlaSystems); }; From 4331798338bb36492f6d3fc09d62304ce6af36c5 Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Fri, 12 Dec 2025 08:57:53 +1300 Subject: [PATCH 04/44] Change symEngineRepresentation to symEngineEquation --- src/analyser.cpp | 8 ++++---- src/analyser_p.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/analyser.cpp b/src/analyser.cpp index f7bf7f00c..6d840e75c 100644 --- a/src/analyser.cpp +++ b/src/analyser.cpp @@ -199,7 +199,7 @@ bool AnalyserInternalEquation::variableOnLhsOrRhs(const AnalyserInternalVariable || variableOnRhs(variable); } -SymEngine::RCP AnalyserInternalEquation::symEngineRepresentation(AnalyserEquationAstPtr ast, const std::map> &symbolMap) +SymEngine::RCP AnalyserInternalEquation::symEngineEquation(AnalyserEquationAstPtr ast, const std::map> &symbolMap) { if (ast == nullptr) { return SymEngine::null; @@ -209,8 +209,8 @@ SymEngine::RCP AnalyserInternalEquation::symEngineRepres AnalyserEquationAstPtr rightAst = ast->rightChild(); // Recursively call getConvertedAst on left and right children. - SymEngine::RCP left = symEngineRepresentation(leftAst, symbolMap); - SymEngine::RCP right = symEngineRepresentation(rightAst, symbolMap); + SymEngine::RCP left = symEngineEquation(leftAst, symbolMap); + SymEngine::RCP right = symEngineEquation(rightAst, symbolMap); // Analyse mAst current type and value. switch (ast->type()) { @@ -287,7 +287,7 @@ AnalyserEquationAstPtr AnalyserInternalEquation::rearrangeFor(const AnalyserInte SymEngine::RCP equation; try { - equation = symEngineRepresentation(mAst, symbolMap); + equation = symEngineEquation(mAst, symbolMap); } catch (const std::runtime_error &e) { // Our parser was unable to convert the AST to a SymEngine expression. return nullptr; diff --git a/src/analyser_p.h b/src/analyser_p.h index b27818673..554ea132c 100644 --- a/src/analyser_p.h +++ b/src/analyser_p.h @@ -136,7 +136,7 @@ struct AnalyserInternalEquation bool variableOnRhs(const AnalyserInternalVariablePtr &variable); bool variableOnLhsOrRhs(const AnalyserInternalVariablePtr &variable); - SymEngine::RCP symEngineRepresentation(AnalyserEquationAstPtr ast, const std::map> &symbolMap); + SymEngine::RCP symEngineEquation(AnalyserEquationAstPtr ast, const std::map> &symbolMap); AnalyserEquationAstPtr parseSymEngineExpression(SymEngine::RCP &seExpression, std::map, AnalyserInternalVariablePtr, SymEngine::RCPBasicKeyLess> &astMap); AnalyserEquationAstPtr rearrangeFor(const AnalyserInternalVariablePtr &variable); From 3953282f5498fb507d76eb3e66b08494ab5f5b5c Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Fri, 12 Dec 2025 09:10:53 +1300 Subject: [PATCH 05/44] Added TODO comments --- src/analyser.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/analyser.cpp b/src/analyser.cpp index 6d840e75c..cf5af0d91 100644 --- a/src/analyser.cpp +++ b/src/analyser.cpp @@ -261,9 +261,7 @@ AnalyserEquationAstPtr AnalyserInternalEquation::parseSymEngineExpression(SymEng break; } - // Assume two children max. - // This is likely wrong since SYMENGINE_ADD could have x + y + z (and thus 3 children), - // but it's sufficient for this very early implementation. + // TODO Update to account for symengine expressions with 3 or more children. if (children.size() > 0) { ast->setLeftChild(parseSymEngineExpression(children[0], astMap)); if (children.size() > 1) { @@ -404,6 +402,7 @@ bool AnalyserInternalEquation::check(const AnalyserModelPtr &analyserModel, bool if ((unknownVariableLeft != nullptr) && !variableOnLhsOrRhs(unknownVariableLeft)) { auto newAst = rearrangeFor(unknownVariableLeft); if (newAst != nullptr) { + // TODO Update variables and/or equation type when necessary. mAst = newAst; } } From 2de909875418fdb886a749c0436371b43d86e99d Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Fri, 12 Dec 2025 09:24:01 +1300 Subject: [PATCH 06/44] Renamed equation to seEquation --- src/analyser.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/analyser.cpp b/src/analyser.cpp index cf5af0d91..6ed947527 100644 --- a/src/analyser.cpp +++ b/src/analyser.cpp @@ -283,15 +283,15 @@ AnalyserEquationAstPtr AnalyserInternalEquation::rearrangeFor(const AnalyserInte astMap[symbol] = variable; } - SymEngine::RCP equation; + SymEngine::RCP seEquation; try { - equation = symEngineEquation(mAst, symbolMap); + seEquation = symEngineEquation(mAst, symbolMap); } catch (const std::runtime_error &e) { // Our parser was unable to convert the AST to a SymEngine expression. return nullptr; } - SymEngine::RCP solutionSet = solve(equation, symbolMap[variable->mVariable->name()]); + SymEngine::RCP solutionSet = solve(seEquation, symbolMap[variable->mVariable->name()]); SymEngine::vec_basic solutions = solutionSet->get_args(); // Our system needs to be able to isolate a single solution. From 123867384846e64fcae8903258855bedf0166c6c Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Fri, 12 Dec 2025 09:48:54 +1300 Subject: [PATCH 07/44] Defined type aliases for symengine maps Also changed the name of astMap to variableMap to better reflect what it's actually mapping to --- src/analyser.cpp | 18 +++++++++--------- src/analyser_p.h | 7 +++++-- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/analyser.cpp b/src/analyser.cpp index 6ed947527..c2a30387e 100644 --- a/src/analyser.cpp +++ b/src/analyser.cpp @@ -199,7 +199,7 @@ bool AnalyserInternalEquation::variableOnLhsOrRhs(const AnalyserInternalVariable || variableOnRhs(variable); } -SymEngine::RCP AnalyserInternalEquation::symEngineEquation(AnalyserEquationAstPtr ast, const std::map> &symbolMap) +SymEngine::RCP AnalyserInternalEquation::symEngineEquation(AnalyserEquationAstPtr ast, const SymEngineSymbolMap &symbolMap) { if (ast == nullptr) { return SymEngine::null; @@ -232,7 +232,7 @@ SymEngine::RCP AnalyserInternalEquation::symEngineEquati } AnalyserEquationAstPtr AnalyserInternalEquation::parseSymEngineExpression(SymEngine::RCP &seExpression, - std::map, AnalyserInternalVariablePtr, SymEngine::RCPBasicKeyLess> &astMap) + SymEngineVariableMap &variableMap) { auto children = seExpression->get_args(); @@ -254,7 +254,7 @@ AnalyserEquationAstPtr AnalyserInternalEquation::parseSymEngineExpression(SymEng case SymEngine::SYMENGINE_SYMBOL: { SymEngine::RCP symbolExpr = SymEngine::rcp_dynamic_cast(seExpression); ast->setType(AnalyserEquationAst::Type::CI); - ast->setVariable(astMap.at(symbolExpr)->mVariable); + ast->setVariable(variableMap.at(symbolExpr)->mVariable); break; } default: @@ -263,9 +263,9 @@ AnalyserEquationAstPtr AnalyserInternalEquation::parseSymEngineExpression(SymEng // TODO Update to account for symengine expressions with 3 or more children. if (children.size() > 0) { - ast->setLeftChild(parseSymEngineExpression(children[0], astMap)); + ast->setLeftChild(parseSymEngineExpression(children[0], variableMap)); if (children.size() > 1) { - ast->setRightChild(parseSymEngineExpression(children[1], astMap)); + ast->setRightChild(parseSymEngineExpression(children[1], variableMap)); } } @@ -274,13 +274,13 @@ AnalyserEquationAstPtr AnalyserInternalEquation::parseSymEngineExpression(SymEng AnalyserEquationAstPtr AnalyserInternalEquation::rearrangeFor(const AnalyserInternalVariablePtr &variable) { - std::map> symbolMap; - std::map, AnalyserInternalVariablePtr, SymEngine::RCPBasicKeyLess> astMap; + SymEngineSymbolMap symbolMap; + SymEngineVariableMap variableMap; for (const auto &variable : mAllVariables) { SymEngine::RCP symbol = SymEngine::symbol(variable->mVariable->name()); symbolMap[variable->mVariable->name()] = symbol; - astMap[symbol] = variable; + variableMap[symbol] = variable; } SymEngine::RCP seEquation; @@ -303,7 +303,7 @@ AnalyserEquationAstPtr AnalyserInternalEquation::rearrangeFor(const AnalyserInte // Rebuild the AST from the rearranged expression. AnalyserEquationAstPtr ast = AnalyserEquationAst::create(); AnalyserEquationAstPtr isolatedVariableAst = AnalyserEquationAst::create(); - AnalyserEquationAstPtr rearrangedEquationAst = parseSymEngineExpression(answer, astMap); + AnalyserEquationAstPtr rearrangedEquationAst = parseSymEngineExpression(answer, variableMap); ast->setType(AnalyserEquationAst::Type::EQUALITY); ast->setLeftChild(isolatedVariableAst); diff --git a/src/analyser_p.h b/src/analyser_p.h index 554ea132c..537a8923a 100644 --- a/src/analyser_p.h +++ b/src/analyser_p.h @@ -47,6 +47,9 @@ using AnalyserEquationPtrs = std::vector; using AnalyserVariablePtrs = std::vector; using AnalyserExternalVariablePtrs = std::vector; +using SymEngineVariableMap = std::map, AnalyserInternalVariablePtr, SymEngine::RCPBasicKeyLess>; +using SymEngineSymbolMap = std::map>; + struct AnalyserInternalVariable { enum struct Type @@ -136,8 +139,8 @@ struct AnalyserInternalEquation bool variableOnRhs(const AnalyserInternalVariablePtr &variable); bool variableOnLhsOrRhs(const AnalyserInternalVariablePtr &variable); - SymEngine::RCP symEngineEquation(AnalyserEquationAstPtr ast, const std::map> &symbolMap); - AnalyserEquationAstPtr parseSymEngineExpression(SymEngine::RCP &seExpression, std::map, AnalyserInternalVariablePtr, SymEngine::RCPBasicKeyLess> &astMap); + SymEngine::RCP symEngineEquation(AnalyserEquationAstPtr ast, const SymEngineSymbolMap &symbolMap); + AnalyserEquationAstPtr parseSymEngineExpression(SymEngine::RCP &seExpression, SymEngineVariableMap &variableMap); AnalyserEquationAstPtr rearrangeFor(const AnalyserInternalVariablePtr &variable); bool check(const AnalyserModelPtr &analyserModel, bool checkNlaSystems); From 539dbf76e1decdd7bce1588e05c3b6667482a33e Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Fri, 12 Dec 2025 09:52:09 +1300 Subject: [PATCH 08/44] Updated function parameters to use const and pass by reference --- src/analyser.cpp | 6 +++--- src/analyser_p.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/analyser.cpp b/src/analyser.cpp index c2a30387e..2bf2de902 100644 --- a/src/analyser.cpp +++ b/src/analyser.cpp @@ -199,7 +199,7 @@ bool AnalyserInternalEquation::variableOnLhsOrRhs(const AnalyserInternalVariable || variableOnRhs(variable); } -SymEngine::RCP AnalyserInternalEquation::symEngineEquation(AnalyserEquationAstPtr ast, const SymEngineSymbolMap &symbolMap) +SymEngine::RCP AnalyserInternalEquation::symEngineEquation(const AnalyserEquationAstPtr &ast, const SymEngineSymbolMap &symbolMap) { if (ast == nullptr) { return SymEngine::null; @@ -231,8 +231,8 @@ SymEngine::RCP AnalyserInternalEquation::symEngineEquati } } -AnalyserEquationAstPtr AnalyserInternalEquation::parseSymEngineExpression(SymEngine::RCP &seExpression, - SymEngineVariableMap &variableMap) +AnalyserEquationAstPtr AnalyserInternalEquation::parseSymEngineExpression(const SymEngine::RCP &seExpression, + const SymEngineVariableMap &variableMap) { auto children = seExpression->get_args(); diff --git a/src/analyser_p.h b/src/analyser_p.h index 537a8923a..2bb7f5eb9 100644 --- a/src/analyser_p.h +++ b/src/analyser_p.h @@ -139,8 +139,8 @@ struct AnalyserInternalEquation bool variableOnRhs(const AnalyserInternalVariablePtr &variable); bool variableOnLhsOrRhs(const AnalyserInternalVariablePtr &variable); - SymEngine::RCP symEngineEquation(AnalyserEquationAstPtr ast, const SymEngineSymbolMap &symbolMap); - AnalyserEquationAstPtr parseSymEngineExpression(SymEngine::RCP &seExpression, SymEngineVariableMap &variableMap); + SymEngine::RCP symEngineEquation(const AnalyserEquationAstPtr &ast, const SymEngineSymbolMap &symbolMap); + AnalyserEquationAstPtr parseSymEngineExpression(const SymEngine::RCP &seExpression, const SymEngineVariableMap &variableMap); AnalyserEquationAstPtr rearrangeFor(const AnalyserInternalVariablePtr &variable); bool check(const AnalyserModelPtr &analyserModel, bool checkNlaSystems); From f2cab14ebd467fb137c5ed4d891f446f5b3312d5 Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Fri, 12 Dec 2025 11:50:18 +1300 Subject: [PATCH 09/44] Added support for parsing SymEngine integers to AST --- src/analyser.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/analyser.cpp b/src/analyser.cpp index 2bf2de902..8267f994c 100644 --- a/src/analyser.cpp +++ b/src/analyser.cpp @@ -257,6 +257,11 @@ AnalyserEquationAstPtr AnalyserInternalEquation::parseSymEngineExpression(const ast->setVariable(variableMap.at(symbolExpr)->mVariable); break; } + case SymEngine::SYMENGINE_INTEGER: { + ast->setType(AnalyserEquationAst::Type::CN); + ast->setValue(seExpression->__str__()); + break; + } default: break; } From bdb3bd112fdefb156efe8db7764a76b9e216e59c Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Fri, 12 Dec 2025 12:06:29 +1300 Subject: [PATCH 10/44] Assign parenthood to recreated ast tree --- src/analyser.cpp | 9 ++++++--- src/analyser_p.h | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/analyser.cpp b/src/analyser.cpp index 8267f994c..33788a527 100644 --- a/src/analyser.cpp +++ b/src/analyser.cpp @@ -232,12 +232,15 @@ SymEngine::RCP AnalyserInternalEquation::symEngineEquati } AnalyserEquationAstPtr AnalyserInternalEquation::parseSymEngineExpression(const SymEngine::RCP &seExpression, + const AnalyserEquationAstPtr &parentAst, const SymEngineVariableMap &variableMap) { auto children = seExpression->get_args(); AnalyserEquationAstPtr ast = AnalyserEquationAst::create(); + ast->setParent(parentAst); + switch (seExpression->get_type_code()) { case SymEngine::SYMENGINE_EQUALITY: { ast->setType(AnalyserEquationAst::Type::EQUALITY); @@ -268,9 +271,9 @@ AnalyserEquationAstPtr AnalyserInternalEquation::parseSymEngineExpression(const // TODO Update to account for symengine expressions with 3 or more children. if (children.size() > 0) { - ast->setLeftChild(parseSymEngineExpression(children[0], variableMap)); + ast->setLeftChild(parseSymEngineExpression(children[0], ast, variableMap)); if (children.size() > 1) { - ast->setRightChild(parseSymEngineExpression(children[1], variableMap)); + ast->setRightChild(parseSymEngineExpression(children[1], ast, variableMap)); } } @@ -308,7 +311,7 @@ AnalyserEquationAstPtr AnalyserInternalEquation::rearrangeFor(const AnalyserInte // Rebuild the AST from the rearranged expression. AnalyserEquationAstPtr ast = AnalyserEquationAst::create(); AnalyserEquationAstPtr isolatedVariableAst = AnalyserEquationAst::create(); - AnalyserEquationAstPtr rearrangedEquationAst = parseSymEngineExpression(answer, variableMap); + AnalyserEquationAstPtr rearrangedEquationAst = parseSymEngineExpression(answer, nullptr, variableMap); ast->setType(AnalyserEquationAst::Type::EQUALITY); ast->setLeftChild(isolatedVariableAst); diff --git a/src/analyser_p.h b/src/analyser_p.h index 2bb7f5eb9..f5aec6745 100644 --- a/src/analyser_p.h +++ b/src/analyser_p.h @@ -140,7 +140,7 @@ struct AnalyserInternalEquation bool variableOnLhsOrRhs(const AnalyserInternalVariablePtr &variable); SymEngine::RCP symEngineEquation(const AnalyserEquationAstPtr &ast, const SymEngineSymbolMap &symbolMap); - AnalyserEquationAstPtr parseSymEngineExpression(const SymEngine::RCP &seExpression, const SymEngineVariableMap &variableMap); + AnalyserEquationAstPtr parseSymEngineExpression(const SymEngine::RCP &seExpression, const AnalyserEquationAstPtr &parentAst, const SymEngineVariableMap &variableMap); AnalyserEquationAstPtr rearrangeFor(const AnalyserInternalVariablePtr &variable); bool check(const AnalyserModelPtr &analyserModel, bool checkNlaSystems); From 3b2c5cc112d0a1d654c4103ac1b76b45ee472e19 Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Fri, 12 Dec 2025 12:31:06 +1300 Subject: [PATCH 11/44] Removed DAE test case and associated files --- tests/generator/generator.cpp | 24 --- .../generator/dae_cellml_1_1_model/model.c | 190 ------------------ .../dae_cellml_1_1_model/model.cellml | 127 ------------ .../generator/dae_cellml_1_1_model/model.h | 37 ---- .../generator/dae_cellml_1_1_model/model.py | 138 ------------- 5 files changed, 516 deletions(-) delete mode 100644 tests/resources/generator/dae_cellml_1_1_model/model.c delete mode 100644 tests/resources/generator/dae_cellml_1_1_model/model.cellml delete mode 100644 tests/resources/generator/dae_cellml_1_1_model/model.h delete mode 100644 tests/resources/generator/dae_cellml_1_1_model/model.py diff --git a/tests/generator/generator.cpp b/tests/generator/generator.cpp index 411080c52..586a1bed7 100644 --- a/tests/generator/generator.cpp +++ b/tests/generator/generator.cpp @@ -1602,30 +1602,6 @@ TEST(Generator, analyserModelScopeTest) EXPECT_EQ_FILE_CONTENTS("generator/hodgkin_huxley_squid_axon_model_1952/model.c", generator->implementationCode(analyserModel)); } -TEST(Generator, daeModel) -{ - auto parser = libcellml::Parser::create(false); - auto model = parser->parseModel(fileContents("generator/dae_cellml_1_1_model/model.cellml")); - - EXPECT_EQ(size_t(0), parser->errorCount()); - - auto analyser = libcellml::Analyser::create(); - - analyser->analyseModel(model); - - EXPECT_EQ(size_t(0), analyser->errorCount()); - - auto analyserModel = analyser->analyserModel(); - auto generator = libcellml::Generator::create(); - - EXPECT_EQ_FILE_CONTENTS("generator/dae_cellml_1_1_model/model.h", generator->interfaceCode(analyserModel)); - EXPECT_EQ_FILE_CONTENTS("generator/dae_cellml_1_1_model/model.c", generator->implementationCode(analyserModel)); - - auto profile = libcellml::GeneratorProfile::create(libcellml::GeneratorProfile::Profile::PYTHON); - - EXPECT_EQ_FILE_CONTENTS("generator/dae_cellml_1_1_model/model.py", generator->implementationCode(analyserModel, profile)); -} - TEST(Generator, variableInitialisedUsingAnotherVariable) { // Note: this should be in sync with the corresponding Analyser test. diff --git a/tests/resources/generator/dae_cellml_1_1_model/model.c b/tests/resources/generator/dae_cellml_1_1_model/model.c deleted file mode 100644 index a5ebbb5eb..000000000 --- a/tests/resources/generator/dae_cellml_1_1_model/model.c +++ /dev/null @@ -1,190 +0,0 @@ -/* The content of this file was generated using the C profile of libCellML 0.6.3. */ - -#include "model.h" - -#include -#include - -const char VERSION[] = "0.7.0"; -const char LIBCELLML_VERSION[] = "0.6.3"; - -const size_t STATE_COUNT = 2; -const size_t CONSTANT_COUNT = 5; -const size_t COMPUTED_CONSTANT_COUNT = 0; -const size_t ALGEBRAIC_VARIABLE_COUNT = 5; - -const VariableInfo VOI_INFO = {"t", "second", "main"}; - -const VariableInfo STATE_INFO[] = { - {"q_1", "coulomb", "main"}, - {"v_3", "C_per_s", "main"} -}; - -const VariableInfo CONSTANT_INFO[] = { - {"v_in", "C_per_s", "main"}, - {"v_out", "C_per_s", "main"}, - {"C", "C2_per_J", "main"}, - {"R", "Js_per_C2", "main"}, - {"L", "Js2_per_C2", "main"} -}; - -const VariableInfo COMPUTED_CONSTANT_INFO[] = { -}; - -const VariableInfo ALGEBRAIC_INFO[] = { - {"v_1", "C_per_s", "main"}, - {"v_2", "C_per_s", "main"}, - {"u_3", "J_per_C", "main"}, - {"u_2", "J_per_C", "main"}, - {"u_1", "J_per_C", "main"} -}; - -double * createStatesArray() -{ - double *res = (double *) malloc(STATE_COUNT*sizeof(double)); - - for (size_t i = 0; i < STATE_COUNT; ++i) { - res[i] = NAN; - } - - return res; -} - -double * createConstantsArray() -{ - double *res = (double *) malloc(CONSTANT_COUNT*sizeof(double)); - - for (size_t i = 0; i < CONSTANT_COUNT; ++i) { - res[i] = NAN; - } - - return res; -} - -double * createComputedConstantsArray() -{ - double *res = (double *) malloc(COMPUTED_CONSTANT_COUNT*sizeof(double)); - - for (size_t i = 0; i < COMPUTED_CONSTANT_COUNT; ++i) { - res[i] = NAN; - } - - return res; -} - -double * createAlgebraicVariablesArray() -{ - double *res = (double *) malloc(ALGEBRAIC_VARIABLE_COUNT*sizeof(double)); - - for (size_t i = 0; i < ALGEBRAIC_VARIABLE_COUNT; ++i) { - res[i] = NAN; - } - - return res; -} - -void deleteArray(double *array) -{ - free(array); -} - -typedef struct { - double voi; - double *states; - double *rates; - double *constants; - double *computedConstants; - double *algebraicVariables; -} RootFindingInfo; - -extern void nlaSolve(void (*objectiveFunction)(double *, double *, void *), - double *u, size_t n, void *data); - -void objectiveFunction0(double *u, double *f, void *data) -{ - double voi = ((RootFindingInfo *) data)->voi; - double *states = ((RootFindingInfo *) data)->states; - double *rates = ((RootFindingInfo *) data)->rates; - double *constants = ((RootFindingInfo *) data)->constants; - double *computedConstants = ((RootFindingInfo *) data)->computedConstants; - double *algebraicVariables = ((RootFindingInfo *) data)->algebraicVariables; - - algebraicVariables[0] = u[0]; - - f[0] = constants[0]-(algebraicVariables[0]+algebraicVariables[1]); -} - -void findRoot0(double voi, double *states, double *rates, double *constants, double *computedConstants, double *algebraicVariables) -{ - RootFindingInfo rfi = { voi, states, rates, constants, computedConstants, algebraicVariables }; - double u[1]; - - u[0] = algebraicVariables[0]; - - nlaSolve(objectiveFunction0, u, 1, &rfi); - - algebraicVariables[0] = u[0]; -} - -void objectiveFunction1(double *u, double *f, void *data) -{ - double voi = ((RootFindingInfo *) data)->voi; - double *states = ((RootFindingInfo *) data)->states; - double *rates = ((RootFindingInfo *) data)->rates; - double *constants = ((RootFindingInfo *) data)->constants; - double *computedConstants = ((RootFindingInfo *) data)->computedConstants; - double *algebraicVariables = ((RootFindingInfo *) data)->algebraicVariables; - - algebraicVariables[2] = u[0]; - - f[0] = algebraicVariables[4]-(algebraicVariables[3]+algebraicVariables[2]); -} - -void findRoot1(double voi, double *states, double *rates, double *constants, double *computedConstants, double *algebraicVariables) -{ - RootFindingInfo rfi = { voi, states, rates, constants, computedConstants, algebraicVariables }; - double u[1]; - - u[0] = algebraicVariables[2]; - - nlaSolve(objectiveFunction1, u, 1, &rfi); - - algebraicVariables[2] = u[0]; -} - -void initialiseArrays(double *states, double *rates, double *constants, double *computedConstants, double *algebraicVariables) -{ - states[0] = 1.0; - states[1] = 0.0; - constants[0] = 1.0; - constants[1] = 1.0; - constants[2] = 20.0; - constants[3] = 2.0; - constants[4] = 10.0; - algebraicVariables[0] = 0.0; - algebraicVariables[2] = 0.0; -} - -void computeComputedConstants(double *states, double *rates, double *constants, double *computedConstants, double *algebraic) -{ -} - -void computeRates(double voi, double *states, double *rates, double *constants, double *computedConstants, double *algebraicVariables) -{ - algebraicVariables[1] = states[1]+constants[1]; - findRoot0(voi, states, rates, constants, computedConstants, algebraicVariables); - rates[0] = algebraicVariables[0]; - algebraicVariables[3] = constants[3]*algebraicVariables[1]; - algebraicVariables[4] = states[0]/constants[2]; - findRoot1(voi, states, rates, constants, computedConstants, algebraicVariables); - rates[1] = algebraicVariables[2]/constants[4]; -} - -void computeVariables(double voi, double *states, double *rates, double *constants, double *computedConstants, double *algebraicVariables) -{ - algebraicVariables[1] = states[1]+constants[1]; - findRoot0(voi, states, rates, constants, computedConstants, algebraicVariables); - algebraicVariables[3] = constants[3]*algebraicVariables[1]; - algebraicVariables[4] = states[0]/constants[2]; - findRoot1(voi, states, rates, constants, computedConstants, algebraicVariables); -} diff --git a/tests/resources/generator/dae_cellml_1_1_model/model.cellml b/tests/resources/generator/dae_cellml_1_1_model/model.cellml deleted file mode 100644 index 3cf399427..000000000 --- a/tests/resources/generator/dae_cellml_1_1_model/model.cellml +++ /dev/null @@ -1,127 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - t - - q_1 - - v_1 - - - - v_in - - - v_1 - v_2 - - - - - v_2 - - - v_3 - v_out - - - - - u_1 - - - u_2 - u_3 - - - - - - u_1 - - - q_1 - C - - - - - u_2 - - - R - v_2 - - - - - - - - t - - v_3 - - - - u_3 - L - - - - - diff --git a/tests/resources/generator/dae_cellml_1_1_model/model.h b/tests/resources/generator/dae_cellml_1_1_model/model.h deleted file mode 100644 index 3350366ae..000000000 --- a/tests/resources/generator/dae_cellml_1_1_model/model.h +++ /dev/null @@ -1,37 +0,0 @@ -/* The content of this file was generated using the C profile of libCellML 0.6.3. */ - -#pragma once - -#include - -extern const char VERSION[]; -extern const char LIBCELLML_VERSION[]; - -extern const size_t STATE_COUNT; -extern const size_t CONSTANT_COUNT; -extern const size_t COMPUTED_CONSTANT_COUNT; -extern const size_t ALGEBRAIC_VARIABLE_COUNT; - -typedef struct { - char name[6]; - char units[11]; - char component[5]; -} VariableInfo; - -extern const VariableInfo VOI_INFO; -extern const VariableInfo STATE_INFO[]; -extern const VariableInfo CONSTANT_INFO[]; -extern const VariableInfo COMPUTED_CONSTANT_INFO[]; -extern const VariableInfo ALGEBRAIC_INFO[]; - -double * createStatesArray(); -double * createConstantsArray(); -double * createComputedConstantsArray(); -double * createAlgebraicVariablesArray(); - -void deleteArray(double *array); - -void initialiseArrays(double *states, double *rates, double *constants, double *computedConstants, double *algebraicVariables); -void computeComputedConstants(double *states, double *rates, double *constants, double *computedConstants, double *algebraic); -void computeRates(double voi, double *states, double *rates, double *constants, double *computedConstants, double *algebraicVariables); -void computeVariables(double voi, double *states, double *rates, double *constants, double *computedConstants, double *algebraicVariables); diff --git a/tests/resources/generator/dae_cellml_1_1_model/model.py b/tests/resources/generator/dae_cellml_1_1_model/model.py deleted file mode 100644 index e84732552..000000000 --- a/tests/resources/generator/dae_cellml_1_1_model/model.py +++ /dev/null @@ -1,138 +0,0 @@ -# The content of this file was generated using the Python profile of libCellML 0.6.3. - -from enum import Enum -from math import * - - -__version__ = "0.6.0" -LIBCELLML_VERSION = "0.6.3" - -STATE_COUNT = 2 -CONSTANT_COUNT = 5 -COMPUTED_CONSTANT_COUNT = 0 -ALGEBRAIC_VARIABLE_COUNT = 5 - -VOI_INFO = {"name": "t", "units": "second", "component": "main"} - -STATE_INFO = [ - {"name": "q_1", "units": "coulomb", "component": "main"}, - {"name": "v_3", "units": "C_per_s", "component": "main"} -] - -CONSTANT_INFO = [ - {"name": "v_in", "units": "C_per_s", "component": "main"}, - {"name": "v_out", "units": "C_per_s", "component": "main"}, - {"name": "C", "units": "C2_per_J", "component": "main"}, - {"name": "R", "units": "Js_per_C2", "component": "main"}, - {"name": "L", "units": "Js2_per_C2", "component": "main"} -] - -COMPUTED_CONSTANT_INFO = [ -] - -ALGEBRAIC_INFO = [ - {"name": "v_1", "units": "C_per_s", "component": "main"}, - {"name": "v_2", "units": "C_per_s", "component": "main"}, - {"name": "u_3", "units": "J_per_C", "component": "main"}, - {"name": "u_2", "units": "J_per_C", "component": "main"}, - {"name": "u_1", "units": "J_per_C", "component": "main"} -] - - -def create_states_array(): - return [nan]*STATE_COUNT - - -def create_constants_array(): - return [nan]*CONSTANT_COUNT - - -def create_computed_constants_array(): - return [nan]*COMPUTED_CONSTANT_COUNT - - -def create_algebraic_variables_array(): - return [nan]*ALGEBRAIC_VARIABLE_COUNT - - -from nlasolver import nla_solve - - -def objective_function_0(u, f, data): - voi = data[0] - states = data[1] - rates = data[2] - constants = data[3] - computed_constants = data[4] - algebraic_variables = data[5] - - algebraicVariables[0] = u[0] - - f[0] = constants[0]-(algebraicVariables[0]+algebraicVariables[1]) - - -def find_root_0(voi, states, rates, constants, computed_constants, algebraic_variables): - u = [nan]*1 - - u[0] = algebraicVariables[0] - - u = nla_solve(objective_function_0, u, 1, [voi, states, rates, constants, computed_constants, algebraic_variables]) - - algebraicVariables[0] = u[0] - - -def objective_function_1(u, f, data): - voi = data[0] - states = data[1] - rates = data[2] - constants = data[3] - computed_constants = data[4] - algebraic_variables = data[5] - - algebraicVariables[2] = u[0] - - f[0] = algebraicVariables[4]-(algebraicVariables[3]+algebraicVariables[2]) - - -def find_root_1(voi, states, rates, constants, computed_constants, algebraic_variables): - u = [nan]*1 - - u[0] = algebraicVariables[2] - - u = nla_solve(objective_function_1, u, 1, [voi, states, rates, constants, computed_constants, algebraic_variables]) - - algebraicVariables[2] = u[0] - - -def initialise_arrays(states, rates, constants, computed_constants, algebraic_variables): - states[0] = 1.0 - states[1] = 0.0 - constants[0] = 1.0 - constants[1] = 1.0 - constants[2] = 20.0 - constants[3] = 2.0 - constants[4] = 10.0 - algebraicVariables[0] = 0.0 - algebraicVariables[2] = 0.0 - - -def compute_computed_constants(states, rates, constants, computed_constants, algebraic): - pass - - -def compute_rates(voi, states, rates, constants, computed_constants, algebraic_variables): - algebraicVariables[1] = states[1]+constants[1] - find_root_0(voi, states, rates, constants, computed_constants, algebraic_variables) - rates[0] = algebraicVariables[0] - algebraicVariables[3] = constants[3]*algebraicVariables[1] - algebraicVariables[4] = states[0]/constants[2] - find_root_1(voi, states, rates, constants, computed_constants, algebraic_variables) - rates[1] = algebraicVariables[2]/constants[4] - - -def compute_variables(voi, states, rates, constants, computed_constants, algebraic_variables): - algebraicVariables[1] = states[1]+constants[1] - find_root_0(voi, states, rates, constants, computed_constants, algebraic_variables) - algebraicVariables[3] = constants[3]*algebraicVariables[1] - algebraicVariables[4] = states[0]/constants[2] - find_root_1(voi, states, rates, constants, computed_constants, algebraic_variables) From 060b8153fd508c1255ac21c0e76afa999a0d9048 Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Fri, 12 Dec 2025 13:14:04 +1300 Subject: [PATCH 12/44] Replaced exception use in symEngineEquation() Substituted with std::pair> use instead --- src/analyser.cpp | 32 ++++++++++++++++---------------- src/analyser_p.h | 3 ++- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/analyser.cpp b/src/analyser.cpp index 33788a527..4df5a570e 100644 --- a/src/analyser.cpp +++ b/src/analyser.cpp @@ -199,35 +199,38 @@ bool AnalyserInternalEquation::variableOnLhsOrRhs(const AnalyserInternalVariable || variableOnRhs(variable); } -SymEngine::RCP AnalyserInternalEquation::symEngineEquation(const AnalyserEquationAstPtr &ast, const SymEngineSymbolMap &symbolMap) +SymEngineEquationResult AnalyserInternalEquation::symEngineEquation(const AnalyserEquationAstPtr &ast, const SymEngineSymbolMap &symbolMap) { if (ast == nullptr) { - return SymEngine::null; + return {true, SymEngine::null}; } AnalyserEquationAstPtr leftAst = ast->leftChild(); AnalyserEquationAstPtr rightAst = ast->rightChild(); // Recursively call getConvertedAst on left and right children. - SymEngine::RCP left = symEngineEquation(leftAst, symbolMap); - SymEngine::RCP right = symEngineEquation(rightAst, symbolMap); + auto [leftSuccess, left] = symEngineEquation(leftAst, symbolMap); + auto [rightSuccess, right] = symEngineEquation(rightAst, symbolMap); + + if (!leftSuccess || !rightSuccess) { + return {false, SymEngine::null}; + } // Analyse mAst current type and value. switch (ast->type()) { case AnalyserEquationAst::Type::EQUALITY: - return Eq(left, right); + return {true, Eq(left, right)}; case AnalyserEquationAst::Type::PLUS: - return add(left, right); + return {true, add(left, right)}; case AnalyserEquationAst::Type::CI: // Seems like the voi doesn't exist in mAllVariables, so we don't have an easy means of access. - // For now we'll just throw an error if the symbol is not found. if (symbolMap.find(ast->variable()->name()) == symbolMap.end()) { - throw std::runtime_error("Unsupported variable in symEngineRepresentation"); + return {false, SymEngine::null}; } - return symbolMap.at(ast->variable()->name()); + return {true, symbolMap.at(ast->variable()->name())}; default: - // Our parser is unable to handle this type, so we need to let the caller know by throwing an error. - throw std::runtime_error("Unsupported AST type in symEngineRepresentation"); + // Rearrangement is not possible with this type. + return {false, SymEngine::null}; } } @@ -291,11 +294,8 @@ AnalyserEquationAstPtr AnalyserInternalEquation::rearrangeFor(const AnalyserInte variableMap[symbol] = variable; } - SymEngine::RCP seEquation; - try { - seEquation = symEngineEquation(mAst, symbolMap); - } catch (const std::runtime_error &e) { - // Our parser was unable to convert the AST to a SymEngine expression. + auto [success, seEquation] = symEngineEquation(mAst, symbolMap); + if (!success) { return nullptr; } diff --git a/src/analyser_p.h b/src/analyser_p.h index f5aec6745..3c1d78544 100644 --- a/src/analyser_p.h +++ b/src/analyser_p.h @@ -49,6 +49,7 @@ using AnalyserExternalVariablePtrs = std::vector; using SymEngineVariableMap = std::map, AnalyserInternalVariablePtr, SymEngine::RCPBasicKeyLess>; using SymEngineSymbolMap = std::map>; +using SymEngineEquationResult = std::pair>; struct AnalyserInternalVariable { @@ -139,7 +140,7 @@ struct AnalyserInternalEquation bool variableOnRhs(const AnalyserInternalVariablePtr &variable); bool variableOnLhsOrRhs(const AnalyserInternalVariablePtr &variable); - SymEngine::RCP symEngineEquation(const AnalyserEquationAstPtr &ast, const SymEngineSymbolMap &symbolMap); + SymEngineEquationResult symEngineEquation(const AnalyserEquationAstPtr &ast, const SymEngineSymbolMap &symbolMap); AnalyserEquationAstPtr parseSymEngineExpression(const SymEngine::RCP &seExpression, const AnalyserEquationAstPtr &parentAst, const SymEngineVariableMap &variableMap); AnalyserEquationAstPtr rearrangeFor(const AnalyserInternalVariablePtr &variable); From 685e1a1619ebbc53f5ad3f41fc1fb05835d13b44 Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Fri, 12 Dec 2025 13:16:51 +1300 Subject: [PATCH 13/44] Removed redundant map include --- src/analyser_p.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/analyser_p.h b/src/analyser_p.h index 3c1d78544..fd1eff93e 100644 --- a/src/analyser_p.h +++ b/src/analyser_p.h @@ -14,8 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -#include - #include "libcellml/generatorprofile.h" #include "libcellml/issue.h" From 81f57664b8c532f2ec8644cac5f39d253c0d7ca7 Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Fri, 12 Dec 2025 14:03:46 +1300 Subject: [PATCH 14/44] Change SymEngineEquationResult to use tuple instead of pair --- src/analyser_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyser_p.h b/src/analyser_p.h index fd1eff93e..733a5c121 100644 --- a/src/analyser_p.h +++ b/src/analyser_p.h @@ -47,7 +47,7 @@ using AnalyserExternalVariablePtrs = std::vector; using SymEngineVariableMap = std::map, AnalyserInternalVariablePtr, SymEngine::RCPBasicKeyLess>; using SymEngineSymbolMap = std::map>; -using SymEngineEquationResult = std::pair>; +using SymEngineEquationResult = std::tuple>; struct AnalyserInternalVariable { From 5dc3fcb235c129e4603f4f86cf82f236d795460a Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Fri, 12 Dec 2025 15:47:32 +1300 Subject: [PATCH 15/44] Added rearrangement test suite Mostly GPT generated test cases --- .../symengine/unarranged_addition.cellml | 55 ++++++++++ .../symengine/unarranged_combination.cellml | 103 ++++++++++++++++++ .../symengine/unarranged_constants.cellml | 63 +++++++++++ .../symengine/unarranged_exponential.cellml | 102 +++++++++++++++++ .../unarranged_hyperbolic_trig.cellml | 55 ++++++++++ .../symengine/unarranged_inverse_trig.cellml | 55 ++++++++++ .../symengine/unarranged_logarithmic.cellml | 55 ++++++++++ .../unarranged_multiplication.cellml | 67 ++++++++++++ .../symengine/unarranged_polynomials.cellml | 49 +++++++++ .../analyser/symengine/unarranged_trig.cellml | 55 ++++++++++ 10 files changed, 659 insertions(+) create mode 100644 tests/resources/analyser/symengine/unarranged_addition.cellml create mode 100644 tests/resources/analyser/symengine/unarranged_combination.cellml create mode 100644 tests/resources/analyser/symengine/unarranged_constants.cellml create mode 100644 tests/resources/analyser/symengine/unarranged_exponential.cellml create mode 100644 tests/resources/analyser/symengine/unarranged_hyperbolic_trig.cellml create mode 100644 tests/resources/analyser/symengine/unarranged_inverse_trig.cellml create mode 100644 tests/resources/analyser/symengine/unarranged_logarithmic.cellml create mode 100644 tests/resources/analyser/symengine/unarranged_multiplication.cellml create mode 100644 tests/resources/analyser/symengine/unarranged_polynomials.cellml create mode 100644 tests/resources/analyser/symengine/unarranged_trig.cellml diff --git a/tests/resources/analyser/symengine/unarranged_addition.cellml b/tests/resources/analyser/symengine/unarranged_addition.cellml new file mode 100644 index 000000000..dba305d29 --- /dev/null +++ b/tests/resources/analyser/symengine/unarranged_addition.cellml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + w2 + x3 + y4 + z5 + + + + + awx + 10 + + + + + + by2 + 1 + + + + + + cx1z1 + 5 + + + + + + dw0y + 0 + + + + + diff --git a/tests/resources/analyser/symengine/unarranged_combination.cellml b/tests/resources/analyser/symengine/unarranged_combination.cellml new file mode 100644 index 000000000..61c1657cf --- /dev/null +++ b/tests/resources/analyser/symengine/unarranged_combination.cellml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + w2 + x3 + y5 + z7 + + + + + a + + + + + + y + xw2 + + + 2z + + + + + + + b + + + + + + + w + xy + + z2 + + + + + + + + c + + + + + aw2 + 3y2z + + x + + + + + + + d + + + + + + + 2z + yw + + + + + x3.141592653589793 + w + + + + y1 + + + + + + diff --git a/tests/resources/analyser/symengine/unarranged_constants.cellml b/tests/resources/analyser/symengine/unarranged_constants.cellml new file mode 100644 index 000000000..cccbff6a5 --- /dev/null +++ b/tests/resources/analyser/symengine/unarranged_constants.cellml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + x3 + y4 + z5 + u2 + + + + + ax + 10.0 + + + + + + bu + 45 + + + + + + cy + + + + + + + dz + + + + + + + eu + + + + + + diff --git a/tests/resources/analyser/symengine/unarranged_exponential.cellml b/tests/resources/analyser/symengine/unarranged_exponential.cellml new file mode 100644 index 000000000..92f58e978 --- /dev/null +++ b/tests/resources/analyser/symengine/unarranged_exponential.cellml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + w2 + x3 + y5 + z7 + + + + + + + + aw + 3 + ax + + + 200 + + + + + + + + + + 2b + by + + xz + + 100 + + + + + + + + + + + cw + z + + + y + cx + + + 500 + + + + + + + + + 4 + dy + + + z + dw + + + + 300 + + + + + diff --git a/tests/resources/analyser/symengine/unarranged_hyperbolic_trig.cellml b/tests/resources/analyser/symengine/unarranged_hyperbolic_trig.cellml new file mode 100644 index 000000000..9db2f3338 --- /dev/null +++ b/tests/resources/analyser/symengine/unarranged_hyperbolic_trig.cellml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + w2 + x3 + y4 + z5 + + + + + ab + 5 + + + + + + bc + x + + + + + + c2d + y + + + + + + 3da + z + + + + + diff --git a/tests/resources/analyser/symengine/unarranged_inverse_trig.cellml b/tests/resources/analyser/symengine/unarranged_inverse_trig.cellml new file mode 100644 index 000000000..4790238c9 --- /dev/null +++ b/tests/resources/analyser/symengine/unarranged_inverse_trig.cellml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + w1 + x2 + y3 + z4 + + + + + ab + w + + + + + + bc + x + + + + + + c2d + y + + + + + + 3da + z + + + + + diff --git a/tests/resources/analyser/symengine/unarranged_logarithmic.cellml b/tests/resources/analyser/symengine/unarranged_logarithmic.cellml new file mode 100644 index 000000000..96ceaeba8 --- /dev/null +++ b/tests/resources/analyser/symengine/unarranged_logarithmic.cellml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + x3 + y1000 + u1.5 + v100 + + + + + ax + 5 + + + + + + yb + 3 + + + + + + cu + 2.5 + + + + + + dv + 4 + + + + + diff --git a/tests/resources/analyser/symengine/unarranged_multiplication.cellml b/tests/resources/analyser/symengine/unarranged_multiplication.cellml new file mode 100644 index 000000000..d4805e1c4 --- /dev/null +++ b/tests/resources/analyser/symengine/unarranged_multiplication.cellml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + w2 + x3 + y4 + z5 + + + + + awx + 4 + + + + + + + + bxy + 2 + + 18 + + + + + + + + cwz + y + + 5 + + + + + + + + d + w3 + + 10 + + + + + diff --git a/tests/resources/analyser/symengine/unarranged_polynomials.cellml b/tests/resources/analyser/symengine/unarranged_polynomials.cellml new file mode 100644 index 000000000..0e5f05dda --- /dev/null +++ b/tests/resources/analyser/symengine/unarranged_polynomials.cellml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + a2 + 2 + + 25 + + + + + + + + + b3 + 4 + + 12 + + + + + + + + + c3 + 3c + + 30 + + + + + diff --git a/tests/resources/analyser/symengine/unarranged_trig.cellml b/tests/resources/analyser/symengine/unarranged_trig.cellml new file mode 100644 index 000000000..4ca49500b --- /dev/null +++ b/tests/resources/analyser/symengine/unarranged_trig.cellml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + w0.5 + x1.0 + y1.5 + z2.0 + + + + + 2aw + 1 + + + + + + x3b + 0.2 + + + + + + cz + 3.5 + + + + + + 0.5dy + -0.25 + + + + + From 4d4ba97a824a5c545d6a782cfecc712e46c36d3b Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Fri, 12 Dec 2025 15:48:29 +1300 Subject: [PATCH 16/44] Create new test cases for rearrangement --- tests/analyser/analysersymengine.cpp | 161 +++++++++++++++++++++++++++ tests/analyser/tests.cmake | 1 + 2 files changed, 162 insertions(+) create mode 100644 tests/analyser/analysersymengine.cpp diff --git a/tests/analyser/analysersymengine.cpp b/tests/analyser/analysersymengine.cpp new file mode 100644 index 000000000..5998b396a --- /dev/null +++ b/tests/analyser/analysersymengine.cpp @@ -0,0 +1,161 @@ +/* +Copyright libCellML Contributors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include "test_utils.h" + +#include "gtest/gtest.h" + +#include + +TEST(Analyser, rearrangeAdditiveEquations) +{ + auto parser = libcellml::Parser::create(); + auto model = parser->parseModel(fileContents("analyser/symengine/unarranged_addition.cellml")); + + EXPECT_EQ(size_t(0), parser->issueCount()); + + auto analyser = libcellml::Analyser::create(); + + analyser->analyseModel(model); + + EXPECT_EQ(libcellml::AnalyserModel::Type::ALGEBRAIC, analyser->analyserModel()->type()); +} + +TEST(Analyser, rearrangeMultiplicativeEquations) +{ + auto parser = libcellml::Parser::create(); + auto model = parser->parseModel(fileContents("analyser/symengine/unarranged_multiplication.cellml")); + + EXPECT_EQ(size_t(0), parser->issueCount()); + + auto analyser = libcellml::Analyser::create(); + + analyser->analyseModel(model); + + EXPECT_EQ(libcellml::AnalyserModel::Type::ALGEBRAIC, analyser->analyserModel()->type()); +} + +TEST(Analyser, rearrangeTrigonometricEquations) +{ + auto parser = libcellml::Parser::create(); + auto model = parser->parseModel(fileContents("analyser/symengine/unarranged_trig.cellml")); + + EXPECT_EQ(size_t(0), parser->issueCount()); + + auto analyser = libcellml::Analyser::create(); + + analyser->analyseModel(model); + + EXPECT_EQ(libcellml::AnalyserModel::Type::ALGEBRAIC, analyser->analyserModel()->type()); +} + +TEST(Analyser, rearrangeInverseTrigonometricEquations) +{ + auto parser = libcellml::Parser::create(); + auto model = parser->parseModel(fileContents("analyser/symengine/unarranged_inverse_trig.cellml")); + + EXPECT_EQ(size_t(0), parser->issueCount()); + + auto analyser = libcellml::Analyser::create(); + + analyser->analyseModel(model); + + EXPECT_EQ(libcellml::AnalyserModel::Type::ALGEBRAIC, analyser->analyserModel()->type()); +} + +TEST(Analyser, rearrangeHyperbolicTrigonometricEquations) +{ + auto parser = libcellml::Parser::create(); + auto model = parser->parseModel(fileContents("analyser/symengine/unarranged_hyperbolic_trig.cellml")); + + EXPECT_EQ(size_t(0), parser->issueCount()); + + auto analyser = libcellml::Analyser::create(); + + analyser->analyseModel(model); + + EXPECT_EQ(libcellml::AnalyserModel::Type::ALGEBRAIC, analyser->analyserModel()->type()); +} + +TEST(Analyser, rearrangeExponentialEquations) +{ + auto parser = libcellml::Parser::create(); + auto model = parser->parseModel(fileContents("analyser/symengine/unarranged_exponential.cellml")); + + EXPECT_EQ(size_t(0), parser->issueCount()); + + auto analyser = libcellml::Analyser::create(); + + analyser->analyseModel(model); + + EXPECT_EQ(libcellml::AnalyserModel::Type::ALGEBRAIC, analyser->analyserModel()->type()); +} + +TEST(Analyser, rearrangeLogarithmicEquations) +{ + auto parser = libcellml::Parser::create(); + auto model = parser->parseModel(fileContents("analyser/symengine/unarranged_logarithmic.cellml")); + + EXPECT_EQ(size_t(0), parser->issueCount()); + + auto analyser = libcellml::Analyser::create(); + + analyser->analyseModel(model); + + EXPECT_EQ(libcellml::AnalyserModel::Type::ALGEBRAIC, analyser->analyserModel()->type()); +} + +TEST(Analyser, rearrangeEquationsWithConstants) +{ + auto parser = libcellml::Parser::create(); + auto model = parser->parseModel(fileContents("analyser/symengine/unarranged_constants.cellml")); + + EXPECT_EQ(size_t(0), parser->issueCount()); + + auto analyser = libcellml::Analyser::create(); + + analyser->analyseModel(model); + + EXPECT_EQ(libcellml::AnalyserModel::Type::ALGEBRAIC, analyser->analyserModel()->type()); +} + +TEST(Analyser, rearrangePolynomialEquations) +{ + auto parser = libcellml::Parser::create(); + auto model = parser->parseModel(fileContents("analyser/symengine/unarranged_polynomials.cellml")); + + EXPECT_EQ(size_t(0), parser->issueCount()); + + auto analyser = libcellml::Analyser::create(); + + analyser->analyseModel(model); + + EXPECT_EQ(libcellml::AnalyserModel::Type::ALGEBRAIC, analyser->analyserModel()->type()); +} + +TEST(Analyser, rearrangeCombinationEquations) +{ + auto parser = libcellml::Parser::create(); + auto model = parser->parseModel(fileContents("analyser/symengine/unarranged_combination.cellml")); + + EXPECT_EQ(size_t(0), parser->issueCount()); + + auto analyser = libcellml::Analyser::create(); + + analyser->analyseModel(model); + + EXPECT_EQ(libcellml::AnalyserModel::Type::ALGEBRAIC, analyser->analyserModel()->type()); +} \ No newline at end of file diff --git a/tests/analyser/tests.cmake b/tests/analyser/tests.cmake index ab22b635e..6e160c1b4 100644 --- a/tests/analyser/tests.cmake +++ b/tests/analyser/tests.cmake @@ -7,4 +7,5 @@ set(${CURRENT_TEST}_SRCS ${CMAKE_CURRENT_LIST_DIR}/analyser.cpp ${CMAKE_CURRENT_LIST_DIR}/analyserexternalvariable.cpp ${CMAKE_CURRENT_LIST_DIR}/analyserunits.cpp + ${CMAKE_CURRENT_LIST_DIR}/analysersymengine.cpp ) From d9c4d102793e542fe59361e619fa41b5710651ea Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Fri, 12 Dec 2025 15:49:14 +1300 Subject: [PATCH 17/44] Remove old rearrangement test case Test case has been superseded by the newly added test suite to more robustly test for equation rearrangement --- tests/analyser/analyser.cpp | 14 --------- .../unarranged_algebraic_equation.cellml | 29 ------------------- 2 files changed, 43 deletions(-) delete mode 100644 tests/resources/analyser/unarranged_algebraic_equation.cellml diff --git a/tests/analyser/analyser.cpp b/tests/analyser/analyser.cpp index add9e0f43..ecc9db714 100644 --- a/tests/analyser/analyser.cpp +++ b/tests/analyser/analyser.cpp @@ -1060,17 +1060,3 @@ TEST(Analyser, unsuitablyConstrainedNlaSystem) EXPECT_EQ(libcellml::AnalyserModel::Type::UNSUITABLY_CONSTRAINED, analyser->analyserModel()->type()); } - -TEST(Analyser, rearrangeAlgebraicEquation) -{ - auto parser = libcellml::Parser::create(); - auto model = parser->parseModel(fileContents("analyser/unarranged_algebraic_equation.cellml")); - - EXPECT_EQ(size_t(0), parser->issueCount()); - - auto analyser = libcellml::Analyser::create(); - - analyser->analyseModel(model); - - EXPECT_EQ(libcellml::AnalyserModel::Type::ALGEBRAIC, analyser->analyserModel()->type()); -} diff --git a/tests/resources/analyser/unarranged_algebraic_equation.cellml b/tests/resources/analyser/unarranged_algebraic_equation.cellml deleted file mode 100644 index 5840ba82b..000000000 --- a/tests/resources/analyser/unarranged_algebraic_equation.cellml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - x - 1 - - - - z - 3 - - - - x - - - y - z - - - - - From bd67b0cbdf95e133861a8e3430911baa4499c51c Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Mon, 15 Dec 2025 09:20:42 +1300 Subject: [PATCH 18/44] Make SymEngine -> AST parsing work with 3+ children Previous system assumed that symengine expressions contained only 0-2 children, but in reality it could be any whole number. This adjusts the existing conversion logic to account for this. --- src/analyser.cpp | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/src/analyser.cpp b/src/analyser.cpp index 4df5a570e..55332dd9a 100644 --- a/src/analyser.cpp +++ b/src/analyser.cpp @@ -238,12 +238,8 @@ AnalyserEquationAstPtr AnalyserInternalEquation::parseSymEngineExpression(const const AnalyserEquationAstPtr &parentAst, const SymEngineVariableMap &variableMap) { - auto children = seExpression->get_args(); - AnalyserEquationAstPtr ast = AnalyserEquationAst::create(); - ast->setParent(parentAst); - switch (seExpression->get_type_code()) { case SymEngine::SYMENGINE_EQUALITY: { ast->setType(AnalyserEquationAst::Type::EQUALITY); @@ -272,14 +268,39 @@ AnalyserEquationAstPtr AnalyserInternalEquation::parseSymEngineExpression(const break; } - // TODO Update to account for symengine expressions with 3 or more children. - if (children.size() > 0) { - ast->setLeftChild(parseSymEngineExpression(children[0], ast, variableMap)); - if (children.size() > 1) { - ast->setRightChild(parseSymEngineExpression(children[1], ast, variableMap)); + auto children = seExpression->get_args(); + auto currentAst = ast; + + // All children except the last are to be assigned as left children in the AST tree. + for (int i = 0; i + 1 < children.size(); ++i) { + auto childSeExpression = children[i]; + AnalyserEquationAstPtr childAst = parseSymEngineExpression(childSeExpression, currentAst, variableMap); + + currentAst->setLeftChild(childAst); + + if (i < children.size() - 2) { + // Since there are more than two children left, we need to create another copy + // of our original AST node. + AnalyserEquationAstPtr newAst = AnalyserEquationAst::create(); + newAst->setType(ast->type()); + newAst->setValue(ast->value()); + newAst->setVariable(ast->variable()); + currentAst->setRightChild(newAst); + newAst->setParent(currentAst); + currentAst = newAst; } } + // Set the final node as the right child. + if (children.size() >= 2) { + auto childSeExpression = children.back(); + AnalyserEquationAstPtr childAst = parseSymEngineExpression(childSeExpression, currentAst, variableMap); + + currentAst->setRightChild(childAst); + } + + ast->setParent(parentAst); + return ast; } From c8d2bd851be36216d25fb0c419dd373d4d5b3b78 Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Mon, 15 Dec 2025 11:44:23 +1300 Subject: [PATCH 19/44] Updated unarranged addition cellml file Made it so that we also now consider mathml unary +/- operators --- .../analyser/symengine/unarranged_addition.cellml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/resources/analyser/symengine/unarranged_addition.cellml b/tests/resources/analyser/symengine/unarranged_addition.cellml index dba305d29..9403b3800 100644 --- a/tests/resources/analyser/symengine/unarranged_addition.cellml +++ b/tests/resources/analyser/symengine/unarranged_addition.cellml @@ -36,18 +36,18 @@ 1 - + - cx1z1 - 5 + cx1 + z - + - dw0y - 0 + dw + y From 080d5c3e04511811cde728e501fa7d0ce9d48bf7 Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Mon, 15 Dec 2025 09:22:23 +1300 Subject: [PATCH 20/44] Add/Sub symengine parsing support Can now handle unary plus, minus, unary minus, and CN types from AST (which means code now passes Analyser.rearrangeAdditiveEquations) --- src/analyser.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/analyser.cpp b/src/analyser.cpp index 55332dd9a..8304e9f14 100644 --- a/src/analyser.cpp +++ b/src/analyser.cpp @@ -221,13 +221,26 @@ SymEngineEquationResult AnalyserInternalEquation::symEngineEquation(const Analys case AnalyserEquationAst::Type::EQUALITY: return {true, Eq(left, right)}; case AnalyserEquationAst::Type::PLUS: + // Handle the case where we have a unary plus. + if (right == SymEngine::null || left == SymEngine::null) { + return {true, right == SymEngine::null ? left : right}; + } return {true, add(left, right)}; + case AnalyserEquationAst::Type::MINUS: + // Handle the case where we have a unary minus. + if (right == SymEngine::null || left == SymEngine::null) { + auto operand = right == SymEngine::null ? left : right; + return {true, SymEngine::mul(SymEngine::integer(-1), operand)}; + } + return {true, sub(left, right)}; case AnalyserEquationAst::Type::CI: // Seems like the voi doesn't exist in mAllVariables, so we don't have an easy means of access. if (symbolMap.find(ast->variable()->name()) == symbolMap.end()) { return {false, SymEngine::null}; } return {true, symbolMap.at(ast->variable()->name())}; + case AnalyserEquationAst::Type::CN: + return {true, SymEngine::integer(std::stoi(ast->value()))}; default: // Rearrangement is not possible with this type. return {false, SymEngine::null}; From cce543fda7ff2930f3950657dd72e16c53bf7294 Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Mon, 15 Dec 2025 09:54:21 +1300 Subject: [PATCH 21/44] Fixed unarranged multiplication cellml file Had an error at equation 4 Also added brackets to equation comments to make things more clear --- .../symengine/unarranged_multiplication.cellml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/resources/analyser/symengine/unarranged_multiplication.cellml b/tests/resources/analyser/symengine/unarranged_multiplication.cellml index d4805e1c4..bdc677d57 100644 --- a/tests/resources/analyser/symengine/unarranged_multiplication.cellml +++ b/tests/resources/analyser/symengine/unarranged_multiplication.cellml @@ -22,14 +22,14 @@ y4 z5 - + awx 4 - + @@ -40,7 +40,7 @@ 18 - + @@ -51,12 +51,12 @@ 5 - + - d + dz w3 10 From 41e85ce491daa8db2e086d27b4c493b4459b9160 Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Mon, 15 Dec 2025 12:03:24 +1300 Subject: [PATCH 22/44] Mult/div symengine parsing support Now passes Analyser.rearrangeMultiplicativeEquations --- src/analyser.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/analyser.cpp b/src/analyser.cpp index 8304e9f14..6fda1a37c 100644 --- a/src/analyser.cpp +++ b/src/analyser.cpp @@ -233,6 +233,10 @@ SymEngineEquationResult AnalyserInternalEquation::symEngineEquation(const Analys return {true, SymEngine::mul(SymEngine::integer(-1), operand)}; } return {true, sub(left, right)}; + case AnalyserEquationAst::Type::TIMES: + return {true, mul(left, right)}; + case AnalyserEquationAst::Type::DIVIDE: + return {true, SymEngine::div(left, right)}; case AnalyserEquationAst::Type::CI: // Seems like the voi doesn't exist in mAllVariables, so we don't have an easy means of access. if (symbolMap.find(ast->variable()->name()) == symbolMap.end()) { @@ -266,6 +270,10 @@ AnalyserEquationAstPtr AnalyserInternalEquation::parseSymEngineExpression(const ast->setType(AnalyserEquationAst::Type::TIMES); break; } + case SymEngine::SYMENGINE_POW: { + ast->setType(AnalyserEquationAst::Type::POWER); + break; + } case SymEngine::SYMENGINE_SYMBOL: { SymEngine::RCP symbolExpr = SymEngine::rcp_dynamic_cast(seExpression); ast->setType(AnalyserEquationAst::Type::CI); From 4cbc7877e55ba1da7554e1b9441f1f96ebccc25d Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Mon, 15 Dec 2025 12:25:27 +1300 Subject: [PATCH 23/44] Improved unarranged trig test cases Now covers a greater variety of cases --- .../analyser/symengine/unarranged_trig.cellml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/resources/analyser/symengine/unarranged_trig.cellml b/tests/resources/analyser/symengine/unarranged_trig.cellml index 4ca49500b..457284c27 100644 --- a/tests/resources/analyser/symengine/unarranged_trig.cellml +++ b/tests/resources/analyser/symengine/unarranged_trig.cellml @@ -29,25 +29,25 @@ 1 - + - x3b - 0.2 + 4xb + 0 - + - cz - 3.5 + c + z - + - 0.5dy - -0.25 + dyx + 2 From a47ce0dd37292de1731907f677951b7e48551a8f Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Mon, 15 Dec 2025 13:21:18 +1300 Subject: [PATCH 24/44] Simple trig rearrangement support Symengine is not capable of rearranging trig functions into inverse trig functions (e.g. tan(x) = y cannot become x = atan(y)) --- src/analyser.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/analyser.cpp b/src/analyser.cpp index 6fda1a37c..1ab80dfa6 100644 --- a/src/analyser.cpp +++ b/src/analyser.cpp @@ -237,6 +237,12 @@ SymEngineEquationResult AnalyserInternalEquation::symEngineEquation(const Analys return {true, mul(left, right)}; case AnalyserEquationAst::Type::DIVIDE: return {true, SymEngine::div(left, right)}; + case AnalyserEquationAst::Type::SIN: + return {true, SymEngine::sin(left)}; + case AnalyserEquationAst::Type::COS: + return {true, SymEngine::cos(left)}; + case AnalyserEquationAst::Type::TAN: + return {true, SymEngine::tan(left)}; case AnalyserEquationAst::Type::CI: // Seems like the voi doesn't exist in mAllVariables, so we don't have an easy means of access. if (symbolMap.find(ast->variable()->name()) == symbolMap.end()) { @@ -274,6 +280,18 @@ AnalyserEquationAstPtr AnalyserInternalEquation::parseSymEngineExpression(const ast->setType(AnalyserEquationAst::Type::POWER); break; } + case SymEngine::SYMENGINE_SIN: { + ast->setType(AnalyserEquationAst::Type::SIN); + break; + } + case SymEngine::SYMENGINE_COS: { + ast->setType(AnalyserEquationAst::Type::COS); + break; + } + case SymEngine::SYMENGINE_TAN: { + ast->setType(AnalyserEquationAst::Type::TAN); + break; + } case SymEngine::SYMENGINE_SYMBOL: { SymEngine::RCP symbolExpr = SymEngine::rcp_dynamic_cast(seExpression); ast->setType(AnalyserEquationAst::Type::CI); From e157f82b110fe10cc303d70be4d57e4dc0ff6fee Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Mon, 15 Dec 2025 14:13:24 +1300 Subject: [PATCH 25/44] Updated unarranged constants cellml file Now has better Eq 1 test case and more consistent variable naming --- .../symengine/unarranged_constants.cellml | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/resources/analyser/symengine/unarranged_constants.cellml b/tests/resources/analyser/symengine/unarranged_constants.cellml index cccbff6a5..de5235735 100644 --- a/tests/resources/analyser/symengine/unarranged_constants.cellml +++ b/tests/resources/analyser/symengine/unarranged_constants.cellml @@ -2,10 +2,10 @@ + - @@ -18,22 +18,22 @@ xmlns:cellml="http://www.cellml.org/cellml/2.0#"> - x3 - y4 - z5 - u2 + w3 + x4 + y5 + z2 - + ax - 10.0 + 8.65 - + - bu + bw 45 @@ -51,10 +51,10 @@ - + - eu + ew From 4fdae5392452b34f062b3b470c96c31f920c1b3f Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Mon, 15 Dec 2025 14:14:14 +1300 Subject: [PATCH 26/44] Added support for various numbers and constants Now supports - Decimal point values (i.e. doubles) - Constants (E, pi, and inf) --- src/analyser.cpp | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/analyser.cpp b/src/analyser.cpp index 1ab80dfa6..f2cafe39c 100644 --- a/src/analyser.cpp +++ b/src/analyser.cpp @@ -243,6 +243,12 @@ SymEngineEquationResult AnalyserInternalEquation::symEngineEquation(const Analys return {true, SymEngine::cos(left)}; case AnalyserEquationAst::Type::TAN: return {true, SymEngine::tan(left)}; + case AnalyserEquationAst::Type::E: + return {true, SymEngine::E}; + case AnalyserEquationAst::Type::PI: + return {true, SymEngine::pi}; + case AnalyserEquationAst::Type::INF: + return {true, SymEngine::Inf}; case AnalyserEquationAst::Type::CI: // Seems like the voi doesn't exist in mAllVariables, so we don't have an easy means of access. if (symbolMap.find(ast->variable()->name()) == symbolMap.end()) { @@ -250,7 +256,7 @@ SymEngineEquationResult AnalyserInternalEquation::symEngineEquation(const Analys } return {true, symbolMap.at(ast->variable()->name())}; case AnalyserEquationAst::Type::CN: - return {true, SymEngine::integer(std::stoi(ast->value()))}; + return {true, SymEngine::number(std::stod(ast->value()))}; default: // Rearrangement is not possible with this type. return {false, SymEngine::null}; @@ -298,11 +304,27 @@ AnalyserEquationAstPtr AnalyserInternalEquation::parseSymEngineExpression(const ast->setVariable(variableMap.at(symbolExpr)->mVariable); break; } - case SymEngine::SYMENGINE_INTEGER: { + case SymEngine::SYMENGINE_INTEGER: + case SymEngine::SYMENGINE_RATIONAL: + case SymEngine::SYMENGINE_REAL_MPFR: + case SymEngine::SYMENGINE_REAL_DOUBLE: { ast->setType(AnalyserEquationAst::Type::CN); ast->setValue(seExpression->__str__()); break; } + case SymEngine::SYMENGINE_CONSTANT: { + SymEngine::RCP constant = SymEngine::rcp_dynamic_cast(seExpression); + if (SymEngine::eq(*constant, *SymEngine::E)) { + ast->setType(AnalyserEquationAst::Type::E); + } else if (SymEngine::eq(*constant, *SymEngine::pi)) { + ast->setType(AnalyserEquationAst::Type::PI); + } + break; + } + case SymEngine::SYMENGINE_INFTY: { + ast->setType(AnalyserEquationAst::Type::INF); + break; + } default: break; } From 339a003d6eaf4dfdb9a240cd06d85bff63fe58b2 Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Mon, 15 Dec 2025 16:43:20 +1300 Subject: [PATCH 27/44] Fix unarranged polynomial test cases We need to ensure that there can only be one real solution for each --- .../symengine/unarranged_polynomials.cellml | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/tests/resources/analyser/symengine/unarranged_polynomials.cellml b/tests/resources/analyser/symengine/unarranged_polynomials.cellml index 0e5f05dda..980838c55 100644 --- a/tests/resources/analyser/symengine/unarranged_polynomials.cellml +++ b/tests/resources/analyser/symengine/unarranged_polynomials.cellml @@ -1,23 +1,31 @@ + + + + + - + + w3 + + a2 - 2 + 3 - 25 + 125 @@ -33,7 +41,7 @@ - + @@ -44,6 +52,15 @@ 30 + + + + + + d3 + + w + From 5caecad70667f3d9954e80d142e964e73dbb4b28 Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Mon, 15 Dec 2025 16:43:42 +1300 Subject: [PATCH 28/44] Added support for rearranging polynomials Some specific things had to be added to handle this - CN representing integers must be appropriately converted since symengine requires exponentials to be whole numbers - We need to filter out real from non-real solutions since we assume users will want to ignore the complex domain in most cases --- src/analyser.cpp | 40 +++++++++++++++++++++++++++++++++++++--- src/analyser_p.h | 1 + 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/analyser.cpp b/src/analyser.cpp index f2cafe39c..65709a0ed 100644 --- a/src/analyser.cpp +++ b/src/analyser.cpp @@ -237,6 +237,8 @@ SymEngineEquationResult AnalyserInternalEquation::symEngineEquation(const Analys return {true, mul(left, right)}; case AnalyserEquationAst::Type::DIVIDE: return {true, SymEngine::div(left, right)}; + case AnalyserEquationAst::Type::POWER: + return {true, SymEngine::pow(left, right)}; case AnalyserEquationAst::Type::SIN: return {true, SymEngine::sin(left)}; case AnalyserEquationAst::Type::COS: @@ -255,14 +257,40 @@ SymEngineEquationResult AnalyserInternalEquation::symEngineEquation(const Analys return {false, SymEngine::null}; } return {true, symbolMap.at(ast->variable()->name())}; - case AnalyserEquationAst::Type::CN: - return {true, SymEngine::number(std::stod(ast->value()))}; + case AnalyserEquationAst::Type::CN: { + // Some symengine operations necessitate integers to be properly represented. + double astValue = std::stod(ast->value()); + if (std::floor(astValue) == astValue) { + return {true, SymEngine::integer(static_cast(astValue))}; + } else { + return {true, SymEngine::number(astValue)}; + } + } default: // Rearrangement is not possible with this type. return {false, SymEngine::null}; } } +bool AnalyserInternalEquation::isSymEngineExpressionComplex(const SymEngine::RCP &seExpression) +{ + if (seExpression == SymEngine::null) { + return false; + } + + if (SymEngine::is_a_Complex(*seExpression)) { + return true; + } + + for (const auto &child : seExpression->get_args()) { + if (isSymEngineExpressionComplex(child)) { + return true; + } + } + + return false; +} + AnalyserEquationAstPtr AnalyserInternalEquation::parseSymEngineExpression(const SymEngine::RCP &seExpression, const AnalyserEquationAstPtr &parentAst, const SymEngineVariableMap &variableMap) @@ -384,7 +412,13 @@ AnalyserEquationAstPtr AnalyserInternalEquation::rearrangeFor(const AnalyserInte SymEngine::RCP solutionSet = solve(seEquation, symbolMap[variable->mVariable->name()]); SymEngine::vec_basic solutions = solutionSet->get_args(); - // Our system needs to be able to isolate a single solution. + // Attempt to isolate a single real solution. + solutions.erase(std::remove_if(solutions.begin(), solutions.end(), + [this](const SymEngine::RCP &solution) { + return isSymEngineExpressionComplex(solution); + }), + solutions.end()); + if (solutions.size() != 1) { return nullptr; } diff --git a/src/analyser_p.h b/src/analyser_p.h index 733a5c121..81947bab9 100644 --- a/src/analyser_p.h +++ b/src/analyser_p.h @@ -139,6 +139,7 @@ struct AnalyserInternalEquation bool variableOnLhsOrRhs(const AnalyserInternalVariablePtr &variable); SymEngineEquationResult symEngineEquation(const AnalyserEquationAstPtr &ast, const SymEngineSymbolMap &symbolMap); + bool isSymEngineExpressionComplex(const SymEngine::RCP &seExpression); AnalyserEquationAstPtr parseSymEngineExpression(const SymEngine::RCP &seExpression, const AnalyserEquationAstPtr &parentAst, const SymEngineVariableMap &variableMap); AnalyserEquationAstPtr rearrangeFor(const AnalyserInternalVariablePtr &variable); From 5d9cd2fd189e78470c1e2c2ea63c36c50fe8a41d Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Tue, 16 Dec 2025 10:54:05 +1300 Subject: [PATCH 29/44] Update unary +/- parsing Now assumes left child will always be the non-null component --- src/analyser.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/analyser.cpp b/src/analyser.cpp index 65709a0ed..e8d4bebb9 100644 --- a/src/analyser.cpp +++ b/src/analyser.cpp @@ -222,15 +222,14 @@ SymEngineEquationResult AnalyserInternalEquation::symEngineEquation(const Analys return {true, Eq(left, right)}; case AnalyserEquationAst::Type::PLUS: // Handle the case where we have a unary plus. - if (right == SymEngine::null || left == SymEngine::null) { - return {true, right == SymEngine::null ? left : right}; + if (right == SymEngine::null) { + return {true, left}; } return {true, add(left, right)}; case AnalyserEquationAst::Type::MINUS: // Handle the case where we have a unary minus. - if (right == SymEngine::null || left == SymEngine::null) { - auto operand = right == SymEngine::null ? left : right; - return {true, SymEngine::mul(SymEngine::integer(-1), operand)}; + if (right == SymEngine::null) { + return {true, SymEngine::mul(SymEngine::integer(-1), left)}; } return {true, sub(left, right)}; case AnalyserEquationAst::Type::TIMES: From 3a553bed57b4b83352c4fb2bb144944e0672100b Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Wed, 17 Dec 2025 17:18:05 +1300 Subject: [PATCH 30/44] Made explicit types auto --- src/analyser.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyser.cpp b/src/analyser.cpp index e8d4bebb9..18089628b 100644 --- a/src/analyser.cpp +++ b/src/analyser.cpp @@ -362,7 +362,7 @@ AnalyserEquationAstPtr AnalyserInternalEquation::parseSymEngineExpression(const // All children except the last are to be assigned as left children in the AST tree. for (int i = 0; i + 1 < children.size(); ++i) { auto childSeExpression = children[i]; - AnalyserEquationAstPtr childAst = parseSymEngineExpression(childSeExpression, currentAst, variableMap); + auto childAst = parseSymEngineExpression(childSeExpression, currentAst, variableMap); currentAst->setLeftChild(childAst); @@ -382,7 +382,7 @@ AnalyserEquationAstPtr AnalyserInternalEquation::parseSymEngineExpression(const // Set the final node as the right child. if (children.size() >= 2) { auto childSeExpression = children.back(); - AnalyserEquationAstPtr childAst = parseSymEngineExpression(childSeExpression, currentAst, variableMap); + auto childAst = parseSymEngineExpression(childSeExpression, currentAst, variableMap); currentAst->setRightChild(childAst); } From 9efff06fc79148bdb0cab40ef8532fbcf268bab2 Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Wed, 17 Dec 2025 17:20:17 +1300 Subject: [PATCH 31/44] Deleted unused test cases --- tests/analyser/analysersymengine.cpp | 70 ------------ .../symengine/unarranged_combination.cellml | 103 ------------------ .../symengine/unarranged_exponential.cellml | 102 ----------------- .../unarranged_hyperbolic_trig.cellml | 55 ---------- .../symengine/unarranged_inverse_trig.cellml | 55 ---------- .../symengine/unarranged_logarithmic.cellml | 55 ---------- 6 files changed, 440 deletions(-) delete mode 100644 tests/resources/analyser/symengine/unarranged_combination.cellml delete mode 100644 tests/resources/analyser/symengine/unarranged_exponential.cellml delete mode 100644 tests/resources/analyser/symengine/unarranged_hyperbolic_trig.cellml delete mode 100644 tests/resources/analyser/symengine/unarranged_inverse_trig.cellml delete mode 100644 tests/resources/analyser/symengine/unarranged_logarithmic.cellml diff --git a/tests/analyser/analysersymengine.cpp b/tests/analyser/analysersymengine.cpp index 5998b396a..2ab2701c9 100644 --- a/tests/analyser/analysersymengine.cpp +++ b/tests/analyser/analysersymengine.cpp @@ -62,62 +62,6 @@ TEST(Analyser, rearrangeTrigonometricEquations) EXPECT_EQ(libcellml::AnalyserModel::Type::ALGEBRAIC, analyser->analyserModel()->type()); } -TEST(Analyser, rearrangeInverseTrigonometricEquations) -{ - auto parser = libcellml::Parser::create(); - auto model = parser->parseModel(fileContents("analyser/symengine/unarranged_inverse_trig.cellml")); - - EXPECT_EQ(size_t(0), parser->issueCount()); - - auto analyser = libcellml::Analyser::create(); - - analyser->analyseModel(model); - - EXPECT_EQ(libcellml::AnalyserModel::Type::ALGEBRAIC, analyser->analyserModel()->type()); -} - -TEST(Analyser, rearrangeHyperbolicTrigonometricEquations) -{ - auto parser = libcellml::Parser::create(); - auto model = parser->parseModel(fileContents("analyser/symengine/unarranged_hyperbolic_trig.cellml")); - - EXPECT_EQ(size_t(0), parser->issueCount()); - - auto analyser = libcellml::Analyser::create(); - - analyser->analyseModel(model); - - EXPECT_EQ(libcellml::AnalyserModel::Type::ALGEBRAIC, analyser->analyserModel()->type()); -} - -TEST(Analyser, rearrangeExponentialEquations) -{ - auto parser = libcellml::Parser::create(); - auto model = parser->parseModel(fileContents("analyser/symengine/unarranged_exponential.cellml")); - - EXPECT_EQ(size_t(0), parser->issueCount()); - - auto analyser = libcellml::Analyser::create(); - - analyser->analyseModel(model); - - EXPECT_EQ(libcellml::AnalyserModel::Type::ALGEBRAIC, analyser->analyserModel()->type()); -} - -TEST(Analyser, rearrangeLogarithmicEquations) -{ - auto parser = libcellml::Parser::create(); - auto model = parser->parseModel(fileContents("analyser/symengine/unarranged_logarithmic.cellml")); - - EXPECT_EQ(size_t(0), parser->issueCount()); - - auto analyser = libcellml::Analyser::create(); - - analyser->analyseModel(model); - - EXPECT_EQ(libcellml::AnalyserModel::Type::ALGEBRAIC, analyser->analyserModel()->type()); -} - TEST(Analyser, rearrangeEquationsWithConstants) { auto parser = libcellml::Parser::create(); @@ -145,17 +89,3 @@ TEST(Analyser, rearrangePolynomialEquations) EXPECT_EQ(libcellml::AnalyserModel::Type::ALGEBRAIC, analyser->analyserModel()->type()); } - -TEST(Analyser, rearrangeCombinationEquations) -{ - auto parser = libcellml::Parser::create(); - auto model = parser->parseModel(fileContents("analyser/symengine/unarranged_combination.cellml")); - - EXPECT_EQ(size_t(0), parser->issueCount()); - - auto analyser = libcellml::Analyser::create(); - - analyser->analyseModel(model); - - EXPECT_EQ(libcellml::AnalyserModel::Type::ALGEBRAIC, analyser->analyserModel()->type()); -} \ No newline at end of file diff --git a/tests/resources/analyser/symengine/unarranged_combination.cellml b/tests/resources/analyser/symengine/unarranged_combination.cellml deleted file mode 100644 index 61c1657cf..000000000 --- a/tests/resources/analyser/symengine/unarranged_combination.cellml +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - - - - - - - - - - - - - - - w2 - x3 - y5 - z7 - - - - - a - - - - - - y - xw2 - - - 2z - - - - - - - b - - - - - - - w - xy - - z2 - - - - - - - - c - - - - - aw2 - 3y2z - - x - - - - - - - d - - - - - - - 2z - yw - - - - - x3.141592653589793 - w - - - - y1 - - - - - - diff --git a/tests/resources/analyser/symengine/unarranged_exponential.cellml b/tests/resources/analyser/symengine/unarranged_exponential.cellml deleted file mode 100644 index 92f58e978..000000000 --- a/tests/resources/analyser/symengine/unarranged_exponential.cellml +++ /dev/null @@ -1,102 +0,0 @@ - - - - - - - - - - - - - - - - - - - - w2 - x3 - y5 - z7 - - - - - - - - aw - 3 - ax - - - 200 - - - - - - - - - - 2b - by - - xz - - 100 - - - - - - - - - - - cw - z - - - y - cx - - - 500 - - - - - - - - - 4 - dy - - - z - dw - - - - 300 - - - - - diff --git a/tests/resources/analyser/symengine/unarranged_hyperbolic_trig.cellml b/tests/resources/analyser/symengine/unarranged_hyperbolic_trig.cellml deleted file mode 100644 index 9db2f3338..000000000 --- a/tests/resources/analyser/symengine/unarranged_hyperbolic_trig.cellml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - - - - - - - - w2 - x3 - y4 - z5 - - - - - ab - 5 - - - - - - bc - x - - - - - - c2d - y - - - - - - 3da - z - - - - - diff --git a/tests/resources/analyser/symengine/unarranged_inverse_trig.cellml b/tests/resources/analyser/symengine/unarranged_inverse_trig.cellml deleted file mode 100644 index 4790238c9..000000000 --- a/tests/resources/analyser/symengine/unarranged_inverse_trig.cellml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - - - - - - - - w1 - x2 - y3 - z4 - - - - - ab - w - - - - - - bc - x - - - - - - c2d - y - - - - - - 3da - z - - - - - diff --git a/tests/resources/analyser/symengine/unarranged_logarithmic.cellml b/tests/resources/analyser/symengine/unarranged_logarithmic.cellml deleted file mode 100644 index 96ceaeba8..000000000 --- a/tests/resources/analyser/symengine/unarranged_logarithmic.cellml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - - - - - - - - x3 - y1000 - u1.5 - v100 - - - - - ax - 5 - - - - - - yb - 3 - - - - - - cu - 2.5 - - - - - - dv - 4 - - - - - From f8a39f52c927c58f18b0da5dc070f701f1d65f4e Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Wed, 17 Dec 2025 17:20:48 +1300 Subject: [PATCH 32/44] Renamed symengine cellml test files --- tests/analyser/analysersymengine.cpp | 10 +++++----- .../{unarranged_addition.cellml => addition.cellml} | 0 .../{unarranged_constants.cellml => constants.cellml} | 0 ...ged_multiplication.cellml => multiplication.cellml} | 0 ...narranged_polynomials.cellml => polynomials.cellml} | 4 ++-- .../{unarranged_trig.cellml => trigonometric.cellml} | 0 6 files changed, 7 insertions(+), 7 deletions(-) rename tests/resources/analyser/symengine/{unarranged_addition.cellml => addition.cellml} (100%) rename tests/resources/analyser/symengine/{unarranged_constants.cellml => constants.cellml} (100%) rename tests/resources/analyser/symengine/{unarranged_multiplication.cellml => multiplication.cellml} (100%) rename tests/resources/analyser/symengine/{unarranged_polynomials.cellml => polynomials.cellml} (95%) rename tests/resources/analyser/symengine/{unarranged_trig.cellml => trigonometric.cellml} (100%) diff --git a/tests/analyser/analysersymengine.cpp b/tests/analyser/analysersymengine.cpp index 2ab2701c9..ec2de77e4 100644 --- a/tests/analyser/analysersymengine.cpp +++ b/tests/analyser/analysersymengine.cpp @@ -23,7 +23,7 @@ limitations under the License. TEST(Analyser, rearrangeAdditiveEquations) { auto parser = libcellml::Parser::create(); - auto model = parser->parseModel(fileContents("analyser/symengine/unarranged_addition.cellml")); + auto model = parser->parseModel(fileContents("analyser/symengine/addition.cellml")); EXPECT_EQ(size_t(0), parser->issueCount()); @@ -37,7 +37,7 @@ TEST(Analyser, rearrangeAdditiveEquations) TEST(Analyser, rearrangeMultiplicativeEquations) { auto parser = libcellml::Parser::create(); - auto model = parser->parseModel(fileContents("analyser/symengine/unarranged_multiplication.cellml")); + auto model = parser->parseModel(fileContents("analyser/symengine/multiplication.cellml")); EXPECT_EQ(size_t(0), parser->issueCount()); @@ -51,7 +51,7 @@ TEST(Analyser, rearrangeMultiplicativeEquations) TEST(Analyser, rearrangeTrigonometricEquations) { auto parser = libcellml::Parser::create(); - auto model = parser->parseModel(fileContents("analyser/symengine/unarranged_trig.cellml")); + auto model = parser->parseModel(fileContents("analyser/symengine/trigonometric.cellml")); EXPECT_EQ(size_t(0), parser->issueCount()); @@ -65,7 +65,7 @@ TEST(Analyser, rearrangeTrigonometricEquations) TEST(Analyser, rearrangeEquationsWithConstants) { auto parser = libcellml::Parser::create(); - auto model = parser->parseModel(fileContents("analyser/symengine/unarranged_constants.cellml")); + auto model = parser->parseModel(fileContents("analyser/symengine/constants.cellml")); EXPECT_EQ(size_t(0), parser->issueCount()); @@ -79,7 +79,7 @@ TEST(Analyser, rearrangeEquationsWithConstants) TEST(Analyser, rearrangePolynomialEquations) { auto parser = libcellml::Parser::create(); - auto model = parser->parseModel(fileContents("analyser/symengine/unarranged_polynomials.cellml")); + auto model = parser->parseModel(fileContents("analyser/symengine/polynomials.cellml")); EXPECT_EQ(size_t(0), parser->issueCount()); diff --git a/tests/resources/analyser/symengine/unarranged_addition.cellml b/tests/resources/analyser/symengine/addition.cellml similarity index 100% rename from tests/resources/analyser/symengine/unarranged_addition.cellml rename to tests/resources/analyser/symengine/addition.cellml diff --git a/tests/resources/analyser/symengine/unarranged_constants.cellml b/tests/resources/analyser/symengine/constants.cellml similarity index 100% rename from tests/resources/analyser/symengine/unarranged_constants.cellml rename to tests/resources/analyser/symengine/constants.cellml diff --git a/tests/resources/analyser/symengine/unarranged_multiplication.cellml b/tests/resources/analyser/symengine/multiplication.cellml similarity index 100% rename from tests/resources/analyser/symengine/unarranged_multiplication.cellml rename to tests/resources/analyser/symengine/multiplication.cellml diff --git a/tests/resources/analyser/symengine/unarranged_polynomials.cellml b/tests/resources/analyser/symengine/polynomials.cellml similarity index 95% rename from tests/resources/analyser/symengine/unarranged_polynomials.cellml rename to tests/resources/analyser/symengine/polynomials.cellml index 980838c55..7a7bc2bd0 100644 --- a/tests/resources/analyser/symengine/unarranged_polynomials.cellml +++ b/tests/resources/analyser/symengine/polynomials.cellml @@ -40,7 +40,7 @@ 12 - + @@ -49,7 +49,7 @@ c3 3c - 30 + 36 diff --git a/tests/resources/analyser/symengine/unarranged_trig.cellml b/tests/resources/analyser/symengine/trigonometric.cellml similarity index 100% rename from tests/resources/analyser/symengine/unarranged_trig.cellml rename to tests/resources/analyser/symengine/trigonometric.cellml From d79d8fc36d48476099d099b6fc5b176602af5865 Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Wed, 17 Dec 2025 18:20:02 +1300 Subject: [PATCH 33/44] Cleaned up symengine trigonometric test case --- .../analyser/symengine/trigonometric.cellml | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/tests/resources/analyser/symengine/trigonometric.cellml b/tests/resources/analyser/symengine/trigonometric.cellml index 457284c27..8268987a6 100644 --- a/tests/resources/analyser/symengine/trigonometric.cellml +++ b/tests/resources/analyser/symengine/trigonometric.cellml @@ -5,13 +5,11 @@ - - - - - + + + @@ -20,7 +18,6 @@ w0.5 x1.0 y1.5 - z2.0 @@ -32,21 +29,14 @@ - 4xb + 4xb 0 - + - c - z - - - - - - dyx + c3y 2 From aab7d89379906f057c17c6335b71580c71982d93 Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Wed, 17 Dec 2025 18:19:02 +1300 Subject: [PATCH 34/44] Fix issue with recreating unary functions for AST --- src/analyser.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/analyser.cpp b/src/analyser.cpp index 18089628b..9173f203b 100644 --- a/src/analyser.cpp +++ b/src/analyser.cpp @@ -359,7 +359,7 @@ AnalyserEquationAstPtr AnalyserInternalEquation::parseSymEngineExpression(const auto children = seExpression->get_args(); auto currentAst = ast; - // All children except the last are to be assigned as left children in the AST tree. + // All children (except the last) are guaranteed to be left children in the AST tree. for (int i = 0; i + 1 < children.size(); ++i) { auto childSeExpression = children[i]; auto childAst = parseSymEngineExpression(childSeExpression, currentAst, variableMap); @@ -379,16 +379,15 @@ AnalyserEquationAstPtr AnalyserInternalEquation::parseSymEngineExpression(const } } - // Set the final node as the right child. - if (children.size() >= 2) { + // The final child is created and placed where appropriate. + if (children.size() != 0) { auto childSeExpression = children.back(); auto childAst = parseSymEngineExpression(childSeExpression, currentAst, variableMap); - currentAst->setRightChild(childAst); + children.size() == 1 ? currentAst->setLeftChild(childAst) : + currentAst->setRightChild(childAst); } - ast->setParent(parentAst); - return ast; } From 9c1e0b8917a00a9789ec44f9dfae7fc87fef7e8b Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Thu, 18 Dec 2025 09:28:01 +1300 Subject: [PATCH 35/44] Fixed children not setting their parent AST I can't believe I didn't get an error for this sooner!!! --- src/analyser.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/analyser.cpp b/src/analyser.cpp index 9173f203b..6b6c446f4 100644 --- a/src/analyser.cpp +++ b/src/analyser.cpp @@ -365,6 +365,7 @@ AnalyserEquationAstPtr AnalyserInternalEquation::parseSymEngineExpression(const auto childAst = parseSymEngineExpression(childSeExpression, currentAst, variableMap); currentAst->setLeftChild(childAst); + childAst->setParent(currentAst); if (i < children.size() - 2) { // Since there are more than two children left, we need to create another copy @@ -386,6 +387,7 @@ AnalyserEquationAstPtr AnalyserInternalEquation::parseSymEngineExpression(const children.size() == 1 ? currentAst->setLeftChild(childAst) : currentAst->setRightChild(childAst); + childAst->setParent(currentAst); } return ast; From fc1cf1c94c935dfda779e6eb16c2cd50b1ed8824 Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Thu, 18 Dec 2025 09:45:19 +1300 Subject: [PATCH 36/44] Cleaned symengine test cases - Added comments - Tried to give each test case a more 'unique' purpose --- .../analyser/symengine/addition.cellml | 4 ++++ .../analyser/symengine/constants.cellml | 11 +++++++++-- .../analyser/symengine/multiplication.cellml | 14 +++++++++----- .../analyser/symengine/polynomials.cellml | 19 +++++++++++++------ .../analyser/symengine/trigonometric.cellml | 5 ++++- 5 files changed, 39 insertions(+), 14 deletions(-) diff --git a/tests/resources/analyser/symengine/addition.cellml b/tests/resources/analyser/symengine/addition.cellml index 9403b3800..953604d4f 100644 --- a/tests/resources/analyser/symengine/addition.cellml +++ b/tests/resources/analyser/symengine/addition.cellml @@ -23,6 +23,7 @@ z5 + awx @@ -30,6 +31,7 @@ + by2 @@ -37,6 +39,7 @@ + cx1 @@ -44,6 +47,7 @@ + dw diff --git a/tests/resources/analyser/symengine/constants.cellml b/tests/resources/analyser/symengine/constants.cellml index de5235735..f34380d14 100644 --- a/tests/resources/analyser/symengine/constants.cellml +++ b/tests/resources/analyser/symengine/constants.cellml @@ -1,5 +1,7 @@ + + @@ -7,7 +9,7 @@ - + @@ -17,13 +19,14 @@ - + w3 x4 y5 z2 + ax @@ -31,6 +34,7 @@ + bw @@ -38,6 +42,7 @@ + cy @@ -45,6 +50,7 @@ + dz @@ -52,6 +58,7 @@ + ew diff --git a/tests/resources/analyser/symengine/multiplication.cellml b/tests/resources/analyser/symengine/multiplication.cellml index bdc677d57..402d955c7 100644 --- a/tests/resources/analyser/symengine/multiplication.cellml +++ b/tests/resources/analyser/symengine/multiplication.cellml @@ -22,25 +22,28 @@ y4 z5 - + + - awx + aw 4 - + + - bxy - 2 + b + y 18 + @@ -52,6 +55,7 @@ + diff --git a/tests/resources/analyser/symengine/polynomials.cellml b/tests/resources/analyser/symengine/polynomials.cellml index 7a7bc2bd0..a26ac6d8f 100644 --- a/tests/resources/analyser/symengine/polynomials.cellml +++ b/tests/resources/analyser/symengine/polynomials.cellml @@ -1,10 +1,12 @@ + + - + @@ -13,11 +15,12 @@ - + w3 + @@ -28,20 +31,23 @@ 125 - - + + + - b3 + b2 + 4b 4 - 12 + 0 + @@ -54,6 +60,7 @@ + diff --git a/tests/resources/analyser/symengine/trigonometric.cellml b/tests/resources/analyser/symengine/trigonometric.cellml index 8268987a6..e9a2c0006 100644 --- a/tests/resources/analyser/symengine/trigonometric.cellml +++ b/tests/resources/analyser/symengine/trigonometric.cellml @@ -20,6 +20,7 @@ y1.5 + 2aw @@ -27,13 +28,15 @@ + 4xb 0 - + + c3y From ec0ace2a3a629ea543ca00e2c7c56daf6f6b57d4 Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Thu, 18 Dec 2025 10:29:15 +1300 Subject: [PATCH 37/44] Test expected generator equation code Also moved initialising constants to the bottom of the mathml block so we guarantee that our tested equation range always starts at 0 --- tests/analyser/analysersymengine.cpp | 25 +++++++++++++++++++ .../analyser/symengine/addition.cellml | 12 ++++----- .../analyser/symengine/constants.cellml | 12 ++++----- .../analyser/symengine/multiplication.cellml | 12 ++++----- .../analyser/symengine/polynomials.cellml | 7 +++--- .../analyser/symengine/trigonometric.cellml | 10 ++++---- 6 files changed, 52 insertions(+), 26 deletions(-) diff --git a/tests/analyser/analysersymengine.cpp b/tests/analyser/analysersymengine.cpp index ec2de77e4..a1b232162 100644 --- a/tests/analyser/analysersymengine.cpp +++ b/tests/analyser/analysersymengine.cpp @@ -32,6 +32,11 @@ TEST(Analyser, rearrangeAdditiveEquations) analyser->analyseModel(model); EXPECT_EQ(libcellml::AnalyserModel::Type::ALGEBRAIC, analyser->analyserModel()->type()); + + EXPECT_EQ("a = 10.0+-1.0*(w+x)", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(0)->ast())); + EXPECT_EQ("b = 1.0+-1.0*(2.0+-1.0*y)", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(1)->ast())); + EXPECT_EQ("c = -1.0*z+-1.0*(1.0+x)", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(2)->ast())); + EXPECT_EQ("d = y+-1.0*w", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(3)->ast())); } TEST(Analyser, rearrangeMultiplicativeEquations) @@ -46,6 +51,11 @@ TEST(Analyser, rearrangeMultiplicativeEquations) analyser->analyseModel(model); EXPECT_EQ(libcellml::AnalyserModel::Type::ALGEBRAIC, analyser->analyserModel()->type()); + + EXPECT_EQ("a = 4.0*pow(w, -1.0)", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(0)->ast())); + EXPECT_EQ("b = 18.0*y", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(1)->ast())); + EXPECT_EQ("c = 5.0*pow(w, -1.0)*y*pow(z, -1.0)", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(2)->ast())); + EXPECT_EQ("d = 30.0*w*pow(z, -1.0)", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(3)->ast())); } TEST(Analyser, rearrangeTrigonometricEquations) @@ -60,6 +70,10 @@ TEST(Analyser, rearrangeTrigonometricEquations) analyser->analyseModel(model); EXPECT_EQ(libcellml::AnalyserModel::Type::ALGEBRAIC, analyser->analyserModel()->type()); + + EXPECT_EQ("a = 1/2.0*(1.0+-1.0*sin(w))", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(0)->ast())); + EXPECT_EQ("b = cos(4.0+x)", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(1)->ast())); + EXPECT_EQ("c = 2.0+tan(3.0+-1.0*y)", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(2)->ast())); } TEST(Analyser, rearrangeEquationsWithConstants) @@ -74,6 +88,12 @@ TEST(Analyser, rearrangeEquationsWithConstants) analyser->analyseModel(model); EXPECT_EQ(libcellml::AnalyserModel::Type::ALGEBRAIC, analyser->analyserModel()->type()); + + EXPECT_EQ("a = 8.65+-1.0*x", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(0)->ast())); + EXPECT_EQ("b = 400000.0*pow(w, -1.0)", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(1)->ast())); + EXPECT_EQ("c = y*2.71828182845905", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(2)->ast())); + EXPECT_EQ("d = -1.0*(-1.0*z+-1.0*3.14159265358979)", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(3)->ast())); + EXPECT_EQ("e = INFINITY+-1.0*w", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(4)->ast())); } TEST(Analyser, rearrangePolynomialEquations) @@ -88,4 +108,9 @@ TEST(Analyser, rearrangePolynomialEquations) analyser->analyseModel(model); EXPECT_EQ(libcellml::AnalyserModel::Type::ALGEBRAIC, analyser->analyserModel()->type()); + + EXPECT_EQ("a = -1/3.0*(6.0+15.0*pow(-1.0, 1/3.0))", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(0)->ast())); + EXPECT_EQ("b = -2.0", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(1)->ast())); + EXPECT_EQ("c = -1/3.0*(1/2.0*pow(2.0, 2/3.0)*pow(-972.0+pow(947700.0, 1/2.0), 1/3.0)+-9.0*pow(2.0, 1/3.0)*pow(-972.0+pow(947700.0, 1/2.0), -1/3.0))", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(2)->ast())); + EXPECT_EQ("d = -1/6.0*pow(2.0, 2/3.0)*pow(-27.0*w+27.0*pow(pow(w, 2.0), 1/2.0), 1/3.0)", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(3)->ast())); } diff --git a/tests/resources/analyser/symengine/addition.cellml b/tests/resources/analyser/symengine/addition.cellml index 953604d4f..be6cbfa35 100644 --- a/tests/resources/analyser/symengine/addition.cellml +++ b/tests/resources/analyser/symengine/addition.cellml @@ -16,12 +16,6 @@ - - w2 - x3 - y4 - z5 - @@ -54,6 +48,12 @@ y + + w2 + x3 + y4 + z5 + diff --git a/tests/resources/analyser/symengine/constants.cellml b/tests/resources/analyser/symengine/constants.cellml index f34380d14..bf0652621 100644 --- a/tests/resources/analyser/symengine/constants.cellml +++ b/tests/resources/analyser/symengine/constants.cellml @@ -19,12 +19,6 @@ - - w3 - x4 - y5 - z2 - @@ -65,6 +59,12 @@ + + w3 + x4 + y5 + z2 + diff --git a/tests/resources/analyser/symengine/multiplication.cellml b/tests/resources/analyser/symengine/multiplication.cellml index 402d955c7..af15c87ea 100644 --- a/tests/resources/analyser/symengine/multiplication.cellml +++ b/tests/resources/analyser/symengine/multiplication.cellml @@ -16,12 +16,6 @@ - - w2 - x3 - y4 - z5 - @@ -66,6 +60,12 @@ 10 + + w2 + x3 + y4 + z5 + diff --git a/tests/resources/analyser/symengine/polynomials.cellml b/tests/resources/analyser/symengine/polynomials.cellml index a26ac6d8f..f2a2445fd 100644 --- a/tests/resources/analyser/symengine/polynomials.cellml +++ b/tests/resources/analyser/symengine/polynomials.cellml @@ -15,9 +15,6 @@ - - w3 - @@ -68,6 +65,10 @@ w + + + w3 + diff --git a/tests/resources/analyser/symengine/trigonometric.cellml b/tests/resources/analyser/symengine/trigonometric.cellml index e9a2c0006..90628d5be 100644 --- a/tests/resources/analyser/symengine/trigonometric.cellml +++ b/tests/resources/analyser/symengine/trigonometric.cellml @@ -14,11 +14,6 @@ - - w0.5 - x1.0 - y1.5 - @@ -43,6 +38,11 @@ 2 + + w0.5 + x1.0 + y1.5 + From f50049e79eeb8ac3cfd23ab71e9fadae927dceee Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Thu, 18 Dec 2025 11:04:05 +1300 Subject: [PATCH 38/44] Made childSeExpression retrieval inline --- src/analyser.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/analyser.cpp b/src/analyser.cpp index 6b6c446f4..744d0fb49 100644 --- a/src/analyser.cpp +++ b/src/analyser.cpp @@ -361,8 +361,7 @@ AnalyserEquationAstPtr AnalyserInternalEquation::parseSymEngineExpression(const // All children (except the last) are guaranteed to be left children in the AST tree. for (int i = 0; i + 1 < children.size(); ++i) { - auto childSeExpression = children[i]; - auto childAst = parseSymEngineExpression(childSeExpression, currentAst, variableMap); + auto childAst = parseSymEngineExpression(children[i], currentAst, variableMap); currentAst->setLeftChild(childAst); childAst->setParent(currentAst); @@ -382,8 +381,7 @@ AnalyserEquationAstPtr AnalyserInternalEquation::parseSymEngineExpression(const // The final child is created and placed where appropriate. if (children.size() != 0) { - auto childSeExpression = children.back(); - auto childAst = parseSymEngineExpression(childSeExpression, currentAst, variableMap); + auto childAst = parseSymEngineExpression(children.back(), currentAst, variableMap); children.size() == 1 ? currentAst->setLeftChild(childAst) : currentAst->setRightChild(childAst); From 5ca91b3b4cc3839127dd89368ed0629778897dff Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Thu, 18 Dec 2025 11:22:26 +1300 Subject: [PATCH 39/44] Fixed spelling error in symengine test files --- tests/resources/analyser/symengine/addition.cellml | 2 +- tests/resources/analyser/symengine/constants.cellml | 2 +- tests/resources/analyser/symengine/multiplication.cellml | 2 +- tests/resources/analyser/symengine/polynomials.cellml | 4 ++-- tests/resources/analyser/symengine/trigonometric.cellml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/resources/analyser/symengine/addition.cellml b/tests/resources/analyser/symengine/addition.cellml index be6cbfa35..dceee0538 100644 --- a/tests/resources/analyser/symengine/addition.cellml +++ b/tests/resources/analyser/symengine/addition.cellml @@ -48,7 +48,7 @@ y - + w2 x3 y4 diff --git a/tests/resources/analyser/symengine/constants.cellml b/tests/resources/analyser/symengine/constants.cellml index bf0652621..cd73cd7c2 100644 --- a/tests/resources/analyser/symengine/constants.cellml +++ b/tests/resources/analyser/symengine/constants.cellml @@ -59,7 +59,7 @@ - + w3 x4 y5 diff --git a/tests/resources/analyser/symengine/multiplication.cellml b/tests/resources/analyser/symengine/multiplication.cellml index af15c87ea..783ceaedc 100644 --- a/tests/resources/analyser/symengine/multiplication.cellml +++ b/tests/resources/analyser/symengine/multiplication.cellml @@ -60,7 +60,7 @@ 10 - + w2 x3 y4 diff --git a/tests/resources/analyser/symengine/polynomials.cellml b/tests/resources/analyser/symengine/polynomials.cellml index f2a2445fd..da4afa4f4 100644 --- a/tests/resources/analyser/symengine/polynomials.cellml +++ b/tests/resources/analyser/symengine/polynomials.cellml @@ -66,9 +66,9 @@ w - + w3 - + diff --git a/tests/resources/analyser/symengine/trigonometric.cellml b/tests/resources/analyser/symengine/trigonometric.cellml index 90628d5be..ba4f7316b 100644 --- a/tests/resources/analyser/symengine/trigonometric.cellml +++ b/tests/resources/analyser/symengine/trigonometric.cellml @@ -38,7 +38,7 @@ 2 - + w0.5 x1.0 y1.5 From 156bd52797c45d2d92b6462a2ac85ec349f41d8f Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Thu, 18 Dec 2025 11:26:25 +1300 Subject: [PATCH 40/44] Removed redundant symengine multiplication test case --- tests/analyser/analysersymengine.cpp | 3 +-- .../analyser/symengine/multiplication.cellml | 19 +++---------------- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/tests/analyser/analysersymengine.cpp b/tests/analyser/analysersymengine.cpp index a1b232162..3cc4e2be2 100644 --- a/tests/analyser/analysersymengine.cpp +++ b/tests/analyser/analysersymengine.cpp @@ -54,8 +54,7 @@ TEST(Analyser, rearrangeMultiplicativeEquations) EXPECT_EQ("a = 4.0*pow(w, -1.0)", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(0)->ast())); EXPECT_EQ("b = 18.0*y", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(1)->ast())); - EXPECT_EQ("c = 5.0*pow(w, -1.0)*y*pow(z, -1.0)", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(2)->ast())); - EXPECT_EQ("d = 30.0*w*pow(z, -1.0)", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(3)->ast())); + EXPECT_EQ("c = 30.0*x*pow(z, -1.0)", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(2)->ast())); } TEST(Analyser, rearrangeTrigonometricEquations) diff --git a/tests/resources/analyser/symengine/multiplication.cellml b/tests/resources/analyser/symengine/multiplication.cellml index 783ceaedc..e4fdc5233 100644 --- a/tests/resources/analyser/symengine/multiplication.cellml +++ b/tests/resources/analyser/symengine/multiplication.cellml @@ -11,7 +11,6 @@ - @@ -36,26 +35,14 @@ 18 - + - cwz - y - - 5 - - - - - - - - - dz - w3 + cz + x3 10 From ef336d5b39230be0da785aa793ee93bc80ebdb2b Mon Sep 17 00:00:00 2001 From: Rayen Lee Date: Thu, 18 Dec 2025 13:23:00 +1300 Subject: [PATCH 41/44] Manual simplification of symengine results x + (-y) now becomes x - y -1 * y now becomes -y this also means x + (-1 * y) becomes x - y --- src/analyser.cpp | 23 +++++++++++++++++++---- tests/analyser/analysersymengine.cpp | 18 +++++++++--------- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/analyser.cpp b/src/analyser.cpp index 744d0fb49..0cb56d3c3 100644 --- a/src/analyser.cpp +++ b/src/analyser.cpp @@ -295,6 +295,8 @@ AnalyserEquationAstPtr AnalyserInternalEquation::parseSymEngineExpression(const const SymEngineVariableMap &variableMap) { AnalyserEquationAstPtr ast = AnalyserEquationAst::create(); + ast->setParent(parentAst); + auto children = seExpression->get_args(); switch (seExpression->get_type_code()) { case SymEngine::SYMENGINE_EQUALITY: { @@ -306,7 +308,13 @@ AnalyserEquationAstPtr AnalyserInternalEquation::parseSymEngineExpression(const break; } case SymEngine::SYMENGINE_MUL: { - ast->setType(AnalyserEquationAst::Type::TIMES); + if (SymEngine::eq(*(children[0]), *SymEngine::integer(-1))) { + // Convert -1 * x to -x. + ast->setType(AnalyserEquationAst::Type::MINUS); + children.erase(children.begin()); + } else { + ast->setType(AnalyserEquationAst::Type::TIMES); + } break; } case SymEngine::SYMENGINE_POW: { @@ -356,7 +364,6 @@ AnalyserEquationAstPtr AnalyserInternalEquation::parseSymEngineExpression(const break; } - auto children = seExpression->get_args(); auto currentAst = ast; // All children (except the last) are guaranteed to be left children in the AST tree. @@ -364,7 +371,6 @@ AnalyserEquationAstPtr AnalyserInternalEquation::parseSymEngineExpression(const auto childAst = parseSymEngineExpression(children[i], currentAst, variableMap); currentAst->setLeftChild(childAst); - childAst->setParent(currentAst); if (i < children.size() - 2) { // Since there are more than two children left, we need to create another copy @@ -385,7 +391,16 @@ AnalyserEquationAstPtr AnalyserInternalEquation::parseSymEngineExpression(const children.size() == 1 ? currentAst->setLeftChild(childAst) : currentAst->setRightChild(childAst); - childAst->setParent(currentAst); + + // Check for special case where we want to simplify x + (-y) to x - y. + if (children.size() >= 2 + && currentAst->type() == AnalyserEquationAst::Type::PLUS + && childAst->type() == AnalyserEquationAst::Type::MINUS + && childAst->rightChild() == nullptr) { + currentAst->setType(AnalyserEquationAst::Type::MINUS); + currentAst->setRightChild(childAst->leftChild()); + childAst->leftChild()->setParent(currentAst); + } } return ast; diff --git a/tests/analyser/analysersymengine.cpp b/tests/analyser/analysersymengine.cpp index 3cc4e2be2..5c92a4193 100644 --- a/tests/analyser/analysersymengine.cpp +++ b/tests/analyser/analysersymengine.cpp @@ -33,10 +33,10 @@ TEST(Analyser, rearrangeAdditiveEquations) EXPECT_EQ(libcellml::AnalyserModel::Type::ALGEBRAIC, analyser->analyserModel()->type()); - EXPECT_EQ("a = 10.0+-1.0*(w+x)", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(0)->ast())); - EXPECT_EQ("b = 1.0+-1.0*(2.0+-1.0*y)", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(1)->ast())); - EXPECT_EQ("c = -1.0*z+-1.0*(1.0+x)", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(2)->ast())); - EXPECT_EQ("d = y+-1.0*w", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(3)->ast())); + EXPECT_EQ("a = 10.0-(w+x)", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(0)->ast())); + EXPECT_EQ("b = 1.0-(2.0-y)", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(1)->ast())); + EXPECT_EQ("c = -z-(1.0+x)", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(2)->ast())); + EXPECT_EQ("d = y-w", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(3)->ast())); } TEST(Analyser, rearrangeMultiplicativeEquations) @@ -70,9 +70,9 @@ TEST(Analyser, rearrangeTrigonometricEquations) EXPECT_EQ(libcellml::AnalyserModel::Type::ALGEBRAIC, analyser->analyserModel()->type()); - EXPECT_EQ("a = 1/2.0*(1.0+-1.0*sin(w))", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(0)->ast())); + EXPECT_EQ("a = 1/2.0*(1.0-sin(w))", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(0)->ast())); EXPECT_EQ("b = cos(4.0+x)", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(1)->ast())); - EXPECT_EQ("c = 2.0+tan(3.0+-1.0*y)", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(2)->ast())); + EXPECT_EQ("c = 2.0+tan(3.0-y)", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(2)->ast())); } TEST(Analyser, rearrangeEquationsWithConstants) @@ -88,11 +88,11 @@ TEST(Analyser, rearrangeEquationsWithConstants) EXPECT_EQ(libcellml::AnalyserModel::Type::ALGEBRAIC, analyser->analyserModel()->type()); - EXPECT_EQ("a = 8.65+-1.0*x", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(0)->ast())); + EXPECT_EQ("a = 8.65-x", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(0)->ast())); EXPECT_EQ("b = 400000.0*pow(w, -1.0)", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(1)->ast())); EXPECT_EQ("c = y*2.71828182845905", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(2)->ast())); - EXPECT_EQ("d = -1.0*(-1.0*z+-1.0*3.14159265358979)", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(3)->ast())); - EXPECT_EQ("e = INFINITY+-1.0*w", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(4)->ast())); + EXPECT_EQ("d = -(-z-3.14159265358979)", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(3)->ast())); + EXPECT_EQ("e = INFINITY-w", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(4)->ast())); } TEST(Analyser, rearrangePolynomialEquations) From a6005dd61befcbdeae5b8210b167441a807b3c93 Mon Sep 17 00:00:00 2001 From: Alan Garny Date: Wed, 17 Dec 2025 17:00:55 +1300 Subject: [PATCH 42/44] CI: temporarily allow CI tests to be run on PRs to the `staging` branch. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b17a4a1ca..25ffd3c36 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ name: CI on: pull_request: - branches: [ main ] + branches: [ main, staging ] workflow_dispatch: env: From 9b686da947f61076814e3bed9c5ee68d0a0d38f1 Mon Sep 17 00:00:00 2001 From: Alan Garny Date: Mon, 15 Dec 2025 19:44:07 +1300 Subject: [PATCH 43/44] CMake: find and use SymEngine. --- cmake/environmentchecks.cmake | 9 +++++++++ src/3rdparty/symengine/symenginebegin.h | 22 ++++++++++++++++++++++ src/3rdparty/symengine/symengineend.h | 19 +++++++++++++++++++ src/CMakeLists.txt | 19 +++++++------------ src/analyser.cpp | 5 +++++ 5 files changed, 62 insertions(+), 12 deletions(-) create mode 100644 src/3rdparty/symengine/symenginebegin.h create mode 100644 src/3rdparty/symengine/symengineend.h diff --git a/cmake/environmentchecks.cmake b/cmake/environmentchecks.cmake index 260b5c0ae..3a960c6d1 100644 --- a/cmake/environmentchecks.cmake +++ b/cmake/environmentchecks.cmake @@ -183,6 +183,15 @@ if(NOT DEFINED _ZLIB_FIND_REPORTED) message(STATUS "Found ZLIB: ${ZLIB_LIBRARIES} (found version \"${ZLIB_VERSION_STRING}\").") endif() +# Set the minimum policy version to work around SymEngine's outdated CMake requirements. +if(NOT DEFINED CMAKE_POLICY_VERSION_MINIMUM OR CMAKE_POLICY_VERSION_MINIMUM VERSION_LESS "3.10") + set(CMAKE_POLICY_VERSION_MINIMUM 3.10) +endif() + +# Find SymEngine. +find_package(SymEngine REQUIRED CONFIG) +message(STATUS "Found SymEngine: ${SYMENGINE_LIBRARIES} (found version \"${SymEngine_VERSION}\").") + if(BUILDCACHE_EXE OR CLCACHE_EXE OR CCACHE_EXE) set(COMPILER_CACHE_AVAILABLE TRUE CACHE INTERNAL "Executable required to cache compilations.") endif() diff --git a/src/3rdparty/symengine/symenginebegin.h b/src/3rdparty/symengine/symenginebegin.h new file mode 100644 index 000000000..84290dadf --- /dev/null +++ b/src/3rdparty/symengine/symenginebegin.h @@ -0,0 +1,22 @@ +/* +Copyright libOpenCOR contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated-literal-operator" +# pragma clang diagnostic ignored "-Wsign-compare" +# pragma clang diagnostic ignored "-Wunused-parameter" +#endif diff --git a/src/3rdparty/symengine/symengineend.h b/src/3rdparty/symengine/symengineend.h new file mode 100644 index 000000000..3d9678e22 --- /dev/null +++ b/src/3rdparty/symengine/symengineend.h @@ -0,0 +1,19 @@ +/* +Copyright libOpenCOR contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d971b4e33..afe5aa1b5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -178,18 +178,6 @@ add_library(cellml ${API_HEADER_FILES} ) -target_include_directories(cellml - PUBLIC - "C:/symengine/include" - "C:/gmp/include" -) - -target_link_libraries(cellml - PUBLIC - "C:/symengine/lib/symengine.lib" - "C:/gmp/lib/gmp.lib" -) - set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/xmldoc.cpp PROPERTIES COMPILE_DEFINITIONS XML_ERROR_CALLBACK_ARGUMENT_TYPE=${CONST_ERROR_STRUCTURED_ERROR_CALLBACK_TYPE}) @@ -215,6 +203,13 @@ endif() apply_libxml2_settings(cellml) +target_link_libraries(cellml PRIVATE ${SYMENGINE_LIBRARIES}) +target_include_directories(cellml + PRIVATE + $ + ${SYMENGINE_INCLUDE_DIRS} +) + # Use target compile features to propagate features to consuming projects. target_compile_features(cellml PUBLIC cxx_std_17) diff --git a/src/analyser.cpp b/src/analyser.cpp index 0cb56d3c3..62c53d51e 100644 --- a/src/analyser.cpp +++ b/src/analyser.cpp @@ -22,7 +22,12 @@ limitations under the License. #include #include + +// clang-format off +#include "symenginebegin.h" #include +#include "symengineend.h" +// clang-format on #include "libcellml/analyserequation.h" #include "libcellml/analyserexternalvariable.h" From 192b717d2bca1ede64adae82c5dbb76a6c405b45 Mon Sep 17 00:00:00 2001 From: Alan Garny Date: Wed, 17 Dec 2025 16:57:36 +1300 Subject: [PATCH 44/44] CI: use SymEngine as a dependency. --- .github/workflows/ci.yml | 113 ++++++++++++++++++++++++++++----------- 1 file changed, 82 insertions(+), 31 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 25ffd3c36..b761b7de2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,17 +22,19 @@ jobs: - name: 'Windows static - C++' os: windows-latest python_bindings: OFF - additional_cmake_options: -DLibXml2_DIR="C:\libxml2\libxml2-2.9.10\CMake" -DZLIB_DIR="C:\zlib\lib\cmake\ZLIB-1.2.12" + additional_cmake_options: -DLibXml2_DIR="C:\libxml2\libxml2-2.9.10\CMake" -DSymEngine_DIR="C:\symengine\CMake" -DZLIB_DIR="C:\zlib\lib\cmake\ZLIB-1.2.12" - name: 'Windows shared - C++/Python' os: windows-latest python_bindings: ON - additional_cmake_options: -DLibXml2_DIR="C:\libxml2\libxml2-2.9.10\CMake" -DZLIB_DIR="C:\zlib\lib\cmake\ZLIB-1.2.12" + additional_cmake_options: -DLibXml2_DIR="C:\libxml2\libxml2-2.9.10\CMake" -DSymEngine_DIR="C:\symengine\CMake" -DZLIB_DIR="C:\zlib\lib\cmake\ZLIB-1.2.12" - name: 'Linux static - C++' os: ubuntu-latest python_bindings: OFF + additional_cmake_options: -DCMAKE_PREFIX_PATH="$HOME" - name: 'Linux shared - C++/Python' os: ubuntu-latest python_bindings: ON + additional_cmake_options: -DCMAKE_PREFIX_PATH="$HOME" - name: 'macOS static - C++ (Intel)' os: macos-15-intel python_bindings: OFF @@ -62,12 +64,44 @@ jobs: - name: Configure MSVC (Windows only) if: ${{ runner.os == 'Windows' }} uses: ilammy/msvc-dev-cmd@v1 + - name: Install GMP (Windows only) + if: ${{ runner.os == 'Windows' }} + run: | + cd C:\ + curl -L https://github.com/cellml/gha/releases/download/gha/gmp-Windows.tar.gz -o gmp.tar.gz -s + tar -xzf gmp.tar.gz + - name: Install GMP (Linux only) + if: ${{ runner.os == 'Linux' }} + run: | + cd $HOME + wget https://github.com/cellml/gha/releases/download/gha/gmp-Linux.tar.gz -O - | tar -xz + - name: Install GMP (macOS only) + if: ${{ runner.os == 'macOS' }} + run: | + cd $HOME + wget https://github.com/cellml/gha/releases/download/gha/gmp-macOS.tar.gz -O - | tar -xz - name: Install libxml2 (Windows only) if: ${{ runner.os == 'Windows' }} run: | cd C:\ curl -L https://github.com/cellml/gha/releases/download/gha/libxml2-Windows.tar.gz -o libxml2.tar.gz -s tar -xzf libxml2.tar.gz + - name: Install SymEngine (Windows only) + if: ${{ runner.os == 'Windows' }} + run: | + cd C:\ + curl -L https://github.com/cellml/gha/releases/download/gha/symengine-Windows.tar.gz -o symengine.tar.gz -s + tar -xzf symengine.tar.gz + - name: Install SymEngine (Linux only) + if: ${{ runner.os == 'Linux' }} + run: | + cd $HOME + wget https://github.com/cellml/gha/releases/download/gha/symengine-Linux.tar.gz -O - | tar -xz + - name: Install SymEngine (macOS only) + if: ${{ runner.os == 'macOS' }} + run: | + brew update + brew install symengine - name: Install zlib (Windows only) if: ${{ runner.os == 'Windows' }} run: | @@ -97,17 +131,25 @@ jobs: uses: cscouto/buildcache-action@v1 - name: Install Emscripten run: brew install --overwrite emscripten + - name: Install GMP + run: | + cd $HOME + wget https://github.com/cellml/gha/releases/download/gha/gmp-WASM.tar.gz -O - | tar -xz - name: Install libxml2 run: | cd $HOME wget https://github.com/cellml/gha/releases/download/gha/libxml2-WASM.tar.gz -O - | tar -xz + - name: Install SymEngine + run: | + cd $HOME + wget https://github.com/cellml/gha/releases/download/gha/symengine-WASM.tar.gz -O - | tar -xz - name: Install zlib run: | cd $HOME wget https://github.com/cellml/gha/releases/download/gha/zlib-WASM.tar.gz -O - | tar -xz - name: Configure libCellML run: | - emcmake cmake -G Ninja -S . -B build-wasm -DBUILD_TYPE=Release -DLIBXML2_INCLUDE_DIR=$HOME/libxml2/include/libxml2 -DLIBXML2_LIBRARY=$HOME/libxml2/lib/libxml2.a -DZLIB_INCLUDE_DIR=$HOME/zlib/include -DZLIB_LIBRARY=$HOME/zlib/lib/libz.a + emcmake cmake -G Ninja -S . -B build-wasm -DBUILD_TYPE=Release -DLIBXML2_DIR=$HOME/libxml2/lib/cmake -DSymEngine_DIR=$HOME/symengine/lib/cmake -DZLIB_DIR=$HOME/zlib/lib/cmake - name: Build libCellML run: cmake --build build-wasm - name: Unit testing @@ -126,15 +168,18 @@ jobs: sudo apt install clang-format - name: Install CMake and Ninja uses: lukka/get-cmake@latest - - name: Configure libCellML + - name: Install GMP run: | - mkdir build - cd build - cmake -G Ninja .. - - name: Code formatting + cd $HOME + wget https://github.com/cellml/gha/releases/download/gha/gmp-Linux.tar.gz -O - | tar -xz + - name: Install SymEngine run: | - cd build - ninja test_clang_format + cd $HOME + wget https://github.com/cellml/gha/releases/download/gha/symengine-Linux.tar.gz -O - | tar -xz + - name: Configure libCellML + run: cmake -G Ninja -S . -B build -DCMAKE_PREFIX_PATH="$HOME" + - name: Code formatting + run: cmake --build build --target test_clang_format coverage: name: Code coverage runs-on: macos-latest @@ -151,16 +196,16 @@ jobs: run: | brew install --overwrite llvm echo 'export PATH="/opt/homebrew/opt/llvm/bin:$PATH"' >> ~/.bash_profile - - name: Configure libCellML + - name: Install SymEngine run: | - mkdir build - cd build - cmake -G Ninja -DBINDINGS_PYTHON=OFF .. + brew update + brew install symengine + - name: Configure libCellML + run: cmake -G Ninja -S . -B build -DBINDINGS_PYTHON=OFF - name: Code coverage run: | - cd build - ninja llvm_coverage - if [ `ninja llvm_coverage | grep TOTAL | sed 's/ /\n/g' | grep "100.00%" | wc -l | sed 's/ //g'` -eq 4 ]; then exit 0; else exit 1; fi + cmake --build build --target llvm_coverage + if [ `cmake --build build --target llvm_coverage | grep TOTAL | sed 's/ /\n/g' | grep "100.00%" | wc -l | sed 's/ //g'` -eq 4 ]; then exit 0; else exit 1; fi memory_leaks: name: Memory leaks runs-on: ubuntu-latest @@ -177,15 +222,18 @@ jobs: run: | sudo apt update sudo apt install valgrind - - name: Configure libCellML + - name: Install GMP run: | - mkdir build - cd build - cmake -G Ninja -DBINDINGS_PYTHON=OFF .. - - name: Memory leaks + cd $HOME + wget https://github.com/cellml/gha/releases/download/gha/gmp-Linux.tar.gz -O - | tar -xz + - name: Install SymEngine run: | - cd build - ninja memcheck + cd $HOME + wget https://github.com/cellml/gha/releases/download/gha/symengine-Linux.tar.gz -O - | tar -xz + - name: Configure libCellML + run: cmake -G Ninja -S . -B build -DBINDINGS_PYTHON=OFF -DCMAKE_PREFIX_PATH="$HOME" + - name: Memory leaks + run: cmake --build build --target memcheck documentation: name: Documentation runs-on: ubuntu-latest @@ -205,12 +253,15 @@ jobs: - name: Install Sphinx run: | pip3 install sphinx - - name: Configure libCellML + - name: Install GMP run: | - mkdir build - cd build - cmake -G Ninja -DBINDINGS_PYTHON=OFF .. - - name: Documentation + cd $HOME + wget https://github.com/cellml/gha/releases/download/gha/gmp-Linux.tar.gz -O - | tar -xz + - name: Install SymEngine run: | - cd build - ninja docs + cd $HOME + wget https://github.com/cellml/gha/releases/download/gha/symengine-Linux.tar.gz -O - | tar -xz + - name: Configure libCellML + run: cmake -G Ninja -S . -B build -DBINDINGS_PYTHON=OFF -DCMAKE_PREFIX_PATH="$HOME" + - name: Documentation + run: cmake --build build --target docs