diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b17a4a1cac..b761b7de28 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: @@ -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 diff --git a/cmake/environmentchecks.cmake b/cmake/environmentchecks.cmake index 260b5c0ae4..3a960c6d10 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 0000000000..84290dadf2 --- /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 0000000000..3d9678e222 --- /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 466fb29304..afe5aa1b58 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -203,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 e79550d274..62c53d51ef 100644 --- a/src/analyser.cpp +++ b/src/analyser.cpp @@ -23,6 +23,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" #include "libcellml/generatorprofile.h" @@ -198,6 +204,262 @@ bool AnalyserInternalEquation::variableOnLhsOrRhs(const AnalyserInternalVariable || variableOnRhs(variable); } +SymEngineEquationResult AnalyserInternalEquation::symEngineEquation(const AnalyserEquationAstPtr &ast, const SymEngineSymbolMap &symbolMap) +{ + if (ast == nullptr) { + return {true, SymEngine::null}; + } + + AnalyserEquationAstPtr leftAst = ast->leftChild(); + AnalyserEquationAstPtr rightAst = ast->rightChild(); + + // Recursively call getConvertedAst on left and right children. + 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 {true, Eq(left, right)}; + case AnalyserEquationAst::Type::PLUS: + // Handle the case where we have a unary plus. + 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) { + return {true, SymEngine::mul(SymEngine::integer(-1), left)}; + } + 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::POWER: + return {true, SymEngine::pow(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::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()) { + return {false, SymEngine::null}; + } + return {true, symbolMap.at(ast->variable()->name())}; + 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) +{ + AnalyserEquationAstPtr ast = AnalyserEquationAst::create(); + ast->setParent(parentAst); + auto children = seExpression->get_args(); + + 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: { + 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: { + 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); + ast->setVariable(variableMap.at(symbolExpr)->mVariable); + break; + } + 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; + } + + 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) { + auto childAst = parseSymEngineExpression(children[i], 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; + } + } + + // The final child is created and placed where appropriate. + if (children.size() != 0) { + auto childAst = parseSymEngineExpression(children.back(), currentAst, variableMap); + + children.size() == 1 ? currentAst->setLeftChild(childAst) : + currentAst->setRightChild(childAst); + + // 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; +} + +AnalyserEquationAstPtr AnalyserInternalEquation::rearrangeFor(const AnalyserInternalVariablePtr &variable) +{ + SymEngineSymbolMap symbolMap; + SymEngineVariableMap variableMap; + + for (const auto &variable : mAllVariables) { + SymEngine::RCP symbol = SymEngine::symbol(variable->mVariable->name()); + symbolMap[variable->mVariable->name()] = symbol; + variableMap[symbol] = variable; + } + + auto [success, seEquation] = symEngineEquation(mAst, symbolMap); + if (!success) { + return nullptr; + } + + SymEngine::RCP solutionSet = solve(seEquation, symbolMap[variable->mVariable->name()]); + SymEngine::vec_basic solutions = solutionSet->get_args(); + + // 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; + } + SymEngine::RCP answer = solutions.front(); + + // Rebuild the AST from the rearranged expression. + AnalyserEquationAstPtr ast = AnalyserEquationAst::create(); + AnalyserEquationAstPtr isolatedVariableAst = AnalyserEquationAst::create(); + AnalyserEquationAstPtr rearrangedEquationAst = parseSymEngineExpression(answer, nullptr, variableMap); + + 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 +540,15 @@ 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) { + // TODO Update variables and/or equation type when necessary. + mAst = newAst; + } + } + if (((unknownVariableLeft != nullptr) && (checkNlaSystems || variableOnLhsOrRhs(unknownVariableLeft))) || !initialisedVariables.empty()) { diff --git a/src/analyser_p.h b/src/analyser_p.h index f888861de4..81947bab96 100644 --- a/src/analyser_p.h +++ b/src/analyser_p.h @@ -22,6 +22,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; @@ -39,6 +45,10 @@ using AnalyserEquationPtrs = std::vector; using AnalyserVariablePtrs = std::vector; using AnalyserExternalVariablePtrs = std::vector; +using SymEngineVariableMap = std::map, AnalyserInternalVariablePtr, SymEngine::RCPBasicKeyLess>; +using SymEngineSymbolMap = std::map>; +using SymEngineEquationResult = std::tuple>; + struct AnalyserInternalVariable { enum struct Type @@ -128,6 +138,11 @@ struct AnalyserInternalEquation bool variableOnRhs(const AnalyserInternalVariablePtr &variable); 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); + bool check(const AnalyserModelPtr &analyserModel, bool checkNlaSystems); }; diff --git a/tests/analyser/analysersymengine.cpp b/tests/analyser/analysersymengine.cpp new file mode 100644 index 0000000000..5c92a41936 --- /dev/null +++ b/tests/analyser/analysersymengine.cpp @@ -0,0 +1,115 @@ +/* +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/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()); + + 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) +{ + auto parser = libcellml::Parser::create(); + auto model = parser->parseModel(fileContents("analyser/symengine/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()); + + 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 = 30.0*x*pow(z, -1.0)", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(2)->ast())); +} + +TEST(Analyser, rearrangeTrigonometricEquations) +{ + auto parser = libcellml::Parser::create(); + auto model = parser->parseModel(fileContents("analyser/symengine/trigonometric.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()); + + 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-y)", libcellml::Generator::equationCode(analyser->analyserModel()->analyserEquation(2)->ast())); +} + +TEST(Analyser, rearrangeEquationsWithConstants) +{ + auto parser = libcellml::Parser::create(); + auto model = parser->parseModel(fileContents("analyser/symengine/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()); + + 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 = -(-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) +{ + auto parser = libcellml::Parser::create(); + auto model = parser->parseModel(fileContents("analyser/symengine/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()); + + 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/analyser/tests.cmake b/tests/analyser/tests.cmake index ab22b635e5..6e160c1b49 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 ) diff --git a/tests/generator/generator.cpp b/tests/generator/generator.cpp index 411080c523..586a1bed7a 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/analyser/symengine/addition.cellml b/tests/resources/analyser/symengine/addition.cellml new file mode 100644 index 0000000000..dceee05389 --- /dev/null +++ b/tests/resources/analyser/symengine/addition.cellml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + awx + 10 + + + + + + + by2 + 1 + + + + + + + cx1 + z + + + + + + + dw + y + + + + w2 + x3 + y4 + z5 + + + + diff --git a/tests/resources/analyser/symengine/constants.cellml b/tests/resources/analyser/symengine/constants.cellml new file mode 100644 index 0000000000..cd73cd7c2e --- /dev/null +++ b/tests/resources/analyser/symengine/constants.cellml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + ax + 8.65 + + + + + + + bw + 45 + + + + + + + cy + + + + + + + + dz + + + + + + + + ew + + + + + w3 + x4 + y5 + z2 + + + + diff --git a/tests/resources/analyser/symengine/multiplication.cellml b/tests/resources/analyser/symengine/multiplication.cellml new file mode 100644 index 0000000000..e4fdc5233e --- /dev/null +++ b/tests/resources/analyser/symengine/multiplication.cellml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + aw + 4 + + + + + + + + + b + y + + 18 + + + + + + + + + cz + x3 + + 10 + + + + w2 + x3 + y4 + z5 + + + + diff --git a/tests/resources/analyser/symengine/polynomials.cellml b/tests/resources/analyser/symengine/polynomials.cellml new file mode 100644 index 0000000000..da4afa4f48 --- /dev/null +++ b/tests/resources/analyser/symengine/polynomials.cellml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + a2 + 3 + + 125 + + + + + + + + + + b2 + 4b + 4 + + 0 + + + + + + + + + + c3 + 3c + + 36 + + + + + + + + + d3 + + w + + + + w3 + + + + diff --git a/tests/resources/analyser/symengine/trigonometric.cellml b/tests/resources/analyser/symengine/trigonometric.cellml new file mode 100644 index 0000000000..ba4f7316bf --- /dev/null +++ b/tests/resources/analyser/symengine/trigonometric.cellml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + 2aw + 1 + + + + + + + 4xb + 0 + + + + + + + c3y + 2 + + + + w0.5 + x1.0 + y1.5 + + + + 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 a5ebbb5eb3..0000000000 --- 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 3cf3994279..0000000000 --- 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 3350366aef..0000000000 --- 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 e847325529..0000000000 --- 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)