From b5177d9ccaa1cc7bf48d8467b1f7b8a3bd81818f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20Fr=C3=B6hlich?= Date: Wed, 23 Nov 2022 19:48:50 +0100 Subject: [PATCH] Release 0.14.0 (#1898) * Fix pandas groupby deprecation warning (#1873) Fixes #1872 * Update reference list (#1874) * Code printing: Use SymPy C99 math optimizations (#1377) for SBML import. Does not currently work with PySB import. Also fixes some argument type issues. Co-authored-by: Daniel Weindl * PEtab import: Add option for treating fixed parameters as constants (#1877) Makes the changes from #1810 optional, as sometimes users prefer more flexible models. The default remains to treat non-estimated parameters in PEtab as constants in AMICI. Also adds a basic check for loading existing models during PEtab, to see if they are compatible to what was supposed to be imported. Only checks non-constant parameter IDs so far. * Make sympy code optimizations optional (#1878) Turns out that applying sympy optimizations as introduced in https://github.com/AMICI-dev/AMICI/pull/1377 is potentially very costly. Therefore, they are disabled by default. They can be enabled by setting `AmiciCxxCodePrinter.optimizations` as shown in the test case. * Update reference list (#1884) * GHA: update actions (#1885) Fixes some GHA warnings. * Add macos hdf5 header/library paths (#1894) * Doxygen 1.9.5 on RTD (#1889) Older binaries are no longer available. * Code-gen: Fix missing `return` in non-void-returning functions (#1892) ... and set `-Werror` for compiling models in debug mode. * Add operator== for ExpData (#1881) Adds `operator==` for `amici::ExpData` and fixes issues for other `operator==` in case of NaNs in arrays. Closes #1880 * Fix SetuptoolsDeprecationWarning (#1893) Setuptools meanwhile requires listing data directories as subpackages. Fixes #1888. * Dockerfile: Update base image to Ubuntu 22.04 (#1896) * GHA: Pin python version for osx tests (#1891) Fixes #1890. Also: * use homebrew boost version * fix CMake logic for selecting a specific Python version. * Fix `petab_import.import_model(..., compile=False)` failure (#1897) Fixes a bug where amici would fail importing an extension that was never built. * bump version, update changelog * fix changelog * -Wno-error=deprecated-declarations Co-authored-by: Daniel Weindl Co-authored-by: Lorenzo Contento Co-authored-by: Daniel Weindl Co-authored-by: Doresic <85789271+Doresic@users.noreply.github.com> --- .github/workflows/deploy_branch.yml | 4 +- .github/workflows/deploy_protected.yml | 4 +- .github/workflows/deploy_release.yml | 4 +- .../test_benchmark_collection_models.yml | 4 +- .github/workflows/test_doc.yml | 8 +- .github/workflows/test_install.yml | 8 +- .github/workflows/test_matlab.yml | 2 +- .github/workflows/test_performance.yml | 4 +- .github/workflows/test_petab_test_suite.yml | 4 +- .github/workflows/test_pypi.yml | 2 +- .github/workflows/test_python_cplusplus.yml | 17 +++-- .github/workflows/test_python_ver_matrix.yml | 4 +- .../test_sbml_semantic_test_suite.yml | 4 +- .github/workflows/test_valgrind.yml | 8 +- .github/workflows/test_windows.yml | 4 +- CHANGELOG.md | 54 +++++++++++++- docker/Dockerfile | 2 +- documentation/amici_refs.bib | 23 ++++++ documentation/conf.py | 2 +- documentation/references.md | 11 ++- include/amici/edata.h | 21 ++++++ include/amici/misc.h | 22 ++++++ include/amici/model_state.h | 10 +++ models/model_calvetti/model_calvetti.h | 4 +- models/model_calvetti/swig/CMakeLists.txt | 5 +- models/model_dirac/model_dirac.h | 4 +- models/model_dirac/swig/CMakeLists.txt | 5 +- models/model_events/model_events.h | 4 +- models/model_events/swig/CMakeLists.txt | 5 +- .../model_jakstat_adjoint.h | 4 +- .../model_jakstat_adjoint/swig/CMakeLists.txt | 5 +- .../model_jakstat_adjoint_o2.h | 4 +- .../swig/CMakeLists.txt | 5 +- .../model_nested_events/model_nested_events.h | 4 +- .../model_nested_events/swig/CMakeLists.txt | 5 +- models/model_neuron/model_neuron.h | 4 +- models/model_neuron/swig/CMakeLists.txt | 5 +- models/model_neuron_o2/model_neuron_o2.h | 4 +- models/model_neuron_o2/swig/CMakeLists.txt | 5 +- models/model_robertson/model_robertson.h | 4 +- models/model_robertson/swig/CMakeLists.txt | 5 +- models/model_steadystate/model_steadystate.h | 4 +- models/model_steadystate/swig/CMakeLists.txt | 5 +- python/CMakeLists.txt | 9 +-- python/sdist/amici/__init__.py | 11 ++- python/sdist/amici/cxxcodeprinter.py | 44 ++++++++++- python/sdist/amici/ode_export.py | 29 ++++---- python/sdist/amici/petab_import.py | 73 ++++++++++++++++++- python/sdist/amici/petab_objective.py | 10 ++- python/sdist/amici/pysb_import.py | 4 + python/sdist/amici/setup.template.py | 5 +- python/sdist/amici/setuptools.py | 9 ++- python/sdist/setup.cfg | 2 +- python/tests/test_cxxcodeprinter.py | 14 ++++ python/tests/test_misc.py | 14 ++++ python/tests/test_ode_export.py | 7 +- python/tests/test_petab_import.py | 9 ++- src/model.cpp | 8 +- src/simulation_parameters.cpp | 26 ++++--- swig/CMakeLists_model.cmake | 7 +- tests/cpp/unittests/testExpData.cpp | 11 +++ tests/cpp/unittests/testMisc.cpp | 10 +++ version.txt | 2 +- 63 files changed, 484 insertions(+), 136 deletions(-) create mode 100644 python/tests/test_cxxcodeprinter.py diff --git a/.github/workflows/deploy_branch.yml b/.github/workflows/deploy_branch.yml index 3e706300f3..8da2f1a842 100644 --- a/.github/workflows/deploy_branch.yml +++ b/.github/workflows/deploy_branch.yml @@ -13,11 +13,11 @@ jobs: steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@master + - uses: actions/checkout@v3 with: fetch-depth: 20 diff --git a/.github/workflows/deploy_protected.yml b/.github/workflows/deploy_protected.yml index ac8b858a16..efbd4f79be 100644 --- a/.github/workflows/deploy_protected.yml +++ b/.github/workflows/deploy_protected.yml @@ -22,10 +22,10 @@ jobs: steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@master + - uses: actions/checkout@v3 - run: git archive -o docker/amici.tar.gz --format=tar.gz HEAD - name: Publish to Registry uses: elgohr/Publish-Docker-Github-Action@v4 diff --git a/.github/workflows/deploy_release.yml b/.github/workflows/deploy_release.yml index 77fe30a9be..258f8f2ace 100644 --- a/.github/workflows/deploy_release.yml +++ b/.github/workflows/deploy_release.yml @@ -16,11 +16,11 @@ jobs: steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@master + - uses: actions/checkout@v3 with: fetch-depth: 20 diff --git a/.github/workflows/test_benchmark_collection_models.yml b/.github/workflows/test_benchmark_collection_models.yml index c382309e81..93b9c2fdb8 100644 --- a/.github/workflows/test_benchmark_collection_models.yml +++ b/.github/workflows/test_benchmark_collection_models.yml @@ -29,11 +29,11 @@ jobs: steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@master + - uses: actions/checkout@v3 with: fetch-depth: 20 diff --git a/.github/workflows/test_doc.yml b/.github/workflows/test_doc.yml index 9742de92eb..e9f30ca423 100644 --- a/.github/workflows/test_doc.yml +++ b/.github/workflows/test_doc.yml @@ -24,11 +24,11 @@ jobs: steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@master + - uses: actions/checkout@v3 - run: git fetch --prune --unshallow - name: apt @@ -59,11 +59,11 @@ jobs: steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@master + - uses: actions/checkout@v3 - run: git fetch --prune --unshallow - run: echo "AMICI_DIR=$(pwd)" >> $GITHUB_ENV diff --git a/.github/workflows/test_install.yml b/.github/workflows/test_install.yml index dd75ff576e..871b972a70 100644 --- a/.github/workflows/test_install.yml +++ b/.github/workflows/test_install.yml @@ -13,11 +13,11 @@ jobs: steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@master + - uses: actions/checkout@v3 - run: git fetch --prune --unshallow - run: echo "AMICI_DIR=$(pwd)" >> $GITHUB_ENV @@ -61,11 +61,11 @@ jobs: steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@master + - uses: actions/checkout@v3 - run: git fetch --prune --unshallow - run: echo "AMICI_DIR=$(pwd)" >> $GITHUB_ENV diff --git a/.github/workflows/test_matlab.yml b/.github/workflows/test_matlab.yml index 90757643d5..7e2d380d50 100644 --- a/.github/workflows/test_matlab.yml +++ b/.github/workflows/test_matlab.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@master + - uses: actions/checkout@v3 - run: git fetch --prune --unshallow - run: echo "AMICI_DIR=$(pwd)" >> $GITHUB_ENV diff --git a/.github/workflows/test_performance.yml b/.github/workflows/test_performance.yml index 5885df4813..2359f8bb80 100644 --- a/.github/workflows/test_performance.yml +++ b/.github/workflows/test_performance.yml @@ -27,11 +27,11 @@ jobs: steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@master + - uses: actions/checkout@v3 with: fetch-depth: 20 diff --git a/.github/workflows/test_petab_test_suite.yml b/.github/workflows/test_petab_test_suite.yml index 3c672bb58f..c83db38d91 100644 --- a/.github/workflows/test_petab_test_suite.yml +++ b/.github/workflows/test_petab_test_suite.yml @@ -27,11 +27,11 @@ jobs: steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@master + - uses: actions/checkout@v3 with: fetch-depth: 20 diff --git a/.github/workflows/test_pypi.yml b/.github/workflows/test_pypi.yml index be090ec029..1ca529fead 100644 --- a/.github/workflows/test_pypi.yml +++ b/.github/workflows/test_pypi.yml @@ -36,7 +36,7 @@ jobs: fi - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} diff --git a/.github/workflows/test_python_cplusplus.yml b/.github/workflows/test_python_cplusplus.yml index 6a857133f2..cf3528e182 100644 --- a/.github/workflows/test_python_cplusplus.yml +++ b/.github/workflows/test_python_cplusplus.yml @@ -18,11 +18,11 @@ jobs: steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@master + - uses: actions/checkout@v3 - run: git fetch --prune --unshallow - run: echo "AMICI_DIR=$(pwd)" >> $GITHUB_ENV @@ -143,11 +143,18 @@ jobs: runs-on: macos-latest steps: - - uses: actions/checkout@master + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: 3.8 + + - uses: actions/checkout@v3 - run: git fetch --prune --unshallow - run: echo "AMICI_DIR=$(pwd)" >> $GITHUB_ENV - run: echo "BNGPATH=${AMICI_DIR}/ThirdParty/BioNetGen-2.7.0" >> $GITHUB_ENV + # Ensure CMake is using the python version that we will use for the python tests later on + - run: echo "PYTHON_EXECUTABLE=${Python3_ROOT_DIR}/bin/python3" >> $GITHUB_ENV # install amici dependencies - name: homebrew @@ -155,8 +162,8 @@ jobs: brew install hdf5 swig gcc cppcheck libomp boost \ && brew ls -v boost \ && brew ls -v libomp \ - && echo LDFLAGS="-L/usr/local/lib/ -L/usr/local/Cellar/boost/1.78.0_1/lib/" >> $GITHUB_ENV \ - && echo CPPFLAGS="-I/usr/local/Cellar/boost/1.78.0_1/include/" >> $GITHUB_ENV + && echo LDFLAGS="-L/usr/local/lib/ -L/usr/local/Cellar/boost/1.80.0/lib/" >> $GITHUB_ENV \ + && echo CPPFLAGS="-I/usr/local/Cellar/boost/1.80.0/include/" >> $GITHUB_ENV - name: Build AMICI run: | diff --git a/.github/workflows/test_python_ver_matrix.yml b/.github/workflows/test_python_ver_matrix.yml index 3a5d1394a5..ed00fc407e 100644 --- a/.github/workflows/test_python_ver_matrix.yml +++ b/.github/workflows/test_python_ver_matrix.yml @@ -32,11 +32,11 @@ jobs: - run: echo "BNGPATH=${AMICI_DIR}/ThirdParty/BioNetGen-2.7.0" >> $GITHUB_ENV - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@master + - uses: actions/checkout@v3 with: fetch-depth: 20 diff --git a/.github/workflows/test_sbml_semantic_test_suite.yml b/.github/workflows/test_sbml_semantic_test_suite.yml index 52f0b1a348..27d487bbf1 100644 --- a/.github/workflows/test_sbml_semantic_test_suite.yml +++ b/.github/workflows/test_sbml_semantic_test_suite.yml @@ -32,11 +32,11 @@ jobs: steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@master + - uses: actions/checkout@v3 with: fetch-depth: 1 - name: apt diff --git a/.github/workflows/test_valgrind.yml b/.github/workflows/test_valgrind.yml index 4f560175af..1138e663c0 100644 --- a/.github/workflows/test_valgrind.yml +++ b/.github/workflows/test_valgrind.yml @@ -27,11 +27,11 @@ jobs: steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@master + - uses: actions/checkout@v3 - run: git fetch --prune --unshallow # install amici dependencies @@ -71,11 +71,11 @@ jobs: steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@master + - uses: actions/checkout@v3 - run: git fetch --prune --unshallow # install amici dependencies diff --git a/.github/workflows/test_windows.yml b/.github/workflows/test_windows.yml index 190af917a1..d89805ec74 100644 --- a/.github/workflows/test_windows.yml +++ b/.github/workflows/test_windows.yml @@ -23,11 +23,11 @@ jobs: steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@master + - uses: actions/checkout@v3 - run: git fetch --prune --unshallow - shell: bash diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a5d1834cf..92ab14f78c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,54 @@ ## v0.X Series +### v0.14.0 (2022-11-23) + +#### Features: + +* Added optional functionality to apply C99 math optimization to generated C++ code + by @dweindl and @lcontento in https://github.com/AMICI-dev/AMICI/pull/1377, https://github.com/AMICI-dev/AMICI/pull/1878 + +* Added option to treat fixed parameters as constants in PEtab import + + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1877 + +* Added equality operator for ExpData + + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1881 + +* Updated base image for Dockerfile to Ubuntu 22.04/Python 3.10 + + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1896 + + +#### Fixes: + +* Fixed deprecation warnings + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1873, https://github.com/AMICI-dev/AMICI/pull/1893 + +* Fixes/updates to GitHub actions + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1885, https://github.com/AMICI-dev/AMICI/pull/1893, https://github.com/AMICI-dev/AMICI/pull/1889, https://github.com/AMICI-dev/AMICI/pull/1891 + +* Added hdf5 search directories for arm64 architecture (M1/M2 macs) + + by @Doresic in https://github.com/AMICI-dev/AMICI/pull/1894 + +* Fixed missing return in generated non-void functions + + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1892 + +* Fixed import failure for pre-compiled models + + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1897 + +#### Documentation: + +* Update reference list + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1874, https://github.com/AMICI-dev/AMICI/pull/1884 + +**Full Changelog**: +https://github.com/AMICI-dev/AMICI/compare/v0.13.0...v0.14.0 + ### v0.13.0 (2022-10-04) * Fixed extraction of common subexpressions @@ -314,7 +362,7 @@ Fixes: @PaulJonasJost in https://github.com/AMICI-dev/AMICI/pull/1620 * Fixed wrong array size in warnings by @dweindl in https://github.com/AMICI-dev/AMICI/pull/1624 - + NOTE: AMICI 0.11.23 requires numpy<1.22.0 **Full Changelog**: @@ -918,7 +966,7 @@ Misc: * Simplify/fix AMICI installation * If available use environment modules to detect dependencies - + * Add SWIG installation script * Update list of publication @@ -949,7 +997,7 @@ Detaills: * Improve finding swig executable and allow user override via SWIG environment variable * Provide installation hints if no SWIG found (Closes #724) * Allow overriding cmake executable with environment variables in build scripts (Closes #738) - + ### v0.10.9 (2019-07-24) diff --git a/docker/Dockerfile b/docker/Dockerfile index a17a87eee9..bff7d07172 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:20.04 +FROM ubuntu:22.04 RUN apt update \ && apt-get install -y \ diff --git a/documentation/amici_refs.bib b/documentation/amici_refs.bib index 5037497638..1c8921ea06 100644 --- a/documentation/amici_refs.bib +++ b/documentation/amici_refs.bib @@ -1052,6 +1052,29 @@ @Article{LakrisenkoSta2022 url = {https://www.biorxiv.org/content/early/2022/08/11/2022.08.08.503176}, } +@Article{VillaverdeRai2022, + author = {Villaverde, Alejandro F. and Raimúndez, Elba and Hasenauer, Jan and Banga, Julio R.}, + journal = {IEEE/ACM Transactions on Computational Biology and Bioinformatics}, + title = {Assessment of Prediction Uncertainty Quantification Methods in Systems Biology}, + year = {2022}, + pages = {1-12}, + doi = {10.1109/TCBB.2022.3213914}, +} + +@Article{MishraWan2023, + author = {Shekhar Mishra and Ziyu Wang and Michael J. Volk and Huimin Zhao}, + journal = {Metabolic Engineering}, + title = {Design and application of a kinetic model of lipid metabolism in Saccharomyces cerevisiae}, + year = {2023}, + issn = {1096-7176}, + pages = {12-18}, + volume = {75}, + abstract = {Lipid biosynthesis plays a vital role in living cells and has been increasingly engineered to overproduce various lipid-based chemicals. However, owing to the tightly constrained and interconnected nature of lipid biosynthesis, both understanding and engineering of lipid metabolism remain challenging, even with the help of mathematical models. Here we report the development of a kinetic metabolic model of lipid metabolism in Saccharomyces cerevisiae that integrates fatty acid biosynthesis, glycerophospholipid metabolism, sphingolipid metabolism, storage lipids, lumped sterol synthesis, and the synthesis and transport of relevant target-chemicals, such as fatty acids and fatty alcohols. The model was trained on lipidomic data of a reference S. cerevisiae strain, single knockout mutants, and lipid overproduction strains reported in literature. The model was used to design mutants for fatty alcohol overproduction and the lipidomic analysis of the resultant mutant strains coupled with model-guided hypothesis led to discovery of a futile cycle in the triacylglycerol biosynthesis pathway. In addition, the model was used to explain successful and unsuccessful mutant designs in metabolic engineering literature. Thus, this kinetic model of lipid metabolism can not only enable the discovery of new phenomenon in lipid metabolism but also the engineering of mutant strains for overproduction of lipids.}, + doi = {https://doi.org/10.1016/j.ymben.2022.11.003}, + keywords = {Lipid metabolism, Kinetic model, Free fatty acid, Fatty alcohol}, + url = {https://www.sciencedirect.com/science/article/pii/S1096717622001380}, +} + @Comment{jabref-meta: databaseType:bibtex;} @Comment{jabref-meta: grouping: diff --git a/documentation/conf.py b/documentation/conf.py index a194f68f79..92327c6043 100644 --- a/documentation/conf.py +++ b/documentation/conf.py @@ -123,7 +123,7 @@ def install_amici_deps_rtd(): def install_doxygen(): """Get a more recent doxygen""" - version = '1.9.3' + version = '1.9.5' doxygen_exe = os.path.join(amici_dir, 'ThirdParty', f'doxygen-{version}', 'bin', 'doxygen') # to create a symlink to doxygen in a location that is already on PATH diff --git a/documentation/references.md b/documentation/references.md index 99d5d8ccc3..2c28ebc355 100644 --- a/documentation/references.md +++ b/documentation/references.md @@ -1,9 +1,15 @@ # References -List of publications using AMICI. Total number is 68. +List of publications using AMICI. Total number is 70. If you applied AMICI in your work and your publication is missing, please let us know via a new Github issue. +

2023

+
+
+

Mishra, Shekhar, Ziyu Wang, Michael J. Volk, and Huimin Zhao. 2023. “Design and Application of a Kinetic Model of Lipid Metabolism in Saccharomyces Cerevisiae.” Metabolic Engineering 75: 12–18. https://doi.org/https://doi.org/10.1016/j.ymben.2022.11.003.

+
+

2022

@@ -24,6 +30,9 @@ If you applied AMICI in your work and your publication is missing, please let us

Sundqvist, Nicolas, Sebastian Sten, Maria Engström, and Gunnar Cedersund. 2022. “Mechanistic Model for Human Brain Metabolism and the Neurovascular Coupling.” bioRxiv. https://doi.org/10.1101/2022.02.15.480629.

+
+

Villaverde, Alejandro F., Elba Raimúndez, Jan Hasenauer, and Julio R. Banga. 2022. “Assessment of Prediction Uncertainty Quantification Methods in Systems Biology.” IEEE/ACM Transactions on Computational Biology and Bioinformatics, 1–12. https://doi.org/10.1109/TCBB.2022.3213914.

+

2021

diff --git a/include/amici/edata.h b/include/amici/edata.h index 406e5fdf66..b15e34fc10 100644 --- a/include/amici/edata.h +++ b/include/amici/edata.h @@ -115,6 +115,8 @@ class ExpData : public SimulationParameters { ~ExpData() = default; + friend inline bool operator==(const ExpData& lhs, const ExpData& rhs); + /** * @brief number of observables of the non-augmented model * @@ -455,6 +457,25 @@ class ExpData : public SimulationParameters { std::vector observed_events_std_dev_; }; + +inline bool operator==(const ExpData& lhs, const ExpData& rhs) { + return *dynamic_cast< const SimulationParameters* >(&lhs) + == *dynamic_cast< const SimulationParameters* >(&rhs) + && lhs.id == rhs.id + && lhs.nytrue_ == rhs.nytrue_ + && lhs.nztrue_ == rhs.nztrue_ + && lhs.nmaxevent_ == rhs.nmaxevent_ + && is_equal(lhs.observed_data_, + rhs.observed_data_) + && is_equal(lhs.observed_data_std_dev_, + rhs.observed_data_std_dev_) + && is_equal(lhs.observed_events_, + rhs.observed_events_) + && is_equal(lhs.observed_events_std_dev_, + rhs.observed_events_std_dev_); +}; + + /** * @brief checks input vector of sigmas for not strictly positive values * diff --git a/include/amici/misc.h b/include/amici/misc.h index 1144ea85a0..fe9fb5b8e2 100644 --- a/include/amici/misc.h +++ b/include/amici/misc.h @@ -221,6 +221,28 @@ class ContextManager{ auto unravel_index(size_t flat_idx, size_t num_cols) -> std::pair; +/** + * @brief Check if two spans are equal, treating NaNs in the same position as + * equal. + * @param a + * @param b + * @return Whether the contents of the two spans are equal. + */ +template +bool is_equal(T const& a, T const& b) { + if(a.size() != b.size()) + return false; + + auto a_data = a.data(); + auto b_data = b.data(); + for(typename T::size_type i = 0; i < a.size(); ++i) { + if(a_data[i] != b_data[i] + && !(std::isnan(a_data[i]) && std::isnan(b_data[i]))) + return false; + } + return true; +} + } // namespace amici #endif // AMICI_MISC_H diff --git a/include/amici/model_state.h b/include/amici/model_state.h index 492f01f1a0..e7695a70be 100644 --- a/include/amici/model_state.h +++ b/include/amici/model_state.h @@ -4,6 +4,7 @@ #include "amici/defines.h" #include "amici/sundials_matrix_wrapper.h" #include "amici/model_dimensions.h" +#include "amici/misc.h" #include @@ -45,6 +46,15 @@ struct ModelState { std::vector plist; }; +inline bool operator==(const ModelState &a, const ModelState &b) { + return is_equal(a.h, b.h) + && is_equal(a.total_cl, b.total_cl) + && is_equal(a.stotal_cl, b.stotal_cl) + && is_equal(a.unscaledParameters, b.unscaledParameters) + && is_equal(a.fixedParameters, b.fixedParameters) + && a.plist == b.plist; +} + /** * @brief Storage for `amici::Model` quantities computed based on diff --git a/models/model_calvetti/model_calvetti.h b/models/model_calvetti/model_calvetti.h index 8c4fb709f9..4728837cab 100644 --- a/models/model_calvetti/model_calvetti.h +++ b/models/model_calvetti/model_calvetti.h @@ -1,6 +1,6 @@ #ifndef _amici_model_calvetti_h #define _amici_model_calvetti_h -/* Generated by amiwrap (R2017b) fee0d5a069aba864a92ded7da50ae68ea7ea43cc */ +/* Generated by amiwrap (R2017b) b04ab2326fb34f3721a15806d9783ff22b6a015a */ #include #include #include "amici/defines.h" @@ -70,7 +70,7 @@ class Model_model_calvetti : public amici::Model_DAE { amici::Model* clone() const override { return new Model_model_calvetti(*this); }; - std::string getAmiciCommit() const override { return "fee0d5a069aba864a92ded7da50ae68ea7ea43cc"; }; + std::string getAmiciCommit() const override { return "b04ab2326fb34f3721a15806d9783ff22b6a015a"; }; void fJSparse(SUNMatrixContent_Sparse JSparse, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype cj, const realtype *dx, const realtype *w, const realtype *dwdx) override { JSparse_model_calvetti(JSparse, t, x, p, k, h, cj, dx, w, dwdx); diff --git a/models/model_calvetti/swig/CMakeLists.txt b/models/model_calvetti/swig/CMakeLists.txt index c2163cf173..6642236fda 100644 --- a/models/model_calvetti/swig/CMakeLists.txt +++ b/models/model_calvetti/swig/CMakeLists.txt @@ -15,12 +15,15 @@ endif(POLICY CMP0086) find_package(SWIG REQUIRED) include(${SWIG_USE_FILE}) +if(DEFINED ENV{PYTHON_EXECUTABLE}) + set(Python3_EXECUTABLE $ENV{PYTHON_EXECUTABLE}) +endif() if(${CMAKE_VERSION} VERSION_LESS "3.12.0") find_package(PythonLibs REQUIRED) include_directories(${PYTHON_INCLUDE_DIRS}) set(Python3_LIBRARIES ${PYTHON_LIBRARIES}) else() - find_package (Python3 COMPONENTS Development) + find_package(Python3 COMPONENTS Interpreter Development) include_directories(${Python3_INCLUDE_DIRS}) endif() diff --git a/models/model_dirac/model_dirac.h b/models/model_dirac/model_dirac.h index 0e3c2ed353..18260ba5cc 100644 --- a/models/model_dirac/model_dirac.h +++ b/models/model_dirac/model_dirac.h @@ -1,6 +1,6 @@ #ifndef _amici_model_dirac_h #define _amici_model_dirac_h -/* Generated by amiwrap (R2017b) fee0d5a069aba864a92ded7da50ae68ea7ea43cc */ +/* Generated by amiwrap (R2017b) b04ab2326fb34f3721a15806d9783ff22b6a015a */ #include #include #include "amici/defines.h" @@ -70,7 +70,7 @@ class Model_model_dirac : public amici::Model_ODE { amici::Model* clone() const override { return new Model_model_dirac(*this); }; - std::string getAmiciCommit() const override { return "fee0d5a069aba864a92ded7da50ae68ea7ea43cc"; }; + std::string getAmiciCommit() const override { return "b04ab2326fb34f3721a15806d9783ff22b6a015a"; }; void fJSparse(SUNMatrixContent_Sparse JSparse, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *dwdx) override { JSparse_model_dirac(JSparse, t, x, p, k, h, w, dwdx); diff --git a/models/model_dirac/swig/CMakeLists.txt b/models/model_dirac/swig/CMakeLists.txt index c2163cf173..6642236fda 100644 --- a/models/model_dirac/swig/CMakeLists.txt +++ b/models/model_dirac/swig/CMakeLists.txt @@ -15,12 +15,15 @@ endif(POLICY CMP0086) find_package(SWIG REQUIRED) include(${SWIG_USE_FILE}) +if(DEFINED ENV{PYTHON_EXECUTABLE}) + set(Python3_EXECUTABLE $ENV{PYTHON_EXECUTABLE}) +endif() if(${CMAKE_VERSION} VERSION_LESS "3.12.0") find_package(PythonLibs REQUIRED) include_directories(${PYTHON_INCLUDE_DIRS}) set(Python3_LIBRARIES ${PYTHON_LIBRARIES}) else() - find_package (Python3 COMPONENTS Development) + find_package(Python3 COMPONENTS Interpreter Development) include_directories(${Python3_INCLUDE_DIRS}) endif() diff --git a/models/model_events/model_events.h b/models/model_events/model_events.h index 706028c7ae..d8c4c12ffb 100644 --- a/models/model_events/model_events.h +++ b/models/model_events/model_events.h @@ -1,6 +1,6 @@ #ifndef _amici_model_events_h #define _amici_model_events_h -/* Generated by amiwrap (R2017b) fee0d5a069aba864a92ded7da50ae68ea7ea43cc */ +/* Generated by amiwrap (R2017b) b04ab2326fb34f3721a15806d9783ff22b6a015a */ #include #include #include "amici/defines.h" @@ -84,7 +84,7 @@ class Model_model_events : public amici::Model_ODE { amici::Model* clone() const override { return new Model_model_events(*this); }; - std::string getAmiciCommit() const override { return "fee0d5a069aba864a92ded7da50ae68ea7ea43cc"; }; + std::string getAmiciCommit() const override { return "b04ab2326fb34f3721a15806d9783ff22b6a015a"; }; void fJSparse(SUNMatrixContent_Sparse JSparse, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *dwdx) override { JSparse_model_events(JSparse, t, x, p, k, h, w, dwdx); diff --git a/models/model_events/swig/CMakeLists.txt b/models/model_events/swig/CMakeLists.txt index c2163cf173..6642236fda 100644 --- a/models/model_events/swig/CMakeLists.txt +++ b/models/model_events/swig/CMakeLists.txt @@ -15,12 +15,15 @@ endif(POLICY CMP0086) find_package(SWIG REQUIRED) include(${SWIG_USE_FILE}) +if(DEFINED ENV{PYTHON_EXECUTABLE}) + set(Python3_EXECUTABLE $ENV{PYTHON_EXECUTABLE}) +endif() if(${CMAKE_VERSION} VERSION_LESS "3.12.0") find_package(PythonLibs REQUIRED) include_directories(${PYTHON_INCLUDE_DIRS}) set(Python3_LIBRARIES ${PYTHON_LIBRARIES}) else() - find_package (Python3 COMPONENTS Development) + find_package(Python3 COMPONENTS Interpreter Development) include_directories(${Python3_INCLUDE_DIRS}) endif() diff --git a/models/model_jakstat_adjoint/model_jakstat_adjoint.h b/models/model_jakstat_adjoint/model_jakstat_adjoint.h index 2c0374e4eb..38b8b9e2e1 100644 --- a/models/model_jakstat_adjoint/model_jakstat_adjoint.h +++ b/models/model_jakstat_adjoint/model_jakstat_adjoint.h @@ -1,6 +1,6 @@ #ifndef _amici_model_jakstat_adjoint_h #define _amici_model_jakstat_adjoint_h -/* Generated by amiwrap (R2017b) fee0d5a069aba864a92ded7da50ae68ea7ea43cc */ +/* Generated by amiwrap (R2017b) b04ab2326fb34f3721a15806d9783ff22b6a015a */ #include #include #include "amici/defines.h" @@ -73,7 +73,7 @@ class Model_model_jakstat_adjoint : public amici::Model_ODE { amici::Model* clone() const override { return new Model_model_jakstat_adjoint(*this); }; - std::string getAmiciCommit() const override { return "fee0d5a069aba864a92ded7da50ae68ea7ea43cc"; }; + std::string getAmiciCommit() const override { return "b04ab2326fb34f3721a15806d9783ff22b6a015a"; }; void fJSparse(SUNMatrixContent_Sparse JSparse, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *dwdx) override { JSparse_model_jakstat_adjoint(JSparse, t, x, p, k, h, w, dwdx); diff --git a/models/model_jakstat_adjoint/swig/CMakeLists.txt b/models/model_jakstat_adjoint/swig/CMakeLists.txt index c2163cf173..6642236fda 100644 --- a/models/model_jakstat_adjoint/swig/CMakeLists.txt +++ b/models/model_jakstat_adjoint/swig/CMakeLists.txt @@ -15,12 +15,15 @@ endif(POLICY CMP0086) find_package(SWIG REQUIRED) include(${SWIG_USE_FILE}) +if(DEFINED ENV{PYTHON_EXECUTABLE}) + set(Python3_EXECUTABLE $ENV{PYTHON_EXECUTABLE}) +endif() if(${CMAKE_VERSION} VERSION_LESS "3.12.0") find_package(PythonLibs REQUIRED) include_directories(${PYTHON_INCLUDE_DIRS}) set(Python3_LIBRARIES ${PYTHON_LIBRARIES}) else() - find_package (Python3 COMPONENTS Development) + find_package(Python3 COMPONENTS Interpreter Development) include_directories(${Python3_INCLUDE_DIRS}) endif() diff --git a/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2.h b/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2.h index 4c7a8b6c42..642afbb3ba 100644 --- a/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2.h +++ b/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2.h @@ -1,6 +1,6 @@ #ifndef _amici_model_jakstat_adjoint_o2_h #define _amici_model_jakstat_adjoint_o2_h -/* Generated by amiwrap (R2017b) fee0d5a069aba864a92ded7da50ae68ea7ea43cc */ +/* Generated by amiwrap (R2017b) b04ab2326fb34f3721a15806d9783ff22b6a015a */ #include #include #include "amici/defines.h" @@ -73,7 +73,7 @@ class Model_model_jakstat_adjoint_o2 : public amici::Model_ODE { amici::Model* clone() const override { return new Model_model_jakstat_adjoint_o2(*this); }; - std::string getAmiciCommit() const override { return "fee0d5a069aba864a92ded7da50ae68ea7ea43cc"; }; + std::string getAmiciCommit() const override { return "b04ab2326fb34f3721a15806d9783ff22b6a015a"; }; void fJSparse(SUNMatrixContent_Sparse JSparse, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *dwdx) override { JSparse_model_jakstat_adjoint_o2(JSparse, t, x, p, k, h, w, dwdx); diff --git a/models/model_jakstat_adjoint_o2/swig/CMakeLists.txt b/models/model_jakstat_adjoint_o2/swig/CMakeLists.txt index c2163cf173..6642236fda 100644 --- a/models/model_jakstat_adjoint_o2/swig/CMakeLists.txt +++ b/models/model_jakstat_adjoint_o2/swig/CMakeLists.txt @@ -15,12 +15,15 @@ endif(POLICY CMP0086) find_package(SWIG REQUIRED) include(${SWIG_USE_FILE}) +if(DEFINED ENV{PYTHON_EXECUTABLE}) + set(Python3_EXECUTABLE $ENV{PYTHON_EXECUTABLE}) +endif() if(${CMAKE_VERSION} VERSION_LESS "3.12.0") find_package(PythonLibs REQUIRED) include_directories(${PYTHON_INCLUDE_DIRS}) set(Python3_LIBRARIES ${PYTHON_LIBRARIES}) else() - find_package (Python3 COMPONENTS Development) + find_package(Python3 COMPONENTS Interpreter Development) include_directories(${Python3_INCLUDE_DIRS}) endif() diff --git a/models/model_nested_events/model_nested_events.h b/models/model_nested_events/model_nested_events.h index 22671533a9..0c8e55c7c9 100644 --- a/models/model_nested_events/model_nested_events.h +++ b/models/model_nested_events/model_nested_events.h @@ -1,6 +1,6 @@ #ifndef _amici_model_nested_events_h #define _amici_model_nested_events_h -/* Generated by amiwrap (R2017b) fee0d5a069aba864a92ded7da50ae68ea7ea43cc */ +/* Generated by amiwrap (R2017b) b04ab2326fb34f3721a15806d9783ff22b6a015a */ #include #include #include "amici/defines.h" @@ -73,7 +73,7 @@ class Model_model_nested_events : public amici::Model_ODE { amici::Model* clone() const override { return new Model_model_nested_events(*this); }; - std::string getAmiciCommit() const override { return "fee0d5a069aba864a92ded7da50ae68ea7ea43cc"; }; + std::string getAmiciCommit() const override { return "b04ab2326fb34f3721a15806d9783ff22b6a015a"; }; void fJSparse(SUNMatrixContent_Sparse JSparse, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *dwdx) override { JSparse_model_nested_events(JSparse, t, x, p, k, h, w, dwdx); diff --git a/models/model_nested_events/swig/CMakeLists.txt b/models/model_nested_events/swig/CMakeLists.txt index c2163cf173..6642236fda 100644 --- a/models/model_nested_events/swig/CMakeLists.txt +++ b/models/model_nested_events/swig/CMakeLists.txt @@ -15,12 +15,15 @@ endif(POLICY CMP0086) find_package(SWIG REQUIRED) include(${SWIG_USE_FILE}) +if(DEFINED ENV{PYTHON_EXECUTABLE}) + set(Python3_EXECUTABLE $ENV{PYTHON_EXECUTABLE}) +endif() if(${CMAKE_VERSION} VERSION_LESS "3.12.0") find_package(PythonLibs REQUIRED) include_directories(${PYTHON_INCLUDE_DIRS}) set(Python3_LIBRARIES ${PYTHON_LIBRARIES}) else() - find_package (Python3 COMPONENTS Development) + find_package(Python3 COMPONENTS Interpreter Development) include_directories(${Python3_INCLUDE_DIRS}) endif() diff --git a/models/model_neuron/model_neuron.h b/models/model_neuron/model_neuron.h index e9311ef68e..0c54682f0d 100644 --- a/models/model_neuron/model_neuron.h +++ b/models/model_neuron/model_neuron.h @@ -1,6 +1,6 @@ #ifndef _amici_model_neuron_h #define _amici_model_neuron_h -/* Generated by amiwrap (R2017b) fee0d5a069aba864a92ded7da50ae68ea7ea43cc */ +/* Generated by amiwrap (R2017b) b04ab2326fb34f3721a15806d9783ff22b6a015a */ #include #include #include "amici/defines.h" @@ -87,7 +87,7 @@ class Model_model_neuron : public amici::Model_ODE { amici::Model* clone() const override { return new Model_model_neuron(*this); }; - std::string getAmiciCommit() const override { return "fee0d5a069aba864a92ded7da50ae68ea7ea43cc"; }; + std::string getAmiciCommit() const override { return "b04ab2326fb34f3721a15806d9783ff22b6a015a"; }; void fJSparse(SUNMatrixContent_Sparse JSparse, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *dwdx) override { JSparse_model_neuron(JSparse, t, x, p, k, h, w, dwdx); diff --git a/models/model_neuron/swig/CMakeLists.txt b/models/model_neuron/swig/CMakeLists.txt index c2163cf173..6642236fda 100644 --- a/models/model_neuron/swig/CMakeLists.txt +++ b/models/model_neuron/swig/CMakeLists.txt @@ -15,12 +15,15 @@ endif(POLICY CMP0086) find_package(SWIG REQUIRED) include(${SWIG_USE_FILE}) +if(DEFINED ENV{PYTHON_EXECUTABLE}) + set(Python3_EXECUTABLE $ENV{PYTHON_EXECUTABLE}) +endif() if(${CMAKE_VERSION} VERSION_LESS "3.12.0") find_package(PythonLibs REQUIRED) include_directories(${PYTHON_INCLUDE_DIRS}) set(Python3_LIBRARIES ${PYTHON_LIBRARIES}) else() - find_package (Python3 COMPONENTS Development) + find_package(Python3 COMPONENTS Interpreter Development) include_directories(${Python3_INCLUDE_DIRS}) endif() diff --git a/models/model_neuron_o2/model_neuron_o2.h b/models/model_neuron_o2/model_neuron_o2.h index 37fbf158a6..bb8f565576 100644 --- a/models/model_neuron_o2/model_neuron_o2.h +++ b/models/model_neuron_o2/model_neuron_o2.h @@ -1,6 +1,6 @@ #ifndef _amici_model_neuron_o2_h #define _amici_model_neuron_o2_h -/* Generated by amiwrap (R2017b) fee0d5a069aba864a92ded7da50ae68ea7ea43cc */ +/* Generated by amiwrap (R2017b) b04ab2326fb34f3721a15806d9783ff22b6a015a */ #include #include #include "amici/defines.h" @@ -89,7 +89,7 @@ class Model_model_neuron_o2 : public amici::Model_ODE { amici::Model* clone() const override { return new Model_model_neuron_o2(*this); }; - std::string getAmiciCommit() const override { return "fee0d5a069aba864a92ded7da50ae68ea7ea43cc"; }; + std::string getAmiciCommit() const override { return "b04ab2326fb34f3721a15806d9783ff22b6a015a"; }; void fJSparse(SUNMatrixContent_Sparse JSparse, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *dwdx) override { JSparse_model_neuron_o2(JSparse, t, x, p, k, h, w, dwdx); diff --git a/models/model_neuron_o2/swig/CMakeLists.txt b/models/model_neuron_o2/swig/CMakeLists.txt index c2163cf173..6642236fda 100644 --- a/models/model_neuron_o2/swig/CMakeLists.txt +++ b/models/model_neuron_o2/swig/CMakeLists.txt @@ -15,12 +15,15 @@ endif(POLICY CMP0086) find_package(SWIG REQUIRED) include(${SWIG_USE_FILE}) +if(DEFINED ENV{PYTHON_EXECUTABLE}) + set(Python3_EXECUTABLE $ENV{PYTHON_EXECUTABLE}) +endif() if(${CMAKE_VERSION} VERSION_LESS "3.12.0") find_package(PythonLibs REQUIRED) include_directories(${PYTHON_INCLUDE_DIRS}) set(Python3_LIBRARIES ${PYTHON_LIBRARIES}) else() - find_package (Python3 COMPONENTS Development) + find_package(Python3 COMPONENTS Interpreter Development) include_directories(${Python3_INCLUDE_DIRS}) endif() diff --git a/models/model_robertson/model_robertson.h b/models/model_robertson/model_robertson.h index 9c5d7a1ab0..85ef3fb920 100644 --- a/models/model_robertson/model_robertson.h +++ b/models/model_robertson/model_robertson.h @@ -1,6 +1,6 @@ #ifndef _amici_model_robertson_h #define _amici_model_robertson_h -/* Generated by amiwrap (R2017b) fee0d5a069aba864a92ded7da50ae68ea7ea43cc */ +/* Generated by amiwrap (R2017b) b04ab2326fb34f3721a15806d9783ff22b6a015a */ #include #include #include "amici/defines.h" @@ -71,7 +71,7 @@ class Model_model_robertson : public amici::Model_DAE { amici::Model* clone() const override { return new Model_model_robertson(*this); }; - std::string getAmiciCommit() const override { return "fee0d5a069aba864a92ded7da50ae68ea7ea43cc"; }; + std::string getAmiciCommit() const override { return "b04ab2326fb34f3721a15806d9783ff22b6a015a"; }; void fJSparse(SUNMatrixContent_Sparse JSparse, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype cj, const realtype *dx, const realtype *w, const realtype *dwdx) override { JSparse_model_robertson(JSparse, t, x, p, k, h, cj, dx, w, dwdx); diff --git a/models/model_robertson/swig/CMakeLists.txt b/models/model_robertson/swig/CMakeLists.txt index c2163cf173..6642236fda 100644 --- a/models/model_robertson/swig/CMakeLists.txt +++ b/models/model_robertson/swig/CMakeLists.txt @@ -15,12 +15,15 @@ endif(POLICY CMP0086) find_package(SWIG REQUIRED) include(${SWIG_USE_FILE}) +if(DEFINED ENV{PYTHON_EXECUTABLE}) + set(Python3_EXECUTABLE $ENV{PYTHON_EXECUTABLE}) +endif() if(${CMAKE_VERSION} VERSION_LESS "3.12.0") find_package(PythonLibs REQUIRED) include_directories(${PYTHON_INCLUDE_DIRS}) set(Python3_LIBRARIES ${PYTHON_LIBRARIES}) else() - find_package (Python3 COMPONENTS Development) + find_package(Python3 COMPONENTS Interpreter Development) include_directories(${Python3_INCLUDE_DIRS}) endif() diff --git a/models/model_steadystate/model_steadystate.h b/models/model_steadystate/model_steadystate.h index 964636ccb5..6694fc6b36 100644 --- a/models/model_steadystate/model_steadystate.h +++ b/models/model_steadystate/model_steadystate.h @@ -1,6 +1,6 @@ #ifndef _amici_model_steadystate_h #define _amici_model_steadystate_h -/* Generated by amiwrap (R2017b) fee0d5a069aba864a92ded7da50ae68ea7ea43cc */ +/* Generated by amiwrap (R2017b) b04ab2326fb34f3721a15806d9783ff22b6a015a */ #include #include #include "amici/defines.h" @@ -70,7 +70,7 @@ class Model_model_steadystate : public amici::Model_ODE { amici::Model* clone() const override { return new Model_model_steadystate(*this); }; - std::string getAmiciCommit() const override { return "fee0d5a069aba864a92ded7da50ae68ea7ea43cc"; }; + std::string getAmiciCommit() const override { return "b04ab2326fb34f3721a15806d9783ff22b6a015a"; }; void fJSparse(SUNMatrixContent_Sparse JSparse, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *dwdx) override { JSparse_model_steadystate(JSparse, t, x, p, k, h, w, dwdx); diff --git a/models/model_steadystate/swig/CMakeLists.txt b/models/model_steadystate/swig/CMakeLists.txt index c2163cf173..6642236fda 100644 --- a/models/model_steadystate/swig/CMakeLists.txt +++ b/models/model_steadystate/swig/CMakeLists.txt @@ -15,12 +15,15 @@ endif(POLICY CMP0086) find_package(SWIG REQUIRED) include(${SWIG_USE_FILE}) +if(DEFINED ENV{PYTHON_EXECUTABLE}) + set(Python3_EXECUTABLE $ENV{PYTHON_EXECUTABLE}) +endif() if(${CMAKE_VERSION} VERSION_LESS "3.12.0") find_package(PythonLibs REQUIRED) include_directories(${PYTHON_INCLUDE_DIRS}) set(Python3_LIBRARIES ${PYTHON_LIBRARIES}) else() - find_package (Python3 COMPONENTS Development) + find_package(Python3 COMPONENTS Interpreter Development) include_directories(${Python3_INCLUDE_DIRS}) endif() diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 96df500377..95ceacd2ab 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -1,11 +1,10 @@ +if(DEFINED ENV{PYTHON_EXECUTABLE}) + set(Python3_EXECUTABLE $ENV{PYTHON_EXECUTABLE}) +endif() if(${CMAKE_VERSION} VERSION_LESS "3.12.0") - if(DEFINED ENV{PYTHON_EXECUTABLE}) - set( PYTHON_EXECUTABLE $ENV{PYTHON_EXECUTABLE} ) - endif() find_package(PythonInterp 3.8 REQUIRED) - set(Python3_EXECUTABLE ${PYTHON_EXECUTABLE}) else() - find_package (Python3 COMPONENTS Interpreter) + find_package(Python3 COMPONENTS Interpreter) endif() diff --git a/python/sdist/amici/__init__.py b/python/sdist/amici/__init__.py index 46eac0cfb1..94acc73df1 100644 --- a/python/sdist/amici/__init__.py +++ b/python/sdist/amici/__init__.py @@ -28,7 +28,7 @@ import sys from pathlib import Path from types import ModuleType as ModelModule -from typing import Optional, Union +from typing import Optional, Union, Callable, Any def _get_amici_path(): @@ -191,3 +191,12 @@ class AmiciVersionError(RuntimeError): """Error thrown if an AMICI model is loaded that is incompatible with the installed AMICI base package""" pass + + +def _get_default_argument(func: Callable, arg: str) -> Any: + """Get the default value of the given argument in the given function.""" + import inspect + signature = inspect.signature(func) + if (default := signature.parameters[arg].default) is not inspect.Parameter.empty: + return default + raise ValueError(f"No default value for argument {arg} of {func}.") diff --git a/python/sdist/amici/cxxcodeprinter.py b/python/sdist/amici/cxxcodeprinter.py index 15f440a0fb..7a27892ab9 100644 --- a/python/sdist/amici/cxxcodeprinter.py +++ b/python/sdist/amici/cxxcodeprinter.py @@ -2,26 +2,62 @@ import itertools import os import re -from typing import Dict, List, Optional, Tuple +from typing import Dict, List, Optional, Tuple, Iterable import sympy as sp +from sympy.codegen.rewriting import optimize, Optimization from sympy.printing.cxx import CXX11CodePrinter from sympy.utilities.iterables import numbered_symbols from toposort import toposort class AmiciCxxCodePrinter(CXX11CodePrinter): - """C++ code printer""" + """ + C++ code printer + + Attributes + ---------- + extract_cse: + Whether to extract common subexpression during code printing. + Currently controlled by environment variable ``AMICI_EXTRACT_CSE``. + optimizations: + Iterable of :class:`sympy.codegen.rewriting.Optimization`s to optimize + generated code (e.g. :data:`sympy.codegen.rewriting.Optimization` for + optimizations, such as ``log(1 + x)`` --> ``logp1(x)``). + Applying these optimizations is potentially quite costly. + """ + + optimizations: Iterable[Optimization] = () def __init__(self): + """ + Create code printer + + :param optimize_code: + Whether to apply code optimizations such as log(1 + x) --> logp1(x) + """ super().__init__() # extract common subexpressions in matrix functions? self.extract_cse = (os.getenv("AMICI_EXTRACT_CSE", "0").lower() in ('1', 'on', 'true')) + # Floating-point optimizations + # e.g., log(1 + x) --> logp1(x) + if self.optimizations: + self._fpoptimizer = lambda x: optimize(x, self.optimizations) + else: + self._fpoptimizer = None + def doprint(self, expr: sp.Expr, assign_to: Optional[str] = None) -> str: + if self._fpoptimizer: + if isinstance(expr, list): + expr = list(map(self._fpoptimizer, expr)) + else: + expr = self._fpoptimizer(expr) + try: + # floating point code = super().doprint(expr, assign_to) code = re.sub(r'(^|\W)M_PI(\W|$)', r'\1amici::pi\2', code) @@ -214,8 +250,8 @@ def csc_matrix( symbol_row_vals.append(row) idx += 1 - symbol_name = f'd{self.doprint(rownames[row])}' \ - f'_d{self.doprint(colnames[col])}' + symbol_name = f'd{rownames[row].name}' \ + f'_d{colnames[col].name}' if identifier: symbol_name += f'_{identifier}' symbol_list.append(symbol_name) diff --git a/python/sdist/amici/ode_export.py b/python/sdist/amici/ode_export.py index ee537efa69..fcae4d883c 100644 --- a/python/sdist/amici/ode_export.py +++ b/python/sdist/amici/ode_export.py @@ -2979,7 +2979,7 @@ def _get_function_body( elif function in self.model.sym_names() \ and function not in non_unique_id_symbols: if function in sparse_functions: - symbols = self.model.sparsesym(function) + symbols = list(map(sp.Symbol, self.model.sparsesym(function))) else: symbols = self.model.sym(function) lines += self.model._code_printer._get_sym_lines_symbols( @@ -3388,23 +3388,22 @@ def get_model_override_implementation(fun: str, name: str, :return: C++ function implementation string """ - impl = '{return_type} f{fun}({signature}) override {{' - - if nobody: - impl += '}}\n' - else: - impl += '\n{ind8}{fun}_{name}({eval_signature});\n{ind4}}}\n' - func_info = functions[fun] - - return impl.format( - ind4=' ' * 4, - ind8=' ' * 8, + body = "" if nobody \ + else '\n{ind8}{maybe_return}{fun}_{name}({eval_signature});{ind4}\n' \ + .format( + ind4=' ' * 4, + ind8=' ' * 8, + maybe_return="" if func_info.return_type == "void" else "return ", + fun=fun, + name=name, + eval_signature=remove_argument_types(func_info.arguments), + ) + return '{return_type} f{fun}({signature}) override {{{body}}}\n'.format( + return_type=func_info.return_type, fun=fun, - name=name, signature=func_info.arguments, - eval_signature=remove_argument_types(func_info.arguments), - return_type=func_info.return_type + body=body, ) diff --git a/python/sdist/amici/petab_import.py b/python/sdist/amici/petab_import.py index 61909340c3..7dc7785149 100644 --- a/python/sdist/amici/petab_import.py +++ b/python/sdist/amici/petab_import.py @@ -73,7 +73,8 @@ def _add_global_parameter(sbml_model: libsbml.Model, def get_fixed_parameters( - petab_problem: petab.Problem + petab_problem: petab.Problem, + non_estimated_parameters_as_constants=True, ) -> List[str]: """ Determine, set and return fixed model parameters. @@ -85,6 +86,12 @@ def get_fixed_parameters( :param petab_problem: The PEtab problem instance + :param non_estimated_parameters_as_constants: + Whether parameters marked as non-estimated in PEtab should be + considered constant in AMICI. Setting this to ``True`` will reduce + model size and simulation times. If sensitivities with respect to those + parameters are required, this should be set to ``False``. + :return: List of IDs of parameters which are to be considered constant. """ @@ -107,7 +114,7 @@ def get_fixed_parameters( # if we have a parameter table, all parameters that are allowed to be # listed in the parameter table, but are not marked as estimated, can be - # turned in to AMICI constants + # turned into AMICI constants # due to legacy API, we might not always have a parameter table, though fixed_parameters = set() if petab_problem.parameter_df is not None: @@ -121,8 +128,13 @@ def get_fixed_parameters( if petab_problem.measurement_df is not None else pd.DataFrame(columns=petab.MEASUREMENT_DF_REQUIRED_COLS), ) - estimated_parameters = petab_problem.parameter_df.index.values[ - petab_problem.parameter_df[ESTIMATE] == 1] + if non_estimated_parameters_as_constants: + estimated_parameters = \ + petab_problem.parameter_df.index.values[ + petab_problem.parameter_df[ESTIMATE] == 1] + else: + # don't treat parameter table parameters as constants + estimated_parameters = petab_problem.parameter_df.index.values fixed_parameters = set(all_parameters) - set(estimated_parameters) sbml_model = petab_problem.sbml_model @@ -234,6 +246,7 @@ def import_petab_problem( model_output_dir: Union[str, Path, None] = None, model_name: str = None, force_compile: bool = False, + non_estimated_parameters_as_constants = True, **kwargs) -> 'amici.Model': """ Import model from petab problem. @@ -254,6 +267,12 @@ def import_petab_problem( Whether to compile the model even if the target folder is not empty, or the model exists already. + :param non_estimated_parameters_as_constants: + Whether parameters marked as non-estimated in PEtab should be + considered constant in AMICI. Setting this to ``True`` will reduce + model size and simulation times. If sensitivities with respect to those + parameters are required, this should be set to ``False``. + :param kwargs: Additional keyword arguments to be passed to :meth:`amici.sbml_import.SbmlImporter.sbml2amici`. @@ -306,11 +325,14 @@ def import_petab_problem( petab_problem=petab_problem, model_name=model_name, model_output_dir=model_output_dir, + non_estimated_parameters_as_constants= + non_estimated_parameters_as_constants, **kwargs) # import model model_module = amici.import_model_module(model_name, model_output_dir) model = model_module.getModel() + check_model(amici_model=model, petab_problem=petab_problem) logger.info(f"Successfully loaded model {model_name} " f"from {model_output_dir}.") @@ -318,6 +340,33 @@ def import_petab_problem( return model +def check_model( + amici_model: amici.Model, + petab_problem: petab.Problem, +) -> None: + """Check that the model is consistent with the PEtab problem.""" + if petab_problem.parameter_df is None: + return + + amici_ids_free = set(amici_model.getParameterIds()) + amici_ids = amici_ids_free | set(amici_model.getFixedParameterIds()) + + petab_ids_free = set(petab_problem.parameter_df.loc[ + petab_problem.parameter_df[ESTIMATE] == 1 + ].index) + + amici_ids_free_required = petab_ids_free.intersection(amici_ids) + + if not amici_ids_free_required.issubset(amici_ids_free): + raise ValueError( + 'The available AMICI model does not support estimating the ' + 'following parameters. Please recompile the model and ensure ' + 'that these parameters are not treated as constants. Deleting ' + 'the current model might also resolve this. Parameters: ' + f'{amici_ids_free_required.difference(amici_ids_free)}' + ) + + def _create_model_output_dir_name(sbml_model: 'libsbml.Model') -> Path: """ Find a folder for storing the compiled amici model. @@ -373,6 +422,7 @@ def import_model_sbml( verbose: Optional[Union[bool, int]] = True, allow_reinit_fixpar_initcond: bool = True, validate: bool = True, + non_estimated_parameters_as_constants=True, **kwargs) -> amici.SbmlImporter: """ Create AMICI model from PEtab problem @@ -415,6 +465,12 @@ def import_model_sbml( :param validate: Whether to validate the PEtab problem + :param non_estimated_parameters_as_constants: + Whether parameters marked as non-estimated in PEtab should be + considered constant in AMICI. Setting this to ``True`` will reduce + model size and simulation times. If sensitivities with respect to those + parameters are required, this should be set to ``False``. + :param kwargs: Additional keyword arguments to be passed to :meth:`amici.sbml_import.SbmlImporter.sbml2amici`. @@ -591,6 +647,8 @@ def import_model_sbml( fixed_parameters.extend( get_fixed_parameters( petab_problem=petab_problem, + non_estimated_parameters_as_constants= + non_estimated_parameters_as_constants, )) logger.debug(f"Fixed parameters are {fixed_parameters}") @@ -611,6 +669,13 @@ def import_model_sbml( verbose=verbose, **kwargs) + if kwargs.get('compile', amici._get_default_argument( + sbml_importer.sbml2amici, 'compile')): + # check that the model extension was compiled successfully + model_module = amici.import_model_module(model_name, model_output_dir) + model = model_module.getModel() + check_model(amici_model=model, petab_problem=petab_problem) + return sbml_importer diff --git a/python/sdist/amici/petab_objective.py b/python/sdist/amici/petab_objective.py index eb0f7ec6cf..e8557cb242 100644 --- a/python/sdist/amici/petab_objective.py +++ b/python/sdist/amici/petab_objective.py @@ -524,11 +524,13 @@ def create_edatas( observable_ids = amici_model.getObservableIds() - measurement_groupvar = [petab.SIMULATION_CONDITION_ID] - if petab.PREEQUILIBRATION_CONDITION_ID in simulation_conditions: - measurement_groupvar.append(petab.PREEQUILIBRATION_CONDITION_ID) measurement_dfs = dict(list( - petab_problem.measurement_df.groupby(measurement_groupvar) + petab_problem.measurement_df.groupby( + [petab.SIMULATION_CONDITION_ID, + petab.PREEQUILIBRATION_CONDITION_ID] + if petab.PREEQUILIBRATION_CONDITION_ID in simulation_conditions + else petab.SIMULATION_CONDITION_ID + ) )) edatas = [] diff --git a/python/sdist/amici/pysb_import.py b/python/sdist/amici/pysb_import.py index 0929283877..0648a0b478 100644 --- a/python/sdist/amici/pysb_import.py +++ b/python/sdist/amici/pysb_import.py @@ -222,6 +222,10 @@ def ode_model_from_pysb_importer( simplify=simplify, cache_simplify=cache_simplify, ) + # Sympy code optimizations are incompatible with PySB objects, as + # `pysb.Observable` comes with its own `.match` which overrides + # `sympy.Basic.match()`, breaking `sympy.codegen.rewriting.optimize`. + ode._code_printer._fpoptimizer = None if constant_parameters is None: constant_parameters = [] diff --git a/python/sdist/amici/setup.template.py b/python/sdist/amici/setup.template.py index 9e5297be62..b1f5db9e4a 100644 --- a/python/sdist/amici/setup.template.py +++ b/python/sdist/amici/setup.template.py @@ -15,7 +15,7 @@ add_debug_flags_if_required, add_openmp_flags, ) -from setuptools import find_packages, setup, Extension +from setuptools import find_namespace_packages, setup, Extension from setuptools.command.build_ext import build_ext @@ -167,12 +167,11 @@ def get_extension() -> Extension: author_email='model-author-todo', # license = 'BSD', ext_modules=[MODEL_EXT], - packages=find_packages(), + packages=find_namespace_packages(), install_requires=['amici==TPL_AMICI_VERSION'], extras_require={'wurlitzer': ['wurlitzer']}, python_requires='>=3.8', package_data={}, zip_safe=False, - include_package_data=True, classifiers=CLASSIFIERS, ) diff --git a/python/sdist/amici/setuptools.py b/python/sdist/amici/setuptools.py index 7cfcf61fe1..bd51a4af88 100644 --- a/python/sdist/amici/setuptools.py +++ b/python/sdist/amici/setuptools.py @@ -112,14 +112,16 @@ def get_hdf5_config() -> PackageInfo: '/usr/include/hdf5/serial', '/usr/local/include', '/usr/include', # travis ubuntu xenial, centos - '/usr/local/Cellar/hdf5/1.10.2_1/include' # travis macOS + '/usr/local/Cellar/hdf5/1.10.2_1/include', # travis macOS + '/opt/homebrew/Cellar/hdf5/1.12.2_2/include' ] hdf5_library_dir_hints = [ '/usr/lib/x86_64-linux-gnu/', # travis ubuntu xenial '/usr/lib/x86_64-linux-gnu/hdf5/serial', '/usr/local/lib', '/usr/lib64/', # CentOS - '/usr/local/Cellar/hdf5/1.10.2_1/lib' # travis macOS + '/usr/local/Cellar/hdf5/1.10.2_1/lib', # travis macOS + '/opt/homebrew/Cellar/hdf5/1.12.2_2/lib' ] # special treatment for conda environments @@ -213,7 +215,8 @@ def add_debug_flags_if_required(cxx_flags: List[str], and os.environ['ENABLE_AMICI_DEBUGGING'] == 'TRUE': print("ENABLE_AMICI_DEBUGGING was set to TRUE." " Building AMICI with debug symbols.") - cxx_flags.extend(['-g', '-O0', '-UNDEBUG']) + cxx_flags.extend(['-g', '-O0', '-UNDEBUG', '-Werror', + '-Wno-error=deprecated-declarations']) linker_flags.extend(['-g']) diff --git a/python/sdist/setup.cfg b/python/sdist/setup.cfg index 35b0796925..0893437f59 100644 --- a/python/sdist/setup.cfg +++ b/python/sdist/setup.cfg @@ -22,7 +22,7 @@ classifiers = Topic :: Scientific/Engineering :: Bio-Informatics [options] -packages = find: +packages = find_namespace: package_dir = amici = amici python_requires = >=3.8 diff --git a/python/tests/test_cxxcodeprinter.py b/python/tests/test_cxxcodeprinter.py new file mode 100644 index 0000000000..833a8cd492 --- /dev/null +++ b/python/tests/test_cxxcodeprinter.py @@ -0,0 +1,14 @@ +from amici.cxxcodeprinter import AmiciCxxCodePrinter +from sympy.codegen.rewriting import optims_c99 +import sympy as sp + + +def test_optimizations(): + """Check that AmiciCxxCodePrinter handles optimizations correctly.""" + try: + old_optim = AmiciCxxCodePrinter.optimizations + AmiciCxxCodePrinter.optimizations = optims_c99 + cp = AmiciCxxCodePrinter() + assert "expm1" in cp.doprint(sp.sympify("exp(x) - 1")) + finally: + AmiciCxxCodePrinter.optimizations = old_optim diff --git a/python/tests/test_misc.py b/python/tests/test_misc.py index 663b40ccdc..df24f7397c 100644 --- a/python/tests/test_misc.py +++ b/python/tests/test_misc.py @@ -103,3 +103,17 @@ def test_monkeypatch(): # check that the monkeypatch is transient assert (t ** n).diff(t).subs(vals) is sp.nan + + +@skip_on_valgrind +def test_get_default_argument(): + # no default + with pytest.raises(ValueError): + amici._get_default_argument(lambda x: x, 'x') + + # non-existant parameter + with pytest.raises(KeyError): + amici._get_default_argument(lambda x: x, 'y') + + # okay + assert amici._get_default_argument(lambda x=1: x, 'x') == 1 diff --git a/python/tests/test_ode_export.py b/python/tests/test_ode_export.py index 2e9c45698f..875438ab6c 100644 --- a/python/tests/test_ode_export.py +++ b/python/tests/test_ode_export.py @@ -10,8 +10,11 @@ def test_csc_matrix(): printer = AmiciCxxCodePrinter() matrix = sp.Matrix([[1, 0], [2, 3]]) symbol_col_ptrs, symbol_row_vals, sparse_list, symbol_list, sparse_matrix \ - = printer.csc_matrix(matrix, rownames=['a1', 'a2'], - colnames=['b1', 'b2']) + = printer.csc_matrix( + matrix, + rownames=[sp.Symbol('a1'), sp.Symbol('a2')], + colnames=[sp.Symbol('b1'), sp.Symbol('b2')] + ) assert symbol_col_ptrs == [0, 2, 3] assert symbol_row_vals == [0, 1, 1] diff --git a/python/tests/test_petab_import.py b/python/tests/test_petab_import.py index d8dfe06c7a..77b8331c48 100644 --- a/python/tests/test_petab_import.py +++ b/python/tests/test_petab_import.py @@ -6,8 +6,7 @@ from amici.testing import skip_on_valgrind -petab = pytest.importorskip("petab") -SbmlModel = pytest.importorskip("petab.models.sbml_model.SbmlModel") +petab = pytest.importorskip("petab", reason="Missing petab") amici_petab_import = pytest.importorskip("amici.petab_import") @@ -47,6 +46,7 @@ def test_get_fixed_parameters(simple_sbml_model): p4: not fixed (via parameter table `estimate=1`) p5: fixed (implicitly, because not listed as estimated) """ + from petab.models.sbml_model import SbmlModel sbml_doc, sbml_model = simple_sbml_model condition_df = petab.get_condition_df( pd.DataFrame({ @@ -69,4 +69,7 @@ def test_get_fixed_parameters(simple_sbml_model): assert set(amici_petab_import.get_fixed_parameters(petab_problem)) \ == {"p1", "p3", "p5"} - # TODO: any non-estimated model parameter should be made constant + assert set(amici_petab_import.get_fixed_parameters( + petab_problem, + non_estimated_parameters_as_constants=False)) \ + == {"p1", "p5"} diff --git a/src/model.cpp b/src/model.cpp index a696437e4b..9fea9942c5 100644 --- a/src/model.cpp +++ b/src/model.cpp @@ -244,16 +244,14 @@ bool operator==(const Model &a, const Model &b) { == static_cast(b)) && (a.o2mode == b.o2mode) && (a.z2event_ == b.z2event_) && (a.idlist == b.idlist) && - (a.state_.h == b.state_.h) && - (a.state_.unscaledParameters == b.state_.unscaledParameters) && (a.simulation_parameters_ == b.simulation_parameters_) && - (a.state_.fixedParameters == b.state_.fixedParameters) && - (a.state_.plist == b.state_.plist) && (a.x0data_ == b.x0data_) && + (a.x0data_ == b.x0data_) && (a.sx0data_ == b.sx0data_) && (a.nmaxevent_ == b.nmaxevent_) && (a.state_is_non_negative_ == b.state_is_non_negative_) && (a.sigma_res_ == b.sigma_res_) && - (a.min_sigma_ == b.min_sigma_); + (a.min_sigma_ == b.min_sigma_) + && a.state_ == b.state_; } bool operator==(const ModelDimensions &a, const ModelDimensions &b) { diff --git a/src/simulation_parameters.cpp b/src/simulation_parameters.cpp index 824f5e2541..990e872dfd 100644 --- a/src/simulation_parameters.cpp +++ b/src/simulation_parameters.cpp @@ -1,21 +1,25 @@ #include "amici/simulation_parameters.h" +#include "amici/misc.h" #include namespace amici { bool operator==(const SimulationParameters &a, const SimulationParameters &b) { - return (a.fixedParameters == b.fixedParameters) && - (a.fixedParametersPreequilibration == b.fixedParametersPreequilibration) && - (a.fixedParametersPresimulation == b.fixedParametersPresimulation) && - (a.parameters == b.parameters) && - (a.plist == b.plist) && - (a.pscale == b.pscale) && - (a.reinitializeFixedParameterInitialStates == b.reinitializeFixedParameterInitialStates) && - (a.sx0 == b.sx0) && - (a.t_presim == b.t_presim) && - (a.tstart_ == b.tstart_) && - (a.ts_ == b.ts_); + return is_equal(a.fixedParameters, b.fixedParameters) && + is_equal(a.fixedParametersPreequilibration, + b.fixedParametersPreequilibration) && + is_equal(a.fixedParametersPresimulation, + b.fixedParametersPresimulation) && + is_equal(a.parameters, b.parameters) && + (a.plist == b.plist) && + (a.pscale == b.pscale) && + (a.reinitializeFixedParameterInitialStates + == b.reinitializeFixedParameterInitialStates) && + is_equal(a.sx0, b.sx0) && + (a.t_presim == b.t_presim) && + (a.tstart_ == b.tstart_) && + (a.ts_ == b.ts_); } void SimulationParameters::reinitializeAllFixedParameterDependentInitialStatesForPresimulation(int nx_rdata) diff --git a/swig/CMakeLists_model.cmake b/swig/CMakeLists_model.cmake index c2163cf173..0c4136b816 100644 --- a/swig/CMakeLists_model.cmake +++ b/swig/CMakeLists_model.cmake @@ -15,12 +15,17 @@ endif(POLICY CMP0086) find_package(SWIG REQUIRED) include(${SWIG_USE_FILE}) +if(DEFINED ENV{PYTHON_EXECUTABLE}) + set(Python3_EXECUTABLE $ENV{PYTHON_EXECUTABLE}) +endif() if(${CMAKE_VERSION} VERSION_LESS "3.12.0") find_package(PythonLibs REQUIRED) include_directories(${PYTHON_INCLUDE_DIRS}) set(Python3_LIBRARIES ${PYTHON_LIBRARIES}) else() - find_package (Python3 COMPONENTS Development) + # We don't need "Interpreter" here, but without that, FindPython3 will + # ignore the Python version selected via $Python3_EXECUTABLE + find_package(Python3 COMPONENTS Interpreter Development) include_directories(${Python3_INCLUDE_DIRS}) endif() diff --git a/tests/cpp/unittests/testExpData.cpp b/tests/cpp/unittests/testExpData.cpp index 5a77c8ad00..83c6dda740 100644 --- a/tests/cpp/unittests/testExpData.cpp +++ b/tests/cpp/unittests/testExpData.cpp @@ -182,6 +182,17 @@ TEST_F(ExpDataTest, CopyConstructable) "ts"); } + +TEST_F(ExpDataTest, Equality) +{ + auto edata = ExpData(testModel); + auto edata2(edata); + ASSERT_TRUE(edata == edata2); + + edata2.id = "different"; + ASSERT_FALSE(edata == edata2); +} + TEST_F(ExpDataTest, DimensionChecks) { std::vector bad_std(ny, -0.1); diff --git a/tests/cpp/unittests/testMisc.cpp b/tests/cpp/unittests/testMisc.cpp index d77aa54f93..a1763c399e 100644 --- a/tests/cpp/unittests/testMisc.cpp +++ b/tests/cpp/unittests/testMisc.cpp @@ -711,4 +711,14 @@ TEST(ReturnCodeToStr, ReturnCodeToStr) simulation_status_to_str(AMICI_UNRECOVERABLE_ERROR)); } +TEST(SpanEqual, SpanEqual) +{ + std::vector a {1, 2, 3}; + std::vector b {1, 2, NAN}; + + EXPECT_TRUE(is_equal(a, a)); + EXPECT_TRUE(is_equal(b, b)); + EXPECT_FALSE(is_equal(a, b)); +} + } // namespace diff --git a/version.txt b/version.txt index 54d1a4f2a4..a803cc227f 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.13.0 +0.14.0