-
-
Notifications
You must be signed in to change notification settings - Fork 22
CMake: add support for SymEngine #1358
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Also changed the name of astMap to variableMap to better reflect what it's actually mapping to
Substituted with std::pair<bool, SymEngine::RCP<const SymEngine::Basic>> use instead
Mostly GPT generated test cases
Test case has been superseded by the newly added test suite to more robustly test for equation rearrangement
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.
Made it so that we also now consider mathml unary +/- operators
Can now handle unary plus, minus, unary minus, and CN types from AST (which means code now passes Analyser.rearrangeAdditiveEquations)
Had an error at equation 4 Also added brackets to equation comments to make things more clear
Now passes Analyser.rearrangeMultiplicativeEquations
Now covers a greater variety of cases
Symengine is not capable of rearranging trig functions into inverse trig functions (e.g. tan(x) = y cannot become x = atan(y))
Now has better Eq 1 test case and more consistent variable naming
Now supports - Decimal point values (i.e. doubles) - Constants (E, pi, and inf)
We need to ensure that there can only be one real solution for each
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
Now assumes left child will always be the non-null component
I can't believe I didn't get an error for this sooner!!!
- Added comments - Tried to give each test case a more 'unique' purpose
Also moved initialising constants to the bottom of the mathml block so we guarantee that our tested equation range always starts at 0
x + (-y) now becomes x - y -1 * y now becomes -y this also means x + (-1 * y) becomes x - y
|
Argh, wrong repo! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request adds support for the SymEngine symbolic mathematics library to enable automatic equation rearrangement in the libCellML analyser. The integration allows the analyser to solve for unknowns in algebraic equations that are not already in the form variable = expression, eliminating the need for numerical equation solvers in some cases.
Key changes:
- Integration of SymEngine library for symbolic equation manipulation and solving
- New equation rearrangement capabilities in the analyser
- Addition of comprehensive test cases for various equation types (trigonometric, polynomial, multiplicative, etc.)
Reviewed changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 14 comments.
Show a summary per file
| File | Description |
|---|---|
| cmake/environmentchecks.cmake | Adds SymEngine package detection and configuration |
| src/CMakeLists.txt | Links SymEngine libraries and includes directories for the cellml target |
| src/analyser_p.h | Adds SymEngine-related type definitions and forward declarations for new equation rearrangement methods |
| src/analyser.cpp | Implements equation conversion to/from SymEngine format, symbolic solving, and AST reconstruction |
| src/3rdparty/symengine/symenginebegin.h | Pragma directives to suppress SymEngine compiler warnings on Clang |
| src/3rdparty/symengine/symengineend.h | Pragma directives to restore compiler warning settings after SymEngine includes |
| tests/analyser/tests.cmake | Adds new SymEngine test file to the build configuration |
| tests/analyser/analysersymengine.cpp | Comprehensive test suite covering addition, multiplication, trigonometric functions, constants, and polynomial equations |
| tests/resources/analyser/symengine/*.cellml | Test model files for validating equation rearrangement functionality |
| tests/generator/generator.cpp | Removes DAE model test (likely now handled by SymEngine rearrangement) |
| tests/resources/generator/dae_cellml_1_1_model/* | Removes DAE model test resources |
| .github/workflows/ci.yml | Adds GMP and SymEngine installation steps for all platforms, updates to staging branch trigger, modernizes CMake invocations |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| auto currentAst = ast; | ||
|
|
||
| // All children (except the last) are guaranteed to be left children in the AST tree. | ||
| for (int i = 0; i + 1 < children.size(); ++i) { |
Copilot
AI
Dec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The loop variable i is declared as int but is being compared with children.size() which returns size_t (an unsigned type). This causes a signed/unsigned comparison warning. Use size_t for the loop variable instead to avoid potential issues with large collections and eliminate compiler warnings.
| if (SymEngine::eq(*constant, *SymEngine::E)) { | ||
| ast->setType(AnalyserEquationAst::Type::E); | ||
| } else if (SymEngine::eq(*constant, *SymEngine::pi)) { | ||
| ast->setType(AnalyserEquationAst::Type::PI); |
Copilot
AI
Dec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When SymEngine::SYMENGINE_CONSTANT is encountered but the constant is neither E nor pi, the AST type is never set, leaving it in an undefined state. This will lead to incorrect behavior. Consider setting a default type, returning an error, or handling all possible SymEngine constants explicitly.
| ast->setType(AnalyserEquationAst::Type::PI); | |
| ast->setType(AnalyserEquationAst::Type::PI); | |
| } else { | |
| ast->setType(AnalyserEquationAst::Type::CN); | |
| ast->setValue(seExpression->__str__()); |
| default: | ||
| break; | ||
| } |
Copilot
AI
Dec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The default case at line 368 doesn't set any AST type, which will leave the AST in an undefined state when an unhandled SymEngine expression type is encountered. This could lead to silent failures or incorrect behavior. Consider logging an error, returning nullptr, or throwing an exception to indicate that the expression type is not supported.
| <apply> | ||
| <apply><power/><ci>d</ci><cn cellml:units="dimensionless">3</cn></apply> | ||
| </apply> |
Copilot
AI
Dec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The XML structure is malformed. The power element at line 64 is missing its second child (the exponent). The apply element should contain both the base and the exponent as children. This will likely cause parsing errors. The structure should be: <apply><power/><ci>d</ci><cn cellml:units="dimensionless">3</cn></apply>.
| <apply> | |
| <apply><power/><ci>d</ci><cn cellml:units="dimensionless">3</cn></apply> | |
| </apply> | |
| <apply><power/><ci>d</ci><cn cellml:units="dimensionless">3</cn></apply> |
| AnalyserEquationAstPtr leftAst = ast->leftChild(); | ||
| AnalyserEquationAstPtr rightAst = ast->rightChild(); | ||
|
|
||
| // Recursively call getConvertedAst on left and right children. |
Copilot
AI
Dec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment at line 216 mentions "getConvertedAst" but the actual function being called is "symEngineEquation". Update the comment to reference the correct function name to avoid confusion.
| // Recursively call getConvertedAst on left and right children. | |
| // Recursively call symEngineEquation on left and right children. |
| for (const auto &variable : mAllVariables) { | ||
| SymEngine::RCP<const SymEngine::Symbol> symbol = SymEngine::symbol(variable->mVariable->name()); | ||
| symbolMap[variable->mVariable->name()] = symbol; | ||
| variableMap[symbol] = variable; |
Copilot
AI
Dec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable name variable shadows the function parameter with the same name. This makes the code confusing and error-prone. Consider renaming the loop variable to something like var or currentVariable to avoid the shadowing.
| for (const auto &variable : mAllVariables) { | |
| SymEngine::RCP<const SymEngine::Symbol> symbol = SymEngine::symbol(variable->mVariable->name()); | |
| symbolMap[variable->mVariable->name()] = symbol; | |
| variableMap[symbol] = variable; | |
| for (const auto ¤tVariable : mAllVariables) { | |
| SymEngine::RCP<const SymEngine::Symbol> symbol = SymEngine::symbol(currentVariable->mVariable->name()); | |
| symbolMap[currentVariable->mVariable->name()] = symbol; | |
| variableMap[symbol] = currentVariable; |
| <apply> | ||
| <apply><power/><ci>d</ci><cn cellml:units="dimensionless">3</cn></apply> | ||
| </apply> |
Copilot
AI
Dec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's an extra nested apply tag that doesn't have an operator. Line 63 shows <apply> followed immediately by another <apply><power/>... on line 64, but line 63's apply has no operator specified. This creates malformed MathML. Remove the extra apply tag on line 63.
| <apply> | |
| <apply><power/><ci>d</ci><cn cellml:units="dimensionless">3</cn></apply> | |
| </apply> | |
| <apply><power/><ci>d</ci><cn cellml:units="dimensionless">3</cn></apply> |
| } | ||
|
|
||
| // The final child is created and placed where appropriate. | ||
| if (children.size() != 0) { |
Copilot
AI
Dec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comparison children.size() != 0 should use the more idiomatic !children.empty() for checking if a container is empty. This is the preferred C++ style and may have better performance characteristics for some containers.
| if (children.size() != 0) { | |
| if (!children.empty()) { |
| @@ -0,0 +1,22 @@ | |||
| /* | |||
| Copyright libOpenCOR contributors. | |||
Copilot
AI
Dec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The copyright notice attributes the code to "libOpenCOR contributors" but this is a libCellML repository. The copyright should be "libCellML Contributors" to be consistent with the rest of the codebase, as seen in other files like tests/analyser/analysersymengine.cpp.
| Copyright libOpenCOR contributors. | |
| Copyright libCellML Contributors. |
| @@ -0,0 +1,19 @@ | |||
| /* | |||
| Copyright libOpenCOR contributors. | |||
Copilot
AI
Dec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The copyright notice attributes the code to "libOpenCOR contributors" but this is a libCellML repository. The copyright should be "libCellML Contributors" to be consistent with the rest of the codebase.
No description provided.