From 2591a3a1f60acbfc44d9096fbfd92ce0f240aa31 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Wed, 17 Jan 2024 10:47:16 +0100 Subject: [PATCH 001/188] Update to swig4.2 (#2268) --- .github/actions/setup-swig/action.yml | 2 +- scripts/downloadAndBuildSwig.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/setup-swig/action.yml b/.github/actions/setup-swig/action.yml index 0eb8a04473..b4441cd05e 100644 --- a/.github/actions/setup-swig/action.yml +++ b/.github/actions/setup-swig/action.yml @@ -7,7 +7,7 @@ inputs: swig_version: description: 'Swig version to build' required: false - default: '4.1.1' + default: '4.2.0' runs: using: "composite" diff --git a/scripts/downloadAndBuildSwig.sh b/scripts/downloadAndBuildSwig.sh index 5fa0896f6c..261192f667 100755 --- a/scripts/downloadAndBuildSwig.sh +++ b/scripts/downloadAndBuildSwig.sh @@ -7,7 +7,7 @@ set -euo pipefail SCRIPT_PATH=$(dirname "$BASH_SOURCE") AMICI_PATH=$(cd "$SCRIPT_PATH/.." && pwd) -swig_version="${1:-"4.1.1"}" +swig_version="${1:-"4.2.0"}" SWIG_ARCHIVE="swig-${swig_version}.tar.gz" SWIG_URL="http://downloads.sourceforge.net/project/swig/swig/swig-${swig_version}/${SWIG_ARCHIVE}" SWIG_DIR="swig-${swig_version}" From 7b8ad5873945b122b24f86200efa555f9412ae0d Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Wed, 17 Jan 2024 11:50:32 +0100 Subject: [PATCH 002/188] Use PyPI trusted publishing (#2270) Use PyPI [trusted publishing](https://docs.pypi.org/trusted-publishers/) instead of username/password. --- .github/workflows/deploy_release.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy_release.yml b/.github/workflows/deploy_release.yml index adcfd88d41..975f8aa5b8 100644 --- a/.github/workflows/deploy_release.yml +++ b/.github/workflows/deploy_release.yml @@ -14,6 +14,13 @@ jobs: matrix: python-version: [3.9] + environment: + name: pypi + url: https://pypi.org/p/amici + + permissions: + id-token: write + steps: - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 @@ -40,8 +47,6 @@ jobs: - name: Publish a Python distribution to PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: - user: __token__ - password: ${{ secrets.pypi_password }} packages-dir: python/sdist/dist bioSimulatorsUpdateCliAndDockerImage: From 239b90518ceb1544983d83b1383298c2d5664ddf Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Wed, 17 Jan 2024 13:35:40 +0100 Subject: [PATCH 003/188] Update copyright notice (#2267) --- LICENSE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE.md b/LICENSE.md index 4dadbc28a9..a086d45955 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -5,7 +5,7 @@ AMICI is released under the 3-Clause BSD License (BSD-3-Clause) with the following terms: -Copyright (c) 2015-2020, Fabian Fröhlich, Jan Hasenauer, Daniel Weindl and Paul Stapor +Copyright (c) 2015-2024, AMICI Developers. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: From 23e0cd39c16d204cfe3d5474194692f131ac67de Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Sun, 21 Jan 2024 19:45:21 +0100 Subject: [PATCH 004/188] Require pyarrow (#2272) Avoids ``` .tox\unit\lib\site-packages\pandas\__init__.py:249: in warnings.warn( E DeprecationWarning: E Pyarrow will become a required dependency of pandas in the next major release of pandas (pandas 3.0), E (to allow more performant data types, such as the Arrow string type, and better interoperability with other libraries) E but was not found to be installed on your system. E If this would cause problems for you, E please provide us feedback at https://github.com/pandas-dev/pandas/issues/54466 ``` --- python/sdist/setup.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/python/sdist/setup.cfg b/python/sdist/setup.cfg index cc82ef08de..009d622a6b 100644 --- a/python/sdist/setup.cfg +++ b/python/sdist/setup.cfg @@ -35,6 +35,7 @@ install_requires = numpy; python_version>='3.12' python-libsbml pandas>=2.0.2 + pyarrow wurlitzer toposort setuptools>=48 From 75a5cdc66dc8a49ad0951c07fdaad11a47e99cbd Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Sun, 21 Jan 2024 19:46:12 +0100 Subject: [PATCH 005/188] Doc: Exhale 0.3.7 (#2273) --- .readthedocs.yml | 1 - documentation/rtd_requirements.txt | 2 +- documentation/rtd_requirements2.txt | 1 - tox.ini | 1 - 4 files changed, 1 insertion(+), 4 deletions(-) delete mode 100644 documentation/rtd_requirements2.txt diff --git a/.readthedocs.yml b/.readthedocs.yml index 38c2e8e41b..23cc6addeb 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -17,7 +17,6 @@ formats: python: install: - requirements: documentation/rtd_requirements.txt - - requirements: documentation/rtd_requirements2.txt build: os: "ubuntu-22.04" apt_packages: diff --git a/documentation/rtd_requirements.txt b/documentation/rtd_requirements.txt index ae49f9dfbb..8d2c2100f9 100644 --- a/documentation/rtd_requirements.txt +++ b/documentation/rtd_requirements.txt @@ -13,7 +13,7 @@ sphinx-autodoc-typehints git+https://github.com/readthedocs/sphinx-hoverxref@main ipython>=8.13.2 breathe>=4.35.0 -#exhale>=0.3.5 +exhale>=0.3.7 -e git+https://github.com/mithro/sphinx-contrib-mithro#egg=sphinx-contrib-exhale-multiproject&subdirectory=sphinx-contrib-exhale-multiproject sphinxcontrib-matlabdomain>=0.20.0 sphinxcontrib-napoleon>=0.7 diff --git a/documentation/rtd_requirements2.txt b/documentation/rtd_requirements2.txt deleted file mode 100644 index 2c307f0ea4..0000000000 --- a/documentation/rtd_requirements2.txt +++ /dev/null @@ -1 +0,0 @@ --e git+https://github.com/svenevs/exhale.git@a1a8551321e246e3ab81f5456e04a8159804595b#egg=exhale diff --git a/tox.ini b/tox.ini index a61ee8a6e2..9f060a7cf0 100644 --- a/tox.ini +++ b/tox.ini @@ -13,7 +13,6 @@ description = Build documentation deps = -r documentation/rtd_requirements.txt - -r documentation/rtd_requirements2.txt # don't install the package, this is already handled by `deps` above skip_install = true change_dir = documentation/ From 5c27c3449036a0cc0759ed19a0235a74cb8b75c5 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Mon, 22 Jan 2024 09:20:07 +0100 Subject: [PATCH 006/188] GHA: Use all available cores for compilation (#2271) https://github.blog/2024-01-17-github-hosted-runners-double-the-power-for-open-source/ --- .github/workflows/test_benchmark_collection_models.yml | 4 ++-- .github/workflows/test_performance.yml | 2 +- .github/workflows/test_petab_test_suite.yml | 2 +- .github/workflows/test_sbml_semantic_test_suite.yml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test_benchmark_collection_models.yml b/.github/workflows/test_benchmark_collection_models.yml index bf9509e5ce..3bf4cc3c1e 100644 --- a/.github/workflows/test_benchmark_collection_models.yml +++ b/.github/workflows/test_benchmark_collection_models.yml @@ -49,7 +49,7 @@ jobs: - name: Install AMICI sdist run: | pip3 install --user petab[vis] && \ - AMICI_PARALLEL_COMPILE=2 pip3 install -v --user \ + AMICI_PARALLEL_COMPILE="" pip3 install -v --user \ $(ls -t python/sdist/dist/amici-*.tar.gz | head -1)[petab,test,vis] # retrieve test models @@ -57,7 +57,7 @@ jobs: run: | git clone --depth 1 https://github.com/benchmarking-initiative/Benchmark-Models-PEtab.git \ && export BENCHMARK_COLLECTION="$(pwd)/Benchmark-Models-PEtab/Benchmark-Models/" \ - && AMICI_PARALLEL_COMPILE=2 tests/benchmark-models/test_benchmark_collection.sh + && AMICI_PARALLEL_COMPILE="" tests/benchmark-models/test_benchmark_collection.sh # run gradient checks - name: Run Gradient Checks diff --git a/.github/workflows/test_performance.yml b/.github/workflows/test_performance.yml index de9dd686d9..c306e0333a 100644 --- a/.github/workflows/test_performance.yml +++ b/.github/workflows/test_performance.yml @@ -51,7 +51,7 @@ jobs: - name: Install AMICI sdist run: | - AMICI_PARALLEL_COMPILE=2 check_time.sh \ + AMICI_PARALLEL_COMPILE="" check_time.sh \ install_sdist pip3 install -v --user \ $(ls -t python/sdist/dist/amici-*.tar.gz | head -1) diff --git a/.github/workflows/test_petab_test_suite.yml b/.github/workflows/test_petab_test_suite.yml index 9b48f21d95..a4791536e9 100644 --- a/.github/workflows/test_petab_test_suite.yml +++ b/.github/workflows/test_petab_test_suite.yml @@ -78,7 +78,7 @@ jobs: - name: Run PEtab test suite run: | source ./build/venv/bin/activate \ - && AMICI_PARALLEL_COMPILE=2 pytest -v \ + && AMICI_PARALLEL_COMPILE="" pytest -v \ --cov-report=xml:coverage.xml \ --cov-append \ --cov=amici \ diff --git a/.github/workflows/test_sbml_semantic_test_suite.yml b/.github/workflows/test_sbml_semantic_test_suite.yml index 2af34d3762..ee123a233b 100644 --- a/.github/workflows/test_sbml_semantic_test_suite.yml +++ b/.github/workflows/test_sbml_semantic_test_suite.yml @@ -44,8 +44,8 @@ jobs: - name: Install apt dependencies uses: ./.github/actions/install-apt-dependencies - - run: AMICI_PARALLEL_COMPILE=2 ./scripts/installAmiciSource.sh - - run: AMICI_PARALLEL_COMPILE=2 ./scripts/run-SBMLTestsuite.sh ${{ matrix.cases }} + - run: AMICI_PARALLEL_COMPILE="" ./scripts/installAmiciSource.sh + - run: AMICI_PARALLEL_COMPILE="" ./scripts/run-SBMLTestsuite.sh ${{ matrix.cases }} - name: "Upload artifact: SBML semantic test suite results" uses: actions/upload-artifact@v3 From 786b58606dea53b1abcc2334b11b6724ff88c360 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Tue, 23 Jan 2024 10:56:39 +0100 Subject: [PATCH 007/188] Update valgrind suppressions (#2274) * Update valgrind suppressions * .. * .. --- python/tests/valgrind-python.supp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/python/tests/valgrind-python.supp b/python/tests/valgrind-python.supp index 9cd3f5de3b..b4e0177033 100644 --- a/python/tests/valgrind-python.supp +++ b/python/tests/valgrind-python.supp @@ -547,6 +547,14 @@ ... } +{ + Pandas 2.2.0 + Memcheck:Leak + match-leak-kinds: definite + fun:realloc + obj:*site-packages/pandas/* +} + { Scipy extensions Memcheck:Leak @@ -728,6 +736,16 @@ fun:_PyImport_FindSharedFuncptr } +{ + _dl_catch_exception + Memcheck:Addr8 + fun:strncmp + fun:is_dst + ... + fun:_dl_catch_exception + ... +} + { Python dictkeys_get_index Memcheck:Value8 From 35cd14e6d9a3e4902c37c72c882c15dc12a7d483 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Wed, 24 Jan 2024 19:29:50 +0100 Subject: [PATCH 008/188] Fix Solver copyctor issues with swig4.2 (#2276) * Fix Solver copyctor issues with swig4.2 Closes #2275 --- scripts/downloadAndBuildSwig.sh | 8 ++++---- swig/solver_cvodes.i | 11 +++++++++++ swig/solver_idas.i | 11 +++++++++++ tests/cpp/unittests/testMisc.cpp | 7 +++++++ 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/scripts/downloadAndBuildSwig.sh b/scripts/downloadAndBuildSwig.sh index 261192f667..29ce32890b 100755 --- a/scripts/downloadAndBuildSwig.sh +++ b/scripts/downloadAndBuildSwig.sh @@ -18,13 +18,13 @@ cd "${AMICI_PATH}/ThirdParty/" if [[ ! -d ${SWIG_DIR} ]]; then if [[ ! -f ${SWIG_ARCHIVE} ]] - then wget ${SWIG_URL} + then wget "${SWIG_URL}" fi - tar -xzf ${SWIG_ARCHIVE} + tar -xzf "${SWIG_ARCHIVE}" fi -cd ${SWIG_DIR} +cd "${SWIG_DIR}" ./configure \ --prefix="${PREFIX}" \ --without-alllang \ @@ -38,6 +38,6 @@ echo "================" echo "SWIG installation successful" echo echo "To use this version of SWIG, add directory ${SWIG_BIN_DIR} to your PATH," -echo "e.g. adding the following line to your .bashrc:" +echo "e.g., adding the following line to your ~/.bashrc:" echo " export PATH=${SWIG_BIN_DIR}:\$PATH" echo "================" diff --git a/swig/solver_cvodes.i b/swig/solver_cvodes.i index 52feb1f4dd..7e2c28a17e 100644 --- a/swig/solver_cvodes.i +++ b/swig/solver_cvodes.i @@ -9,5 +9,16 @@ using namespace amici; %newobject amici::CVodeSolver::clone; %feature("notabstract") amici::CVodeSolver; +// Required with SWIG 4.2.0 https://github.com/AMICI-dev/AMICI/issues/2275 +%extend amici::CVodeSolver { + CVodeSolver() { + return new CVodeSolver(); + } + + CVodeSolver(Solver const& solver) { + return new CVodeSolver(dynamic_cast(solver)); + } +} + // Process symbols in header %include "amici/solver_cvodes.h" diff --git a/swig/solver_idas.i b/swig/solver_idas.i index b4a49a836f..7ec9f663bb 100644 --- a/swig/solver_idas.i +++ b/swig/solver_idas.i @@ -9,5 +9,16 @@ using namespace amici; %newobject amici::IDASolver::clone; %feature("notabstract") amici::IDASolver; +// Required for SWIG 4.2.0 https://github.com/AMICI-dev/AMICI/issues/2275 +%extend amici::IDASolver { + IDASolver() { + return new IDASolver(); + } + + IDASolver(Solver const& solver) { + return new IDASolver(dynamic_cast(solver)); + } +} + // Process symbols in header %include "amici/solver_idas.h" diff --git a/tests/cpp/unittests/testMisc.cpp b/tests/cpp/unittests/testMisc.cpp index f18de96b79..1f464b3433 100644 --- a/tests/cpp/unittests/testMisc.cpp +++ b/tests/cpp/unittests/testMisc.cpp @@ -261,6 +261,13 @@ TEST(SolverIdasTest, DefaultConstructableAndNotLeaky) IDASolver solver; } +TEST(SolverIdasTest, CopyCtor) +{ + IDASolver solver1; + IDASolver solver2(solver1); +} + + class SolverTest : public ::testing::Test { protected: From 854eac523deffa36dfc28b5622d7b5f60a30495f Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Thu, 25 Jan 2024 09:35:14 +0100 Subject: [PATCH 009/188] GHA: update actions to use Node.js 20 (#2278) * GHA: update actions to use Node.js 20 Fixes this warnings: > Node.js 16 actions are deprecated. Please update the following actions to use Node.js 20: actions/setup-python@v4, actions/checkout@v3, actions/upload-artifact@v3 Closes #2277 * unique artifact names * codecov-action@v3.1.4 --- .github/workflows/deploy_branch.yml | 6 ++-- .github/workflows/deploy_protected.yml | 4 +-- .github/workflows/deploy_release.yml | 4 +-- .../test_benchmark_collection_models.yml | 8 +++--- .github/workflows/test_doc.yml | 8 +++--- .github/workflows/test_install.yml | 12 ++++---- .github/workflows/test_matlab.yml | 2 +- .github/workflows/test_performance.yml | 6 ++-- .github/workflows/test_petab_test_suite.yml | 6 ++-- .github/workflows/test_pypi.yml | 2 +- .github/workflows/test_python_cplusplus.yml | 28 +++++++++---------- .github/workflows/test_python_ver_matrix.yml | 4 +-- .../test_sbml_semantic_test_suite.yml | 10 +++---- .github/workflows/test_valgrind.yml | 8 +++--- .github/workflows/test_windows.yml | 6 ++-- 15 files changed, 57 insertions(+), 57 deletions(-) diff --git a/.github/workflows/deploy_branch.yml b/.github/workflows/deploy_branch.yml index 82d1452d37..ff1be3145d 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@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 20 @@ -31,7 +31,7 @@ jobs: scripts/buildSdist.sh - name: "Upload artifact: sdist" - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: sdist path: python/sdist/dist/amici-*.gz diff --git a/.github/workflows/deploy_protected.yml b/.github/workflows/deploy_protected.yml index cd9257818d..c520896a70 100644 --- a/.github/workflows/deploy_protected.yml +++ b/.github/workflows/deploy_protected.yml @@ -25,10 +25,10 @@ jobs: steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: git archive -o container/amici.tar.gz --format=tar.gz HEAD - name: Set up QEMU uses: docker/setup-qemu-action@v2 diff --git a/.github/workflows/deploy_release.yml b/.github/workflows/deploy_release.yml index 975f8aa5b8..de7a073cc2 100644 --- a/.github/workflows/deploy_release.yml +++ b/.github/workflows/deploy_release.yml @@ -23,11 +23,11 @@ jobs: steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 20 diff --git a/.github/workflows/test_benchmark_collection_models.yml b/.github/workflows/test_benchmark_collection_models.yml index 3bf4cc3c1e..ab29938a12 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@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 20 @@ -66,9 +66,9 @@ jobs: && cd tests/benchmark-models && pytest ./test_petab_benchmark.py # upload results - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: - name: computation times + name: computation-times-${{ matrix.python-version }}-${{ matrix.extract_subexpressions }} path: | tests/benchmark-models/computation_times.csv tests/benchmark-models/computation_times.png diff --git a/.github/workflows/test_doc.yml b/.github/workflows/test_doc.yml index 7a60aa99d5..a4fe10de22 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@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: git fetch --prune --unshallow - name: Set up doxygen @@ -47,11 +47,11 @@ jobs: steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - 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 166616b434..0c624a33e5 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@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: git fetch --prune --unshallow - run: echo "AMICI_DIR=$(pwd)" >> $GITHUB_ENV @@ -54,11 +54,11 @@ jobs: steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: git fetch --prune --unshallow - run: echo "AMICI_DIR=$(pwd)" >> $GITHUB_ENV @@ -87,11 +87,11 @@ jobs: steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - 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 914ce19f09..46185e9d45 100644 --- a/.github/workflows/test_matlab.yml +++ b/.github/workflows/test_matlab.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - 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 c306e0333a..c02245224c 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@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 20 @@ -64,7 +64,7 @@ jobs: AMICI_IMPORT_NPROCS=2 check_time.sh petab_import python tests/performance/test.py import - name: "Upload artifact: CS_Signalling_ERBB_RAS_AKT_petab" - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: model_performance_test path: model_performance_test diff --git a/.github/workflows/test_petab_test_suite.yml b/.github/workflows/test_petab_test_suite.yml index a4791536e9..c167c927fa 100644 --- a/.github/workflows/test_petab_test_suite.yml +++ b/.github/workflows/test_petab_test_suite.yml @@ -26,11 +26,11 @@ jobs: steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 20 @@ -86,7 +86,7 @@ jobs: - name: Codecov if: github.event_name == 'pull_request' || github.repository_owner == 'AMICI-dev' - uses: codecov/codecov-action@v3.1.0 + uses: codecov/codecov-action@v3.1.4 with: token: ${{ secrets.CODECOV_TOKEN }} file: coverage.xml diff --git a/.github/workflows/test_pypi.yml b/.github/workflows/test_pypi.yml index 68675c578a..fecda3edb0 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@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} diff --git a/.github/workflows/test_python_cplusplus.yml b/.github/workflows/test_python_cplusplus.yml index 5aa47173b8..10ba5b0a0f 100644 --- a/.github/workflows/test_python_cplusplus.yml +++ b/.github/workflows/test_python_cplusplus.yml @@ -19,11 +19,11 @@ jobs: steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: git fetch --prune --unshallow - run: echo "AMICI_DIR=$(pwd)" >> $GITHUB_ENV @@ -67,7 +67,7 @@ jobs: - name: Codecov Python if: github.event_name == 'pull_request' || github.repository_owner == 'AMICI-dev' - uses: codecov/codecov-action@v3.1.0 + uses: codecov/codecov-action@v3.1.4 with: token: ${{ secrets.CODECOV_TOKEN }} file: build/coverage_py.xml @@ -87,7 +87,7 @@ jobs: - name: Codecov CPP if: github.event_name == 'pull_request' || github.repository_owner == 'AMICI-dev' - uses: codecov/codecov-action@v3.1.0 + uses: codecov/codecov-action@v3.1.4 with: token: ${{ secrets.CODECOV_TOKEN }} file: coverage.info @@ -114,11 +114,11 @@ jobs: steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: git fetch --prune --unshallow - run: echo "AMICI_DIR=$(pwd)" >> $GITHUB_ENV @@ -142,7 +142,7 @@ jobs: - name: Codecov Python if: github.event_name == 'pull_request' || github.repository_owner == 'AMICI-dev' - uses: codecov/codecov-action@v3.1.0 + uses: codecov/codecov-action@v3.1.4 with: token: ${{ secrets.CODECOV_TOKEN }} file: build/coverage_py.xml @@ -162,7 +162,7 @@ jobs: - name: Codecov CPP if: github.event_name == 'pull_request' || github.repository_owner == 'AMICI-dev' - uses: codecov/codecov-action@v3.1.0 + uses: codecov/codecov-action@v3.1.4 with: token: ${{ secrets.CODECOV_TOKEN }} file: coverage.info @@ -190,11 +190,11 @@ jobs: steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: git fetch --prune --unshallow - run: echo "AMICI_DIR=$(pwd)" >> $GITHUB_ENV @@ -224,11 +224,11 @@ jobs: steps: - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.9 - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: git fetch --prune --unshallow - name: Install dependencies @@ -266,11 +266,11 @@ jobs: steps: - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.9 - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: git fetch --prune --unshallow - name: Install dependencies diff --git a/.github/workflows/test_python_ver_matrix.yml b/.github/workflows/test_python_ver_matrix.yml index 9290cd0c1a..5f9d96d295 100644 --- a/.github/workflows/test_python_ver_matrix.yml +++ b/.github/workflows/test_python_ver_matrix.yml @@ -33,11 +33,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@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 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 ee123a233b..df7fd4a01e 100644 --- a/.github/workflows/test_sbml_semantic_test_suite.yml +++ b/.github/workflows/test_sbml_semantic_test_suite.yml @@ -33,11 +33,11 @@ jobs: steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 1 @@ -48,14 +48,14 @@ jobs: - run: AMICI_PARALLEL_COMPILE="" ./scripts/run-SBMLTestsuite.sh ${{ matrix.cases }} - name: "Upload artifact: SBML semantic test suite results" - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: amici-semantic-results + name: amici-semantic-results-${{ matrix.cases }} path: tests/amici-semantic-results - name: Codecov SBMLSuite if: github.event_name == 'pull_request' || github.repository_owner == 'AMICI-dev' - uses: codecov/codecov-action@v3.1.0 + uses: codecov/codecov-action@v3.1.4 with: token: ${{ secrets.CODECOV_TOKEN }} file: coverage_SBMLSuite.xml diff --git a/.github/workflows/test_valgrind.yml b/.github/workflows/test_valgrind.yml index 32eea2e0c0..034945d461 100644 --- a/.github/workflows/test_valgrind.yml +++ b/.github/workflows/test_valgrind.yml @@ -25,11 +25,11 @@ jobs: steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: git fetch --prune --unshallow - name: Install apt dependencies @@ -64,11 +64,11 @@ jobs: steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: git fetch --prune --unshallow - name: Install apt dependencies diff --git a/.github/workflows/test_windows.yml b/.github/workflows/test_windows.yml index 9dc13efbea..fbd91ce73a 100644 --- a/.github/workflows/test_windows.yml +++ b/.github/workflows/test_windows.yml @@ -31,11 +31,11 @@ jobs: steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: git fetch --prune --unshallow - shell: bash @@ -61,7 +61,7 @@ jobs: shell: powershell run: scripts/installOpenBLAS - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: OpenBLAS path: C:\BLAS\OpenBLAS From aa39743e794837461fe3286d52b5257f26ddee6b Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Tue, 30 Jan 2024 21:37:28 +0100 Subject: [PATCH 010/188] Fix amici.ExpData() (#2280) Fix `IndexError: tuple index out of range` when calling `amici.ExpData` without any argument. --- python/sdist/amici/swig_wrappers.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/python/sdist/amici/swig_wrappers.py b/python/sdist/amici/swig_wrappers.py index e16d4c2580..e942cc76dd 100644 --- a/python/sdist/amici/swig_wrappers.py +++ b/python/sdist/amici/swig_wrappers.py @@ -136,17 +136,22 @@ def ExpData(*args) -> "amici_swig.ExpData": :returns: ExpData Instance """ + if not args: + return amici_swig.ExpData() + if isinstance(args[0], numpy.ReturnDataView): return amici_swig.ExpData(_get_ptr(args[0]["ptr"]), *args[1:]) - elif isinstance(args[0], (amici_swig.ExpData, amici_swig.ExpDataPtr)): + + if isinstance(args[0], (amici_swig.ExpData, amici_swig.ExpDataPtr)): # the *args[:1] should be empty, but by the time you read this, # the constructor signature may have changed, and you are glad this # wrapper did not break. return amici_swig.ExpData(_get_ptr(args[0]), *args[1:]) - elif isinstance(args[0], (amici_swig.Model, amici_swig.ModelPtr)): + + if isinstance(args[0], (amici_swig.Model, amici_swig.ModelPtr)): return amici_swig.ExpData(_get_ptr(args[0])) - else: - return amici_swig.ExpData(*args) + + return amici_swig.ExpData(*args) def runAmiciSimulations( From 51b021947494b4eb26b34d12440f227691ed88a0 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Wed, 31 Jan 2024 21:08:16 +0100 Subject: [PATCH 011/188] GHA: codecov/codecov-action@v4 (#2284) > PRs made from forks to the upstream public repos will support tokenless (e.g. contributors to OS projects do not need the upstream repo's Codecov token) --- .github/workflows/test_petab_test_suite.yml | 3 +-- .github/workflows/test_python_cplusplus.yml | 12 ++++-------- .github/workflows/test_sbml_semantic_test_suite.yml | 3 +-- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/.github/workflows/test_petab_test_suite.yml b/.github/workflows/test_petab_test_suite.yml index c167c927fa..1af341009f 100644 --- a/.github/workflows/test_petab_test_suite.yml +++ b/.github/workflows/test_petab_test_suite.yml @@ -85,8 +85,7 @@ jobs: tests/petab_test_suite/ - name: Codecov - if: github.event_name == 'pull_request' || github.repository_owner == 'AMICI-dev' - uses: codecov/codecov-action@v3.1.4 + uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} file: coverage.xml diff --git a/.github/workflows/test_python_cplusplus.yml b/.github/workflows/test_python_cplusplus.yml index 10ba5b0a0f..507283b156 100644 --- a/.github/workflows/test_python_cplusplus.yml +++ b/.github/workflows/test_python_cplusplus.yml @@ -66,8 +66,7 @@ jobs: ${AMICI_DIR}/python/tests/test_splines.py - name: Codecov Python - if: github.event_name == 'pull_request' || github.repository_owner == 'AMICI-dev' - uses: codecov/codecov-action@v3.1.4 + uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} file: build/coverage_py.xml @@ -86,8 +85,7 @@ jobs: && lcov -a coverage_cpp.info -a coverage_py.info -o coverage.info - name: Codecov CPP - if: github.event_name == 'pull_request' || github.repository_owner == 'AMICI-dev' - uses: codecov/codecov-action@v3.1.4 + uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} file: coverage.info @@ -141,8 +139,7 @@ jobs: ${AMICI_DIR}/python/tests/test_splines_short.py - name: Codecov Python - if: github.event_name == 'pull_request' || github.repository_owner == 'AMICI-dev' - uses: codecov/codecov-action@v3.1.4 + uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} file: build/coverage_py.xml @@ -161,8 +158,7 @@ jobs: && lcov -a coverage_cpp.info -a coverage_py.info -o coverage.info - name: Codecov CPP - if: github.event_name == 'pull_request' || github.repository_owner == 'AMICI-dev' - uses: codecov/codecov-action@v3.1.4 + uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} file: coverage.info diff --git a/.github/workflows/test_sbml_semantic_test_suite.yml b/.github/workflows/test_sbml_semantic_test_suite.yml index df7fd4a01e..cf03a5d458 100644 --- a/.github/workflows/test_sbml_semantic_test_suite.yml +++ b/.github/workflows/test_sbml_semantic_test_suite.yml @@ -54,8 +54,7 @@ jobs: path: tests/amici-semantic-results - name: Codecov SBMLSuite - if: github.event_name == 'pull_request' || github.repository_owner == 'AMICI-dev' - uses: codecov/codecov-action@v3.1.4 + uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} file: coverage_SBMLSuite.xml From 7946710fc68d16fac3ce5038e228627f5a93a7c5 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Wed, 31 Jan 2024 22:22:13 +0100 Subject: [PATCH 012/188] clang-format, cmake-format (#2283) Some formatting changed with clang17. Re-run clang-format and cmake-format. --- CMakeLists.txt | 5 +- include/amici/abstract_model.h | 84 +++---- include/amici/backwardproblem.h | 2 +- include/amici/misc.h | 4 +- include/amici/model.h | 56 ++--- include/amici/model_dae.h | 16 +- include/amici/model_ode.h | 10 +- include/amici/rdata.h | 2 +- include/amici/returndata_matlab.h | 2 +- include/amici/serialization.h | 298 ++++++++++++------------ include/amici/simulation_parameters.h | 12 +- include/amici/solver.h | 9 +- include/amici/solver_cvodes.h | 2 +- include/amici/solver_idas.h | 2 +- include/amici/splinefunctions.h | 14 +- include/amici/sundials_matrix_wrapper.h | 12 +- src/abstract_model.cpp | 60 ++--- src/edata.cpp | 10 +- src/forwardproblem.cpp | 5 +- src/hdf5.cpp | 6 +- src/interface_matlab.cpp | 28 +-- src/model.cpp | 88 +++---- src/model_dae.cpp | 40 ++-- src/model_ode.cpp | 44 ++-- src/rdata.cpp | 2 +- src/solver.cpp | 40 ++-- src/solver_cvodes.cpp | 46 ++-- src/solver_idas.cpp | 44 ++-- src/splinefunctions.cpp | 14 +- src/sundials_matrix_wrapper.cpp | 12 +- swig/CMakeLists.txt | 7 +- 31 files changed, 494 insertions(+), 482 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d6f08a5097..8b6c9140e9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,8 +130,9 @@ set(VENDORED_SUNDIALS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ThirdParty/sundials) set(VENDORED_SUNDIALS_BUILD_DIR ${VENDORED_SUNDIALS_DIR}/build) set(VENDORED_SUNDIALS_INSTALL_DIR ${VENDORED_SUNDIALS_BUILD_DIR}) set(SUNDIALS_PRIVATE_INCLUDE_DIRS "${VENDORED_SUNDIALS_DIR}/src") -find_package(SUNDIALS REQUIRED PATHS - "${VENDORED_SUNDIALS_INSTALL_DIR}/${CMAKE_INSTALL_LIBDIR}/cmake/sundials/") +find_package( + SUNDIALS REQUIRED PATHS + "${VENDORED_SUNDIALS_INSTALL_DIR}/${CMAKE_INSTALL_LIBDIR}/cmake/sundials/") message(STATUS "Found SUNDIALS: ${SUNDIALS_DIR}") set(GSL_LITE_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ThirdParty/gsl") diff --git a/include/amici/abstract_model.h b/include/amici/abstract_model.h index bb824577b9..d88347078d 100644 --- a/include/amici/abstract_model.h +++ b/include/amici/abstract_model.h @@ -41,7 +41,7 @@ class AbstractModel { * @param root array to which values of the root function will be written */ virtual void froot( - const realtype t, AmiVector const& x, AmiVector const& dx, + realtype const t, AmiVector const& x, AmiVector const& dx, gsl::span root ) = 0; @@ -54,7 +54,7 @@ class AbstractModel { * written */ virtual void fxdot( - const realtype t, AmiVector const& x, AmiVector const& dx, + realtype const t, AmiVector const& x, AmiVector const& dx, AmiVector& xdot ) = 0; @@ -70,7 +70,7 @@ class AbstractModel { * will be written */ virtual void fsxdot( - const realtype t, AmiVector const& x, AmiVector const& dx, int ip, + realtype const t, AmiVector const& x, AmiVector const& dx, int ip, AmiVector const& sx, AmiVector const& sdx, AmiVector& sxdot ) = 0; @@ -83,7 +83,7 @@ class AbstractModel { * written */ virtual void fxBdot_ss( - const realtype t, AmiVector const& xB, AmiVector const& dxB, + realtype const t, AmiVector const& xB, AmiVector const& dxB, AmiVector& xBdot ) = 0; @@ -105,7 +105,7 @@ class AbstractModel { * @param xBdot Vector with the adjoint state right hand side */ virtual void writeSteadystateJB( - const realtype t, realtype cj, AmiVector const& x, AmiVector const& dx, + realtype const t, realtype cj, AmiVector const& x, AmiVector const& dx, AmiVector const& xB, AmiVector const& dxB, AmiVector const& xBdot ) = 0; @@ -119,7 +119,7 @@ class AbstractModel { * @param J dense matrix to which values of the jacobian will be written */ virtual void - fJ(const realtype t, realtype cj, AmiVector const& x, AmiVector const& dx, + fJ(realtype const t, realtype cj, AmiVector const& x, AmiVector const& dx, AmiVector const& xdot, SUNMatrix J) = 0; @@ -135,7 +135,7 @@ class AbstractModel { * @param JB dense matrix to which values of the jacobian will be written */ virtual void - fJB(const realtype t, realtype cj, AmiVector const& x, AmiVector const& dx, + fJB(realtype const t, realtype cj, AmiVector const& x, AmiVector const& dx, AmiVector const& xB, AmiVector const& dxB, AmiVector const& xBdot, SUNMatrix JB) = 0; @@ -150,7 +150,7 @@ class AbstractModel { * @param J sparse matrix to which values of the Jacobian will be written */ virtual void fJSparse( - const realtype t, realtype cj, AmiVector const& x, AmiVector const& dx, + realtype const t, realtype cj, AmiVector const& x, AmiVector const& dx, AmiVector const& xdot, SUNMatrix J ) = 0; @@ -166,7 +166,7 @@ class AbstractModel { * @param JB dense matrix to which values of the jacobian will be written */ virtual void fJSparseB( - const realtype t, realtype cj, AmiVector const& x, AmiVector const& dx, + realtype const t, realtype cj, AmiVector const& x, AmiVector const& dx, AmiVector const& xB, AmiVector const& dxB, AmiVector const& xBdot, SUNMatrix JB ) = 0; @@ -180,7 +180,7 @@ class AbstractModel { * @param dx time derivative of state (DAE only) */ virtual void fJDiag( - const realtype t, AmiVector& Jdiag, realtype cj, AmiVector const& x, + realtype const t, AmiVector& Jdiag, realtype cj, AmiVector const& x, AmiVector const& dx ) = 0; @@ -192,7 +192,7 @@ class AbstractModel { * @param dx time derivative of state (DAE only) */ virtual void - fdxdotdp(const realtype t, AmiVector const& x, AmiVector const& dx) + fdxdotdp(realtype const t, AmiVector const& x, AmiVector const& dx) = 0; /** @@ -206,7 +206,7 @@ class AbstractModel { * @param cj scaling factor (inverse of timestep, DAE only) */ virtual void - fJv(const realtype t, AmiVector const& x, AmiVector const& dx, + fJv(realtype const t, AmiVector const& x, AmiVector const& dx, AmiVector const& xdot, AmiVector const& v, AmiVector& nJv, realtype cj) = 0; @@ -230,7 +230,7 @@ class AbstractModel { * @param k constant vector */ virtual void - fx0(realtype* x0, const realtype t, realtype const* p, realtype const* k); + fx0(realtype* x0, realtype const t, realtype const* p, realtype const* k); /** * @brief Function indicating whether reinitialization of states depending @@ -250,7 +250,7 @@ class AbstractModel { * based on provided constants / fixed parameters. */ virtual void fx0_fixedParameters( - realtype* x0, const realtype t, realtype const* p, realtype const* k, + realtype* x0, realtype const t, realtype const* p, realtype const* k, gsl::span reinitialization_state_idxs ); @@ -266,7 +266,7 @@ class AbstractModel { * based on provided constants / fixed parameters. */ virtual void fsx0_fixedParameters( - realtype* sx0, const realtype t, realtype const* x0, realtype const* p, + realtype* sx0, realtype const t, realtype const* x0, realtype const* p, realtype const* k, int ip, gsl::span reinitialization_state_idxs ); @@ -281,7 +281,7 @@ class AbstractModel { * @param ip sensitivity index */ virtual void fsx0( - realtype* sx0, const realtype t, realtype const* x0, realtype const* p, + realtype* sx0, realtype const t, realtype const* x0, realtype const* p, realtype const* k, int ip ); @@ -308,7 +308,7 @@ class AbstractModel { * @param ie event index */ virtual void fstau( - realtype* stau, const realtype t, realtype const* x, realtype const* p, + realtype* stau, realtype const t, realtype const* x, realtype const* p, realtype const* k, realtype const* h, realtype const* tcl, realtype const* sx, int ip, int ie ); @@ -324,7 +324,7 @@ class AbstractModel { * @param w repeating elements vector */ virtual void - fy(realtype* y, const realtype t, realtype const* x, realtype const* p, + fy(realtype* y, realtype const t, realtype const* x, realtype const* p, realtype const* k, realtype const* h, realtype const* w); /** @@ -340,7 +340,7 @@ class AbstractModel { * @param dwdp Recurring terms in xdot, parameter derivative */ virtual void fdydp( - realtype* dydp, const realtype t, realtype const* x, realtype const* p, + realtype* dydp, realtype const t, realtype const* x, realtype const* p, realtype const* k, realtype const* h, int ip, realtype const* w, realtype const* dwdp ); @@ -362,7 +362,7 @@ class AbstractModel { * \f$ */ virtual void fdydp( - realtype* dydp, const realtype t, realtype const* x, realtype const* p, + realtype* dydp, realtype const t, realtype const* x, realtype const* p, realtype const* k, realtype const* h, int ip, realtype const* w, realtype const* tcl, realtype const* dtcldp, realtype const* spl, realtype const* sspl @@ -380,7 +380,7 @@ class AbstractModel { * @param dwdx Recurring terms in xdot, state derivative */ virtual void fdydx( - realtype* dydx, const realtype t, realtype const* x, realtype const* p, + realtype* dydx, realtype const t, realtype const* x, realtype const* p, realtype const* k, realtype const* h, realtype const* w, realtype const* dwdx ); @@ -396,7 +396,7 @@ class AbstractModel { * @param h Heaviside vector */ virtual void - fz(realtype* z, int ie, const realtype t, realtype const* x, + fz(realtype* z, int ie, realtype const t, realtype const* x, realtype const* p, realtype const* k, realtype const* h); /** @@ -412,7 +412,7 @@ class AbstractModel { * @param ip sensitivity index */ virtual void - fsz(realtype* sz, int ie, const realtype t, realtype const* x, + fsz(realtype* sz, int ie, realtype const t, realtype const* x, realtype const* p, realtype const* k, realtype const* h, realtype const* sx, int ip); @@ -428,7 +428,7 @@ class AbstractModel { * @param h Heaviside vector */ virtual void - frz(realtype* rz, int ie, const realtype t, realtype const* x, + frz(realtype* rz, int ie, realtype const t, realtype const* x, realtype const* p, realtype const* k, realtype const* h); /** @@ -444,7 +444,7 @@ class AbstractModel { * @param ip sensitivity index */ virtual void fsrz( - realtype* srz, int ie, const realtype t, realtype const* x, + realtype* srz, int ie, realtype const t, realtype const* x, realtype const* p, realtype const* k, realtype const* h, realtype const* sx, int ip ); @@ -462,7 +462,7 @@ class AbstractModel { * @param ip parameter index w.r.t. which the derivative is requested */ virtual void fdzdp( - realtype* dzdp, int ie, const realtype t, realtype const* x, + realtype* dzdp, int ie, realtype const t, realtype const* x, realtype const* p, realtype const* k, realtype const* h, int ip ); @@ -478,7 +478,7 @@ class AbstractModel { * @param h Heaviside vector */ virtual void fdzdx( - realtype* dzdx, int ie, const realtype t, realtype const* x, + realtype* dzdx, int ie, realtype const t, realtype const* x, realtype const* p, realtype const* k, realtype const* h ); @@ -495,7 +495,7 @@ class AbstractModel { * @param ip parameter index w.r.t. which the derivative is requested */ virtual void fdrzdp( - realtype* drzdp, int ie, const realtype t, realtype const* x, + realtype* drzdp, int ie, realtype const t, realtype const* x, realtype const* p, realtype const* k, realtype const* h, int ip ); @@ -510,7 +510,7 @@ class AbstractModel { * @param h Heaviside vector */ virtual void fdrzdx( - realtype* drzdx, int ie, const realtype t, realtype const* x, + realtype* drzdx, int ie, realtype const t, realtype const* x, realtype const* p, realtype const* k, realtype const* h ); @@ -527,7 +527,7 @@ class AbstractModel { * @param xdot_old previous model right hand side */ virtual void fdeltax( - realtype* deltax, const realtype t, realtype const* x, + realtype* deltax, realtype const t, realtype const* x, realtype const* p, realtype const* k, realtype const* h, int ie, realtype const* xdot, realtype const* xdot_old ); @@ -550,7 +550,7 @@ class AbstractModel { * @param tcl total abundances for conservation laws */ virtual void fdeltasx( - realtype* deltasx, const realtype t, realtype const* x, + realtype* deltasx, realtype const t, realtype const* x, realtype const* p, realtype const* k, realtype const* h, realtype const* w, int ip, int ie, realtype const* xdot, realtype const* xdot_old, realtype const* sx, realtype const* stau, @@ -571,7 +571,7 @@ class AbstractModel { * @param xB current adjoint state */ virtual void fdeltaxB( - realtype* deltaxB, const realtype t, realtype const* x, + realtype* deltaxB, realtype const t, realtype const* x, realtype const* p, realtype const* k, realtype const* h, int ie, realtype const* xdot, realtype const* xdot_old, realtype const* xB ); @@ -591,7 +591,7 @@ class AbstractModel { * @param xB adjoint state */ virtual void fdeltaqB( - realtype* deltaqB, const realtype t, realtype const* x, + realtype* deltaqB, realtype const t, realtype const* x, realtype const* p, realtype const* k, realtype const* h, int ip, int ie, realtype const* xdot, realtype const* xdot_old, realtype const* xB ); @@ -605,7 +605,7 @@ class AbstractModel { * @param y model output at timepoint t */ virtual void fsigmay( - realtype* sigmay, const realtype t, realtype const* p, + realtype* sigmay, realtype const t, realtype const* p, realtype const* k, realtype const* y ); @@ -619,7 +619,7 @@ class AbstractModel { * @param ip sensitivity index */ virtual void fdsigmaydp( - realtype* dsigmaydp, const realtype t, realtype const* p, + realtype* dsigmaydp, realtype const t, realtype const* p, realtype const* k, realtype const* y, int ip ); /** @@ -632,7 +632,7 @@ class AbstractModel { * @param y model output at timepoint t */ virtual void fdsigmaydy( - realtype* dsigmaydy, const realtype t, realtype const* p, + realtype* dsigmaydy, realtype const t, realtype const* p, realtype const* k, realtype const* y ); @@ -644,7 +644,7 @@ class AbstractModel { * @param k constant vector */ virtual void fsigmaz( - realtype* sigmaz, const realtype t, realtype const* p, realtype const* k + realtype* sigmaz, realtype const t, realtype const* p, realtype const* k ); /** @@ -657,7 +657,7 @@ class AbstractModel { * @param ip sensitivity index */ virtual void fdsigmazdp( - realtype* dsigmazdp, const realtype t, realtype const* p, + realtype* dsigmazdp, realtype const t, realtype const* p, realtype const* k, int ip ); @@ -822,7 +822,7 @@ class AbstractModel { * @param spl spline value vector */ virtual void - fw(realtype* w, const realtype t, realtype const* x, realtype const* p, + fw(realtype* w, realtype const t, realtype const* x, realtype const* p, realtype const* k, realtype const* h, realtype const* tcl, realtype const* spl); @@ -842,7 +842,7 @@ class AbstractModel { * \f$ */ virtual void fdwdp( - realtype* dwdp, const realtype t, realtype const* x, realtype const* p, + realtype* dwdp, realtype const t, realtype const* x, realtype const* p, realtype const* k, realtype const* h, realtype const* w, realtype const* tcl, realtype const* stcl, realtype const* spl, realtype const* sspl @@ -876,7 +876,7 @@ class AbstractModel { * @param ip sensitivity parameter index */ virtual void fdwdp( - realtype* dwdp, const realtype t, realtype const* x, realtype const* p, + realtype* dwdp, realtype const t, realtype const* x, realtype const* p, realtype const* k, realtype const* h, realtype const* w, realtype const* tcl, realtype const* stcl, realtype const* spl, realtype const* sspl, int ip @@ -895,7 +895,7 @@ class AbstractModel { * @param spl spline value vector */ virtual void fdwdx( - realtype* dwdx, const realtype t, realtype const* x, realtype const* p, + realtype* dwdx, realtype const t, realtype const* x, realtype const* p, realtype const* k, realtype const* h, realtype const* w, realtype const* tcl, realtype const* spl ); diff --git a/include/amici/backwardproblem.h b/include/amici/backwardproblem.h index 1c26186c17..a5d32e3c47 100644 --- a/include/amici/backwardproblem.h +++ b/include/amici/backwardproblem.h @@ -134,7 +134,7 @@ class BackwardProblem { /** state derivative of data likelihood */ std::vector dJydx_; /** state derivative of event likelihood */ - const std::vector dJzdx_; + std::vector const dJzdx_; }; } // namespace amici diff --git a/include/amici/misc.h b/include/amici/misc.h index 6dc3294240..6874c19185 100644 --- a/include/amici/misc.h +++ b/include/amici/misc.h @@ -85,7 +85,7 @@ void checkBufferSize( * @param buffer buffer to which values are to be written */ template -void writeSlice(const gsl::span slice, gsl::span buffer) { +void writeSlice(gsl::span const slice, gsl::span buffer) { checkBufferSize(buffer, slice.size()); std::copy(slice.begin(), slice.end(), buffer.data()); }; @@ -97,7 +97,7 @@ void writeSlice(const gsl::span slice, gsl::span buffer) { * @param buffer buffer to which values are to be added */ template -void addSlice(const gsl::span slice, gsl::span buffer) { +void addSlice(gsl::span const slice, gsl::span buffer) { checkBufferSize(buffer, slice.size()); std::transform( slice.begin(), slice.end(), buffer.begin(), buffer.begin(), diff --git a/include/amici/model.h b/include/amici/model.h index 481164afdf..23f6ac63c8 100644 --- a/include/amici/model.h +++ b/include/amici/model.h @@ -92,7 +92,7 @@ enum class ModelQuantity { drzdx, }; -extern const std::map model_quantity_to_str; +extern std::map const model_quantity_to_str; /** * @brief The Model class represents an AMICI ODE/DAE model. @@ -918,7 +918,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param x Current state */ void - getExpression(gsl::span w, const realtype t, AmiVector const& x); + getExpression(gsl::span w, realtype const t, AmiVector const& x); /** * @brief Get time-resolved observables. @@ -927,7 +927,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param x Current state */ void - getObservable(gsl::span y, const realtype t, AmiVector const& x); + getObservable(gsl::span y, realtype const t, AmiVector const& x); /** * @brief Get scaling type for observable @@ -947,7 +947,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param sx State sensitivities */ void getObservableSensitivity( - gsl::span sy, const realtype t, AmiVector const& x, + gsl::span sy, realtype const t, AmiVector const& x, AmiVectorArray const& sx ); @@ -1045,7 +1045,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param x State variables */ void getEvent( - gsl::span z, int const ie, const realtype t, + gsl::span z, int const ie, realtype const t, AmiVector const& x ); /** @@ -1060,7 +1060,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param sx State sensitivities */ void getEventSensitivity( - gsl::span sz, int const ie, const realtype t, + gsl::span sz, int const ie, realtype const t, AmiVector const& x, AmiVectorArray const& sx ); @@ -1082,7 +1082,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param x State variables */ void getEventRegularization( - gsl::span rz, int const ie, const realtype t, + gsl::span rz, int const ie, realtype const t, AmiVector const& x ); @@ -1099,7 +1099,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param sx State sensitivities */ void getEventRegularizationSensitivity( - gsl::span srz, int const ie, const realtype t, + gsl::span srz, int const ie, realtype const t, AmiVector const& x, AmiVectorArray const& sx ); /** @@ -1113,7 +1113,7 @@ class Model : public AbstractModel, public ModelDimensions { */ void getEventSigma( gsl::span sigmaz, int const ie, int const nroots, - const realtype t, ExpData const* edata + realtype const t, ExpData const* edata ); /** @@ -1131,7 +1131,7 @@ class Model : public AbstractModel, public ModelDimensions { */ void getEventSigmaSensitivity( gsl::span ssigmaz, int const ie, int const nroots, - const realtype t, ExpData const* edata + realtype const t, ExpData const* edata ); /** @@ -1144,7 +1144,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param edata Experimental data */ void addEventObjective( - realtype& Jz, int const ie, int const nroots, const realtype t, + realtype& Jz, int const ie, int const nroots, realtype const t, AmiVector const& x, ExpData const& edata ); @@ -1158,7 +1158,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param edata Experimental data */ void addEventObjectiveRegularization( - realtype& Jrz, int const ie, int const nroots, const realtype t, + realtype& Jrz, int const ie, int const nroots, realtype const t, AmiVector const& x, ExpData const& edata ); @@ -1180,7 +1180,7 @@ class Model : public AbstractModel, public ModelDimensions { */ void addEventObjectiveSensitivity( std::vector& sllh, std::vector& s2llh, int const ie, - int const nroots, const realtype t, AmiVector const& x, + int const nroots, realtype const t, AmiVector const& x, AmiVectorArray const& sx, ExpData const& edata ); @@ -1201,7 +1201,7 @@ class Model : public AbstractModel, public ModelDimensions { */ void addPartialEventObjectiveSensitivity( std::vector& sllh, std::vector& s2llh, int const ie, - int const nroots, const realtype t, AmiVector const& x, + int const nroots, realtype const t, AmiVector const& x, ExpData const& edata ); @@ -1219,7 +1219,7 @@ class Model : public AbstractModel, public ModelDimensions { */ void getAdjointStateEventUpdate( gsl::span dJzdx, int const ie, int const nroots, - const realtype t, AmiVector const& x, ExpData const& edata + realtype const t, AmiVector const& x, ExpData const& edata ); /** @@ -1234,7 +1234,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param sx State sensitivities */ void getEventTimeSensitivity( - std::vector& stau, const realtype t, int const ie, + std::vector& stau, realtype const t, int const ie, AmiVector const& x, AmiVectorArray const& sx ); @@ -1247,7 +1247,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param xdot_old Value of residual function before event */ void addStateEventUpdate( - AmiVector& x, int const ie, const realtype t, AmiVector const& xdot, + AmiVector& x, int const ie, realtype const t, AmiVector const& xdot, AmiVector const& xdot_old ); @@ -1263,7 +1263,7 @@ class Model : public AbstractModel, public ModelDimensions { * `Model::getEventTimeSensitivity` */ void addStateSensitivityEventUpdate( - AmiVectorArray& sx, int const ie, const realtype t, + AmiVectorArray& sx, int const ie, realtype const t, AmiVector const& x_old, AmiVector const& xdot, AmiVector const& xdot_old, std::vector const& stau ); @@ -1278,7 +1278,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param xdot_old Value of residual function before event */ void addAdjointStateEventUpdate( - AmiVector& xB, int const ie, const realtype t, AmiVector const& x, + AmiVector& xB, int const ie, realtype const t, AmiVector const& x, AmiVector const& xdot, AmiVector const& xdot_old ); @@ -1293,7 +1293,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param xdot_old Value of residual function before event */ void addAdjointQuadratureEventUpdate( - AmiVector xQB, int const ie, const realtype t, AmiVector const& x, + AmiVector xQB, int const ie, realtype const t, AmiVector const& x, AmiVector const& xB, AmiVector const& xdot, AmiVector const& xdot_old ); @@ -1693,7 +1693,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param edata Experimental data */ void fsigmaz( - int const ie, int const nroots, const realtype t, ExpData const* edata + int const ie, int const nroots, realtype const t, ExpData const* edata ); /** @@ -1727,7 +1727,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param edata Experimental data */ void fdJzdz( - int const ie, int const nroots, const realtype t, AmiVector const& x, + int const ie, int const nroots, realtype const t, AmiVector const& x, ExpData const& edata ); @@ -1741,7 +1741,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param edata Pointer to experimental data instance */ void fdJzdsigma( - int const ie, int const nroots, const realtype t, AmiVector const& x, + int const ie, int const nroots, realtype const t, AmiVector const& x, ExpData const& edata ); @@ -1794,7 +1794,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param edata Experimental data */ void fdJrzdz( - int const ie, int const nroots, const realtype t, AmiVector const& x, + int const ie, int const nroots, realtype const t, AmiVector const& x, ExpData const& edata ); @@ -1808,7 +1808,7 @@ class Model : public AbstractModel, public ModelDimensions { * @param edata pointer to experimental data instance */ void fdJrzdsigma( - int const ie, int const nroots, const realtype t, AmiVector const& x, + int const ie, int const nroots, realtype const t, AmiVector const& x, ExpData const& edata ); @@ -2016,11 +2016,13 @@ class Model : public AbstractModel, public ModelDimensions { /** method for steady-state computation */ SteadyStateComputationMode steadystate_computation_mode_{ - SteadyStateComputationMode::integrateIfNewtonFails}; + SteadyStateComputationMode::integrateIfNewtonFails + }; /** method for steadystate sensitivities computation */ SteadyStateSensitivityMode steadystate_sensitivity_mode_{ - SteadyStateSensitivityMode::integrateIfNewtonFails}; + SteadyStateSensitivityMode::integrateIfNewtonFails + }; /** * Indicates whether the result of every call to `Model::f*` should be diff --git a/include/amici/model_dae.h b/include/amici/model_dae.h index b35cfa1d70..232bfc867d 100644 --- a/include/amici/model_dae.h +++ b/include/amici/model_dae.h @@ -46,7 +46,7 @@ class Model_DAE : public Model { Model_DAE( ModelDimensions const& model_dimensions, SimulationParameters simulation_parameters, - const SecondOrderMode o2mode, std::vector const& idlist, + SecondOrderMode const o2mode, std::vector const& idlist, std::vector const& z2event, bool const pythonGenerated = false, int const ndxdotdp_explicit = 0, int const ndxdotdx_explicit = 0, int const w_recursion_depth = 0, @@ -85,7 +85,7 @@ class Model_DAE : public Model { const_N_Vector xdot, SUNMatrix J); void - fJB(const realtype t, realtype cj, AmiVector const& x, AmiVector const& dx, + fJB(realtype const t, realtype cj, AmiVector const& x, AmiVector const& dx, AmiVector const& xB, AmiVector const& dxB, AmiVector const& xBdot, SUNMatrix JB) override; @@ -122,7 +122,7 @@ class Model_DAE : public Model { ); void fJSparseB( - const realtype t, realtype cj, AmiVector const& x, AmiVector const& dx, + realtype const t, realtype cj, AmiVector const& x, AmiVector const& dx, AmiVector const& xB, AmiVector const& dxB, AmiVector const& xBdot, SUNMatrix JB ) override; @@ -253,7 +253,7 @@ class Model_DAE : public Model { ); void fxBdot_ss( - const realtype t, AmiVector const& xB, AmiVector const& dxB, + realtype const t, AmiVector const& xB, AmiVector const& dxB, AmiVector& xBdot ) override; @@ -298,7 +298,7 @@ class Model_DAE : public Model { * @param xBdot Vector with the adjoint state right hand side */ void writeSteadystateJB( - const realtype t, realtype cj, AmiVector const& x, AmiVector const& dx, + realtype const t, realtype cj, AmiVector const& x, AmiVector const& dx, AmiVector const& xB, AmiVector const& dxB, AmiVector const& xBdot ) override; @@ -308,8 +308,8 @@ class Model_DAE : public Model { * @param x Vector with the states * @param dx Vector with the derivative states */ - void fdxdotdp(realtype t, const const_N_Vector x, const const_N_Vector dx); - void fdxdotdp(const realtype t, AmiVector const& x, AmiVector const& dx) + void fdxdotdp(realtype t, const_N_Vector const x, const_N_Vector const dx); + void fdxdotdp(realtype const t, AmiVector const& x, AmiVector const& dx) override { fdxdotdp(t, x.getNVector(), dx.getNVector()); }; @@ -526,7 +526,7 @@ class Model_DAE : public Model { * @param k constants vector */ virtual void - fM(realtype* M, const realtype t, realtype const* x, realtype const* p, + fM(realtype* M, realtype const t, realtype const* x, realtype const* p, realtype const* k); }; } // namespace amici diff --git a/include/amici/model_ode.h b/include/amici/model_ode.h index e03e1867d1..3632c2198f 100644 --- a/include/amici/model_ode.h +++ b/include/amici/model_ode.h @@ -45,7 +45,7 @@ class Model_ODE : public Model { Model_ODE( ModelDimensions const& model_dimensions, SimulationParameters simulation_parameters, - const SecondOrderMode o2mode, std::vector const& idlist, + SecondOrderMode const o2mode, std::vector const& idlist, std::vector const& z2event, bool const pythonGenerated = false, int const ndxdotdp_explicit = 0, int const ndxdotdx_explicit = 0, int const w_recursion_depth = 0, @@ -75,7 +75,7 @@ class Model_ODE : public Model { void fJ(realtype t, const_N_Vector x, const_N_Vector xdot, SUNMatrix J); void - fJB(const realtype t, realtype cj, AmiVector const& x, AmiVector const& dx, + fJB(realtype const t, realtype cj, AmiVector const& x, AmiVector const& dx, AmiVector const& xB, AmiVector const& dxB, AmiVector const& xBdot, SUNMatrix JB) override; @@ -109,7 +109,7 @@ class Model_ODE : public Model { void fJSparse(realtype t, const_N_Vector x, SUNMatrix J); void fJSparseB( - const realtype t, realtype cj, AmiVector const& x, AmiVector const& dx, + realtype const t, realtype cj, AmiVector const& x, AmiVector const& dx, AmiVector const& xB, AmiVector const& dxB, AmiVector const& xBdot, SUNMatrix JB ) override; @@ -229,7 +229,7 @@ class Model_ODE : public Model { fqBdot(realtype t, const_N_Vector x, const_N_Vector xB, N_Vector qBdot); void fxBdot_ss( - const realtype t, AmiVector const& xB, AmiVector const& /*dxB*/, + realtype const t, AmiVector const& xB, AmiVector const& /*dxB*/, AmiVector& xBdot ) override; @@ -268,7 +268,7 @@ class Model_ODE : public Model { * @param xBdot Vector with the adjoint state right hand side */ void writeSteadystateJB( - const realtype t, realtype cj, AmiVector const& x, AmiVector const& dx, + realtype const t, realtype cj, AmiVector const& x, AmiVector const& dx, AmiVector const& xB, AmiVector const& dxB, AmiVector const& xBdot ) override; diff --git a/include/amici/rdata.h b/include/amici/rdata.h index 3b9ea01b9f..1de02c99db 100644 --- a/include/amici/rdata.h +++ b/include/amici/rdata.h @@ -688,7 +688,7 @@ class ReturnData : public ModelDimensions { * @param edata ExpData instance carrying experimental data */ void getEventOutput( - realtype t, const std::vector rootidx, Model& model, + realtype t, std::vector const rootidx, Model& model, ExpData const* edata ); diff --git a/include/amici/returndata_matlab.h b/include/amici/returndata_matlab.h index 6a51db5264..26b7d55d9e 100644 --- a/include/amici/returndata_matlab.h +++ b/include/amici/returndata_matlab.h @@ -55,7 +55,7 @@ void writeMatlabField0( template void writeMatlabField1( mxArray* matlabStruct, char const* fieldName, - gsl::span const& fieldData, const mwSize dim0 + gsl::span const& fieldData, mwSize const dim0 ); /** diff --git a/include/amici/serialization.h b/include/amici/serialization.h index c2a69d4b3a..6cab4f0353 100644 --- a/include/amici/serialization.h +++ b/include/amici/serialization.h @@ -34,11 +34,11 @@ void archiveVector(Archive& ar, T** p, int size) { if (Archive::is_loading::value) { if (*p != nullptr) delete[] *p; - ar& size; + ar & size; *p = size ? new T[size] : nullptr; } else { size = *p == nullptr ? 0 : size; - ar& size; + ar & size; } ar& make_array(*p, size); } @@ -51,40 +51,40 @@ void archiveVector(Archive& ar, T** p, int size) { */ template void serialize(Archive& ar, amici::Solver& s, unsigned int const /*version*/) { - ar& s.sensi_; - ar& s.atol_; - ar& s.rtol_; - ar& s.atolB_; - ar& s.rtolB_; - ar& s.atol_fsa_; - ar& s.rtol_fsa_; - ar& s.quad_atol_; - ar& s.quad_rtol_; - ar& s.ss_tol_factor_; - ar& s.ss_atol_; - ar& s.ss_rtol_; - ar& s.ss_tol_sensi_factor_; - ar& s.ss_atol_sensi_; - ar& s.ss_rtol_sensi_; - ar& s.maxsteps_; - ar& s.maxstepsB_; - ar& s.newton_maxsteps_; - ar& s.newton_damping_factor_mode_; - ar& s.newton_damping_factor_lower_bound_; - ar& s.ism_; - ar& s.sensi_meth_; - ar& s.linsol_; - ar& s.interp_type_; - ar& s.lmm_; - ar& s.iter_; - ar& s.stldet_; - ar& s.ordering_; - ar& s.cpu_time_; - ar& s.cpu_timeB_; - ar& s.newton_step_steadystate_conv_; - ar& s.check_sensi_steadystate_conv_; - ar& s.rdata_mode_; - ar& s.maxtime_; + ar & s.sensi_; + ar & s.atol_; + ar & s.rtol_; + ar & s.atolB_; + ar & s.rtolB_; + ar & s.atol_fsa_; + ar & s.rtol_fsa_; + ar & s.quad_atol_; + ar & s.quad_rtol_; + ar & s.ss_tol_factor_; + ar & s.ss_atol_; + ar & s.ss_rtol_; + ar & s.ss_tol_sensi_factor_; + ar & s.ss_atol_sensi_; + ar & s.ss_rtol_sensi_; + ar & s.maxsteps_; + ar & s.maxstepsB_; + ar & s.newton_maxsteps_; + ar & s.newton_damping_factor_mode_; + ar & s.newton_damping_factor_lower_bound_; + ar & s.ism_; + ar & s.sensi_meth_; + ar & s.linsol_; + ar & s.interp_type_; + ar & s.lmm_; + ar & s.iter_; + ar & s.stldet_; + ar & s.ordering_; + ar & s.cpu_time_; + ar & s.cpu_timeB_; + ar & s.newton_step_steadystate_conv_; + ar & s.check_sensi_steadystate_conv_; + ar & s.rdata_mode_; + ar & s.maxtime_; } /** @@ -99,11 +99,11 @@ void serialize( ) { Period tmp_period; if (Archive::is_loading::value) { - ar& tmp_period; + ar & tmp_period; d = std::chrono::duration(tmp_period); } else { tmp_period = d.count(); - ar& tmp_period; + ar & tmp_period; } } @@ -127,24 +127,24 @@ void serialize( template void serialize(Archive& ar, amici::Model& m, unsigned int const /*version*/) { ar& dynamic_cast(m); - ar& m.simulation_parameters_; - ar& m.o2mode; - ar& m.z2event_; - ar& m.idlist; - ar& m.state_.h; - ar& m.state_.unscaledParameters; - ar& m.state_.fixedParameters; - ar& m.state_.plist; - ar& m.x0data_; - ar& m.sx0data_; - ar& m.nmaxevent_; - ar& m.state_is_non_negative_; - ar& m.pythonGenerated; - ar& m.min_sigma_; - ar& m.sigma_res_; - ar& m.steadystate_computation_mode_; - ar& m.steadystate_sensitivity_mode_; - ar& m.state_independent_events_; + ar & m.simulation_parameters_; + ar & m.o2mode; + ar & m.z2event_; + ar & m.idlist; + ar & m.state_.h; + ar & m.state_.unscaledParameters; + ar & m.state_.fixedParameters; + ar & m.state_.plist; + ar & m.x0data_; + ar & m.sx0data_; + ar & m.nmaxevent_; + ar & m.state_is_non_negative_; + ar & m.pythonGenerated; + ar & m.min_sigma_; + ar & m.sigma_res_; + ar & m.steadystate_computation_mode_; + ar & m.steadystate_sensitivity_mode_; + ar & m.state_independent_events_; } /** @@ -156,18 +156,18 @@ template void serialize( Archive& ar, amici::SimulationParameters& s, unsigned int const /*version*/ ) { - ar& s.fixedParameters; - ar& s.fixedParametersPreequilibration; - ar& s.fixedParametersPresimulation; - ar& s.parameters; - ar& s.x0; - ar& s.sx0; - ar& s.pscale; - ar& s.plist; - ar& s.ts_; - ar& s.tstart_; - ar& s.t_presim; - ar& s.reinitializeFixedParameterInitialStates; + ar & s.fixedParameters; + ar & s.fixedParametersPreequilibration; + ar & s.fixedParametersPresimulation; + ar & s.parameters; + ar & s.x0; + ar & s.sx0; + ar & s.pscale; + ar & s.plist; + ar & s.ts_; + ar & s.tstart_; + ar & s.t_presim; + ar & s.reinitializeFixedParameterInitialStates; } /** @@ -181,63 +181,63 @@ void serialize( Archive& ar, amici::ReturnData& r, unsigned int const /*version*/ ) { ar& dynamic_cast(r); - ar& r.id; - ar& r.nx; - ar& r.nxtrue; - ar& r.nplist; - ar& r.nmaxevent; - ar& r.nt; - ar& r.newton_maxsteps; - ar& r.pscale; - ar& r.o2mode; - ar& r.sensi; - ar& r.sensi_meth; - - ar& r.ts; - ar& r.xdot; - ar& r.J; - ar& r.w; - ar& r.z& r.sigmaz; - ar& r.sz& r.ssigmaz; - ar& r.rz; - ar& r.srz; - ar& r.s2rz; - ar& r.x; - ar& r.sx; - ar& r.y& r.sigmay; - ar& r.sy& r.ssigmay; - - ar& r.numsteps; - ar& r.numstepsB; - ar& r.numrhsevals; - ar& r.numrhsevalsB; - ar& r.numerrtestfails; - ar& r.numerrtestfailsB; - ar& r.numnonlinsolvconvfails; - ar& r.numnonlinsolvconvfailsB; - ar& r.order; - ar& r.cpu_time; - ar& r.cpu_timeB; - ar& r.cpu_time_total; - ar& r.preeq_cpu_time; - ar& r.preeq_cpu_timeB; - ar& r.preeq_status; - ar& r.preeq_numsteps; - ar& r.preeq_wrms; - ar& r.preeq_t; - ar& r.posteq_cpu_time; - ar& r.posteq_cpu_timeB; - ar& r.posteq_status; - ar& r.posteq_numsteps; - ar& r.posteq_wrms; - ar& r.posteq_t; - ar& r.x0; - ar& r.sx0; - ar& r.llh; - ar& r.chi2; - ar& r.sllh; - ar& r.s2llh; - ar& r.status; + ar & r.id; + ar & r.nx; + ar & r.nxtrue; + ar & r.nplist; + ar & r.nmaxevent; + ar & r.nt; + ar & r.newton_maxsteps; + ar & r.pscale; + ar & r.o2mode; + ar & r.sensi; + ar & r.sensi_meth; + + ar & r.ts; + ar & r.xdot; + ar & r.J; + ar & r.w; + ar & r.z & r.sigmaz; + ar & r.sz & r.ssigmaz; + ar & r.rz; + ar & r.srz; + ar & r.s2rz; + ar & r.x; + ar & r.sx; + ar & r.y & r.sigmay; + ar & r.sy & r.ssigmay; + + ar & r.numsteps; + ar & r.numstepsB; + ar & r.numrhsevals; + ar & r.numrhsevalsB; + ar & r.numerrtestfails; + ar & r.numerrtestfailsB; + ar & r.numnonlinsolvconvfails; + ar & r.numnonlinsolvconvfailsB; + ar & r.order; + ar & r.cpu_time; + ar & r.cpu_timeB; + ar & r.cpu_time_total; + ar & r.preeq_cpu_time; + ar & r.preeq_cpu_timeB; + ar & r.preeq_status; + ar & r.preeq_numsteps; + ar & r.preeq_wrms; + ar & r.preeq_t; + ar & r.posteq_cpu_time; + ar & r.posteq_cpu_timeB; + ar & r.posteq_status; + ar & r.posteq_numsteps; + ar & r.posteq_wrms; + ar & r.posteq_t; + ar & r.x0; + ar & r.sx0; + ar & r.llh; + ar & r.chi2; + ar & r.sllh; + ar & r.s2llh; + ar & r.status; } /** @@ -250,30 +250,30 @@ template void serialize( Archive& ar, amici::ModelDimensions& m, unsigned int const /*version*/ ) { - ar& m.nx_rdata; - ar& m.nxtrue_rdata; - ar& m.nx_solver; - ar& m.nxtrue_solver; - ar& m.nx_solver_reinit; - ar& m.np; - ar& m.nk; - ar& m.ny; - ar& m.nytrue; - ar& m.nz; - ar& m.nztrue; - ar& m.ne; - ar& m.ne_solver; - ar& m.nspl; - ar& m.nw; - ar& m.ndwdx; - ar& m.ndwdp; - ar& m.ndwdw; - ar& m.ndxdotdw; - ar& m.ndJydy; - ar& m.nnz; - ar& m.nJ; - ar& m.ubw; - ar& m.lbw; + ar & m.nx_rdata; + ar & m.nxtrue_rdata; + ar & m.nx_solver; + ar & m.nxtrue_solver; + ar & m.nx_solver_reinit; + ar & m.np; + ar & m.nk; + ar & m.ny; + ar & m.nytrue; + ar & m.nz; + ar & m.nztrue; + ar & m.ne; + ar & m.ne_solver; + ar & m.nspl; + ar & m.nw; + ar & m.ndwdx; + ar & m.ndwdp; + ar & m.ndwdw; + ar & m.ndxdotdw; + ar & m.ndJydy; + ar & m.nnz; + ar & m.nJ; + ar & m.ubw; + ar & m.lbw; } #endif } // namespace serialization diff --git a/include/amici/simulation_parameters.h b/include/amici/simulation_parameters.h index 8a1c1ec23d..110b8fc03e 100644 --- a/include/amici/simulation_parameters.h +++ b/include/amici/simulation_parameters.h @@ -37,8 +37,16 @@ class SimulationParameters { #ifndef SWIGPYTHON /* - * include/amici/simulation_parameters.h:71: Warning 509: Overloaded method amici::SimulationParameters::SimulationParameters(std::vector< amici::realtype,std::allocator< amici::realtype > >,std::vector< amici::realtype,std::allocator< amici::realtype > >,std::vector< amici::realtype,std::allocator< amici::realtype > >) effectively ignored, - * include/amici/simulation_parameters.h:54: Warning 509: as it is shadowed by amici::SimulationParameters::SimulationParameters(std::vector< amici::realtype,std::allocator< amici::realtype > >,std::vector< amici::realtype,std::allocator< amici::realtype > >,std::vector< int,std::allocator< int > >). + * include/amici/simulation_parameters.h:71: Warning 509: Overloaded method + * amici::SimulationParameters::SimulationParameters(std::vector< + * amici::realtype,std::allocator< amici::realtype > >,std::vector< + * amici::realtype,std::allocator< amici::realtype > >,std::vector< + * amici::realtype,std::allocator< amici::realtype > >) effectively ignored, + * include/amici/simulation_parameters.h:54: Warning 509: as it is shadowed + * by amici::SimulationParameters::SimulationParameters(std::vector< + * amici::realtype,std::allocator< amici::realtype > >,std::vector< + * amici::realtype,std::allocator< amici::realtype > >,std::vector< + * int,std::allocator< int > >). */ /** * @brief Constructor diff --git a/include/amici/solver.h b/include/amici/solver.h index 4a1c95b96a..92ae4c47fb 100644 --- a/include/amici/solver.h +++ b/include/amici/solver.h @@ -134,7 +134,7 @@ class Solver { */ void setupSteadystate( - const realtype t0, Model* model, AmiVector const& x0, + realtype const t0, Model* model, AmiVector const& x0, AmiVector const& dx0, AmiVector const& xB0, AmiVector const& dxB0, AmiVector const& xQ0 ) const; @@ -1612,7 +1612,7 @@ class Solver { mutable std::unique_ptr solver_memory_; /** pointer to solver memory block */ - mutable std::vector> + mutable std::vector> solver_memory_B_; /** Sundials user_data */ @@ -1709,7 +1709,7 @@ class Solver { * @param preequilibration flag indicating preequilibration or simulation */ void checkSensitivityMethod( - const SensitivityMethod sensi_meth, bool preequilibration + SensitivityMethod const sensi_meth, bool preequilibration ) const; /** state (dimension: nx_solver) */ @@ -1784,7 +1784,8 @@ class Solver { /** Damping factor state used int the Newton method */ NewtonDampingFactorMode newton_damping_factor_mode_{ - NewtonDampingFactorMode::on}; + NewtonDampingFactorMode::on + }; /** Lower bound of the damping factor. */ realtype newton_damping_factor_lower_bound_{1e-8}; diff --git a/include/amici/solver_cvodes.h b/include/amici/solver_cvodes.h index d6d1dcea24..f98fb34c1a 100644 --- a/include/amici/solver_cvodes.h +++ b/include/amici/solver_cvodes.h @@ -218,7 +218,7 @@ class CVodeSolver : public Solver { init(realtype t0, AmiVector const& x0, AmiVector const& dx0) const override; void initSteadystate( - const realtype t0, AmiVector const& x0, AmiVector const& dx0 + realtype const t0, AmiVector const& x0, AmiVector const& dx0 ) const override; void sensInit1(AmiVectorArray const& sx0, AmiVectorArray const& sdx0) diff --git a/include/amici/solver_idas.h b/include/amici/solver_idas.h index 0dba1a9504..b985a090af 100644 --- a/include/amici/solver_idas.h +++ b/include/amici/solver_idas.h @@ -198,7 +198,7 @@ class IDASolver : public Solver { init(realtype t0, AmiVector const& x0, AmiVector const& dx0) const override; void initSteadystate( - const realtype t0, AmiVector const& x0, AmiVector const& dx0 + realtype const t0, AmiVector const& x0, AmiVector const& dx0 ) const override; void sensInit1(AmiVectorArray const& sx0, AmiVectorArray const& sdx0) diff --git a/include/amici/splinefunctions.h b/include/amici/splinefunctions.h index db4410de91..ed9afb9ffc 100644 --- a/include/amici/splinefunctions.h +++ b/include/amici/splinefunctions.h @@ -72,7 +72,7 @@ class AbstractSpline { * @param t point at which the spline is to be evaluated * @return value of the spline at `t` */ - realtype get_value(const realtype t) const; + realtype get_value(realtype const t) const; /** * @brief Get the value of this spline at a given point @@ -80,7 +80,7 @@ class AbstractSpline { * @param t point at which the spline is to be evaluated * @return scaled value of the spline at `t` */ - virtual realtype get_value_scaled(const realtype t) const = 0; + virtual realtype get_value_scaled(realtype const t) const = 0; /** * @brief Get the value of this spline at a given node @@ -105,7 +105,7 @@ class AbstractSpline { * @return sensitivity of the spline with respect to the `ip`th parameter * at `t` */ - realtype get_sensitivity(const realtype t, int const ip) const; + realtype get_sensitivity(realtype const t, int const ip) const; /** * @brief Get the derivative of this spline with respect to a given @@ -119,7 +119,7 @@ class AbstractSpline { * at `t` */ realtype - get_sensitivity(const realtype t, int const ip, const realtype value) const; + get_sensitivity(realtype const t, int const ip, realtype const value) const; /** * @brief Get the derivative of this spline with respect to a given @@ -131,7 +131,7 @@ class AbstractSpline { * parameter at `t` */ virtual realtype - get_sensitivity_scaled(const realtype t, int const ip) const + get_sensitivity_scaled(realtype const t, int const ip) const = 0; /** @@ -329,7 +329,7 @@ class HermiteSpline : public AbstractSpline { gsl::span dspline_slopesdp ) override; - realtype get_value_scaled(const realtype t) const override; + realtype get_value_scaled(realtype const t) const override; /** * @brief Get the derivative of the spline at a given node @@ -347,7 +347,7 @@ class HermiteSpline : public AbstractSpline { realtype get_node_derivative_scaled(int const i) const; realtype - get_sensitivity_scaled(const realtype t, int const ip) const override; + get_sensitivity_scaled(realtype const t, int const ip) const override; /** * @brief Whether derivatives of this spline are computed diff --git a/include/amici/sundials_matrix_wrapper.h b/include/amici/sundials_matrix_wrapper.h index 8d63eca5ea..ee2516f78d 100644 --- a/include/amici/sundials_matrix_wrapper.h +++ b/include/amici/sundials_matrix_wrapper.h @@ -266,7 +266,7 @@ class SUNMatrixWrapper { * @brief Set the index values of a sparse matrix * @param vals rows (CSC) or columns (CSR) for data entries */ - void set_indexvals(const gsl::span vals) { + void set_indexvals(gsl::span const vals) { assert(matrix_); assert(matrix_id() == SUNMATRIX_SPARSE); assert(gsl::narrow(vals.size()) == capacity()); @@ -309,7 +309,7 @@ class SUNMatrixWrapper { * @param ptrs starting data-indices where the columns (CSC) or rows (CSR) * start */ - void set_indexptrs(const gsl::span ptrs) { + void set_indexptrs(gsl::span const ptrs) { assert(matrix_); assert(matrix_id() == SUNMATRIX_SPARSE); assert(gsl::narrow(ptrs.size()) == num_indexptrs() + 1); @@ -357,7 +357,7 @@ class SUNMatrixWrapper { */ void multiply( gsl::span c, gsl::span b, - const realtype alpha = 1.0 + realtype const alpha = 1.0 ) const; /** @@ -440,8 +440,8 @@ class SUNMatrixWrapper { * @return updated number of nonzeros in C */ sunindextype scatter( - const sunindextype k, const realtype beta, sunindextype* w, - gsl::span x, const sunindextype mark, SUNMatrixWrapper* C, + sunindextype const k, realtype const beta, sunindextype* w, + gsl::span x, sunindextype const mark, SUNMatrixWrapper* C, sunindextype nnz ) const; @@ -455,7 +455,7 @@ class SUNMatrixWrapper { * set to ncols/nrows */ void transpose( - SUNMatrixWrapper& C, const realtype alpha, sunindextype blocksize + SUNMatrixWrapper& C, realtype const alpha, sunindextype blocksize ) const; /** diff --git a/src/abstract_model.cpp b/src/abstract_model.cpp index dc2c469173..041d06b361 100644 --- a/src/abstract_model.cpp +++ b/src/abstract_model.cpp @@ -11,7 +11,7 @@ std::string AbstractModel::getAmiciCommit() const { } void AbstractModel:: - fx0(realtype* /*x0*/, const realtype /*t*/, realtype const* /*p*/, + fx0(realtype* /*x0*/, realtype const /*t*/, realtype const* /*p*/, realtype const* /*k*/) { throw AmiException( "Requested functionality is not supported as %s is " @@ -25,14 +25,14 @@ bool AbstractModel::isFixedParameterStateReinitializationAllowed() const { } void AbstractModel::fx0_fixedParameters( - realtype* /*x0*/, const realtype /*t*/, realtype const* /*p*/, + realtype* /*x0*/, realtype const /*t*/, realtype const* /*p*/, realtype const* /*k*/, gsl::span /*reinitialization_state_idxs*/ ) { // no-op default implementation } void AbstractModel::fsx0_fixedParameters( - realtype* /*sx0*/, const realtype /*t*/, realtype const* /*x0*/, + realtype* /*sx0*/, realtype const /*t*/, realtype const* /*x0*/, realtype const* /*p*/, realtype const* /*k*/, int const /*ip*/, gsl::span /*reinitialization_state_idxs*/ ) { @@ -40,7 +40,7 @@ void AbstractModel::fsx0_fixedParameters( } void AbstractModel::fsx0( - realtype* /*sx0*/, const realtype /*t*/, realtype const* /*x0*/, + realtype* /*sx0*/, realtype const /*t*/, realtype const* /*x0*/, realtype const* /*p*/, realtype const* /*k*/, int const /*ip*/ ) { throw AmiException( @@ -55,7 +55,7 @@ void AbstractModel::fdx0(AmiVector& /*x0*/, AmiVector& /*dx0*/) { } void AbstractModel::fstau( - realtype* /*stau*/, const realtype /*t*/, realtype const* /*x*/, + realtype* /*stau*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, realtype const* /*tcl*/, realtype const* /*sx*/, int const /*ip*/, int const /*ie*/ @@ -68,7 +68,7 @@ void AbstractModel::fstau( } void AbstractModel:: - fy(realtype* /*y*/, const realtype /*t*/, realtype const* /*x*/, + fy(realtype* /*y*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, realtype const* /*w*/) { throw AmiException( @@ -79,7 +79,7 @@ void AbstractModel:: } void AbstractModel::fdydp( - realtype* /*dydp*/, const realtype /*t*/, realtype const* /*x*/, + realtype* /*dydp*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, int const /*ip*/, realtype const* /*w*/, realtype const* /*dwdp*/ ) { @@ -91,7 +91,7 @@ void AbstractModel::fdydp( } void AbstractModel::fdydp( - realtype* /*dydp*/, const realtype /*t*/, realtype const* /*x*/, + realtype* /*dydp*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, int /*ip*/, realtype const* /*w*/, realtype const* /*tcl*/, realtype const* /*dtcldp*/, realtype const* /*spl*/, @@ -105,7 +105,7 @@ void AbstractModel::fdydp( } void AbstractModel::fdydx( - realtype* /*dydx*/, const realtype /*t*/, realtype const* /*x*/, + realtype* /*dydx*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, realtype const* /*w*/, realtype const* /*dwdx*/ ) { @@ -117,7 +117,7 @@ void AbstractModel::fdydx( } void AbstractModel:: - fz(realtype* /*z*/, int const /*ie*/, const realtype /*t*/, + fz(realtype* /*z*/, int const /*ie*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/) { throw AmiException( @@ -128,7 +128,7 @@ void AbstractModel:: } void AbstractModel:: - fsz(realtype* /*sz*/, int const /*ie*/, const realtype /*t*/, + fsz(realtype* /*sz*/, int const /*ie*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, realtype const* /*sx*/, int const /*ip*/) { throw AmiException( @@ -139,7 +139,7 @@ void AbstractModel:: } void AbstractModel:: - frz(realtype* /*rz*/, int const /*ie*/, const realtype /*t*/, + frz(realtype* /*rz*/, int const /*ie*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/) { throw AmiException( @@ -150,7 +150,7 @@ void AbstractModel:: } void AbstractModel::fsrz( - realtype* /*srz*/, int const /*ie*/, const realtype /*t*/, + realtype* /*srz*/, int const /*ie*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, realtype const* /*sx*/, int const /*ip*/ ) { @@ -162,7 +162,7 @@ void AbstractModel::fsrz( } void AbstractModel::fdzdp( - realtype* /*dzdp*/, int const /*ie*/, const realtype /*t*/, + realtype* /*dzdp*/, int const /*ie*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, int const /*ip*/ ) { @@ -174,7 +174,7 @@ void AbstractModel::fdzdp( } void AbstractModel::fdzdx( - realtype* /*dzdx*/, int const /*ie*/, const realtype /*t*/, + realtype* /*dzdx*/, int const /*ie*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/ ) { @@ -186,7 +186,7 @@ void AbstractModel::fdzdx( } void AbstractModel::fdrzdp( - realtype* /*drzdp*/, int const /*ie*/, const realtype /*t*/, + realtype* /*drzdp*/, int const /*ie*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, int const /*ip*/ ) { @@ -198,7 +198,7 @@ void AbstractModel::fdrzdp( } void AbstractModel::fdrzdx( - realtype* /*drzdx*/, int const /*ie*/, const realtype /*t*/, + realtype* /*drzdx*/, int const /*ie*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/ ) { @@ -210,7 +210,7 @@ void AbstractModel::fdrzdx( } void AbstractModel::fdeltax( - realtype* /*deltax*/, const realtype /*t*/, realtype const* /*x*/, + realtype* /*deltax*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, int const /*ie*/, realtype const* /*xdot*/, realtype const* /*xdot_old*/ ) { @@ -222,7 +222,7 @@ void AbstractModel::fdeltax( } void AbstractModel::fdeltasx( - realtype* /*deltasx*/, const realtype /*t*/, realtype const* /*x*/, + realtype* /*deltasx*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, realtype const* /*w*/, int const /*ip*/, int const /*ie*/, realtype const* /*xdot*/, realtype const* /*xdot_old*/, @@ -236,7 +236,7 @@ void AbstractModel::fdeltasx( } void AbstractModel::fdeltaxB( - realtype* /*deltaxB*/, const realtype /*t*/, realtype const* /*x*/, + realtype* /*deltaxB*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, int const /*ie*/, realtype const* /*xdot*/, realtype const* /*xdot_old*/, realtype const* /*xB*/ @@ -249,7 +249,7 @@ void AbstractModel::fdeltaxB( } void AbstractModel::fdeltaqB( - realtype* /*deltaqB*/, const realtype /*t*/, realtype const* /*x*/, + realtype* /*deltaqB*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, int const /*ip*/, int const /*ie*/, realtype const* /*xdot*/, realtype const* /*xdot_old*/, realtype const* /*xB*/ @@ -262,7 +262,7 @@ void AbstractModel::fdeltaqB( } void AbstractModel::fsigmay( - realtype* /*sigmay*/, const realtype /*t*/, realtype const* /*p*/, + realtype* /*sigmay*/, realtype const /*t*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*y*/ ) { throw AmiException( @@ -273,7 +273,7 @@ void AbstractModel::fsigmay( } void AbstractModel::fdsigmaydp( - realtype* /*dsigmaydp*/, const realtype /*t*/, realtype const* /*p*/, + realtype* /*dsigmaydp*/, realtype const /*t*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*y*/, int const /*ip*/ ) { throw AmiException( @@ -284,7 +284,7 @@ void AbstractModel::fdsigmaydp( } void AbstractModel::fdsigmaydy( - realtype* /*dsigmaydy*/, const realtype /*t*/, realtype const* /*p*/, + realtype* /*dsigmaydy*/, realtype const /*t*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*y*/ ) { throw AmiException( @@ -295,7 +295,7 @@ void AbstractModel::fdsigmaydy( } void AbstractModel::fsigmaz( - realtype* /*sigmaz*/, const realtype /*t*/, realtype const* /*p*/, + realtype* /*sigmaz*/, realtype const /*t*/, realtype const* /*p*/, realtype const* /*k*/ ) { throw AmiException( @@ -306,7 +306,7 @@ void AbstractModel::fsigmaz( } void AbstractModel::fdsigmazdp( - realtype* /*dsigmazdp*/, const realtype /*t*/, realtype const* /*p*/, + realtype* /*dsigmazdp*/, realtype const /*t*/, realtype const* /*p*/, realtype const* /*k*/, int const /*ip*/ ) { throw AmiException( @@ -438,7 +438,7 @@ void AbstractModel::fdJrzdsigma( } void AbstractModel:: - fw(realtype* /*w*/, const realtype /*t*/, realtype const* /*x*/, + fw(realtype* /*w*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, realtype const* /*tcl*/, realtype const* /*spl*/) { throw AmiException( @@ -449,7 +449,7 @@ void AbstractModel:: } void AbstractModel::fdwdp( - realtype* /*dwdp*/, const realtype /*t*/, realtype const* /*x*/, + realtype* /*dwdp*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, realtype const* /*w*/, realtype const* /*tcl*/, realtype const* /*stcl*/, realtype const* /*spl*/, realtype const* /*sspl*/ @@ -478,7 +478,7 @@ void AbstractModel::fdwdp_rowvals(SUNMatrixWrapper& /*dwdp*/) { } void AbstractModel::fdwdp( - realtype* /*dwdp*/, const realtype /*t*/, realtype const* /*x*/, + realtype* /*dwdp*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, realtype const* /*w*/, realtype const* /*tcl*/, realtype const* /*stcl*/, realtype const* /*spl*/, realtype const* /*sspl*/, int const /*ip*/ @@ -491,7 +491,7 @@ void AbstractModel::fdwdp( } void AbstractModel::fdwdx( - realtype* /*dwdx*/, const realtype /*t*/, realtype const* /*x*/, + realtype* /*dwdx*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, realtype const* /*w*/, realtype const* /*tcl*/, realtype const* /*spl*/ ) { diff --git a/src/edata.cpp b/src/edata.cpp index 05499bb9cf..2408e4becd 100644 --- a/src/edata.cpp +++ b/src/edata.cpp @@ -194,7 +194,7 @@ void ExpData::setObservedDataStdDev( observed_data_std_dev_.clear(); } -void ExpData::setObservedDataStdDev(const realtype stdDev) { +void ExpData::setObservedDataStdDev(realtype const stdDev) { checkSigmaPositivity(stdDev, "stdDev"); std::fill( observed_data_std_dev_.begin(), observed_data_std_dev_.end(), stdDev @@ -216,7 +216,7 @@ void ExpData::setObservedDataStdDev( = observedDataStdDev.at(it); } -void ExpData::setObservedDataStdDev(const realtype stdDev, int iy) { +void ExpData::setObservedDataStdDev(realtype const stdDev, int iy) { checkSigmaPositivity(stdDev, "stdDev"); for (int it = 0; it < nt(); ++it) observed_data_std_dev_.at(iy + it * nytrue_) = stdDev; @@ -290,7 +290,7 @@ void ExpData::setObservedEventsStdDev( observed_events_std_dev_.clear(); } -void ExpData::setObservedEventsStdDev(const realtype stdDev) { +void ExpData::setObservedEventsStdDev(realtype const stdDev) { checkSigmaPositivity(stdDev, "stdDev"); std::fill( observed_events_std_dev_.begin(), observed_events_std_dev_.end(), stdDev @@ -313,7 +313,7 @@ void ExpData::setObservedEventsStdDev( = observedEventsStdDev.at(ie); } -void ExpData::setObservedEventsStdDev(const realtype stdDev, int iz) { +void ExpData::setObservedEventsStdDev(realtype const stdDev, int iz) { checkSigmaPositivity(stdDev, "stdDev"); for (int ie = 0; ie < nmaxevent_; ++ie) @@ -392,7 +392,7 @@ void checkSigmaPositivity( checkSigmaPositivity(sigma, vectorName); } -void checkSigmaPositivity(const realtype sigma, char const* sigmaName) { +void checkSigmaPositivity(realtype const sigma, char const* sigmaName) { if (sigma <= 0.0) throw AmiException( "Encountered sigma <= 0 in %s! value: %f", sigmaName, sigma diff --git a/src/forwardproblem.cpp b/src/forwardproblem.cpp index c090f51280..b4bcd9740f 100644 --- a/src/forwardproblem.cpp +++ b/src/forwardproblem.cpp @@ -22,11 +22,12 @@ namespace amici { */ bool is_next_t_too_close(realtype cur_t, realtype t_next) { auto tdiff = t_next - cur_t; - if(tdiff == 0.0) + if (tdiff == 0.0) return true; auto tdist = std::fabs(tdiff); - auto tround = std::numeric_limits::epsilon() * std::max(std::fabs(cur_t), std::fabs(t_next)); + auto tround = std::numeric_limits::epsilon() + * std::max(std::fabs(cur_t), std::fabs(t_next)); if (tdist < 2.0 * tround) return true; diff --git a/src/hdf5.cpp b/src/hdf5.cpp index d9d875cdf7..e459eee138 100644 --- a/src/hdf5.cpp +++ b/src/hdf5.cpp @@ -655,7 +655,7 @@ void createAndWriteDouble2DDataset( const H5::H5File& file, std::string const& datasetName, gsl::span buffer, hsize_t m, hsize_t n ) { - const hsize_t adims[]{m, n}; + hsize_t const adims[]{m, n}; H5::DataSpace dataspace(2, adims); auto dataset = file.createDataSet( datasetName.c_str(), H5::PredType::NATIVE_DOUBLE, dataspace @@ -667,7 +667,7 @@ void createAndWriteInt2DDataset( H5::H5File const& file, std::string const& datasetName, gsl::span buffer, hsize_t m, hsize_t n ) { - const hsize_t adims[]{m, n}; + hsize_t const adims[]{m, n}; H5::DataSpace dataspace(2, adims); auto dataset = file.createDataSet( datasetName.c_str(), H5::PredType::NATIVE_INT, dataspace @@ -679,7 +679,7 @@ void createAndWriteDouble3DDataset( H5::H5File const& file, std::string const& datasetName, gsl::span buffer, hsize_t m, hsize_t n, hsize_t o ) { - const hsize_t adims[]{m, n, o}; + hsize_t const adims[]{m, n, o}; H5::DataSpace dataspace(3, adims); auto dataset = file.createDataSet( datasetName.c_str(), H5::PredType::NATIVE_DOUBLE, dataspace diff --git a/src/interface_matlab.cpp b/src/interface_matlab.cpp index 3caae66a96..e0f4702f34 100644 --- a/src/interface_matlab.cpp +++ b/src/interface_matlab.cpp @@ -71,12 +71,12 @@ void amici_dgemm( ) { assert(layout == BLASLayout::colMajor); - const ptrdiff_t M_ = M; - const ptrdiff_t N_ = N; - const ptrdiff_t K_ = K; - const ptrdiff_t lda_ = lda; - const ptrdiff_t ldb_ = ldb; - const ptrdiff_t ldc_ = ldc; + ptrdiff_t const M_ = M; + ptrdiff_t const N_ = N; + ptrdiff_t const K_ = K; + ptrdiff_t const lda_ = lda; + ptrdiff_t const ldb_ = ldb; + ptrdiff_t const ldc_ = ldc; char const transA = amici_blasCBlasTransToBlasTrans(TransA); char const transB = amici_blasCBlasTransToBlasTrans(TransB); @@ -92,11 +92,11 @@ void amici_dgemv( ) { assert(layout == BLASLayout::colMajor); - const ptrdiff_t M_ = M; - const ptrdiff_t N_ = N; - const ptrdiff_t lda_ = lda; - const ptrdiff_t incX_ = incX; - const ptrdiff_t incY_ = incY; + ptrdiff_t const M_ = M; + ptrdiff_t const N_ = N; + ptrdiff_t const lda_ = lda; + ptrdiff_t const incX_ = incX; + ptrdiff_t const incY_ = incY; char const transA = amici_blasCBlasTransToBlasTrans(TransA); FORTRAN_WRAPPER(dgemv) @@ -107,9 +107,9 @@ void amici_daxpy( int n, double alpha, double const* x, int const incx, double* y, int incy ) { - const ptrdiff_t n_ = n; - const ptrdiff_t incx_ = incx; - const ptrdiff_t incy_ = incy; + ptrdiff_t const n_ = n; + ptrdiff_t const incx_ = incx; + ptrdiff_t const incy_ = incy; FORTRAN_WRAPPER(daxpy)(&n_, &alpha, x, &incx_, y, &incy_); } diff --git a/src/model.cpp b/src/model.cpp index 3c78802731..2485867a4c 100644 --- a/src/model.cpp +++ b/src/model.cpp @@ -19,7 +19,7 @@ namespace amici { /** * @brief Maps ModelQuantity items to their string value */ -const std::map model_quantity_to_str{ +std::map const model_quantity_to_str{ {ModelQuantity::J, "J"}, {ModelQuantity::JB, "JB"}, {ModelQuantity::Jv, "Jv"}, @@ -996,7 +996,7 @@ void Model::setUnscaledInitialStateSensitivities( sx0data_ = sx0; } -void Model::setSteadyStateComputationMode(const SteadyStateComputationMode mode +void Model::setSteadyStateComputationMode(SteadyStateComputationMode const mode ) { steadystate_computation_mode_ = mode; } @@ -1005,7 +1005,7 @@ SteadyStateComputationMode Model::getSteadyStateComputationMode() const { return steadystate_computation_mode_; } -void Model::setSteadyStateSensitivityMode(const SteadyStateSensitivityMode mode +void Model::setSteadyStateSensitivityMode(SteadyStateSensitivityMode const mode ) { steadystate_sensitivity_mode_ = mode; } @@ -1046,14 +1046,14 @@ void Model::requireSensitivitiesForAllParameters() { } void Model::getExpression( - gsl::span w, const realtype t, AmiVector const& x + gsl::span w, realtype const t, AmiVector const& x ) { fw(t, computeX_pos(x)); writeSlice(derived_state_.w_, w); } void Model::getObservable( - gsl::span y, const realtype t, AmiVector const& x + gsl::span y, realtype const t, AmiVector const& x ) { fy(t, x); writeSlice(derived_state_.y_, y); @@ -1064,7 +1064,7 @@ ObservableScaling Model::getObservableScaling(int /*iy*/) const { } void Model::getObservableSensitivity( - gsl::span sy, const realtype t, AmiVector const& x, + gsl::span sy, realtype const t, AmiVector const& x, AmiVectorArray const& sx ) { if (!ny) @@ -1205,14 +1205,14 @@ void Model::getAdjointStateObservableUpdate( } void Model::getEvent( - gsl::span z, int const ie, const realtype t, AmiVector const& x + gsl::span z, int const ie, realtype const t, AmiVector const& x ) { fz(ie, t, x); writeSliceEvent(derived_state_.z_, z, ie); } void Model::getEventSensitivity( - gsl::span sz, int const ie, const realtype t, AmiVector const& x, + gsl::span sz, int const ie, realtype const t, AmiVector const& x, AmiVectorArray const& sx ) { if (pythonGenerated) { @@ -1263,14 +1263,14 @@ void Model::getUnobservedEventSensitivity( } void Model::getEventRegularization( - gsl::span rz, int const ie, const realtype t, AmiVector const& x + gsl::span rz, int const ie, realtype const t, AmiVector const& x ) { frz(ie, t, x); writeSliceEvent(derived_state_.rz_, rz, ie); } void Model::getEventRegularizationSensitivity( - gsl::span srz, int const ie, const realtype t, AmiVector const& x, + gsl::span srz, int const ie, realtype const t, AmiVector const& x, AmiVectorArray const& sx ) { if (pythonGenerated) { @@ -1313,7 +1313,7 @@ void Model::getEventRegularizationSensitivity( void Model::getEventSigma( gsl::span sigmaz, int const ie, int const nroots, - const realtype t, ExpData const* edata + realtype const t, ExpData const* edata ) { fsigmaz(ie, nroots, t, edata); writeSliceEvent(derived_state_.sigmaz_, sigmaz, ie); @@ -1321,14 +1321,14 @@ void Model::getEventSigma( void Model::getEventSigmaSensitivity( gsl::span ssigmaz, int const ie, int const nroots, - const realtype t, ExpData const* edata + realtype const t, ExpData const* edata ) { fdsigmazdp(ie, nroots, t, edata); writeSensitivitySliceEvent(derived_state_.dsigmazdp_, ssigmaz, ie); } void Model::addEventObjective( - realtype& Jz, int const ie, int const nroots, const realtype t, + realtype& Jz, int const ie, int const nroots, realtype const t, AmiVector const& x, ExpData const& edata ) { fz(ie, t, x); @@ -1348,7 +1348,7 @@ void Model::addEventObjective( } void Model::addEventObjectiveRegularization( - realtype& Jrz, int const ie, int const nroots, const realtype t, + realtype& Jrz, int const ie, int const nroots, realtype const t, AmiVector const& x, ExpData const& edata ) { frz(ie, t, x); @@ -1370,7 +1370,7 @@ void Model::addEventObjectiveRegularization( void Model::addEventObjectiveSensitivity( std::vector& sllh, std::vector& s2llh, int const ie, - int const nroots, const realtype t, AmiVector const& x, + int const nroots, realtype const t, AmiVector const& x, AmiVectorArray const& sx, ExpData const& edata ) { @@ -1405,7 +1405,7 @@ void Model::addEventObjectiveSensitivity( } void Model::getAdjointStateEventUpdate( - gsl::span dJzdx, int const ie, int const nroots, const realtype t, + gsl::span dJzdx, int const ie, int const nroots, realtype const t, AmiVector const& x, ExpData const& edata ) { fdJzdx(ie, nroots, t, x, edata); @@ -1414,7 +1414,7 @@ void Model::getAdjointStateEventUpdate( void Model::addPartialEventObjectiveSensitivity( std::vector& sllh, std::vector& s2llh, int const ie, - int const nroots, const realtype t, AmiVector const& x, ExpData const& edata + int const nroots, realtype const t, AmiVector const& x, ExpData const& edata ) { if (!nz) return; @@ -1425,7 +1425,7 @@ void Model::addPartialEventObjectiveSensitivity( } void Model::getEventTimeSensitivity( - std::vector& stau, const realtype t, int const ie, + std::vector& stau, realtype const t, int const ie, AmiVector const& x, AmiVectorArray const& sx ) { @@ -1441,7 +1441,7 @@ void Model::getEventTimeSensitivity( } void Model::addStateEventUpdate( - AmiVector& x, int const ie, const realtype t, AmiVector const& xdot, + AmiVector& x, int const ie, realtype const t, AmiVector const& xdot, AmiVector const& xdot_old ) { @@ -1465,7 +1465,7 @@ void Model::addStateEventUpdate( } void Model::addStateSensitivityEventUpdate( - AmiVectorArray& sx, int const ie, const realtype t, AmiVector const& x_old, + AmiVectorArray& sx, int const ie, realtype const t, AmiVector const& x_old, AmiVector const& xdot, AmiVector const& xdot_old, std::vector const& stau ) { @@ -1497,7 +1497,7 @@ void Model::addStateSensitivityEventUpdate( } void Model::addAdjointStateEventUpdate( - AmiVector& xB, int const ie, const realtype t, AmiVector const& x, + AmiVector& xB, int const ie, realtype const t, AmiVector const& x, AmiVector const& xdot, AmiVector const& xdot_old ) { @@ -1522,7 +1522,7 @@ void Model::addAdjointStateEventUpdate( } void Model::addAdjointQuadratureEventUpdate( - AmiVector xQB, int const ie, const realtype t, AmiVector const& x, + AmiVector xQB, int const ie, realtype const t, AmiVector const& x, AmiVector const& xB, AmiVector const& xdot, AmiVector const& xdot_old ) { for (int ip = 0; ip < nplist(); ip++) { @@ -2032,7 +2032,7 @@ void Model::initializeVectors() { derived_state_.dxdotdp = AmiVectorArray(nx_solver, nplist()); } -void Model::fy(const realtype t, AmiVector const& x) { +void Model::fy(realtype const t, AmiVector const& x) { if (!ny) return; @@ -2052,7 +2052,7 @@ void Model::fy(const realtype t, AmiVector const& x) { } } -void Model::fdydp(const realtype t, AmiVector const& x) { +void Model::fdydp(realtype const t, AmiVector const& x) { if (!ny) return; @@ -2086,7 +2086,7 @@ void Model::fdydp(const realtype t, AmiVector const& x) { } } -void Model::fdydx(const realtype t, AmiVector const& x) { +void Model::fdydx(realtype const t, AmiVector const& x) { if (!ny) return; @@ -2420,7 +2420,7 @@ void Model::fdJydx(int const it, AmiVector const& x, ExpData const& edata) { } } -void Model::fz(int const ie, const realtype t, AmiVector const& x) { +void Model::fz(int const ie, realtype const t, AmiVector const& x) { derived_state_.z_.assign(nz, 0.0); @@ -2429,7 +2429,7 @@ void Model::fz(int const ie, const realtype t, AmiVector const& x) { state_.h.data()); } -void Model::fdzdp(int const ie, const realtype t, AmiVector const& x) { +void Model::fdzdp(int const ie, realtype const t, AmiVector const& x) { if (!nz) return; @@ -2448,7 +2448,7 @@ void Model::fdzdp(int const ie, const realtype t, AmiVector const& x) { } } -void Model::fdzdx(int const ie, const realtype t, AmiVector const& x) { +void Model::fdzdx(int const ie, realtype const t, AmiVector const& x) { if (!nz) return; @@ -2465,7 +2465,7 @@ void Model::fdzdx(int const ie, const realtype t, AmiVector const& x) { } } -void Model::frz(int const ie, const realtype t, AmiVector const& x) { +void Model::frz(int const ie, realtype const t, AmiVector const& x) { derived_state_.rz_.assign(nz, 0.0); @@ -2474,7 +2474,7 @@ void Model::frz(int const ie, const realtype t, AmiVector const& x) { state_.h.data()); } -void Model::fdrzdp(int const ie, const realtype t, AmiVector const& x) { +void Model::fdrzdp(int const ie, realtype const t, AmiVector const& x) { if (!nz) return; @@ -2493,7 +2493,7 @@ void Model::fdrzdp(int const ie, const realtype t, AmiVector const& x) { } } -void Model::fdrzdx(int const ie, const realtype t, AmiVector const& x) { +void Model::fdrzdx(int const ie, realtype const t, AmiVector const& x) { if (!nz) return; @@ -2511,7 +2511,7 @@ void Model::fdrzdx(int const ie, const realtype t, AmiVector const& x) { } void Model::fsigmaz( - int const ie, int const nroots, const realtype t, ExpData const* edata + int const ie, int const nroots, realtype const t, ExpData const* edata ) { if (!nz) return; @@ -2547,7 +2547,7 @@ void Model::fsigmaz( } void Model::fdsigmazdp( - int const ie, int const nroots, const realtype t, ExpData const* edata + int const ie, int const nroots, realtype const t, ExpData const* edata ) { if (!nz) return; @@ -2583,7 +2583,7 @@ void Model::fdsigmazdp( } void Model::fdJzdz( - int const ie, int const nroots, const realtype t, AmiVector const& x, + int const ie, int const nroots, realtype const t, AmiVector const& x, ExpData const& edata ) { if (!nz) @@ -2615,7 +2615,7 @@ void Model::fdJzdz( } void Model::fdJzdsigma( - int const ie, int const nroots, const realtype t, AmiVector const& x, + int const ie, int const nroots, realtype const t, AmiVector const& x, ExpData const& edata ) { if (!nz) @@ -2715,7 +2715,7 @@ void Model::fdJzdp( } void Model::fdJzdx( - int const ie, int const nroots, const realtype t, AmiVector const& x, + int const ie, int const nroots, realtype const t, AmiVector const& x, ExpData const& edata ) { // dJzdz nJ x nz x nztrue @@ -2763,7 +2763,7 @@ void Model::fdJzdx( } void Model::fdJrzdz( - int const ie, int const nroots, const realtype t, AmiVector const& x, + int const ie, int const nroots, realtype const t, AmiVector const& x, ExpData const& edata ) { if (!nz) @@ -2794,7 +2794,7 @@ void Model::fdJrzdz( } void Model::fdJrzdsigma( - int const ie, int const nroots, const realtype t, AmiVector const& x, + int const ie, int const nroots, realtype const t, AmiVector const& x, ExpData const& edata ) { if (!nz) @@ -2825,12 +2825,12 @@ void Model::fdJrzdsigma( } } -void Model::fspl(const realtype t) { +void Model::fspl(realtype const t) { for (int ispl = 0; ispl < nspl; ispl++) state_.spl_[ispl] = splines_[ispl].get_value(t); } -void Model::fsspl(const realtype t) { +void Model::fsspl(realtype const t) { derived_state_.sspl_.zero(); realtype* sspl_data = derived_state_.sspl_.data(); for (int ip = 0; ip < nplist(); ip++) { @@ -2840,7 +2840,7 @@ void Model::fsspl(const realtype t) { } } -void Model::fw(const realtype t, realtype const* x) { +void Model::fw(realtype const t, realtype const* x) { std::fill(derived_state_.w_.begin(), derived_state_.w_.end(), 0.0); fspl(t); fw(derived_state_.w_.data(), t, x, state_.unscaledParameters.data(), @@ -2852,7 +2852,7 @@ void Model::fw(const realtype t, realtype const* x) { } } -void Model::fdwdp(const realtype t, realtype const* x) { +void Model::fdwdp(realtype const t, realtype const* x) { if (!nw) return; @@ -2901,7 +2901,7 @@ void Model::fdwdp(const realtype t, realtype const* x) { } } -void Model::fdwdx(const realtype t, realtype const* x) { +void Model::fdwdx(realtype const t, realtype const* x) { if (!nw) return; @@ -2947,7 +2947,7 @@ void Model::fdwdx(const realtype t, realtype const* x) { } } -void Model::fdwdw(const realtype t, realtype const* x) { +void Model::fdwdw(realtype const t, realtype const* x) { if (!nw || !dwdw_.capacity()) return; dwdw_.zero(); diff --git a/src/model_dae.cpp b/src/model_dae.cpp index 43a8d81313..3b2e74e0e1 100644 --- a/src/model_dae.cpp +++ b/src/model_dae.cpp @@ -4,7 +4,7 @@ namespace amici { void Model_DAE::fJ( - const realtype t, const realtype cj, AmiVector const& x, + realtype const t, realtype const cj, AmiVector const& x, AmiVector const& dx, AmiVector const& xdot, SUNMatrix J ) { fJ(t, cj, x.getNVector(), dx.getNVector(), xdot.getNVector(), J); @@ -21,7 +21,7 @@ void Model_DAE::fJ( } void Model_DAE::fJSparse( - const realtype t, const realtype cj, AmiVector const& x, + realtype const t, realtype const cj, AmiVector const& x, AmiVector const& dx, AmiVector const& /*xdot*/, SUNMatrix J ) { fJSparse(t, cj, x.getNVector(), dx.getNVector(), J); @@ -75,9 +75,9 @@ void Model_DAE::fJSparse( } void Model_DAE::fJv( - const realtype t, AmiVector const& x, AmiVector const& dx, + realtype const t, AmiVector const& x, AmiVector const& dx, AmiVector const& /*xdot*/, AmiVector const& v, AmiVector& Jv, - const realtype cj + realtype const cj ) { fJv(t, x.getNVector(), dx.getNVector(), v.getNVector(), Jv.getNVector(), cj); @@ -94,7 +94,7 @@ void Model_DAE::fJv( } void Model_DAE::froot( - const realtype t, AmiVector const& x, AmiVector const& dx, + realtype const t, AmiVector const& x, AmiVector const& dx, gsl::span root ) { froot(t, x.getNVector(), dx.getNVector(), root); @@ -113,7 +113,7 @@ void Model_DAE::froot( } void Model_DAE::fxdot( - const realtype t, AmiVector const& x, AmiVector const& dx, AmiVector& xdot + realtype const t, AmiVector const& x, AmiVector const& dx, AmiVector& xdot ) { fxdot(t, x.getNVector(), dx.getNVector(), xdot.getNVector()); } @@ -132,7 +132,7 @@ void Model_DAE::fxdot( } void Model_DAE::fJDiag( - const realtype t, AmiVector& JDiag, const realtype /*cj*/, + realtype const t, AmiVector& JDiag, realtype const /*cj*/, AmiVector const& x, AmiVector const& dx ) { fJSparse(t, 0.0, x.getNVector(), dx.getNVector(), derived_state_.J_.get()); @@ -143,7 +143,7 @@ void Model_DAE::fJDiag( } void Model_DAE::fdxdotdw( - const realtype t, const_N_Vector x, const const_N_Vector dx + realtype const t, const_N_Vector x, const_N_Vector const dx ) { derived_state_.dxdotdw_.zero(); if (nw > 0 && derived_state_.dxdotdw_.capacity()) { @@ -161,7 +161,7 @@ void Model_DAE::fdxdotdw( } void Model_DAE::fdxdotdp( - const realtype t, const const_N_Vector x, const const_N_Vector dx + realtype const t, const_N_Vector const x, const_N_Vector const dx ) { auto x_pos = computeX_pos(x); @@ -230,7 +230,7 @@ void Model_DAE::fJSparse( } void Model_DAE::froot( - realtype* /*root*/, const realtype /*t*/, realtype const* /*x*/, + realtype* /*root*/, realtype const /*t*/, realtype const* /*x*/, double const* /*p*/, double const* /*k*/, realtype const* /*h*/, realtype const* /*dx*/ ) { @@ -242,7 +242,7 @@ void Model_DAE::froot( } void Model_DAE::fdxdotdp( - realtype* /*dxdotdp*/, const realtype /*t*/, realtype const* /*x*/, + realtype* /*dxdotdp*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, int const /*ip*/, realtype const* /*dx*/, realtype const* /*w*/, realtype const* /*dwdp*/ @@ -255,7 +255,7 @@ void Model_DAE::fdxdotdp( } void Model_DAE::fdxdotdp_explicit( - realtype* /*dxdotdp_explicit*/, const realtype /*t*/, realtype const* /*x*/, + realtype* /*dxdotdp_explicit*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, realtype const* /*dx*/, realtype const* /*w*/ ) { @@ -283,7 +283,7 @@ void Model_DAE::fdxdotdp_explicit_rowvals(SUNMatrixWrapper& /*dxdotdp*/) { } void Model_DAE::fdxdotdx_explicit( - realtype* /*dxdotdx_explicit*/, const realtype /*t*/, realtype const* /*x*/, + realtype* /*dxdotdx_explicit*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, realtype const* /*dx*/, realtype const* /*w*/ ) { @@ -311,7 +311,7 @@ void Model_DAE::fdxdotdx_explicit_rowvals(SUNMatrixWrapper& /*dxdotdx*/) { } void Model_DAE::fdxdotdw( - realtype* /*dxdotdw*/, const realtype /*t*/, realtype const* /*x*/, + realtype* /*dxdotdw*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, realtype const* /*dx*/, realtype const* /*w*/ ) { @@ -339,11 +339,11 @@ void Model_DAE::fdxdotdw_rowvals(SUNMatrixWrapper& /*dxdotdw*/) { } void Model_DAE:: - fM(realtype* /*M*/, const realtype /*t*/, realtype const* /*x*/, + fM(realtype* /*M*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/) {} void Model_DAE::fJB( - const realtype t, realtype cj, AmiVector const& x, AmiVector const& dx, + realtype const t, realtype cj, AmiVector const& x, AmiVector const& dx, AmiVector const& xB, AmiVector const& /*dxB*/, AmiVector const& /*xBdot*/, SUNMatrix JB ) { @@ -362,7 +362,7 @@ void Model_DAE::fJB( } void Model_DAE::fJSparseB( - const realtype t, realtype cj, AmiVector const& x, AmiVector const& dx, + realtype const t, realtype cj, AmiVector const& x, AmiVector const& dx, AmiVector const& xB, AmiVector const& dxB, AmiVector const& /*xBdot*/, SUNMatrix JB ) { @@ -427,7 +427,7 @@ void Model_DAE::fqBdot( } void Model_DAE::fxBdot_ss( - const realtype t, AmiVector const& xB, AmiVector const& dxB, + realtype const t, AmiVector const& xB, AmiVector const& dxB, AmiVector& xBdot ) { fxBdot_ss(t, xB.getNVector(), dxB.getNVector(), xBdot.getNVector()); @@ -459,7 +459,7 @@ void Model_DAE::fJSparseB_ss(SUNMatrix JB) { } void Model_DAE::writeSteadystateJB( - const realtype t, realtype cj, AmiVector const& x, AmiVector const& dx, + realtype const t, realtype cj, AmiVector const& x, AmiVector const& dx, AmiVector const& xB, AmiVector const& dxB, AmiVector const& /*xBdot*/ ) { /* Get backward Jacobian */ @@ -473,7 +473,7 @@ void Model_DAE::writeSteadystateJB( } void Model_DAE::fsxdot( - const realtype t, AmiVector const& x, AmiVector const& dx, int const ip, + realtype const t, AmiVector const& x, AmiVector const& dx, int const ip, AmiVector const& sx, AmiVector const& sdx, AmiVector& sxdot ) { fsxdot( diff --git a/src/model_ode.cpp b/src/model_ode.cpp index 24787df8ad..b3d517b36e 100644 --- a/src/model_ode.cpp +++ b/src/model_ode.cpp @@ -5,7 +5,7 @@ namespace amici { void Model_ODE::fJ( - const realtype t, const realtype /*cj*/, AmiVector const& x, + realtype const t, realtype const /*cj*/, AmiVector const& x, AmiVector const& /*dx*/, AmiVector const& xdot, SUNMatrix J ) { fJ(t, x.getNVector(), xdot.getNVector(), J); @@ -23,7 +23,7 @@ void Model_ODE::fJ( } void Model_ODE::fJSparse( - const realtype t, const realtype /*cj*/, AmiVector const& x, + realtype const t, realtype const /*cj*/, AmiVector const& x, AmiVector const& /*dx*/, AmiVector const& /*xdot*/, SUNMatrix J ) { fJSparse(t, x.getNVector(), J); @@ -69,9 +69,9 @@ void Model_ODE::fJSparse(realtype t, const_N_Vector x, SUNMatrix J) { } void Model_ODE:: - fJv(const realtype t, AmiVector const& x, AmiVector const& /*dx*/, + fJv(realtype const t, AmiVector const& x, AmiVector const& /*dx*/, AmiVector const& /*xdot*/, AmiVector const& v, AmiVector& Jv, - const realtype /*cj*/) { + realtype const /*cj*/) { fJv(v.getNVector(), Jv.getNVector(), t, x.getNVector()); } @@ -85,7 +85,7 @@ void Model_ODE::fJv( } void Model_ODE::froot( - const realtype t, AmiVector const& x, AmiVector const& /*dx*/, + realtype const t, AmiVector const& x, AmiVector const& /*dx*/, gsl::span root ) { froot(t, x.getNVector(), root); @@ -102,7 +102,7 @@ void Model_ODE::froot(realtype t, const_N_Vector x, gsl::span root) { } void Model_ODE::fxdot( - const realtype t, AmiVector const& x, AmiVector const& /*dx*/, + realtype const t, AmiVector const& x, AmiVector const& /*dx*/, AmiVector& xdot ) { fxdot(t, x.getNVector(), xdot.getNVector()); @@ -120,7 +120,7 @@ void Model_ODE::fxdot(realtype t, const_N_Vector x, N_Vector xdot) { } void Model_ODE::fJDiag( - const realtype t, AmiVector& JDiag, const realtype /*cj*/, + realtype const t, AmiVector& JDiag, realtype const /*cj*/, AmiVector const& x, AmiVector const& /*dx*/ ) { fJDiag(t, JDiag.getNVector(), x.getNVector()); @@ -128,7 +128,7 @@ void Model_ODE::fJDiag( throw AmiException("Evaluation of fJDiag failed!"); } -void Model_ODE::fdxdotdw(const realtype t, const_N_Vector x) { +void Model_ODE::fdxdotdw(realtype const t, const_N_Vector x) { derived_state_.dxdotdw_.zero(); if (nw > 0 && derived_state_.dxdotdw_.capacity()) { auto x_pos = computeX_pos(x); @@ -143,7 +143,7 @@ void Model_ODE::fdxdotdw(const realtype t, const_N_Vector x) { } } -void Model_ODE::fdxdotdp(const realtype t, const_N_Vector x) { +void Model_ODE::fdxdotdp(realtype const t, const_N_Vector x) { auto x_pos = computeX_pos(x); fdwdp(t, N_VGetArrayPointerConst(x_pos)); @@ -189,7 +189,7 @@ void Model_ODE::fdxdotdp(const realtype t, const_N_Vector x) { } void Model_ODE:: - fdxdotdp(const realtype t, AmiVector const& x, AmiVector const& /*dx*/) { + fdxdotdp(realtype const t, AmiVector const& x, AmiVector const& /*dx*/) { fdxdotdp(t, x.getNVector()); } @@ -198,7 +198,7 @@ std::unique_ptr Model_ODE::getSolver() { } void Model_ODE::fJSparse( - SUNMatrixContent_Sparse /*JSparse*/, const realtype /*t*/, + SUNMatrixContent_Sparse /*JSparse*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, realtype const* /*w*/, realtype const* /*dwdx*/ ) { @@ -210,7 +210,7 @@ void Model_ODE::fJSparse( } void Model_ODE::fJSparse( - realtype* /*JSparse*/, const realtype /*t*/, realtype const* /*x*/, + realtype* /*JSparse*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, realtype const* /*w*/, realtype const* /*dwdx*/ ) { @@ -238,7 +238,7 @@ void Model_ODE::fJSparse_rowvals(SUNMatrixWrapper& /*JSparse*/) { } void Model_ODE::froot( - realtype* /*root*/, const realtype /*t*/, realtype const* /*x*/, + realtype* /*root*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, realtype const* /*tcl*/ ) { @@ -250,7 +250,7 @@ void Model_ODE::froot( } void Model_ODE::fdxdotdp( - realtype* /*dxdotdp*/, const realtype /*t*/, realtype const* /*x*/, + realtype* /*dxdotdp*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, int const /*ip*/, realtype const* /*w*/, realtype const* /*dwdp*/ ) { @@ -262,7 +262,7 @@ void Model_ODE::fdxdotdp( } void Model_ODE::fdxdotdp_explicit( - realtype* /*dxdotdp_explicit*/, const realtype /*t*/, realtype const* /*x*/, + realtype* /*dxdotdp_explicit*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, realtype const* /*w*/ ) { @@ -290,7 +290,7 @@ void Model_ODE::fdxdotdp_explicit_rowvals(SUNMatrixWrapper& /*dxdotdp*/) { } void Model_ODE::fdxdotdx_explicit( - realtype* /*dxdotdx_explicit*/, const realtype /*t*/, realtype const* /*x*/, + realtype* /*dxdotdx_explicit*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, realtype const* /*w*/ ) { @@ -318,7 +318,7 @@ void Model_ODE::fdxdotdx_explicit_rowvals(SUNMatrixWrapper& /*dxdotdx*/) { } void Model_ODE::fdxdotdw( - realtype* /*dxdotdw*/, const realtype /*t*/, realtype const* /*x*/, + realtype* /*dxdotdw*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, realtype const* /*w*/ ) { @@ -346,7 +346,7 @@ void Model_ODE::fdxdotdw_rowvals(SUNMatrixWrapper& /*dxdotdw*/) { } void Model_ODE::fJB( - const realtype t, realtype /*cj*/, AmiVector const& x, + realtype const t, realtype /*cj*/, AmiVector const& x, AmiVector const& /*dx*/, AmiVector const& xB, AmiVector const& /*dxB*/, AmiVector const& xBdot, SUNMatrix JB ) { @@ -364,7 +364,7 @@ void Model_ODE::fJB( } void Model_ODE::fJSparseB( - const realtype t, realtype /*cj*/, AmiVector const& x, + realtype const t, realtype /*cj*/, AmiVector const& x, AmiVector const& /*dx*/, AmiVector const& xB, AmiVector const& /*dxB*/, AmiVector const& xBdot, SUNMatrix JB ) { @@ -436,7 +436,7 @@ void Model_ODE::fqBdot( } void Model_ODE::fxBdot_ss( - const realtype t, AmiVector const& xB, AmiVector const& /*dx*/, + realtype const t, AmiVector const& xB, AmiVector const& /*dx*/, AmiVector& xBdot ) { fxBdot_ss(t, xB.getNVector(), xBdot.getNVector()); @@ -463,7 +463,7 @@ void Model_ODE::fJSparseB_ss(SUNMatrix JB) { } void Model_ODE::writeSteadystateJB( - const realtype t, realtype /*cj*/, AmiVector const& x, + realtype const t, realtype /*cj*/, AmiVector const& x, AmiVector const& /*dx*/, AmiVector const& xB, AmiVector const& /*dxB*/, AmiVector const& xBdot ) { @@ -478,7 +478,7 @@ void Model_ODE::writeSteadystateJB( } void Model_ODE::fsxdot( - const realtype t, AmiVector const& x, AmiVector const& /*dx*/, int const ip, + realtype const t, AmiVector const& x, AmiVector const& /*dx*/, int const ip, AmiVector const& sx, AmiVector const& /*sdx*/, AmiVector& sxdot ) { fsxdot(t, x.getNVector(), ip, sx.getNVector(), sxdot.getNVector()); diff --git a/src/rdata.cpp b/src/rdata.cpp index e96f295d23..d7b99f3c8a 100644 --- a/src/rdata.cpp +++ b/src/rdata.cpp @@ -707,7 +707,7 @@ void ReturnData::applyChainRuleFactorToSimulationResults(Model const& model) { for (int IND1 = 0; (IND1) < (N1T); ++(IND1)) \ for (int ip = 0; ip < nplist; ++ip) \ for (int IND2 = 0; (IND2) < (N2); ++(IND2)) { \ - s##QUANT.at(((IND2)*nplist + ip) * (N1) + (IND1)) \ + s##QUANT.at(((IND2) * nplist + ip) * (N1) + (IND1)) \ *= pcoefficient.at(ip); \ } diff --git a/src/solver.cpp b/src/solver.cpp index 22e1723640..1a74e8b44b 100644 --- a/src/solver.cpp +++ b/src/solver.cpp @@ -81,7 +81,7 @@ void Solver::apply_max_num_steps_B() const { } } -int Solver::run(const realtype tout) const { +int Solver::run(realtype const tout) const { setStopTime(tout); CpuTimer cpu_timer; int status = AMICI_SUCCESS; @@ -100,7 +100,7 @@ int Solver::run(const realtype tout) const { return status; } -int Solver::step(const realtype tout) const { +int Solver::step(realtype const tout) const { int status = AMICI_SUCCESS; apply_max_num_steps(); @@ -116,7 +116,7 @@ int Solver::step(const realtype tout) const { return status; } -void Solver::runB(const realtype tout) const { +void Solver::runB(realtype const tout) const { CpuTimer cpu_timer; apply_max_num_steps_B(); @@ -128,7 +128,7 @@ void Solver::runB(const realtype tout) const { } void Solver::setup( - const realtype t0, Model* model, AmiVector const& x0, AmiVector const& dx0, + realtype const t0, Model* model, AmiVector const& x0, AmiVector const& dx0, AmiVectorArray const& sx0, AmiVectorArray const& sdx0 ) const { if (nx() != model->nx_solver || nplist() != model->nplist() @@ -196,7 +196,7 @@ void Solver::setup( } void Solver::setupB( - int* which, const realtype tf, Model* model, AmiVector const& xB0, + int* which, realtype const tf, Model* model, AmiVector const& xB0, AmiVector const& dxB0, AmiVector const& xQB0 ) const { if (!solver_memory_) @@ -228,7 +228,7 @@ void Solver::setupB( } void Solver::setupSteadystate( - const realtype t0, Model* model, AmiVector const& x0, AmiVector const& dx0, + realtype const t0, Model* model, AmiVector const& x0, AmiVector const& dx0, AmiVector const& xB0, AmiVector const& dxB0, AmiVector const& xQ0 ) const { /* Initialize CVodes/IDAs solver with steadystate RHS function */ @@ -642,20 +642,20 @@ SensitivityMethod Solver::getSensitivityMethodPreequilibration() const { return sensi_meth_preeq_; } -void Solver::setSensitivityMethod(const SensitivityMethod sensi_meth) { +void Solver::setSensitivityMethod(SensitivityMethod const sensi_meth) { checkSensitivityMethod(sensi_meth, false); this->sensi_meth_ = sensi_meth; } void Solver::setSensitivityMethodPreequilibration( - const SensitivityMethod sensi_meth_preeq + SensitivityMethod const sensi_meth_preeq ) { checkSensitivityMethod(sensi_meth_preeq, true); sensi_meth_preeq_ = sensi_meth_preeq; } void Solver::checkSensitivityMethod( - const SensitivityMethod sensi_meth, bool preequilibration + SensitivityMethod const sensi_meth, bool preequilibration ) const { if (rdata_mode_ == RDataReporting::residuals && sensi_meth == SensitivityMethod::adjoint) @@ -693,7 +693,7 @@ void Solver::setNewtonDampingFactorLowerBound(double dampingFactorLowerBound) { SensitivityOrder Solver::getSensitivityOrder() const { return sensi_; } -void Solver::setSensitivityOrder(const SensitivityOrder sensi) { +void Solver::setSensitivityOrder(SensitivityOrder const sensi) { if (sensi_ != sensi) resetMutableMemory(nx(), nplist(), nquad()); sensi_ = sensi; @@ -950,7 +950,7 @@ void Solver::setMaxStepsBackwardProblem(long int const maxsteps) { LinearMultistepMethod Solver::getLinearMultistepMethod() const { return lmm_; } -void Solver::setLinearMultistepMethod(const LinearMultistepMethod lmm) { +void Solver::setLinearMultistepMethod(LinearMultistepMethod const lmm) { if (solver_memory_) resetMutableMemory(nx(), nplist(), nquad()); lmm_ = lmm; @@ -960,7 +960,7 @@ NonlinearSolverIteration Solver::getNonlinearSolverIteration() const { return iter_; } -void Solver::setNonlinearSolverIteration(const NonlinearSolverIteration iter) { +void Solver::setNonlinearSolverIteration(NonlinearSolverIteration const iter) { if (solver_memory_) resetMutableMemory(nx(), nplist(), nquad()); iter_ = iter; @@ -968,7 +968,7 @@ void Solver::setNonlinearSolverIteration(const NonlinearSolverIteration iter) { InterpolationType Solver::getInterpolationType() const { return interp_type_; } -void Solver::setInterpolationType(const InterpolationType interpType) { +void Solver::setInterpolationType(InterpolationType const interpType) { if (!solver_memory_B_.empty()) resetMutableMemory(nx(), nplist(), nquad()); interp_type_ = interpType; @@ -1020,7 +1020,7 @@ InternalSensitivityMethod Solver::getInternalSensitivityMethod() const { return ism_; } -void Solver::setInternalSensitivityMethod(const InternalSensitivityMethod ism) { +void Solver::setInternalSensitivityMethod(InternalSensitivityMethod const ism) { if (solver_memory_) resetMutableMemory(nx(), nplist(), nquad()); ism_ = ism; @@ -1181,7 +1181,7 @@ void Solver::writeSolutionB( xQB.copy(getAdjointQuadrature(which, *t)); } -AmiVector const& Solver::getState(const realtype t) const { +AmiVector const& Solver::getState(realtype const t) const { if (t == t_) return x_; @@ -1191,7 +1191,7 @@ AmiVector const& Solver::getState(const realtype t) const { return dky_; } -AmiVector const& Solver::getDerivativeState(const realtype t) const { +AmiVector const& Solver::getDerivativeState(realtype const t) const { if (t == t_) return dx_; @@ -1201,7 +1201,7 @@ AmiVector const& Solver::getDerivativeState(const realtype t) const { return dky_; } -AmiVectorArray const& Solver::getStateSensitivity(const realtype t) const { +AmiVectorArray const& Solver::getStateSensitivity(realtype const t) const { if (sens_initialized_ && solver_was_called_F_) { if (t == t_) { getSens(); @@ -1213,7 +1213,7 @@ AmiVectorArray const& Solver::getStateSensitivity(const realtype t) const { } AmiVector const& -Solver::getAdjointState(int const which, const realtype t) const { +Solver::getAdjointState(int const which, realtype const t) const { if (adj_initialized_) { if (solver_was_called_B_) { if (t == t_) { @@ -1229,7 +1229,7 @@ Solver::getAdjointState(int const which, const realtype t) const { } AmiVector const& -Solver::getAdjointDerivativeState(int const which, const realtype t) const { +Solver::getAdjointDerivativeState(int const which, realtype const t) const { if (adj_initialized_) { if (solver_was_called_B_) { if (t == t_) { @@ -1245,7 +1245,7 @@ Solver::getAdjointDerivativeState(int const which, const realtype t) const { } AmiVector const& -Solver::getAdjointQuadrature(int const which, const realtype t) const { +Solver::getAdjointQuadrature(int const which, realtype const t) const { if (adj_initialized_) { if (solver_was_called_B_) { if (t == t_) { diff --git a/src/solver_cvodes.cpp b/src/solver_cvodes.cpp index b53be68c2e..f5150a87c7 100644 --- a/src/solver_cvodes.cpp +++ b/src/solver_cvodes.cpp @@ -104,7 +104,7 @@ static int fsxdot( /* Function implementations */ void CVodeSolver:: - init(const realtype t0, AmiVector const& x0, AmiVector const& /*dx0*/) + init(realtype const t0, AmiVector const& x0, AmiVector const& /*dx0*/) const { solver_was_called_F_ = false; force_reinit_postprocess_F_ = false; @@ -122,7 +122,7 @@ void CVodeSolver:: } void CVodeSolver::initSteadystate( - const realtype /*t0*/, AmiVector const& /*x0*/, AmiVector const& /*dx0*/ + realtype const /*t0*/, AmiVector const& /*x0*/, AmiVector const& /*dx0*/ ) const { // We need to set the steadystate rhs function. Sundials doesn't have this // in its public API, so we have to change it in the solver memory, @@ -161,7 +161,7 @@ void CVodeSolver:: } void CVodeSolver::binit( - int const which, const realtype tf, AmiVector const& xB0, + int const which, realtype const tf, AmiVector const& xB0, AmiVector const& /*dxB0*/ ) const { solver_was_called_B_ = false; @@ -452,12 +452,12 @@ void CVodeSolver::resetState(void* ami_mem, const_N_Vector y0) const { N_VScale(ONE, const_cast(y0), cv_mem->cv_zn[0]); } -void CVodeSolver::reInitPostProcessF(const realtype tnext) const { +void CVodeSolver::reInitPostProcessF(realtype const tnext) const { reInitPostProcess(solver_memory_.get(), &t_, &x_, tnext); force_reinit_postprocess_F_ = false; } -void CVodeSolver::reInitPostProcessB(const realtype tnext) const { +void CVodeSolver::reInitPostProcessB(realtype const tnext) const { realtype tBret; auto cv_mem = static_cast(solver_memory_.get()); auto ca_mem = cv_mem->cv_adj_mem; @@ -477,7 +477,7 @@ void CVodeSolver::reInitPostProcessB(const realtype tnext) const { } void CVodeSolver::reInitPostProcess( - void* ami_mem, realtype* t, AmiVector* yout, const realtype tout + void* ami_mem, realtype* t, AmiVector* yout, realtype const tout ) const { auto cv_mem = static_cast(ami_mem); auto nst_tmp = cv_mem->cv_nst; @@ -499,7 +499,7 @@ void CVodeSolver::reInitPostProcess( ); if (status != CV_SUCCESS) { std::stringstream msg; - msg<<"tout: "<(solver_memory_.get()); cv_mem->cv_tn = t0; @@ -559,7 +559,7 @@ void CVodeSolver:: } void CVodeSolver::reInitB( - int const which, const realtype tB0, AmiVector const& yyB0, + int const which, realtype const tB0, AmiVector const& yyB0, AmiVector const& /*ypB0*/ ) const { auto cv_memB = static_cast( @@ -616,14 +616,14 @@ void CVodeSolver::getSens() const { throw CvodeException(status, "CVodeGetSens"); } -void CVodeSolver::getSensDky(const realtype t, int const k) const { +void CVodeSolver::getSensDky(realtype const t, int const k) const { int status = CVodeGetSensDky(solver_memory_.get(), t, k, sx_.getNVectorArray()); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeGetSens"); } -void CVodeSolver::getDkyB(const realtype t, int const k, int const which) +void CVodeSolver::getDkyB(realtype const t, int const k, int const which) const { int status = CVodeGetDky( CVodeGetAdjCVodeBmem(solver_memory_.get(), which), t, k, @@ -648,7 +648,7 @@ void CVodeSolver::getQuad(realtype& t) const { throw CvodeException(status, "CVodeGetQuad"); } -void CVodeSolver::getQuadDkyB(const realtype t, int const k, int which) const { +void CVodeSolver::getQuadDkyB(realtype const t, int const k, int which) const { int status = CVodeGetQuadDky( CVodeGetAdjCVodeBmem(solver_memory_.get(), which), t, k, xQB_.getNVector() @@ -657,7 +657,7 @@ void CVodeSolver::getQuadDkyB(const realtype t, int const k, int which) const { throw CvodeException(status, "CVodeGetQuadDkyB"); } -void CVodeSolver::getQuadDky(const realtype t, int const k) const { +void CVodeSolver::getQuadDky(realtype const t, int const k) const { int status = CVodeGetQuadDky(solver_memory_.get(), t, k, xQ_.getNVector()); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeGetQuadDky"); @@ -712,7 +712,7 @@ void CVodeSolver::allocateSolverB(int* which) const { } void CVodeSolver::setSStolerancesB( - int const which, const realtype relTolB, const realtype absTolB + int const which, realtype const relTolB, realtype const absTolB ) const { int status = CVodeSStolerancesB(solver_memory_.get(), which, relTolB, absTolB); @@ -721,7 +721,7 @@ void CVodeSolver::setSStolerancesB( } void CVodeSolver::quadSStolerancesB( - int const which, const realtype reltolQB, const realtype abstolQB + int const which, realtype const reltolQB, realtype const abstolQB ) const { int status = CVodeQuadSStolerancesB( solver_memory_.get(), which, reltolQB, abstolQB @@ -731,7 +731,7 @@ void CVodeSolver::quadSStolerancesB( } void CVodeSolver::quadSStolerances( - const realtype reltolQB, const realtype abstolQB + realtype const reltolQB, realtype const abstolQB ) const { int status = CVodeQuadSStolerances(solver_memory_.get(), reltolQB, abstolQB); @@ -747,7 +747,7 @@ void CVodeSolver::getB(int const which) const { throw CvodeException(status, "CVodeGetB"); } -int CVodeSolver::solve(const realtype tout, int const itask) const { +int CVodeSolver::solve(realtype const tout, int const itask) const { if (force_reinit_postprocess_F_) reInitPostProcessF(tout); int status = CVode(solver_memory_.get(), tout, x_.getNVector(), &t_, itask); @@ -757,7 +757,7 @@ int CVodeSolver::solve(const realtype tout, int const itask) const { return status; } -int CVodeSolver::solveF(const realtype tout, int const itask, int* ncheckPtr) +int CVodeSolver::solveF(realtype const tout, int const itask, int* ncheckPtr) const { if (force_reinit_postprocess_F_) reInitPostProcessF(tout); @@ -770,7 +770,7 @@ int CVodeSolver::solveF(const realtype tout, int const itask, int* ncheckPtr) return status; } -void CVodeSolver::solveB(const realtype tBout, int const itaskB) const { +void CVodeSolver::solveB(realtype const tBout, int const itaskB) const { if (force_reinit_postprocess_B_) reInitPostProcessB(tBout); int status = CVodeB(solver_memory_.get(), tBout, itaskB); @@ -839,12 +839,12 @@ void* CVodeSolver::getAdjBmem(void* ami_mem, int which) const { return CVodeGetAdjCVodeBmem(ami_mem, which); } -void CVodeSolver::calcIC(const realtype /*tout1*/) const {}; +void CVodeSolver::calcIC(realtype const /*tout1*/) const {}; -void CVodeSolver::calcICB(int const /*which*/, const realtype /*tout1*/) - const {}; +void CVodeSolver::calcICB(int const /*which*/, realtype const /*tout1*/) const { +}; -void CVodeSolver::setStopTime(const realtype tstop) const { +void CVodeSolver::setStopTime(realtype const tstop) const { int status = CVodeSetStopTime(solver_memory_.get(), tstop); if (status != CV_SUCCESS) throw CvodeException(status, "CVodeSetStopTime"); diff --git a/src/solver_idas.cpp b/src/solver_idas.cpp index 63f65b184d..dac085e98d 100644 --- a/src/solver_idas.cpp +++ b/src/solver_idas.cpp @@ -100,7 +100,7 @@ static int fsxdot( /* Function implementations */ void IDASolver::init( - const realtype t0, AmiVector const& x0, AmiVector const& dx0 + realtype const t0, AmiVector const& x0, AmiVector const& dx0 ) const { int status; solver_was_called_F_ = false; @@ -122,7 +122,7 @@ void IDASolver::init( } void IDASolver::initSteadystate( - const realtype /*t0*/, AmiVector const& /*x0*/, AmiVector const& /*dx0*/ + realtype const /*t0*/, AmiVector const& /*x0*/, AmiVector const& /*dx0*/ ) const { /* We need to set the steadystate rhs function. SUndials doesn't have this in its public api, so we have to change it in the solver memory, @@ -157,7 +157,7 @@ void IDASolver::sensInit1(AmiVectorArray const& sx0, AmiVectorArray const& sdx0) } void IDASolver::binit( - int const which, const realtype tf, AmiVector const& xB0, + int const which, realtype const tf, AmiVector const& xB0, AmiVector const& dxB0 ) const { int status; @@ -263,13 +263,13 @@ void IDASolver::allocateSolver() const { ); } -void IDASolver::setSStolerances(const realtype rtol, const realtype atol) +void IDASolver::setSStolerances(realtype const rtol, realtype const atol) const { int status = IDASStolerances(solver_memory_.get(), rtol, atol); if (status != IDA_SUCCESS) throw IDAException(status, "IDASStolerances"); } -void IDASolver::setSensSStolerances(const realtype rtol, realtype const* atol) +void IDASolver::setSensSStolerances(realtype const rtol, realtype const* atol) const { int status = IDASensSStolerances( solver_memory_.get(), rtol, const_cast(atol) @@ -374,11 +374,11 @@ void IDASolver::resetState( ida_mem->ida_kk = 0; } -void IDASolver::reInitPostProcessF(const realtype tnext) const { +void IDASolver::reInitPostProcessF(realtype const tnext) const { reInitPostProcess(solver_memory_.get(), &t_, &x_, &dx_, tnext); } -void IDASolver::reInitPostProcessB(const realtype tnext) const { +void IDASolver::reInitPostProcessB(realtype const tnext) const { realtype tBret; auto ida_mem = static_cast(solver_memory_.get()); auto idaadj_mem = ida_mem->ida_adj_mem; @@ -445,7 +445,7 @@ void IDASolver::reInitPostProcess( } void IDASolver::reInit( - const realtype t0, AmiVector const& yy0, AmiVector const& yp0 + realtype const t0, AmiVector const& yy0, AmiVector const& yp0 ) const { auto ida_mem = static_cast(solver_memory_.get()); @@ -492,7 +492,7 @@ void IDASolver::sensToggleOff() const { } void IDASolver::reInitB( - int const which, const realtype tB0, AmiVector const& yyB0, + int const which, realtype const tB0, AmiVector const& yyB0, AmiVector const& ypB0 ) const { @@ -526,7 +526,7 @@ void IDASolver::setSensParams( throw IDAException(status, "IDASetSensParams"); } -void IDASolver::getDky(const realtype t, int const k) const { +void IDASolver::getDky(realtype const t, int const k) const { int status = IDAGetDky(solver_memory_.get(), t, k, dky_.getNVector()); if (status != IDA_SUCCESS) throw IDAException(status, "IDAGetDky"); @@ -540,7 +540,7 @@ void IDASolver::getSens() const { throw IDAException(status, "IDAGetSens"); } -void IDASolver::getSensDky(const realtype t, int const k) const { +void IDASolver::getSensDky(realtype const t, int const k) const { int status = IDAGetSensDky(solver_memory_.get(), t, k, sx_.getNVectorArray()); if (status != IDA_SUCCESS) @@ -557,7 +557,7 @@ void IDASolver::getB(int const which) const { throw IDAException(status, "IDAGetB"); } -void IDASolver::getDkyB(const realtype t, int k, int const which) const { +void IDASolver::getDkyB(realtype const t, int k, int const which) const { int status = IDAGetDky( IDAGetAdjIDABmem(solver_memory_.get(), which), t, k, dky_.getNVector() ); @@ -578,7 +578,7 @@ void IDASolver::getQuad(realtype& t) const { throw IDAException(status, "IDAGetQuad"); } -void IDASolver::getQuadDkyB(const realtype t, int k, int const which) const { +void IDASolver::getQuadDkyB(realtype const t, int k, int const which) const { int status = IDAGetQuadDky( IDAGetAdjIDABmem(solver_memory_.get(), which), t, k, xQB_.getNVector() ); @@ -586,7 +586,7 @@ void IDASolver::getQuadDkyB(const realtype t, int k, int const which) const { throw IDAException(status, "IDAGetB"); } -void IDASolver::getQuadDky(const realtype t, int const k) const { +void IDASolver::getQuadDky(realtype const t, int const k) const { int status = IDAGetQuadDky(solver_memory_.get(), t, k, xQ_.getNVector()); if (status != IDA_SUCCESS) throw IDAException(status, "IDAGetQuadDky"); @@ -639,7 +639,7 @@ void IDASolver::allocateSolverB(int* which) const { } void IDASolver::setSStolerancesB( - int const which, const realtype relTolB, const realtype absTolB + int const which, realtype const relTolB, realtype const absTolB ) const { int status = IDASStolerancesB(solver_memory_.get(), which, relTolB, absTolB); @@ -648,7 +648,7 @@ void IDASolver::setSStolerancesB( } void IDASolver::quadSStolerancesB( - int const which, const realtype reltolQB, const realtype abstolQB + int const which, realtype const reltolQB, realtype const abstolQB ) const { int status = IDAQuadSStolerancesB(solver_memory_.get(), which, reltolQB, abstolQB); @@ -657,14 +657,14 @@ void IDASolver::quadSStolerancesB( } void IDASolver::quadSStolerances( - const realtype reltolQB, const realtype abstolQB + realtype const reltolQB, realtype const abstolQB ) const { int status = IDAQuadSStolerances(solver_memory_.get(), reltolQB, abstolQB); if (status != IDA_SUCCESS) throw IDAException(status, "IDAQuadSStolerances"); } -int IDASolver::solve(const realtype tout, int const itask) const { +int IDASolver::solve(realtype const tout, int const itask) const { if (force_reinit_postprocess_F_) reInitPostProcessF(tout); int status = IDASolve( @@ -677,7 +677,7 @@ int IDASolver::solve(const realtype tout, int const itask) const { return status; } -int IDASolver::solveF(const realtype tout, int const itask, int* ncheckPtr) +int IDASolver::solveF(realtype const tout, int const itask, int* ncheckPtr) const { if (force_reinit_postprocess_F_) reInitPostProcessF(tout); @@ -691,7 +691,7 @@ int IDASolver::solveF(const realtype tout, int const itask, int* ncheckPtr) return status; } -void IDASolver::solveB(const realtype tBout, int const itaskB) const { +void IDASolver::solveB(realtype const tBout, int const itaskB) const { if (force_reinit_postprocess_B_) reInitPostProcessB(tBout); int status = IDASolveB(solver_memory_.get(), tBout, itaskB); @@ -768,7 +768,7 @@ void IDASolver::calcIC(realtype tout1) const { throw IDAException(status, "IDACalcIC"); } -void IDASolver::calcICB(int const which, const realtype tout1) const { +void IDASolver::calcICB(int const which, realtype const tout1) const { int status = IDACalcICB( solver_memory_.get(), which, tout1, xB_.getNVector(), dxB_.getNVector() ); @@ -776,7 +776,7 @@ void IDASolver::calcICB(int const which, const realtype tout1) const { throw IDAException(status, "IDACalcICB"); } -void IDASolver::setStopTime(const realtype tstop) const { +void IDASolver::setStopTime(realtype const tstop) const { int status = IDASetStopTime(solver_memory_.get(), tstop); if (status != IDA_SUCCESS) throw IDAException(status, "IDASetStopTime"); diff --git a/src/splinefunctions.cpp b/src/splinefunctions.cpp index 8c888b450a..f5d2599ea7 100644 --- a/src/splinefunctions.cpp +++ b/src/splinefunctions.cpp @@ -54,18 +54,18 @@ AbstractSpline::AbstractSpline( } } -realtype AbstractSpline::get_value(const realtype t) const { +realtype AbstractSpline::get_value(realtype const t) const { auto y = get_value_scaled(t); return logarithmic_parametrization_ ? std::exp(y) : y; } -realtype AbstractSpline::get_sensitivity(const realtype t, int const ip) const { +realtype AbstractSpline::get_sensitivity(realtype const t, int const ip) const { auto s = get_sensitivity_scaled(t, ip); return logarithmic_parametrization_ ? s * get_value(t) : s; } realtype AbstractSpline::get_sensitivity( - const realtype t, int const ip, const realtype value + realtype const t, int const ip, realtype const value ) const { auto s = get_sensitivity_scaled(t, ip); return logarithmic_parametrization_ ? s * value : s; @@ -387,12 +387,12 @@ void HermiteSpline::compute_coefficients_extrapolation() { #ifdef DVALUESDP #error "Preprocessor macro DVALUESDP already defined?!" #else -#define DVALUESDP(i_node) dvaluesdp[node_offset + (i_node)*nplist] +#define DVALUESDP(i_node) dvaluesdp[node_offset + (i_node) * nplist] #endif #ifdef DSLOPESDP #error "Preprocessor macro DSLOPESDP already defined?!" #else -#define DSLOPESDP(i_node) dslopesdp[node_offset + (i_node)*nplist] +#define DSLOPESDP(i_node) dslopesdp[node_offset + (i_node) * nplist] #endif void HermiteSpline::compute_coefficients_sensi( @@ -821,7 +821,7 @@ void HermiteSpline::compute_final_sensitivity( set_final_sensitivity_scaled(finalSensitivity); } -realtype HermiteSpline::get_value_scaled(const realtype t) const { +realtype HermiteSpline::get_value_scaled(realtype const t) const { /* Is this a steady state computation? */ if (std::isinf(t)) return get_final_value_scaled(); @@ -922,7 +922,7 @@ realtype HermiteSpline::get_value_scaled(const realtype t) const { } realtype -HermiteSpline::get_sensitivity_scaled(const realtype t, int const ip) const { +HermiteSpline::get_sensitivity_scaled(realtype const t, int const ip) const { /* Is this a steady state computation? */ if (std::isinf(t)) return get_final_sensitivity_scaled(ip); diff --git a/src/sundials_matrix_wrapper.cpp b/src/sundials_matrix_wrapper.cpp index a86574cd82..c415c26942 100644 --- a/src/sundials_matrix_wrapper.cpp +++ b/src/sundials_matrix_wrapper.cpp @@ -214,7 +214,7 @@ void SUNMatrixWrapper::scale(realtype a) { } void SUNMatrixWrapper::multiply( - N_Vector c, const_N_Vector b, const realtype alpha + N_Vector c, const_N_Vector b, realtype const alpha ) const { multiply( gsl::make_span(NV_DATA_S(c), NV_LENGTH_S(c)), @@ -233,7 +233,7 @@ inline static void check_csc(SUNMatrixWrapper const* /*mat*/) {} #endif void SUNMatrixWrapper::multiply( - gsl::span c, gsl::span b, const realtype alpha + gsl::span c, gsl::span b, realtype const alpha ) const { if (!matrix_) @@ -522,8 +522,8 @@ void SUNMatrixWrapper::sparse_sum(std::vector const& mats) { } sunindextype SUNMatrixWrapper::scatter( - const sunindextype acol, const realtype beta, sunindextype* w, - gsl::span x, const sunindextype mark, SUNMatrixWrapper* C, + sunindextype const acol, realtype const beta, sunindextype* w, + gsl::span x, sunindextype const mark, SUNMatrixWrapper* C, sunindextype nnz ) const { if (!matrix_) @@ -576,7 +576,7 @@ static void cumsum(gsl::span p, std::vector& c) { } void SUNMatrixWrapper::transpose( - SUNMatrixWrapper& C, const realtype alpha, sunindextype blocksize + SUNMatrixWrapper& C, realtype const alpha, sunindextype blocksize ) const { if (!matrix_ || !C.matrix_) return; @@ -804,7 +804,7 @@ unravel_index(sunindextype i, SUNMatrix m) { ++col; // This can happen if indexvals / indexptrs haven't been set. - if(col == ncols) + if (col == ncols) return {-1, -1}; gsl_EnsuresDebug(row >= 0); diff --git a/swig/CMakeLists.txt b/swig/CMakeLists.txt index 87d607d33a..73faccda7e 100644 --- a/swig/CMakeLists.txt +++ b/swig/CMakeLists.txt @@ -112,10 +112,9 @@ if(${SWIG_VERSION} VERSION_LESS 4.1.0) PROPERTY SWIG_COMPILE_OPTIONS -py3) endif() -# NOTE: No public definitions of any dependency are forwarded to swig, -# they are only used for compiling the swig-generated source file. -# Any definition that are relevant for swig-code generation, need to be -# forwarded manually. +# NOTE: No public definitions of any dependency are forwarded to swig, they are +# only used for compiling the swig-generated source file. Any definition that +# are relevant for swig-code generation, need to be forwarded manually. target_link_libraries(_amici amici Python3::Python) if(WIN32) add_custom_command( From 26bb902441d64032124968598323a1a29404f201 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Thu, 1 Feb 2024 14:13:55 +0100 Subject: [PATCH 013/188] Fix invalid type error when loading antimony model from file (#2281) * Fix invalid type error when loading antimony model from file `pathlib.Path` -> `str` * More informative exceptions --- python/sdist/amici/antimony_import.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/python/sdist/amici/antimony_import.py b/python/sdist/amici/antimony_import.py index cc23079787..545a2654bd 100644 --- a/python/sdist/amici/antimony_import.py +++ b/python/sdist/amici/antimony_import.py @@ -28,11 +28,13 @@ def antimony2sbml(ant_model: Union[str, Path]) -> str: is_file = False if is_file: - status = ant.loadAntimonyFile(ant_model) + status = ant.loadAntimonyFile(str(ant_model)) else: status = ant.loadAntimonyString(ant_model) if status < 0: - raise RuntimeError("Antimony model could not be loaded.") + raise RuntimeError( + f"Antimony model could not be loaded: {ant.getLastError()}" + ) if (main_module_name := ant.getMainModuleName()) is None: raise AssertionError("There is no Antimony module.") From 37975ed05c09d5365c8397c845c713741a76e7ee Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Thu, 1 Feb 2024 14:15:12 +0100 Subject: [PATCH 014/188] Fix type annotation for `import_model_module` (#2282) The second argument never was optional. Not sure whether there would be any good default value. --- python/sdist/amici/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/sdist/amici/__init__.py b/python/sdist/amici/__init__.py index b06ea6d0f3..204933565f 100644 --- a/python/sdist/amici/__init__.py +++ b/python/sdist/amici/__init__.py @@ -13,7 +13,7 @@ import sys from pathlib import Path from types import ModuleType as ModelModule -from typing import Any, Callable, Optional, Union +from typing import Any, Callable, Union def _get_amici_path(): @@ -153,7 +153,7 @@ def __exit__(self, exc_type, exc_value, traceback): def import_model_module( - module_name: str, module_path: Optional[Union[Path, str]] = None + module_name: str, module_path: Union[Path, str] ) -> ModelModule: """ Import Python module of an AMICI model From 5e64ed4d24d2f13ed1928ca7a9b52eee6893c706 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Tue, 6 Feb 2024 14:00:17 +0100 Subject: [PATCH 015/188] Update release notes, bump version --- CHANGELOG.md | 12 ++++++++++++ version.txt | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fa8153e35..45edeb8adf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ ## v0.X Series +### v0.21.2 (2024-02-06) + +* Fixed `Solver` copyctor issues with swig4.2 that resulted in installation + errors + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2276 +* Fixed error when calling `amici.ExpData()` + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2280 +* Fixed invalid-type-error when loading an antimony model from file + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2281 + +**Full Changelog**: https://github.com/AMICI-dev/AMICI/compare/v0.21.1...v0.21.2 + ### v0.21.1 (2024-01-17) Fixed package configuration for PyPI upload. No further changes. diff --git a/version.txt b/version.txt index a67cebaf7f..59dad104b0 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.21.1 +0.21.2 From 5bd921f41b902f4ced64c511890e8c8a896c93af Mon Sep 17 00:00:00 2001 From: Dilan Pathirana <59329744+dilpath@users.noreply.github.com> Date: Fri, 16 Feb 2024 19:45:09 +0100 Subject: [PATCH 016/188] User option to fail if model needs to be compiled (#2289) * support "do not compile" for petab problems * rewrite * do not pass new kwarg to sbml2amici --- documentation/ExampleJax.ipynb | 2 +- python/examples/example_errors.ipynb | 8 +++--- python/examples/example_jax/ExampleJax.ipynb | 2 +- python/sdist/amici/petab/petab_import.py | 29 +++++++++++++++----- tests/petab_test_suite/test_petab_suite.py | 2 +- 5 files changed, 29 insertions(+), 14 deletions(-) diff --git a/documentation/ExampleJax.ipynb b/documentation/ExampleJax.ipynb index c9fbb589e5..53e03788da 100644 --- a/documentation/ExampleJax.ipynb +++ b/documentation/ExampleJax.ipynb @@ -572,7 +572,7 @@ "source": [ "from amici.petab.petab_import import import_petab_problem\n", "\n", - "amici_model = import_petab_problem(petab_problem, force_compile=True)" + "amici_model = import_petab_problem(petab_problem, compile_=True)" ] }, { diff --git a/python/examples/example_errors.ipynb b/python/examples/example_errors.ipynb index 2b35964d8b..3bf81aff61 100644 --- a/python/examples/example_errors.ipynb +++ b/python/examples/example_errors.ipynb @@ -77,7 +77,7 @@ "source": [ "petab_problem = benchmark_models_petab.get_problem(\"Fujita_SciSignal2010\")\n", "amici_model = import_petab_problem(\n", - " petab_problem, verbose=False, force_compile=False\n", + " petab_problem, verbose=False, compile_=None\n", ")\n", "\n", "np.random.seed(2991)\n", @@ -423,7 +423,7 @@ "source": [ "petab_problem = benchmark_models_petab.get_problem(\"Weber_BMC2015\")\n", "amici_model = import_petab_problem(\n", - " petab_problem, verbose=False, force_compile=False\n", + " petab_problem, verbose=False, compile_=None\n", ")\n", "\n", "np.random.seed(4)\n", @@ -699,7 +699,7 @@ "amici_model = import_petab_problem(\n", " petab_problem,\n", " verbose=False,\n", - " force_compile=True,\n", + " compile_=True,\n", " model_name=\"Blasi_CellSystems2016_1\",\n", ")\n", "\n", @@ -819,7 +819,7 @@ " # we need a different model name if we import the model again\n", " # we cannot load a model with the same name as an already loaded model\n", " model_name=\"Blasi_CellSystems2016_2\",\n", - " force_compile=True,\n", + " compile_=True,\n", ")\n", "del os.environ[\"AMICI_EXPERIMENTAL_SBML_NONCONST_CLS\"]\n", "\n", diff --git a/python/examples/example_jax/ExampleJax.ipynb b/python/examples/example_jax/ExampleJax.ipynb index 225bd13667..62391ce9be 100644 --- a/python/examples/example_jax/ExampleJax.ipynb +++ b/python/examples/example_jax/ExampleJax.ipynb @@ -265,7 +265,7 @@ "from amici.petab.petab_import import import_petab_problem\n", "\n", "amici_model = import_petab_problem(\n", - " petab_problem, force_compile=True, verbose=False\n", + " petab_problem, compile_=True, verbose=False\n", ")" ] }, diff --git a/python/sdist/amici/petab/petab_import.py b/python/sdist/amici/petab/petab_import.py index 558f51ca15..902bf837ef 100644 --- a/python/sdist/amici/petab/petab_import.py +++ b/python/sdist/amici/petab/petab_import.py @@ -35,7 +35,7 @@ def import_petab_problem( petab_problem: petab.Problem, model_output_dir: Union[str, Path, None] = None, model_name: str = None, - force_compile: bool = False, + compile_: bool = None, non_estimated_parameters_as_constants=True, **kwargs, ) -> "amici.Model": @@ -53,9 +53,10 @@ def import_petab_problem( Name of the generated model module. Defaults to the ID of the model or the model file name without the extension. - :param force_compile: - Whether to compile the model even if the target folder is not empty, - or the model exists already. + :param compile_: + If ``True``, the model will be compiled. If ``False``, the model will + not be compiled. If ``None``, the model will be compiled if it cannot + be imported. :param non_estimated_parameters_as_constants: Whether parameters marked as non-estimated in PEtab should be @@ -71,6 +72,17 @@ def import_petab_problem( :return: The imported model. """ + if "force_compile" in kwargs: + if kwargs["force_compile"]: + compile_ = True + del kwargs["force_compile"] + warn( + "The `force_compile` option is deprecated, please use the " + "new `compile_` option, which also supports 'do not compile'.", + DeprecationWarning, + stacklevel=2, + ) + if petab_problem.model.type_id not in (MODEL_TYPE_SBML, MODEL_TYPE_PYSB): raise NotImplementedError( "Unsupported model type " + petab_problem.model.type_id @@ -107,12 +119,15 @@ def import_petab_problem( os.makedirs(model_output_dir) # check if compilation necessary - if force_compile or not _can_import_model(model_name, model_output_dir): + if compile_ or ( + compile_ is None + and not _can_import_model(model_name, model_output_dir) + ): # check if folder exists - if os.listdir(model_output_dir) and not force_compile: + if os.listdir(model_output_dir) and not compile_: raise ValueError( f"Cannot compile to {model_output_dir}: not empty. " - "Please assign a different target or set `force_compile`." + "Please assign a different target or set `compile_` to `True`." ) # remove folder if exists diff --git a/tests/petab_test_suite/test_petab_suite.py b/tests/petab_test_suite/test_petab_suite.py index 0924c09576..6d08cfb693 100755 --- a/tests/petab_test_suite/test_petab_suite.py +++ b/tests/petab_test_suite/test_petab_suite.py @@ -62,7 +62,7 @@ def _test_case(case, model_type, version): petab_problem=problem, model_output_dir=model_output_dir, model_name=model_name, - force_compile=True, + compile_=True, ) solver = model.getSolver() solver.setSteadyStateToleranceFactor(1.0) From ebd374a3c5249dff6746281bf9f1bfe46efebea9 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Tue, 20 Feb 2024 11:18:22 +0100 Subject: [PATCH 017/188] GHA: Skip codecov action on pushes to forks (#2293) Submitting coverage results to codecov from push-triggered actions from forks will result in missing-token errors. Therefore, skip codecov submission in those cases. Reverts some removals from https://github.com/AMICI-dev/AMICI/pull/2284. --- .github/workflows/test_petab_test_suite.yml | 1 + .github/workflows/test_python_cplusplus.yml | 4 ++++ .github/workflows/test_sbml_semantic_test_suite.yml | 1 + 3 files changed, 6 insertions(+) diff --git a/.github/workflows/test_petab_test_suite.yml b/.github/workflows/test_petab_test_suite.yml index 1af341009f..6301269e03 100644 --- a/.github/workflows/test_petab_test_suite.yml +++ b/.github/workflows/test_petab_test_suite.yml @@ -85,6 +85,7 @@ jobs: tests/petab_test_suite/ - name: Codecov + if: github.event_name == 'pull_request' || github.repository_owner == 'AMICI-dev' uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/test_python_cplusplus.yml b/.github/workflows/test_python_cplusplus.yml index 507283b156..fb90476eb8 100644 --- a/.github/workflows/test_python_cplusplus.yml +++ b/.github/workflows/test_python_cplusplus.yml @@ -66,6 +66,7 @@ jobs: ${AMICI_DIR}/python/tests/test_splines.py - name: Codecov Python + if: github.event_name == 'pull_request' || github.repository_owner == 'AMICI-dev' uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} @@ -85,6 +86,7 @@ jobs: && lcov -a coverage_cpp.info -a coverage_py.info -o coverage.info - name: Codecov CPP + if: github.event_name == 'pull_request' || github.repository_owner == 'AMICI-dev' uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} @@ -139,6 +141,7 @@ jobs: ${AMICI_DIR}/python/tests/test_splines_short.py - name: Codecov Python + if: github.event_name == 'pull_request' || github.repository_owner == 'AMICI-dev' uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} @@ -158,6 +161,7 @@ jobs: && lcov -a coverage_cpp.info -a coverage_py.info -o coverage.info - name: Codecov CPP + if: github.event_name == 'pull_request' || github.repository_owner == 'AMICI-dev' uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/test_sbml_semantic_test_suite.yml b/.github/workflows/test_sbml_semantic_test_suite.yml index cf03a5d458..ddc78e1b89 100644 --- a/.github/workflows/test_sbml_semantic_test_suite.yml +++ b/.github/workflows/test_sbml_semantic_test_suite.yml @@ -54,6 +54,7 @@ jobs: path: tests/amici-semantic-results - name: Codecov SBMLSuite + if: github.event_name == 'pull_request' || github.repository_owner == 'AMICI-dev' uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} From 043480232bf42de9780be9fb6894f43323a02caf Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Tue, 20 Feb 2024 11:19:01 +0100 Subject: [PATCH 018/188] More informative error message for ReturnDataView.by_id (#2295) --- python/sdist/amici/numpy.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/sdist/amici/numpy.py b/python/sdist/amici/numpy.py index fdf802147c..6e2966ba4b 100644 --- a/python/sdist/amici/numpy.py +++ b/python/sdist/amici/numpy.py @@ -359,7 +359,8 @@ def by_id( ) or self._swigptr.parameter_ids else: raise NotImplementedError( - f"Subsetting {field} by ID is not implemented or not possible." + f"Subsetting `{field}` by ID (`{entity_id}`) " + "is not implemented or not possible." ) col_index = ids.index(entity_id) return getattr(self, field)[:, ..., col_index] From 24685a39c38f32786ad7ac5a9a9a0b720a4cd288 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Tue, 20 Feb 2024 11:20:07 +0100 Subject: [PATCH 019/188] GHA: update deploy_protected.yml (#2294) Closes #2288 Fixes: 1. > Node.js 16 actions are deprecated. Please update the following actions to use Node.js 20: docker/setup-qemu-action@v2, docker/setup-buildx-action@v2. For more information see: https://github.blog/changelog/2023-09-22-github-actions-transitioning-from-node-16-to-node-20/. 2. Prevent errors on fork actions: > Run elgohr/Publish-Docker-Github-Action@v4 Run $GITHUB_ACTION_PATH/entrypoint.sh Unable to find the username. Did you set with.username? Either check for organization=amici-dev, or whether the username is set. --- .github/workflows/deploy_protected.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deploy_protected.yml b/.github/workflows/deploy_protected.yml index c520896a70..eaf625ea22 100644 --- a/.github/workflows/deploy_protected.yml +++ b/.github/workflows/deploy_protected.yml @@ -14,9 +14,8 @@ on: jobs: dockerhub: - # https://github.com/marketplace/actions/publish-docker name: Deploy Dockerhub - + if: github.repository_owner == 'AMICI-dev' runs-on: ubuntu-22.04 strategy: @@ -31,9 +30,9 @@ jobs: - uses: actions/checkout@v4 - run: git archive -o container/amici.tar.gz --format=tar.gz HEAD - name: Set up QEMU - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Publish to Registry uses: elgohr/Publish-Docker-Github-Action@v4 with: From f9bfefc8229093a58964bf400e2b54524443984f Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Tue, 20 Feb 2024 12:19:11 +0100 Subject: [PATCH 020/188] Refactor DEModel.splines -> DEModel._splines (#2292) To be consistent with all other model components. --- python/sdist/amici/de_export.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/python/sdist/amici/de_export.py b/python/sdist/amici/de_export.py index 4e7e0999f2..12cde4630f 100644 --- a/python/sdist/amici/de_export.py +++ b/python/sdist/amici/de_export.py @@ -768,7 +768,7 @@ def __init__( self._expressions: list[Expression] = [] self._conservation_laws: list[ConservationLaw] = [] self._events: list[Event] = [] - self.splines = [] + self._splines = [] self._symboldim_funs: dict[str, Callable[[], int]] = { "sx": self.num_states_solver, "v": self.num_states_solver, @@ -968,7 +968,7 @@ def import_from_sbml_importer( value=spline_expr, ) ) - self.splines = si.splines + self._splines = si.splines # get symbolic expression from SBML importers symbols = copy.copy(si.symbols) @@ -1690,7 +1690,7 @@ def _generate_symbol(self, name: str) -> None: # placeholders for the numeric spline values. # Need to create symbols self._syms[name] = sp.Matrix( - [[f"spl_{isp}" for isp in range(len(self.splines))]] + [[f"spl_{isp}" for isp in range(len(self._splines))]] ) return elif name == "sspl": @@ -1698,7 +1698,7 @@ def _generate_symbol(self, name: str) -> None: self._syms[name] = sp.Matrix( [ [f"sspl_{isp}_{ip}" for ip in range(len(self._syms["p"]))] - for isp in range(len(self.splines)) + for isp in range(len(self._splines)) ] ) return @@ -2050,7 +2050,7 @@ def _compute_equation(self, name: str) -> None: elif name == "spline_values": # force symbols self._eqs[name] = sp.Matrix( - [y for spline in self.splines for y in spline.values_at_nodes] + [y for spline in self._splines for y in spline.values_at_nodes] ) elif name == "spline_slopes": @@ -2058,7 +2058,7 @@ def _compute_equation(self, name: str) -> None: self._eqs[name] = sp.Matrix( [ d - for spline in self.splines + for spline in self._splines for d in ( sp.zeros(len(spline.derivatives_at_nodes), 1) if spline.derivatives_by_fd @@ -2892,7 +2892,7 @@ def __init__( self.model: DEModel = de_model self.model._code_printer.known_functions.update( splines.spline_user_functions( - self.model.splines, self._get_index("p") + self.model._splines, self._get_index("p") ) ) @@ -3553,14 +3553,14 @@ def _get_function_body( return [line for line in lines if line] def _get_create_splines_body(self): - if not self.model.splines: + if not self.model._splines: return [" return {};"] ind4 = " " * 4 ind8 = " " * 8 body = ["return {"] - for ispl, spline in enumerate(self.model.splines): + for ispl, spline in enumerate(self.model._splines): if isinstance(spline.nodes, splines.UniformGrid): nodes = ( f"{ind8}{{{spline.nodes.start}, {spline.nodes.stop}}}, " @@ -3674,7 +3674,7 @@ def _write_model_header_cpp(self) -> None: "NEVENT": self.model.num_events(), "NEVENT_SOLVER": self.model.num_events_solver(), "NOBJECTIVE": "1", - "NSPL": len(self.model.splines), + "NSPL": len(self.model._splines), "NW": len(self.model.sym("w")), "NDWDP": len( self.model.sparsesym( From 35731318e19e88a8b3e68b23007066958b112444 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Tue, 20 Feb 2024 14:14:31 +0100 Subject: [PATCH 021/188] Fix `ENABLE_AMICI_DEBUGGING=TRUE` not working with MSVC (#2296) Fixes ``` cl : Command line warning D9002 : ignoring unknown option '-O0' cl : Command line warning D9002 : ignoring unknown option '-g' ``` see also https://learn.microsoft.com/en-us/cpp/build/reference/debug-generate-debug-info?view=msvc-170 Fixes #2228 --- CMakeLists.txt | 7 ++++++- src/CMakeLists.template.cmake | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8b6c9140e9..032a9fb084 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -84,7 +84,12 @@ endif() # Debug build? if("$ENV{ENABLE_AMICI_DEBUGGING}" OR "$ENV{ENABLE_GCOV_COVERAGE}") - add_compile_options(-UNDEBUG -O0 -g) + add_compile_options(-UNDEBUG) + if(MSVC) + add_compile_options(-DEBUG) + else() + add_compile_options(-O0 -g) + endif() set(CMAKE_BUILD_TYPE "Debug") endif() diff --git a/src/CMakeLists.template.cmake b/src/CMakeLists.template.cmake index 572efede80..ee50a551e8 100644 --- a/src/CMakeLists.template.cmake +++ b/src/CMakeLists.template.cmake @@ -41,7 +41,12 @@ message(STATUS "Found AMICI ${Amici_DIR}") # Debug build? if("$ENV{ENABLE_AMICI_DEBUGGING}" OR "$ENV{ENABLE_GCOV_COVERAGE}") - add_compile_options(-UNDEBUG -O0 -g) + add_compile_options(-UNDEBUG) + if(MSVC) + add_compile_options(-DEBUG) + else() + add_compile_options(-O0 -g) + endif() set(CMAKE_BUILD_TYPE "Debug") endif() From 3673940ba5b5ce95935d8002ac84233e4b0654ef Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Wed, 21 Feb 2024 22:58:54 +0100 Subject: [PATCH 022/188] Fix MANIFEST.in warning (#2297) * Fix MANIFEST.in Fixes `warning: manifest_maker: MANIFEST.in, line 13: path '**/build/' cannot end with '/'` e.g. https://github.com/AMICI-dev/AMICI/actions/runs/7970974169/job/21759690743?pr=2296#step:14:163 * .. * .. --- .github/workflows/deploy_protected.yml | 2 +- python/sdist/MANIFEST.in | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy_protected.yml b/.github/workflows/deploy_protected.yml index eaf625ea22..5b32786c37 100644 --- a/.github/workflows/deploy_protected.yml +++ b/.github/workflows/deploy_protected.yml @@ -15,7 +15,7 @@ on: jobs: dockerhub: name: Deploy Dockerhub - if: github.repository_owner == 'AMICI-dev' + if: github.event.pull_request.head.repo.fork == false runs-on: ubuntu-22.04 strategy: diff --git a/python/sdist/MANIFEST.in b/python/sdist/MANIFEST.in index 762987665f..5be0b1ebc6 100644 --- a/python/sdist/MANIFEST.in +++ b/python/sdist/MANIFEST.in @@ -10,7 +10,7 @@ include version.txt include LICENSE.md exclude amici/*.so exclude amici/*.dll -prune **/build/ +prune **/build prune amici/share prune amici/lib prune amici/ThirdParty/SuiteSparse/lib From 74d4e1f816e822a8c5b0d66460e588848a94be6b Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Wed, 21 Feb 2024 23:03:39 +0100 Subject: [PATCH 023/188] Skip unnecessary toposorting in `DEModel._collect_heaviside_roots` (#2299) Sorting is only required if roots have been found. Most often this will not be the case. Only sort when necessary. --- python/sdist/amici/de_export.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python/sdist/amici/de_export.py b/python/sdist/amici/de_export.py index 12cde4630f..0770894884 100644 --- a/python/sdist/amici/de_export.py +++ b/python/sdist/amici/de_export.py @@ -2724,6 +2724,9 @@ def _collect_heaviside_roots( elif arg.has(sp.Heaviside): root_funs.extend(self._collect_heaviside_roots(arg.args)) + if not root_funs: + return [] + # substitute 'w' expressions into root expressions now, to avoid # rewriting 'root.cpp' and 'stau.cpp' headers # to include 'w.h' From f0c7c59e0236af42e8ca9c917a66f040091a594c Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Wed, 21 Feb 2024 23:35:40 +0100 Subject: [PATCH 024/188] Fix missing toposort after rateOf-substitutions in `w` (#2291) Fixes potentially incorrect simulation results when using rateOf in `w` where the rates depend on `w`. Fixes #2290 --- python/sdist/amici/de_export.py | 82 ++++++++++++++++--- .../test_sbml_import_special_functions.py | 54 +++++++++++- 2 files changed, 124 insertions(+), 12 deletions(-) diff --git a/python/sdist/amici/de_export.py b/python/sdist/amici/de_export.py index 0770894884..3bc65d5a7f 100644 --- a/python/sdist/amici/de_export.py +++ b/python/sdist/amici/de_export.py @@ -30,7 +30,6 @@ Union, ) from collections.abc import Sequence - import numpy as np import sympy as sp from sympy.matrices.dense import MutableDenseMatrix @@ -1117,11 +1116,10 @@ def transform_dxdt_to_concentration(species_id, dxdt): for llh in si.symbols[SymbolId.LLHY].values() ) - self._process_sbml_rate_of( - symbols - ) # substitute SBML-rateOf constructs + # substitute SBML-rateOf constructs + self._process_sbml_rate_of() - def _process_sbml_rate_of(self, symbols) -> None: + def _process_sbml_rate_of(self) -> None: """Substitute any SBML-rateOf constructs in the model equations""" rate_of_func = sp.core.function.UndefinedFunction("rateOf") species_sym_to_xdot = dict(zip(self.sym("x"), self.sym("xdot"))) @@ -1129,8 +1127,6 @@ def _process_sbml_rate_of(self, symbols) -> None: def get_rate(symbol: sp.Symbol): """Get rate of change of the given symbol""" - nonlocal symbols - if symbol.find(rate_of_func): raise SBMLException("Nesting rateOf() is not allowed.") @@ -1142,6 +1138,7 @@ def get_rate(symbol: sp.Symbol): return 0 # replace rateOf-instances in xdot by xdot symbols + made_substitutions = False for i_state in range(len(self.eq("xdot"))): if rate_ofs := self._eqs["xdot"][i_state].find(rate_of_func): self._eqs["xdot"][i_state] = self._eqs["xdot"][i_state].subs( @@ -1151,9 +1148,14 @@ def get_rate(symbol: sp.Symbol): for rate_of in rate_ofs } ) - # substitute in topological order - subs = toposort_symbols(dict(zip(self.sym("xdot"), self.eq("xdot")))) - self._eqs["xdot"] = smart_subs_dict(self.eq("xdot"), subs) + made_substitutions = True + + if made_substitutions: + # substitute in topological order + subs = toposort_symbols( + dict(zip(self.sym("xdot"), self.eq("xdot"))) + ) + self._eqs["xdot"] = smart_subs_dict(self.eq("xdot"), subs) # replace rateOf-instances in x0 by xdot equation for i_state in range(len(self.eq("x0"))): @@ -1165,9 +1167,55 @@ def get_rate(symbol: sp.Symbol): } ) + # replace rateOf-instances in w by xdot equation + # here we may need toposort, as xdot may depend on w + made_substitutions = False + for i_expr in range(len(self.eq("w"))): + if rate_ofs := self._eqs["w"][i_expr].find(rate_of_func): + self._eqs["w"][i_expr] = self._eqs["w"][i_expr].subs( + { + rate_of: get_rate(rate_of.args[0]) + for rate_of in rate_ofs + } + ) + made_substitutions = True + + if made_substitutions: + # Sort expressions in self._expressions, w symbols, and w equations + # in topological order. Ideally, this would already happen before + # adding the expressions to the model, but at that point, we don't + # have access to xdot yet. + # NOTE: elsewhere, conservations law expressions are expected to + # occur before any other w expressions, so we must maintain their + # position + # toposort everything but conservation law expressions, + # then prepend conservation laws + w_sorted = toposort_symbols( + dict( + zip( + self.sym("w")[self.num_cons_law() :, :], + self.eq("w")[self.num_cons_law() :, :], + ) + ) + ) + w_sorted = ( + dict( + zip( + self.sym("w")[: self.num_cons_law(), :], + self.eq("w")[: self.num_cons_law(), :], + ) + ) + | w_sorted + ) + old_syms = tuple(self._syms["w"]) + topo_expr_syms = tuple(w_sorted.keys()) + new_order = [old_syms.index(s) for s in topo_expr_syms] + self._expressions = [self._expressions[i] for i in new_order] + self._syms["w"] = sp.Matrix(topo_expr_syms) + self._eqs["w"] = sp.Matrix(list(w_sorted.values())) + for component in chain( self.observables(), - self.expressions(), self.events(), self._algebraic_equations, ): @@ -2210,6 +2258,18 @@ def _compute_equation(self, name: str) -> None: self._eqs[name] = self.sym(name) elif name == "dwdx": + if ( + expected := list( + map( + ConservationLaw.get_x_rdata, + reversed(self.conservation_laws()), + ) + ) + ) != (actual := self.eq("w")[: self.num_cons_law()]): + raise AssertionError( + "Conservation laws are not at the beginning of 'w'. " + f"Got {actual}, expected {expected}." + ) x = self.sym("x") self._eqs[name] = sp.Matrix( [ diff --git a/python/tests/test_sbml_import_special_functions.py b/python/tests/test_sbml_import_special_functions.py index 9d8f447511..3f8383ce94 100644 --- a/python/tests/test_sbml_import_special_functions.py +++ b/python/tests/test_sbml_import_special_functions.py @@ -12,7 +12,11 @@ from amici.antimony_import import antimony2amici from amici.gradient_check import check_derivatives from amici.testing import TemporaryDirectoryWinSafe, skip_on_valgrind -from numpy.testing import assert_approx_equal, assert_array_almost_equal_nulp +from numpy.testing import ( + assert_approx_equal, + assert_array_almost_equal_nulp, + assert_allclose, +) from scipy.special import loggamma @@ -222,3 +226,51 @@ def test_rateof(): assert_array_almost_equal_nulp( rdata.by_id("p2"), 1 + rdata.by_id("S1") ) + + +@skip_on_valgrind +def test_rateof_with_expression_dependent_rate(): + """Test rateOf, where the rateOf argument depends on `w` and requires + toposorting.""" + ant_model = """ + model test_rateof_with_expression_dependent_rate + S1 = 0; + S2 = 0; + S1' = rate; + S2' = 2 * rateOf(S1); + # the id of the following expression must be alphabetically before + # `rate`, so that toposort is required to evaluate the expressions + # in the correct order + e1 := 2 * rateOf(S1); + rate := time + end + """ + module_name = "test_rateof_with_expression_dependent_rate" + with TemporaryDirectoryWinSafe(prefix=module_name) as outdir: + antimony2amici( + ant_model, + model_name=module_name, + output_dir=outdir, + ) + model_module = amici.import_model_module( + module_name=module_name, module_path=outdir + ) + amici_model = model_module.getModel() + t = np.linspace(0, 10, 11) + amici_model.setTimepoints(t) + amici_solver = amici_model.getSolver() + rdata = amici.runAmiciSimulation(amici_model, amici_solver) + + state_ids_solver = amici_model.getStateIdsSolver() + + assert_array_almost_equal_nulp(rdata.by_id("e1"), 2 * t, 1) + + i_S1 = state_ids_solver.index("S1") + i_S2 = state_ids_solver.index("S2") + assert_approx_equal(rdata["xdot"][i_S1], t[-1]) + assert_approx_equal(rdata["xdot"][i_S2], 2 * t[-1]) + + assert_allclose(np.diff(rdata.by_id("S1")), t[:-1] + 0.5, atol=1e-9) + assert_array_almost_equal_nulp( + rdata.by_id("S2"), 2 * rdata.by_id("S1"), 10 + ) From 67d478cbc3ee2e08a12ac11129e82a4bcc51c82b Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Thu, 22 Feb 2024 10:14:59 +0100 Subject: [PATCH 025/188] Fix redundant calls to Model::fdwdx from Model_ODE::fJ (#2298) Remove an extraneous call to Model::fdwdx, the result of which will be overridden without use by Model_ODE::fJSparse. --- src/model_ode.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/model_ode.cpp b/src/model_ode.cpp index b3d517b36e..52275008e3 100644 --- a/src/model_ode.cpp +++ b/src/model_ode.cpp @@ -14,8 +14,6 @@ void Model_ODE::fJ( void Model_ODE::fJ( realtype t, const_N_Vector x, const_N_Vector /*xdot*/, SUNMatrix J ) { - auto x_pos = computeX_pos(x); - fdwdx(t, N_VGetArrayPointerConst(x_pos)); fJSparse(t, x, derived_state_.J_.get()); derived_state_.J_.refresh(); auto JDense = SUNMatrixWrapper(J); From 4334557c8827a9be4744f51f2df465dc7a7c88a0 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Thu, 22 Feb 2024 16:59:01 +0100 Subject: [PATCH 026/188] Add .gitignore to model directories (#2301) Add a `.gitignore` file to the amici-generated model directory to exclude the auto-generated files from version control. Closes #2300. --- python/sdist/amici/de_export.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/python/sdist/amici/de_export.py b/python/sdist/amici/de_export.py index 3bc65d5a7f..f2badbea76 100644 --- a/python/sdist/amici/de_export.py +++ b/python/sdist/amici/de_export.py @@ -3035,6 +3035,7 @@ def _generate_c_code(self) -> None: self._write_c_make_file() self._write_swig_files() self._write_module_setup() + _write_gitignore(Path(self.model_path)) shutil.copy( CXX_MAIN_TEMPLATE_FILE, os.path.join(self.model_path, "main.cpp") @@ -4385,3 +4386,17 @@ def _parallel_applyfunc(obj: sp.Matrix, func: Callable) -> sp.Matrix: "to a module-level function or disable parallelization by " "setting `AMICI_IMPORT_NPROCS=1`." ) from e + + +def _write_gitignore(dest_dir: Path) -> None: + """Write .gitignore file. + + Generate a `.gitignore` file to ignore a model directory. + + :param dest_dir: + Path to the directory to write the `.gitignore` file to. + """ + dest_dir.mkdir(exist_ok=True, parents=True) + + with open(dest_dir / ".gitignore", "w") as f: + f.write("**") From 8b324bca5b796a93094195d22a023e5f8e945ef1 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Fri, 23 Feb 2024 18:13:58 +0100 Subject: [PATCH 027/188] Update reference list (#2279) --- documentation/amici_refs.bib | 68 ++++++---- documentation/references.md | 233 +++++++++++++++++------------------ 2 files changed, 161 insertions(+), 140 deletions(-) diff --git a/documentation/amici_refs.bib b/documentation/amici_refs.bib index 4c31869d87..ef283eaf52 100644 --- a/documentation/amici_refs.bib +++ b/documentation/amici_refs.bib @@ -1100,19 +1100,20 @@ @Article{FroehlichGer2023 } @Article{FroehlichSor2022, - author = {Fröhlich, Fabian AND Sorger, Peter K.}, - journal = {PLOS Computational Biology}, - title = {Fides: Reliable trust-region optimization for parameter estimation of ordinary differential equation models}, - year = {2022}, - month = {07}, - number = {7}, - pages = {1-28}, - volume = {18}, - abstract = {Ordinary differential equation (ODE) models are widely used to study biochemical reactions in cellular networks since they effectively describe the temporal evolution of these networks using mass action kinetics. The parameters of these models are rarely known a priori and must instead be estimated by calibration using experimental data. Optimization-based calibration of ODE models on is often challenging, even for low-dimensional problems. Multiple hypotheses have been advanced to explain why biochemical model calibration is challenging, including non-identifiability of model parameters, but there are few comprehensive studies that test these hypotheses, likely because tools for performing such studies are also lacking. Nonetheless, reliable model calibration is essential for uncertainty analysis, model comparison, and biological interpretation. We implemented an established trust-region method as a modular Python framework (fides) to enable systematic comparison of different approaches to ODE model calibration involving a variety of Hessian approximation schemes. We evaluated fides on a recently developed corpus of biologically realistic benchmark problems for which real experimental data are available. Unexpectedly, we observed high variability in optimizer performance among different implementations of the same mathematical instructions (algorithms). Analysis of possible sources of poor optimizer performance identified limitations in the widely used Gauss-Newton, BFGS and SR1 Hessian approximation schemes. We addressed these drawbacks with a novel hybrid Hessian approximation scheme that enhances optimizer performance and outperforms existing hybrid approaches. When applied to the corpus of test models, we found that fides was on average more reliable and efficient than existing methods using a variety of criteria. We expect fides to be broadly useful for ODE constrained optimization problems in biochemical models and to be a foundation for future methods development.}, - creationdate = {2023-04-15T08:12:41}, - doi = {10.1371/journal.pcbi.1010322}, - publisher = {Public Library of Science}, - url = {https://doi.org/10.1371/journal.pcbi.1010322}, + author = {Fröhlich, Fabian and Sorger, Peter K.}, + journal = {PLOS Computational Biology}, + title = {Fides: Reliable trust-region optimization for parameter estimation of ordinary differential equation models}, + year = {2022}, + month = {07}, + number = {7}, + pages = {1-28}, + volume = {18}, + abstract = {Ordinary differential equation (ODE) models are widely used to study biochemical reactions in cellular networks since they effectively describe the temporal evolution of these networks using mass action kinetics. The parameters of these models are rarely known a priori and must instead be estimated by calibration using experimental data. Optimization-based calibration of ODE models on is often challenging, even for low-dimensional problems. Multiple hypotheses have been advanced to explain why biochemical model calibration is challenging, including non-identifiability of model parameters, but there are few comprehensive studies that test these hypotheses, likely because tools for performing such studies are also lacking. Nonetheless, reliable model calibration is essential for uncertainty analysis, model comparison, and biological interpretation. We implemented an established trust-region method as a modular Python framework (fides) to enable systematic comparison of different approaches to ODE model calibration involving a variety of Hessian approximation schemes. We evaluated fides on a recently developed corpus of biologically realistic benchmark problems for which real experimental data are available. Unexpectedly, we observed high variability in optimizer performance among different implementations of the same mathematical instructions (algorithms). Analysis of possible sources of poor optimizer performance identified limitations in the widely used Gauss-Newton, BFGS and SR1 Hessian approximation schemes. We addressed these drawbacks with a novel hybrid Hessian approximation scheme that enhances optimizer performance and outperforms existing hybrid approaches. When applied to the corpus of test models, we found that fides was on average more reliable and efficient than existing methods using a variety of criteria. We expect fides to be broadly useful for ODE constrained optimization problems in biochemical models and to be a foundation for future methods development.}, + creationdate = {2023-04-15T08:12:41}, + doi = {10.1371/journal.pcbi.1010322}, + modificationdate = {2024-02-23T18:10:55}, + publisher = {Public Library of Science}, + url = {https://doi.org/10.1371/journal.pcbi.1010322}, } @Article{ErdemMut2022, @@ -1163,15 +1164,6 @@ @InBook{Froehlich2023 url = {https://doi.org/10.1007/978-1-0716-3008-2_3}, } -@Misc{SluijsZho2023, - author = {Bob van Sluijs and Tao Zhou and Britta Helwig and Mathieu Baltussen and Frank Nelissen and Hans Heus and Wilhelm Huck}, - title = {Inverse Design of Enzymatic Reaction Network States}, - year = {2023}, - creationdate = {2023-07-06T10:39:46}, - doi = {10.21203/rs.3.rs-2646906/v1}, - modificationdate = {2023-07-06T10:40:37}, -} - @Article{BuckBas2023, author = {Michèle C. Buck and Lisa Bast and Judith S. Hecker and Jennifer Rivière and Maja Rothenberg-Thurley and Luisa Vogel and Dantong Wang and Immanuel Andrä and Fabian J. Theis and Florian Bassermann and Klaus H. Metzeler and Robert A.J. Oostendorp and Carsten Marr and Katharina S. Götze}, journal = {iScience}, @@ -1251,6 +1243,38 @@ @Misc{HuckBal2023 publisher = {Research Square Platform LLC}, } +@Article{LangPen2024, + author = {Lang, Paul F. and Penas, David R. and Banga, Julio R. and Weindl, Daniel and Novak, Bela}, + journal = {PLOS Computational Biology}, + title = {Reusable rule-based cell cycle model explains compartment-resolved dynamics of 16 observables in RPE-1 cells}, + year = {2024}, + month = {01}, + number = {1}, + pages = {1-24}, + volume = {20}, + abstract = {The mammalian cell cycle is regulated by a well-studied but complex biochemical reaction system. Computational models provide a particularly systematic and systemic description of the mechanisms governing mammalian cell cycle control. By combining both state-of-the-art multiplexed experimental methods and powerful computational tools, this work aims at improving on these models along four dimensions: model structure, validation data, validation methodology and model reusability. We developed a comprehensive model structure of the full cell cycle that qualitatively explains the behaviour of human retinal pigment epithelial-1 cells. To estimate the model parameters, time courses of eight cell cycle regulators in two compartments were reconstructed from single cell snapshot measurements. After optimisation with a parallel global optimisation metaheuristic we obtained excellent agreements between simulations and measurements. The PEtab specification of the optimisation problem facilitates reuse of model, data and/or optimisation results. Future perturbation experiments will improve parameter identifiability and allow for testing model predictive power. Such a predictive model may aid in drug discovery for cell cycle-related disorders.}, + creationdate = {2024-01-24T20:02:16}, + doi = {10.1371/journal.pcbi.1011151}, + modificationdate = {2024-02-23T18:10:08}, + publisher = {Public Library of Science}, + url = {https://doi.org/10.1371/journal.pcbi.1011151}, +} + +@Article{SluijsZho2024, + author = {van Sluijs, Bob and Zhou, Tao and Helwig, Britta and Baltussen, Mathieu G. and Nelissen, Frank H. T. and Heus, Hans A. and Huck, Wilhelm T. S.}, + journal = {Nature Communications}, + title = {Iterative design of training data to control intricate enzymatic reaction networks}, + year = {2024}, + issn = {2041-1723}, + month = feb, + number = {1}, + volume = {15}, + creationdate = {2024-02-23T17:09:35}, + doi = {10.1038/s41467-024-45886-9}, + modificationdate = {2024-02-23T17:09:35}, + publisher = {Springer Science and Business Media LLC}, +} + @Comment{jabref-meta: databaseType:bibtex;} @Comment{jabref-meta: grouping: diff --git a/documentation/references.md b/documentation/references.md index 00c3f40cc8..2164037aaf 100644 --- a/documentation/references.md +++ b/documentation/references.md @@ -1,6 +1,6 @@ # References -List of publications using AMICI. Total number is 82. +List of publications using AMICI. Total number is 83. If you applied AMICI in your work and your publication is missing, please let us know via a new GitHub issue. @@ -11,17 +11,35 @@ If you applied AMICI in your work and your publication is missing, please let us } +

2024

+
+
+Lang, Paul F., David R. Penas, Julio R. Banga, Daniel Weindl, and Bela +Novak. 2024. “Reusable Rule-Based Cell Cycle Model Explains +Compartment-Resolved Dynamics of 16 Observables in RPE-1 Cells.” +PLOS Computational Biology 20 (1): 1–24. https://doi.org/10.1371/journal.pcbi.1011151. +
+
+Sluijs, Bob van, Tao Zhou, Britta Helwig, Mathieu G. Baltussen, Frank H. +T. Nelissen, Hans A. Heus, and Wilhelm T. S. Huck. 2024. +“Iterative Design of Training Data to Control Intricate Enzymatic +Reaction Networks.” Nature Communications 15 (1). https://doi.org/10.1038/s41467-024-45886-9. +
+

2023

-
+role="list"> +
Buck, Michèle C., Lisa Bast, Judith S. Hecker, Jennifer Rivière, Maja Rothenberg-Thurley, Luisa Vogel, Dantong Wang, et al. 2023. “Progressive Disruption of Hematopoietic Architecture from Clonal Hematopoiesis to MDS.” iScience, 107328. https://doi.org/10.1016/j.isci.2023.107328.
-
+
Contento, Lorenzo, Noemi Castelletti, Elba Raimúndez, Ronan Le Gleut, Yannik Schälte, Paul Stapor, Ludwig Christian Hinske, et al. 2023. “Integrative Modelling of Reported Case Numbers and Seroprevalence @@ -29,14 +47,14 @@ Reveals Time-Dependent Test Efficiency and Infectious Contacts.” Epidemics 43: 100681. https://doi.org/10.1016/j.epidem.2023.100681.
-
+
Contento, Lorenzo, Paul Stapor, Daniel Weindl, and Jan Hasenauer. 2023. “A More Expressive Spline Representation for SBML Models Improves Code Generation Performance in AMICI.” bioRxiv. https://doi.org/10.1101/2023.06.29.547120.
-
+
Fröhlich, Fabian. 2023. “A Practical Guide for the Efficient Formulation and Calibration of Large, Energy- and Rule-Based Models of Cellular Signal Transduction.” In Computational Modeling of @@ -44,29 +62,28 @@ Signaling Networks, edited by Lan K. Nguyen, 59–86. New York, NY: Springer US. https://doi.org/10.1007/978-1-0716-3008-2_3.
-
+
Fröhlich, Fabian, Luca Gerosa, Jeremy Muhlich, and Peter K Sorger. 2023. “Mechanistic Model of MAPK Signaling Reveals How Allostery and Rewiring Contribute to Drug Resistance.” Molecular Systems Biology 19 (2): e10988. https://doi.org/10.15252/msb.202210988.
-
+
Hasenauer, Jan, Simon Merkt, Solomon Ali, Esayas Gudina, Wondimagegn Adissu, Maximilian Münchhoff, Alexander Graf, et al. 2023. “Long-Term Monitoring of SARS-CoV-2 Seroprevalence and Variants in Ethiopia Provides Prediction for Immunity and Cross-Immunity.” https://doi.org/10.21203/rs.3.rs-3307821/v1.
-
+
Huck, Wilhelm, Mathieu Baltussen, Thijs de Jong, Quentin Duez, and William Robinson. 2023. “Chemical Reservoir Computation in a Self-Organizing Reaction Network.” Research Square Platform LLC. https://doi.org/10.21203/rs.3.rs-3487081/v1.
-
+
Lakrisenko, Polina, Paul Stapor, Stephan Grein, Łukasz Paszkowski, Dilan Pathirana, Fabian Fröhlich, Glenn Terje Lines, Daniel Weindl, and Jan Hasenauer. 2023. “Efficient Computation of Adjoint Sensitivities @@ -74,37 +91,31 @@ at Steady-State in ODE Models of Biochemical Reaction Networks.” PLOS Computational Biology 19 (1): 1–19. https://doi.org/10.1371/journal.pcbi.1010783.
-
+
Mendes, Pedro. 2023. “Reproducibility and FAIR Principles: The Case of a Segment Polarity Network Model.” Frontiers in Cell and Developmental Biology 11. https://doi.org/10.3389/fcell.2023.1201673.
-
+
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/10.1016/j.ymben.2022.11.003.
-
+
Raimúndez, Elba, Michael Fedders, and Jan Hasenauer. 2023. “Posterior Marginalization Accelerates Bayesian Inference for Dynamical Models of Biological Processes.” iScience, September, 108083. https://doi.org/10.1016/j.isci.2023.108083.
-
-Sluijs, Bob van, Tao Zhou, Britta Helwig, Mathieu Baltussen, Frank -Nelissen, Hans Heus, and Wilhelm Huck. 2023. “Inverse Design of -Enzymatic Reaction Network States.” https://doi.org/10.21203/rs.3.rs-2646906/v1. -
-
+
Tunedal, Kajsa, Federica Viola, Belén Casas Garcia, Ann Bolger, Fredrik H. Nyström, Carl Johan Östgren, Jan Engvall, et al. 2023. “Haemodynamic Effects of Hypertension and Type 2 Diabetes: -Insights from a 4d Flow 4D Flow MRI-based Personalized Cardiovascular Mathematical Model.” The Journal of Physiology n/a (n/a). https://doi.org/10.1113/JP284652. @@ -112,8 +123,8 @@ href="https://doi.org/10.1113/JP284652">https://doi.org/10.1113/JP284652.

2022

-
+role="list"> +
Albadry, Mohamed, Sebastian Höpfl, Nadia Ehteshamzad, Matthias König, Michael Böttcher, Jasna Neumann, Amelie Lupp, et al. 2022. “Periportal Steatosis in Mice Affects Distinct Parameters of @@ -121,7 +132,7 @@ Pericentral Drug Metabolism.” Scientific Reports 12 (1): 21825. https://doi.org/10.1038/s41598-022-26483-6.
-
+
Erdem, Cemal, Arnab Mutsuddy, Ethan M. Bensman, William B. Dodd, Michael M. Saint-Antoine, Mehdi Bouhaddou, Robert C. Blake, et al. 2022. “A Scalable, Open-Source Implementation of a Large-Scale @@ -129,21 +140,21 @@ Mechanistic Model for Single Cell Proliferation and Death Signaling.” Nature Communications 13 (1): 3555. https://doi.org/10.1038/s41467-022-31138-1.
-
-Fröhlich, Peter K., Fabian AND Sorger. 2022. “Fides: Reliable +
+Fröhlich, Fabian, and Peter K. Sorger. 2022. “Fides: Reliable Trust-Region Optimization for Parameter Estimation of Ordinary Differential Equation Models.” PLOS Computational Biology 18 (7): 1–28. https://doi.org/10.1371/journal.pcbi.1010322.
-
+
Massonis, Gemma, Alejandro F Villaverde, and Julio R Banga. 2022. Improving dynamic predictions with ensembles of observable models.” Bioinformatics, November. https://doi.org/10.1093/bioinformatics/btac755.
-
+
Meyer, Kristian, Mikkel Søes Ibsen, Lisa Vetter-Joss, Ernst Broberg Hansen, and Jens Abildskov. 2022. “Industrial Ion-Exchange Chromatography Development Using Discontinuous Galerkin Methods Coupled @@ -151,21 +162,21 @@ with Forward Sensitivity Analysis.” Journal of Chromatography A, 463741. https://doi.org/10.1016/j.chroma.2022.463741.
-
+
Schmucker, Robin, Gabriele Farina, James Faeder, Fabian Fröhlich, Ali Sinan Saglam, and Tuomas Sandholm. 2022. “Combination Treatment Optimization Using a Pan-Cancer Pathway Model.” PLOS Computational Biology 17 (12): 1–22. https://doi.org/10.1371/journal.pcbi.1009689.
-
+
Sluijs, Bob van, Roel J. M. Maas, Ardjan J. van der Linden, Tom F. A. de Greef, and Wilhelm T. S. Huck. 2022. “A Microfluidic Optimal Experimental Design Platform for Forward Design of Cell-Free Genetic Networks.” Nature Communications 13 (1): 3626. https://doi.org/10.1038/s41467-022-31306-3.
-
+
Stapor, Paul, Leonard Schmiester, Christoph Wierling, Simon Merkt, Dilan Pathirana, Bodo M. H. Lange, Daniel Weindl, and Jan Hasenauer. 2022. Mini-batch optimization enables training of @@ -173,7 +184,7 @@ ODE models on large-scale datasets.” Nature Communications 13 (1): 34. https://doi.org/10.1038/s41467-021-27374-6.
-
+
Sundqvist, Nicolas, Sebastian Sten, Peter Thompson, Benjamin Jan Andersson, Maria Engström, and Gunnar Cedersund. 2022. “Mechanistic Model for Human Brain Metabolism and Its Connection @@ -181,8 +192,7 @@ to the Neurovascular Coupling.” PLOS Computational Biology 18 (12): 1–24. https://doi.org/10.1371/journal.pcbi.1010798.
-
+
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 @@ -192,16 +202,16 @@ href="https://doi.org/10.1109/TCBB.2022.3213914">https://doi.org/10.1109/TCBB.20

2021

-
+role="list"> +
Adlung, Lorenz, Paul Stapor, Christian Tönsing, Leonard Schmiester, Luisa E. Schwarzmüller, Lena Postawa, Dantong Wang, et al. 2021. -“Cell-to-Cell Variability in Jak2/Stat5 Pathway Components and +“Cell-to-Cell Variability in JAK2/STAT5 Pathway Components and Cytoplasmic Volumes Defines Survival Threshold in Erythroid Progenitor Cells.” Cell Reports 36 (6): 109507. https://doi.org/10.1016/j.celrep.2021.109507.
-
+
Bast, Lisa, Michèle C. Buck, Judith S. Hecker, Robert A. J. Oostendorp, Katharina S. Götze, and Carsten Marr. 2021. “Computational Modeling of Stem and Progenitor Cell Kinetics Identifies Plausible @@ -209,13 +219,13 @@ Hematopoietic Lineage Hierarchies.” iScience 24 (2): 102120. https://doi.org/10.1016/j.isci.2021.102120.
-
+
Gaspari, Erika. 2021. “Model-Driven Design of Mycoplasma as a Vaccine Chassis.” PhD thesis, Wageningen: Wageningen University. https://doi.org/10.18174/539593.
-
+
Gudina, Esayas Kebede, Solomon Ali, Eyob Girma, Addisu Gize, Birhanemeskel Tegene, Gadissa Bedada Hundie, Wondewosen Tsegaye Sime, et al. 2021. Seroepidemiology and model-based @@ -224,13 +234,13 @@ front-line hospital workers and communities.” The Lancet Global Health 9 (11): e1517–27. https://doi.org/10.1016/S2214-109X(21)00386-7.
-
+
Maier, Corinna. 2021. “Bayesian Data Assimilation and Reinforcement Learning for Model-Informed Precision Dosing in Oncology.” Doctoralthesis, Universität Potsdam. https://doi.org/10.25932/publishup-51587.
-
+
Raimúndez, Elba, Erika Dudkin, Jakob Vanhoefer, Emad Alamoudi, Simon Merkt, Lara Fuhrmann, Fan Bai, and Jan Hasenauer. 2021. “COVID-19 Outbreak in Wuhan Demonstrates the Limitations of Publicly Available @@ -239,42 +249,41 @@ Case Numbers for Epidemiological Modeling.” Epidemics href="https://doi.org/10.1016/j.epidem.2021.100439">https://doi.org/10.1016/j.epidem.2021.100439.
+role="listitem"> Schmiester, Leonard, Daniel Weindl, and Jan Hasenauer. 2021. “Efficient Gradient-Based Parameter Estimation for Dynamic Models Using Qualitative Data.” bioRxiv. https://doi.org/10.1101/2021.02.06.430039.
-
+
Städter, Philipp, Yannik Schälte, Leonard Schmiester, Jan Hasenauer, and Paul L. Stapor. 2021. “Benchmarking of Numerical Integration Methods for ODE Models of Biological Systems.” Scientific Reports 11 (1): 2696. https://doi.org/10.1038/s41598-021-82196-2.
-
+
Sten, Sebastian, Henrik Podéus, Nicolas Sundqvist, Fredrik Elinder, Maria Engström, and Gunnar Cedersund. 2021. “A Multi-Data Based Quantitative Model for the Neurovascular Coupling in the Brain.” bioRxiv. https://doi.org/10.1101/2021.03.25.437053.
-
+
Tomasoni, Danilo, Alessio Paris, Stefano Giampiccolo, Federico Reali, Giulia Simoni, Luca Marchetti, Chanchala Kaddi, et al. 2021. QSPcc Reduces Bottlenecks in Computational Model Simulations.” Communications Biology 4 (1): 1022. https://doi.org/10.1038/s42003-021-02553-9.
-
+
van Rosmalen, R. P., R. W. Smith, V. A. P. Martins dos Santos, C. Fleck, and M. Suarez-Diez. 2021. “Model Reduction of Genome-Scale Metabolic Models as a Basis for Targeted Kinetic Models.” Metabolic Engineering 64: 74–84. https://doi.org/10.1016/j.ymben.2021.01.008.
-
+
Vanhoefer, Jakob, Marta R. A. Matos, Dilan Pathirana, Yannik Schälte, and Jan Hasenauer. 2021. “Yaml2sbml: Human-Readable and -Writable Specification of ODE Models and Their Conversion to @@ -282,8 +291,7 @@ Specification of ODE Models and Their Conversion to (61): 3215. https://doi.org/10.21105/joss.03215.
-
+
Villaverde, Alejandro F, Dilan Pathirana, Fabian Fröhlich, Jan Hasenauer, and Julio R Banga. 2021. A protocol for dynamic model calibration.” Briefings in @@ -293,16 +301,16 @@ href="https://doi.org/10.1093/bib/bbab387">https://doi.org/10.1093/bib/bbab387

2020

-
+role="list"> +
Alabert, Constance, Carolin Loos, Moritz Voelker-Albert, Simona Graziano, Ignasi Forné, Nazaret Reveron-Gomez, Lea Schuh, et al. 2020. “Domain Model Explains Propagation Dynamics and Stability of -Histone H3k27 and H3k36 Methylation Landscapes.” Cell +Histone H3K27 and H3K36 Methylation Landscapes.” Cell Reports 30 (January): 1223–1234.e8. https://doi.org/10.1016/j.celrep.2019.12.060.
-
+
Erdem, Cemal, Ethan M. Bensman, Arnab Mutsuddy, Michael M. Saint-Antoine, Mehdi Bouhaddou, Robert C. Blake, Will Dodd, et al. 2020. “A Simple and Efficient Pipeline for Construction, Merging, @@ -310,7 +318,7 @@ Expansion, and Simulation of Large-Scale, Single-Cell Mechanistic Models.” bioRxiv. https://doi.org/10.1101/2020.11.09.373407.
-
+
Gerosa, Luca, Christopher Chidley, Fabian Fröhlich, Gabriela Sanchez, Sang Kyun Lim, Jeremy Muhlich, Jia-Yun Chen, et al. 2020. “Receptor-Driven ERK Pulses Reconfigure MAPK Signaling and Enable @@ -318,21 +326,20 @@ Persistence of Drug-Adapted BRAF-Mutant Melanoma Cells.” Cell Systems. https://doi.org/10.1016/j.cels.2020.10.002.
-
+
Kuritz, Karsten, Alain R Bonny, João Pedro Fonseca, and Frank Allgöwer. 2020. “PDE-Constrained Optimization for Estimating Population Dynamics over Cell Cycle from Static Single Cell Measurements.” bioRxiv. https://doi.org/10.1101/2020.03.30.015909.
-
+
Maier, Corinna, Niklas Hartung, Charlotte Kloft, Wilhelm Huisinga, and Jana de Wiljes. 2020. “Reinforcement Learning and Bayesian Data Assimilation for Model-Informed Precision Dosing in Oncology.” https://arxiv.org/abs/2006.01061.
-
+
Schälte, Yannik, and Jan Hasenauer. 2020. Efficient exact inference for dynamical systems with noisy measurements using sequential approximate Bayesian @@ -340,23 +347,22 @@ computation.” Bioinformatics 36 (Supplement_1): i551–59. https://doi.org/10.1093/bioinformatics/btaa397.
-
+
Schmiester, Leonard, Daniel Weindl, and Jan Hasenauer. 2020. “Parameterization of Mechanistic Models from Qualitative Data Using an Efficient Optimal Scaling Approach.” Journal of Mathematical Biology, July. https://doi.org/10.1007/s00285-020-01522-w.
-
+
Schuh, Lea, Carolin Loos, Daniil Pokrovsky, Axel Imhof, Ralph A. W. -Rupp, and Carsten Marr. 2020. “H4k20 Methylation Is Differently +Rupp, and Carsten Marr. 2020. “H4K20 Methylation Is Differently Regulated by Dilution and Demethylation in Proliferating and Cell-Cycle-Arrested Xenopus Embryos.” Cell Systems 11 (6): 653–662.e8. https://doi.org/10.1016/j.cels.2020.11.003.
-
+
Sten, Sebastian. 2020. “Mathematical Modeling of Neurovascular Coupling.” Linköping University Medical Dissertations. PhD thesis, Linköping UniversityLinköping UniversityLinköping University, @@ -365,14 +371,14 @@ Health Sciences, Center for Medical Image Science; Visualization (CMIV); Linköping University, Division of Diagnostics; Specialist Medicine. https://doi.org/10.3384/diss.diva-167806.
-
+
Sten, Sebastian, Fredrik Elinder, Gunnar Cedersund, and Maria Engström. 2020. “A Quantitative Analysis of Cell-Specific Contributions and the Role of Anesthetics to the Neurovascular Coupling.” NeuroImage 215: 116827. https://doi.org/10.1016/j.neuroimage.2020.116827.
-
+
Tsipa, Argyro, Jake Alan Pitt, Julio R. Banga, and Athanasios Mantalaris. 2020. “A Dual-Parameter Identification Approach for Data-Based Predictive Modeling of Hybrid Gene Regulatory Network-Growth @@ -383,16 +389,15 @@ href="https://doi.org/10.1007/s00449-020-02360-2">https://doi.org/10.1007/s00449

2019

-
+role="list"> +
Dharmarajan, Lekshmi, Hans-Michael Kaltenbach, Fabian Rudolf, and Joerg Stelling. 2019. “A Simple and Flexible Computational Framework for Inferring Sources of Heterogeneity from Single-Cell Dynamics.” Cell Systems 8 (1): 15–26.e11. https://doi.org/10.1016/j.cels.2018.12.007.
-
+
Fischer, David S., Anna K. Fiedler, Eric Kernfeld, Ryan M. J. Genga, Aimée Bastidas-Ponce, Mostafa Bakhti, Heiko Lickert, Jan Hasenauer, Rene Maehr, and Fabian J. Theis. 2019. “Inferring Population Dynamics @@ -400,22 +405,21 @@ from Single-Cell RNA-Sequencing Time Series Data.” Nature Biotechnology 37: 461–68. https://doi.org/10.1038/s41587-019-0088-0.
-
+
Gregg, Robert W, Saumendra N Sarkar, and Jason E Shoemaker. 2019. “Mathematical Modeling of the cGAS Pathway Reveals Robustness of -DNA Sensing to Trex1 Feedback.” Journal of Theoretical +DNA Sensing to TREX1 Feedback.” Journal of Theoretical Biology 462 (February): 148–57. https://doi.org/10.1016/j.jtbi.2018.11.001.
-
+
Kapfer, Eva-Maria, Paul Stapor, and Jan Hasenauer. 2019. “Challenges in the Calibration of Large-Scale Ordinary Differential Equation Models.” IFAC-PapersOnLine 52 (26): 58–64. https://doi.org/10.1016/j.ifacol.2019.12.236.
-
+
Nousiainen, Kari, Jukka Intosalmi, and Harri Lähdesmäki. 2019. “A Mathematical Model for Enhancer Activation Kinetics During Cell Differentiation.” In Algorithms for Computational @@ -423,50 +427,47 @@ Biology, edited by Ian Holmes, Carlos Martı́n-Vide, and Miguel A. Vega-Rodrı́guez, 191–202. Cham: Springer International Publishing. https://doi.org/10.1007/978-3-030-18174-1_14.
-
+
Pedretscher, B., B. Kaltenbacher, and O. Pfeiler. 2019. “Parameter Identification and Uncertainty Quantification in Stochastic State Space Models and Its Application to Texture Analysis.” Applied Numerical Mathematics 146: 38–54. https://doi.org/10.1016/j.apnum.2019.06.020.
-
+
Pitt, Jake Alan, and Julio R Banga. 2019. “Parameter Estimation in Models of Biological Oscillators: An Automated Regularised Estimation Approach.” BMC Bioinformatics 20 (February): 82. https://doi.org/10.1186/s12859-019-2630-y.
-
+
Schmiester, Leonard, Yannik Schälte, Fabian Fröhlich, Jan Hasenauer, and Daniel Weindl. 2019. Efficient parameterization of large-scale dynamic models based on relative measurements.” Bioinformatics, July. https://doi.org/10.1093/bioinformatics/btz581.
-
+
Terje Lines, Glenn, Łukasz Paszkowski, Leonard Schmiester, Daniel Weindl, Paul Stapor, and Jan Hasenauer. 2019. “Efficient Computation of Steady States in Large-Scale ODE Models of Biochemical Reaction Networks.” IFAC-PapersOnLine 52 (26): 32–37. https://doi.org/10.1016/j.ifacol.2019.12.232.
-
+
Villaverde, Alejandro F., Elba Raimúndez, Jan Hasenauer, and Julio R. Banga. 2019. “A Comparison of Methods for Quantifying Prediction Uncertainty in Systems Biology.” IFAC-PapersOnLine 52 (26): 45–51. https://doi.org/10.1016/j.ifacol.2019.12.234.
-
+
Wang, Dantong, Paul Stapor, and Jan Hasenauer. 2019. “Dirac Mixture Distributions for the Approximation of Mixed Effects Models.” IFAC-PapersOnLine 52 (26): 200–206. https://doi.org/10.1016/j.ifacol.2019.12.258.
-
+
Watanabe, Simon Berglund. 2019. “Identifiability of Parameters in PBPK Models.” Master’s thesis, Chalmers University of Technology / Department of Mathematical Sciences. https://hdl.handle.net/20.500.

2018

-
+role="list"> +
Ballnus, Benjamin, Steffen Schaper, Fabian J Theis, and Jan Hasenauer. 2018. “Bayesian Parameter Estimation for Biochemical Reaction Networks Using Region-Based Adaptive Parallel Tempering.” Bioinformatics 34 (13): i494–501. https://doi.org/10.1093/bioinformatics/bty229.
-
+
Bast, Lisa, Filippo Calzolari, Michael Strasser, Jan Hasenauer, Fabian J. Theis, Jovica Ninkovic, and Carsten Marr. 2018. “Subtle Changes in Clonal Dynamics Underlie the Age-Related Decline in Neurogenesis.” Cell Reports. https://doi.org/10.1016/j.celrep.2018.11.088.
-
+
Fröhlich, Fabian, Thomas Kessler, Daniel Weindl, Alexey Shadrin, Leonard Schmiester, Hendrik Hache, Artur Muradyan, et al. 2018. “Efficient Parameter Estimation Enables the Prediction of Drug Response Using a @@ -498,7 +499,7 @@ Mechanistic Pan-Cancer Pathway Model.” Cell Systems 7 (6): 567–579.e6. https://doi.org/10.1016/j.cels.2018.10.013.
-
+
Fröhlich, Fabian, Anita Reiser, Laura Fink, Daniel Woschée, Thomas Ligon, Fabian Joachim Theis, Joachim Oskar Rädler, and Jan Hasenauer. 2018. “Multi-Experiment Nonlinear Mixed Effect Modeling of @@ -506,50 +507,48 @@ Single-Cell Translation Kinetics After Transfection.” Npj Systems Biology and Applications 5 (1): 1. https://doi.org/10.1038/s41540-018-0079-7.
-
+
Kaltenbacher, Barbara, and Barbara Pedretscher. 2018. “Parameter Estimation in SDEs via the Fokker–Planck Equation: Likelihood Function and Adjoint Based Gradient Computation.” Journal of Mathematical Analysis and Applications 465 (2): 872–84. https://doi.org/10.1016/j.jmaa.2018.05.048.
-
+
Loos, Carolin, Sabrina Krause, and Jan Hasenauer. 2018. “Hierarchical Optimization for the Efficient Parametrization of ODE Models.” Bioinformatics 34 (24): 4266–73. https://doi.org/10.1093/bioinformatics/bty514.
-
+
Loos, Carolin, Katharina Moeller, Fabian Fröhlich, Tim Hucho, and Jan Hasenauer. 2018. “A Hierarchical, Data-Driven Approach to Modeling Single-Cell Populations Predicts Latent Causes of Cell-to-Cell Variability.” Cell Systems 6 (5): 593–603. https://doi.org/10.1016/j.cels.2018.04.008.
-
+
Pitt, Jake Alan, Lucian Gomoescu, Constantinos C. Pantelides, Benoît Chachuat, and Julio R. Banga. 2018. “Critical Assessment of Parameter Estimation Methods in Models of Biological Oscillators.” IFAC-PapersOnLine 51 (19): 72–75. https://doi.org/10.1016/j.ifacol.2018.09.040.
-
+
Schälte, Y., P. Stapor, and J. Hasenauer. 2018. “Evaluation of Derivative-Free Optimizers for Parameter Estimation in Systems Biology.” FAC-PapersOnLine 51 (19): 98–101. https://doi.org/10.1016/j.ifacol.2018.09.025.
-
+
Stapor, Paul, Fabian Fröhlich, and Jan Hasenauer. 2018. “Optimization and Profile Calculation of ODE Models Using Second Order Adjoint Sensitivity Analysis.” Bioinformatics 34 (13): i151–59. https://doi.org/10.1093/bioinformatics/bty230.
-
+
Villaverde, Alejandro F, Fabian Fröhlich, Daniel Weindl, Jan Hasenauer, and Julio R Banga. 2018. Benchmarking optimization methods for parameter estimation in large kinetic @@ -559,36 +558,35 @@ href="https://doi.org/10.1093/bioinformatics/bty736">https://doi.org/10.1093/bio

2017

-
+role="list"> +
Ballnus, B., S. Hug, K. Hatz, L. Görlitz, J. Hasenauer, and F. J. Theis. 2017. “Comprehensive Benchmarking of Markov Chain Monte Carlo Methods for Dynamical Systems.” BMC Syst. Biol. 11 (63). https://doi.org/10.1186/s12918-017-0433-1.
-
+
Fröhlich, F., B. Kaltenbacher, F. J. Theis, and J. Hasenauer. 2017. “Scalable Parameter Estimation for Genome-Scale Biochemical Reaction Networks.” PLoS Comput. Biol. 13 (1): e1005331. https://doi.org/10.1371/journal.pcbi.1005331.
-
+
Fröhlich, F., F. J. Theis, J. O. Rädler, and J. Hasenauer. 2017. “Parameter Estimation for Dynamical Systems with Discrete Events and Logical Operations.” Bioinformatics 33 (7): 1049–56. https://doi.org/10.1093/bioinformatics/btw764.
-
+
Kazeroonian, A., F. J. Theis, and J. Hasenauer. 2017. “A Scalable Moment-Closure Approximation for Large-Scale Biochemical Reaction Networks.” Bioinformatics 33 (14): i293–300. https://doi.org/10.1093/bioinformatics/btx249.
-
+
Maier, C., C. Loos, and J. Hasenauer. 2017. “Robust Parameter Estimation for Dynamical Systems from Outlier-Corrupted Data.” Bioinformatics 33 (5): 718–25. https://doi.org/10.1093/bio

2016

-
+role="list"> +
Boiger, R., J. Hasenauer, S. Hross, and B. Kaltenbacher. 2016. “Integration Based Profile Likelihood Calculation for PDE Constrained Parameter Estimation Problems.” Inverse Prob. 32 (12): 125009. https://doi.org/10.1088/0266-5611/32/12/125009.
-
+
Fiedler, A., S. Raeth, F. J. Theis, A. Hausser, and J. Hasenauer. 2016. “Tailored Parameter Optimization Methods for Ordinary Differential Equation Models with Steady-State Constraints.” BMC Syst. Biol. 10 (80). https://doi.org/10.1186/s12918-016-0319-7.
-
+
Fröhlich, F., P. Thomas, A. Kazeroonian, F. J. Theis, R. Grima, and J. Hasenauer. 2016. “Inference for Stochastic Chemical Kinetics Using Moment Equations and System Size Expansion.” PLoS Comput. Biol. 12 (7): e1005030. https://doi.org/10.1371/journal.pcbi.1005030.
-
+
Hross, S., A. Fiedler, F. J. Theis, and J. Hasenauer. 2016. “Quantitative Comparison of Competing PDE Models for Pom1p Dynamics in Fission Yeast.” In Proc. 6th @@ -628,8 +626,7 @@ Findeisen, E. Bullinger, E. Balsa-Canto, and K. Bernaerts, 49:264–69. 26. IFAC-PapersOnLine. https://doi.org/10.1016/j.ifacol.2016.12.136.
-
+
Kazeroonian, A., F. Fröhlich, A. Raue, F. J. Theis, and J. Hasenauer. 2016. CERENA: Chemical REaction Network Analyzer – A Toolbox for the @@ -637,7 +634,7 @@ Simulation and Analysis of Stochastic Chemical Kinetics.” PLoS ONE 11 (1): e0146732. https://doi.org/10.1371/journal.pone.0146732.
-
+
Loos, C., A. Fiedler, and J. Hasenauer. 2016. “Parameter Estimation for Reaction Rate Equation Constrained Mixture Models.” In Proc. 13th Int. Conf. Comp. Meth. Syst. @@ -648,8 +645,8 @@ href="https://doi.org/10.1007/978-3-319-45177-0">https://doi.org/10.1007/978-3-3

2015

-
+role="list"> +
Loos, C., C. Marr, F. J. Theis, and J. Hasenauer. 2015. “Computational Methods in Systems Biology.” In, edited by O. Roux and J. Bourdon, 9308:52–63. Lecture Notes in Computer Science. From 04407553de6dfbe342519ed06e3fae9427a60a2a Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Fri, 23 Feb 2024 18:27:05 +0100 Subject: [PATCH 028/188] Bump version number; update changelog --- CHANGELOG.md | 30 ++++++++++++++++++++++++++++++ version.txt | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 45edeb8adf..8c1fd68c5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,36 @@ ## v0.X Series +### v0.22.0 (2024-02-23) + +**Features** + +* PEtab import: User option to fail if model needs to be compiled + by @dilpath in https://github.com/AMICI-dev/AMICI/pull/2289 + + The `force_compile` argument is now **deprecated**. Use `compile_` instead. + +* Model import now adds a `.gitignore` file to the model output directory + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2301 + +**Fixes** + +* **Fixed a bug that may have caused wrong simulation results for certain** + **SBML models that contain `rateOf`-expressions** + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2291 +* More informative error message for `ReturnDataView.by_id` + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2295 +* Fixed `ENABLE_AMICI_DEBUGGING=TRUE` not working with MSVC + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2296 +* Fixed MANIFEST.in warning by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2297 +* (performance) Skip unnecessary toposorting in `DEModel._collect_heaviside_roots` + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2299 +* (performance) Fix redundant calls to `Model::fdwdx` from `Model_ODE::fJ` + (only relevant for dense and banded solvers) + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2298 + +**Full Changelog**: https://github.com/AMICI-dev/AMICI/compare/v0.21.2...v0.22.0 + ### v0.21.2 (2024-02-06) * Fixed `Solver` copyctor issues with swig4.2 that resulted in installation diff --git a/version.txt b/version.txt index 59dad104b0..2157409059 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.21.2 +0.22.0 From b355ab7c58eda8ecd3a6c9c396fe8a8a5147ea6a Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Mon, 26 Feb 2024 11:52:40 +0100 Subject: [PATCH 029/188] GHA: only try to publish docker image if secrets are set (#2310) --- .github/workflows/deploy_protected.yml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy_protected.yml b/.github/workflows/deploy_protected.yml index 5b32786c37..881a4eb77a 100644 --- a/.github/workflows/deploy_protected.yml +++ b/.github/workflows/deploy_protected.yml @@ -13,9 +13,25 @@ on: workflow_dispatch: jobs: + check-secret: + runs-on: ubuntu-latest + outputs: + secrets-defined: ${{ steps.secret-check.outputs.defined }} + steps: + - name: Check for Secret availability + id: secret-check + shell: bash + run: | + if [ "${{ secrets.DOCKER_USERNAME }}" != '' ]; then + echo "defined=true" >> $GITHUB_OUTPUT; + else + echo "defined=false" >> $GITHUB_OUTPUT; + fi + dockerhub: name: Deploy Dockerhub - if: github.event.pull_request.head.repo.fork == false + needs: [check-secret] + if: needs.check-secret.outputs.secrets-defined == 'true' runs-on: ubuntu-22.04 strategy: From 16ec8b279f4bf9bf3bb0192170f0e5bd2dbd096c Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Mon, 26 Feb 2024 11:56:15 +0100 Subject: [PATCH 030/188] Refactor DEExporter/DEModel/csc_matrix (#2311) * Refactor DEExporter/DEModel/csc_matrix Reduce unnecessary coupling: * `csc_matrix` as free function - removes the need for the codeprinter in DEModel * Move the codeprinter to `DEExporter` where it's actually needed * .. --- python/sdist/amici/cxxcodeprinter.py | 165 +++++++++++++-------------- python/sdist/amici/de_export.py | 54 +++++---- python/sdist/amici/pysb_import.py | 8 +- python/tests/test_ode_export.py | 13 +-- 4 files changed, 119 insertions(+), 121 deletions(-) diff --git a/python/sdist/amici/cxxcodeprinter.py b/python/sdist/amici/cxxcodeprinter.py index 3fe5b8cd17..032089393d 100644 --- a/python/sdist/amici/cxxcodeprinter.py +++ b/python/sdist/amici/cxxcodeprinter.py @@ -207,90 +207,6 @@ def format_line(symbol: sp.Symbol): if math not in [0, 0.0] ] - def csc_matrix( - self, - matrix: sp.Matrix, - rownames: list[sp.Symbol], - colnames: list[sp.Symbol], - identifier: Optional[int] = 0, - pattern_only: Optional[bool] = False, - ) -> tuple[list[int], list[int], sp.Matrix, list[str], sp.Matrix]: - """ - Generates the sparse symbolic identifiers, symbolic identifiers, - sparse matrix, column pointers and row values for a symbolic - variable - - :param matrix: - dense matrix to be sparsified - - :param rownames: - ids of the variable of which the derivative is computed (assuming - matrix is the jacobian) - - :param colnames: - ids of the variable with respect to which the derivative is computed - (assuming matrix is the jacobian) - - :param identifier: - additional identifier that gets appended to symbol names to - ensure their uniqueness in outer loops - - :param pattern_only: - flag for computing sparsity pattern without whole matrix - - :return: - symbol_col_ptrs, symbol_row_vals, sparse_list, symbol_list, - sparse_matrix - """ - idx = 0 - - nrows, ncols = matrix.shape - - if not pattern_only: - sparse_matrix = sp.zeros(nrows, ncols) - symbol_list = [] - sparse_list = [] - symbol_col_ptrs = [] - symbol_row_vals = [] - - for col in range(ncols): - symbol_col_ptrs.append(idx) - for row in range(nrows): - if matrix[row, col] == 0: - continue - - symbol_row_vals.append(row) - idx += 1 - symbol_name = ( - f"d{rownames[row].name}" f"_d{colnames[col].name}" - ) - if identifier: - symbol_name += f"_{identifier}" - symbol_list.append(symbol_name) - if pattern_only: - continue - - sparse_matrix[row, col] = sp.Symbol(symbol_name, real=True) - sparse_list.append(matrix[row, col]) - - if idx == 0: - symbol_col_ptrs = [] # avoid bad memory access for empty matrices - else: - symbol_col_ptrs.append(idx) - - if pattern_only: - sparse_matrix = None - else: - sparse_list = sp.Matrix(sparse_list) - - return ( - symbol_col_ptrs, - symbol_row_vals, - sparse_list, - symbol_list, - sparse_matrix, - ) - @staticmethod def print_bool(expr) -> str: """Print the boolean value of the given expression""" @@ -360,3 +276,84 @@ def get_switch_statement( ), indent0 + "}", ] + + +def csc_matrix( + matrix: sp.Matrix, + rownames: list[sp.Symbol], + colnames: list[sp.Symbol], + identifier: Optional[int] = 0, + pattern_only: Optional[bool] = False, +) -> tuple[list[int], list[int], sp.Matrix, list[str], sp.Matrix]: + """ + Generates the sparse symbolic identifiers, symbolic identifiers, + sparse matrix, column pointers and row values for a symbolic + variable + + :param matrix: + dense matrix to be sparsified + + :param rownames: + ids of the variable of which the derivative is computed (assuming + matrix is the jacobian) + + :param colnames: + ids of the variable with respect to which the derivative is computed + (assuming matrix is the jacobian) + + :param identifier: + additional identifier that gets appended to symbol names to + ensure their uniqueness in outer loops + + :param pattern_only: + flag for computing sparsity pattern without whole matrix + + :return: + symbol_col_ptrs, symbol_row_vals, sparse_list, symbol_list, + sparse_matrix + """ + idx = 0 + nrows, ncols = matrix.shape + + if not pattern_only: + sparse_matrix = sp.zeros(nrows, ncols) + symbol_list = [] + sparse_list = [] + symbol_col_ptrs = [] + symbol_row_vals = [] + + for col in range(ncols): + symbol_col_ptrs.append(idx) + for row in range(nrows): + if matrix[row, col] == 0: + continue + + symbol_row_vals.append(row) + idx += 1 + symbol_name = f"d{rownames[row].name}" f"_d{colnames[col].name}" + if identifier: + symbol_name += f"_{identifier}" + symbol_list.append(symbol_name) + if pattern_only: + continue + + sparse_matrix[row, col] = sp.Symbol(symbol_name, real=True) + sparse_list.append(matrix[row, col]) + + if idx == 0: + symbol_col_ptrs = [] # avoid bad memory access for empty matrices + else: + symbol_col_ptrs.append(idx) + + if pattern_only: + sparse_matrix = None + else: + sparse_list = sp.Matrix(sparse_list) + + return ( + symbol_col_ptrs, + symbol_row_vals, + sparse_list, + symbol_list, + sparse_matrix, + ) diff --git a/python/sdist/amici/de_export.py b/python/sdist/amici/de_export.py index f2badbea76..0a6813a6ca 100644 --- a/python/sdist/amici/de_export.py +++ b/python/sdist/amici/de_export.py @@ -44,7 +44,11 @@ splines, ) from .constants import SymbolId -from .cxxcodeprinter import AmiciCxxCodePrinter, get_switch_statement +from .cxxcodeprinter import ( + AmiciCxxCodePrinter, + get_switch_statement, + csc_matrix, +) from .de_model import * from .import_utils import ( ObservableTransformation, @@ -725,9 +729,6 @@ class DEModel: whether all observables have a gaussian noise model, i.e. whether res and FIM make sense. - :ivar _code_printer: - Code printer to generate C++ code - :ivar _z2event: list of event indices for each event observable """ @@ -869,10 +870,6 @@ def cached_simplify( self._has_quadratic_nllh: bool = True set_log_level(logger, verbose) - self._code_printer = AmiciCxxCodePrinter() - for fun in CUSTOM_FUNCTIONS: - self._code_printer.known_functions[fun["sympy"]] = fun["c++"] - def differential_states(self) -> list[DifferentialState]: """Get all differential states.""" return self._differential_states @@ -1882,7 +1879,7 @@ def _generate_sparse_symbol(self, name: str) -> None: sparse_list, symbol_list, sparse_matrix, - ) = self._code_printer.csc_matrix( + ) = csc_matrix( matrix[iy, :], rownames=rownames, colnames=colnames, @@ -1900,7 +1897,7 @@ def _generate_sparse_symbol(self, name: str) -> None: sparse_list, symbol_list, sparse_matrix, - ) = self._code_printer.csc_matrix( + ) = csc_matrix( matrix, rownames=rownames, colnames=colnames, @@ -2884,6 +2881,9 @@ class DEExporter: If the given model uses special functions, this set contains hints for model building. + :ivar _code_printer: + Code printer to generate C++ code + :ivar generate_sensitivity_code: Specifies whether code for sensitivity computation is to be generated @@ -2950,10 +2950,14 @@ def __init__( self.set_name(model_name) self.set_paths(outdir) + self._code_printer = AmiciCxxCodePrinter() + for fun in CUSTOM_FUNCTIONS: + self._code_printer.known_functions[fun["sympy"]] = fun["c++"] + # Signatures and properties of generated model functions (see # include/amici/model.h for details) self.model: DEModel = de_model - self.model._code_printer.known_functions.update( + self._code_printer.known_functions.update( splines.spline_user_functions( self.model._splines, self._get_index("p") ) @@ -3519,7 +3523,7 @@ def _get_function_body( f"reinitialization_state_idxs.cend(), {index}) != " "reinitialization_state_idxs.cend())", f" {function}[{index}] = " - f"{self.model._code_printer.doprint(formula)};", + f"{self._code_printer.doprint(formula)};", ] ) cases[ipar] = expressions @@ -3534,12 +3538,12 @@ def _get_function_body( f"reinitialization_state_idxs.cend(), {index}) != " "reinitialization_state_idxs.cend())\n " f"{function}[{index}] = " - f"{self.model._code_printer.doprint(formula)};" + f"{self._code_printer.doprint(formula)};" ) elif function in event_functions: cases = { - ie: self.model._code_printer._get_sym_lines_array( + ie: self._code_printer._get_sym_lines_array( equations[ie], function, 0 ) for ie in range(self.model.num_events()) @@ -3552,7 +3556,7 @@ def _get_function_body( for ie, inner_equations in enumerate(equations): inner_lines = [] inner_cases = { - ipar: self.model._code_printer._get_sym_lines_array( + ipar: self._code_printer._get_sym_lines_array( inner_equations[:, ipar], function, 0 ) for ipar in range(self.model.num_par()) @@ -3567,7 +3571,7 @@ def _get_function_body( and equations.shape[1] == self.model.num_par() ): cases = { - ipar: self.model._code_printer._get_sym_lines_array( + ipar: self._code_printer._get_sym_lines_array( equations[:, ipar], function, 0 ) for ipar in range(self.model.num_par()) @@ -3577,7 +3581,7 @@ def _get_function_body( elif function in multiobs_functions: if function == "dJydy": cases = { - iobs: self.model._code_printer._get_sym_lines_array( + iobs: self._code_printer._get_sym_lines_array( equations[iobs], function, 0 ) for iobs in range(self.model.num_obs()) @@ -3585,7 +3589,7 @@ def _get_function_body( } else: cases = { - iobs: self.model._code_printer._get_sym_lines_array( + iobs: self._code_printer._get_sym_lines_array( equations[:, iobs], function, 0 ) for iobs in range(equations.shape[1]) @@ -3605,12 +3609,12 @@ def _get_function_body( symbols = list(map(sp.Symbol, self.model.sparsesym(function))) else: symbols = self.model.sym(function) - lines += self.model._code_printer._get_sym_lines_symbols( + lines += self._code_printer._get_sym_lines_symbols( symbols, equations, function, 4 ) else: - lines += self.model._code_printer._get_sym_lines_array( + lines += self._code_printer._get_sym_lines_array( equations, function, 4 ) @@ -3766,10 +3770,10 @@ def _write_model_header_cpp(self) -> None: "NK": self.model.num_const(), "O2MODE": "amici::SecondOrderMode::none", # using code printer ensures proper handling of nan/inf - "PARAMETERS": self.model._code_printer.doprint( - self.model.val("p") - )[1:-1], - "FIXED_PARAMETERS": self.model._code_printer.doprint( + "PARAMETERS": self._code_printer.doprint(self.model.val("p"))[ + 1:-1 + ], + "FIXED_PARAMETERS": self._code_printer.doprint( self.model.val("k") )[1:-1], "PARAMETER_NAMES_INITIALIZER_LIST": self._get_symbol_name_initializer_list( @@ -3961,7 +3965,7 @@ def _get_symbol_id_initializer_list(self, name: str) -> str: Template initializer list of ids """ return "\n".join( - f'"{self.model._code_printer.doprint(symbol)}", // {name}[{idx}]' + f'"{self._code_printer.doprint(symbol)}", // {name}[{idx}]' for idx, symbol in enumerate(self.model.sym(name)) ) diff --git a/python/sdist/amici/pysb_import.py b/python/sdist/amici/pysb_import.py index 4f843033f1..c79a8c50f9 100644 --- a/python/sdist/amici/pysb_import.py +++ b/python/sdist/amici/pysb_import.py @@ -178,6 +178,10 @@ def pysb2amici( compiler=compiler, generate_sensitivity_code=generate_sensitivity_code, ) + # 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`. + exporter._code_printer._fpoptimizer = None exporter.generate_model_code() if compile: @@ -241,10 +245,6 @@ 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/tests/test_ode_export.py b/python/tests/test_ode_export.py index f34d78892d..65af2935bb 100644 --- a/python/tests/test_ode_export.py +++ b/python/tests/test_ode_export.py @@ -1,14 +1,13 @@ """Miscellaneous AMICI Python interface tests""" import sympy as sp -from amici.cxxcodeprinter import AmiciCxxCodePrinter +from amici.cxxcodeprinter import csc_matrix from amici.testing import skip_on_valgrind @skip_on_valgrind def test_csc_matrix(): """Test sparse CSC matrix creation""" - printer = AmiciCxxCodePrinter() matrix = sp.Matrix([[1, 0], [2, 3]]) ( symbol_col_ptrs, @@ -16,7 +15,7 @@ def test_csc_matrix(): sparse_list, symbol_list, sparse_matrix, - ) = printer.csc_matrix( + ) = csc_matrix( matrix, rownames=[sp.Symbol("a1"), sp.Symbol("a2")], colnames=[sp.Symbol("b1"), sp.Symbol("b2")], @@ -32,7 +31,6 @@ def test_csc_matrix(): @skip_on_valgrind def test_csc_matrix_empty(): """Test sparse CSC matrix creation for empty matrix""" - printer = AmiciCxxCodePrinter() matrix = sp.Matrix() ( symbol_col_ptrs, @@ -40,7 +38,7 @@ def test_csc_matrix_empty(): sparse_list, symbol_list, sparse_matrix, - ) = printer.csc_matrix(matrix, rownames=[], colnames=[]) + ) = csc_matrix(matrix, rownames=[], colnames=[]) assert symbol_col_ptrs == [] assert symbol_row_vals == [] @@ -52,7 +50,6 @@ def test_csc_matrix_empty(): @skip_on_valgrind def test_csc_matrix_vector(): """Test sparse CSC matrix creation from matrix slice""" - printer = AmiciCxxCodePrinter() matrix = sp.Matrix([[1, 0], [2, 3]]) ( symbol_col_ptrs, @@ -60,7 +57,7 @@ def test_csc_matrix_vector(): sparse_list, symbol_list, sparse_matrix, - ) = printer.csc_matrix( + ) = csc_matrix( matrix[:, 0], colnames=[sp.Symbol("b")], rownames=[sp.Symbol("a1"), sp.Symbol("a2")], @@ -79,7 +76,7 @@ def test_csc_matrix_vector(): sparse_list, symbol_list, sparse_matrix, - ) = printer.csc_matrix( + ) = csc_matrix( matrix[:, 1], colnames=[sp.Symbol("b")], rownames=[sp.Symbol("a1"), sp.Symbol("a2")], From 8271da178d915bf098767aa0c0bd7a0e814bd4f3 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Mon, 26 Feb 2024 13:44:43 +0100 Subject: [PATCH 031/188] Don't eliminate parameters that are initial assignment targets (pt1) (#2304) Currently, parameters that are targets of initial assignments don't show up as parameters or expressions in the amici model. This is rather not what most users would expect. As a first step: treat all SBML parameters that are initial assignment targets and whose initial assignment evaluates to a number as amici parameters. Related to #2150. --- python/sdist/amici/sbml_import.py | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/python/sdist/amici/sbml_import.py b/python/sdist/amici/sbml_import.py index 8d36ad7b81..9d6b7229c5 100644 --- a/python/sdist/amici/sbml_import.py +++ b/python/sdist/amici/sbml_import.py @@ -1051,15 +1051,23 @@ def _process_parameters( "Parameter does not exist." % parameter ) + # parameter ID => initial assignment sympy expression + par_id_to_ia = { + par.getId(): ia + for par in self.sbml.getListOfParameters() + if (ia := self._get_element_initial_assignment(par.getId())) + is not None + } + fixed_parameters = [ parameter for parameter in self.sbml.getListOfParameters() if parameter.getId() in constant_parameters ] for parameter in fixed_parameters: + ia_math = par_id_to_ia.get(parameter.getId()) if ( - self._get_element_initial_assignment(parameter.getId()) - is not None + (ia_math is not None and not ia_math.is_Number) or self.is_assignment_rule_target(parameter) or self.is_rate_rule_target(parameter) ): @@ -1074,7 +1082,10 @@ def _process_parameters( parameter for parameter in self.sbml.getListOfParameters() if parameter.getId() not in constant_parameters - and self._get_element_initial_assignment(parameter.getId()) is None + and ( + (ia_math := par_id_to_ia.get(parameter.getId())) is None + or ia_math.is_Number + ) and not self.is_assignment_rule_target(parameter) and parameter.getId() not in hardcode_symbols ] @@ -1091,16 +1102,16 @@ def _process_parameters( for par in settings["var"]: self.symbols[partype][_get_identifier_symbol(par)] = { "name": par.getName() if par.isSetName() else par.getId(), - "value": sp.Float(par.getValue()), + "value": par_id_to_ia.get( + par.getId(), sp.Float(par.getValue()) + ), } # Parameters that need to be turned into expressions # so far, this concerns parameters with initial assignments containing rateOf(.) # (those have been skipped above) for par in self.sbml.getListOfParameters(): - if ( - ia := self._get_element_initial_assignment(par.getId()) - ) is not None and ia.find( + if (ia := par_id_to_ia.get(par.getId())) is not None and ia.find( sp.core.function.UndefinedFunction("rateOf") ): self.symbols[SymbolId.EXPRESSION][ @@ -1877,7 +1888,10 @@ def _process_initial_assignments(self): for ia in self.sbml.getListOfInitialAssignments(): identifier = _get_identifier_symbol(ia) if identifier in itt.chain( - self.symbols[SymbolId.SPECIES], self.compartments + self.symbols[SymbolId.SPECIES], + self.compartments, + self.symbols[SymbolId.PARAMETER], + self.symbols[SymbolId.FIXED_PARAMETER], ): continue From aeb5f3478ddb85372c3ae69268aafa6f2fdef91c Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Mon, 26 Feb 2024 13:58:02 +0100 Subject: [PATCH 032/188] Refactor de_export.py, extract sympy_utils.py (#2307) No changes in functionality. Related to #2306. --- python/sdist/amici/de_export.py | 205 ++--------------------------- python/sdist/amici/import_utils.py | 7 + python/sdist/amici/pysb_import.py | 2 +- python/sdist/amici/sbml_import.py | 4 +- python/sdist/amici/sympy_utils.py | 196 +++++++++++++++++++++++++++ python/tests/test_misc.py | 21 --- python/tests/test_sympy_utils.py | 24 ++++ 7 files changed, 241 insertions(+), 218 deletions(-) create mode 100644 python/sdist/amici/sympy_utils.py create mode 100644 python/tests/test_sympy_utils.py diff --git a/python/sdist/amici/de_export.py b/python/sdist/amici/de_export.py index 0a6813a6ca..3f8696a86b 100644 --- a/python/sdist/amici/de_export.py +++ b/python/sdist/amici/de_export.py @@ -1,7 +1,7 @@ """ C++ Export ---------- -This module provides all necessary functionality specify an DE model and +This module provides all necessary functionality specify a DE model and generate executable C++ simulation code. The user generally won't have to directly call any function from this module as this will be done by :py:func:`amici.pysb_import.pysb2amici`, @@ -18,12 +18,11 @@ import subprocess import sys from dataclasses import dataclass -from itertools import chain, starmap +from itertools import chain from pathlib import Path from string import Template from typing import ( TYPE_CHECKING, - Any, Callable, Literal, Optional, @@ -59,8 +58,17 @@ strip_pysb, toposort_symbols, unique_preserve_order, + _default_simplify, ) from .logging import get_logger, log_execution_time, set_log_level +from .sympy_utils import ( + _custom_pow_eval_derivative, + _monkeypatched, + smart_jacobian, + smart_multiply, + smart_is_zero_matrix, + _parallel_applyfunc, +) if TYPE_CHECKING: from . import sbml_import @@ -509,109 +517,6 @@ def var_in_function_signature(name: str, varname: str, ode: bool) -> bool: } -@log_execution_time("running smart_jacobian", logger) -def smart_jacobian( - eq: sp.MutableDenseMatrix, sym_var: sp.MutableDenseMatrix -) -> sp.MutableSparseMatrix: - """ - Wrapper around symbolic jacobian with some additional checks that reduce - computation time for large matrices - - :param eq: - equation - :param sym_var: - differentiation variable - :return: - jacobian of eq wrt sym_var - """ - nrow = eq.shape[0] - ncol = sym_var.shape[0] - if ( - not min(eq.shape) - or not min(sym_var.shape) - or smart_is_zero_matrix(eq) - or smart_is_zero_matrix(sym_var) - ): - return sp.MutableSparseMatrix(nrow, ncol, dict()) - - # preprocess sparsity pattern - elements = ( - (i, j, a, b) - for i, a in enumerate(eq) - for j, b in enumerate(sym_var) - if a.has(b) - ) - - if (n_procs := int(os.environ.get("AMICI_IMPORT_NPROCS", 1))) == 1: - # serial - return sp.MutableSparseMatrix( - nrow, ncol, dict(starmap(_jacobian_element, elements)) - ) - - # parallel - from multiprocessing import get_context - - # "spawn" should avoid potential deadlocks occurring with fork - # see e.g. https://stackoverflow.com/a/66113051 - ctx = get_context("spawn") - with ctx.Pool(n_procs) as p: - mapped = p.starmap(_jacobian_element, elements) - return sp.MutableSparseMatrix(nrow, ncol, dict(mapped)) - - -@log_execution_time("running smart_multiply", logger) -def smart_multiply( - x: Union[sp.MutableDenseMatrix, sp.MutableSparseMatrix], - y: sp.MutableDenseMatrix, -) -> Union[sp.MutableDenseMatrix, sp.MutableSparseMatrix]: - """ - Wrapper around symbolic multiplication with some additional checks that - reduce computation time for large matrices - - :param x: - educt 1 - :param y: - educt 2 - :return: - product - """ - if ( - not x.shape[0] - or not y.shape[1] - or smart_is_zero_matrix(x) - or smart_is_zero_matrix(y) - ): - return sp.zeros(x.shape[0], y.shape[1]) - return x.multiply(y) - - -def smart_is_zero_matrix( - x: Union[sp.MutableDenseMatrix, sp.MutableSparseMatrix], -) -> bool: - """A faster implementation of sympy's is_zero_matrix - - Avoids repeated indexer type checks and double iteration to distinguish - False/None. Found to be about 100x faster for large matrices. - - :param x: Matrix to check - """ - - if isinstance(x, sp.MutableDenseMatrix): - return all(xx.is_zero is True for xx in x.flat()) - - if isinstance(x, list): - return all(smart_is_zero_matrix(xx) for xx in x) - - return x.nnz() == 0 - - -def _default_simplify(x): - """Default simplification applied in DEModel""" - # We need this as a free function instead of a lambda to have it picklable - # for parallel simplification - return sp.powsimp(x, deep=True) - - class DEModel: """ Defines a Differential Equation as set of ModelQuantities. @@ -4304,94 +4209,6 @@ def is_valid_identifier(x: str) -> bool: return IDENTIFIER_PATTERN.match(x) is not None -@contextlib.contextmanager -def _monkeypatched(obj: object, name: str, patch: Any): - """ - Temporarily monkeypatches an object. - - :param obj: - object to be patched - - :param name: - name of the attribute to be patched - - :param patch: - patched value - """ - pre_patched_value = getattr(obj, name) - setattr(obj, name, patch) - try: - yield object - finally: - setattr(obj, name, pre_patched_value) - - -def _custom_pow_eval_derivative(self, s): - """ - Custom Pow derivative that removes a removable singularity for - ``self.base == 0`` and ``self.base.diff(s) == 0``. This function is - intended to be monkeypatched into :py:method:`sympy.Pow._eval_derivative`. - - :param self: - sp.Pow class - - :param s: - variable with respect to which the derivative will be computed - """ - dbase = self.base.diff(s) - dexp = self.exp.diff(s) - part1 = sp.Pow(self.base, self.exp - 1) * self.exp * dbase - part2 = self * dexp * sp.log(self.base) - if self.base.is_nonzero or dbase.is_nonzero or part2.is_zero: - # first piece never applies or is zero anyways - return part1 + part2 - - return part1 + sp.Piecewise( - (self.base, sp.And(sp.Eq(self.base, 0), sp.Eq(dbase, 0))), - (part2, True), - ) - - -def _jacobian_element(i, j, eq_i, sym_var_j): - """Compute a single element of a jacobian""" - return (i, j), eq_i.diff(sym_var_j) - - -def _parallel_applyfunc(obj: sp.Matrix, func: Callable) -> sp.Matrix: - """Parallel implementation of sympy's Matrix.applyfunc""" - if (n_procs := int(os.environ.get("AMICI_IMPORT_NPROCS", 1))) == 1: - # serial - return obj.applyfunc(func) - - # parallel - from multiprocessing import get_context - from pickle import PicklingError - - from sympy.matrices.dense import DenseMatrix - - # "spawn" should avoid potential deadlocks occurring with fork - # see e.g. https://stackoverflow.com/a/66113051 - ctx = get_context("spawn") - with ctx.Pool(n_procs) as p: - try: - if isinstance(obj, DenseMatrix): - return obj._new(obj.rows, obj.cols, p.map(func, obj)) - elif isinstance(obj, sp.SparseMatrix): - dok = obj.todok() - mapped = p.map(func, dok.values()) - dok = {k: v for k, v in zip(dok.keys(), mapped) if v != 0} - return obj._new(obj.rows, obj.cols, dok) - else: - raise ValueError(f"Unsupported matrix type {type(obj)}") - except PicklingError as e: - raise ValueError( - f"Couldn't pickle {func}. This is likely because the argument " - "was not a module-level function. Either rewrite the argument " - "to a module-level function or disable parallelization by " - "setting `AMICI_IMPORT_NPROCS=1`." - ) from e - - def _write_gitignore(dest_dir: Path) -> None: """Write .gitignore file. diff --git a/python/sdist/amici/import_utils.py b/python/sdist/amici/import_utils.py index 63a160c1de..029c2cc6de 100644 --- a/python/sdist/amici/import_utils.py +++ b/python/sdist/amici/import_utils.py @@ -748,3 +748,10 @@ def unique_preserve_order(seq: Sequence) -> list: sbml_time_symbol = symbol_with_assumptions("time") amici_time_symbol = symbol_with_assumptions("t") + + +def _default_simplify(x): + """Default simplification applied in DEModel""" + # We need this as a free function instead of a lambda to have it picklable + # for parallel simplification + return sp.powsimp(x, deep=True) diff --git a/python/sdist/amici/pysb_import.py b/python/sdist/amici/pysb_import.py index c79a8c50f9..05f9ed9b28 100644 --- a/python/sdist/amici/pysb_import.py +++ b/python/sdist/amici/pysb_import.py @@ -34,7 +34,6 @@ Observable, Parameter, SigmaY, - _default_simplify, ) from .import_utils import ( _get_str_symbol_identifiers, @@ -42,6 +41,7 @@ generate_measurement_symbol, noise_distribution_to_cost_function, noise_distribution_to_observable_transformation, + _default_simplify, ) from .logging import get_logger, log_execution_time, set_log_level diff --git a/python/sdist/amici/sbml_import.py b/python/sdist/amici/sbml_import.py index 9d6b7229c5..8fc2ab9fd9 100644 --- a/python/sdist/amici/sbml_import.py +++ b/python/sdist/amici/sbml_import.py @@ -31,9 +31,8 @@ from .de_export import ( DEExporter, DEModel, - _default_simplify, - smart_is_zero_matrix, ) +from .sympy_utils import smart_is_zero_matrix from .import_utils import ( RESERVED_SYMBOLS, _check_unsupported_functions, @@ -50,6 +49,7 @@ smart_subs_dict, symbol_with_assumptions, toposort_symbols, + _default_simplify, ) from .logging import get_logger, log_execution_time, set_log_level from .sbml_utils import SBMLException, _parse_logical_operators diff --git a/python/sdist/amici/sympy_utils.py b/python/sdist/amici/sympy_utils.py new file mode 100644 index 0000000000..863fc57f14 --- /dev/null +++ b/python/sdist/amici/sympy_utils.py @@ -0,0 +1,196 @@ +"""Functionality for working with sympy objects.""" +import os +from itertools import starmap +from typing import Union, Any, Callable +import contextlib +import sympy as sp +import logging +from amici.de_export import get_logger +from amici.logging import log_execution_time + + +logger = get_logger(__name__, logging.ERROR) + + +def _custom_pow_eval_derivative(self, s): + """ + Custom Pow derivative that removes a removable singularity for + ``self.base == 0`` and ``self.base.diff(s) == 0``. This function is + intended to be monkeypatched into :py:method:`sympy.Pow._eval_derivative`. + + :param self: + sp.Pow class + + :param s: + variable with respect to which the derivative will be computed + """ + dbase = self.base.diff(s) + dexp = self.exp.diff(s) + part1 = sp.Pow(self.base, self.exp - 1) * self.exp * dbase + part2 = self * dexp * sp.log(self.base) + if self.base.is_nonzero or dbase.is_nonzero or part2.is_zero: + # first piece never applies or is zero anyway + return part1 + part2 + + return part1 + sp.Piecewise( + (self.base, sp.And(sp.Eq(self.base, 0), sp.Eq(dbase, 0))), + (part2, True), + ) + + +@contextlib.contextmanager +def _monkeypatched(obj: object, name: str, patch: Any): + """ + Temporarily monkeypatches an object. + + :param obj: + object to be patched + + :param name: + name of the attribute to be patched + + :param patch: + patched value + """ + pre_patched_value = getattr(obj, name) + setattr(obj, name, patch) + try: + yield object + finally: + setattr(obj, name, pre_patched_value) + + +@log_execution_time("running smart_jacobian", logger) +def smart_jacobian( + eq: sp.MutableDenseMatrix, sym_var: sp.MutableDenseMatrix +) -> sp.MutableSparseMatrix: + """ + Wrapper around symbolic jacobian with some additional checks that reduce + computation time for large matrices + + :param eq: + equation + :param sym_var: + differentiation variable + :return: + jacobian of eq wrt sym_var + """ + nrow = eq.shape[0] + ncol = sym_var.shape[0] + if ( + not min(eq.shape) + or not min(sym_var.shape) + or smart_is_zero_matrix(eq) + or smart_is_zero_matrix(sym_var) + ): + return sp.MutableSparseMatrix(nrow, ncol, dict()) + + # preprocess sparsity pattern + elements = ( + (i, j, a, b) + for i, a in enumerate(eq) + for j, b in enumerate(sym_var) + if a.has(b) + ) + + if (n_procs := int(os.environ.get("AMICI_IMPORT_NPROCS", 1))) == 1: + # serial + return sp.MutableSparseMatrix( + nrow, ncol, dict(starmap(_jacobian_element, elements)) + ) + + # parallel + from multiprocessing import get_context + + # "spawn" should avoid potential deadlocks occurring with fork + # see e.g. https://stackoverflow.com/a/66113051 + ctx = get_context("spawn") + with ctx.Pool(n_procs) as p: + mapped = p.starmap(_jacobian_element, elements) + return sp.MutableSparseMatrix(nrow, ncol, dict(mapped)) + + +@log_execution_time("running smart_multiply", logger) +def smart_multiply( + x: Union[sp.MutableDenseMatrix, sp.MutableSparseMatrix], + y: sp.MutableDenseMatrix, +) -> Union[sp.MutableDenseMatrix, sp.MutableSparseMatrix]: + """ + Wrapper around symbolic multiplication with some additional checks that + reduce computation time for large matrices + + :param x: + educt 1 + :param y: + educt 2 + :return: + product + """ + if ( + not x.shape[0] + or not y.shape[1] + or smart_is_zero_matrix(x) + or smart_is_zero_matrix(y) + ): + return sp.zeros(x.shape[0], y.shape[1]) + return x.multiply(y) + + +def smart_is_zero_matrix( + x: Union[sp.MutableDenseMatrix, sp.MutableSparseMatrix], +) -> bool: + """A faster implementation of sympy's is_zero_matrix + + Avoids repeated indexer type checks and double iteration to distinguish + False/None. Found to be about 100x faster for large matrices. + + :param x: Matrix to check + """ + + if isinstance(x, sp.MutableDenseMatrix): + return all(xx.is_zero is True for xx in x.flat()) + + if isinstance(x, list): + return all(smart_is_zero_matrix(xx) for xx in x) + + return x.nnz() == 0 + + +def _jacobian_element(i, j, eq_i, sym_var_j): + """Compute a single element of a jacobian""" + return (i, j), eq_i.diff(sym_var_j) + + +def _parallel_applyfunc(obj: sp.Matrix, func: Callable) -> sp.Matrix: + """Parallel implementation of sympy's Matrix.applyfunc""" + if (n_procs := int(os.environ.get("AMICI_IMPORT_NPROCS", 1))) == 1: + # serial + return obj.applyfunc(func) + + # parallel + from multiprocessing import get_context + from pickle import PicklingError + + from sympy.matrices.dense import DenseMatrix + + # "spawn" should avoid potential deadlocks occurring with fork + # see e.g. https://stackoverflow.com/a/66113051 + ctx = get_context("spawn") + with ctx.Pool(n_procs) as p: + try: + if isinstance(obj, DenseMatrix): + return obj._new(obj.rows, obj.cols, p.map(func, obj)) + elif isinstance(obj, sp.SparseMatrix): + dok = obj.todok() + mapped = p.map(func, dok.values()) + dok = {k: v for k, v in zip(dok.keys(), mapped) if v != 0} + return obj._new(obj.rows, obj.cols, dok) + else: + raise ValueError(f"Unsupported matrix type {type(obj)}") + except PicklingError as e: + raise ValueError( + f"Couldn't pickle {func}. This is likely because the argument " + "was not a module-level function. Either rewrite the argument " + "to a module-level function or disable parallelization by " + "setting `AMICI_IMPORT_NPROCS=1`." + ) from e diff --git a/python/tests/test_misc.py b/python/tests/test_misc.py index 24bba79888..1ddeb3f760 100644 --- a/python/tests/test_misc.py +++ b/python/tests/test_misc.py @@ -8,8 +8,6 @@ import pytest import sympy as sp from amici.de_export import ( - _custom_pow_eval_derivative, - _monkeypatched, smart_subs_dict, ) from amici.testing import skip_on_valgrind @@ -108,25 +106,6 @@ def test_smart_subs_dict(): assert sp.simplify(result_reverse - expected_reverse).is_zero -@skip_on_valgrind -def test_monkeypatch(): - t = sp.Symbol("t") - n = sp.Symbol("n") - vals = [(t, 0), (n, 1)] - - # check that the removable singularity still exists - assert (t**n).diff(t).subs(vals) is sp.nan - - # check that we can monkeypatch it out - with _monkeypatched( - sp.Pow, "_eval_derivative", _custom_pow_eval_derivative - ): - assert (t**n).diff(t).subs(vals) is not sp.nan - - # 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 diff --git a/python/tests/test_sympy_utils.py b/python/tests/test_sympy_utils.py new file mode 100644 index 0000000000..da89741352 --- /dev/null +++ b/python/tests/test_sympy_utils.py @@ -0,0 +1,24 @@ +"""Tests related to the sympy_utils module.""" + +from amici.sympy_utils import _custom_pow_eval_derivative, _monkeypatched +import sympy as sp +from amici.testing import skip_on_valgrind + + +@skip_on_valgrind +def test_monkeypatch(): + t = sp.Symbol("t") + n = sp.Symbol("n") + vals = [(t, 0), (n, 1)] + + # check that the removable singularity still exists + assert (t**n).diff(t).subs(vals) is sp.nan + + # check that we can monkeypatch it out + with _monkeypatched( + sp.Pow, "_eval_derivative", _custom_pow_eval_derivative + ): + assert (t**n).diff(t).subs(vals) is not sp.nan + + # check that the monkeypatch is transient + assert (t**n).diff(t).subs(vals) is sp.nan From c9b08ac292b29c830e6d9142def464fd2f271863 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Mon, 26 Feb 2024 13:58:27 +0100 Subject: [PATCH 033/188] Refactor de_export.py, extract template functions (#2314) Start moving functionality for amici model code generation to a private subpackage `amici._codegen`. More to follow. --- python/sdist/amici/_codegen/__init__.py | 0 python/sdist/amici/_codegen/template.py | 42 +++++++++++++++++++++++++ python/sdist/amici/de_export.py | 40 +---------------------- 3 files changed, 43 insertions(+), 39 deletions(-) create mode 100644 python/sdist/amici/_codegen/__init__.py create mode 100644 python/sdist/amici/_codegen/template.py diff --git a/python/sdist/amici/_codegen/__init__.py b/python/sdist/amici/_codegen/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python/sdist/amici/_codegen/template.py b/python/sdist/amici/_codegen/template.py new file mode 100644 index 0000000000..34f3391ed6 --- /dev/null +++ b/python/sdist/amici/_codegen/template.py @@ -0,0 +1,42 @@ +"""Functions to apply template substitution to files.""" +from pathlib import Path +from string import Template +from typing import Union + + +class TemplateAmici(Template): + """ + Template format used in AMICI (see :class:`string.Template` for more + details). + + :cvar delimiter: + delimiter that identifies template variables + """ + + delimiter = "TPL_" + + +def apply_template( + source_file: Union[str, Path], + target_file: Union[str, Path], + template_data: dict[str, str], +) -> None: + """ + Load source file, apply template substitution as provided in + templateData and save as targetFile. + + :param source_file: + relative or absolute path to template file + + :param target_file: + relative or absolute path to output file + + :param template_data: + template keywords to substitute (key is template + variable without :attr:`TemplateAmici.delimiter`) + """ + with open(source_file) as filein: + src = TemplateAmici(filein.read()) + result = src.safe_substitute(template_data) + with open(target_file, "w") as fileout: + fileout.write(result) diff --git a/python/sdist/amici/de_export.py b/python/sdist/amici/de_export.py index 3f8696a86b..513a7c0e63 100644 --- a/python/sdist/amici/de_export.py +++ b/python/sdist/amici/de_export.py @@ -20,7 +20,6 @@ from dataclasses import dataclass from itertools import chain from pathlib import Path -from string import Template from typing import ( TYPE_CHECKING, Callable, @@ -42,6 +41,7 @@ amiciSwigPath, splines, ) +from ._codegen.template import apply_template from .constants import SymbolId from .cxxcodeprinter import ( AmiciCxxCodePrinter, @@ -3995,44 +3995,6 @@ def set_name(self, model_name: str) -> None: self.model_name = model_name -class TemplateAmici(Template): - """ - Template format used in AMICI (see :class:`string.Template` for more - details). - - :cvar delimiter: - delimiter that identifies template variables - """ - - delimiter = "TPL_" - - -def apply_template( - source_file: Union[str, Path], - target_file: Union[str, Path], - template_data: dict[str, str], -) -> None: - """ - Load source file, apply template substitution as provided in - templateData and save as targetFile. - - :param source_file: - relative or absolute path to template file - - :param target_file: - relative or absolute path to output file - - :param template_data: - template keywords to substitute (key is template - variable without :attr:`TemplateAmici.delimiter`) - """ - with open(source_file) as filein: - src = TemplateAmici(filein.read()) - result = src.safe_substitute(template_data) - with open(target_file, "w") as fileout: - fileout.write(result) - - def get_function_extern_declaration(fun: str, name: str, ode: bool) -> str: """ Constructs the extern function declaration for a given function From eda7ad0327310caa6de97011119073496bddde01 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Mon, 26 Feb 2024 14:09:20 +0100 Subject: [PATCH 034/188] Refactor: smoother conversion from SUNMatrixWrapper to SUNMatrix (#2317) Adds an implicit conversion function to SUNMatrixWrapper make things more readable. --- include/amici/rdata.h | 2 +- include/amici/sundials_matrix_wrapper.h | 5 +++++ src/model.cpp | 8 ++++---- src/model_dae.cpp | 20 +++++++++---------- src/model_ode.cpp | 20 +++++++++---------- src/newton_solver.cpp | 26 ++++++++++++------------- src/sundials_linsol_wrapper.cpp | 8 ++++---- tests/cpp/unittests/testMisc.cpp | 8 ++++---- 8 files changed, 51 insertions(+), 46 deletions(-) diff --git a/include/amici/rdata.h b/include/amici/rdata.h index 1de02c99db..6357d24748 100644 --- a/include/amici/rdata.h +++ b/include/amici/rdata.h @@ -576,7 +576,7 @@ class ReturnData : public ModelDimensions { if (!this->J.empty()) { SUNMatrixWrapper J(nx_solver, nx_solver); - model.fJ(t_, 0.0, x_solver_, dx_solver_, xdot, J.get()); + model.fJ(t_, 0.0, x_solver_, dx_solver_, xdot, J); // CVODES uses colmajor, so we need to transform to rowmajor for (int ix = 0; ix < model.nx_solver; ix++) for (int jx = 0; jx < model.nx_solver; jx++) diff --git a/include/amici/sundials_matrix_wrapper.h b/include/amici/sundials_matrix_wrapper.h index ee2516f78d..0bb9b9215f 100644 --- a/include/amici/sundials_matrix_wrapper.h +++ b/include/amici/sundials_matrix_wrapper.h @@ -72,6 +72,11 @@ class SUNMatrixWrapper { ~SUNMatrixWrapper(); + /** + * @brief Conversion function. + */ + operator SUNMatrix() { return get(); }; + /** * @brief Copy constructor * @param other diff --git a/src/model.cpp b/src/model.cpp index 2485867a4c..3478610bbe 100644 --- a/src/model.cpp +++ b/src/model.cpp @@ -2249,7 +2249,7 @@ void Model::fdJydy(int const it, AmiVector const& x, ExpData const& edata) { auto tmp_sparse = SUNMatrixWrapper(tmp_dense, 0.0, CSC_MAT); auto ret = SUNMatScaleAdd( - 1.0, derived_state_.dJydy_.at(iyt).get(), tmp_sparse.get() + 1.0, derived_state_.dJydy_.at(iyt), tmp_sparse ); if (ret != SUNMAT_SUCCESS) { throw AmiException( @@ -2897,7 +2897,7 @@ void Model::fdwdp(realtype const t, realtype const* x) { } if (always_check_finite_) { - checkFinite(derived_state_.dwdp_.get(), ModelQuantity::dwdp, t); + checkFinite(derived_state_.dwdp_, ModelQuantity::dwdp, t); } } @@ -2943,7 +2943,7 @@ void Model::fdwdx(realtype const t, realtype const* x) { } if (always_check_finite_) { - checkFinite(derived_state_.dwdx_.get(), ModelQuantity::dwdx, t); + checkFinite(derived_state_.dwdx_, ModelQuantity::dwdx, t); } } @@ -2960,7 +2960,7 @@ void Model::fdwdw(realtype const t, realtype const* x) { ); if (always_check_finite_) { - checkFinite(dwdw_.get(), ModelQuantity::dwdw, t); + checkFinite(dwdw_, ModelQuantity::dwdw, t); } } diff --git a/src/model_dae.cpp b/src/model_dae.cpp index 3b2e74e0e1..a3a8aaea26 100644 --- a/src/model_dae.cpp +++ b/src/model_dae.cpp @@ -14,7 +14,7 @@ void Model_DAE::fJ( realtype t, realtype cj, const_N_Vector x, const_N_Vector dx, const_N_Vector /*xdot*/, SUNMatrix J ) { - fJSparse(t, cj, x, dx, derived_state_.J_.get()); + fJSparse(t, cj, x, dx, derived_state_.J_); derived_state_.J_.refresh(); auto JDense = SUNMatrixWrapper(J); derived_state_.J_.to_dense(JDense); @@ -88,7 +88,7 @@ void Model_DAE::fJv( N_Vector Jv, realtype cj ) { N_VConst(0.0, Jv); - fJSparse(t, cj, x, dx, derived_state_.J_.get()); + fJSparse(t, cj, x, dx, derived_state_.J_); derived_state_.J_.refresh(); derived_state_.J_.multiply(Jv, v); } @@ -135,7 +135,7 @@ void Model_DAE::fJDiag( realtype const t, AmiVector& JDiag, realtype const /*cj*/, AmiVector const& x, AmiVector const& dx ) { - fJSparse(t, 0.0, x.getNVector(), dx.getNVector(), derived_state_.J_.get()); + fJSparse(t, 0.0, x.getNVector(), dx.getNVector(), derived_state_.J_); derived_state_.J_.refresh(); derived_state_.J_.to_diag(JDiag.getNVector()); if (checkFinite(JDiag.getVector(), ModelQuantity::JDiag) != AMICI_SUCCESS) @@ -355,7 +355,7 @@ void Model_DAE::fJB( realtype t, realtype cj, const_N_Vector x, const_N_Vector dx, const_N_Vector /*xB*/, const_N_Vector /*dxB*/, SUNMatrix JB ) { - fJSparse(t, cj, x, dx, derived_state_.J_.get()); + fJSparse(t, cj, x, dx, derived_state_.J_); derived_state_.J_.refresh(); auto JBDense = SUNMatrixWrapper(JB); derived_state_.J_.transpose(JBDense, -1.0, nxtrue_solver); @@ -376,7 +376,7 @@ void Model_DAE::fJSparseB( realtype t, realtype cj, const_N_Vector x, const_N_Vector dx, const_N_Vector /*xB*/, const_N_Vector /*dxB*/, SUNMatrix JB ) { - fJSparse(t, cj, x, dx, derived_state_.J_.get()); + fJSparse(t, cj, x, dx, derived_state_.J_); derived_state_.J_.refresh(); auto JSparseB = SUNMatrixWrapper(JB); derived_state_.J_.transpose(JSparseB, -1.0, nxtrue_solver); @@ -387,7 +387,7 @@ void Model_DAE::fJvB( const_N_Vector dxB, const_N_Vector vB, N_Vector JvB, realtype cj ) { N_VConst(0.0, JvB); - fJSparseB(t, cj, x, dx, xB, dxB, derived_state_.JB_.get()); + fJSparseB(t, cj, x, dx, xB, dxB, derived_state_.JB_); derived_state_.JB_.refresh(); derived_state_.JB_.multiply(JvB, vB); } @@ -397,7 +397,7 @@ void Model_DAE::fxBdot( const_N_Vector dxB, N_Vector xBdot ) { N_VConst(0.0, xBdot); - fJSparseB(t, 1.0, x, dx, xB, dxB, derived_state_.JB_.get()); + fJSparseB(t, 1.0, x, dx, xB, dxB, derived_state_.JB_); derived_state_.JB_.refresh(); fM(t, x); derived_state_.JB_.multiply(xBdot, xB); @@ -454,7 +454,7 @@ void Model_DAE::fqBdot_ss( void Model_DAE::fJSparseB_ss(SUNMatrix JB) { /* Just pass the model Jacobian on to JB */ - SUNMatCopy(derived_state_.JB_.get(), JB); + SUNMatCopy(derived_state_.JB_, JB); derived_state_.JB_.refresh(); } @@ -465,7 +465,7 @@ void Model_DAE::writeSteadystateJB( /* Get backward Jacobian */ fJSparseB( t, cj, x.getNVector(), dx.getNVector(), xB.getNVector(), - dxB.getNVector(), derived_state_.JB_.get() + dxB.getNVector(), derived_state_.JB_ ); derived_state_.JB_.refresh(); /* Switch sign, as we integrate forward in time, not backward */ @@ -491,7 +491,7 @@ void Model_DAE::fsxdot( // the same for all remaining fM(t, x); fdxdotdp(t, x, dx); - fJSparse(t, 0.0, x, dx, derived_state_.J_.get()); + fJSparse(t, 0.0, x, dx, derived_state_.J_); derived_state_.J_.refresh(); } diff --git a/src/model_ode.cpp b/src/model_ode.cpp index 52275008e3..257ef45289 100644 --- a/src/model_ode.cpp +++ b/src/model_ode.cpp @@ -14,7 +14,7 @@ void Model_ODE::fJ( void Model_ODE::fJ( realtype t, const_N_Vector x, const_N_Vector /*xdot*/, SUNMatrix J ) { - fJSparse(t, x, derived_state_.J_.get()); + fJSparse(t, x, derived_state_.J_); derived_state_.J_.refresh(); auto JDense = SUNMatrixWrapper(J); derived_state_.J_.to_dense(JDense); @@ -77,7 +77,7 @@ void Model_ODE::fJv( const_N_Vector v, N_Vector Jv, realtype t, const_N_Vector x ) { N_VConst(0.0, Jv); - fJSparse(t, x, derived_state_.J_.get()); + fJSparse(t, x, derived_state_.J_); derived_state_.J_.refresh(); derived_state_.J_.multiply(Jv, v); } @@ -355,7 +355,7 @@ void Model_ODE::fJB( realtype t, const_N_Vector x, const_N_Vector /*xB*/, const_N_Vector /*xBdot*/, SUNMatrix JB ) { - fJSparse(t, x, derived_state_.J_.get()); + fJSparse(t, x, derived_state_.J_); derived_state_.J_.refresh(); auto JDenseB = SUNMatrixWrapper(JB); derived_state_.J_.transpose(JDenseB, -1.0, nxtrue_solver); @@ -373,14 +373,14 @@ void Model_ODE::fJSparseB( realtype t, const_N_Vector x, const_N_Vector /*xB*/, const_N_Vector /*xBdot*/, SUNMatrix JB ) { - fJSparse(t, x, derived_state_.J_.get()); + fJSparse(t, x, derived_state_.J_); derived_state_.J_.refresh(); auto JSparseB = SUNMatrixWrapper(JB); derived_state_.J_.transpose(JSparseB, -1.0, nxtrue_solver); } void Model_ODE::fJDiag(realtype t, N_Vector JDiag, const_N_Vector x) { - fJSparse(t, x, derived_state_.J_.get()); + fJSparse(t, x, derived_state_.J_); derived_state_.J_.refresh(); derived_state_.J_.to_diag(JDiag); } @@ -390,14 +390,14 @@ void Model_ODE::fJvB( const_N_Vector xB ) { N_VConst(0.0, JvB); - fJSparseB(t, x, xB, nullptr, derived_state_.JB_.get()); + fJSparseB(t, x, xB, nullptr, derived_state_.JB_); derived_state_.JB_.refresh(); derived_state_.JB_.multiply(JvB, vB); } void Model_ODE::fxBdot(realtype t, N_Vector x, N_Vector xB, N_Vector xBdot) { N_VConst(0.0, xBdot); - fJSparseB(t, x, xB, nullptr, derived_state_.JB_.get()); + fJSparseB(t, x, xB, nullptr, derived_state_.JB_); derived_state_.JB_.refresh(); derived_state_.JB_.multiply(xBdot, xB); } @@ -456,7 +456,7 @@ void Model_ODE::fqBdot_ss(realtype /*t*/, N_Vector xB, N_Vector qBdot) const { void Model_ODE::fJSparseB_ss(SUNMatrix JB) { /* Just copy the model Jacobian */ - SUNMatCopy(derived_state_.JB_.get(), JB); + SUNMatCopy(derived_state_.JB_, JB); derived_state_.JB_.refresh(); } @@ -468,7 +468,7 @@ void Model_ODE::writeSteadystateJB( /* Get backward Jacobian */ fJSparseB( t, x.getNVector(), xB.getNVector(), xBdot.getNVector(), - derived_state_.JB_.get() + derived_state_.JB_ ); derived_state_.JB_.refresh(); /* Switch sign, as we integrate forward in time, not backward */ @@ -492,7 +492,7 @@ void Model_ODE::fsxdot( // we only need to call this for the first parameter index will be // the same for all remaining fdxdotdp(t, x); - fJSparse(t, x, derived_state_.J_.get()); + fJSparse(t, x, derived_state_.J_); derived_state_.J_.refresh(); } if (pythonGenerated) { diff --git a/src/newton_solver.cpp b/src/newton_solver.cpp index 8c3fcca5f4..9f011bac1e 100644 --- a/src/newton_solver.cpp +++ b/src/newton_solver.cpp @@ -108,7 +108,7 @@ void NewtonSolver::computeNewtonSensis( NewtonSolverDense::NewtonSolverDense(Model const& model) : NewtonSolver(model) , Jtmp_(model.nx_solver, model.nx_solver) - , linsol_(SUNLinSol_Dense(x_.getNVector(), Jtmp_.get())) { + , linsol_(SUNLinSol_Dense(x_.getNVector(), Jtmp_)) { auto status = SUNLinSolInitialize_Dense(linsol_); if (status != SUNLS_SUCCESS) throw NewtonFailure(status, "SUNLinSolInitialize_Dense"); @@ -117,9 +117,9 @@ NewtonSolverDense::NewtonSolverDense(Model const& model) void NewtonSolverDense::prepareLinearSystem( Model& model, SimulationState const& state ) { - model.fJ(state.t, 0.0, state.x, state.dx, xdot_, Jtmp_.get()); + model.fJ(state.t, 0.0, state.x, state.dx, xdot_, Jtmp_); Jtmp_.refresh(); - auto status = SUNLinSolSetup_Dense(linsol_, Jtmp_.get()); + auto status = SUNLinSolSetup_Dense(linsol_, Jtmp_); if (status != SUNLS_SUCCESS) throw NewtonFailure(status, "SUNLinSolSetup_Dense"); } @@ -127,16 +127,16 @@ void NewtonSolverDense::prepareLinearSystem( void NewtonSolverDense::prepareLinearSystemB( Model& model, SimulationState const& state ) { - model.fJB(state.t, 0.0, state.x, state.dx, xB_, dxB_, xdot_, Jtmp_.get()); + model.fJB(state.t, 0.0, state.x, state.dx, xB_, dxB_, xdot_, Jtmp_); Jtmp_.refresh(); - auto status = SUNLinSolSetup_Dense(linsol_, Jtmp_.get()); + auto status = SUNLinSolSetup_Dense(linsol_, Jtmp_); if (status != SUNLS_SUCCESS) throw NewtonFailure(status, "SUNLinSolSetup_Dense"); } void NewtonSolverDense::solveLinearSystem(AmiVector& rhs) { auto status = SUNLinSolSolve_Dense( - linsol_, Jtmp_.get(), rhs.getNVector(), rhs.getNVector(), 0.0 + linsol_, Jtmp_, rhs.getNVector(), rhs.getNVector(), 0.0 ); Jtmp_.refresh(); // last argument is tolerance and does not have any influence on result @@ -167,7 +167,7 @@ NewtonSolverDense::~NewtonSolverDense() { NewtonSolverSparse::NewtonSolverSparse(Model const& model) : NewtonSolver(model) , Jtmp_(model.nx_solver, model.nx_solver, model.nnz, CSC_MAT) - , linsol_(SUNKLU(x_.getNVector(), Jtmp_.get())) { + , linsol_(SUNKLU(x_.getNVector(), Jtmp_)) { auto status = SUNLinSolInitialize_KLU(linsol_); if (status != SUNLS_SUCCESS) throw NewtonFailure(status, "SUNLinSolInitialize_KLU"); @@ -177,9 +177,9 @@ void NewtonSolverSparse::prepareLinearSystem( Model& model, SimulationState const& state ) { /* Get sparse Jacobian */ - model.fJSparse(state.t, 0.0, state.x, state.dx, xdot_, Jtmp_.get()); + model.fJSparse(state.t, 0.0, state.x, state.dx, xdot_, Jtmp_); Jtmp_.refresh(); - auto status = SUNLinSolSetup_KLU(linsol_, Jtmp_.get()); + auto status = SUNLinSolSetup_KLU(linsol_, Jtmp_); if (status != SUNLS_SUCCESS) throw NewtonFailure(status, "SUNLinSolSetup_KLU"); } @@ -189,10 +189,10 @@ void NewtonSolverSparse::prepareLinearSystemB( ) { /* Get sparse Jacobian */ model.fJSparseB( - state.t, 0.0, state.x, state.dx, xB_, dxB_, xdot_, Jtmp_.get() + state.t, 0.0, state.x, state.dx, xB_, dxB_, xdot_, Jtmp_ ); Jtmp_.refresh(); - auto status = SUNLinSolSetup_KLU(linsol_, Jtmp_.get()); + auto status = SUNLinSolSetup_KLU(linsol_, Jtmp_); if (status != SUNLS_SUCCESS) throw NewtonFailure(status, "SUNLinSolSetup_KLU"); } @@ -200,7 +200,7 @@ void NewtonSolverSparse::prepareLinearSystemB( void NewtonSolverSparse::solveLinearSystem(AmiVector& rhs) { /* Pass pointer to the linear solver */ auto status = SUNLinSolSolve_KLU( - linsol_, Jtmp_.get(), rhs.getNVector(), rhs.getNVector(), 0.0 + linsol_, Jtmp_, rhs.getNVector(), rhs.getNVector(), 0.0 ); // last argument is tolerance and does not have any influence on result @@ -211,7 +211,7 @@ void NewtonSolverSparse::solveLinearSystem(AmiVector& rhs) { void NewtonSolverSparse::reinitialize() { /* partial reinitialization, don't need to reallocate Jtmp_ */ auto status = SUNLinSol_KLUReInit( - linsol_, Jtmp_.get(), Jtmp_.capacity(), SUNKLU_REINIT_PARTIAL + linsol_, Jtmp_, Jtmp_.capacity(), SUNKLU_REINIT_PARTIAL ); if (status != SUNLS_SUCCESS) throw NewtonFailure(status, "SUNLinSol_KLUReInit"); diff --git a/src/sundials_linsol_wrapper.cpp b/src/sundials_linsol_wrapper.cpp index 765f2a1f91..5752ea03c3 100644 --- a/src/sundials_linsol_wrapper.cpp +++ b/src/sundials_linsol_wrapper.cpp @@ -161,7 +161,7 @@ SUNLinSolBand::SUNLinSolBand(N_Vector x, SUNMatrix A) SUNLinSolBand::SUNLinSolBand(AmiVector const& x, int ubw, int lbw) : A_(SUNMatrixWrapper(x.getLength(), ubw, lbw)) { - solver_ = SUNLinSol_Band(const_cast(x.getNVector()), A_.get()); + solver_ = SUNLinSol_Band(const_cast(x.getNVector()), A_); if (!solver_) throw AmiException("Failed to create solver."); } @@ -170,7 +170,7 @@ SUNMatrix SUNLinSolBand::getMatrix() const { return A_.get(); } SUNLinSolDense::SUNLinSolDense(AmiVector const& x) : A_(SUNMatrixWrapper(x.getLength(), x.getLength())) { - solver_ = SUNLinSol_Dense(const_cast(x.getNVector()), A_.get()); + solver_ = SUNLinSol_Dense(const_cast(x.getNVector()), A_); if (!solver_) throw AmiException("Failed to create solver."); } @@ -187,7 +187,7 @@ SUNLinSolKLU::SUNLinSolKLU( AmiVector const& x, int nnz, int sparsetype, StateOrdering ordering ) : A_(SUNMatrixWrapper(x.getLength(), x.getLength(), nnz, sparsetype)) { - solver_ = SUNLinSol_KLU(const_cast(x.getNVector()), A_.get()); + solver_ = SUNLinSol_KLU(const_cast(x.getNVector()), A_); if (!solver_) throw AmiException("Failed to create solver."); @@ -197,7 +197,7 @@ SUNLinSolKLU::SUNLinSolKLU( SUNMatrix SUNLinSolKLU::getMatrix() const { return A_.get(); } void SUNLinSolKLU::reInit(int nnz, int reinit_type) { - int status = SUNLinSol_KLUReInit(solver_, A_.get(), nnz, reinit_type); + int status = SUNLinSol_KLUReInit(solver_, A_, nnz, reinit_type); if (status != SUNLS_SUCCESS) throw AmiException("SUNLinSol_KLUReInit failed with %d", status); } diff --git a/tests/cpp/unittests/testMisc.cpp b/tests/cpp/unittests/testMisc.cpp index 1f464b3433..a722b567a7 100644 --- a/tests/cpp/unittests/testMisc.cpp +++ b/tests/cpp/unittests/testMisc.cpp @@ -681,7 +681,7 @@ TEST(UnravelIndex, UnravelIndexSunMatDense) A.set_data(2, 1, 5); for(int i = 0; i < 6; ++i) { - auto idx = unravel_index(i, A.get()); + auto idx = unravel_index(i, A); EXPECT_EQ(A.get_data(idx.first, idx.second), i); } } @@ -706,7 +706,7 @@ TEST(UnravelIndex, UnravelIndexSunMatSparse) D.set_data(2, 1, 0); D.set_data(3, 1, 0); - auto S = SUNSparseFromDenseMatrix(D.get(), 1e-15, CSC_MAT); + auto S = SUNSparseFromDenseMatrix(D, 1e-15, CSC_MAT); EXPECT_EQ(unravel_index(0, S), std::make_pair((sunindextype) 2, (sunindextype) 0)); EXPECT_EQ(unravel_index(1, S), std::make_pair((sunindextype) 3, (sunindextype) 0)); @@ -720,8 +720,8 @@ TEST(UnravelIndex, UnravelIndexSunMatSparseMissingIndices) { // Sparse matrix without any indices set SUNMatrixWrapper mat = SUNMatrixWrapper(2, 3, 2, CSC_MAT); - EXPECT_EQ(unravel_index(0, mat.get()), std::make_pair((sunindextype) -1, (sunindextype) -1)); - EXPECT_EQ(unravel_index(1, mat.get()), std::make_pair((sunindextype) -1, (sunindextype) -1)); + EXPECT_EQ(unravel_index(0, mat), std::make_pair((sunindextype) -1, (sunindextype) -1)); + EXPECT_EQ(unravel_index(1, mat), std::make_pair((sunindextype) -1, (sunindextype) -1)); } From bd915ad5d9f317e586260dc1868b9a7de66c239c Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Mon, 26 Feb 2024 14:16:42 +0100 Subject: [PATCH 035/188] Cache remote files for tests (#2313) Cache remote files for tests locally via pooch, and cache the pooch cache for GitHub actions. Today, I had dozens of workflow failures due to rate-limiting or unavailability of biomodels. This is avoidable. --- .github/workflows/test_python_cplusplus.yml | 14 ++++++++++ python/sdist/setup.cfg | 1 + .../test_conserved_quantities_demartino.py | 27 +++++++++---------- python/tests/test_sbml_import.py | 13 +++++---- 4 files changed, 34 insertions(+), 21 deletions(-) diff --git a/.github/workflows/test_python_cplusplus.yml b/.github/workflows/test_python_cplusplus.yml index fb90476eb8..58dd5480e5 100644 --- a/.github/workflows/test_python_cplusplus.yml +++ b/.github/workflows/test_python_cplusplus.yml @@ -18,6 +18,13 @@ jobs: python-version: [ "3.9" ] steps: + - name: Cache + uses: actions/cache@v3 + with: + path: | + ~/.cache/pooch + key: ${{ runner.os }}-py${{ matrix.python-version }}-${{ github.job }} + - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: @@ -265,6 +272,13 @@ jobs: runs-on: macos-latest steps: + - name: Cache + uses: actions/cache@v3 + with: + path: | + ~/Library/Caches/pooch + key: ${{ runner.os }}-py${{ matrix.python-version }}-${{ github.job }} + - name: Set up Python uses: actions/setup-python@v5 with: diff --git a/python/sdist/setup.cfg b/python/sdist/setup.cfg index 009d622a6b..0d27a0918e 100644 --- a/python/sdist/setup.cfg +++ b/python/sdist/setup.cfg @@ -62,6 +62,7 @@ test = # unsupported x86_64 / x86_64h antimony!=2.14; platform_system=='Darwin' and platform_machine in 'x86_64h' scipy + pooch vis = matplotlib seaborn diff --git a/python/tests/test_conserved_quantities_demartino.py b/python/tests/test_conserved_quantities_demartino.py index 339743cc4e..ca40946db3 100644 --- a/python/tests/test_conserved_quantities_demartino.py +++ b/python/tests/test_conserved_quantities_demartino.py @@ -155,15 +155,15 @@ def data_demartino2014(): """Get tests from DeMartino2014 Suppl. Material""" import gzip - import io - import urllib.request + import pooch # stoichiometric matrix - response = urllib.request.urlopen( - r"https://github.com/AMICI-dev/AMICI/files/11430971/DeMartinoDe2014_test-ecoli.dat.gz", - timeout=10, + data = gzip.GzipFile( + pooch.retrieve( + "https://github.com/AMICI-dev/AMICI/files/11430971/DeMartinoDe2014_test-ecoli.dat.gz", + known_hash="md5:899873f8f1c413d13c3f8e94c1496b7e", + ) ) - data = gzip.GzipFile(fileobj=io.BytesIO(response.read())) S = [ int(item) for sl in [ @@ -174,14 +174,13 @@ def data_demartino2014(): ] # metabolite / row names - response = urllib.request.urlopen( - r"https://github.com/AMICI-dev/AMICI/files/11430970/test-ecoli-met.txt", - timeout=10, - ) - row_names = [ - entry.decode("ascii").strip() for entry in io.BytesIO(response.read()) - ] - + with open( + pooch.retrieve( + "https://github.com/AMICI-dev/AMICI/files/11430970/test-ecoli-met.txt", + known_hash="md5:d71e711a3655311390b38d00dcd6aa7f", + ) + ) as f: + row_names = [entry.strip() for entry in f.readlines()] return S, row_names diff --git a/python/tests/test_sbml_import.py b/python/tests/test_sbml_import.py index aa343dfcc3..74a51d020a 100644 --- a/python/tests/test_sbml_import.py +++ b/python/tests/test_sbml_import.py @@ -3,7 +3,6 @@ import re from numbers import Number from pathlib import Path -from urllib.request import urlopen import amici import libsbml @@ -543,13 +542,13 @@ def test_sympy_exp_monkeypatch(): monkeypatching sympy.Pow._eval_derivative in order to be able to compute non-nan sensitivities """ - url = ( - "https://www.ebi.ac.uk/biomodels/model/download/BIOMD0000000529.2?" - "filename=BIOMD0000000529_url.xml" - ) - importer = amici.SbmlImporter( - urlopen(url, timeout=20).read().decode("utf-8"), from_file=False + import pooch + + model_file = pooch.retrieve( + url="https://www.ebi.ac.uk/biomodels/model/download/BIOMD0000000529.2?filename=BIOMD0000000529_url.xml", + known_hash="md5:c6e0b298397485b93d7acfab80b21fd4", ) + importer = amici.SbmlImporter(model_file) module_name = "BIOMD0000000529" with TemporaryDirectory() as outdir: From 780d7670eeb3c6aa02241171e6376ec61a69bac5 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Mon, 26 Feb 2024 19:56:22 +0100 Subject: [PATCH 036/188] CMake: update BLAS detection (#2318) * Search for 64bit BLAS if supported by CMake. * On apple, try accelerate if nothing else was specified. Use the new interface (https://developer.apple.com/documentation/accelerate/blas). Fixes #2286. --- CMakeLists.txt | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 032a9fb084..324426f2a7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -169,9 +169,35 @@ if(${BLAS} STREQUAL "MKL" OR DEFINED ENV{MKLROOT}) -lmkl CACHE STRING "") endif() -elseif(NOT DEFINED ENV{BLAS_LIBS} AND NOT DEFINED ENV{BLAS_CFLAGS}) +elseif( + NOT DEFINED ENV{BLAS_LIBS} + AND NOT DEFINED ENV{BLAS_CFLAGS} + AND NOT BLAS_FOUND) # if nothing is specified via environment variables, let's try FindBLAS - find_package(BLAS) + if($(CMAKE_VERSION) VERSION_GREATER_EQUAL 3.22) + set(BLA_SIZEOF_INTEGER 8) + endif() + + if(APPLE AND (NOT DEFINED BLA_VENDOR OR BLA_VENDOR STREQUAL "All")) + set(BLA_VENDOR Apple) + find_package(BLAS) + if(BLAS_FOUND) + set_property( + TARGET BLAS::BLAS + APPEND + PROPERTY INTERFACE_COMPILE_DEFINITIONS ACCELERATE_NEW_LAPACK) + set_property( + TARGET BLAS::BLAS + APPEND + PROPERTY INTERFACE_COMPILE_DEFINITIONS ACCELERATE_LAPACK_ILP64) + else() + set(BLA_VENDOR "All") + endif() + + endif() + if(NOT BLAS_FOUND) + find_package(BLAS) + endif() if(NOT BLAS_FOUND) # Nothing specified by the user and FindBLAS didn't find anything; let's try # if cblas is available on the system paths. From 4b1e73bfca29cbe6ba8a04fe921ab8aab981535a Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Mon, 26 Feb 2024 20:45:58 +0100 Subject: [PATCH 037/188] Refactor de_export.py, extract model compilation (#2315) Move model compilation to a free function in a separate module. Easier to test and more reusable for recompilation after initial import. Related to #2306 --- python/sdist/amici/compile.py | 80 +++++++++++++++++++++++++++++++++ python/sdist/amici/de_export.py | 73 +++--------------------------- 2 files changed, 87 insertions(+), 66 deletions(-) create mode 100644 python/sdist/amici/compile.py diff --git a/python/sdist/amici/compile.py b/python/sdist/amici/compile.py new file mode 100644 index 0000000000..6c4a336afc --- /dev/null +++ b/python/sdist/amici/compile.py @@ -0,0 +1,80 @@ +""" +Functionality for building the C++ extensions of an amici-created model +package. +""" +import subprocess +import sys +from typing import Optional, Union +from pathlib import Path +import os + + +def build_model_extension( + package_dir: Union[str, Path], + verbose: Optional[Union[bool, int]] = False, + compiler: Optional[str] = None, + extra_msg: Optional[str] = None, +) -> None: + """ + Compile the model extension of an amici-created model package. + + :param package_dir: + Directory of the model package to be compiled. I.e., the directory + containing the `setup.py` file. + + :param verbose: + Make model compilation verbose. + + :param compiler: + Absolute path to the compiler executable to be used to build the Python + extension, e.g. ``/usr/bin/clang``. + + :param extra_msg: + Additional message to be printed in case of a failed build. + """ + # setup.py assumes it is run from within the model directory + package_dir = Path(package_dir) + script_args = [sys.executable, package_dir / "setup.py"] + + if verbose: + script_args.append("--verbose") + else: + script_args.append("--quiet") + + script_args.extend( + [ + "build_ext", + f"--build-lib={package_dir}", + # This is generally not required, but helps to reduce the path + # length of intermediate build files, that may easily become + # problematic on Windows, due to its ridiculous 255-character path + # length limit. + f'--build-temp={package_dir / "build"}', + ] + ) + + env = os.environ.copy() + if compiler is not None: + # CMake will use the compiler specified in the CXX environment variable + env["CXX"] = compiler + + # distutils.core.run_setup looks nicer, but does not let us check the + # result easily + try: + result = subprocess.run( + script_args, + cwd=str(package_dir), + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + check=True, + env=env, + ) + except subprocess.CalledProcessError as e: + print(e.output.decode("utf-8")) + print("Failed building the model extension.") + if extra_msg: + print(f"Note: {extra_msg}") + raise + + if verbose: + print(result.stdout.decode("utf-8")) diff --git a/python/sdist/amici/de_export.py b/python/sdist/amici/de_export.py index 513a7c0e63..37958edf97 100644 --- a/python/sdist/amici/de_export.py +++ b/python/sdist/amici/de_export.py @@ -15,8 +15,6 @@ import os import re import shutil -import subprocess -import sys from dataclasses import dataclass from itertools import chain from pathlib import Path @@ -61,6 +59,7 @@ _default_simplify, ) from .logging import get_logger, log_execution_time, set_log_level +from .compile import build_model_extension from .sympy_utils import ( _custom_pow_eval_derivative, _monkeypatched, @@ -2893,7 +2892,12 @@ def compile_model(self) -> None: """ Compiles the generated code it into a simulatable module """ - self._compile_c_code(compiler=self.compiler, verbose=self.verbose) + build_model_extension( + package_dir=self.model_path, + compiler=self.compiler, + verbose=self.verbose, + extra_msg="\n".join(self._build_hints), + ) def _prepare_model_folder(self) -> None: """ @@ -2950,69 +2954,6 @@ def _generate_c_code(self) -> None: CXX_MAIN_TEMPLATE_FILE, os.path.join(self.model_path, "main.cpp") ) - def _compile_c_code( - self, - verbose: Optional[Union[bool, int]] = False, - compiler: Optional[str] = None, - ) -> None: - """ - Compile the generated model code - - :param verbose: - Make model compilation verbose - - :param compiler: - Absolute path to the compiler executable to be used to build the Python - extension, e.g. ``/usr/bin/clang``. - """ - # setup.py assumes it is run from within the model directory - module_dir = self.model_path - script_args = [sys.executable, os.path.join(module_dir, "setup.py")] - - if verbose: - script_args.append("--verbose") - else: - script_args.append("--quiet") - - script_args.extend( - [ - "build_ext", - f"--build-lib={module_dir}", - # This is generally not required, but helps to reduce the path - # length of intermediate build files, that may easily become - # problematic on Windows, due to its ridiculous 255-character path - # length limit. - f'--build-temp={Path(module_dir, "build")}', - ] - ) - - env = os.environ.copy() - if compiler is not None: - # CMake will use the compiler specified in the CXX environment variable - env["CXX"] = compiler - - # distutils.core.run_setup looks nicer, but does not let us check the - # result easily - try: - result = subprocess.run( - script_args, - cwd=module_dir, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - check=True, - env=env, - ) - except subprocess.CalledProcessError as e: - print(e.output.decode("utf-8")) - print("Failed building the model extension.") - if self._build_hints: - print("Note:") - print("\n".join(self._build_hints)) - raise - - if verbose: - print(result.stdout.decode("utf-8")) - def _generate_m_code(self) -> None: """ Create a Matlab script for compiling code files to a mex file From e679e51f6ad29b8a1b8b7f5f45a9a27c46ee3d93 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Mon, 26 Feb 2024 21:14:39 +0100 Subject: [PATCH 038/188] Upgrade to SuiteSparse 7.6 (#2316) * Upgrade to SuiteSparse 7.6 * Update build scripts Closes #2302 --- ThirdParty/SuiteSparse/.gitignore | 4 + ThirdParty/SuiteSparse/AMD/CMakeLists.txt | 257 ++- ThirdParty/SuiteSparse/AMD/Config/AMD.pc.in | 17 + .../SuiteSparse/AMD/Config/AMDConfig.cmake.in | 152 ++ ThirdParty/SuiteSparse/AMD/Config/amd.h.in | 24 +- .../SuiteSparse/AMD/Doc/AMD_UserGuide.tex | 18 +- ThirdParty/SuiteSparse/AMD/Doc/ChangeLog | 22 + .../SuiteSparse/AMD/Doc/amd_version.tex | 2 +- ThirdParty/SuiteSparse/AMD/Include/amd.h | 30 +- .../SuiteSparse/AMD/Include/amd_internal.h | 9 +- ThirdParty/SuiteSparse/AMD/Makefile | 34 +- ThirdParty/SuiteSparse/AMD/Source/amd_order.c | 17 +- .../SuiteSparse/AMD/Source/amd_version.c | 19 + .../AMD/cmake_modules/FindAMD.cmake | 129 -- ThirdParty/SuiteSparse/BTF/CMakeLists.txt | 208 ++- ThirdParty/SuiteSparse/BTF/Config/BTF.pc.in | 16 + .../SuiteSparse/BTF/Config/BTFConfig.cmake.in | 152 ++ ThirdParty/SuiteSparse/BTF/Config/btf.h.in | 29 +- ThirdParty/SuiteSparse/BTF/Doc/ChangeLog | 21 + ThirdParty/SuiteSparse/BTF/Include/btf.h | 35 +- .../SuiteSparse/BTF/Include/btf_internal.h | 2 +- ThirdParty/SuiteSparse/BTF/Makefile | 8 +- .../SuiteSparse/BTF/Source/btf_version.c | 19 + .../BTF/cmake_modules/FindBTF.cmake | 129 -- ThirdParty/SuiteSparse/CMakeLists.txt | 540 ++++++ ThirdParty/SuiteSparse/COLAMD/CMakeLists.txt | 222 ++- .../SuiteSparse/COLAMD/Config/COLAMD.pc.in | 17 + .../COLAMD/Config/COLAMDConfig.cmake.in | 152 ++ .../SuiteSparse/COLAMD/Config/colamd.h.in | 27 +- ThirdParty/SuiteSparse/COLAMD/Doc/ChangeLog | 25 + .../SuiteSparse/COLAMD/Include/colamd.h | 33 +- ThirdParty/SuiteSparse/COLAMD/Makefile | 20 +- ThirdParty/SuiteSparse/COLAMD/Source/colamd.c | 1 - .../COLAMD/Source/colamd_version.c | 19 + .../COLAMD/cmake_modules/FindCOLAMD.cmake | 129 -- ThirdParty/SuiteSparse/ChangeLog | 176 +- ThirdParty/SuiteSparse/KLU/CMakeLists.txt | 555 ++++-- ThirdParty/SuiteSparse/KLU/Config/KLU.pc.in | 17 + .../SuiteSparse/KLU/Config/KLUConfig.cmake.in | 193 ++ ThirdParty/SuiteSparse/KLU/Config/klu.h.in | 46 +- ThirdParty/SuiteSparse/KLU/Doc/ChangeLog | 30 + .../SuiteSparse/KLU/Doc/KLU_UserGuide.tex | 2 +- .../SuiteSparse/KLU/Doc/klu_version.tex | 2 +- ThirdParty/SuiteSparse/KLU/Include/klu.h | 52 +- .../SuiteSparse/KLU/Include/klu_internal.h | 2 +- .../SuiteSparse/KLU/Include/klu_version.h | 2 +- ThirdParty/SuiteSparse/KLU/Makefile | 10 +- .../SuiteSparse/KLU/Source/klu_version.c | 19 + .../KLU/cmake_modules/FindKLU.cmake | 129 -- .../KLU/cmake_modules/FindKLU_CHOLMOD.cmake | 137 -- ThirdParty/SuiteSparse/LICENSE.txt | 170 +- ThirdParty/SuiteSparse/README.md | 1545 +++++++++++------ .../SuiteSparse_config/CMakeLists.txt | 308 +++- .../SuiteSparse_config/Config/README.md.in | 1543 ++++++++++------ .../Config/SuiteSparse_config.h.in | 1006 ++++++++++- .../Config/SuiteSparse_config.pc.in | 16 + .../Config/SuiteSparse_configConfig.cmake.in | 171 ++ .../SuiteSparse/SuiteSparse_config/Makefile | 14 +- .../SuiteSparse/SuiteSparse_config/README.txt | 2 +- .../SuiteSparse_config/SuiteSparse_config.c | 9 +- .../SuiteSparse_config/SuiteSparse_config.h | 1012 ++++++++++- .../FindSuiteSparse_config.cmake | 141 -- .../cmake_modules/SuiteSparseBLAS.cmake | 65 +- .../cmake_modules/SuiteSparseBLAS64.cmake | 6 +- .../cmake_modules/SuiteSparseLAPACK.cmake | 15 +- .../cmake_modules/SuiteSparsePolicy.cmake | 318 ++-- .../cmake_modules/SuiteSparseReport.cmake | 29 +- .../cmake_modules/SuiteSparse__thread.cmake | 72 + .../cmake_modules/SuiteSparse_ssize_t.cmake | 32 - ThirdParty/SuiteSparse/build/.gitignore | 4 + ThirdParty/SuiteSparse/lib/.gitignore | 4 + cmake/AmiciConfig.cmake | 7 + python/sdist/setup.py | 26 +- scripts/buildSuiteSparse.sh | 13 +- scripts/buildSundials.sh | 2 +- 75 files changed, 7690 insertions(+), 2750 deletions(-) create mode 100644 ThirdParty/SuiteSparse/AMD/Config/AMD.pc.in create mode 100644 ThirdParty/SuiteSparse/AMD/Config/AMDConfig.cmake.in create mode 100644 ThirdParty/SuiteSparse/AMD/Source/amd_version.c delete mode 100644 ThirdParty/SuiteSparse/AMD/cmake_modules/FindAMD.cmake create mode 100644 ThirdParty/SuiteSparse/BTF/Config/BTF.pc.in create mode 100644 ThirdParty/SuiteSparse/BTF/Config/BTFConfig.cmake.in create mode 100644 ThirdParty/SuiteSparse/BTF/Source/btf_version.c delete mode 100644 ThirdParty/SuiteSparse/BTF/cmake_modules/FindBTF.cmake create mode 100644 ThirdParty/SuiteSparse/CMakeLists.txt create mode 100644 ThirdParty/SuiteSparse/COLAMD/Config/COLAMD.pc.in create mode 100644 ThirdParty/SuiteSparse/COLAMD/Config/COLAMDConfig.cmake.in create mode 100644 ThirdParty/SuiteSparse/COLAMD/Source/colamd_version.c delete mode 100644 ThirdParty/SuiteSparse/COLAMD/cmake_modules/FindCOLAMD.cmake create mode 100644 ThirdParty/SuiteSparse/KLU/Config/KLU.pc.in create mode 100644 ThirdParty/SuiteSparse/KLU/Config/KLUConfig.cmake.in create mode 100644 ThirdParty/SuiteSparse/KLU/Source/klu_version.c delete mode 100644 ThirdParty/SuiteSparse/KLU/cmake_modules/FindKLU.cmake delete mode 100644 ThirdParty/SuiteSparse/KLU/cmake_modules/FindKLU_CHOLMOD.cmake create mode 100644 ThirdParty/SuiteSparse/SuiteSparse_config/Config/SuiteSparse_config.pc.in create mode 100644 ThirdParty/SuiteSparse/SuiteSparse_config/Config/SuiteSparse_configConfig.cmake.in delete mode 100644 ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/FindSuiteSparse_config.cmake create mode 100644 ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparse__thread.cmake delete mode 100644 ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparse_ssize_t.cmake create mode 100644 ThirdParty/SuiteSparse/build/.gitignore create mode 100644 ThirdParty/SuiteSparse/lib/.gitignore diff --git a/ThirdParty/SuiteSparse/.gitignore b/ThirdParty/SuiteSparse/.gitignore index d0d4223fd7..82b356ef4c 100644 --- a/ThirdParty/SuiteSparse/.gitignore +++ b/ThirdParty/SuiteSparse/.gitignore @@ -127,6 +127,7 @@ CSparse/Tcov/cov.sort CSparse/Tcov/cover.out CSparse/Tcov/covs.out CSparse/Tcov/cs_*.c +CSparse/Tcov/csparse_version.c CSparse/Tcov/cstcov_test CSparse/Tcov/*.out CSparse/Tcov/cs_demo1 @@ -138,6 +139,7 @@ CXSparse/Tcov/cov.sort CXSparse/Tcov/cover.out CXSparse/Tcov/covs.out CXSparse/Tcov/cs_*.c +CXSparse/Tcov/cxsparse_version.c CXSparse/Tcov/*.out CXSparse/Tcov/cs_demo1_ci CXSparse/Tcov/cs_demo1_cl @@ -168,7 +170,9 @@ SPQR/Tcov/gpu_results.txt SPQR/Tcov/gpuqrengine_demo SPQR/Tcov/qrdemo_gpu SPQR/Tcov/qrtest +SPQR/Tcov/qrtest32 SPQR/Tcov/qrtest_out.txt +SPQR/Tcov/qrtest_out32.txt SPQR/Tcov/troll.m SPQR/Tcov/cov.out diff --git a/ThirdParty/SuiteSparse/AMD/CMakeLists.txt b/ThirdParty/SuiteSparse/AMD/CMakeLists.txt index 4fdf61499f..3253759112 100644 --- a/ThirdParty/SuiteSparse/AMD/CMakeLists.txt +++ b/ThirdParty/SuiteSparse/AMD/CMakeLists.txt @@ -2,7 +2,7 @@ # SuiteSparse/AMD/CMakeLists.txt: cmake for AMD #------------------------------------------------------------------------------- -# Copyright (c) 1996-2022, Timothy A. Davis, Patrick Amestoy, Iain Duff. +# Copyright (c) 1996-2023, Timothy A. Davis, Patrick Amestoy, Iain Duff. # All Rights Reserved. # SPDX-License-Identifier: BSD-3-clause @@ -10,12 +10,12 @@ # get the version #------------------------------------------------------------------------------- -cmake_minimum_required ( VERSION 3.19 ) +cmake_minimum_required ( VERSION 3.22 ) -set ( AMD_DATE "Jan 17, 2023" ) -set ( AMD_VERSION_MAJOR 3 ) -set ( AMD_VERSION_MINOR 0 ) -set ( AMD_VERSION_SUB 3 ) +set ( AMD_DATE "Jan 10, 2024" ) +set ( AMD_VERSION_MAJOR 3 CACHE STRING "" FORCE ) +set ( AMD_VERSION_MINOR 3 CACHE STRING "" FORCE ) +set ( AMD_VERSION_SUB 1 CACHE STRING "" FORCE ) message ( STATUS "Building AMD version: v" ${AMD_VERSION_MAJOR}. @@ -23,41 +23,38 @@ message ( STATUS "Building AMD version: v" ${AMD_VERSION_SUB} " (" ${AMD_DATE} ")" ) #------------------------------------------------------------------------------- -# SuiteSparse policies +# define the project #------------------------------------------------------------------------------- -set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} - ${CMAKE_SOURCE_DIR}/cmake_modules - ${CMAKE_SOURCE_DIR}/../SuiteSparse_config/cmake_modules ) - -include ( SuiteSparsePolicy ) +project ( AMD + VERSION "${AMD_VERSION_MAJOR}.${AMD_VERSION_MINOR}.${AMD_VERSION_SUB}" + LANGUAGES C ) #------------------------------------------------------------------------------- -# define the project +# SuiteSparse policies #------------------------------------------------------------------------------- -if ( WIN32 ) - # disable Fortran in AMD when compiling on Windows - set ( NFORTRAN true ) -endif ( ) +set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} + ${PROJECT_SOURCE_DIR}/../SuiteSparse_config/cmake_modules ) + +include ( SuiteSparsePolicy ) -if ( NOT NFORTRAN ) +if ( SUITESPARSE_HAS_FORTRAN ) # Fortan is available and enabled - project ( amd - VERSION "${AMD_VERSION_MAJOR}.${AMD_VERSION_MINOR}.${AMD_VERSION_SUB}" - LANGUAGES C Fortran ) -else ( ) - # no Fortran compiler available; do not compile Source/*.f or Demo/*.f - project ( amd - VERSION "${AMD_VERSION_MAJOR}.${AMD_VERSION_MINOR}.${AMD_VERSION_SUB}" - LANGUAGES C ) + enable_language ( Fortran ) endif ( ) #------------------------------------------------------------------------------- # find library dependencies #------------------------------------------------------------------------------- -find_package ( SuiteSparse_config 7.0.0 REQUIRED ) +if ( NOT SUITESPARSE_ROOT_CMAKELISTS ) + find_package ( SuiteSparse_config 7.5.0 + PATHS ${CMAKE_SOURCE_DIR}/../SuiteSparse_config/build NO_DEFAULT_PATH ) + if ( NOT TARGET SuiteSparse::SuiteSparseConfig ) + find_package ( SuiteSparse_config 7.5.0 REQUIRED ) + endif ( ) +endif ( ) #------------------------------------------------------------------------------- # configure files @@ -72,59 +69,91 @@ configure_file ( "Config/amd_version.tex.in" "${PROJECT_SOURCE_DIR}/Doc/amd_vers # include directories #------------------------------------------------------------------------------- -include_directories ( Source Include ${SUITESPARSE_CONFIG_INCLUDE_DIR} ) +include_directories ( Source Include ) #------------------------------------------------------------------------------- # dynamic amd library properties #------------------------------------------------------------------------------- -if ( NOT NFORTRAN ) +if ( SUITESPARSE_HAS_FORTRAN ) file ( GLOB AMD_SOURCES "Source/*.c" "Source/*.f" ) else ( ) file ( GLOB AMD_SOURCES "Source/*.c" ) endif ( ) -add_library ( amd SHARED ${AMD_SOURCES} ) -set_target_properties ( amd PROPERTIES - VERSION ${AMD_VERSION_MAJOR}.${AMD_VERSION_MINOR}.${AMD_VERSION_SUB} - C_STANDARD_REQUIRED 11 - SOVERSION ${AMD_VERSION_MAJOR} - PUBLIC_HEADER "Include/amd.h" - WINDOWS_EXPORT_ALL_SYMBOLS ON ) +if ( BUILD_SHARED_LIBS ) + add_library ( AMD SHARED ${AMD_SOURCES} ) + set_target_properties ( AMD PROPERTIES + VERSION ${AMD_VERSION_MAJOR}.${AMD_VERSION_MINOR}.${AMD_VERSION_SUB} + C_STANDARD 11 + C_STANDARD_REQUIRED ON + OUTPUT_NAME amd + SOVERSION ${AMD_VERSION_MAJOR} + PUBLIC_HEADER "Include/amd.h" + WINDOWS_EXPORT_ALL_SYMBOLS ON ) + + if ( ${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.25" ) + set_target_properties ( AMD PROPERTIES EXPORT_NO_SYSTEM ON ) + endif ( ) + + target_include_directories ( AMD + INTERFACE $ + $ ) +endif ( ) #------------------------------------------------------------------------------- # static amd library properties #------------------------------------------------------------------------------- -if ( NOT NSTATIC ) - add_library ( amd_static STATIC ${AMD_SOURCES} ) - set_target_properties ( amd_static PROPERTIES - VERSION ${AMD_VERSION_MAJOR}.${AMD_VERSION_MINOR}.${AMD_VERSION_SUB} - C_STANDARD_REQUIRED 11 +if ( BUILD_STATIC_LIBS ) + add_library ( AMD_static STATIC ${AMD_SOURCES} ) + set_target_properties ( AMD_static PROPERTIES + C_STANDARD 11 + C_STANDARD_REQUIRED ON OUTPUT_NAME amd - SOVERSION ${AMD_VERSION_MAJOR} ) + PUBLIC_HEADER "Include/amd.h" ) if ( MSVC ) - set_target_properties ( amd_static PROPERTIES + set_target_properties ( AMD_static PROPERTIES OUTPUT_NAME amd_static ) endif ( ) + + if ( ${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.25" ) + set_target_properties ( AMD_static PROPERTIES EXPORT_NO_SYSTEM ON ) + endif ( ) + + target_include_directories ( AMD_static + INTERFACE $ + $ ) + endif ( ) #------------------------------------------------------------------------------- # add the library dependencies #------------------------------------------------------------------------------- -# suitesparseconfig: -target_link_libraries ( amd PUBLIC ${SUITESPARSE_CONFIG_LIBRARIES} ) -if ( NOT NSTATIC ) - target_link_libraries ( amd_static PUBLIC ${SUITESPARSE_CONFIG_STATIC} ) +# SuiteSparseConfig: +if ( BUILD_SHARED_LIBS ) + target_link_libraries ( AMD PRIVATE SuiteSparse::SuiteSparseConfig ) + target_include_directories ( AMD PUBLIC + "$" ) +endif ( ) +if ( BUILD_STATIC_LIBS ) + if ( TARGET SuiteSparse::SuiteSparseConfig_static ) + target_link_libraries ( AMD_static PUBLIC SuiteSparse::SuiteSparseConfig_static ) + else ( ) + target_link_libraries ( AMD_static PUBLIC SuiteSparse::SuiteSparseConfig ) + endif ( ) endif ( ) # libm: if ( NOT WIN32 ) - target_link_libraries ( amd PUBLIC m ) - if ( NOT NSTATIC ) - target_link_libraries ( amd_static PUBLIC m ) + if ( BUILD_SHARED_LIBS ) + target_link_libraries ( AMD PRIVATE m ) + endif ( ) + if ( BUILD_STATIC_LIBS ) + set ( AMD_STATIC_LIBS "${AMD_STATIC_LIBS} -lm" ) + target_link_libraries ( AMD_static PUBLIC m ) endif ( ) endif ( ) @@ -132,25 +161,99 @@ endif ( ) # AMD installation location #------------------------------------------------------------------------------- -install ( TARGETS amd - LIBRARY DESTINATION ${SUITESPARSE_LIBDIR} - ARCHIVE DESTINATION ${SUITESPARSE_LIBDIR} - RUNTIME DESTINATION ${SUITESPARSE_BINDIR} - PUBLIC_HEADER DESTINATION ${SUITESPARSE_INCLUDEDIR} ) -install ( FILES ${CMAKE_SOURCE_DIR}/cmake_modules/FindAMD.cmake - DESTINATION ${SUITESPARSE_LIBDIR}/cmake/SuiteSparse - COMPONENT Development ) -if ( NOT NSTATIC ) - install ( TARGETS amd_static - ARCHIVE DESTINATION ${SUITESPARSE_LIBDIR} ) +include ( CMakePackageConfigHelpers ) + +if ( BUILD_SHARED_LIBS ) + install ( TARGETS AMD + EXPORT AMDTargets + LIBRARY DESTINATION ${SUITESPARSE_LIBDIR} + ARCHIVE DESTINATION ${SUITESPARSE_LIBDIR} + RUNTIME DESTINATION ${SUITESPARSE_BINDIR} + PUBLIC_HEADER DESTINATION ${SUITESPARSE_INCLUDEDIR} ) +endif ( ) +if ( BUILD_STATIC_LIBS ) + install ( TARGETS AMD_static + EXPORT AMDTargets + ARCHIVE DESTINATION ${SUITESPARSE_LIBDIR} + PUBLIC_HEADER DESTINATION ${SUITESPARSE_INCLUDEDIR} ) +endif ( ) + +# create (temporary) export target file during build +export ( EXPORT AMDTargets + NAMESPACE SuiteSparse:: + FILE ${CMAKE_CURRENT_BINARY_DIR}/AMDTargets.cmake ) + +# install export target, config and version files for find_package +install ( EXPORT AMDTargets + NAMESPACE SuiteSparse:: + DESTINATION ${SUITESPARSE_PKGFILEDIR}/cmake/AMD ) + +# generate config file to be used in common build tree +set ( SUITESPARSE_IN_BUILD_TREE ON ) +configure_package_config_file ( + Config/AMDConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/AMDConfig.cmake + INSTALL_DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/AMDConfig.cmake ) + +# generate config file to be installed +set ( SUITESPARSE_IN_BUILD_TREE OFF ) +configure_package_config_file ( + Config/AMDConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/target/AMDConfig.cmake + INSTALL_DESTINATION ${SUITESPARSE_PKGFILEDIR}/cmake/AMD ) + +write_basic_package_version_file ( + ${CMAKE_CURRENT_BINARY_DIR}/AMDConfigVersion.cmake + COMPATIBILITY SameMajorVersion ) + +install ( FILES + ${CMAKE_CURRENT_BINARY_DIR}/target/AMDConfig.cmake + ${CMAKE_CURRENT_BINARY_DIR}/AMDConfigVersion.cmake + DESTINATION ${SUITESPARSE_PKGFILEDIR}/cmake/AMD ) + +#------------------------------------------------------------------------------- +# create pkg-config file +#------------------------------------------------------------------------------- + +if ( NOT MSVC ) + set ( prefix "${CMAKE_INSTALL_PREFIX}" ) + set ( exec_prefix "\${prefix}" ) + cmake_path ( IS_ABSOLUTE SUITESPARSE_LIBDIR SUITESPARSE_LIBDIR_IS_ABSOLUTE ) + if (SUITESPARSE_LIBDIR_IS_ABSOLUTE) + set ( libdir "${SUITESPARSE_LIBDIR}") + else ( ) + set ( libdir "\${exec_prefix}/${SUITESPARSE_LIBDIR}") + endif ( ) + cmake_path ( IS_ABSOLUTE SUITESPARSE_INCLUDEDIR SUITESPARSE_INCLUDEDIR_IS_ABSOLUTE ) + if (SUITESPARSE_INCLUDEDIR_IS_ABSOLUTE) + set ( includedir "${SUITESPARSE_INCLUDEDIR}") + else ( ) + set ( includedir "\${prefix}/${SUITESPARSE_INCLUDEDIR}") + endif ( ) + if ( BUILD_SHARED_LIBS ) + set ( SUITESPARSE_LIB_BASE_NAME $ ) + else ( ) + set ( SUITESPARSE_LIB_BASE_NAME $ ) + endif ( ) + configure_file ( + Config/AMD.pc.in + AMD.pc.out + @ONLY + NEWLINE_STYLE LF ) + file ( GENERATE + OUTPUT AMD.pc + INPUT ${CMAKE_CURRENT_BINARY_DIR}/AMD.pc.out + NEWLINE_STYLE LF ) + install ( FILES + ${CMAKE_CURRENT_BINARY_DIR}/AMD.pc + DESTINATION ${SUITESPARSE_PKGFILEDIR}/pkgconfig ) endif ( ) #------------------------------------------------------------------------------- # Demo library and programs #------------------------------------------------------------------------------- -option ( DEMO "ON: Build the demo programs. OFF (default): do not build the demo programs." off ) -if ( DEMO ) +if ( SUITESPARSE_DEMOS ) #--------------------------------------------------------------------------- # demo library @@ -166,19 +269,30 @@ if ( DEMO ) add_executable ( amd_l_demo "Demo/amd_l_demo.c" ) add_executable ( amd_demo2 "Demo/amd_demo2.c" ) add_executable ( amd_simple "Demo/amd_simple.c" ) - if ( NOT NFORTRAN ) + if ( SUITESPARSE_HAS_FORTRAN ) add_executable ( amd_f77demo "Demo/amd_f77demo.f" ) add_executable ( amd_f77simple "Demo/amd_f77simple.f" ) endif ( ) # Libraries required for Demo programs - target_link_libraries ( amd_demo PUBLIC amd ) - target_link_libraries ( amd_l_demo PUBLIC amd ) - target_link_libraries ( amd_demo2 PUBLIC amd ) - target_link_libraries ( amd_simple PUBLIC amd ) - if ( NOT NFORTRAN ) - target_link_libraries ( amd_f77demo PUBLIC amd ) - target_link_libraries ( amd_f77simple PUBLIC amd ) + if ( BUILD_SHARED_LIBS ) + target_link_libraries ( amd_demo PUBLIC AMD ) + target_link_libraries ( amd_l_demo PUBLIC AMD ) + target_link_libraries ( amd_demo2 PUBLIC AMD ) + target_link_libraries ( amd_simple PUBLIC AMD ) + if ( SUITESPARSE_HAS_FORTRAN ) + target_link_libraries ( amd_f77demo PUBLIC AMD ) + target_link_libraries ( amd_f77simple PUBLIC AMD ) + endif ( ) + else ( ) + target_link_libraries ( amd_demo PUBLIC AMD_static ) + target_link_libraries ( amd_l_demo PUBLIC AMD_static ) + target_link_libraries ( amd_demo2 PUBLIC AMD_static ) + target_link_libraries ( amd_simple PUBLIC AMD_static ) + if ( SUITESPARSE_HAS_FORTRAN ) + target_link_libraries ( amd_f77demo PUBLIC AMD_static ) + target_link_libraries ( amd_f77simple PUBLIC AMD_static ) + endif ( ) endif ( ) else ( ) @@ -192,4 +306,3 @@ endif ( ) #------------------------------------------------------------------------------- include ( SuiteSparseReport ) - diff --git a/ThirdParty/SuiteSparse/AMD/Config/AMD.pc.in b/ThirdParty/SuiteSparse/AMD/Config/AMD.pc.in new file mode 100644 index 0000000000..e5b509861f --- /dev/null +++ b/ThirdParty/SuiteSparse/AMD/Config/AMD.pc.in @@ -0,0 +1,17 @@ +# AMD, Copyright (c) 1996-2023, Timothy A. Davis. +# All Rights Reserved. +# SPDX-License-Identifier: BSD-3-Clause + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: AMD +URL: https://github.com/DrTimothyAldenDavis/SuiteSparse +Description: Routines for permuting sparse matrices prior to factorization in SuiteSparse +Version: @AMD_VERSION_MAJOR@.@AMD_VERSION_MINOR@.@AMD_VERSION_SUB@ +Requires.private: SuiteSparse_config +Libs: -L${libdir} -l@SUITESPARSE_LIB_BASE_NAME@ +Libs.private: @AMD_STATIC_LIBS@ +Cflags: -I${includedir} diff --git a/ThirdParty/SuiteSparse/AMD/Config/AMDConfig.cmake.in b/ThirdParty/SuiteSparse/AMD/Config/AMDConfig.cmake.in new file mode 100644 index 0000000000..d21e050171 --- /dev/null +++ b/ThirdParty/SuiteSparse/AMD/Config/AMDConfig.cmake.in @@ -0,0 +1,152 @@ +#------------------------------------------------------------------------------- +# SuiteSparse/AMD/cmake_modules/AMDConfig.cmake +#------------------------------------------------------------------------------- + +# The following copyright and license applies to just this file only, not to +# the library itself: +# AMDConfig.cmake, Copyright (c) 2023, Timothy A. Davis. All Rights Reserved. +# SPDX-License-Identifier: BSD-3-clause + +#------------------------------------------------------------------------------- + +# Finds the AMD include file and compiled library. +# The following targets are defined: +# SuiteSparse::AMD - for the shared library (if available) +# SuiteSparse::AMD_static - for the static library (if available) + +# For backward compatibility the following variables are set: + +# AMD_INCLUDE_DIR - where to find amd.h +# AMD_LIBRARY - dynamic AMD library +# AMD_STATIC - static AMD library +# AMD_LIBRARIES - libraries when using AMD +# AMD_FOUND - true if AMD found + +# Set ``CMAKE_MODULE_PATH`` to the parent folder where this module file is +# installed. + +#------------------------------------------------------------------------------- + +@PACKAGE_INIT@ + +set ( AMD_DATE "@AMD_DATE@" ) +set ( AMD_VERSION_MAJOR @AMD_VERSION_MAJOR@ ) +set ( AMD_VERSION_MINOR @AMD_VERSION_MINOR@ ) +set ( AMD_VERSION_PATCH @AMD_VERSION_SUB@ ) +set ( AMD_VERSION "@AMD_VERSION_MAJOR@.@AMD_VERSION_MINOR@.@AMD_VERSION_SUB@" ) + +# Check for dependent targets +include ( CMakeFindDependencyMacro ) + +# Look for SuiteSparse_config target +if ( @SUITESPARSE_IN_BUILD_TREE@ ) + if ( NOT TARGET SuiteSparse::SuiteSparseConfig ) + # First check in a common build tree + find_dependency ( SuiteSparse_config @SUITESPARSE_CONFIG_VERSION_MAJOR@.@SUITESPARSE_CONFIG_VERSION_MINOR@ + PATHS ${CMAKE_SOURCE_DIR}/../SuiteSparse_config/build NO_DEFAULT_PATH ) + # Then, check in the currently active CMAKE_MODULE_PATH + if ( NOT SuiteSparse_config_FOUND ) + find_dependency ( SuiteSparse_config @SUITESPARSE_CONFIG_VERSION_MAJOR@.@SUITESPARSE_CONFIG_VERSION_MINOR@ ) + endif ( ) + endif ( ) +else ( ) + if ( NOT TARGET SuiteSparse::SuiteSparseConfig ) + find_dependency ( SuiteSparse_config @SUITESPARSE_CONFIG_VERSION_MAJOR@.@SUITESPARSE_CONFIG_VERSION_MINOR@ ) + endif ( ) +endif ( ) +if ( NOT SuiteSparse_config_FOUND ) + set ( AMD_FOUND OFF ) + return ( ) +endif ( ) + + +# Import target +include ( ${CMAKE_CURRENT_LIST_DIR}/AMDTargets.cmake ) + +# The following is only for backward compatibility with FindAMD. + +set ( _target_shared SuiteSparse::AMD ) +set ( _target_static SuiteSparse::AMD_static ) +set ( _var_prefix "AMD" ) + +if ( NOT @BUILD_SHARED_LIBS@ AND NOT TARGET ${_target_shared} ) + # make sure there is always an import target without suffix ) + add_library ( ${_target_shared} ALIAS ${_target_static} ) +endif ( ) + +get_target_property ( ${_var_prefix}_INCLUDE_DIR ${_target_shared} INTERFACE_INCLUDE_DIRECTORIES ) +if ( ${_var_prefix}_INCLUDE_DIR ) + # First item in SuiteSparse targets contains the "main" header directory. + list ( GET ${_var_prefix}_INCLUDE_DIR 0 ${_var_prefix}_INCLUDE_DIR ) +endif ( ) +get_target_property ( ${_var_prefix}_LIBRARY ${_target_shared} IMPORTED_IMPLIB ) +if ( NOT ${_var_prefix}_LIBRARY ) + get_target_property ( _library_chk ${_target_shared} IMPORTED_LOCATION ) + if ( EXISTS ${_library_chk} ) + set ( ${_var_prefix}_LIBRARY ${_library_chk} ) + endif ( ) +endif ( ) +if ( TARGET ${_target_static} ) + get_target_property ( ${_var_prefix}_STATIC ${_target_static} IMPORTED_LOCATION ) +endif ( ) + +# Check for most common build types +set ( _config_types "Debug" "Release" "RelWithDebInfo" "MinSizeRel" "None" ) + +get_property ( _isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG ) +if ( _isMultiConfig ) + # For multi-configuration generators (e.g., Visual Studio), prefer those + # configurations. + list ( PREPEND _config_types ${CMAKE_CONFIGURATION_TYPES} ) +else ( ) + # For single-configuration generators, prefer the current configuration. + list ( PREPEND _config_types ${CMAKE_BUILD_TYPE} ) +endif ( ) + +list ( REMOVE_DUPLICATES _config_types ) + +foreach ( _config ${_config_types} ) + string ( TOUPPER ${_config} _uc_config ) + if ( NOT ${_var_prefix}_LIBRARY ) + get_target_property ( _library_chk ${_target_shared} + IMPORTED_IMPLIB_${_uc_config} ) + if ( EXISTS ${_library_chk} ) + set ( ${_var_prefix}_LIBRARY ${_library_chk} ) + endif ( ) + endif ( ) + if ( NOT ${_var_prefix}_LIBRARY ) + get_target_property ( _library_chk ${_target_shared} + IMPORTED_LOCATION_${_uc_config} ) + if ( EXISTS ${_library_chk} ) + set ( ${_var_prefix}_LIBRARY ${_library_chk} ) + endif ( ) + endif ( ) + if ( TARGET ${_target_static} AND NOT ${_var_prefix}_STATIC ) + get_target_property ( _library_chk ${_target_static} + IMPORTED_LOCATION_${_uc_config} ) + if ( EXISTS ${_library_chk} ) + set ( ${_var_prefix}_STATIC ${_library_chk} ) + endif ( ) + endif ( ) +endforeach ( ) + +set ( AMD_LIBRARIES ${AMD_LIBRARY} ) + +macro ( suitesparse_check_exist _var _files ) + # ignore generator expressions + string ( GENEX_STRIP "${_files}" _files2 ) + + foreach ( _file ${_files2} ) + if ( NOT EXISTS "${_file}" ) + message ( FATAL_ERROR "File or directory ${_file} referenced by variable ${_var} does not exist!" ) + endif ( ) + endforeach () +endmacro ( ) + +suitesparse_check_exist ( AMD_INCLUDE_DIR ${AMD_INCLUDE_DIR} ) +suitesparse_check_exist ( AMD_LIBRARY ${AMD_LIBRARY} ) + +message ( STATUS "AMD version: ${AMD_VERSION}" ) +message ( STATUS "AMD include: ${AMD_INCLUDE_DIR}") +message ( STATUS "AMD library: ${AMD_LIBRARY}") +message ( STATUS "AMD static: ${AMD_STATIC}") diff --git a/ThirdParty/SuiteSparse/AMD/Config/amd.h.in b/ThirdParty/SuiteSparse/AMD/Config/amd.h.in index f2a6916654..3192e11391 100644 --- a/ThirdParty/SuiteSparse/AMD/Config/amd.h.in +++ b/ThirdParty/SuiteSparse/AMD/Config/amd.h.in @@ -2,7 +2,7 @@ // AMD/Include/amd.h: approximate minimum degree ordering //------------------------------------------------------------------------------ -// AMD, Copyright (c) 1996-2022, Timothy A. Davis, Patrick R. Amestoy, and +// AMD, Copyright (c) 1996-2024, Timothy A. Davis, Patrick R. Amestoy, and // Iain S. Duff. All Rights Reserved. // SPDX-License-Identifier: BSD-3-clause @@ -35,13 +35,13 @@ #ifndef AMD_H #define AMD_H +#include "SuiteSparse_config.h" + /* make it easy for C++ programs to include AMD */ #ifdef __cplusplus extern "C" { #endif -#include "SuiteSparse_config.h" - int amd_order /* returns AMD_OK, AMD_OK_BUT_JUMBLED, * AMD_INVALID, or AMD_OUT_OF_MEMORY */ ( @@ -313,6 +313,14 @@ void amd_l_control (double Control [ ]) ; void amd_info (double Info [ ]) ; void amd_l_info (double Info [ ]) ; +// amd_version: return AMD version. The version array is returned with +// version [0..2] = {AMD_MAIN_VERSION, AMD_SUB_VERSION, AMD_SUBSUB_VERSION} +void amd_version (int version [3]) ; + +#ifdef __cplusplus +} +#endif + #define AMD_CONTROL 5 /* size of Control array */ #define AMD_INFO 20 /* size of Info array */ @@ -379,11 +387,13 @@ void amd_l_info (double Info [ ]) ; #define AMD_SUB_VERSION @AMD_VERSION_MINOR@ #define AMD_SUBSUB_VERSION @AMD_VERSION_SUB@ -#define AMD_VERSION_CODE(main,sub) ((main) * 1000 + (sub)) -#define AMD_VERSION AMD_VERSION_CODE(AMD_MAIN_VERSION,AMD_SUB_VERSION) +#define AMD_VERSION_CODE(main,sub) SUITESPARSE_VER_CODE(main,sub) +#define AMD_VERSION AMD_VERSION_CODE(@AMD_VERSION_MAJOR@,@AMD_VERSION_MINOR@) -#ifdef __cplusplus -} +#define AMD__VERSION SUITESPARSE__VERCODE(@AMD_VERSION_MAJOR@,@AMD_VERSION_MINOR@,@AMD_VERSION_SUB@) +#if !defined (SUITESPARSE__VERSION) || \ + (SUITESPARSE__VERSION < SUITESPARSE__VERCODE(7,5,0)) +#error "AMD @AMD_VERSION_MAJOR@.@AMD_VERSION_MINOR@.@AMD_VERSION_SUB@ requires SuiteSparse_config 7.5.0 or later" #endif #endif diff --git a/ThirdParty/SuiteSparse/AMD/Doc/AMD_UserGuide.tex b/ThirdParty/SuiteSparse/AMD/Doc/AMD_UserGuide.tex index 2e5e1edef3..794a29cde7 100644 --- a/ThirdParty/SuiteSparse/AMD/Doc/AMD_UserGuide.tex +++ b/ThirdParty/SuiteSparse/AMD/Doc/AMD_UserGuide.tex @@ -46,7 +46,7 @@ \end{abstract} %------------------------------------------------------------------------------ -AMD Copyright\copyright 1996-2022 by Timothy A. +AMD Copyright\copyright 1996-2023 by Timothy A. Davis, Patrick R. Amestoy, and Iain S. Duff. All Rights Reserved. AMD is available under alternate licences; contact T. Davis for details. @@ -202,8 +202,8 @@ \section{Using AMD in a C program} \label{Cversion} %------------------------------------------------------------------------------ -The C-callable AMD library consists of seven user-callable routines and one -include file. There are two versions of each of the routines, with +The C-callable AMD library consists of eight user-callable routines and one +include file. There are two versions of seven of the routines, with \verb'int32_t' and \verb'int64_t' integers. The routines with prefix {\tt amd\_l\_} use \verb'int64_t' integer arguments; the others use @@ -303,6 +303,8 @@ \section{Using AMD in a C program} but it destroys the matrix on output. Additional workspace must be passed. Refer to the source file {\tt AMD/Source/amd\_2.c} for a description. +\item \verb'amd_version': returns the AMD version. + \end{itemize} The nonzero pattern of the matrix $\m{A}$ is represented in compressed column @@ -480,6 +482,16 @@ \section{Synopsis of C-callable routines} \end{verbatim} } +The \verb'amd_version' function uses plain \verb'int': + +{\footnotesize +\begin{verbatim} +#include "amd.h" +int version [3] ; +amd_version (version) ; +\end{verbatim} +} + %------------------------------------------------------------------------------ \section{Using AMD in a Fortran program} %------------------------------------------------------------------------------ diff --git a/ThirdParty/SuiteSparse/AMD/Doc/ChangeLog b/ThirdParty/SuiteSparse/AMD/Doc/ChangeLog index 97dbc0d871..3828f5ab22 100644 --- a/ThirdParty/SuiteSparse/AMD/Doc/ChangeLog +++ b/ThirdParty/SuiteSparse/AMD/Doc/ChangeLog @@ -1,3 +1,25 @@ +Jan 10, 2024: version 3.3.1 + + * minor updates to build system + +Dec 30, 2023: version 3.3.0 + + * major change to build system: by Markus Mützel + * revised test for integer overflow: for CHOLMOD 5.1.0 tests + * amd_version: added to return version of AMD + +Sept 18, 2023: version 3.2.1 + + * cmake update: add "None" build type, from Antonio Rojas, for Arch Linux + +Sept 8, 2023: version 3.2.0 + + * cmake updates: SuiteSparse:: namespace by Markus Muetzel + +June 16, 2023: version 3.0.4 + + * cmake build system updates: update by Markus Muetzel + Jan 17, 2023: version 3.0.3 * NFORTRAN: option added to disable Fortran entirely diff --git a/ThirdParty/SuiteSparse/AMD/Doc/amd_version.tex b/ThirdParty/SuiteSparse/AMD/Doc/amd_version.tex index e24c8cda6a..31528c9f32 100644 --- a/ThirdParty/SuiteSparse/AMD/Doc/amd_version.tex +++ b/ThirdParty/SuiteSparse/AMD/Doc/amd_version.tex @@ -1,2 +1,2 @@ % version of SuiteSparse/AMD -\date{VERSION 3.0.3, Jan 17, 2023} +\date{VERSION 3.3.1, Jan 10, 2024} diff --git a/ThirdParty/SuiteSparse/AMD/Include/amd.h b/ThirdParty/SuiteSparse/AMD/Include/amd.h index 94aa96f113..188ee5a67f 100644 --- a/ThirdParty/SuiteSparse/AMD/Include/amd.h +++ b/ThirdParty/SuiteSparse/AMD/Include/amd.h @@ -2,7 +2,7 @@ // AMD/Include/amd.h: approximate minimum degree ordering //------------------------------------------------------------------------------ -// AMD, Copyright (c) 1996-2022, Timothy A. Davis, Patrick R. Amestoy, and +// AMD, Copyright (c) 1996-2024, Timothy A. Davis, Patrick R. Amestoy, and // Iain S. Duff. All Rights Reserved. // SPDX-License-Identifier: BSD-3-clause @@ -35,13 +35,13 @@ #ifndef AMD_H #define AMD_H +#include "SuiteSparse_config.h" + /* make it easy for C++ programs to include AMD */ #ifdef __cplusplus extern "C" { #endif -#include "SuiteSparse_config.h" - int amd_order /* returns AMD_OK, AMD_OK_BUT_JUMBLED, * AMD_INVALID, or AMD_OUT_OF_MEMORY */ ( @@ -313,6 +313,14 @@ void amd_l_control (double Control [ ]) ; void amd_info (double Info [ ]) ; void amd_l_info (double Info [ ]) ; +// amd_version: return AMD version. The version array is returned with +// version [0..2] = {AMD_MAIN_VERSION, AMD_SUB_VERSION, AMD_SUBSUB_VERSION} +void amd_version (int version [3]) ; + +#ifdef __cplusplus +} +#endif + #define AMD_CONTROL 5 /* size of Control array */ #define AMD_INFO 20 /* size of Info array */ @@ -374,16 +382,18 @@ void amd_l_info (double Info [ ]) ; * Versions 1.1 and earlier of AMD do not include a #define'd version number. */ -#define AMD_DATE "Jan 17, 2023" +#define AMD_DATE "Jan 10, 2024" #define AMD_MAIN_VERSION 3 -#define AMD_SUB_VERSION 0 -#define AMD_SUBSUB_VERSION 3 +#define AMD_SUB_VERSION 3 +#define AMD_SUBSUB_VERSION 1 -#define AMD_VERSION_CODE(main,sub) ((main) * 1000 + (sub)) -#define AMD_VERSION AMD_VERSION_CODE(AMD_MAIN_VERSION,AMD_SUB_VERSION) +#define AMD_VERSION_CODE(main,sub) SUITESPARSE_VER_CODE(main,sub) +#define AMD_VERSION AMD_VERSION_CODE(3,3) -#ifdef __cplusplus -} +#define AMD__VERSION SUITESPARSE__VERCODE(3,3,1) +#if !defined (SUITESPARSE__VERSION) || \ + (SUITESPARSE__VERSION < SUITESPARSE__VERCODE(7,5,0)) +#error "AMD 3.3.1 requires SuiteSparse_config 7.5.0 or later" #endif #endif diff --git a/ThirdParty/SuiteSparse/AMD/Include/amd_internal.h b/ThirdParty/SuiteSparse/AMD/Include/amd_internal.h index 85d43930b6..b5649cbc94 100644 --- a/ThirdParty/SuiteSparse/AMD/Include/amd_internal.h +++ b/ThirdParty/SuiteSparse/AMD/Include/amd_internal.h @@ -2,7 +2,7 @@ // AMD/Include/amd_internal.h: internal definitions for AMD //------------------------------------------------------------------------------ -// AMD, Copyright (c) 1996-2022, Timothy A. Davis, Patrick R. Amestoy, and +// AMD, Copyright (c) 1996-2023, Timothy A. Davis, Patrick R. Amestoy, and // Iain S. Duff. All Rights Reserved. // SPDX-License-Identifier: BSD-3-clause @@ -37,12 +37,9 @@ #define NDEBUG #endif -/* - To enable debugging, uncomment the following line: -#undef NDEBUG -*/ +// To enable debugging, uncomment the following line: +// #undef NDEBUG -#define SUITESPARSE_LIBRARY #include "amd.h" /* ------------------------------------------------------------------------- */ diff --git a/ThirdParty/SuiteSparse/AMD/Makefile b/ThirdParty/SuiteSparse/AMD/Makefile index ad08cc1d1a..15da7ceab0 100644 --- a/ThirdParty/SuiteSparse/AMD/Makefile +++ b/ThirdParty/SuiteSparse/AMD/Makefile @@ -36,36 +36,36 @@ default: library # default is to install only in /usr/local library: - ( cd build && cmake $(CMAKE_OPTIONS) .. && cmake --build . -j${JOBS} ) + ( cd build && cmake $(CMAKE_OPTIONS) .. && cmake --build . --config Release -j${JOBS} ) # install only in SuiteSparse/lib and SuiteSparse/include local: - ( cd build && cmake $(CMAKE_OPTIONS) -DLOCAL_INSTALL=1 .. && cmake --build . -j${JOBS} ) + ( cd build && cmake $(CMAKE_OPTIONS) -USUITESPARSE_PKGFILEDIR -DSUITESPARSE_LOCAL_INSTALL=1 .. && cmake --build . --config Release -j${JOBS} ) # install only in /usr/local (default) global: - ( cd build && cmake $(CMAKE_OPTIONS) -DLOCAL_INSTALL=0 .. && cmake --build . -j${JOBS} ) + ( cd build && cmake $(CMAKE_OPTIONS) -USUITESPARSE_PKGFILEDIR -DSUITESPARSE_LOCAL_INSTALL=0 .. && cmake --build . --config Release -j${JOBS} ) debug: - ( cd build && cmake $(CMAKE_OPTIONS) -DCMAKE_BUILD_TYPE=Debug .. && cmake --build . -j${JOBS} ) + ( cd build && cmake $(CMAKE_OPTIONS) -DCMAKE_BUILD_TYPE=Debug .. && cmake --build . --config Debug -j${JOBS} ) all: library demos: library - ( cd build && cmake $(CMAKE_OPTIONS) -DDEMO=1 .. && cmake --build . -j${JOBS} ) - ./build/amd_demo > build/amd_demo.out - - diff --strip-trailing-cr Demo/amd_demo.out build/amd_demo.out - ./build/amd_l_demo > build/amd_l_demo.out - - diff --strip-trailing-cr Demo/amd_l_demo.out build/amd_l_demo.out - ./build/amd_demo2 > build/amd_demo2.out - - diff --strip-trailing-cr Demo/amd_demo2.out build/amd_demo2.out - ./build/amd_simple > build/amd_simple.out - - diff --strip-trailing-cr Demo/amd_simple.out build/amd_simple.out + ( cd build && cmake $(CMAKE_OPTIONS) -DSUITESPARSE_DEMOS=1 .. && cmake --build . --config Release -j${JOBS} ) + ./build/amd_demo > build/amd_demo.out && ( command -v d2u && d2u ./build/amd_demo.out || true ) + - diff Demo/amd_demo.out build/amd_demo.out + ./build/amd_l_demo > build/amd_l_demo.out && ( command -v d2u && d2u ./build/amd_l_demo.out || true ) + - diff Demo/amd_l_demo.out build/amd_l_demo.out + ./build/amd_demo2 > build/amd_demo2.out && ( command -v d2u && d2u ./build/amd_demo2.out || true ) + - diff Demo/amd_demo2.out build/amd_demo2.out + ./build/amd_simple > build/amd_simple.out && ( command -v d2u && d2u ./build/amd_simple.out || true ) + - diff Demo/amd_simple.out build/amd_simple.out # Fortran demos will fail if no Fortran compiler is available - - ./build/amd_f77simple > build/amd_f77simple.out - - diff --strip-trailing-cr Demo/amd_f77simple.out build/amd_f77simple.out - - ./build/amd_f77demo > build/amd_f77demo.out - - diff --strip-trailing-cr Demo/amd_f77demo.out build/amd_f77demo.out + - ./build/amd_f77simple > build/amd_f77simple.out && ( command -v d2u && d2u ./build/amd_f77simple.out || true ) + - diff Demo/amd_f77simple.out build/amd_f77simple.out + - ./build/amd_f77demo > build/amd_f77demo.out && ( command -v d2u && d2u ./build/amd_f77demo.out || true ) + - diff Demo/amd_f77demo.out build/amd_f77demo.out # just compile after running cmake; do not run cmake again remake: diff --git a/ThirdParty/SuiteSparse/AMD/Source/amd_order.c b/ThirdParty/SuiteSparse/AMD/Source/amd_order.c index 1dcc15a009..9df32164e0 100644 --- a/ThirdParty/SuiteSparse/AMD/Source/amd_order.c +++ b/ThirdParty/SuiteSparse/AMD/Source/amd_order.c @@ -71,9 +71,9 @@ int AMD_order return (AMD_INVALID) ; } - /* check if n or nz will cause size_t overflow */ - if (((size_t) n) >= SIZE_T_MAX / sizeof (Int) - || ((size_t) nz) >= SIZE_T_MAX / sizeof (Int)) + /* check if n or nz will cause integer overflow */ + if (((size_t) n) >= Int_MAX / sizeof (Int) + || ((size_t) nz) >= Int_MAX / sizeof (Int)) { if (info) Info [AMD_STATUS] = AMD_OUT_OF_MEMORY ; return (AMD_OUT_OF_MEMORY) ; /* problem too large */ @@ -89,8 +89,9 @@ int AMD_order } /* allocate two size-n integer workspaces */ - Len = SuiteSparse_malloc (n, sizeof (Int)) ; - Pinv = SuiteSparse_malloc (n, sizeof (Int)) ; + size_t nn = (size_t) n ; + Len = SuiteSparse_malloc (nn, sizeof (Int)) ; + Pinv = SuiteSparse_malloc (nn, sizeof (Int)) ; mem += n ; mem += n ; if (!Len || !Pinv) @@ -106,7 +107,7 @@ int AMD_order { /* sort the input matrix and remove duplicate entries */ AMD_DEBUG1 (("Matrix is jumbled\n")) ; - Rp = SuiteSparse_malloc (n+1, sizeof (Int)) ; + Rp = SuiteSparse_malloc (nn+1, sizeof (Int)) ; Ri = SuiteSparse_malloc (nz, sizeof (Int)) ; mem += (n+1) ; mem += MAX (nz,1) ; @@ -152,8 +153,8 @@ int AMD_order slen += nzaat/5 ; /* add elbow room */ for (i = 0 ; ok && i < 7 ; i++) { - ok = ((slen + n) > slen) ; /* check for size_t overflow */ - slen += n ; /* size-n elbow room, 6 size-n work */ + ok = ((slen + nn) > slen) ; /* check for size_t overflow */ + slen += nn ; /* size-n elbow room, 6 size-n work */ } mem += slen ; ok = ok && (slen < SIZE_T_MAX / sizeof (Int)) ; /* check for overflow */ diff --git a/ThirdParty/SuiteSparse/AMD/Source/amd_version.c b/ThirdParty/SuiteSparse/AMD/Source/amd_version.c new file mode 100644 index 0000000000..7d045f3516 --- /dev/null +++ b/ThirdParty/SuiteSparse/AMD/Source/amd_version.c @@ -0,0 +1,19 @@ +//------------------------------------------------------------------------------ +// AMD/Source/amd_version: return AMD version +//------------------------------------------------------------------------------ + +// AMD, Copyright (c) 1996-2023, Timothy A. Davis, Patrick R. Amestoy, and +// Iain S. Duff. All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause + +//------------------------------------------------------------------------------ + +#include "amd_internal.h" + +void amd_version (int version [3]) +{ + version [0] = AMD_MAIN_VERSION ; + version [1] = AMD_SUB_VERSION ; + version [2] = AMD_SUBSUB_VERSION ; +} + diff --git a/ThirdParty/SuiteSparse/AMD/cmake_modules/FindAMD.cmake b/ThirdParty/SuiteSparse/AMD/cmake_modules/FindAMD.cmake deleted file mode 100644 index 1b135e05a7..0000000000 --- a/ThirdParty/SuiteSparse/AMD/cmake_modules/FindAMD.cmake +++ /dev/null @@ -1,129 +0,0 @@ -#------------------------------------------------------------------------------- -# SuiteSparse/AMD/cmake_modules/FindAMD.cmake -#------------------------------------------------------------------------------- - -# The following copyright and license applies to just this file only, not to -# the library itself: -# FindAMD.cmake, Copyright (c) 2022-2023, Timothy A. Davis. All Rights Reserved. -# SPDX-License-Identifier: BSD-3-clause - -#------------------------------------------------------------------------------- - -# Finds the AMD include file and compiled library and sets: - -# AMD_INCLUDE_DIR - where to find amd.h -# AMD_LIBRARY - dynamic AMD library -# AMD_STATIC - static AMD library -# AMD_LIBRARIES - libraries when using AMD -# AMD_FOUND - true if AMD found - -# set ``AMD_ROOT`` to an AMD installation root to -# tell this module where to look. - -# All the Find*.cmake files in SuiteSparse are installed by 'make install' into -# /usr/local/lib/cmake/SuiteSparse (where '/usr/local' is the -# ${CMAKE_INSTALL_PREFIX}). To access this file, place the following commands -# in your CMakeLists.txt file. See also SuiteSparse/Example/CMakeLists.txt: -# -# set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} -# ${CMAKE_INSTALL_PREFIX}/lib/cmake/SuiteSparse ) - -#------------------------------------------------------------------------------- - -# include files for AMD -find_path ( AMD_INCLUDE_DIR - NAMES amd.h - HINTS ${CMAKE_SOURCE_DIR}/.. - HINTS ${CMAKE_SOURCE_DIR}/../SuiteSparse/AMD - HINTS ${CMAKE_SOURCE_DIR}/../AMD - PATH_SUFFIXES include Include -) - -# dynamic AMD library (or static if no dynamic library was built) -find_library ( AMD_LIBRARY - NAMES amd amd_static - HINTS ${CMAKE_SOURCE_DIR}/.. - HINTS ${CMAKE_SOURCE_DIR}/../SuiteSparse/AMD - HINTS ${CMAKE_SOURCE_DIR}/../AMD - PATH_SUFFIXES lib build build/Release build/Debug -) - -if ( MSVC ) - set ( STATIC_NAME amd_static ) -else ( ) - set ( STATIC_NAME amd ) - set ( save ${CMAKE_FIND_LIBRARY_SUFFIXES} ) - set ( CMAKE_FIND_LIBRARY_SUFFIXES - ${CMAKE_STATIC_LIBRARY_SUFFIX} ${CMAKE_FIND_LIBRARY_SUFFIXES} ) -endif ( ) - -# static AMD library -find_library ( AMD_STATIC - NAMES ${STATIC_NAME} - HINTS ${CMAKE_SOURCE_DIR}/.. - HINTS ${CMAKE_SOURCE_DIR}/../SuiteSparse/AMD - HINTS ${CMAKE_SOURCE_DIR}/../AMD - PATH_SUFFIXES lib build build/Release build/Debug -) - -if ( NOT MSVC ) - # restore the CMAKE_FIND_LIBRARY_SUFFIXES variable - set ( CMAKE_FIND_LIBRARY_SUFFIXES ${save} ) -endif ( ) - -# get version of the library from the dynamic library name -get_filename_component ( AMD_LIBRARY ${AMD_LIBRARY} REALPATH ) -get_filename_component ( AMD_FILENAME ${AMD_LIBRARY} NAME ) -string ( - REGEX MATCH "[0-9]+.[0-9]+.[0-9]+" - AMD_VERSION - ${AMD_FILENAME} -) - -# set ( AMD_VERSION "" ) -if ( EXISTS "${AMD_INCLUDE_DIR}" AND NOT AMD_VERSION ) - # if the version does not appear in the filename, read the include file - file ( STRINGS ${AMD_INCLUDE_DIR}/amd.h AMD_MAJOR_STR - REGEX "define AMD_MAIN_VERSION" ) - file ( STRINGS ${AMD_INCLUDE_DIR}/amd.h AMD_MINOR_STR - REGEX "define AMD_SUB_VERSION" ) - file ( STRINGS ${AMD_INCLUDE_DIR}/amd.h AMD_PATCH_STR - REGEX "define AMD_SUBSUB_VERSION" ) - message ( STATUS "major: ${AMD_MAJOR_STR}" ) - message ( STATUS "minor: ${AMD_MINOR_STR}" ) - message ( STATUS "patch: ${AMD_PATCH_STR}" ) - string ( REGEX MATCH "[0-9]+" AMD_MAJOR ${AMD_MAJOR_STR} ) - string ( REGEX MATCH "[0-9]+" AMD_MINOR ${AMD_MINOR_STR} ) - string ( REGEX MATCH "[0-9]+" AMD_PATCH ${AMD_PATCH_STR} ) - set (AMD_VERSION "${AMD_MAJOR}.${AMD_MINOR}.${AMD_PATCH}") -endif ( ) - -set ( AMD_LIBRARIES ${AMD_LIBRARY} ) - -include (FindPackageHandleStandardArgs) - -find_package_handle_standard_args ( AMD - REQUIRED_VARS AMD_LIBRARY AMD_INCLUDE_DIR - VERSION_VAR AMD_VERSION -) - -mark_as_advanced ( - AMD_INCLUDE_DIR - AMD_LIBRARY - AMD_STATIC - AMD_LIBRARIES -) - -if ( AMD_FOUND ) - message ( STATUS "AMD version: ${AMD_VERSION}" ) - message ( STATUS "AMD include: ${AMD_INCLUDE_DIR}") - message ( STATUS "AMD library: ${AMD_LIBRARY}") - message ( STATUS "AMD static: ${AMD_STATIC}") -else ( ) - message ( STATUS "AMD not found" ) - set ( AMD_INCLUDE_DIR "" ) - set ( AMD_LIBRARIES "" ) - set ( AMD_LIBRARY "" ) - set ( AMD_STATIC "" ) -endif ( ) - diff --git a/ThirdParty/SuiteSparse/BTF/CMakeLists.txt b/ThirdParty/SuiteSparse/BTF/CMakeLists.txt index ce4ecf45a9..059bb80c25 100644 --- a/ThirdParty/SuiteSparse/BTF/CMakeLists.txt +++ b/ThirdParty/SuiteSparse/BTF/CMakeLists.txt @@ -2,7 +2,7 @@ # SuiteSparse/BTF/CMakeLists.txt: cmake for BTF #------------------------------------------------------------------------------- -# BTF, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +# BTF, Copyright (c) 2004-2023, University of Florida. All Rights Reserved. # Author: Timothy A. Davis. # SPDX-License-Identifier: LGPL-2.1+ @@ -10,12 +10,12 @@ # get the version #------------------------------------------------------------------------------- -cmake_minimum_required ( VERSION 3.19 ) +cmake_minimum_required ( VERSION 3.22 ) -set ( BTF_DATE "Jan 17, 2023" ) -set ( BTF_VERSION_MAJOR 2 ) -set ( BTF_VERSION_MINOR 0 ) -set ( BTF_VERSION_SUB 3 ) +set ( BTF_DATE "Jan 10, 2024" ) +set ( BTF_VERSION_MAJOR 2 CACHE STRING "" FORCE ) +set ( BTF_VERSION_MINOR 3 CACHE STRING "" FORCE ) +set ( BTF_VERSION_SUB 1 CACHE STRING "" FORCE ) message ( STATUS "Building BTF version: v" ${BTF_VERSION_MAJOR}. @@ -23,28 +23,33 @@ message ( STATUS "Building BTF version: v" ${BTF_VERSION_SUB} " (" ${BTF_DATE} ")" ) #------------------------------------------------------------------------------- -# SuiteSparse policies +# define the project #------------------------------------------------------------------------------- -set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} - ${CMAKE_SOURCE_DIR}/cmake_modules - ${CMAKE_SOURCE_DIR}/../SuiteSparse_config/cmake_modules ) - -include ( SuiteSparsePolicy ) +project ( BTF + VERSION "${BTF_VERSION_MAJOR}.${BTF_VERSION_MINOR}.${BTF_VERSION_SUB}" + LANGUAGES C ) #------------------------------------------------------------------------------- -# define the project +# SuiteSparse policies #------------------------------------------------------------------------------- -project ( btf - VERSION "${BTF_VERSION_MAJOR}.${BTF_VERSION_MINOR}.${BTF_VERSION_SUB}" - LANGUAGES C ) +set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} + ${PROJECT_SOURCE_DIR}/../SuiteSparse_config/cmake_modules ) + +include ( SuiteSparsePolicy ) #------------------------------------------------------------------------------- # find library dependencies #------------------------------------------------------------------------------- -find_package ( SuiteSparse_config 7.0.0 REQUIRED ) +if ( NOT SUITESPARSE_ROOT_CMAKELISTS ) + find_package ( SuiteSparse_config 7.5.0 + PATHS ${CMAKE_SOURCE_DIR}/../SuiteSparse_config/build NO_DEFAULT_PATH ) + if ( NOT TARGET SuiteSparse::SuiteSparseConfig ) + find_package ( SuiteSparse_config 7.5.0 REQUIRED ) + endif ( ) +endif ( ) #------------------------------------------------------------------------------- # configure files @@ -57,7 +62,7 @@ configure_file ( "Config/btf.h.in" "${PROJECT_SOURCE_DIR}/Include/btf.h" # include directories #------------------------------------------------------------------------------- -include_directories ( Source Include ${SUITESPARSE_CONFIG_INCLUDE_DIR} ) +include_directories ( Source Include ) #------------------------------------------------------------------------------- # dynamic btf library properties @@ -65,49 +70,76 @@ include_directories ( Source Include ${SUITESPARSE_CONFIG_INCLUDE_DIR} ) file ( GLOB BTF_SOURCES "Source/*.c" ) -add_library ( btf SHARED ${BTF_SOURCES} ) +if ( BUILD_SHARED_LIBS ) + add_library ( BTF SHARED ${BTF_SOURCES} ) + + set_target_properties ( BTF PROPERTIES + VERSION ${BTF_VERSION_MAJOR}.${BTF_VERSION_MINOR}.${BTF_VERSION_SUB} + C_STANDARD 11 + C_STANDARD_REQUIRED ON + OUTPUT_NAME btf + SOVERSION ${BTF_VERSION_MAJOR} + PUBLIC_HEADER "Include/btf.h" + WINDOWS_EXPORT_ALL_SYMBOLS ON) + + if ( ${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.25" ) + set_target_properties ( BTF PROPERTIES EXPORT_NO_SYSTEM ON ) + endif ( ) -set_target_properties ( btf PROPERTIES - VERSION ${BTF_VERSION_MAJOR}.${BTF_VERSION_MINOR}.${BTF_VERSION_SUB} - C_STANDARD_REQUIRED 11 - SOVERSION ${BTF_VERSION_MAJOR} - PUBLIC_HEADER "Include/btf.h" - WINDOWS_EXPORT_ALL_SYMBOLS ON) + target_include_directories ( BTF + INTERFACE $ + $ ) +endif ( ) #------------------------------------------------------------------------------- # static btf library properties #------------------------------------------------------------------------------- -if ( NOT NSTATIC ) - add_library ( btf_static STATIC ${BTF_SOURCES} ) +if ( BUILD_STATIC_LIBS ) + add_library ( BTF_static STATIC ${BTF_SOURCES} ) - set_target_properties ( btf_static PROPERTIES - VERSION ${BTF_VERSION_MAJOR}.${BTF_VERSION_MINOR}.${BTF_VERSION_SUB} - C_STANDARD_REQUIRED 11 + set_target_properties ( BTF_static PROPERTIES + C_STANDARD 11 + C_STANDARD_REQUIRED ON OUTPUT_NAME btf - SOVERSION ${BTF_VERSION_MAJOR} ) + PUBLIC_HEADER "Include/btf.h" ) if ( MSVC ) - set_target_properties ( btf_static PROPERTIES + set_target_properties ( BTF_static PROPERTIES OUTPUT_NAME btf_static ) endif ( ) + + if ( ${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.25" ) + set_target_properties ( BTF_static PROPERTIES EXPORT_NO_SYSTEM ON ) + endif ( ) + + target_include_directories ( BTF_static + INTERFACE $ + $ ) endif ( ) #------------------------------------------------------------------------------- # add the library dependencies #------------------------------------------------------------------------------- -# suitesparseconfig: -target_link_libraries ( btf PUBLIC ${SUITESPARSE_CONFIG_LIBRARIES} ) -if ( NOT NSTATIC ) - target_link_libraries ( btf_static PUBLIC ${SUITESPARSE_CONFIG_STATIC} ) +# SuiteSparseConfig: +if ( BUILD_SHARED_LIBS ) + target_include_directories ( BTF PUBLIC + "$" ) +endif ( ) +if ( BUILD_STATIC_LIBS ) + target_include_directories ( BTF_static PUBLIC + "$" ) endif ( ) # libm: if ( NOT WIN32 ) - target_link_libraries ( btf PUBLIC m ) - if ( NOT NSTATIC ) - target_link_libraries ( btf_static PUBLIC m ) + if ( BUILD_SHARED_LIBS ) + target_link_libraries ( BTF PRIVATE m ) + endif ( ) + if ( BUILD_STATIC_LIBS ) + set ( BTF_STATIC_LIBS "${BTF_STATIC_LIBS} -lm" ) + target_link_libraries ( BTF_static PUBLIC m ) endif ( ) endif ( ) @@ -115,17 +147,92 @@ endif ( ) # BTF installation location #------------------------------------------------------------------------------- -install ( TARGETS btf - LIBRARY DESTINATION ${SUITESPARSE_LIBDIR} - ARCHIVE DESTINATION ${SUITESPARSE_LIBDIR} - RUNTIME DESTINATION ${SUITESPARSE_BINDIR} - PUBLIC_HEADER DESTINATION ${SUITESPARSE_INCLUDEDIR} ) -install ( FILES ${CMAKE_SOURCE_DIR}/cmake_modules/FindBTF.cmake - DESTINATION ${SUITESPARSE_LIBDIR}/cmake/SuiteSparse - COMPONENT Development ) -if ( NOT NSTATIC ) - install ( TARGETS btf_static - ARCHIVE DESTINATION ${SUITESPARSE_LIBDIR} ) +include ( CMakePackageConfigHelpers ) + +if ( BUILD_SHARED_LIBS ) + install ( TARGETS BTF + EXPORT BTFTargets + LIBRARY DESTINATION ${SUITESPARSE_LIBDIR} + ARCHIVE DESTINATION ${SUITESPARSE_LIBDIR} + RUNTIME DESTINATION ${SUITESPARSE_BINDIR} + PUBLIC_HEADER DESTINATION ${SUITESPARSE_INCLUDEDIR} ) +endif ( ) +if ( BUILD_STATIC_LIBS ) + install ( TARGETS BTF_static + EXPORT BTFTargets + ARCHIVE DESTINATION ${SUITESPARSE_LIBDIR} + PUBLIC_HEADER DESTINATION ${SUITESPARSE_INCLUDEDIR} ) +endif ( ) + +# create (temporary) export target file during build +export ( EXPORT BTFTargets + NAMESPACE SuiteSparse:: + FILE ${CMAKE_CURRENT_BINARY_DIR}/BTFTargets.cmake ) + +# install export target, config and version files for find_package +install ( EXPORT BTFTargets + NAMESPACE SuiteSparse:: + DESTINATION ${SUITESPARSE_PKGFILEDIR}/cmake/BTF ) + +# generate config file to be used in common build tree +set ( SUITESPARSE_IN_BUILD_TREE ON ) +configure_package_config_file ( + Config/BTFConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/BTFConfig.cmake + INSTALL_DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/BTFConfig.cmake ) + +# generate config file to be installed +set ( SUITESPARSE_IN_BUILD_TREE OFF ) +configure_package_config_file ( + Config/BTFConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/target/BTFConfig.cmake + INSTALL_DESTINATION ${SUITESPARSE_PKGFILEDIR}/cmake/BTF ) + +write_basic_package_version_file ( + ${CMAKE_CURRENT_BINARY_DIR}/BTFConfigVersion.cmake + COMPATIBILITY SameMajorVersion ) + +install ( FILES + ${CMAKE_CURRENT_BINARY_DIR}/target/BTFConfig.cmake + ${CMAKE_CURRENT_BINARY_DIR}/BTFConfigVersion.cmake + DESTINATION ${SUITESPARSE_PKGFILEDIR}/cmake/BTF ) + +#------------------------------------------------------------------------------- +# create pkg-config file +#------------------------------------------------------------------------------- + +if ( NOT MSVC ) + set ( prefix "${CMAKE_INSTALL_PREFIX}" ) + set ( exec_prefix "\${prefix}" ) + cmake_path ( IS_ABSOLUTE SUITESPARSE_LIBDIR SUITESPARSE_LIBDIR_IS_ABSOLUTE ) + if (SUITESPARSE_LIBDIR_IS_ABSOLUTE) + set ( libdir "${SUITESPARSE_LIBDIR}") + else ( ) + set ( libdir "\${exec_prefix}/${SUITESPARSE_LIBDIR}") + endif ( ) + cmake_path ( IS_ABSOLUTE SUITESPARSE_INCLUDEDIR SUITESPARSE_INCLUDEDIR_IS_ABSOLUTE ) + if (SUITESPARSE_INCLUDEDIR_IS_ABSOLUTE) + set ( includedir "${SUITESPARSE_INCLUDEDIR}") + else ( ) + set ( includedir "\${prefix}/${SUITESPARSE_INCLUDEDIR}") + endif ( ) + if ( BUILD_SHARED_LIBS ) + set ( SUITESPARSE_LIB_BASE_NAME $ ) + else ( ) + set ( SUITESPARSE_LIB_BASE_NAME $ ) + endif ( ) + configure_file ( + Config/BTF.pc.in + BTF.pc.out + @ONLY + NEWLINE_STYLE LF ) + file ( GENERATE + OUTPUT BTF.pc + INPUT ${CMAKE_CURRENT_BINARY_DIR}/BTF.pc.out + NEWLINE_STYLE LF ) + install ( FILES + ${CMAKE_CURRENT_BINARY_DIR}/BTF.pc + DESTINATION ${SUITESPARSE_PKGFILEDIR}/pkgconfig ) endif ( ) #------------------------------------------------------------------------------- @@ -133,4 +240,3 @@ endif ( ) #------------------------------------------------------------------------------- include ( SuiteSparseReport ) - diff --git a/ThirdParty/SuiteSparse/BTF/Config/BTF.pc.in b/ThirdParty/SuiteSparse/BTF/Config/BTF.pc.in new file mode 100644 index 0000000000..13de408ff6 --- /dev/null +++ b/ThirdParty/SuiteSparse/BTF/Config/BTF.pc.in @@ -0,0 +1,16 @@ +# BTF, Copyright (c) 2004-2023, Timothy A. Davis. +# All Rights Reserved. +# SPDX-License-Identifier: LGPL-2.1-or-later + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: BTF +URL: https://github.com/DrTimothyAldenDavis/SuiteSparse +Description: Software package for permuting a matrix into block upper triangular form in SuiteSparse +Version: @BTF_VERSION_MAJOR@.@BTF_VERSION_MINOR@.@BTF_VERSION_SUB@ +Libs: -L${libdir} -l@SUITESPARSE_LIB_BASE_NAME@ +Libs.private: @BTF_STATIC_LIBS@ +Cflags: -I${includedir} diff --git a/ThirdParty/SuiteSparse/BTF/Config/BTFConfig.cmake.in b/ThirdParty/SuiteSparse/BTF/Config/BTFConfig.cmake.in new file mode 100644 index 0000000000..4fdca7a1ec --- /dev/null +++ b/ThirdParty/SuiteSparse/BTF/Config/BTFConfig.cmake.in @@ -0,0 +1,152 @@ +#------------------------------------------------------------------------------- +# SuiteSparse/BTF/cmake_modules/BTFConfig.cmake +#------------------------------------------------------------------------------- + +# The following copyright and license applies to just this file only, not to +# the library itself: +# BTFConfig.cmake, Copyright (c) 2023, Timothy A. Davis. All Rights Reserved. +# SPDX-License-Identifier: BSD-3-clause + +#------------------------------------------------------------------------------- + +# Finds the BTF include file and compiled library. +# The following targets are defined: +# SuiteSparse::BTF - for the shared library (if available) +# SuiteSparse::BTF_static - for the static library (if available) + +# For backward compatibility the following variables are set: + +# BTF_INCLUDE_DIR - where to find btf.h +# BTF_LIBRARY - dynamic BTF library +# BTF_STATIC - static BTF library +# BTF_LIBRARIES - libraries when using BTF +# BTF_FOUND - true if BTF found + +# Set ``CMAKE_MODULE_PATH`` to the parent folder where this module file is +# installed. + +#------------------------------------------------------------------------------- + +@PACKAGE_INIT@ + +set ( BTF_DATE "@BTF_DATE@" ) +set ( BTF_VERSION_MAJOR @BTF_VERSION_MAJOR@ ) +set ( BTF_VERSION_MINOR @BTF_VERSION_MINOR@ ) +set ( BTF_VERSION_SUB @BTF_VERSION_SUB@ ) +set ( BTF_VERSION "@BTF_VERSION_MAJOR@.@BTF_VERSION_MINOR@.@BTF_VERSION_SUB@" ) + +# Check for dependent targets +include ( CMakeFindDependencyMacro ) + +# Look for SuiteSparse_config target +if ( @SUITESPARSE_IN_BUILD_TREE@ ) + if ( NOT TARGET SuiteSparse::SuiteSparseConfig ) + # First check in a common build tree + find_dependency ( SuiteSparse_config @SUITESPARSE_CONFIG_VERSION_MAJOR@.@SUITESPARSE_CONFIG_VERSION_MINOR@ + PATHS ${CMAKE_SOURCE_DIR}/../SuiteSparse_config/build NO_DEFAULT_PATH ) + # Then, check in the currently active CMAKE_MODULE_PATH + if ( NOT SuiteSparse_config_FOUND ) + find_dependency ( SuiteSparse_config @SUITESPARSE_CONFIG_VERSION_MAJOR@.@SUITESPARSE_CONFIG_VERSION_MINOR@ ) + endif ( ) + endif ( ) +else ( ) + if ( NOT TARGET SuiteSparse::SuiteSparseConfig ) + find_dependency ( SuiteSparse_config @SUITESPARSE_CONFIG_VERSION_MAJOR@.@SUITESPARSE_CONFIG_VERSION_MINOR@ ) + endif ( ) +endif ( ) +if ( NOT SuiteSparse_config_FOUND ) + set ( BTF_FOUND OFF ) + return ( ) +endif ( ) + + +# Import target +include ( ${CMAKE_CURRENT_LIST_DIR}/BTFTargets.cmake ) + +# The following is only for backward compatibility with FindBTF. + +set ( _target_shared SuiteSparse::BTF ) +set ( _target_static SuiteSparse::BTF_static ) +set ( _var_prefix "BTF" ) + +if ( NOT @BUILD_SHARED_LIBS@ AND NOT TARGET ${_target_shared} ) + # make sure there is always an import target without suffix ) + add_library ( ${_target_shared} ALIAS ${_target_static} ) +endif ( ) + +get_target_property ( ${_var_prefix}_INCLUDE_DIR ${_target_shared} INTERFACE_INCLUDE_DIRECTORIES ) +if ( ${_var_prefix}_INCLUDE_DIR ) + # First item in SuiteSparse targets contains the "main" header directory. + list ( GET ${_var_prefix}_INCLUDE_DIR 0 ${_var_prefix}_INCLUDE_DIR ) +endif ( ) +get_target_property ( ${_var_prefix}_LIBRARY ${_target_shared} IMPORTED_IMPLIB ) +if ( NOT ${_var_prefix}_LIBRARY ) + get_target_property ( _library_chk ${_target_shared} IMPORTED_LOCATION ) + if ( EXISTS ${_library_chk} ) + set ( ${_var_prefix}_LIBRARY ${_library_chk} ) + endif ( ) +endif ( ) +if ( TARGET ${_target_static} ) + get_target_property ( ${_var_prefix}_STATIC ${_target_static} IMPORTED_LOCATION ) +endif ( ) + +# Check for most common build types +set ( _config_types "Debug" "Release" "RelWithDebInfo" "MinSizeRel" "None" ) + +get_property ( _isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG ) +if ( _isMultiConfig ) + # For multi-configuration generators (e.g., Visual Studio), prefer those + # configurations. + list ( PREPEND _config_types ${CMAKE_CONFIGURATION_TYPES} ) +else ( ) + # For single-configuration generators, prefer the current configuration. + list ( PREPEND _config_types ${CMAKE_BUILD_TYPE} ) +endif ( ) + +list ( REMOVE_DUPLICATES _config_types ) + +foreach ( _config ${_config_types} ) + string ( TOUPPER ${_config} _uc_config ) + if ( NOT ${_var_prefix}_LIBRARY ) + get_target_property ( _library_chk ${_target_shared} + IMPORTED_IMPLIB_${_uc_config} ) + if ( EXISTS ${_library_chk} ) + set ( ${_var_prefix}_LIBRARY ${_library_chk} ) + endif ( ) + endif ( ) + if ( NOT ${_var_prefix}_LIBRARY ) + get_target_property ( _library_chk ${_target_shared} + IMPORTED_LOCATION_${_uc_config} ) + if ( EXISTS ${_library_chk} ) + set ( ${_var_prefix}_LIBRARY ${_library_chk} ) + endif ( ) + endif ( ) + if ( TARGET ${_target_static} AND NOT ${_var_prefix}_STATIC ) + get_target_property ( _library_chk ${_target_static} + IMPORTED_LOCATION_${_uc_config} ) + if ( EXISTS ${_library_chk} ) + set ( ${_var_prefix}_STATIC ${_library_chk} ) + endif ( ) + endif ( ) +endforeach ( ) + +set ( BTF_LIBRARIES ${BTF_LIBRARY} ) + +macro ( suitesparse_check_exist _var _files ) + # ignore generator expressions + string ( GENEX_STRIP "${_files}" _files2 ) + + foreach ( _file ${_files2} ) + if ( NOT EXISTS "${_file}" ) + message ( FATAL_ERROR "File or directory ${_file} referenced by variable ${_var} does not exist!" ) + endif ( ) + endforeach () +endmacro ( ) + +suitesparse_check_exist ( BTF_INCLUDE_DIR ${BTF_INCLUDE_DIR} ) +suitesparse_check_exist ( BTF_LIBRARY ${BTF_LIBRARY} ) + +message ( STATUS "BTF version: ${BTF_VERSION}" ) +message ( STATUS "BTF include: ${BTF_INCLUDE_DIR}" ) +message ( STATUS "BTF library: ${BTF_LIBRARY}" ) +message ( STATUS "BTF static: ${BTF_STATIC}" ) diff --git a/ThirdParty/SuiteSparse/BTF/Config/btf.h.in b/ThirdParty/SuiteSparse/BTF/Config/btf.h.in index 01fda8fd8d..a5985db8af 100644 --- a/ThirdParty/SuiteSparse/BTF/Config/btf.h.in +++ b/ThirdParty/SuiteSparse/BTF/Config/btf.h.in @@ -2,7 +2,7 @@ // BTF/Include/btf.h: include file for BTF //------------------------------------------------------------------------------ -// BTF, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// BTF, Copyright (c) 2004-2024, University of Florida. All Rights Reserved. // Author: Timothy A. Davis. // SPDX-License-Identifier: LGPL-2.1+ @@ -90,13 +90,13 @@ #ifndef _BTF_H #define _BTF_H +#include "SuiteSparse_config.h" + /* make it easy for C++ programs to include BTF */ #ifdef __cplusplus extern "C" { #endif -#include "SuiteSparse_config.h" - int32_t btf_maxtrans /* returns # of columns matched */ ( /* --- input, not modified: --- */ @@ -218,6 +218,16 @@ int32_t btf_order /* returns number of blocks found */ int64_t btf_l_order (int64_t, int64_t *, int64_t *, double , double *, int64_t *, int64_t *, int64_t *, int64_t *, int64_t *) ; +//------------------------------------------------------------------------------ +// btf_version: return BTF version +//------------------------------------------------------------------------------ + +void btf_version (int version [3]) ; + +#ifdef __cplusplus +} +#endif + /* ========================================================================== */ /* === BTF marking of singular columns ====================================== */ @@ -247,7 +257,7 @@ int64_t btf_l_order (int64_t, int64_t *, int64_t *, double , double *, * * This also works during compile-time: * - * #if (BTF >= BTF_VERSION_CODE (1,2)) + * #if (BTF_VERSION >= BTF_VERSION_CODE (1,2)) * printf ("This is version 1.2 or later\n") ; * #else * printf ("This is an early version\n") ; @@ -259,10 +269,13 @@ int64_t btf_l_order (int64_t, int64_t *, int64_t *, double , double *, #define BTF_SUB_VERSION @BTF_VERSION_MINOR@ #define BTF_SUBSUB_VERSION @BTF_VERSION_SUB@ -#define BTF_VERSION_CODE(main,sub) ((main) * 1000 + (sub)) -#define BTF_VERSION BTF_VERSION_CODE(BTF_MAIN_VERSION,BTF_SUB_VERSION) +#define BTF_VERSION_CODE(main,sub) SUITESPARSE_VER_CODE(main,sub) +#define BTF_VERSION BTF_VERSION_CODE(@BTF_VERSION_MAJOR@,@BTF_VERSION_MINOR@) -#ifdef __cplusplus -} +#define BTF__VERSION SUITESPARSE__VERCODE(@BTF_VERSION_MAJOR@,@BTF_VERSION_MINOR@,@BTF_VERSION_SUB@) +#if !defined (SUITESPARSE__VERSION) || \ + (SUITESPARSE__VERSION < SUITESPARSE__VERCODE(7,5,0)) +#error "BTF @BTF_VERSION_MAJOR@.@BTF_VERSION_MINOR@.@BTF_VERSION_SUB@ requires SuiteSparse_config 7.5.0 or later" #endif + #endif diff --git a/ThirdParty/SuiteSparse/BTF/Doc/ChangeLog b/ThirdParty/SuiteSparse/BTF/Doc/ChangeLog index d984d8a694..d857cbc6ed 100644 --- a/ThirdParty/SuiteSparse/BTF/Doc/ChangeLog +++ b/ThirdParty/SuiteSparse/BTF/Doc/ChangeLog @@ -1,3 +1,24 @@ +Jan 10, 2024: version 2.3.1 + + * minor updates to build system + +Dec 30, 2023: version 2.3.0 + + * major change to build system: by Markus Mützel + * btf_version: added to return version of BTF + +Sept 18, 2023: version 2.2.1 + + * cmake update: add "None" build type, from Antonio Rojas, for Arch Linux + +Sept 8, 2023: version 2.2.0 + + * cmake updates: SuiteSparse:: namespace by Markus Muetzel + +June 16, 2023: version 2.0.4 + + * cmake build system updates: update by Markus Muetzel + Jan 17, 2023: version 2.0.3 * SuiteSparse_config: now v7.0.0 diff --git a/ThirdParty/SuiteSparse/BTF/Include/btf.h b/ThirdParty/SuiteSparse/BTF/Include/btf.h index 58cb94d7e3..c152e879aa 100644 --- a/ThirdParty/SuiteSparse/BTF/Include/btf.h +++ b/ThirdParty/SuiteSparse/BTF/Include/btf.h @@ -2,7 +2,7 @@ // BTF/Include/btf.h: include file for BTF //------------------------------------------------------------------------------ -// BTF, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// BTF, Copyright (c) 2004-2024, University of Florida. All Rights Reserved. // Author: Timothy A. Davis. // SPDX-License-Identifier: LGPL-2.1+ @@ -90,13 +90,13 @@ #ifndef _BTF_H #define _BTF_H +#include "SuiteSparse_config.h" + /* make it easy for C++ programs to include BTF */ #ifdef __cplusplus extern "C" { #endif -#include "SuiteSparse_config.h" - int32_t btf_maxtrans /* returns # of columns matched */ ( /* --- input, not modified: --- */ @@ -218,6 +218,16 @@ int32_t btf_order /* returns number of blocks found */ int64_t btf_l_order (int64_t, int64_t *, int64_t *, double , double *, int64_t *, int64_t *, int64_t *, int64_t *, int64_t *) ; +//------------------------------------------------------------------------------ +// btf_version: return BTF version +//------------------------------------------------------------------------------ + +void btf_version (int version [3]) ; + +#ifdef __cplusplus +} +#endif + /* ========================================================================== */ /* === BTF marking of singular columns ====================================== */ @@ -247,22 +257,25 @@ int64_t btf_l_order (int64_t, int64_t *, int64_t *, double , double *, * * This also works during compile-time: * - * #if (BTF >= BTF_VERSION_CODE (1,2)) + * #if (BTF_VERSION >= BTF_VERSION_CODE (1,2)) * printf ("This is version 1.2 or later\n") ; * #else * printf ("This is an early version\n") ; * #endif */ -#define BTF_DATE "Jan 17, 2023" +#define BTF_DATE "Jan 10, 2024" #define BTF_MAIN_VERSION 2 -#define BTF_SUB_VERSION 0 -#define BTF_SUBSUB_VERSION 3 +#define BTF_SUB_VERSION 3 +#define BTF_SUBSUB_VERSION 1 -#define BTF_VERSION_CODE(main,sub) ((main) * 1000 + (sub)) -#define BTF_VERSION BTF_VERSION_CODE(BTF_MAIN_VERSION,BTF_SUB_VERSION) +#define BTF_VERSION_CODE(main,sub) SUITESPARSE_VER_CODE(main,sub) +#define BTF_VERSION BTF_VERSION_CODE(2,3) -#ifdef __cplusplus -} +#define BTF__VERSION SUITESPARSE__VERCODE(2,3,1) +#if !defined (SUITESPARSE__VERSION) || \ + (SUITESPARSE__VERSION < SUITESPARSE__VERCODE(7,5,0)) +#error "BTF 2.3.1 requires SuiteSparse_config 7.5.0 or later" #endif + #endif diff --git a/ThirdParty/SuiteSparse/BTF/Include/btf_internal.h b/ThirdParty/SuiteSparse/BTF/Include/btf_internal.h index 15b1b04452..96acda9015 100644 --- a/ThirdParty/SuiteSparse/BTF/Include/btf_internal.h +++ b/ThirdParty/SuiteSparse/BTF/Include/btf_internal.h @@ -2,7 +2,7 @@ // BTF/Include/btf_internsl.h: internal include file for BTF //------------------------------------------------------------------------------ -// BTF, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// BTF, Copyright (c) 2004-2023, University of Florida. All Rights Reserved. // Author: Timothy A. Davis. // SPDX-License-Identifier: LGPL-2.1+ diff --git a/ThirdParty/SuiteSparse/BTF/Makefile b/ThirdParty/SuiteSparse/BTF/Makefile index f86ddabfde..5e9baae9f5 100644 --- a/ThirdParty/SuiteSparse/BTF/Makefile +++ b/ThirdParty/SuiteSparse/BTF/Makefile @@ -36,18 +36,18 @@ default: library # default is to install only in /usr/local library: - ( cd build && cmake $(CMAKE_OPTIONS) .. && cmake --build . -j${JOBS} ) + ( cd build && cmake $(CMAKE_OPTIONS) .. && cmake --build . --config Release -j${JOBS} ) # install only in SuiteSparse/lib and SuiteSparse/include local: - ( cd build && cmake $(CMAKE_OPTIONS) -DLOCAL_INSTALL=1 .. && cmake --build . -j${JOBS} ) + ( cd build && cmake $(CMAKE_OPTIONS) -USUITESPARSE_PKGFILEDIR -DSUITESPARSE_LOCAL_INSTALL=1 .. && cmake --build . --config Release -j${JOBS} ) # install only in /usr/local (default) global: - ( cd build && cmake $(CMAKE_OPTIONS) -DLOCAL_INSTALL=0 .. && cmake --build . -j${JOBS} ) + ( cd build && cmake $(CMAKE_OPTIONS) -USUITESPARSE_PKGFILEDIR -DSUITESPARSE_LOCAL_INSTALL=0 .. && cmake --build . --config Release -j${JOBS} ) debug: - ( cd build && cmake $(CMAKE_OPTIONS) -DCMAKE_BUILD_TYPE=Debug .. && cmake --build . -j${JOBS} ) + ( cd build && cmake $(CMAKE_OPTIONS) -DCMAKE_BUILD_TYPE=Debug .. && cmake --build . --config Debug -j${JOBS} ) all: library diff --git a/ThirdParty/SuiteSparse/BTF/Source/btf_version.c b/ThirdParty/SuiteSparse/BTF/Source/btf_version.c new file mode 100644 index 0000000000..151f24d79d --- /dev/null +++ b/ThirdParty/SuiteSparse/BTF/Source/btf_version.c @@ -0,0 +1,19 @@ +//------------------------------------------------------------------------------ +// BTF/Source/btf_version: return BTF version +//------------------------------------------------------------------------------ + +// BTF, Copyright (c) 2004-2023, University of Florida. All Rights Reserved. +// Author: Timothy A. Davis. +// SPDX-License-Identifier: LGPL-2.1+ + +//------------------------------------------------------------------------------ + +#include "btf.h" + +void btf_version (int version [3]) +{ + version [0] = BTF_MAIN_VERSION ; + version [1] = BTF_SUB_VERSION ; + version [2] = BTF_SUBSUB_VERSION ; +} + diff --git a/ThirdParty/SuiteSparse/BTF/cmake_modules/FindBTF.cmake b/ThirdParty/SuiteSparse/BTF/cmake_modules/FindBTF.cmake deleted file mode 100644 index b5e6153ed4..0000000000 --- a/ThirdParty/SuiteSparse/BTF/cmake_modules/FindBTF.cmake +++ /dev/null @@ -1,129 +0,0 @@ -#------------------------------------------------------------------------------- -# SuiteSparse/BTF/cmake_modules/FindBTF.cmake -#------------------------------------------------------------------------------- - -# The following copyright and license applies to just this file only, not to -# the library itself: -# FindBTF.cmake, Copyright (c) 2022-2023, Timothy A. Davis. All Rights Reserved. -# SPDX-License-Identifier: BSD-3-clause - -#------------------------------------------------------------------------------- - -# Finds the BTF include file and compiled library and sets: - -# BTF_INCLUDE_DIR - where to find btf.h -# BTF_LIBRARY - dynamic BTF library -# BTF_STATIC - static BTF library -# BTF_LIBRARIES - libraries when using BTF -# BTF_FOUND - true if BTF found - -# set ``BTF_ROOT`` to a BTF installation root to -# tell this module where to look. - -# All the Find*.cmake files in SuiteSparse are installed by 'make install' into -# /usr/local/lib/cmake/SuiteSparse (where '/usr/local' is the -# ${CMAKE_INSTALL_PREFIX}). To access this file, place the following commands -# in your CMakeLists.txt file. See also SuiteSparse/Example/CMakeLists.txt: -# -# set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} -# ${CMAKE_INSTALL_PREFIX}/lib/cmake/SuiteSparse ) - -#------------------------------------------------------------------------------- - -# include files for BTF -find_path ( BTF_INCLUDE_DIR - NAMES btf.h - HINTS ${CMAKE_SOURCE_DIR}/.. - HINTS ${CMAKE_SOURCE_DIR}/../SuiteSparse/BTF - HINTS ${CMAKE_SOURCE_DIR}/../BTF - PATH_SUFFIXES include Include -) - -# dynamic BTF library (or static if no dynamic library was built) -find_library ( BTF_LIBRARY - NAMES btf btf_static - HINTS ${CMAKE_SOURCE_DIR}/.. - HINTS ${CMAKE_SOURCE_DIR}/../SuiteSparse/BTF - HINTS ${CMAKE_SOURCE_DIR}/../BTF - PATH_SUFFIXES lib build build/Release build/Debug -) - -if ( MSVC ) - set ( STATIC_NAME btf_static ) -else ( ) - set ( STATIC_NAME btf ) - set ( save ${CMAKE_FIND_LIBRARY_SUFFIXES} ) - set ( CMAKE_FIND_LIBRARY_SUFFIXES - ${CMAKE_STATIC_LIBRARY_SUFFIX} ${CMAKE_FIND_LIBRARY_SUFFIXES} ) -endif ( ) - -# static BTF library -find_library ( BTF_STATIC - NAMES ${STATIC_NAME} - HINTS ${CMAKE_SOURCE_DIR}/.. - HINTS ${CMAKE_SOURCE_DIR}/../SuiteSparse/BTF - HINTS ${CMAKE_SOURCE_DIR}/../BTF - PATH_SUFFIXES lib build build/Release build/Debug -) - -if ( NOT MSVC ) - # restore the CMAKE_FIND_LIBRARY_SUFFIXES variable - set ( CMAKE_FIND_LIBRARY_SUFFIXES ${save} ) -endif ( ) - -# get version of the library from the dynamic library name -get_filename_component ( BTF_LIBRARY ${BTF_LIBRARY} REALPATH ) -get_filename_component ( BTF_FILENAME ${BTF_LIBRARY} NAME ) -string ( - REGEX MATCH "[0-9]+.[0-9]+.[0-9]+" - BTF_VERSION - ${BTF_FILENAME} -) - -# set ( BTF_VERSION "" ) -if ( EXISTS "${BTF_INCLUDE_DIR}" AND NOT BTF_VERSION ) - # if the version does not appear in the filename, read the include file - file ( STRINGS ${BTF_INCLUDE_DIR}/btf.h BTF_MAJOR_STR - REGEX "define BTF_MAIN_VERSION" ) - file ( STRINGS ${BTF_INCLUDE_DIR}/btf.h BTF_MINOR_STR - REGEX "define BTF_SUB_VERSION" ) - file ( STRINGS ${BTF_INCLUDE_DIR}/btf.h BTF_PATCH_STR - REGEX "define BTF_SUBSUB_VERSION" ) - message ( STATUS "major: ${BTF_MAJOR_STR}" ) - message ( STATUS "minor: ${BTF_MINOR_STR}" ) - message ( STATUS "patch: ${BTF_PATCH_STR}" ) - string ( REGEX MATCH "[0-9]+" BTF_MAJOR ${BTF_MAJOR_STR} ) - string ( REGEX MATCH "[0-9]+" BTF_MINOR ${BTF_MINOR_STR} ) - string ( REGEX MATCH "[0-9]+" BTF_PATCH ${BTF_PATCH_STR} ) - set (BTF_VERSION "${BTF_MAJOR}.${BTF_MINOR}.${BTF_PATCH}") -endif ( ) - -set ( BTF_LIBRARIES ${BTF_LIBRARY} ) - -include (FindPackageHandleStandardArgs) - -find_package_handle_standard_args ( BTF - REQUIRED_VARS BTF_LIBRARY BTF_INCLUDE_DIR - VERSION_VAR BTF_VERSION -) - -mark_as_advanced ( - BTF_INCLUDE_DIR - BTF_LIBRARY - BTF_STATIC - BTF_LIBRARIES -) - -if ( BTF_FOUND ) - message ( STATUS "BTF version: ${BTF_VERSION}" ) - message ( STATUS "BTF include: ${BTF_INCLUDE_DIR}" ) - message ( STATUS "BTF library: ${BTF_LIBRARY}" ) - message ( STATUS "BTF static: ${BTF_STATIC}" ) -else ( ) - message ( STATUS "BTF not found" ) - set ( BTF_INCLUDE_DIR "" ) - set ( BTF_LIBRARIES "" ) - set ( BTF_LIBRARY "" ) - set ( BTF_STATIC "" ) -endif ( ) - diff --git a/ThirdParty/SuiteSparse/CMakeLists.txt b/ThirdParty/SuiteSparse/CMakeLists.txt new file mode 100644 index 0000000000..0a23a5a480 --- /dev/null +++ b/ThirdParty/SuiteSparse/CMakeLists.txt @@ -0,0 +1,540 @@ +#------------------------------------------------------------------------------- +# SuiteSparse/CMakeLists.txt: root CMake build rules +#------------------------------------------------------------------------------- + +# Copyright (c) 2023-2024, Timothy A. Davis, All Rights Reserved. +# Just this particular file is under the Apache-2.0 license; each package has +# its own license. +# SPDX-License-Identifier: Apache-2.0 + +# This file and most packages in SuiteSparse require cmake 3.22 or later. Some +# packages can be built as stand-alone packages with their own CMakeLists.txt +# files, with older versions of cmake (GraphBLAS, LAGraph, and CSparse): +# +# GraphBLAS and LAGraph: 3.20 +# CSparse: 3.13 +# GraphBLAS jitifyer (for end user JIT kernels): 3.13 +# +# Other CMakeLists.txt files inside SuiteSparse are from dependent packages +# (LAGraph/deps/json_h, GraphBLAS/cpu_features, and CHOLMOD/SuiteSparse_metis +# which is a slightly revised copy of METIS 5.0.1) but none of those +# CMakeLists.txt files are used to build any package in SuiteSparse. +cmake_minimum_required ( VERSION 3.22 ) + +project ( "SuiteSparse" ) + +#------------------------------------------------------------------------------- +# SuiteSparse policies +#------------------------------------------------------------------------------- + +set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} + ${PROJECT_SOURCE_DIR}/SuiteSparse_config/cmake_modules ) + +#------------------------------------------------------------------------------- +# build options +#------------------------------------------------------------------------------- + +# lower-case list of all projects that can be built by this root CMake file +set ( SUITESPARSE_ALL_PROJECTS + "suitesparse_config;mongoose;amd;btf;camd;ccolamd;colamd;cholmod;cxsparse;ldl;klu;umfpack;paru;rbio;spqr;spex;graphblas;lagraph" ) + +# lower-case list of extra projects that can be built by this root CMake file +set ( SUITESPARSE_EXTRA_PROJECTS + "csparse" ) + +# lower-case list of known projects that can be built by this root CMake file +set ( SUITESPARSE_KNOWN_PROJECTS "${SUITESPARSE_ALL_PROJECTS};${SUITESPARSE_EXTRA_PROJECTS}" ) + +set ( SUITESPARSE_ENABLE_PROJECTS "all" CACHE STRING + "Semicolon-separated list of SuiteSparse projects to be built (${SUITESPARSE_KNOWN_PROJECTS}, or \"all\")" ) + +# expand "all" early on +if ( "all" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + set ( SUITESPARSE_ENABLE_PROJECTS "${SUITESPARSE_ENABLE_PROJECTS};${SUITESPARSE_ALL_PROJECTS}" ) + list ( REMOVE_ITEM SUITESPARSE_ENABLE_PROJECTS "all" ) + list ( REMOVE_DUPLICATES SUITESPARSE_ENABLE_PROJECTS ) +endif ( ) + +# check for unknown projects in list +foreach ( proj ${SUITESPARSE_ENABLE_PROJECTS} ) + if ( NOT "${proj}" IN_LIST SUITESPARSE_KNOWN_PROJECTS ) + message ( FATAL_ERROR "${proj} is not a known project: ${SUITESPARSE_KNOWN_PROJECTS}." ) + endif ( ) +endforeach ( ) + +# CHOLMOD options affecting dependencies +option ( CHOLMOD_CAMD "ON (default): use CAMD/CCOLAMD. OFF: do not use CAMD/CCOLAMD" ON ) + +# KLU options affecting dependencies +option ( KLU_USE_CHOLMOD "ON (default): use CHOLMOD in KLU. OFF: do not use CHOLMOD in KLU" ON ) + +# UMFPACK options affecting dependencies +option ( UMFPACK_USE_CHOLMOD "ON (default): use CHOLMOD in UMFPACK. OFF: do not use CHOLMOD in UMFPACK" ON ) + +# overwrite BUILD_STATIC_LIBS specifically for GraphBLAS because building the +# library takes a long time +option ( GRAPHBLAS_BUILD_STATIC_LIBS "OFF (default): Do not build static libraries for GraphBLAS project. ON: Use same value of BUILD_STATIC_LIBS for GraphBLAS like in the other projects" OFF ) + +# options to build with libraries installed on the system instead of building +# dependencies automatically +option ( SUITESPARSE_USE_SYSTEM_BTF "ON: use BTF libraries installed on the build system. OFF (default): Automatically build BTF as dependency if needed." OFF ) +option ( SUITESPARSE_USE_SYSTEM_CHOLMOD "ON: use CHOLMOD libraries installed on the build system. OFF (default): Automatically build CHOLMOD as dependency if needed." OFF ) +option ( SUITESPARSE_USE_SYSTEM_AMD "ON: use AMD libraries installed on the build system. OFF (default): Automatically build AMD as dependency if needed." OFF ) +option ( SUITESPARSE_USE_SYSTEM_COLAMD "ON: use COLAMD libraries installed on the build system. OFF (default): Automatically build COLAMD as dependency if needed." OFF ) +option ( SUITESPARSE_USE_SYSTEM_CAMD "ON: use CAMD libraries installed on the build system. OFF (default): Automatically build CAMD as dependency if needed." OFF ) +option ( SUITESPARSE_USE_SYSTEM_CCOLAMD "ON: use CCOLAMD libraries installed on the build system. OFF (default): Automatically build CCOLAMD as dependency if needed." OFF ) +option ( SUITESPARSE_USE_SYSTEM_GRAPHBLAS "ON: use GraphBLAS libraries installed on the build system. OFF (default): Automatically build GraphBLAS as dependency if needed." OFF ) +option ( SUITESPARSE_USE_SYSTEM_SUITESPARSE_CONFIG "ON: use SuiteSparse_config libraries installed on the build system. OFF (default): Automatically build SuiteSparse_config as dependency if needed." OFF ) + +#------------------------------------------------------------------------------- +# global variables +#------------------------------------------------------------------------------- + +# Set to indicate that we are building from a root CMake file. +# That will change the directory layout and (imported) target names (namespace!) +# during the build process. +set ( SUITESPARSE_ROOT_CMAKELISTS ON ) + +#------------------------------------------------------------------------------- +# common SuiteSparse modules +#------------------------------------------------------------------------------- + +set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} + ${CMAKE_SOURCE_DIR}/SuiteSparse_config/cmake_modules ) + +include ( SuiteSparsePolicy ) + +#------------------------------------------------------------------------------- +# check/add project dependencies +#------------------------------------------------------------------------------- + +if ( SUITESPARSE_USE_SYSTEM_GRAPHBLAS ) + list ( REMOVE_ITEM SUITESPARSE_ENABLE_PROJECTS "graphblas" ) + find_package ( GraphBLAS 9.0.1 REQUIRED ) +else ( ) + if ( "lagraph" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + # LAGraph requires GraphBLAS. + if ( NOT "graphblas" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + message ( STATUS "Adding \"graphblas\" to the list of built targets." ) + list ( APPEND SUITESPARSE_ENABLE_PROJECTS "graphblas" ) + endif ( ) + endif ( ) +endif ( ) + +if ( SUITESPARSE_USE_SYSTEM_BTF ) + list ( REMOVE_ITEM SUITESPARSE_ENABLE_PROJECTS "btf" ) + find_package ( BTF 2.3.1 REQUIRED ) +else ( ) + if ( "klu" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + # KLU requires BTF. + if ( NOT "btf" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + message ( STATUS "Adding \"btf\" to the list of built targets." ) + list ( APPEND SUITESPARSE_ENABLE_PROJECTS "btf" ) + endif ( ) + endif ( ) +endif ( ) + +if ( SUITESPARSE_USE_SYSTEM_CHOLMOD ) + list ( REMOVE_ITEM SUITESPARSE_ENABLE_PROJECTS "cholmod" ) + find_package ( CHOLMOD 5.2.0 REQUIRED ) +else ( ) + if ( ( KLU_USE_CHOLMOD AND "klu" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + OR ( UMFPACK_USE_CHOLMOD AND "umfpack" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + OR "spqr" IN_LIST SUITESPARSE_ENABLE_PROJECTS + OR "paru" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + # SPQR and ParU both require CHOLMOD. KLU and UMFPACK can optionally + # use CHOLMOD. Add CHOLMOD to the list of projects, if it has been + # requested by SPQR, ParU, KLU, or UMFPACK, if not already there. + if ( NOT "cholmod" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + message ( STATUS "Adding \"cholmod\" to the list of built targets." ) + list ( APPEND SUITESPARSE_ENABLE_PROJECTS "cholmod" ) + endif ( ) + endif ( ) +endif ( ) + +if ( SUITESPARSE_USE_SYSTEM_AMD ) + list ( REMOVE_ITEM SUITESPARSE_ENABLE_PROJECTS "amd" ) + find_package ( AMD 3.3.1 REQUIRED ) +else ( ) + if ( "cholmod" IN_LIST SUITESPARSE_ENABLE_PROJECTS + OR "ldl" IN_LIST SUITESPARSE_ENABLE_PROJECTS + OR "umfpack" IN_LIST SUITESPARSE_ENABLE_PROJECTS + OR "spex" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + # CHOLMOD, LDL, UMFPACK, and SPEX require AMD. + if ( NOT "amd" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + message ( STATUS "Adding \"amd\" to the list of built targets." ) + list ( APPEND SUITESPARSE_ENABLE_PROJECTS "amd" ) + endif ( ) + endif ( ) +endif ( ) + +if ( SUITESPARSE_USE_SYSTEM_COLAMD ) + list ( REMOVE_ITEM SUITESPARSE_ENABLE_PROJECTS "colamd" ) + find_package ( COLAMD 3.3.2 REQUIRED ) +else ( ) + if ( "cholmod" IN_LIST SUITESPARSE_ENABLE_PROJECTS + OR "spex" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + # CHOLMOD and SPEX require COLAMD. + if ( NOT "colamd" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + message ( STATUS "Adding \"colamd\" to the list of built targets." ) + list ( APPEND SUITESPARSE_ENABLE_PROJECTS "colamd" ) + endif ( ) + endif ( ) +endif ( ) + +if ( SUITESPARSE_USE_SYSTEM_CAMD ) + list ( REMOVE_ITEM SUITESPARSE_ENABLE_PROJECTS "camd" ) + find_package ( CAMD 3.3.1 REQUIRED ) +else ( ) + if ( CHOLMOD_CAMD AND "cholmod" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + # CHOLMOD can optionally use CAMD. + if ( NOT SUITESPARSE_USE_SYSTEM_CAMD AND NOT "camd" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + message ( STATUS "Adding \"camd\" to the list of built targets." ) + list ( APPEND SUITESPARSE_ENABLE_PROJECTS "camd" ) + endif ( ) + endif ( ) +endif ( ) + +if ( SUITESPARSE_USE_SYSTEM_CCOLAMD ) + list ( REMOVE_ITEM SUITESPARSE_ENABLE_PROJECTS "ccolamd" ) + find_package ( CCOLAMD 3.3.2 REQUIRED ) +else ( ) + if ( CHOLMOD_CAMD AND "cholmod" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + # CHOLMOD can optionally use CCOLAMD. + if ( NOT "ccolamd" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + message ( STATUS "Adding \"ccolamd\" to the list of built targets." ) + list ( APPEND SUITESPARSE_ENABLE_PROJECTS "ccolamd" ) + endif ( ) + endif ( ) +endif ( ) + +if ( SUITESPARSE_USE_SYSTEM_SUITESPARSE_CONFIG ) + list ( REMOVE_ITEM SUITESPARSE_ENABLE_PROJECTS "suitesparse_config" ) + find_package ( SuiteSparse_config 7.6.0 REQUIRED ) +else ( ) + if ( "mongoose" IN_LIST SUITESPARSE_ENABLE_PROJECTS + OR "amd" IN_LIST SUITESPARSE_ENABLE_PROJECTS + OR "btf" IN_LIST SUITESPARSE_ENABLE_PROJECTS + OR "camd" IN_LIST SUITESPARSE_ENABLE_PROJECTS + OR "ccolamd" IN_LIST SUITESPARSE_ENABLE_PROJECTS + OR "colamd" IN_LIST SUITESPARSE_ENABLE_PROJECTS + OR "cholmod" IN_LIST SUITESPARSE_ENABLE_PROJECTS + OR "cxsparse" IN_LIST SUITESPARSE_ENABLE_PROJECTS + OR "ldl" IN_LIST SUITESPARSE_ENABLE_PROJECTS + OR "klu" IN_LIST SUITESPARSE_ENABLE_PROJECTS + OR "umfpack" IN_LIST SUITESPARSE_ENABLE_PROJECTS + OR "paru" IN_LIST SUITESPARSE_ENABLE_PROJECTS + OR "rbio" IN_LIST SUITESPARSE_ENABLE_PROJECTS + OR "spqr" IN_LIST SUITESPARSE_ENABLE_PROJECTS + OR "spex" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + # All but CSparse, GraphBLAS, and LAGraph require SuiteSparse_config. + if ( NOT "suitesparse_config" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + message ( STATUS "Adding \"suitesparse_config\" to the list of built targets." ) + list ( APPEND SUITESPARSE_ENABLE_PROJECTS "suitesparse_config" ) + endif ( ) + endif ( ) +endif ( ) + + +if ( CMAKE_VERSION VERSION_LESS 3.24 ) + # work around missing GLOBAL option of find_package in older CMake versions + # If SuiteSparse is included as a sub-project in other projects, they might + # need to manually import the OpenMP targets for older CMake versions, too. + if ( "suitesparse_config" IN_LIST SUITESPARSE_ENABLE_PROJECTS + OR "cholmod" IN_LIST SUITESPARSE_ENABLE_PROJECTS + OR "graphblas" IN_LIST SUITESPARSE_ENABLE_PROJECTS + OR "paru" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + find_package ( OpenMP COMPONENTS C ) + endif ( ) + if ( "paru" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + find_package ( OpenMP COMPONENTS CXX ) + endif ( ) +endif ( ) + + +#------------------------------------------------------------------------------- +# include selected projects +#------------------------------------------------------------------------------- + +if ( "suitesparse_config" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + add_subdirectory ( "SuiteSparse_config" ) + if ( TARGET SuiteSparseConfig ) + add_library ( SuiteSparse::SuiteSparseConfig ALIAS SuiteSparseConfig ) + else ( ) + add_library ( SuiteSparse::SuiteSparseConfig ALIAS SuiteSparseConfig_static ) + endif ( ) + if ( TARGET SuiteSparseConfig_static ) + add_library ( SuiteSparse::SuiteSparseConfig_static ALIAS SuiteSparseConfig_static ) + endif ( ) +endif ( ) + +if ( "mongoose" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + add_subdirectory ( "Mongoose" ) + if ( TARGET Mongoose ) + add_library ( SuiteSparse::Mongoose ALIAS Mongoose ) + else ( ) + add_library ( SuiteSparse::Mongoose ALIAS Mongoose_static ) + endif ( ) + if ( TARGET Mongoose_static ) + add_library ( SuiteSparse::Mongoose_static ALIAS Mongoose_static ) + endif ( ) +endif ( ) + +if ( "amd" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + add_subdirectory ( "AMD" ) + if ( TARGET AMD ) + add_library ( SuiteSparse::AMD ALIAS AMD ) + else ( ) + add_library ( SuiteSparse::AMD ALIAS AMD_static ) + endif ( ) + if ( TARGET AMD_static ) + add_library ( SuiteSparse::AMD_static ALIAS AMD_static ) + endif ( ) +endif ( ) + +if ( "btf" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + add_subdirectory ( "BTF" ) + if ( TARGET BTF ) + add_library ( SuiteSparse::BTF ALIAS BTF ) + else ( ) + add_library ( SuiteSparse::BTF ALIAS BTF_static ) + endif ( ) + if ( TARGET BTF_static ) + add_library ( SuiteSparse::BTF_static ALIAS BTF_static ) + endif ( ) +endif ( ) + +if ( "camd" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + add_subdirectory ( "CAMD" ) + if ( TARGET CAMD ) + add_library ( SuiteSparse::CAMD ALIAS CAMD ) + else ( ) + add_library ( SuiteSparse::CAMD ALIAS CAMD_static ) + endif ( ) + if ( TARGET CAMD_static ) + add_library ( SuiteSparse::CAMD_static ALIAS CAMD_static ) + endif ( ) +endif ( ) + +if ( "ccolamd" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + add_subdirectory ( "CCOLAMD" ) + if ( TARGET CCOLAMD ) + add_library ( SuiteSparse::CCOLAMD ALIAS CCOLAMD ) + else ( ) + add_library ( SuiteSparse::CCOLAMD ALIAS CCOLAMD_static ) + endif ( ) + if ( TARGET CCOLAMD_static ) + add_library ( SuiteSparse::CCOLAMD_static ALIAS CCOLAMD_static ) + endif ( ) +endif ( ) + +if ( "colamd" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + add_subdirectory ( "COLAMD" ) + if ( TARGET COLAMD ) + add_library ( SuiteSparse::COLAMD ALIAS COLAMD ) + else ( ) + add_library ( SuiteSparse::COLAMD ALIAS COLAMD_static ) + endif ( ) + if ( TARGET COLAMD_static ) + add_library ( SuiteSparse::COLAMD_static ALIAS COLAMD_static ) + endif ( ) +endif ( ) + +if ( "cholmod" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + add_subdirectory ( "CHOLMOD" ) + if ( TARGET CHOLMOD ) + add_library ( SuiteSparse::CHOLMOD ALIAS CHOLMOD ) + else ( ) + add_library ( SuiteSparse::CHOLMOD ALIAS CHOLMOD_static ) + endif ( ) + if ( TARGET CHOLMOD_static ) + add_library ( SuiteSparse::CHOLMOD_static ALIAS CHOLMOD_static ) + endif ( ) + if ( TARGET CHOLMOD_CUDA ) + add_library ( SuiteSparse::CHOLMOD_CUDA ALIAS CHOLMOD_CUDA ) + elseif ( TARGET CHOLMOD_CUDA_static ) + add_library ( SuiteSparse::CHOLMOD_CUDA ALIAS CHOLMOD_CUDA_static ) + endif ( ) + if ( TARGET CHOLMOD_CUDA_static ) + add_library ( SuiteSparse::CHOLMOD_CUDA_static ALIAS CHOLMOD_CUDA_static ) + endif ( ) +endif ( ) + +if ( "cxsparse" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + add_subdirectory ( "CXSparse" ) + if ( TARGET CXSparse ) + add_library ( SuiteSparse::CXSparse ALIAS CXSparse ) + else ( ) + add_library ( SuiteSparse::CXSparse ALIAS CXSparse_static ) + endif ( ) + if ( TARGET CXSparse_static ) + add_library ( SuiteSparse::CXSparse_static ALIAS CXSparse_static ) + endif ( ) +endif ( ) + +if ( "ldl" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + add_subdirectory ( "LDL" ) + if ( TARGET LDL ) + add_library ( SuiteSparse::LDL ALIAS LDL ) + else ( ) + add_library ( SuiteSparse::LDL ALIAS LDL_static ) + endif ( ) + if ( TARGET LDL_static ) + add_library ( SuiteSparse::LDL_static ALIAS LDL_static ) + endif ( ) +endif ( ) + +if ( "klu" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + add_subdirectory ( "KLU" ) + if ( TARGET KLU ) + add_library ( SuiteSparse::KLU ALIAS KLU ) + else ( ) + add_library ( SuiteSparse::KLU ALIAS KLU_static ) + endif ( ) + if ( TARGET KLU_static ) + add_library ( SuiteSparse::KLU_static ALIAS KLU_static ) + endif ( ) +endif ( ) + +if ( "umfpack" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + add_subdirectory ( "UMFPACK" ) + if ( TARGET UMFPACK ) + add_library ( SuiteSparse::UMFPACK ALIAS UMFPACK ) + else ( ) + add_library ( SuiteSparse::UMFPACK ALIAS UMFPACK_static ) + endif ( ) + if ( TARGET UMFPACK_static ) + add_library ( SuiteSparse::UMFPACK_static ALIAS UMFPACK_static ) + endif ( ) +endif ( ) + +if ( "paru" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + add_subdirectory ( "ParU" ) + if ( TARGET ParU ) + add_library ( SuiteSparse::ParU ALIAS ParU ) + else ( ) + add_library ( SuiteSparse::ParU ALIAS ParU_static ) + endif ( ) + if ( TARGET ParU_static ) + add_library ( SuiteSparse::ParU_static ALIAS ParU_static ) + endif ( ) +endif ( ) + +if ( "rbio" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + add_subdirectory ( "RBio" ) + if ( TARGET RBio ) + add_library ( SuiteSparse::RBio ALIAS RBio ) + else ( ) + add_library ( SuiteSparse::RBio ALIAS RBio_static ) + endif ( ) + if ( TARGET RBio_static ) + add_library ( SuiteSparse::RBio_static ALIAS RBio_static ) + endif ( ) +endif ( ) + +if ( "spqr" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + add_subdirectory ( "SPQR" ) + if ( TARGET SPQR ) + add_library ( SuiteSparse::SPQR ALIAS SPQR ) + else ( ) + add_library ( SuiteSparse::SPQR ALIAS SPQR_static ) + endif ( ) + if ( TARGET SPQR_static ) + add_library ( SuiteSparse::SPQR_static ALIAS SPQR_static ) + endif ( ) +endif ( ) + +if ( "spex" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + add_subdirectory ( "SPEX" ) + if ( TARGET SPEX ) + add_library ( SuiteSparse::SPEX ALIAS SPEX ) + else ( ) + add_library ( SuiteSparse::SPEX ALIAS SPEX_static ) + endif ( ) + if ( TARGET SPEX_static ) + add_library ( SuiteSparse::SPEX_static ALIAS SPEX_static ) + endif ( ) +endif ( ) + +if ( "graphblas" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + add_subdirectory ( "GraphBLAS" ) + if ( TARGET GraphBLAS ) + add_library ( SuiteSparse::GraphBLAS ALIAS GraphBLAS ) + else ( ) + add_library ( SuiteSparse::GraphBLAS ALIAS GraphBLAS_static ) + endif ( ) + if ( TARGET GraphBLAS_static ) + add_library ( SuiteSparse::GraphBLAS_static ALIAS GraphBLAS_static ) + endif ( ) +endif ( ) + +if ( "lagraph" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + add_subdirectory ( "LAGraph" ) + if ( TARGET LAGraph ) + add_library ( SuiteSparse::LAGraph ALIAS LAGraph ) + else ( ) + add_library ( SuiteSparse::LAGraph ALIAS LAGraph_static ) + endif ( ) + if ( TARGET LAGraph_static ) + add_library ( SuiteSparse::LAGraph_static ALIAS LAGraph_static ) + endif ( ) +endif ( ) + +if ( "csparse" IN_LIST SUITESPARSE_ENABLE_PROJECTS ) + add_subdirectory ( "CSparse" ) + if ( TARGET CSparse ) + add_library ( SuiteSparse::CSparse ALIAS CSparse ) + else ( ) + add_library ( SuiteSparse::CSparse ALIAS CSparse_static ) + endif ( ) + if ( TARGET CSparse_static ) + add_library ( SuiteSparse::CSparse_static ALIAS CSparse_static ) + endif ( ) +endif ( ) + +#------------------------------------------------------------------------------- +# report status +#------------------------------------------------------------------------------- + +include ( SuiteSparseReport ) + +#------------------------------------------------------------------------------- +# enable testing facilities +#------------------------------------------------------------------------------- + +# Currently, only LAGraph, Mongoose, and CHOLMOD have ctests. + +# FIXME: convert more of the existing demos to ctests. + +# Most packages have a ./Tcov folder with a full statement coverage test, +# but these are not imported into cmake yet. + +# Most packages also have a ./Demo folder, with shorter examples. These would +# be nice to add as quick ctests. + +# CHOLMOD/Tcov takes about 20 minutes to run. It is also a full coverage +# test of AMD, CAMD, COLAMD, and CCOLAMD, however. The current CHOLMOD +# ctest is based on a few ./Demo programs. It's fast but not a full coverate +# test. + +# The CSparse/CXSparse Tcov tests are very fast and would be good candidates to +# add. + +include ( CTest ) + +#------------------------------------------------------------------------------- +# rule to remove all files in build directory +#------------------------------------------------------------------------------- + +file ( GLOB SUITESPARSE_BUILT_FILES ${PROJECT_BINARY_DIR}/* ) +file ( REAL_PATH ${PROJECT_SOURCE_DIR} _real_project_source_dir ) +file ( REAL_PATH ${PROJECT_BINARY_DIR} _real_project_binary_dir ) +if ( _real_project_source_dir STREQUAL _real_project_binary_dir ) + add_custom_target ( purge + COMMENT "The target 'purge' is a no-op for in-tree builds. Consider building out of the source tree." ) +else ( ) + add_custom_target ( purge + COMMAND ${CMAKE_COMMAND} -E echo "Removing files..." + COMMAND ${CMAKE_COMMAND} -E rm -rf ${SUITESPARSE_BUILT_FILES} + COMMENT "Purge all files in the build tree" ) +endif ( ) diff --git a/ThirdParty/SuiteSparse/COLAMD/CMakeLists.txt b/ThirdParty/SuiteSparse/COLAMD/CMakeLists.txt index 0f9603c950..39ac72edac 100644 --- a/ThirdParty/SuiteSparse/COLAMD/CMakeLists.txt +++ b/ThirdParty/SuiteSparse/COLAMD/CMakeLists.txt @@ -2,19 +2,19 @@ # SuiteSparse/COLAMD/CMakeLists.txt: cmake for COLAMD #------------------------------------------------------------------------------- -# Copyright (c) 1998-2023, Timothy A. Davis. All Rights Reserved. +# Copyright (c) 1998-2024, Timothy A. Davis. All Rights Reserved. # SPDX-License-Identifier: BSD-3-clause #------------------------------------------------------------------------------- # get the version #------------------------------------------------------------------------------- -cmake_minimum_required ( VERSION 3.19 ) +cmake_minimum_required ( VERSION 3.22 ) -set ( COLAMD_DATE "Jan 17, 2023" ) -set ( COLAMD_VERSION_MAJOR 3 ) -set ( COLAMD_VERSION_MINOR 0 ) -set ( COLAMD_VERSION_SUB 3 ) +set ( COLAMD_DATE "Jan 20, 2024" ) +set ( COLAMD_VERSION_MAJOR 3 CACHE STRING "" FORCE ) +set ( COLAMD_VERSION_MINOR 3 CACHE STRING "" FORCE ) +set ( COLAMD_VERSION_SUB 2 CACHE STRING "" FORCE ) message ( STATUS "Building COLAMD version: v" ${COLAMD_VERSION_MAJOR}. @@ -22,28 +22,33 @@ message ( STATUS "Building COLAMD version: v" ${COLAMD_VERSION_SUB} " (" ${COLAMD_DATE} ")" ) #------------------------------------------------------------------------------- -# SuiteSparse policies +# define the project #------------------------------------------------------------------------------- -set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} - ${CMAKE_SOURCE_DIR}/cmake_modules - ${CMAKE_SOURCE_DIR}/../SuiteSparse_config/cmake_modules ) - -include ( SuiteSparsePolicy ) +project ( COLAMD + VERSION "${COLAMD_VERSION_MAJOR}.${COLAMD_VERSION_MINOR}.${COLAMD_VERSION_SUB}" + LANGUAGES C ) #------------------------------------------------------------------------------- -# define the project +# SuiteSparse policies #------------------------------------------------------------------------------- -project ( colamd - VERSION "${COLAMD_VERSION_MAJOR}.${COLAMD_VERSION_MINOR}.${COLAMD_VERSION_SUB}" - LANGUAGES C ) +set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} + ${PROJECT_SOURCE_DIR}/../SuiteSparse_config/cmake_modules ) + +include ( SuiteSparsePolicy ) #------------------------------------------------------------------------------- # find library dependencies #------------------------------------------------------------------------------- -find_package ( SuiteSparse_config 7.0.0 REQUIRED ) +if ( NOT SUITESPARSE_ROOT_CMAKELISTS ) + find_package ( SuiteSparse_config 7.6.0 + PATHS ${CMAKE_SOURCE_DIR}/../SuiteSparse_config/build NO_DEFAULT_PATH ) + if ( NOT TARGET SuiteSparse::SuiteSparseConfig ) + find_package ( SuiteSparse_config 7.6.0 REQUIRED ) + endif ( ) +endif ( ) #------------------------------------------------------------------------------- # configure files @@ -57,7 +62,7 @@ configure_file ( "Config/colamd.h.in" # include directories #------------------------------------------------------------------------------- -include_directories ( Source Include ${SUITESPARSE_CONFIG_INCLUDE_DIR} ) +include_directories ( Source Include ) #------------------------------------------------------------------------------- # dynamic colamd library properties @@ -65,48 +70,79 @@ include_directories ( Source Include ${SUITESPARSE_CONFIG_INCLUDE_DIR} ) file ( GLOB COLAMD_SOURCES "Source/*.c" ) -add_library ( colamd SHARED ${COLAMD_SOURCES} ) +if ( BUILD_SHARED_LIBS ) + add_library ( COLAMD SHARED ${COLAMD_SOURCES} ) + + set_target_properties ( COLAMD PROPERTIES + VERSION ${COLAMD_VERSION_MAJOR}.${COLAMD_VERSION_MINOR}.${COLAMD_VERSION_SUB} + C_STANDARD 11 + C_STANDARD_REQUIRED ON + OUTPUT_NAME colamd + SOVERSION ${COLAMD_VERSION_MAJOR} + PUBLIC_HEADER "Include/colamd.h" + WINDOWS_EXPORT_ALL_SYMBOLS ON ) + + if ( ${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.25" ) + set_target_properties ( COLAMD PROPERTIES EXPORT_NO_SYSTEM ON ) + endif ( ) -set_target_properties ( colamd PROPERTIES - VERSION ${COLAMD_VERSION_MAJOR}.${COLAMD_VERSION_MINOR}.${COLAMD_VERSION_SUB} - C_STANDARD_REQUIRED 11 - SOVERSION ${COLAMD_VERSION_MAJOR} - PUBLIC_HEADER "Include/colamd.h" - WINDOWS_EXPORT_ALL_SYMBOLS ON ) + target_include_directories ( COLAMD + INTERFACE $ + $ ) +endif ( ) #------------------------------------------------------------------------------- # static colamd library properties #------------------------------------------------------------------------------- -if ( NOT NSTATIC ) - add_library ( colamd_static STATIC ${COLAMD_SOURCES} ) +if ( BUILD_STATIC_LIBS ) + add_library ( COLAMD_static STATIC ${COLAMD_SOURCES} ) - set_target_properties ( colamd_static PROPERTIES - VERSION ${COLAMD_VERSION_MAJOR}.${COLAMD_VERSION_MINOR}.${COLAMD_VERSION_SUB} + set_target_properties ( COLAMD_static PROPERTIES OUTPUT_NAME colamd - C_STANDARD_REQUIRED 11 - SOVERSION ${COLAMD_VERSION_MAJOR} ) + C_STANDARD 11 + C_STANDARD_REQUIRED ON + PUBLIC_HEADER "Include/colamd.h" ) if ( MSVC ) - set_target_properties ( colamd_static PROPERTIES + set_target_properties ( COLAMD_static PROPERTIES OUTPUT_NAME colamd_static ) endif ( ) + + if ( ${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.25" ) + set_target_properties ( COLAMD_static PROPERTIES EXPORT_NO_SYSTEM ON ) + endif ( ) + + target_include_directories ( COLAMD_static + INTERFACE $ + $ ) endif ( ) #------------------------------------------------------------------------------- # add the library dependencies #------------------------------------------------------------------------------- -target_link_libraries ( colamd PUBLIC ${SUITESPARSE_CONFIG_LIBRARY} ) -if ( NOT NSTATIC ) - target_link_libraries ( colamd_static PUBLIC ${SUITESPARSE_CONFIG_STATIC} ) +if ( BUILD_SHARED_LIBS ) + target_link_libraries ( COLAMD PRIVATE SuiteSparse::SuiteSparseConfig ) + target_include_directories ( COLAMD PUBLIC + "$" ) +endif ( ) +if ( BUILD_STATIC_LIBS ) + if ( TARGET SuiteSparse::SuiteSparseConfig_static ) + target_link_libraries ( COLAMD_static PUBLIC SuiteSparse::SuiteSparseConfig_static ) + else ( ) + target_link_libraries ( COLAMD_static PUBLIC SuiteSparse::SuiteSparseConfig ) + endif ( ) endif ( ) # libm: if ( NOT WIN32 ) - target_link_libraries ( colamd PUBLIC m ) - if ( NOT NSTATIC ) - target_link_libraries ( colamd_static PUBLIC m ) + if ( BUILD_SHARED_LIBS ) + target_link_libraries ( COLAMD PRIVATE m ) + endif ( ) + if ( BUILD_STATIC_LIBS ) + set ( COLAMD_STATIC_LIBS "${COLAMD_STATIC_LIBS} -lm" ) + target_link_libraries ( COLAMD_static PUBLIC m ) endif ( ) endif ( ) @@ -114,25 +150,99 @@ endif ( ) # COLAMD installation location #------------------------------------------------------------------------------- -install ( TARGETS colamd - LIBRARY DESTINATION ${SUITESPARSE_LIBDIR} - ARCHIVE DESTINATION ${SUITESPARSE_LIBDIR} - RUNTIME DESTINATION ${SUITESPARSE_BINDIR} - PUBLIC_HEADER DESTINATION ${SUITESPARSE_INCLUDEDIR} ) -install ( FILES ${CMAKE_SOURCE_DIR}/cmake_modules/FindCOLAMD.cmake - DESTINATION ${SUITESPARSE_LIBDIR}/cmake/SuiteSparse - COMPONENT Development ) -if ( NOT NSTATIC ) - install ( TARGETS colamd_static - ARCHIVE DESTINATION ${SUITESPARSE_LIBDIR} ) +include ( CMakePackageConfigHelpers ) + +if ( BUILD_SHARED_LIBS ) + install ( TARGETS COLAMD + EXPORT COLAMDTargets + LIBRARY DESTINATION ${SUITESPARSE_LIBDIR} + ARCHIVE DESTINATION ${SUITESPARSE_LIBDIR} + RUNTIME DESTINATION ${SUITESPARSE_BINDIR} + PUBLIC_HEADER DESTINATION ${SUITESPARSE_INCLUDEDIR} ) +endif ( ) +if ( BUILD_STATIC_LIBS ) + install ( TARGETS COLAMD_static + EXPORT COLAMDTargets + ARCHIVE DESTINATION ${SUITESPARSE_LIBDIR} + PUBLIC_HEADER DESTINATION ${SUITESPARSE_INCLUDEDIR} ) +endif ( ) + +# create (temporary) export target file during build +export ( EXPORT COLAMDTargets + NAMESPACE SuiteSparse:: + FILE ${CMAKE_CURRENT_BINARY_DIR}/COLAMDTargets.cmake ) + +# install export target, config and version files for find_package +install ( EXPORT COLAMDTargets + NAMESPACE SuiteSparse:: + DESTINATION ${SUITESPARSE_PKGFILEDIR}/cmake/COLAMD ) + +# generate config file to be used in common build tree +set ( SUITESPARSE_IN_BUILD_TREE ON ) +configure_package_config_file ( + Config/COLAMDConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/COLAMDConfig.cmake + INSTALL_DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/COLAMDConfig.cmake ) + +# generate config file to be installed +set ( SUITESPARSE_IN_BUILD_TREE OFF ) +configure_package_config_file ( + Config/COLAMDConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/target/COLAMDConfig.cmake + INSTALL_DESTINATION ${SUITESPARSE_PKGFILEDIR}/cmake/COLAMD ) + +write_basic_package_version_file ( + ${CMAKE_CURRENT_BINARY_DIR}/COLAMDConfigVersion.cmake + COMPATIBILITY SameMajorVersion ) + +install ( FILES + ${CMAKE_CURRENT_BINARY_DIR}/target/COLAMDConfig.cmake + ${CMAKE_CURRENT_BINARY_DIR}/COLAMDConfigVersion.cmake + DESTINATION ${SUITESPARSE_PKGFILEDIR}/cmake/COLAMD ) + +#------------------------------------------------------------------------------- +# create pkg-config file +#------------------------------------------------------------------------------- + +if ( NOT MSVC ) + set ( prefix "${CMAKE_INSTALL_PREFIX}" ) + set ( exec_prefix "\${prefix}" ) + cmake_path ( IS_ABSOLUTE SUITESPARSE_LIBDIR SUITESPARSE_LIBDIR_IS_ABSOLUTE ) + if (SUITESPARSE_LIBDIR_IS_ABSOLUTE) + set ( libdir "${SUITESPARSE_LIBDIR}") + else ( ) + set ( libdir "\${exec_prefix}/${SUITESPARSE_LIBDIR}") + endif ( ) + cmake_path ( IS_ABSOLUTE SUITESPARSE_INCLUDEDIR SUITESPARSE_INCLUDEDIR_IS_ABSOLUTE ) + if (SUITESPARSE_INCLUDEDIR_IS_ABSOLUTE) + set ( includedir "${SUITESPARSE_INCLUDEDIR}") + else ( ) + set ( includedir "\${prefix}/${SUITESPARSE_INCLUDEDIR}") + endif ( ) + if ( BUILD_SHARED_LIBS ) + set ( SUITESPARSE_LIB_BASE_NAME $ ) + else ( ) + set ( SUITESPARSE_LIB_BASE_NAME $ ) + endif ( ) + configure_file ( + Config/COLAMD.pc.in + COLAMD.pc.out + @ONLY + NEWLINE_STYLE LF ) + file ( GENERATE + OUTPUT COLAMD.pc + INPUT ${CMAKE_CURRENT_BINARY_DIR}/COLAMD.pc.out + NEWLINE_STYLE LF ) + install ( FILES + ${CMAKE_CURRENT_BINARY_DIR}/COLAMD.pc + DESTINATION ${SUITESPARSE_PKGFILEDIR}/pkgconfig ) endif ( ) #------------------------------------------------------------------------------- # Demo library and programs #------------------------------------------------------------------------------- -option ( DEMO "ON: Build the demo programs. OFF (default): do not build the demo programs." off ) -if ( DEMO ) +if ( SUITESPARSE_DEMOS ) #--------------------------------------------------------------------------- # demo library @@ -148,8 +258,13 @@ if ( DEMO ) add_executable ( colamd_l_example "Demo/colamd_l_example.c" ) # Libraries required for Demo programs - target_link_libraries ( colamd_example PUBLIC colamd ) - target_link_libraries ( colamd_l_example PUBLIC colamd ) + if ( BUILD_SHARED_LIBS ) + target_link_libraries ( colamd_example PUBLIC COLAMD ) + target_link_libraries ( colamd_l_example PUBLIC COLAMD ) + else ( ) + target_link_libraries ( colamd_example PUBLIC COLAMD_static ) + target_link_libraries ( colamd_l_example PUBLIC COLAMD_static ) + endif ( ) else ( ) @@ -162,4 +277,3 @@ endif ( ) #------------------------------------------------------------------------------- include ( SuiteSparseReport ) - diff --git a/ThirdParty/SuiteSparse/COLAMD/Config/COLAMD.pc.in b/ThirdParty/SuiteSparse/COLAMD/Config/COLAMD.pc.in new file mode 100644 index 0000000000..5444c608f8 --- /dev/null +++ b/ThirdParty/SuiteSparse/COLAMD/Config/COLAMD.pc.in @@ -0,0 +1,17 @@ +# COLAMD, Copyright (c) 1998-2023, Timothy A. Davis. +# All Rights Reserved. +# SPDX-License-Identifier: BSD-3-Clause + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: COLAMD +URL: https://github.com/DrTimothyAldenDavis/SuiteSparse +Description: Routines for column approximate minimum degree ordering algorithm in SuiteSparse +Version: @COLAMD_VERSION_MAJOR@.@COLAMD_VERSION_MINOR@.@COLAMD_VERSION_SUB@ +Requires.private: SuiteSparse_config +Libs: -L${libdir} -l@SUITESPARSE_LIB_BASE_NAME@ +Libs.private: @COLAMD_STATIC_LIBS@ +Cflags: -I${includedir} diff --git a/ThirdParty/SuiteSparse/COLAMD/Config/COLAMDConfig.cmake.in b/ThirdParty/SuiteSparse/COLAMD/Config/COLAMDConfig.cmake.in new file mode 100644 index 0000000000..7bb4701f49 --- /dev/null +++ b/ThirdParty/SuiteSparse/COLAMD/Config/COLAMDConfig.cmake.in @@ -0,0 +1,152 @@ +#------------------------------------------------------------------------------- +# SuiteSparse/COLAMD/cmake_modules/COLAMDConfig.cmake +#------------------------------------------------------------------------------- + +# The following copyright and license applies to just this file only, not to +# the library itself: +# COLAMDConfig.cmake, Copyright (c) 2023, Timothy A. Davis. All Rights Reserved. +# SPDX-License-Identifier: BSD-3-clause + +#------------------------------------------------------------------------------- + +# Finds the COLAMD include file and compiled library. +# The following targets are defined: +# SuiteSparse::COLAMD - for the shared library (if available) +# SuiteSparse::COLAMD_static - for the static library (if available) + +# For backward compatibility the following variables are set: + +# COLAMD_INCLUDE_DIR - where to find colamd.h +# COLAMD_LIBRARY - dynamic COLAMD library +# COLAMD_STATIC - static COLAMD library +# COLAMD_LIBRARIES - libraries when using COLAMD +# COLAMD_FOUND - true if COLAMD found + +# Set ``CMAKE_MODULE_PATH`` to the parent folder where this module file is +# installed. + +#------------------------------------------------------------------------------- + +@PACKAGE_INIT@ + +set ( COLAMD_DATE "@COLAMD_DATE@" ) +set ( COLAMD_VERSION_MAJOR @COLAMD_VERSION_MAJOR@ ) +set ( COLAMD_VERSION_MINOR @COLAMD_VERSION_MINOR@ ) +set ( COLAMD_VERSION_PATCH @COLAMD_VERSION_SUB@ ) +set ( COLAMD_VERSION "@COLAMD_VERSION_MAJOR@.@COLAMD_VERSION_MINOR@.@COLAMD_VERSION_SUB@" ) + +# Check for dependent targets +include ( CMakeFindDependencyMacro ) + +# Look for SuiteSparse_config target +if ( @SUITESPARSE_IN_BUILD_TREE@ ) + if ( NOT TARGET SuiteSparse::SuiteSparseConfig ) + # First check in a common build tree + find_dependency ( SuiteSparse_config @SUITESPARSE_CONFIG_VERSION_MAJOR@.@SUITESPARSE_CONFIG_VERSION_MINOR@ + PATHS ${CMAKE_SOURCE_DIR}/../SuiteSparse_config/build NO_DEFAULT_PATH ) + # Then, check in the currently active CMAKE_MODULE_PATH + if ( NOT SuiteSparse_config_FOUND ) + find_dependency ( SuiteSparse_config @SUITESPARSE_CONFIG_VERSION_MAJOR@.@SUITESPARSE_CONFIG_VERSION_MINOR@ ) + endif ( ) + endif ( ) +else ( ) + if ( NOT TARGET SuiteSparse::SuiteSparseConfig ) + find_dependency ( SuiteSparse_config @SUITESPARSE_CONFIG_VERSION_MAJOR@.@SUITESPARSE_CONFIG_VERSION_MINOR@ ) + endif ( ) +endif ( ) +if ( NOT SuiteSparse_config_FOUND ) + set ( COLAMD_FOUND OFF ) + return ( ) +endif ( ) + + +# Import target +include ( ${CMAKE_CURRENT_LIST_DIR}/COLAMDTargets.cmake ) + +# The following is only for backward compatibility with FindCOLAMD. + +set ( _target_shared SuiteSparse::COLAMD ) +set ( _target_static SuiteSparse::COLAMD_static ) +set ( _var_prefix "COLAMD" ) + +if ( NOT @BUILD_SHARED_LIBS@ AND NOT TARGET ${_target_shared} ) + # make sure there is always an import target without suffix ) + add_library ( ${_target_shared} ALIAS ${_target_static} ) +endif ( ) + +get_target_property ( ${_var_prefix}_INCLUDE_DIR ${_target_shared} INTERFACE_INCLUDE_DIRECTORIES ) +if ( ${_var_prefix}_INCLUDE_DIR ) + # First item in SuiteSparse targets contains the "main" header directory. + list ( GET ${_var_prefix}_INCLUDE_DIR 0 ${_var_prefix}_INCLUDE_DIR ) +endif ( ) +get_target_property ( ${_var_prefix}_LIBRARY ${_target_shared} IMPORTED_IMPLIB ) +if ( NOT ${_var_prefix}_LIBRARY ) + get_target_property ( _library_chk ${_target_shared} IMPORTED_LOCATION ) + if ( EXISTS ${_library_chk} ) + set ( ${_var_prefix}_LIBRARY ${_library_chk} ) + endif ( ) +endif ( ) +if ( TARGET ${_target_static} ) + get_target_property ( ${_var_prefix}_STATIC ${_target_static} IMPORTED_LOCATION ) +endif ( ) + +# Check for most common build types +set ( _config_types "Debug" "Release" "RelWithDebInfo" "MinSizeRel" "None" ) + +get_property ( _isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG ) +if ( _isMultiConfig ) + # For multi-configuration generators (e.g., Visual Studio), prefer those + # configurations. + list ( PREPEND _config_types ${CMAKE_CONFIGURATION_TYPES} ) +else ( ) + # For single-configuration generators, prefer the current configuration. + list ( PREPEND _config_types ${CMAKE_BUILD_TYPE} ) +endif ( ) + +list ( REMOVE_DUPLICATES _config_types ) + +foreach ( _config ${_config_types} ) + string ( TOUPPER ${_config} _uc_config ) + if ( NOT ${_var_prefix}_LIBRARY ) + get_target_property ( _library_chk ${_target_shared} + IMPORTED_IMPLIB_${_uc_config} ) + if ( EXISTS ${_library_chk} ) + set ( ${_var_prefix}_LIBRARY ${_library_chk} ) + endif ( ) + endif ( ) + if ( NOT ${_var_prefix}_LIBRARY ) + get_target_property ( _library_chk ${_target_shared} + IMPORTED_LOCATION_${_uc_config} ) + if ( EXISTS ${_library_chk} ) + set ( ${_var_prefix}_LIBRARY ${_library_chk} ) + endif ( ) + endif ( ) + if ( TARGET ${_target_static} AND NOT ${_var_prefix}_STATIC ) + get_target_property ( _library_chk ${_target_static} + IMPORTED_LOCATION_${_uc_config} ) + if ( EXISTS ${_library_chk} ) + set ( ${_var_prefix}_STATIC ${_library_chk} ) + endif ( ) + endif ( ) +endforeach ( ) + +set ( COLAMD_LIBRARIES ${COLAMD_LIBRARY} ) + +macro ( suitesparse_check_exist _var _files ) + # ignore generator expressions + string ( GENEX_STRIP "${_files}" _files2 ) + + foreach ( _file ${_files2} ) + if ( NOT EXISTS "${_file}" ) + message ( FATAL_ERROR "File or directory ${_file} referenced by variable ${_var} does not exist!" ) + endif ( ) + endforeach () +endmacro ( ) + +suitesparse_check_exist ( COLAMD_INCLUDE_DIR ${COLAMD_INCLUDE_DIR} ) +suitesparse_check_exist ( COLAMD_LIBRARY ${COLAMD_LIBRARY} ) + +message ( STATUS "COLAMD version: ${COLAMD_VERSION}" ) +message ( STATUS "COLAMD include: ${COLAMD_INCLUDE_DIR}" ) +message ( STATUS "COLAMD library: ${COLAMD_LIBRARY}" ) +message ( STATUS "COLAMD static: ${COLAMD_STATIC}" ) diff --git a/ThirdParty/SuiteSparse/COLAMD/Config/colamd.h.in b/ThirdParty/SuiteSparse/COLAMD/Config/colamd.h.in index fb0450ac45..2d9a0c07f7 100644 --- a/ThirdParty/SuiteSparse/COLAMD/Config/colamd.h.in +++ b/ThirdParty/SuiteSparse/COLAMD/Config/colamd.h.in @@ -1,8 +1,8 @@ //------------------------------------------------------------------------------ -// COLAMD/Source/colamd.h: include file for COLAMD +// COLAMD/Include/colamd.h: include file for COLAMD //------------------------------------------------------------------------------ -// COLAMD, Copyright (c) 1998-2022, Timothy A. Davis and Stefan Larimore, +// COLAMD, Copyright (c) 1998-2024, Timothy A. Davis and Stefan Larimore, // All Rights Reserved. // SPDX-License-Identifier: BSD-3-clause @@ -37,11 +37,6 @@ #ifndef COLAMD_H #define COLAMD_H -/* make it easy for C++ programs to include COLAMD */ -#ifdef __cplusplus -extern "C" { -#endif - /* ========================================================================== */ /* === Include files ======================================================== */ /* ========================================================================== */ @@ -75,9 +70,14 @@ extern "C" { #define COLAMD_SUB_VERSION @COLAMD_VERSION_MINOR@ #define COLAMD_SUBSUB_VERSION @COLAMD_VERSION_SUB@ -#define COLAMD_VERSION_CODE(main,sub) ((main) * 1000 + (sub)) -#define COLAMD_VERSION \ - COLAMD_VERSION_CODE(COLAMD_MAIN_VERSION,COLAMD_SUB_VERSION) +#define COLAMD_VERSION_CODE(main,sub) SUITESPARSE_VER_CODE(main,sub) +#define COLAMD_VERSION COLAMD_VERSION_CODE(@COLAMD_VERSION_MAJOR@,@COLAMD_VERSION_MINOR@) + +#define COLAMD__VERSION SUITESPARSE__VERCODE(@COLAMD_VERSION_MAJOR@,@COLAMD_VERSION_MINOR@,@COLAMD_VERSION_SUB@) +#if !defined (SUITESPARSE__VERSION) || \ + (SUITESPARSE__VERSION < SUITESPARSE__VERCODE(7,6,0)) +#error "COLAMD @COLAMD_VERSION_MAJOR@.@COLAMD_VERSION_MINOR@.@COLAMD_VERSION_SUB@ requires SuiteSparse_config 7.6.0 or later" +#endif /* ========================================================================== */ /* === Knob and statistics definitions ====================================== */ @@ -129,6 +129,11 @@ extern "C" { /* === Prototypes of user-callable routines ================================= */ /* ========================================================================== */ +/* make it easy for C++ programs to include COLAMD */ +#ifdef __cplusplus +extern "C" { +#endif + size_t colamd_recommended /* returns recommended value of Alen, */ /* or 0 if input arguments are erroneous */ ( @@ -229,6 +234,8 @@ void symamd_l_report int64_t stats [COLAMD_STATS] ) ; +void colamd_version (int version [3]) ; + #ifdef __cplusplus } #endif diff --git a/ThirdParty/SuiteSparse/COLAMD/Doc/ChangeLog b/ThirdParty/SuiteSparse/COLAMD/Doc/ChangeLog index f7a3dcf3c8..9904dacaf3 100644 --- a/ThirdParty/SuiteSparse/COLAMD/Doc/ChangeLog +++ b/ThirdParty/SuiteSparse/COLAMD/Doc/ChangeLog @@ -1,3 +1,28 @@ +Jan 20, 2024: version 3.3.2 + + * minor updates to build system + +Jan 10, 2024: version 3.3.1 + + * minor updates to build system + +Dec 30, 2023: version 3.3.0 + + * major change to build system: by Markus Mützel + * colamd_version: added to return version of COLAMD + +Sept 18, 2023: version 3.2.1 + + * cmake update: add "None" build type, from Antonio Rojas, for Arch Linux + +Sept 8, 2023: version 3.2.0 + + * cmake updates: SuiteSparse:: namespace by Markus Muetzel + +June 16, 2023: version 3.0.4 + + * cmake build system updates: update by Markus Muetzel + Jan 17, 2023: version 3.0.3 * SuiteSparse_config: now v7.0.0 diff --git a/ThirdParty/SuiteSparse/COLAMD/Include/colamd.h b/ThirdParty/SuiteSparse/COLAMD/Include/colamd.h index f258ef68ec..93c2c08eba 100644 --- a/ThirdParty/SuiteSparse/COLAMD/Include/colamd.h +++ b/ThirdParty/SuiteSparse/COLAMD/Include/colamd.h @@ -1,8 +1,8 @@ //------------------------------------------------------------------------------ -// COLAMD/Source/colamd.h: include file for COLAMD +// COLAMD/Include/colamd.h: include file for COLAMD //------------------------------------------------------------------------------ -// COLAMD, Copyright (c) 1998-2022, Timothy A. Davis and Stefan Larimore, +// COLAMD, Copyright (c) 1998-2024, Timothy A. Davis and Stefan Larimore, // All Rights Reserved. // SPDX-License-Identifier: BSD-3-clause @@ -37,11 +37,6 @@ #ifndef COLAMD_H #define COLAMD_H -/* make it easy for C++ programs to include COLAMD */ -#ifdef __cplusplus -extern "C" { -#endif - /* ========================================================================== */ /* === Include files ======================================================== */ /* ========================================================================== */ @@ -70,14 +65,19 @@ extern "C" { * Versions 2.3 and earlier of COLAMD do not include a #define'd version number. */ -#define COLAMD_DATE "Jan 17, 2023" +#define COLAMD_DATE "Jan 20, 2024" #define COLAMD_MAIN_VERSION 3 -#define COLAMD_SUB_VERSION 0 -#define COLAMD_SUBSUB_VERSION 3 +#define COLAMD_SUB_VERSION 3 +#define COLAMD_SUBSUB_VERSION 2 + +#define COLAMD_VERSION_CODE(main,sub) SUITESPARSE_VER_CODE(main,sub) +#define COLAMD_VERSION COLAMD_VERSION_CODE(3,3) -#define COLAMD_VERSION_CODE(main,sub) ((main) * 1000 + (sub)) -#define COLAMD_VERSION \ - COLAMD_VERSION_CODE(COLAMD_MAIN_VERSION,COLAMD_SUB_VERSION) +#define COLAMD__VERSION SUITESPARSE__VERCODE(3,3,2) +#if !defined (SUITESPARSE__VERSION) || \ + (SUITESPARSE__VERSION < SUITESPARSE__VERCODE(7,6,0)) +#error "COLAMD 3.3.2 requires SuiteSparse_config 7.6.0 or later" +#endif /* ========================================================================== */ /* === Knob and statistics definitions ====================================== */ @@ -129,6 +129,11 @@ extern "C" { /* === Prototypes of user-callable routines ================================= */ /* ========================================================================== */ +/* make it easy for C++ programs to include COLAMD */ +#ifdef __cplusplus +extern "C" { +#endif + size_t colamd_recommended /* returns recommended value of Alen, */ /* or 0 if input arguments are erroneous */ ( @@ -229,6 +234,8 @@ void symamd_l_report int64_t stats [COLAMD_STATS] ) ; +void colamd_version (int version [3]) ; + #ifdef __cplusplus } #endif diff --git a/ThirdParty/SuiteSparse/COLAMD/Makefile b/ThirdParty/SuiteSparse/COLAMD/Makefile index 2c7de9ee43..787ee26f3b 100644 --- a/ThirdParty/SuiteSparse/COLAMD/Makefile +++ b/ThirdParty/SuiteSparse/COLAMD/Makefile @@ -36,27 +36,27 @@ default: library # default is to install only in /usr/local library: - ( cd build && cmake $(CMAKE_OPTIONS) .. && cmake --build . -j${JOBS} ) + ( cd build && cmake $(CMAKE_OPTIONS) .. && cmake --build . --config Release -j${JOBS} ) # install only in SuiteSparse/lib and SuiteSparse/include local: - ( cd build && cmake $(CMAKE_OPTIONS) -DLOCAL_INSTALL=1 .. && cmake --build . -j${JOBS} ) + ( cd build && cmake $(CMAKE_OPTIONS) -USUITESPARSE_PKGFILEDIR -DSUITESPARSE_LOCAL_INSTALL=1 .. && cmake --build . --config Release -j${JOBS} ) # install only in /usr/local (default) global: - ( cd build && cmake $(CMAKE_OPTIONS) -DLOCAL_INSTALL=0 .. && cmake --build . -j${JOBS} ) + ( cd build && cmake $(CMAKE_OPTIONS) -USUITESPARSE_PKGFILEDIR -DSUITESPARSE_LOCAL_INSTALL=0 .. && cmake --build . --config Release -j${JOBS} ) debug: - ( cd build && cmake $(CMAKE_OPTIONS) -DCMAKE_BUILD_TYPE=Debug .. && cmake --build . -j${JOBS} ) + ( cd build && cmake $(CMAKE_OPTIONS) -DCMAKE_BUILD_TYPE=Debug .. && cmake --build . --config Debug -j${JOBS} ) all: library demos: library - ( cd build && cmake $(CMAKE_OPTIONS) -DDEMO=1 .. && cmake --build . -j${JOBS} ) - - ./build/colamd_example > ./build/colamd_example.out - - diff --strip-trailing-cr ./Demo/colamd_example.out ./build/colamd_example.out - - ./build/colamd_l_example > ./build/colamd_l_example.out - - diff --strip-trailing-cr ./Demo/colamd_l_example.out ./build/colamd_l_example.out + ( cd build && cmake $(CMAKE_OPTIONS) -DSUITESPARSE_DEMOS=1 .. && cmake --build . --config Release -j${JOBS} ) + - ./build/colamd_example > ./build/colamd_example.out && ( command -v d2u && d2u ./build/colamd_example.out || true ) + - diff ./Demo/colamd_example.out ./build/colamd_example.out + - ./build/colamd_l_example > ./build/colamd_l_example.out && ( command -v d2u && d2u ./build/colamd_l_example.out || true ) + - diff ./Demo/colamd_l_example.out ./build/colamd_l_example.out # just compile after running cmake; do not run cmake again remake: @@ -64,7 +64,7 @@ remake: # just run cmake to set things up setup: - ( cd build ; cmake $(CMAKE_OPTIONS) .. ) + ( cd build && cmake $(CMAKE_OPTIONS) .. ) install: ( cd build && cmake --install . ) diff --git a/ThirdParty/SuiteSparse/COLAMD/Source/colamd.c b/ThirdParty/SuiteSparse/COLAMD/Source/colamd.c index af5b27f7a0..968d90fc6a 100644 --- a/ThirdParty/SuiteSparse/COLAMD/Source/colamd.c +++ b/ThirdParty/SuiteSparse/COLAMD/Source/colamd.c @@ -1046,7 +1046,6 @@ size_t COLAMD_recommended /* returns recommended value of Alen. */ return (ok ? s : 0) ; } - /* ========================================================================== */ /* === colamd_set_defaults ================================================== */ /* ========================================================================== */ diff --git a/ThirdParty/SuiteSparse/COLAMD/Source/colamd_version.c b/ThirdParty/SuiteSparse/COLAMD/Source/colamd_version.c new file mode 100644 index 0000000000..9184817efa --- /dev/null +++ b/ThirdParty/SuiteSparse/COLAMD/Source/colamd_version.c @@ -0,0 +1,19 @@ +//------------------------------------------------------------------------------ +// COLAMD/Source/colamd_version.c: return COLAMD version +//------------------------------------------------------------------------------ + +// COLAMD, Copyright (c) 1998-2022, Timothy A. Davis and Stefan Larimore, +// All Rights Reserved. +// SPDX-License-Identifier: BSD-3-clause + +//------------------------------------------------------------------------------ + +#include "colamd.h" + +void colamd_version (int version [3]) +{ + version [0] = COLAMD_MAIN_VERSION ; + version [1] = COLAMD_SUB_VERSION ; + version [2] = COLAMD_SUBSUB_VERSION ; +} + diff --git a/ThirdParty/SuiteSparse/COLAMD/cmake_modules/FindCOLAMD.cmake b/ThirdParty/SuiteSparse/COLAMD/cmake_modules/FindCOLAMD.cmake deleted file mode 100644 index 00e8a9ac0f..0000000000 --- a/ThirdParty/SuiteSparse/COLAMD/cmake_modules/FindCOLAMD.cmake +++ /dev/null @@ -1,129 +0,0 @@ -#------------------------------------------------------------------------------- -# SuiteSparse/COLAMD/cmake_modules/FindCOLAMD.cmake -#------------------------------------------------------------------------------- - -# The following copyright and license applies to just this file only, not to -# the library itself: -# FindCOLAMD.cmake, Copyright (c) 2022-2023, Timothy A. Davis. All Rights Reserved. -# SPDX-License-Identifier: BSD-3-clause - -#------------------------------------------------------------------------------- - -# Finds the COLAMD include file and compiled library and sets: - -# COLAMD_INCLUDE_DIR - where to find colamd.h -# COLAMD_LIBRARY - dynamic COLAMD library -# COLAMD_STATIC - static COLAMD library -# COLAMD_LIBRARIES - libraries when using COLAMD -# COLAMD_FOUND - true if COLAMD found - -# set ``COLAMD_ROOT`` to a COLAMD installation root to -# tell this module where to look. - -# All the Find*.cmake files in SuiteSparse are installed by 'make install' into -# /usr/local/lib/cmake/SuiteSparse (where '/usr/local' is the -# ${CMAKE_INSTALL_PREFIX}). To access this file, place the following commands -# in your CMakeLists.txt file. See also SuiteSparse/Example/CMakeLists.txt: -# -# set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} -# ${CMAKE_INSTALL_PREFIX}/lib/cmake/SuiteSparse ) - -#------------------------------------------------------------------------------- - -# include files for COLAMD -find_path ( COLAMD_INCLUDE_DIR - NAMES colamd.h - HINTS ${CMAKE_SOURCE_DIR}/.. - HINTS ${CMAKE_SOURCE_DIR}/../SuiteSparse/COLAMD - HINTS ${CMAKE_SOURCE_DIR}/../COLAMD - PATH_SUFFIXES include Include -) - -# dynamic COLAMD library (or static if no dynamic library was built) -find_library ( COLAMD_LIBRARY - NAMES colamd colamd_static - HINTS ${CMAKE_SOURCE_DIR}/.. - HINTS ${CMAKE_SOURCE_DIR}/../SuiteSparse/COLAMD - HINTS ${CMAKE_SOURCE_DIR}/../COLAMD - PATH_SUFFIXES lib build build/Release build/Debug -) - -if ( MSVC ) - set ( STATIC_NAME colamd_static ) -else ( ) - set ( STATIC_NAME colamd ) - set ( save ${CMAKE_FIND_LIBRARY_SUFFIXES} ) - set ( CMAKE_FIND_LIBRARY_SUFFIXES - ${CMAKE_STATIC_LIBRARY_SUFFIX} ${CMAKE_FIND_LIBRARY_SUFFIXES} ) -endif ( ) - -# static COLAMD library -find_library ( COLAMD_STATIC - NAMES ${STATIC_NAME} - HINTS ${CMAKE_SOURCE_DIR}/.. - HINTS ${CMAKE_SOURCE_DIR}/../SuiteSparse/COLAMD - HINTS ${CMAKE_SOURCE_DIR}/../COLAMD - PATH_SUFFIXES lib build build/Release build/Debug -) - -if ( NOT MSVC ) - # restore the CMAKE_FIND_LIBRARY_SUFFIXES variable - set ( CMAKE_FIND_LIBRARY_SUFFIXES ${save} ) -endif ( ) - -# get version of the library from the dynamic library name -get_filename_component ( COLAMD_LIBRARY ${COLAMD_LIBRARY} REALPATH ) -get_filename_component ( COLAMD_FILENAME ${COLAMD_LIBRARY} NAME ) -string ( - REGEX MATCH "[0-9]+.[0-9]+.[0-9]+" - COLAMD_VERSION - ${COLAMD_FILENAME} -) - -# set ( COLAMD_VERSION "" ) -if ( EXISTS "${COLAMD_INCLUDE_DIR}" AND NOT COLAMD_VERSION ) - # if the version does not appear in the filename, read the include file - file ( STRINGS ${COLAMD_INCLUDE_DIR}/colamd.h COLAMD_MAJOR_STR - REGEX "define COLAMD_MAIN_VERSION" ) - file ( STRINGS ${COLAMD_INCLUDE_DIR}/colamd.h COLAMD_MINOR_STR - REGEX "define COLAMD_SUB_VERSION" ) - file ( STRINGS ${COLAMD_INCLUDE_DIR}/colamd.h COLAMD_PATCH_STR - REGEX "define COLAMD_SUBSUB_VERSION" ) - message ( STATUS "major: ${COLAMD_MAJOR_STR}" ) - message ( STATUS "minor: ${COLAMD_MINOR_STR}" ) - message ( STATUS "patch: ${COLAMD_PATCH_STR}" ) - string ( REGEX MATCH "[0-9]+" COLAMD_MAJOR ${COLAMD_MAJOR_STR} ) - string ( REGEX MATCH "[0-9]+" COLAMD_MINOR ${COLAMD_MINOR_STR} ) - string ( REGEX MATCH "[0-9]+" COLAMD_PATCH ${COLAMD_PATCH_STR} ) - set (COLAMD_VERSION "${COLAMD_MAJOR}.${COLAMD_MINOR}.${COLAMD_PATCH}") -endif ( ) - -set (COLAMD_LIBRARIES ${COLAMD_LIBRARY}) - -include (FindPackageHandleStandardArgs) - -find_package_handle_standard_args ( COLAMD - REQUIRED_VARS COLAMD_LIBRARY COLAMD_INCLUDE_DIR - VERSION_VAR COLAMD_VERSION -) - -mark_as_advanced ( - COLAMD_INCLUDE_DIR - COLAMD_LIBRARY - COLAMD_STATIC - COLAMD_LIBRARIES -) - -if ( COLAMD_FOUND ) - message ( STATUS "COLAMD version: ${COLAMD_VERSION}" ) - message ( STATUS "COLAMD include: ${COLAMD_INCLUDE_DIR}" ) - message ( STATUS "COLAMD library: ${COLAMD_LIBRARY}" ) - message ( STATUS "COLAMD static: ${COLAMD_STATIC}" ) -else ( ) - message ( STATUS "COLAMD not found" ) - set ( COLAMD_INCLUDE_DIR "" ) - set ( COLAMD_LIBRARIES "" ) - set ( COLAMD_LIBRARY "" ) - set ( COLAMD_STATIC "" ) -endif ( ) - diff --git a/ThirdParty/SuiteSparse/ChangeLog b/ThirdParty/SuiteSparse/ChangeLog index 06fe8950bc..0f1eb87902 100644 --- a/ThirdParty/SuiteSparse/ChangeLog +++ b/ThirdParty/SuiteSparse/ChangeLog @@ -1,3 +1,175 @@ +Jan 20, 2024: version 7.6.0 + + * CHOLMOD 5.2.0: bug fix (restore ABI compatibility with 5.0.x, i.e., 5.2.0 + is ABI incompatible to 5.1.x) + * SuiteSparse_config 7.6.0, Mongoose 3.3.2, COLAMD 3.3.2, CCOLAMD 3.3.2: + port Makefile to Windows + * SPQR 4.3.2: remove unused parameters + * LAGraph 1.1.2, CSparse 4.3.1, ParU 0.1.2, GraphBLAS 9.0.1: + minor updates to build system + * Example 1.6.2, UMFPACK 6.3.2, KLU 2.3.2, SuiteSparse_Mongoose 3.3.2, + SPEX 2.3.2: revise version numbers of dependent packages + * AMD, BTF, CAMD, CXSparse, LDL, RBio: unchanged + * Package versions in this release: + SuiteSparse_config 7.6.0 + AMD 3.3.1 + BTF 2.3.1 + CAMD 3.3.1 + CCOLAMD 3.3.2 + CHOLMOD 5.2.0 + COLAMD 3.3.2 + CSparse 4.3.1 + CXSparse 4.3.1 + Example 1.6.2 + GraphBLAS 9.0.1 + KLU 2.3.2 + LDL 3.3.1 + LAGraph 1.1.2 + SuiteSparse_Mongoose 3.3.2 + ParU 0.1.2 + RBio 4.3.1 + SPEX 2.3.2 + SPQR 4.3.2 + UMFPACK 6.3.2 + +Jan 12, 2024: version 7.5.1 + + * SuiteSparse_config: bug fix to SUITESPARSE__VERCODE macro. + * Example 1.6.1: add tests for *__VERSION macros. + + * Package versions in this release: + SuiteSparse_config 7.5.1 + AMD 3.3.1 + BTF 2.3.1 + CAMD 3.3.1 + CCOLAMD 3.3.1 + CHOLMOD 5.1.1 + COLAMD 3.3.1 + CSparse 4.3.0 + CXSparse 4.3.1 + Example 1.6.1 + GraphBLAS 9.0.0 + KLU 2.3.1 + LDL 3.3.1 + LAGraph 1.1.1 + SuiteSparse_Mongoose 3.3.1 + ParU 0.1.1 + RBio 4.3.1 + SPEX 2.3.1 + SPQR 4.3.1 + UMFPACK 6.3.1 + +Jan 10, 2024: version 7.5.0 + + * SuiteSparse_config: 7.5.0, to reflect the addition of GraphBLAS 9.0.0. + Minor updates to build system, including bug fixes when specifying a + specific BLAS/LAPACK library, and configuration of *.pc files. + * GraphBLAS 9.0.0: supporting the v2.1 C API; + see https://github.com/GraphBLAS/graphblas-api-c + * Example 1.6.0: using GraphBLAS 9.0.0 and SuiteSparse_config 7.5.0, + remove explicit dependencies on OpenMP, libm, GMP, and MPFR. + Add programs to test the *Config.cmake of each package. + * All other packages (except CSparse): minor updates to build system + and MATLAB interfaces + + * Package versions in this release: + SuiteSparse_config 7.5.0 + AMD 3.3.1 + BTF 2.3.1 + CAMD 3.3.1 + CCOLAMD 3.3.1 + CHOLMOD 5.1.1 + COLAMD 3.3.1 + CSparse 4.3.0 (unchanged from SuiteSparse 7.4.0) + CXSparse 4.3.1 + Example 1.6.0 + GraphBLAS 9.0.0 + KLU 2.3.1 + LDL 3.3.1 + LAGraph 1.1.1 + SuiteSparse_Mongoose 3.3.1 + ParU 0.1.1 + RBio 4.3.1 + SPEX 2.3.1 + SPQR 4.3.1 + UMFPACK 6.3.1 + +Dec 30, 2023: version 7.4.0 + + * major change to build system: by Markus Mützel. Includes a + top-level CMakeLists.txt that builds all packages, and support for + pkg-config. Default location of files is now listed below, where + PACKAGE is one of the packages in SuiteSparse: + * CMAKE_INSTALL_PREFIX/include/suitesparse: include files + * CMAKE_INSTALL_PREFIX/lib: compiled libraries + * CMAKE_INSTALL_PREFIX/lib/cmake/SuiteSparse: helper *.cmake scripts + for all of SuiteSparse + * CMAKE_INSTALL_PREFIX/lib/cmake/PACKAGE: *Config.cmake scripts for a + specific package + * CMAKE_INSTALL_PREFIX/lib/pkgconfig/PACKAGE.pc: *.pc pkg-config + files with information for a specific package + Additional changes are listed below. + * LAGraph 1.1.0: new package: graph algorithms based on GraphBLAS + * ParU 0.1.0: new package: parallel unsymmetric multifrontal method, + with Mohsen Aznaveh. This is a stable package but is tagged as 0.1.0 + since the API is still subject to change. + * CHOLMOD 5.1.0: full support for sparse single precision matrices, + bug fixes in the GPU Module. + * AMD 3.3.0: minor change for CHOLMOD 5.1.0 tests + * CAMD 3.3.0: minor change for CHOLMOD 5.1.0 tests + * SuiteSparse_config 7.4.0: added wrappers for single-precision BLAS/LAPACK, + added SUITESPARSE_TIME macro. + * *_version: added methods to all package that didn't have them: + AMD, CAMD, COLAMD, CCOLAMD, BTF, CSparse, CXSparse, KLU, BTF, RBio, + SPEX, SPQR, and UMFPACK. + +Oct 31, 2023: version 7.3.1 + + * CHOLMOD 5.0.1: remove "I" from cholmod.h. + +Oct 23, 2023: version 7.3.0 + + * CHOLMOD 5.0.0: initial support for sparse single precision matries. + CHOLMOD:Core replaced with CHOLMOD:Utility + * updated to require CHOLMOD 5.0.0: + Example 1.4.3, GPUQREngine 3.3.3, KLU 2.2.2, SPQR 4.2.2, UMFPACK 6.2.2 + * SuiteSparseLAPACK.cmake: allow the use of BLIS/FLAME for LAPACK; + update from Theirry Thomas. + * build system: further updates to cmake, by Markus Muetzel. + +Oct 18, 2023: version 7.2.2 + + * CHOLMOD 4.2.2: bug fix to CHOLMOD/Supernodal (heuristic to determine + # threads to use for last supernode was incorrect) + +Oct 7, 2023: version 7.2.1 + + * GraphBLAS 8.2.1: bug fix to GrB_mxm; incorrect handling of typecasting + * cross-compiler support: replace check_c_source_runs with _compiles, + for GraphBLAS and SuiteSparse_config, and remove check for + getenv("HOME"). + * cmake update: add "None" build type, from Antonio Rojas, for Arch Linux, + to all *Config.cmake files for all packages except CSparse (CXSparse + is built instead, and CSparse does not have CSparseConfig.cmake file) + * UMFPACK v6.2.1 and GPUQREngine v3.2.1: copies internal include files + from other SuiteSparse packages (AMD and SuiteSparse_GPURuntime), + so these two packages can be built independently. + +Sept 8, 2023: version 7.2.0 + + * build system: modern cmake structure, by Markus Muetzel. + Most packages updated to vX.2.0 where X is unchanged (except SPQR + and Example package). + * SPQR v4.2.0: Major SO update. Support for int32 indices by Raye Kimmerer + +June 29, 2023: version 7.1.0 + + * GraphBLAS v8.0.2: major update with a new JIT feature. + * build system: many changes to build systems of all packages, contributed + by Markus Muetzel. + * RBio 4.0.0: revised API: declaring many input parameters as const + * CXSparse 4.0.4: changed complex types for C++ callers + Jan 20, 2023: version 7.0.1 * GraphBLAS v7.4.3: debug was left on in GrB_Matrix_removeElement @@ -128,7 +300,7 @@ Mar 14, 2022, SuiteSparse 5.11.0 v1.9.3, Copyright (c) 2011-2016, Yann Collet, All Rights Reserved. * iso-valued matrices and vectors: to exploit the common case of an unweighted graph - * bug fixes: 4 bugs fixed since SuiteSparse 5.10.1 with + * bug fixes: 4 bugs fixed since SuiteSparse 5.10.1 with GraphBLAS v5.0.5. 12 other bugs appeared in the interim but appeared in versions after v5.0.5 but fixed before ever affecting SuiteSparse itself. @@ -496,7 +668,7 @@ June 20, 2012: SuiteSparse version 4.0.1 * UMFPACK 5.6.1: minor fix to MATLAB install; no change to C except version nubmer * MATLAB_Tools: - update to UFcollection (filesep) + update to UFcollection (filesep) June 1, 2012: SuiteSparse version 4.0.0 diff --git a/ThirdParty/SuiteSparse/KLU/CMakeLists.txt b/ThirdParty/SuiteSparse/KLU/CMakeLists.txt index f2e88576e3..05025e37f1 100644 --- a/ThirdParty/SuiteSparse/KLU/CMakeLists.txt +++ b/ThirdParty/SuiteSparse/KLU/CMakeLists.txt @@ -2,7 +2,7 @@ # SuiteSparse/KLU/CMakeLists.txt: cmake for KLU #------------------------------------------------------------------------------- -# KLU, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +# KLU, Copyright (c) 2004-2023, University of Florida. All Rights Reserved. # Authors: Timothy A. Davis and Ekanathan Palamadai. # SPDX-License-Identifier: LGPL-2.1+ @@ -10,12 +10,12 @@ # get the version #------------------------------------------------------------------------------- -cmake_minimum_required ( VERSION 3.19 ) +cmake_minimum_required ( VERSION 3.22 ) -set ( KLU_DATE "Jan 17, 2023" ) -set ( KLU_VERSION_MAJOR 2 ) -set ( KLU_VERSION_MINOR 0 ) -set ( KLU_VERSION_SUB 3 ) +set ( KLU_DATE "Jan 20, 2024" ) +set ( KLU_VERSION_MAJOR 2 CACHE STRING "" FORCE ) +set ( KLU_VERSION_MINOR 3 CACHE STRING "" FORCE ) +set ( KLU_VERSION_SUB 2 CACHE STRING "" FORCE ) message ( STATUS "Building KLU version: v" ${KLU_VERSION_MAJOR}. @@ -23,60 +23,86 @@ message ( STATUS "Building KLU version: v" ${KLU_VERSION_SUB} " (" ${KLU_DATE} ")" ) #------------------------------------------------------------------------------- +# define the project +#------------------------------------------------------------------------------- + +project ( KLU + VERSION "${KLU_VERSION_MAJOR}.${KLU_VERSION_MINOR}.${KLU_VERSION_SUB}" + LANGUAGES C ) +#------------------------------------------------------------------------------- # SuiteSparse policies #------------------------------------------------------------------------------- set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} - ${CMAKE_SOURCE_DIR}/cmake_modules - ${CMAKE_SOURCE_DIR}/../BTF/cmake_modules - ${CMAKE_SOURCE_DIR}/../AMD/cmake_modules - ${CMAKE_SOURCE_DIR}/../COLAMD/cmake_modules - ${CMAKE_SOURCE_DIR}/../CAMD/cmake_modules - ${CMAKE_SOURCE_DIR}/../CCOLAMD/cmake_modules - ${CMAKE_SOURCE_DIR}/../CHOLMOD/cmake_modules - ${CMAKE_SOURCE_DIR}/../SuiteSparse_config/cmake_modules ) + ${PROJECT_SOURCE_DIR}/../SuiteSparse_config/cmake_modules ) include ( SuiteSparsePolicy ) #------------------------------------------------------------------------------- -# define the project +# find library dependencies #------------------------------------------------------------------------------- -project ( klu - VERSION "${KLU_VERSION_MAJOR}.${KLU_VERSION_MINOR}.${KLU_VERSION_SUB}" - LANGUAGES C ) +if ( NOT SUITESPARSE_ROOT_CMAKELISTS ) + find_package ( SuiteSparse_config 7.6.0 + PATHS ${CMAKE_SOURCE_DIR}/../SuiteSparse_config/build NO_DEFAULT_PATH ) + if ( NOT TARGET SuiteSparse::SuiteSparseConfig ) + find_package ( SuiteSparse_config 7.6.0 REQUIRED ) + endif ( ) -#------------------------------------------------------------------------------- -# find library dependencies -#------------------------------------------------------------------------------- + find_package ( AMD 3.3.1 + PATHS ${CMAKE_SOURCE_DIR}/../AMD/build NO_DEFAULT_PATH ) + if ( NOT TARGET SuiteSparse::AMD ) + find_package ( AMD 3.3.1 REQUIRED ) + endif ( ) + + find_package ( COLAMD 3.3.2 + PATHS ${CMAKE_SOURCE_DIR}/../COLAMD/build NO_DEFAULT_PATH ) + if ( NOT TARGET SuiteSparse::COLAMD ) + find_package ( COLAMD 3.3.2 REQUIRED ) + endif ( ) -find_package ( SuiteSparse_config 7.0.0 REQUIRED ) -find_package ( BTF 2.0.3 REQUIRED ) -find_package ( COLAMD 3.0.3 REQUIRED ) -find_package ( AMD 3.0.3 REQUIRED ) - -option ( NCHOLMOD "ON: do not use CHOLMOD. OFF (default): use CHOLMOD" off ) - -if ( NOT NCHOLMOD ) - # look for CHOLMOD (optional fill-reducing orderings) - find_package ( CHOLMOD 4.0.3 ) - find_package ( CHOLMOD_CUDA 4.0.3 ) - # look for CHOLMOD's dependencies: AMD and COLAMD are required. CAMD and - # CCOLAMD are optional, but must be found if CHOLMOD was built with them. - find_package ( CAMD 3.0.3 ) - find_package ( CCOLAMD 3.0.3 ) - if ( NOT CHOLMOD_FOUND OR NOT AMD_FOUND OR NOT COLAMD_FOUND ) - # CHOLMOD not found so disable it - set ( NCHOLMOD true ) + find_package ( BTF 2.3.1 + PATHS ${CMAKE_SOURCE_DIR}/../BTF/build NO_DEFAULT_PATH ) + if ( NOT TARGET SuiteSparse::BTF ) + find_package ( BTF 2.3.1 REQUIRED ) endif ( ) endif ( ) -if ( NCHOLMOD ) - # tell KLU that CHOLMOD is not available - message ( STATUS "CHOLMOD not found or not requested" ) - add_compile_definitions ( NCHOLMOD ) +option ( KLU_USE_CHOLMOD "ON (default): use CHOLMOD in KLU. OFF: do not use CHOLMOD in KLU" ON ) + +if ( SUITESPARSE_ROOT_CMAKELISTS ) + # if KLU_USE_CHOLMOD is true, then CHOLMOD has been added to the + # list of packages to compile in the root CMakeLists.txt. + set ( KLU_HAS_CHOLMOD ${KLU_USE_CHOLMOD} ) else ( ) + if ( KLU_USE_CHOLMOD ) + # look for CHOLMOD (optional fill-reducing orderings) + find_package ( CHOLMOD 5.2.0 + PATHS ${CMAKE_SOURCE_DIR}/../CHOLMOD/build NO_DEFAULT_PATH ) + if ( NOT TARGET SuiteSparse::CHOLMOD ) + find_package ( CHOLMOD 5.2.0 ) + endif ( ) + if ( NOT CHOLMOD_FOUND ) + # CHOLMOD not found so disable it + set ( KLU_HAS_CHOLMOD OFF ) + else ( ) + set ( KLU_HAS_CHOLMOD ON ) + endif ( ) + else ( ) + set ( KLU_HAS_CHOLMOD OFF ) + endif ( ) +endif ( ) + +if ( KLU_HAS_CHOLMOD ) message ( STATUS "Using CHOLMOD for addtional pre-ordering options" ) +else ( ) + add_compile_definitions ( NCHOLMOD ) + message ( STATUS "CHOLMOD not found or not requested" ) +endif ( ) + +# check for strict usage +if ( SUITESPARSE_USE_STRICT AND KLU_USE_CHOLMOD AND NOT KLU_HAS_CHOLMOD ) + message ( FATAL_ERROR "CHOLMOD required for KLU but not found" ) endif ( ) #------------------------------------------------------------------------------- @@ -94,8 +120,7 @@ configure_file ( "Config/klu_version.tex.in" # include directories #------------------------------------------------------------------------------- -include_directories ( Source Include User ${SUITESPARSE_CONFIG_INCLUDE_DIR} - ${AMD_INCLUDE_DIR} ${COLAMD_INCLUDE_DIR} ${BTF_INCLUDE_DIR} ) +include_directories ( Source Include User ) #------------------------------------------------------------------------------- # dynamic klu library properties @@ -103,64 +128,103 @@ include_directories ( Source Include User ${SUITESPARSE_CONFIG_INCLUDE_DIR} file ( GLOB KLU_SOURCES "Source/*.c" ) -add_library ( klu SHARED ${KLU_SOURCES} ) +if ( BUILD_SHARED_LIBS ) + add_library ( KLU SHARED ${KLU_SOURCES} ) + + set_target_properties ( KLU PROPERTIES + VERSION ${KLU_VERSION_MAJOR}.${KLU_VERSION_MINOR}.${KLU_VERSION_SUB} + C_STANDARD 11 + C_STANDARD_REQUIRED ON + OUTPUT_NAME klu + SOVERSION ${KLU_VERSION_MAJOR} + PUBLIC_HEADER "Include/klu.h" + WINDOWS_EXPORT_ALL_SYMBOLS ON ) + + if ( ${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.25" ) + set_target_properties ( KLU PROPERTIES EXPORT_NO_SYSTEM ON ) + endif ( ) -set_target_properties ( klu PROPERTIES - VERSION ${KLU_VERSION_MAJOR}.${KLU_VERSION_MINOR}.${KLU_VERSION_SUB} - C_STANDARD_REQUIRED 11 - SOVERSION ${KLU_VERSION_MAJOR} - PUBLIC_HEADER "Include/klu.h" - WINDOWS_EXPORT_ALL_SYMBOLS ON ) + target_include_directories ( KLU + INTERFACE $ + $ ) +endif ( ) #------------------------------------------------------------------------------- # static klu library properties #------------------------------------------------------------------------------- -if ( NOT NSTATIC ) - add_library ( klu_static STATIC ${KLU_SOURCES} ) +if ( BUILD_STATIC_LIBS ) + add_library ( KLU_static STATIC ${KLU_SOURCES} ) - set_target_properties ( klu_static PROPERTIES - VERSION ${KLU_VERSION_MAJOR}.${KLU_VERSION_MINOR}.${KLU_VERSION_SUB} - C_STANDARD_REQUIRED 11 + set_target_properties ( KLU_static PROPERTIES + C_STANDARD 11 + C_STANDARD_REQUIRED ON OUTPUT_NAME klu - SOVERSION ${KLU_VERSION_MAJOR} ) + PUBLIC_HEADER "Include/klu.h" ) if ( MSVC ) - set_target_properties ( klu_static PROPERTIES + set_target_properties ( KLU_static PROPERTIES OUTPUT_NAME klu_static ) endif ( ) + + if ( ${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.25" ) + set_target_properties ( KLU_static PROPERTIES EXPORT_NO_SYSTEM ON ) + endif ( ) + + target_include_directories ( KLU_static + INTERFACE $ + $ ) endif ( ) #------------------------------------------------------------------------------- # klu_cholmod library properties #------------------------------------------------------------------------------- -if ( NOT NCHOLMOD ) +if ( KLU_HAS_CHOLMOD ) file ( GLOB KLU_CHOLMOD_SOURCES "User/*.c" ) - add_library ( klu_cholmod SHARED ${KLU_CHOLMOD_SOURCES} ) - include_directories ( ${CHOLMOD_INCLUDE_DIR} ) + if ( BUILD_SHARED_LIBS ) + add_library ( KLU_CHOLMOD SHARED ${KLU_CHOLMOD_SOURCES} ) - set_target_properties ( klu_cholmod PROPERTIES - VERSION ${KLU_VERSION_MAJOR}.${KLU_VERSION_MINOR}.${KLU_VERSION_SUB} - C_STANDARD_REQUIRED 11 - SOVERSION ${KLU_VERSION_MAJOR} - PUBLIC_HEADER "User/klu_cholmod.h" ) + set_target_properties ( KLU_CHOLMOD PROPERTIES + VERSION ${KLU_VERSION_MAJOR}.${KLU_VERSION_MINOR}.${KLU_VERSION_SUB} + C_STANDARD 11 + C_STANDARD_REQUIRED ON + OUTPUT_NAME klu_cholmod + SOVERSION ${KLU_VERSION_MAJOR} + PUBLIC_HEADER "User/klu_cholmod.h" ) - if ( NOT NSTATIC ) - add_library ( klu_cholmod_static STATIC ${KLU_CHOLMOD_SOURCES} ) + if ( ${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.25" ) + set_target_properties ( KLU_CHOLMOD PROPERTIES EXPORT_NO_SYSTEM ON ) + endif ( ) - set_target_properties ( klu_cholmod_static PROPERTIES - VERSION ${KLU_VERSION_MAJOR}.${KLU_VERSION_MINOR}.${KLU_VERSION_SUB} - C_STANDARD_REQUIRED 11 + target_include_directories ( KLU_CHOLMOD + INTERFACE $ + $ ) + endif ( ) + + if ( BUILD_STATIC_LIBS ) + add_library ( KLU_CHOLMOD_static STATIC ${KLU_CHOLMOD_SOURCES} ) + + set_target_properties ( KLU_CHOLMOD_static PROPERTIES + C_STANDARD 11 + C_STANDARD_REQUIRED ON OUTPUT_NAME klu_cholmod - SOVERSION ${KLU_VERSION_MAJOR} ) + PUBLIC_HEADER "User/klu_cholmod.h" ) if ( MSVC ) - set_target_properties ( klu_cholmod_static PROPERTIES + set_target_properties ( KLU_CHOLMOD_static PROPERTIES OUTPUT_NAME klu_cholmod_static ) endif ( ) + + if ( ${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.25" ) + set_target_properties ( KLU_CHOLMOD_static PROPERTIES EXPORT_NO_SYSTEM ON ) + endif ( ) + + target_include_directories ( KLU_CHOLMOD_static + INTERFACE $ + $ ) endif ( ) endif ( ) @@ -169,99 +233,285 @@ endif ( ) # add the library dependencies #------------------------------------------------------------------------------- -# suitesparseconfig: -target_link_libraries ( klu PUBLIC ${SUITESPARSE_CONFIG_LIBRARIES} ) -if ( NOT NSTATIC ) - target_link_libraries ( klu_static PUBLIC ${SUITESPARSE_CONFIG_STATIC} ) +# SuiteSparseConfig: +if ( BUILD_SHARED_LIBS ) + target_link_libraries ( KLU PRIVATE SuiteSparse::SuiteSparseConfig ) endif ( ) - -# libm: -if ( NOT WIN32 ) - target_link_libraries ( klu PUBLIC m ) - if ( NOT NSTATIC ) - target_link_libraries ( klu_static PUBLIC m ) +if ( BUILD_STATIC_LIBS ) + if ( TARGET SuiteSparse::SuiteSparseConfig_static ) + target_link_libraries ( KLU_static PRIVATE SuiteSparse::SuiteSparseConfig_static ) + else ( ) + target_link_libraries ( KLU_static PRIVATE SuiteSparse::SuiteSparseConfig ) endif ( ) endif ( ) -# amd: -target_link_libraries ( klu PUBLIC ${AMD_LIBRARIES} ) -if ( NOT NSTATIC ) - target_link_libraries ( klu_static PUBLIC ${AMD_STATIC} ) +# AMD: +if ( BUILD_SHARED_LIBS ) + target_link_libraries ( KLU PRIVATE SuiteSparse::AMD ) + target_include_directories ( KLU PUBLIC + "$" ) +endif ( ) +if ( BUILD_STATIC_LIBS ) + if ( TARGET SuiteSparse::AMD_static ) + target_link_libraries ( KLU_static PUBLIC SuiteSparse::AMD_static ) + else ( ) + target_link_libraries ( KLU_static PUBLIC SuiteSparse::AMD ) + endif ( ) endif ( ) -# colamd: -target_link_libraries ( klu PUBLIC ${COLAMD_LIBRARIES} ) -if ( NOT NSTATIC ) - target_link_libraries ( klu_static PUBLIC ${COLAMD_STATIC} ) +# COLAMD: +if ( BUILD_SHARED_LIBS ) + target_link_libraries ( KLU PRIVATE SuiteSparse::COLAMD ) + target_include_directories ( KLU PUBLIC + "$" ) +endif ( ) +if ( BUILD_STATIC_LIBS ) + if ( TARGET SuiteSparse::COLAMD_static ) + target_link_libraries ( KLU_static PUBLIC SuiteSparse::COLAMD_static ) + else ( ) + target_link_libraries ( KLU_static PUBLIC SuiteSparse::COLAMD ) + endif ( ) endif ( ) -# btf: -target_link_libraries ( klu PUBLIC ${BTF_LIBRARIES} ) -if ( NOT NSTATIC ) - target_link_libraries ( klu_static PUBLIC ${BTF_STATIC} ) +# BTF: +if ( BUILD_SHARED_LIBS ) + target_link_libraries ( KLU PRIVATE SuiteSparse::BTF ) + target_include_directories ( KLU PUBLIC + "$" ) +endif ( ) +if ( BUILD_STATIC_LIBS ) + if ( TARGET SuiteSparse::BTF_static ) + target_link_libraries ( KLU_static PUBLIC SuiteSparse::BTF_static ) + else ( ) + target_link_libraries ( KLU_static PUBLIC SuiteSparse::BTF ) + endif ( ) endif ( ) -if ( NOT NCHOLMOD ) +if ( KLU_HAS_CHOLMOD ) - # cholmod: + # CHOLMOD: # link with CHOLMOD and its dependencies, both required and optional - target_link_libraries ( klu PUBLIC - ${CHOLMOD_LIBRARIES} ${CHOLMOD_CUDA_LIBRARIES} - ${AMD_LIBRARIES} ${COLAMD_LIBRARIES} - ${CAMD_LIBRARIES} ${CCOLAMD_LIBRARIES} ) - target_link_libraries ( klu_cholmod PUBLIC - ${CHOLMOD_LIBRARIES} ${CHOLMOD_CUDA_LIBRARIES} - ${AMD_LIBRARIES} ${COLAMD_LIBRARIES} - ${CAMD_LIBRARIES} ${CCOLAMD_LIBRARIES} ) - if ( NOT NSTATIC ) - target_link_libraries ( klu_static PUBLIC - ${CHOLMOD_STATIC} ${CHOLMOD_CUDA_STATIC} - ${AMD_STATIC} ${COLAMD_STATIC} - ${CAMD_STATIC} ${CCOLAMD_STATIC} ) - target_link_libraries ( klu_cholmod_static PUBLIC - ${CHOLMOD_STATIC} ${CHOLMOD_CUDA_STATIC} - ${AMD_STATIC} ${COLAMD_STATIC} - ${CAMD_STATIC} ${CCOLAMD_STATIC} ) + # CHOLMOD without CUDA + if ( BUILD_SHARED_LIBS ) + target_link_libraries ( KLU_CHOLMOD PRIVATE SuiteSparse::CHOLMOD ) + endif ( ) + if ( BUILD_STATIC_LIBS ) + set ( KLU_STATIC_MODULES "${KLU_STATIC_MODULES} CHOLMOD" ) + if ( TARGET SuiteSparse::CHOLMOD_static ) + target_link_libraries ( KLU_CHOLMOD_static PRIVATE SuiteSparse::CHOLMOD_static ) + else ( ) + target_link_libraries ( KLU_CHOLMOD_static PRIVATE SuiteSparse::CHOLMOD ) + endif ( ) endif ( ) # klu: - target_link_libraries ( klu_cholmod PUBLIC klu ${BTF_LIBRARIES} ) - if ( NOT NSTATIC ) - target_link_libraries ( klu_cholmod_static PUBLIC - klu_static ${BTF_STATIC} ) + if ( BUILD_SHARED_LIBS ) + target_link_libraries ( KLU_CHOLMOD PRIVATE KLU ) + endif ( ) + if ( BUILD_STATIC_LIBS ) + target_link_libraries ( KLU_CHOLMOD_static PRIVATE KLU_static ) + if ( TARGET SuiteSparse::BTF_static ) + target_link_libraries ( KLU_CHOLMOD_static PRIVATE SuiteSparse::BTF_static ) + else ( ) + target_link_libraries ( KLU_CHOLMOD_static PRIVATE SuiteSparse::BTF ) + endif ( ) endif ( ) endif ( ) +# libm: +if ( NOT WIN32 ) + if ( BUILD_SHARED_LIBS ) + target_link_libraries ( KLU PRIVATE m ) + endif ( ) + if ( BUILD_STATIC_LIBS ) + set ( KLU_STATIC_LIBS "${KLU_STATIC_LIBS} -lm" ) + target_link_libraries ( KLU_static PUBLIC m ) + endif ( ) +endif ( ) + #------------------------------------------------------------------------------- # KLU installation location #------------------------------------------------------------------------------- -install ( TARGETS klu - LIBRARY DESTINATION ${SUITESPARSE_LIBDIR} - ARCHIVE DESTINATION ${SUITESPARSE_LIBDIR} - RUNTIME DESTINATION ${SUITESPARSE_BINDIR} - PUBLIC_HEADER DESTINATION ${SUITESPARSE_INCLUDEDIR} ) -install ( FILES - ${CMAKE_SOURCE_DIR}/cmake_modules/FindKLU.cmake - ${CMAKE_SOURCE_DIR}/cmake_modules/FindKLU_CHOLMOD.cmake - DESTINATION ${SUITESPARSE_LIBDIR}/cmake/SuiteSparse - COMPONENT Development ) - -if ( NOT NSTATIC ) - install ( TARGETS klu_static - ARCHIVE DESTINATION ${SUITESPARSE_LIBDIR} ) -endif ( ) +include ( CMakePackageConfigHelpers ) -if ( NOT NCHOLMOD ) - install ( TARGETS klu_cholmod +if ( BUILD_SHARED_LIBS ) + install ( TARGETS KLU + EXPORT KLUTargets LIBRARY DESTINATION ${SUITESPARSE_LIBDIR} ARCHIVE DESTINATION ${SUITESPARSE_LIBDIR} RUNTIME DESTINATION ${SUITESPARSE_BINDIR} PUBLIC_HEADER DESTINATION ${SUITESPARSE_INCLUDEDIR} ) - if ( NOT NSTATIC ) - install ( TARGETS klu_cholmod_static - ARCHIVE DESTINATION ${SUITESPARSE_LIBDIR} ) +endif ( ) + +if ( BUILD_STATIC_LIBS ) + install ( TARGETS KLU_static + EXPORT KLUTargets + ARCHIVE DESTINATION ${SUITESPARSE_LIBDIR} + PUBLIC_HEADER DESTINATION ${SUITESPARSE_INCLUDEDIR} ) +endif ( ) + +# create (temporary) export target file during build +export ( EXPORT KLUTargets + NAMESPACE SuiteSparse:: + FILE ${CMAKE_CURRENT_BINARY_DIR}/KLUTargets.cmake ) + +# install export target, config and version files for find_package +install ( EXPORT KLUTargets + NAMESPACE SuiteSparse:: + DESTINATION ${SUITESPARSE_PKGFILEDIR}/cmake/KLU ) + +# generate config file to be used in common build tree +set ( SUITESPARSE_IN_BUILD_TREE ON ) +configure_package_config_file ( + Config/KLUConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/KLUConfig.cmake + INSTALL_DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/KLUConfig.cmake ) + +# generate config file to be installed +set ( SUITESPARSE_IN_BUILD_TREE OFF ) +configure_package_config_file ( + Config/KLUConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/target/KLUConfig.cmake + INSTALL_DESTINATION ${SUITESPARSE_PKGFILEDIR}/cmake/KLU ) + +write_basic_package_version_file ( + ${CMAKE_CURRENT_BINARY_DIR}/KLUConfigVersion.cmake + COMPATIBILITY SameMajorVersion ) + +install ( FILES + ${CMAKE_CURRENT_BINARY_DIR}/target/KLUConfig.cmake + ${CMAKE_CURRENT_BINARY_DIR}/KLUConfigVersion.cmake + DESTINATION ${SUITESPARSE_PKGFILEDIR}/cmake/KLU ) + +#------------------------------------------------------------------------------- +# create pkg-config file for KLU +#------------------------------------------------------------------------------- + +if ( NOT MSVC ) + set ( prefix "${CMAKE_INSTALL_PREFIX}" ) + set ( exec_prefix "\${prefix}" ) + cmake_path ( IS_ABSOLUTE SUITESPARSE_LIBDIR SUITESPARSE_LIBDIR_IS_ABSOLUTE ) + if (SUITESPARSE_LIBDIR_IS_ABSOLUTE) + set ( libdir "${SUITESPARSE_LIBDIR}") + else ( ) + set ( libdir "\${exec_prefix}/${SUITESPARSE_LIBDIR}") + endif ( ) + cmake_path ( IS_ABSOLUTE SUITESPARSE_INCLUDEDIR SUITESPARSE_INCLUDEDIR_IS_ABSOLUTE ) + if (SUITESPARSE_INCLUDEDIR_IS_ABSOLUTE) + set ( includedir "${SUITESPARSE_INCLUDEDIR}") + else ( ) + set ( includedir "\${prefix}/${SUITESPARSE_INCLUDEDIR}") + endif ( ) + if ( BUILD_SHARED_LIBS ) + set ( SUITESPARSE_LIB_BASE_NAME $ ) + else ( ) + set ( SUITESPARSE_LIB_BASE_NAME $ ) + endif ( ) + configure_file ( + Config/KLU.pc.in + KLU.pc.out + @ONLY + NEWLINE_STYLE LF ) + file ( GENERATE + OUTPUT KLU.pc + INPUT ${CMAKE_CURRENT_BINARY_DIR}/KLU.pc.out + NEWLINE_STYLE LF ) + install ( FILES + ${CMAKE_CURRENT_BINARY_DIR}/KLU.pc + DESTINATION ${SUITESPARSE_PKGFILEDIR}/pkgconfig ) +endif ( ) + +#------------------------------------------------------------------------------- +# KLU_CHOLMOD installation +#------------------------------------------------------------------------------- + +if ( KLU_HAS_CHOLMOD ) + if ( BUILD_SHARED_LIBS ) + install ( TARGETS KLU_CHOLMOD + EXPORT KLU_CHOLMODTargets + LIBRARY DESTINATION ${SUITESPARSE_LIBDIR} + ARCHIVE DESTINATION ${SUITESPARSE_LIBDIR} + RUNTIME DESTINATION ${SUITESPARSE_BINDIR} + PUBLIC_HEADER DESTINATION ${SUITESPARSE_INCLUDEDIR} ) + endif ( ) + if ( BUILD_STATIC_LIBS ) + install ( TARGETS KLU_CHOLMOD_static + EXPORT KLU_CHOLMODTargets + ARCHIVE DESTINATION ${SUITESPARSE_LIBDIR} + PUBLIC_HEADER DESTINATION ${SUITESPARSE_INCLUDEDIR} ) + endif ( ) + + # create (temporary) export target file during build + export ( EXPORT KLU_CHOLMODTargets + NAMESPACE SuiteSparse:: + FILE ${CMAKE_CURRENT_BINARY_DIR}/KLU_CHOLMODTargets.cmake ) + + # install export target, config and version files for find_package + install ( EXPORT KLU_CHOLMODTargets + NAMESPACE SuiteSparse:: + DESTINATION ${SUITESPARSE_PKGFILEDIR}/cmake/KLU_CHOLMOD ) + + # generate config file to be used in common build tree + set ( SUITESPARSE_IN_BUILD_TREE ON ) + configure_package_config_file ( + Config/KLU_CHOLMODConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/KLU_CHOLMODConfig.cmake + INSTALL_DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/KLU_CHOLMODConfig.cmake ) + + # generate config file to be installed + set ( SUITESPARSE_IN_BUILD_TREE OFF ) + configure_package_config_file ( + Config/KLU_CHOLMODConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/target/KLU_CHOLMODConfig.cmake + INSTALL_DESTINATION ${SUITESPARSE_PKGFILEDIR}/cmake/KLU_CHOLMOD ) + + write_basic_package_version_file ( + ${CMAKE_CURRENT_BINARY_DIR}/KLU_CHOLMODConfigVersion.cmake + COMPATIBILITY SameMajorVersion ) + + install ( FILES + ${CMAKE_CURRENT_BINARY_DIR}/KLU_CHOLMODConfig.cmake + ${CMAKE_CURRENT_BINARY_DIR}/KLU_CHOLMODConfigVersion.cmake + DESTINATION ${SUITESPARSE_PKGFILEDIR}/cmake/KLU_CHOLMOD ) + + #--------------------------------------------------------------------------- + # create pkg-config file for KLU_CHOLMOD + #--------------------------------------------------------------------------- + + if ( NOT MSVC ) + set ( prefix "${CMAKE_INSTALL_PREFIX}" ) + set ( exec_prefix "\${prefix}" ) + cmake_path ( IS_ABSOLUTE SUITESPARSE_LIBDIR SUITESPARSE_LIBDIR_IS_ABSOLUTE ) + if (SUITESPARSE_LIBDIR_IS_ABSOLUTE) + set ( libdir "${SUITESPARSE_LIBDIR}") + else ( ) + set ( libdir "\${exec_prefix}/${SUITESPARSE_LIBDIR}") + endif ( ) + cmake_path ( IS_ABSOLUTE SUITESPARSE_INCLUDEDIR SUITESPARSE_INCLUDEDIR_IS_ABSOLUTE ) + if (SUITESPARSE_INCLUDEDIR_IS_ABSOLUTE) + set ( includedir "${SUITESPARSE_INCLUDEDIR}") + else ( ) + set ( includedir "\${prefix}/${SUITESPARSE_INCLUDEDIR}") + endif ( ) + if ( BUILD_SHARED_LIBS ) + set ( SUITESPARSE_LIB_BASE_NAME $ ) + else ( ) + set ( SUITESPARSE_LIB_BASE_NAME $ ) + endif ( ) + configure_file ( + Config/KLU_CHOLMOD.pc.in + KLU_CHOLMOD.pc.out + @ONLY + NEWLINE_STYLE LF ) + file ( GENERATE + OUTPUT KLU_CHOLMOD.pc + INPUT ${CMAKE_CURRENT_BINARY_DIR}/KLU_CHOLMOD.pc.out + NEWLINE_STYLE LF ) + install ( FILES + ${CMAKE_CURRENT_BINARY_DIR}/KLU_CHOLMOD.pc + DESTINATION ${SUITESPARSE_PKGFILEDIR}/pkgconfig ) endif ( ) endif ( ) @@ -269,8 +519,7 @@ endif ( ) # Demo library and programs #------------------------------------------------------------------------------- -option ( DEMO "ON: Build the demo programs. OFF (default): do not build the demo programs." off ) -if ( DEMO ) +if ( SUITESPARSE_DEMOS ) #--------------------------------------------------------------------------- # demo library @@ -283,16 +532,29 @@ if ( DEMO ) #--------------------------------------------------------------------------- add_executable ( klu_simple "Demo/klu_simple.c" ) - if ( NOT NCHOLMOD ) + if ( KLU_HAS_CHOLMOD ) add_executable ( kludemo "Demo/kludemo.c" ) add_executable ( kluldemo "Demo/kluldemo.c" ) endif ( ) # Libraries required for Demo programs - target_link_libraries ( klu_simple PUBLIC klu ) - if ( NOT NCHOLMOD ) - target_link_libraries ( kludemo PUBLIC klu_cholmod ) - target_link_libraries ( kluldemo PUBLIC klu_cholmod ) + if ( BUILD_SHARED_LIBS ) + target_link_libraries ( klu_simple PUBLIC KLU ) + else ( ) + target_link_libraries ( klu_simple PUBLIC KLU_static ) + endif ( ) + if ( KLU_HAS_CHOLMOD ) + if ( BUILD_SHARED_LIBS ) + target_link_libraries ( kludemo PUBLIC KLU_CHOLMOD KLU SuiteSparse::CHOLMOD ) + target_link_libraries ( kluldemo PUBLIC KLU_CHOLMOD KLU SuiteSparse::CHOLMOD ) + else ( ) + target_link_libraries ( kludemo PUBLIC KLU_CHOLMOD_static KLU_static SuiteSparse::CHOLMOD ) + target_link_libraries ( kluldemo PUBLIC KLU_CHOLMOD_static KLU_static SuiteSparse::CHOLMOD ) + endif ( ) + if ( NOT WIN32 ) + target_link_libraries ( kludemo PUBLIC m ) + target_link_libraries ( kluldemo PUBLIC m ) + endif ( ) endif ( ) else ( ) @@ -306,4 +568,3 @@ endif ( ) #------------------------------------------------------------------------------- include ( SuiteSparseReport ) - diff --git a/ThirdParty/SuiteSparse/KLU/Config/KLU.pc.in b/ThirdParty/SuiteSparse/KLU/Config/KLU.pc.in new file mode 100644 index 0000000000..df7191b118 --- /dev/null +++ b/ThirdParty/SuiteSparse/KLU/Config/KLU.pc.in @@ -0,0 +1,17 @@ +# KLU, Copyright (c) 2004-2024, Timothy A. Davis. +# All Rights Reserved. +# SPDX-License-Identifier: LGPL-2.1-or-later + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: KLU +URL: https://github.com/DrTimothyAldenDavis/SuiteSparse +Description: Routines for solving sparse linear systems of equations in SuiteSparse +Version: @KLU_VERSION_MAJOR@.@KLU_VERSION_MINOR@.@KLU_VERSION_SUB@ +Requires.private: SuiteSparse_config AMD COLAMD BTF @KLU_STATIC_MODULES@ +Libs: -L${libdir} -l@SUITESPARSE_LIB_BASE_NAME@ +Libs.private: @KLU_STATIC_LIBS@ +Cflags: -I${includedir} diff --git a/ThirdParty/SuiteSparse/KLU/Config/KLUConfig.cmake.in b/ThirdParty/SuiteSparse/KLU/Config/KLUConfig.cmake.in new file mode 100644 index 0000000000..f28e22ba73 --- /dev/null +++ b/ThirdParty/SuiteSparse/KLU/Config/KLUConfig.cmake.in @@ -0,0 +1,193 @@ +#------------------------------------------------------------------------------- +# SuiteSparse/KLU/cmake_modules/KLUConfig.cmake +#------------------------------------------------------------------------------- + +# The following copyright and license applies to just this file only, not to +# the library itself: +# KLUConfig.cmake, Copyright (c) 2023-2024, Timothy A. Davis. All Rights Reserved. +# SPDX-License-Identifier: BSD-3-clause + +#------------------------------------------------------------------------------- + +# Finds the KLU include file and compiled library. +# The following targets are defined: +# SuiteSparse::KLU - for the shared library (if available) +# SuiteSparse::KLU_static - for the static library (if available) + +# For backward compatibility the following variables are set: + +# KLU_INCLUDE_DIR - where to find klu.h +# KLU_LIBRARY - dynamic KLU library +# KLU_STATIC - static KLU library +# KLU_LIBRARIES - libraries when using KLU +# KLU_FOUND - true if KLU found + +# Set ``CMAKE_MODULE_PATH`` to the parent folder where this module file is +# installed. + +#------------------------------------------------------------------------------- + +@PACKAGE_INIT@ + +set ( KLU_DATE "@KLU_DATE@" ) +set ( KLU_VERSION_MAJOR @KLU_VERSION_MAJOR@ ) +set ( KLU_VERSION_MINOR @KLU_VERSION_MINOR@ ) +set ( KLU_VERSION_PATCH @KLU_VERSION_SUB@ ) +set ( KLU_VERSION "@KLU_VERSION_MAJOR@.@KLU_VERSION_MINOR@.@KLU_VERSION_SUB@" ) + +# Check for dependent targets +include ( CMakeFindDependencyMacro ) + +# Look for SuiteSparse_config, BTF, AMD and COLAMD targets +if ( @SUITESPARSE_IN_BUILD_TREE@ ) + if ( NOT TARGET SuiteSparse::SuiteSparseConfig ) + # First check in a common build tree + find_dependency ( SuiteSparse_config @SUITESPARSE_CONFIG_VERSION_MAJOR@.@SUITESPARSE_CONFIG_VERSION_MINOR@ + PATHS ${CMAKE_SOURCE_DIR}/../SuiteSparse_config/build NO_DEFAULT_PATH ) + # Then, check in the currently active CMAKE_MODULE_PATH + if ( NOT SuiteSparse_config_FOUND ) + find_dependency ( SuiteSparse_config @SUITESPARSE_CONFIG_VERSION_MAJOR@.@SUITESPARSE_CONFIG_VERSION_MINOR@ ) + endif ( ) + endif ( ) + + if ( NOT TARGET SuiteSparse::BTF ) + # First check in a common build tree + find_dependency ( BTF @BTF_VERSION_MAJOR@.@BTF_VERSION_MINOR@ + PATHS ${CMAKE_SOURCE_DIR}/../BTF/build NO_DEFAULT_PATH ) + # Then, check in the currently active CMAKE_MODULE_PATH + if ( NOT BTF_FOUND ) + find_dependency ( BTF @BTF_VERSION_MAJOR@.@BTF_VERSION_MINOR@ ) + endif ( ) + endif ( ) + + if ( NOT TARGET SuiteSparse::AMD ) + # First check in a common build tree + find_dependency ( AMD @AMD_VERSION_MAJOR@.@AMD_VERSION_MINOR@ + PATHS ${CMAKE_SOURCE_DIR}/../AMD/build NO_DEFAULT_PATH ) + # Then, check in the currently active CMAKE_MODULE_PATH + if ( NOT AMD_FOUND ) + find_dependency ( AMD @AMD_VERSION_MAJOR@.@AMD_VERSION_MINOR@ ) + endif ( ) + endif ( ) + + if ( NOT TARGET SuiteSparse::COLAMD ) + # First check in a common build tree + find_dependency ( COLAMD @COLAMD_VERSION_MAJOR@.@COLAMD_VERSION_MINOR@ + PATHS ${CMAKE_SOURCE_DIR}/../COLAMD/build NO_DEFAULT_PATH ) + # Then, check in the currently active CMAKE_MODULE_PATH + if ( NOT COLAMD_FOUND ) + find_dependency ( COLAMD @COLAMD_VERSION_MAJOR@.@COLAMD_VERSION_MINOR@ ) + endif ( ) + endif ( ) + +else ( ) + if ( NOT TARGET SuiteSparse::SuiteSparseConfig ) + find_dependency ( SuiteSparse_config @SUITESPARSE_CONFIG_VERSION_MAJOR@.@SUITESPARSE_CONFIG_VERSION_MINOR@ ) + endif ( ) + if ( NOT TARGET SuiteSparse::BTF ) + find_dependency ( BTF @BTF_VERSION_MAJOR@.@BTF_VERSION_MINOR@ ) + endif ( ) + if ( NOT TARGET SuiteSparse::AMD ) + find_dependency ( AMD @AMD_VERSION_MAJOR@.@AMD_VERSION_MINOR@ ) + endif ( ) + if ( NOT TARGET SuiteSparse::COLAMD ) + find_dependency ( COLAMD @COLAMD_VERSION_MAJOR@.@COLAMD_VERSION_MINOR@ ) + endif ( ) +endif ( ) + +if ( NOT SuiteSparse_config_FOUND OR NOT BTF_FOUND OR NOT AMD_FOUND OR NOT COLAMD_FOUND ) + set ( KLU_FOUND OFF ) + return ( ) +endif ( ) + + +# Import target +include ( ${CMAKE_CURRENT_LIST_DIR}/KLUTargets.cmake ) + +# The following is only for backward compatibility with FindKLU. + +set ( _target_shared SuiteSparse::KLU ) +set ( _target_static SuiteSparse::KLU_static ) +set ( _var_prefix "KLU" ) + +if ( NOT @BUILD_SHARED_LIBS@ AND NOT TARGET ${_target_shared} ) + # make sure there is always an import target without suffix ) + add_library ( ${_target_shared} ALIAS ${_target_static} ) +endif ( ) + +get_target_property ( ${_var_prefix}_INCLUDE_DIR ${_target_shared} INTERFACE_INCLUDE_DIRECTORIES ) +if ( ${_var_prefix}_INCLUDE_DIR ) + # First item in SuiteSparse targets contains the "main" header directory. + list ( GET ${_var_prefix}_INCLUDE_DIR 0 ${_var_prefix}_INCLUDE_DIR ) +endif ( ) +get_target_property ( ${_var_prefix}_LIBRARY ${_target_shared} IMPORTED_IMPLIB ) +if ( NOT ${_var_prefix}_LIBRARY ) + get_target_property ( _library_chk ${_target_shared} IMPORTED_LOCATION ) + if ( EXISTS ${_library_chk} ) + set ( ${_var_prefix}_LIBRARY ${_library_chk} ) + endif ( ) +endif ( ) +if ( TARGET ${_target_static} ) + get_target_property ( ${_var_prefix}_STATIC ${_target_static} IMPORTED_LOCATION ) +endif ( ) + +# Check for most common build types +set ( _config_types "Debug" "Release" "RelWithDebInfo" "MinSizeRel" "None" ) + +get_property ( _isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG ) +if ( _isMultiConfig ) + # For multi-configuration generators (e.g., Visual Studio), prefer those + # configurations. + list ( PREPEND _config_types ${CMAKE_CONFIGURATION_TYPES} ) +else ( ) + # For single-configuration generators, prefer the current configuration. + list ( PREPEND _config_types ${CMAKE_BUILD_TYPE} ) +endif ( ) + +list ( REMOVE_DUPLICATES _config_types ) + +foreach ( _config ${_config_types} ) + string ( TOUPPER ${_config} _uc_config ) + if ( NOT ${_var_prefix}_LIBRARY ) + get_target_property ( _library_chk ${_target_shared} + IMPORTED_IMPLIB_${_uc_config} ) + if ( EXISTS ${_library_chk} ) + set ( ${_var_prefix}_LIBRARY ${_library_chk} ) + endif ( ) + endif ( ) + if ( NOT ${_var_prefix}_LIBRARY ) + get_target_property ( _library_chk ${_target_shared} + IMPORTED_LOCATION_${_uc_config} ) + if ( EXISTS ${_library_chk} ) + set ( ${_var_prefix}_LIBRARY ${_library_chk} ) + endif ( ) + endif ( ) + if ( TARGET ${_target_static} AND NOT ${_var_prefix}_STATIC ) + get_target_property ( _library_chk ${_target_static} + IMPORTED_LOCATION_${_uc_config} ) + if ( EXISTS ${_library_chk} ) + set ( ${_var_prefix}_STATIC ${_library_chk} ) + endif ( ) + endif ( ) +endforeach ( ) + +set ( KLU_LIBRARIES ${KLU_LIBRARY} ) + +macro ( suitesparse_check_exist _var _files ) + # ignore generator expressions + string ( GENEX_STRIP "${_files}" _files2 ) + + foreach ( _file ${_files2} ) + if ( NOT EXISTS "${_file}" ) + message ( FATAL_ERROR "File or directory ${_file} referenced by variable ${_var} does not exist!" ) + endif ( ) + endforeach () +endmacro ( ) + +suitesparse_check_exist ( KLU_INCLUDE_DIR ${KLU_INCLUDE_DIR} ) +suitesparse_check_exist ( KLU_LIBRARY ${KLU_LIBRARY} ) + +message ( STATUS "KLU version: ${KLU_VERSION}" ) +message ( STATUS "KLU include: ${KLU_INCLUDE_DIR}" ) +message ( STATUS "KLU library: ${KLU_LIBRARY}" ) +message ( STATUS "KLU static: ${KLU_STATIC}" ) diff --git a/ThirdParty/SuiteSparse/KLU/Config/klu.h.in b/ThirdParty/SuiteSparse/KLU/Config/klu.h.in index 769a82aa6f..1f02c4be5f 100644 --- a/ThirdParty/SuiteSparse/KLU/Config/klu.h.in +++ b/ThirdParty/SuiteSparse/KLU/Config/klu.h.in @@ -2,7 +2,7 @@ // KLU/Source/klu.h: include file for KLU //------------------------------------------------------------------------------ -// KLU, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// KLU, Copyright (c) 2004-2024, University of Florida. All Rights Reserved. // Authors: Timothy A. Davis and Ekanathan Palamadai. // SPDX-License-Identifier: LGPL-2.1+ @@ -13,15 +13,15 @@ #ifndef _KLU_H #define _KLU_H +#include "amd.h" +#include "colamd.h" +#include "btf.h" + /* make it easy for C++ programs to include KLU */ #ifdef __cplusplus extern "C" { #endif -#include "amd.h" -#include "colamd.h" -#include "btf.h" - /* -------------------------------------------------------------------------- */ /* Symbolic object - contains the pre-ordering computed by klu_analyze */ /* -------------------------------------------------------------------------- */ @@ -795,6 +795,15 @@ void *klu_l_free (void *, size_t, size_t, klu_l_common *) ; void *klu_l_realloc (size_t, size_t, size_t, void *, klu_l_common *) ; +//------------------------------------------------------------------------------ +// klu_version: return KLU version +//------------------------------------------------------------------------------ + +void klu_version (int version [3]) ; + +#ifdef __cplusplus +} +#endif /* ========================================================================== */ /* === KLU version ========================================================== */ @@ -819,10 +828,29 @@ void *klu_l_realloc (size_t, size_t, size_t, void *, klu_l_common *) ; #define KLU_SUB_VERSION @KLU_VERSION_MINOR@ #define KLU_SUBSUB_VERSION @KLU_VERSION_SUB@ -#define KLU_VERSION_CODE(main,sub) ((main) * 1000 + (sub)) -#define KLU_VERSION KLU_VERSION_CODE(KLU_MAIN_VERSION,KLU_SUB_VERSION) +#define KLU_VERSION_CODE(main,sub) SUITESPARSE_VER_CODE(main,sub) +#define KLU_VERSION KLU_VERSION_CODE(@KLU_VERSION_MAJOR@,@KLU_VERSION_MINOR@) -#ifdef __cplusplus -} +#define KLU__VERSION SUITESPARSE__VERCODE(@KLU_VERSION_MAJOR@,@KLU_VERSION_MINOR@,@KLU_VERSION_SUB@) +#if !defined (SUITESPARSE__VERSION) || \ + (SUITESPARSE__VERSION < SUITESPARSE__VERCODE(7,6,0)) +#error "KLU @KLU_VERSION_MAJOR@.@KLU_VERSION_MINOR@.@KLU_VERSION_SUB@ requires SuiteSparse_config 7.6.0 or later" +#endif + +#if !defined (AMD__VERSION) || \ + (AMD__VERSION < SUITESPARSE__VERCODE(3,3,1)) +#error "KLU @KLU_VERSION_MAJOR@.@KLU_VERSION_MINOR@.@KLU_VERSION_SUB@ requires AMD 3.3.1 or later" #endif + +#if !defined (COLAMD__VERSION) || \ + (COLAMD__VERSION < SUITESPARSE__VERCODE(3,3,2)) +#error "KLU @KLU_VERSION_MAJOR@.@KLU_VERSION_MINOR@.@KLU_VERSION_SUB@ requires COLAMD 3.3.2 or later" +#endif + +#if !defined (BTF__VERSION) || \ + (BTF__VERSION < SUITESPARSE__VERCODE(2,3,1)) +#error "KLU @KLU_VERSION_MAJOR@.@KLU_VERSION_MINOR@.@KLU_VERSION_SUB@ requires BTF 2.3.1 or later" #endif + +#endif + diff --git a/ThirdParty/SuiteSparse/KLU/Doc/ChangeLog b/ThirdParty/SuiteSparse/KLU/Doc/ChangeLog index b4a4edabeb..d4313dbc55 100644 --- a/ThirdParty/SuiteSparse/KLU/Doc/ChangeLog +++ b/ThirdParty/SuiteSparse/KLU/Doc/ChangeLog @@ -1,3 +1,33 @@ +Jan 20, 2024: version 2.3.2 + + * revise version numbers for dependencies + +Jan 10, 2024: version 2.3.1 + + * MATLAB interface: add -DNO_SSIZE_T for Windows + * minor updates to build system + +Dec 30, 2023: version 2.3.0 + + * major change to build system: by Markus Mützel + * klu_version: added to return version of KLU + +Oct 23, 2023: version 2.2.2 + + * for SuiteSparse 7.3.0: update for CHOLMOD 5.0.0 + +Sept 18, 2023: version 2.2.1 + + * cmake update: add "None" build type, from Antonio Rojas, for Arch Linux + +Sept 8, 2023: version 2.2.0 + + * cmake updates: SuiteSparse:: namespace by Markus Muetzel + +June 16, 2023: version 2.0.4 + + * cmake build system updates: update by Markus Muetzel + Jan 17, 2023: version 2.0.3 * SuiteSparse_config: now v7.0.0 diff --git a/ThirdParty/SuiteSparse/KLU/Doc/KLU_UserGuide.tex b/ThirdParty/SuiteSparse/KLU/Doc/KLU_UserGuide.tex index 8ed9466291..e710ff3887 100644 --- a/ThirdParty/SuiteSparse/KLU/Doc/KLU_UserGuide.tex +++ b/ThirdParty/SuiteSparse/KLU/Doc/KLU_UserGuide.tex @@ -51,7 +51,7 @@ \section{License and Copyright} %------------------------------------------------------------------------------ -KLU, Copyright\copyright 2004-2022 University of Florida. +KLU, Copyright\copyright 2004-2023 University of Florida. All Rights Reserved. KLU is available under alternate licenses; contact T. Davis for details. diff --git a/ThirdParty/SuiteSparse/KLU/Doc/klu_version.tex b/ThirdParty/SuiteSparse/KLU/Doc/klu_version.tex index f15e030533..5e71c34bcb 100644 --- a/ThirdParty/SuiteSparse/KLU/Doc/klu_version.tex +++ b/ThirdParty/SuiteSparse/KLU/Doc/klu_version.tex @@ -1,2 +1,2 @@ % version of SuiteSparse/KLU -\date{VERSION 2.0.3, Jan 17, 2023} +\date{VERSION 2.3.2, Jan 20, 2024} diff --git a/ThirdParty/SuiteSparse/KLU/Include/klu.h b/ThirdParty/SuiteSparse/KLU/Include/klu.h index 80204ecf75..22155ff8b5 100644 --- a/ThirdParty/SuiteSparse/KLU/Include/klu.h +++ b/ThirdParty/SuiteSparse/KLU/Include/klu.h @@ -2,7 +2,7 @@ // KLU/Source/klu.h: include file for KLU //------------------------------------------------------------------------------ -// KLU, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// KLU, Copyright (c) 2004-2024, University of Florida. All Rights Reserved. // Authors: Timothy A. Davis and Ekanathan Palamadai. // SPDX-License-Identifier: LGPL-2.1+ @@ -13,15 +13,15 @@ #ifndef _KLU_H #define _KLU_H +#include "amd.h" +#include "colamd.h" +#include "btf.h" + /* make it easy for C++ programs to include KLU */ #ifdef __cplusplus extern "C" { #endif -#include "amd.h" -#include "colamd.h" -#include "btf.h" - /* -------------------------------------------------------------------------- */ /* Symbolic object - contains the pre-ordering computed by klu_analyze */ /* -------------------------------------------------------------------------- */ @@ -795,6 +795,15 @@ void *klu_l_free (void *, size_t, size_t, klu_l_common *) ; void *klu_l_realloc (size_t, size_t, size_t, void *, klu_l_common *) ; +//------------------------------------------------------------------------------ +// klu_version: return KLU version +//------------------------------------------------------------------------------ + +void klu_version (int version [3]) ; + +#ifdef __cplusplus +} +#endif /* ========================================================================== */ /* === KLU version ========================================================== */ @@ -814,15 +823,34 @@ void *klu_l_realloc (size_t, size_t, size_t, void *, klu_l_common *) ; * #endif */ -#define KLU_DATE "Jan 17, 2023" +#define KLU_DATE "Jan 20, 2024" #define KLU_MAIN_VERSION 2 -#define KLU_SUB_VERSION 0 -#define KLU_SUBSUB_VERSION 3 +#define KLU_SUB_VERSION 3 +#define KLU_SUBSUB_VERSION 2 -#define KLU_VERSION_CODE(main,sub) ((main) * 1000 + (sub)) -#define KLU_VERSION KLU_VERSION_CODE(KLU_MAIN_VERSION,KLU_SUB_VERSION) +#define KLU_VERSION_CODE(main,sub) SUITESPARSE_VER_CODE(main,sub) +#define KLU_VERSION KLU_VERSION_CODE(2,3) -#ifdef __cplusplus -} +#define KLU__VERSION SUITESPARSE__VERCODE(2,3,2) +#if !defined (SUITESPARSE__VERSION) || \ + (SUITESPARSE__VERSION < SUITESPARSE__VERCODE(7,6,0)) +#error "KLU 2.3.2 requires SuiteSparse_config 7.6.0 or later" +#endif + +#if !defined (AMD__VERSION) || \ + (AMD__VERSION < SUITESPARSE__VERCODE(3,3,1)) +#error "KLU 2.3.2 requires AMD 3.3.1 or later" #endif + +#if !defined (COLAMD__VERSION) || \ + (COLAMD__VERSION < SUITESPARSE__VERCODE(3,3,2)) +#error "KLU 2.3.2 requires COLAMD 3.3.2 or later" +#endif + +#if !defined (BTF__VERSION) || \ + (BTF__VERSION < SUITESPARSE__VERCODE(2,3,1)) +#error "KLU 2.3.2 requires BTF 2.3.1 or later" #endif + +#endif + diff --git a/ThirdParty/SuiteSparse/KLU/Include/klu_internal.h b/ThirdParty/SuiteSparse/KLU/Include/klu_internal.h index 60fa514a3a..b30f937759 100644 --- a/ThirdParty/SuiteSparse/KLU/Include/klu_internal.h +++ b/ThirdParty/SuiteSparse/KLU/Include/klu_internal.h @@ -2,7 +2,7 @@ // KLU/Include/klu_internal.h: internal include file for KLU //------------------------------------------------------------------------------ -// KLU, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// KLU, Copyright (c) 2004-2023, University of Florida. All Rights Reserved. // Authors: Timothy A. Davis and Ekanathan Palamadai. // SPDX-License-Identifier: LGPL-2.1+ diff --git a/ThirdParty/SuiteSparse/KLU/Include/klu_version.h b/ThirdParty/SuiteSparse/KLU/Include/klu_version.h index f2d4915c97..0e0951d112 100644 --- a/ThirdParty/SuiteSparse/KLU/Include/klu_version.h +++ b/ThirdParty/SuiteSparse/KLU/Include/klu_version.h @@ -2,7 +2,7 @@ // KLU/Include/klu_version.h: internal include file for KLU //------------------------------------------------------------------------------ -// KLU, Copyright (c) 2004-2022, University of Florida. All Rights Reserved. +// KLU, Copyright (c) 2004-2023, University of Florida. All Rights Reserved. // Authors: Timothy A. Davis and Ekanathan Palamadai. // SPDX-License-Identifier: LGPL-2.1+ diff --git a/ThirdParty/SuiteSparse/KLU/Makefile b/ThirdParty/SuiteSparse/KLU/Makefile index 896ea4a1b6..97bb49f4e8 100644 --- a/ThirdParty/SuiteSparse/KLU/Makefile +++ b/ThirdParty/SuiteSparse/KLU/Makefile @@ -36,23 +36,23 @@ default: library # default is to install only in /usr/local library: - ( cd build && cmake $(CMAKE_OPTIONS) .. && cmake --build . -j${JOBS} ) + ( cd build && cmake $(CMAKE_OPTIONS) .. && cmake --build . --config Release -j${JOBS} ) # install only in SuiteSparse/lib and SuiteSparse/include local: - ( cd build && cmake $(CMAKE_OPTIONS) -DLOCAL_INSTALL=1 .. && cmake --build . -j${JOBS} ) + ( cd build && cmake $(CMAKE_OPTIONS) -USUITESPARSE_PKGFILEDIR -DSUITESPARSE_LOCAL_INSTALL=1 .. && cmake --build . --config Release -j${JOBS} ) # install only in /usr/local (default) global: - ( cd build && cmake $(CMAKE_OPTIONS) -DLOCAL_INSTALL=0 .. && cmake --build . -j${JOBS} ) + ( cd build && cmake $(CMAKE_OPTIONS) -USUITESPARSE_PKGFILEDIR -DSUITESPARSE_LOCAL_INSTALL=0 .. && cmake --build . --config Release -j${JOBS} ) debug: - ( cd build && cmake $(CMAKE_OPTIONS) -DCMAKE_BUILD_TYPE=Debug .. && cmake --build . -j${JOBS} ) + ( cd build && cmake $(CMAKE_OPTIONS) -DCMAKE_BUILD_TYPE=Debug .. && cmake --build . --config Debug -j${JOBS} ) all: library demos: library - ( cd build && cmake $(CMAKE_OPTIONS) -DDEMO=1 .. && cmake --build . -j${JOBS} ) + ( cd build && cmake $(CMAKE_OPTIONS) -DSUITESPARSE_DEMOS=1 .. && cmake --build . --config Release -j${JOBS} ) - ./build/klu_simple - ./build/kludemo < ./Matrix/1c.mtx - ./build/kludemo < ./Matrix/arrowc.mtx diff --git a/ThirdParty/SuiteSparse/KLU/Source/klu_version.c b/ThirdParty/SuiteSparse/KLU/Source/klu_version.c new file mode 100644 index 0000000000..e96215f580 --- /dev/null +++ b/ThirdParty/SuiteSparse/KLU/Source/klu_version.c @@ -0,0 +1,19 @@ +//------------------------------------------------------------------------------ +// KLU/Source/klu_version: return KLU version +//------------------------------------------------------------------------------ + +// KLU, Copyright (c) 2004-2023, University of Florida. All Rights Reserved. +// Authors: Timothy A. Davis and Ekanathan Palamadai. +// SPDX-License-Identifier: LGPL-2.1+ + +//------------------------------------------------------------------------------ + +#include "klu_internal.h" + +void klu_version (int version [3]) +{ + version [0] = KLU_MAIN_VERSION ; + version [1] = KLU_SUB_VERSION ; + version [2] = KLU_SUBSUB_VERSION ; +} + diff --git a/ThirdParty/SuiteSparse/KLU/cmake_modules/FindKLU.cmake b/ThirdParty/SuiteSparse/KLU/cmake_modules/FindKLU.cmake deleted file mode 100644 index cd3f4040e4..0000000000 --- a/ThirdParty/SuiteSparse/KLU/cmake_modules/FindKLU.cmake +++ /dev/null @@ -1,129 +0,0 @@ -#------------------------------------------------------------------------------- -# SuiteSparse/KLU/cmake_modules/FindKLU.cmake -#------------------------------------------------------------------------------- - -# The following copyright and license applies to just this file only, not to -# the library itself: -# FindKLU.cmake, Copyright (c) 2022-2023, Timothy A. Davis. All Rights Reserved. -# SPDX-License-Identifier: BSD-3-clause - -#------------------------------------------------------------------------------- - -# Finds the KLU include file and compiled library and sets: - -# KLU_INCLUDE_DIR - where to find klu.h -# KLU_LIBRARY - dynamic KLU library -# KLU_STATIC - static KLU library -# KLU_LIBRARIES - libraries when using KLU -# KLU_FOUND - true if KLU found - -# set ``KLU_ROOT`` to a KLU installation root to -# tell this module where to look. - -# All the Find*.cmake files in SuiteSparse are installed by 'make install' into -# /usr/local/lib/cmake/SuiteSparse (where '/usr/local' is the -# ${CMAKE_INSTALL_PREFIX}). To access this file, place the following commands -# in your CMakeLists.txt file. See also SuiteSparse/Example/CMakeLists.txt: -# -# set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} -# ${CMAKE_INSTALL_PREFIX}/lib/cmake/SuiteSparse ) - -#------------------------------------------------------------------------------- - -# include files for KLU -find_path ( KLU_INCLUDE_DIR - NAMES klu.h - HINTS ${CMAKE_SOURCE_DIR}/.. - HINTS ${CMAKE_SOURCE_DIR}/../SuiteSparse/KLU - HINTS ${CMAKE_SOURCE_DIR}/../KLU - PATH_SUFFIXES include Include -) - -# dynamic KLU library (or static if no dynamic library was built) -find_library ( KLU_LIBRARY - NAMES klu klu_static - HINTS ${CMAKE_SOURCE_DIR}/.. - HINTS ${CMAKE_SOURCE_DIR}/../SuiteSparse/KLU - HINTS ${CMAKE_SOURCE_DIR}/../KLU - PATH_SUFFIXES lib build build/Release build/Debug -) - -if ( MSVC ) - set ( STATIC_NAME klu_static ) -else ( ) - set ( STATIC_NAME klu ) - set ( save ${CMAKE_FIND_LIBRARY_SUFFIXES} ) - set ( CMAKE_FIND_LIBRARY_SUFFIXES - ${CMAKE_STATIC_LIBRARY_SUFFIX} ${CMAKE_FIND_LIBRARY_SUFFIXES} ) -endif ( ) - -# static KLU library -find_library ( KLU_STATIC - NAMES ${STATIC_NAME} - HINTS ${CMAKE_SOURCE_DIR}/.. - HINTS ${CMAKE_SOURCE_DIR}/../SuiteSparse/KLU - HINTS ${CMAKE_SOURCE_DIR}/../KLU - PATH_SUFFIXES lib build build/Release build/Debug -) - -if ( NOT MSVC ) - # restore the CMAKE_FIND_LIBRARY_SUFFIXES variable - set ( CMAKE_FIND_LIBRARY_SUFFIXES ${save} ) -endif ( ) - -# get version of the library from the dynamic library name -get_filename_component ( KLU_LIBRARY ${KLU_LIBRARY} REALPATH ) -get_filename_component ( KLU_FILENAME ${KLU_LIBRARY} NAME ) -string ( - REGEX MATCH "[0-9]+.[0-9]+.[0-9]+" - KLU_VERSION - ${KLU_FILENAME} -) - -# set ( KLU_VERSION "" ) -if ( EXISTS "${KLU_INCLUDE_DIR}" AND NOT KLU_VERSION ) - # if the version does not appear in the filename, read the include file - file ( STRINGS ${KLU_INCLUDE_DIR}/klu.h KLU_MAJOR_STR - REGEX "define KLU_MAIN_VERSION" ) - file ( STRINGS ${KLU_INCLUDE_DIR}/klu.h KLU_MINOR_STR - REGEX "define KLU_SUB_VERSION" ) - file ( STRINGS ${KLU_INCLUDE_DIR}/klu.h KLU_PATCH_STR - REGEX "define KLU_SUBSUB_VERSION" ) - message ( STATUS "major: ${KLU_MAJOR_STR}" ) - message ( STATUS "minor: ${KLU_MINOR_STR}" ) - message ( STATUS "patch: ${KLU_PATCH_STR}" ) - string ( REGEX MATCH "[0-9]+" KLU_MAJOR ${KLU_MAJOR_STR} ) - string ( REGEX MATCH "[0-9]+" KLU_MINOR ${KLU_MINOR_STR} ) - string ( REGEX MATCH "[0-9]+" KLU_PATCH ${KLU_PATCH_STR} ) - set (KLU_VERSION "${KLU_MAJOR}.${KLU_MINOR}.${KLU_PATCH}") -endif ( ) - -set ( KLU_LIBRARIES ${KLU_LIBRARY} ) - -include (FindPackageHandleStandardArgs) - -find_package_handle_standard_args ( KLU - REQUIRED_VARS KLU_LIBRARY KLU_INCLUDE_DIR - VERSION_VAR KLU_VERSION - ) - -mark_as_advanced ( - KLU_INCLUDE_DIR - KLU_LIBRARY - KLU_STATIC - KLU_LIBRARIES - ) - -if ( KLU_FOUND ) - message ( STATUS "KLU version: ${KLU_VERSION}" ) - message ( STATUS "KLU include: ${KLU_INCLUDE_DIR}" ) - message ( STATUS "KLU library: ${KLU_LIBRARY}" ) - message ( STATUS "KLU static: ${KLU_STATIC}" ) -else ( ) - message ( STATUS "KLU not found" ) - set ( KLU_INCLUDE_DIR "" ) - set ( KLU_LIBRARIES "" ) - set ( KLU_LIBRARY "" ) - set ( KLU_STATIC "" ) -endif ( ) - diff --git a/ThirdParty/SuiteSparse/KLU/cmake_modules/FindKLU_CHOLMOD.cmake b/ThirdParty/SuiteSparse/KLU/cmake_modules/FindKLU_CHOLMOD.cmake deleted file mode 100644 index d3b3663228..0000000000 --- a/ThirdParty/SuiteSparse/KLU/cmake_modules/FindKLU_CHOLMOD.cmake +++ /dev/null @@ -1,137 +0,0 @@ -#------------------------------------------------------------------------------- -# SuiteSparse/KLU/cmake_modules/FindKLU_CHOLMOD.cmake -#------------------------------------------------------------------------------- - -# The following copyright and license applies to just this file only, not to -# the library itself: -# FindKLU_CHOLMOD.cmake, Copyright (c) 2022-2023, Timothy A. Davis. All Rights Reserved. -# SPDX-License-Identifier: BSD-3-clause - -#------------------------------------------------------------------------------- - -# Finds the KLU_CHOLMOD include file and compiled library and sets: - -# KLU_CHOLMOD_INCLUDE_DIR - where to find klu_cholmod.h -# KLU_CHOLMOD_LIBRARY - compiled KLU_CHOLMOD library -# KLU_CHOLMOD_LIBRARIES - libraries when using KLU_CHOLMOD -# KLU_CHOLMOD_FOUND - true if KLU_CHOLMOD found - -# set ``KLU_CHOLMOD_ROOT`` to a KLU_CHOLMOD installation root to -# tell this module where to look. - -# All the Find*.cmake files in SuiteSparse are installed by 'make install' into -# /usr/local/lib/cmake/SuiteSparse (where '/usr/local' is the -# ${CMAKE_INSTALL_PREFIX}). To access this file, place the following commands -# in your CMakeLists.txt file. See also SuiteSparse/Example/CMakeLists.txt: -# -# set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} -# ${CMAKE_INSTALL_PREFIX}/lib/cmake/SuiteSparse ) - -#------------------------------------------------------------------------------- - -# include files for KLU_CHOLMOD -find_path ( KLU_CHOLMOD_INCLUDE_DIR - NAMES klu_cholmod.h - HINTS ${CMAKE_SOURCE_DIR}/.. - HINTS ${CMAKE_SOURCE_DIR}/../SuiteSparse/KLU/User - HINTS ${CMAKE_SOURCE_DIR}/../KLU/User - PATH_SUFFIXES include Include -) - -# include files for KLU -find_path ( KLU_INCLUDE_DIR - NAMES klu.h - HINTS ${CMAKE_SOURCE_DIR}/.. - HINTS ${CMAKE_SOURCE_DIR}/../SuiteSparse/KLU - HINTS ${CMAKE_SOURCE_DIR}/../KLU - PATH_SUFFIXES include Include -) - -# dynamic KLU_CHOLMOD library (or static if no dynamic library was built) -find_library ( KLU_CHOLMOD_LIBRARY - NAMES klu_cholmod klu_cholmod_static - HINTS ${CMAKE_SOURCE_DIR}/.. - HINTS ${CMAKE_SOURCE_DIR}/../SuiteSparse/KLU/User - HINTS ${CMAKE_SOURCE_DIR}/../KLU/User - PATH_SUFFIXES lib build build/Release build/Debug -) - -if ( MSVC ) - set ( STATIC_NAME klu_cholmod_static ) -else ( ) - set ( STATIC_NAME klu_cholmod ) - set ( save ${CMAKE_FIND_LIBRARY_SUFFIXES} ) - set ( CMAKE_FIND_LIBRARY_SUFFIXES - ${CMAKE_STATIC_LIBRARY_SUFFIX} ${CMAKE_FIND_LIBRARY_SUFFIXES} ) -endif ( ) - -# static KLU_CHOLMOD library -find_library ( KLU_CHOLMOD_STATIC - NAMES ${STATIC_NAME} - HINTS ${CMAKE_SOURCE_DIR}/.. - HINTS ${CMAKE_SOURCE_DIR}/../SuiteSparse/KLU/User - HINTS ${CMAKE_SOURCE_DIR}/../KLU/User - PATH_SUFFIXES lib build build/Release build/Debug -) - -if ( NOT MSVC ) - # restore the CMAKE_FIND_LIBRARY_SUFFIXES variable - set ( CMAKE_FIND_LIBRARY_SUFFIXES ${save} ) -endif ( ) - -# get version of the library from the dynamic library name -get_filename_component ( KLU_CHOLMOD_LIBRARY ${KLU_CHOLMOD_LIBRARY} REALPATH ) -get_filename_component ( KLU_CHOLMOD_FILENAME ${KLU_CHOLMOD_LIBRARY} NAME ) -string ( - REGEX MATCH "[0-9]+.[0-9]+.[0-9]+" - KLU_CHOLMOD_VERSION - ${KLU_CHOLMOD_FILENAME} -) - -# set ( KLU_CHOLMOD_VERSION "" ) -if ( EXISTS "${KLU_INCLUDE_DIR}" AND NOT KLU_CHOLMOD_VERSION ) - # if the version does not appear in the filename, read the include file - file ( STRINGS ${KLU_INCLUDE_DIR}/klu.h KLU_CHOLMOD_MAJOR_STR - REGEX "define KLU_MAIN_VERSION" ) - file ( STRINGS ${KLU_INCLUDE_DIR}/klu.h KLU_CHOLMOD_MINOR_STR - REGEX "define KLU_SUB_VERSION" ) - file ( STRINGS ${KLU_INCLUDE_DIR}/klu.h KLU_CHOLMOD_PATCH_STR - REGEX "define KLU_SUBSUB_VERSION" ) - message ( STATUS "major: ${KLU_CHOLMOD_MAJOR_STR}" ) - message ( STATUS "minor: ${KLU_CHOLMOD_MINOR_STR}" ) - message ( STATUS "patch: ${KLU_CHOLMOD_PATCH_STR}" ) - string ( REGEX MATCH "[0-9]+" KLU_CHOLMOD_MAJOR ${KLU_CHOLMOD_MAJOR_STR} ) - string ( REGEX MATCH "[0-9]+" KLU_CHOLMOD_MINOR ${KLU_CHOLMOD_MINOR_STR} ) - string ( REGEX MATCH "[0-9]+" KLU_CHOLMOD_PATCH ${KLU_CHOLMOD_PATCH_STR} ) - set (KLU_CHOLMOD_VERSION "${KLU_CHOLMOD_MAJOR}.${KLU_CHOLMOD_MINOR}.${KLU_CHOLMOD_PATCH}") -endif ( ) - -set ( KLU_CHOLMOD_LIBRARIES ${KLU_CHOLMOD_LIBRARY} ) - -include (FindPackageHandleStandardArgs) - -find_package_handle_standard_args ( KLU_CHOLMOD - REQUIRED_VARS KLU_CHOLMOD_LIBRARY KLU_CHOLMOD_INCLUDE_DIR - VERSION_VAR KLU_CHOLMOD_VERSION -) - -mark_as_advanced ( - KLU_CHOLMOD_INCLUDE_DIR - KLU_CHOLMOD_LIBRARY - KLU_CHOLMOD_STATIC - KLU_CHOLMOD_LIBRARIES -) - -if ( KLU_CHOLMOD_FOUND ) - message ( STATUS "KLU_CHOLMOD version: ${KLU_CHOLMOD_VERSION}" ) - message ( STATUS "KLU_CHOLMOD include: ${KLU_CHOLMOD_INCLUDE_DIR}" ) - message ( STATUS "KLU_CHOLMOD library: ${KLU_CHOLMOD_LIBRARY}" ) - message ( STATUS "KLU_CHOLMOD static: ${KLU_CHOLMOD_STATIC}" ) -else ( ) - message ( STATUS "KLU_CHOLMOD not found" ) - set ( KLU_CHOLMOD_INCLUDE_DIR "" ) - set ( KLU_CHOLMOD_LIBRARIES "" ) - set ( KLU_CHOLMOD_LIBRARY "" ) - set ( KLU_CHOLMOD_STATIC "" ) -endif ( ) - diff --git a/ThirdParty/SuiteSparse/LICENSE.txt b/ThirdParty/SuiteSparse/LICENSE.txt index 0e51e9e0c8..8c6d47346c 100644 --- a/ThirdParty/SuiteSparse/LICENSE.txt +++ b/ThirdParty/SuiteSparse/LICENSE.txt @@ -6,7 +6,7 @@ found in the lists below. SPEX: a Sparse Left-looking Integer-Preserving LU Factorization - Copyright (c) 2019-2022, Christopher Lourenco, JinHao Chen, Erick Moreno- + Copyright (c) 2019-2023, Christopher Lourenco, JinHao Chen, Erick Moreno- Centeno, and Timothy A. Davis. Available at: @@ -46,7 +46,7 @@ found in the lists below. ==> AMD/Doc/License.txt <== - AMD, Copyright (c), 1996-2022, Timothy A. Davis, + AMD, Copyright (c), 1996-2023, Timothy A. Davis, Patrick R. Amestoy, and Iain S. Duff. All Rights Reserved. Availability: @@ -82,7 +82,7 @@ found in the lists below. DAMAGE. ==> BTF/Doc/License.txt <== - BTF, Copyright (C) 2004-2022, University of Florida + BTF, Copyright (C) 2004-2023, University of Florida by Timothy A. Davis and Ekanathan Palamadai. BTF is also available under other licenses; contact authors for details. http://suitesparse.com @@ -143,7 +143,7 @@ found in the lists below. ==> CCOLAMD/Doc/License.txt <== CCOLAMD: constrained column approximate minimum degree ordering - Copyright (C) 2005-2022, Univ. of Florida. Authors: Timothy A. Davis, + Copyright (C) 2005-2023, Univ. of Florida. Authors: Timothy A. Davis, Sivasankaran Rajamanickam, and Stefan Larimore. Closely based on COLAMD by Davis, Stefan Larimore, in collaboration with Esmond Ng, and John Gilbert. http://suitesparse.com @@ -183,7 +183,7 @@ found in the lists below. ==> Check/License.txt <== -------------------------------------------------------------------------------- - CHOLMOD/Check Module. Copyright (C) 2005-2022, Timothy A. Davis CHOLMOD is + CHOLMOD/Check Module. Copyright (C) 2005-2023, Timothy A. Davis CHOLMOD is also available under other licenses; contact authors for details. http://suitesparse.com @@ -210,7 +210,7 @@ found in the lists below. ==> Cholesky/License.txt <== -------------------------------------------------------------------------------- - CHOLMOD/Cholesky module, Copyright (C) 2005-2022, Timothy A. Davis. + CHOLMOD/Cholesky module, Copyright (C) 2005-2023, Timothy A. Davis. CHOLMOD is also available under other licenses; contact authors for details. http://suitesparse.com @@ -235,14 +235,14 @@ found in the lists below. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -------------------------------------------------------------------------------- - ==> Core/License.txt <== + ==> Utility/License.txt <== -------------------------------------------------------------------------------- - CHOLMOD/Core Module. Copyright (C) 2005-2022, Univ. of Florida. Author: - Timothy A. Davis. CHOLMOD is also available under other licenses; contact - authors for details. http://suitesparse.com + CHOLMOD/Utility Module, Copyright (C) 2023, Timothy A. Davis. + CHOLMOD is also available under other licenses; contact authors for + details. http://suitesparse.com - Note that this license is for the CHOLMOD/Core module only. + Note that this license is for the CHOLMOD/Utility module only. All CHOLMOD modules are licensed separately. @@ -267,7 +267,7 @@ found in the lists below. ==> Demo/License.txt <== -------------------------------------------------------------------------------- - CHOLMOD/Demo Module. Copyright (C) 2005-2022, Timothy A. Davis. CHOLMOD + CHOLMOD/Demo Module. Copyright (C) 2005-2023, Timothy A. Davis. CHOLMOD is also available under other licenses; contact authors for details. http://suitesparse.com @@ -296,35 +296,18 @@ found in the lists below. ==> Include/License.txt <== -------------------------------------------------------------------------------- - CHOLMOD/Include/* files. Copyright (C) 2005-2022, either Univ. of Florida - or T. Davis, depending on the file. - - Each file is licensed separately, according to the Module for which it - contains definitions and prototypes: - - Include/cholmod.h LGPL - Include/cholmod_camd.h part of Partition module - Include/cholmod_check.h part of Check module - Include/cholmod_cholesky.h part of Cholesky module - Include/cholmod_complexity.h LGPL - Include/cholmod_config.h LGPL - Include/cholmod_core.h part of Core module - Include/cholmod_function.h no license; freely usable, no restrictions - Include/cholmod_gpu.h part of GPU module - Include/cholmod_gpu_kernels.h part of GPU module - Include/cholmod_internal.h LGPL - Include/cholmod_io64.h LGPL - Include/cholmod_matrixops.h part of MatrixOps module - Include/cholmod_modify.h part of Modify module - Include/cholmod_partition.h part of Partition module - Include/cholmod_supernodal.h part of Supernodal module - Include/cholmod_template.h LGPL + CHOLMOD/Include/* files. Copyright (C) 2005-2023 + + CHOLMOD/Include/cholmod.h SPDX-License-Identifier: Apache-2.0 + CHOLMOD/Include/cholmod_internal.h SPDX-License-Identifier: Apache-2.0 + CHOLMOD/Include/cholmod_template.h SPDX-License-Identifier: Apache-2.0 + CHOLMOD/Include/cholmod_types.h SPDX-License-Identifier: Apache-2.0 -------------------------------------------------------------------------------- ==> MATLAB/License.txt <== -------------------------------------------------------------------------------- - CHOLMOD/MATLAB Module. Copyright (C) 2005-2022, Timothy A. Davis. CHOLMOD + CHOLMOD/MATLAB Module. Copyright (C) 2005-2023, Timothy A. Davis. CHOLMOD is also available under other licenses; contact authors for details. MATLAB(tm) is a Registered Trademark of The MathWorks, Inc. http://suitesparse.com @@ -353,7 +336,7 @@ found in the lists below. ==> MatrixOps/License.txt <== -------------------------------------------------------------------------------- - CHOLMOD/MatrixOps Module. Copyright (C) 2005-2022, Timothy A. Davis. + CHOLMOD/MatrixOps Module. Copyright (C) 2005-2023, Timothy A. Davis. CHOLMOD is also available under other licenses; contact authors for details. http://suitesparse.com @@ -382,7 +365,7 @@ found in the lists below. ==> Modify/License.txt <== -------------------------------------------------------------------------------- - CHOLMOD/Modify Module. Copyright (C) 2005-2022, Timothy A. Davis and + CHOLMOD/Modify Module. Copyright (C) 2005-2023, Timothy A. Davis and William W. Hager. CHOLMOD is also available under other licenses; contact authors for details. http://suitesparse.com @@ -412,7 +395,7 @@ found in the lists below. -------------------------------------------------------------------------------- CHOLMOD/Partition Module. - Copyright (C) 2005-2022, Univ. of Florida. Author: Timothy A. Davis + Copyright (C) 2005-2023, Univ. of Florida. Author: Timothy A. Davis CHOLMOD is also available under other licenses; contact authors for details. http://suitesparse.com @@ -442,7 +425,7 @@ found in the lists below. -------------------------------------------------------------------------------- CHOLMOD/Supernodal Module. - Copyright (C) 2005-2022, Timothy A. Davis + Copyright (C) 2005-2023, Timothy A. Davis CHOLMOD is also available under other licenses; contact authors for details. http://suitesparse.com @@ -471,7 +454,7 @@ found in the lists below. ==> Tcov/License.txt <== -------------------------------------------------------------------------------- - CHOLMOD/Tcov Module. Copyright (C) 2005-2022, Timothy A. Davis + CHOLMOD/Tcov Module. Copyright (C) 2005-2023, Timothy A. Davis CHOLMOD is also available under other licenses; contact authors for details. http://suitesparse.com @@ -500,7 +483,7 @@ found in the lists below. ==> Valgrind/License.txt <== -------------------------------------------------------------------------------- - CHOLMOD/Valgrind Module. Copyright (C) 2005-2022, Timothy A. Davis. + CHOLMOD/Valgrind Module. Copyright (C) 2005-2023, Timothy A. Davis. CHOLMOD is also available under other licenses; contact authors for details. http://suitesparse.com @@ -526,7 +509,7 @@ found in the lists below. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. ==> COLAMD/Doc/License.txt <== - COLAMD, Copyright 1998-2022, Timothy A. Davis. http://suitesparse.com + COLAMD, Copyright 1998-2023, Timothy A. Davis. http://suitesparse.com http://suitesparse.com COLAMD License: BSD 3-clause @@ -557,7 +540,7 @@ found in the lists below. ==> CSparse/Doc/License.txt <== CSparse: a Concise Sparse matrix package. - Copyright (c) 2006-2022, Timothy A. Davis. + Copyright (c) 2006-2023, Timothy A. Davis. http://suitesparse.com -------------------------------------------------------------------------------- @@ -578,7 +561,7 @@ found in the lists below. ==> CXSparse/Doc/License.txt <== CXSparse: a Concise Sparse matrix package - Extended. - Copyright (c) 2006-2022, Timothy A. Davis. + Copyright (c) 2006-2023, Timothy A. Davis. http://suitesparse.com -------------------------------------------------------------------------------- @@ -599,7 +582,7 @@ found in the lists below. ==> CXSparse_newfiles/Doc/License.txt <== CXSparse: a Concise Sparse matrix package - Extended. - Copyright (c) 2006-2022, Timothy A. Davis. + Copyright (c) 2006-2023, Timothy A. Davis. http://suitesparse.com -------------------------------------------------------------------------------- @@ -618,17 +601,17 @@ found in the lists below. License along with this Module; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -==> GPUQREngine/Doc/License.txt <== - GPUQREngine Copyright (c) 2013-2022, Timothy A. Davis, Sencer Nuri Yeralan, +==> SPQR/GPUQREngine/Doc/License.txt <== + SPQR/GPUQREngine Copyright (c) 2013-2023, Timothy A. Davis, Sencer Nuri Yeralan, and Sanjay Ranka. http://suitesparse.com - GPUQREngine is free software; you can redistribute it and/or modify it under + SPQR/GPUQREngine is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - GPUQREngine is distributed in the hope that it will be useful, but WITHOUT + SPQR/GPUQREngine is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. @@ -638,7 +621,7 @@ found in the lists below. ==> KLU/Doc/License.txt <== - KLU, Copyright (C) 2004-2022, University of Florida + KLU, Copyright (C) 2004-2023, University of Florida by Timothy A. Davis and Ekanathan Palamadai. KLU is also available under other licenses; contact authors for details. http://suitesparse.com @@ -660,7 +643,7 @@ found in the lists below. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ==> LDL/Doc/License.txt <== - LDL Copyright (c) 2005-2022 by Timothy A. Davis. + LDL Copyright (c) 2005-2023 by Timothy A. Davis. LDL is also available under other licenses; contact the author for details. http://suitesparse.com @@ -739,7 +722,7 @@ found in the lists below. SSMULT License: -------------------------------------------------------------------------------- - SSMULT, Copyright (c) 2007-2022, Timothy A. Davis, + SSMULT, Copyright (c) 2007-2023, Timothy A. Davis, http://suitesparse.com. SSMULT is free software; you can redistribute it and/or modify it under the @@ -758,7 +741,7 @@ found in the lists below. ==> RBio/Doc/License.txt <== - RBio toolbox. Copyright (C) 2006-2022, Timothy A. Davis + RBio toolbox. Copyright (C) 2006-2023, Timothy A. Davis RBio is also available under other licenses; contact authors for details. http://suitesparse.com @@ -780,7 +763,7 @@ found in the lists below. ==> SPQR/Doc/License.txt <== - SPQR, Copyright 2008-2022 by Timothy A. Davis. + SPQR, Copyright 2008-2023 by Timothy A. Davis. All Rights Reserved. SPQR is available under alternate licenses, contact T. Davis for details. @@ -819,18 +802,18 @@ found in the lists below. http://suitesparse.com -==> SuiteSparse_GPURuntime/Doc/License.txt <== - SuiteSparse_GPURuntime Copyright (c) 2013-2022, Timothy A. Davis, +==> SPQR/GPURuntime/Doc/License.txt <== + SPQR/GPURuntime Copyright (c) 2013-2023, Timothy A. Davis, Sencer Nuri Yeralan, and Sanjay Ranka. http://suitesparse.com -------------------------------------------------------------------------------- - SuiteSparse_GPURuntime is free software; you can redistribute it and/or modify + SPQR/GPURuntime is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - SuiteSparse_GPURuntime is distributed in the hope that it will be useful, but + SPQR/GPURuntime is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. @@ -840,7 +823,7 @@ found in the lists below. Street, Fifth Floor, Boston, MA 02110-1301, USA. ==> ssget/Doc/License.txt <== - Copyright (c), 2009-2022, Timothy A. Davis, All Rights Reserved. + Copyright (c), 2009-2023, Timothy A. Davis, All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -868,7 +851,7 @@ found in the lists below. ==> UMFPACK/Doc/License.txt <== - UMFPACK, Copyright 1995-2022 by Timothy A. Davis. + UMFPACK, Copyright 1995-2023, by Timothy A. Davis. All Rights Reserved. UMFPACK is available under alternate licenses, contact T. Davis for details. @@ -908,7 +891,7 @@ found in the lists below. ==> CSparse/MATLAB/ssget/Doc/License.txt <== - Copyright (c), 2009-2022, Timothy A. Davis, All Rights Reserved. + Copyright (c), 2009-2023, Timothy A. Davis, All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -936,7 +919,7 @@ found in the lists below. ==> CXSparse/MATLAB/ssget/Doc/License.txt <== - Copyright (c), 2009-2022, Timothy A. Davis, All Rights Reserved. + Copyright (c), 2009-2023, Timothy A. Davis, All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -1003,13 +986,13 @@ found in the lists below. SPDX-License-Identifier: GPL-3.0-or-later ==> Mongoose License <== - Mongoose, Copyright 2018-2022, Timothy A. Davis, Scott P. Kolodziej, + Mongoose, Copyright 2018-2023, Timothy A. Davis, Scott P. Kolodziej, William W. Hager, S. Nuri Yeralan Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3, 29 June 2007 ==> Example License <== -Example package, Copyright (c), 2022, Timothy A. Davis, All Rights Reserved. +Example package, Copyright (c), 2023, Timothy A. Davis, All Rights Reserved. SPDX-License-Identifier: BSD-3-clause Redistribution and use in source and binary forms, with or without @@ -1036,3 +1019,60 @@ SPDX-License-Identifier: BSD-3-clause OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +==> ParU License <== + + ParU package, Copyright (c), 2023, Mohsen Aznaveh and Timothy A. Davis, All + Rights Reserved. + SPDX-License-Identifier: GPL-3.0-or-later + +==> LAGraph License <== + + SPDX-License-Identifier: BSD-2-clause + + File: LICENSE + + LAGraph + + Copyright 2019-2023 LAGraph Contributors. All Rights Reserved. + (see Contributors.txt for a full list of Contributors; + see ContributionInstructions.txt for information on how you can + Contribute to this project). + + BSD + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + ACKNOWLEDGMENTS AND DISCLAIMERS: + + This program includes and/or can make use of certain third party source code, + object code, documentation and other files ("Third Party Software"). The Third + Party Software that is used by this program is dependent upon your system + configuration. By using this program, You agree to comply with any and all + relevant Third Party Software terms and conditions contained in any such Third + Party Software or separate license file distributed with such Third Party + Software. The parties who own the Third Party Software ("Third Party + Licensors") are intended third party beneficiaries to this License with + respect to the terms applicable to their Third Party Software. Third Party + Software licenses only apply to the Third Party Software and not any other + portion of this program or this program as a whole. + + Created, in part, with funding and support from the United States Government. + (see Acknowledgments.txt file). + + NO WARRANTY. THIS MATERIAL IS FURNISHED ON AN "AS-IS" BASIS. THE LAGRAPH + CONTRIBUTORS MAKE NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, AS + TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE + OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE + MATERIAL. THE CONTRIBUTORS DO NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT + TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT. + + DM22-0790 + diff --git a/ThirdParty/SuiteSparse/README.md b/ThirdParty/SuiteSparse/README.md index 6771fa1d86..29c74df122 100644 --- a/ThirdParty/SuiteSparse/README.md +++ b/ThirdParty/SuiteSparse/README.md @@ -2,18 +2,48 @@ SuiteSparse: A Suite of Sparse matrix packages at http://suitesparse.com ----------------------------------------------------------------------------- -Jan 20, 2023, SuiteSparse VERSION 7.0.1 +Jan 20, 2024, SuiteSparse VERSION 7.6.0 SuiteSparse is a set of sparse-matrix-related packages written or co-authored by Tim Davis, available at https://github.com/DrTimothyAldenDavis/SuiteSparse . Primary author of SuiteSparse (codes and algorithms, excl. METIS): Tim Davis -Code co-authors, in alphabetical order (not including METIS): - Patrick Amestoy, David Bateman, Jinhao Chen, Yanqing Chen, Iain Duff, - Les Foster, William Hager, Scott Kolodziej, Chris Lourenco, Stefan - Larimore, Erick Moreno-Centeno, Ekanathan Palamadai, Sivasankaran - Rajamanickam, Sanjay Ranka, Wissam Sid-Lakhdar, Nuri Yeralan. +Code co-authors, in alphabetical order (not including METIS or LAGraph): + Patrick Amestoy, Mohsen Aznaveh, David Bateman, Jinhao Chen, Yanqing Chen, + Iain Duff, Joe Eaton, Les Foster, William Hager, Raye Kimmerer, Scott + Kolodziej, Chris Lourenco, Stefan Larimore, Lorena Mejia Domenzain, Erick + Moreno-Centeno, Markus Mützel, Corey Nolel, Ekanathan Palamadai, + Sivasankaran Rajamanickam, Sanjay Ranka, Wissam Sid-Lakhdar, and + Nuri Yeralan. + +LAGraph has been developed by the highest number of developers of any of +the packages in SuiteSparse and deserves its own list. The list also +appears in LAGraph/Contibutors.txt: + + Janos B. Antal, Budapest University of Technology and Economics, Hungary + Mohsen Aznaveh, Texas A&M University + David A. Bader New Jersey Institute of Technology + Aydin Buluc, Lawrence Berkeley National Lab + Jinhao Chen, Texas A&M University + Tim Davis, Texas A&M University + Florentin Dorre, Technische Univeritat Dresden, Neo4j + Marton Elekes, Budapest University of Technology and Economics, Hungary + Balint Hegyi, Budapest University of Technology and Economics, Hungary + Tanner Hoke, Texas A&M University + James Kitchen, Anaconda + Scott Kolodziej, Texas A&M University + Pranav Konduri, Texas A&M University + Roi Lipman, Redis Labs (now FalkorDB) + Tze Meng Low, Carnegie Mellon University + Tim Mattson, Intel + Scott McMillan, Carnegie Mellon University + Markus Muetzel + Michel Pelletier, Graphegon + Gabor Szarnyas, CWI Amsterdam, The Netherlands + Erik Welch, Anaconda, NVIDIA + Carl Yang, University of California at Davis, Waymo + Yongzhe Zhang, SOKENDAI, Japan METIS is authored by George Karypis. @@ -21,17 +51,460 @@ Additional algorithm designers: Esmond Ng and John Gilbert. Refer to each package for license, copyright, and author information. +----------------------------------------------------------------------------- +Documentation +----------------------------------------------------------------------------- + +Refer to each package for the documentation on each package, typically in the +Doc subfolder. + ----------------------------------------------------------------------------- SuiteSparse branches ----------------------------------------------------------------------------- - * dev: the default branch, with recent updates of features to appear in - the next stable release. The intent is to keep this branch in - fully working order at all times, but the features will not be - finalized at any given time. - * stable: the most recent stable release. - * dev2: working branch. All submitted PRs should made to this branch. - This branch might not always be in working order. +* dev: the default branch, with recent updates of features to appear in + the next stable release. The intent is to keep this branch in + fully working order at all times, but the features will not be + finalized at any given time. +* stable: the most recent stable release. +* dev2: working branch. All submitted PRs should made to this branch. + This branch might not always be in working order. + +----------------------------------------------------------------------------- +SuiteSparse Packages +----------------------------------------------------------------------------- + +Packages in SuiteSparse, and files in this directory: + +* `AMD` + + approximate minimum degree ordering. This is the built-in AMD function in + MATLAB. + + authors: Tim Davis, Patrick Amestoy, Iain Duff + +* `bin` + + where programs are placed when compiled, for `make local` + +* `BTF` + + permutation to block triangular form + + authors: Tim Davis, Ekanathan Palamadai + +* `build` + + folder for default build tree + +* `CAMD` + + constrained approximate minimum degree ordering + + authors: Tim Davis, Patrick Amestoy, Iain Duff, Yanqing Chen + +* `CCOLAMD` + + constrained column approximate minimum degree ordering + + authors: Tim Davis, Sivasankaran Rajamanickam, Stefan Larimore. + + Algorithm design collaborators: Esmond Ng, John Gilbert (for COLAMD) + +* `ChangeLog` + + a summary of changes to SuiteSparse. See `*/Doc/ChangeLog` for details for + each package. + +* `CHOLMOD` + + sparse Cholesky factorization. Requires AMD, COLAMD, CCOLAMD, the BLAS, and + LAPACK. Optionally uses METIS. This is `chol` and `x=A\b` in MATLAB. + + author for all modules: Tim Davis + + CHOLMOD/Modify module authors: Tim Davis and William W. Hager + + CHOLMOD/SuiteSparse_metis: a modified version of METIS, embedded into the + CHOLMOD library. See the README.txt files for details. author: George + Karypis. This is a slightly modified copy included with SuiteSparse via the + open-source license provided by George Karypis. SuiteSparse cannot use an + unmodified copy of METIS. + +* `CITATION.bib` + + citations for SuiteSparse packages, in bibtex format. + +* `CMakeLists.txt` + + optional, to compile all of SuiteSparse. See below. + +* `CODE_OF_CONDUCT.md` + + community guidelines + +* `COLAMD` + + column approximate minimum degree ordering. This is the built-in COLAMD + function in MATLAB. + + authors (of the code): Tim Davis and Stefan Larimore + + Algorithm design collaborators: Esmond Ng, John Gilbert + +* `Contents.m` + + a list of contents for 'help SuiteSparse' in MATLAB. + +* `CONTRIBUTING.md` + + how to contribute to SuiteSparse + +* `CONTRIBUTOR-LICENSE.txt` + + required contributor agreement + +* `CSparse` + + a concise sparse matrix package, developed for my book, "Direct Methods for + Sparse Linear Systems", published by SIAM. Intended primarily for teaching. + Note that the code is (c) Tim Davis, as stated in the book. + + For production, use CXSparse instead. In particular, both CSparse and + CXSparse have the same include filename: `cs.h`. This package is used for + the built-in DMPERM in MATLAB. + + author: Tim Davis + +* `CXSparse` + + CSparse Extended. Includes support for complex matrices and both int or long + integers. Use this instead of CSparse for production use; it creates a + libcsparse.so (or dylib on the Mac) with the same name as CSparse. It is a + superset of CSparse. Any code that links against CSparse should also be able + to link against CXSparse instead. + + author: Tim Davis, David Bateman + +* `Example` + + a simple package that relies on almost all of SuiteSparse + +* `.github` + + workflows for CI testing on GitHub. + +* `GraphBLAS` + + graph algorithms in the language of linear algebra. + + https://graphblas.org + + authors: Tim Davis, Joe Eaton, Corey Nolet + +* `include` + + `make install` places user-visible include files for each package here, after + `make local`. + +* `KLU` + + sparse LU factorization, primarily for circuit simulation. Requires AMD, + COLAMD, and BTF. Optionally uses CHOLMOD, CAMD, CCOLAMD, and METIS. + + authors: Tim Davis, Ekanathan Palamadai + +* `LAGraph` + + a graph algorithms library based on GraphBLAS. See also + https://github.com/GraphBLAS/LAGraph + + Authors: many. + +* `LDL` + + a very concise LDL' factorization package + + author: Tim Davis + +* `lib` + + `make install` places shared libraries for each package here, after + `make local`. + +* `LICENSE.txt` + + collected licenses for each package. + +* `Makefile` + + optional, to compile all of SuiteSparse using `make`, which is used as a + simple wrapper for `cmake` in each subproject. + + * `make` + + compiles SuiteSparse libraries. Subsequent `make install` will install + in `CMAKE_INSTALL_PATH` (might default to `/usr/local/lib` on Linux or Mac). + + * `make local` + + compiles SuiteSparse. Subsequent `make install` will install in `./lib`, + `./include`. Does not install in `CMAKE_INSTALL_PATH`. + + * `make global` + + compiles SuiteSparse libraries. Subsequent `make install` will install in + `/usr/local/lib` (or whatever the configured `CMAKE_INSTALL_PREFIX` is). + Does not install in `./lib` and `./include`. + + * `make install` + + installs in the current directory (`./lib`, `./include`), or in + `/usr/local/lib` and `/usr/local/include`, (the latter defined by + `CMAKE_INSTALL_PREFIX`) depending on whether `make`, `make local`, or + `make global` has been done. + + * `make uninstall` + + undoes `make install`. + + * `make distclean` + + removes all files not in distribution, including `./bin`, `./share`, + `./lib`, and `./include`. + + * `make purge` + + same as `make distclean`. + + * `make clean` + + removes all files not in distribution, but keeps compiled libraries and + demos, `./lib`, `./share`, and `./include`. + + Each individual subproject also has each of the above `make` targets. + + Things you don't need to do: + + * `make docs` + + creates user guides from LaTeX files + + * `make cov` + + runs statement coverage tests (Linux only) + +* `MATLAB_Tools` + + various m-files for use in MATLAB + + author: Tim Davis (all parts) + + for `spqr_rank`: author Les Foster and Tim Davis + + * `Contents.m` + + list of contents + + * `dimacs10` + + loads matrices for DIMACS10 collection + + * `Factorize` + + object-oriented `x=A\b` for MATLAB + + * `find_components` + + finds connected components in an image + + * `GEE` + + simple Gaussian elimination + + * `getversion.m` + + determine MATLAB version + + * `gipper.m` + + create MATLAB archive + + * `hprintf.m` + + print hyperlinks in command window + + * `LINFACTOR` + + predecessor to `Factorize` package + + * `MESHND` + + nested dissection ordering of regular meshes + + * `pagerankdemo.m` + + illustrates how PageRank works + + * `SFMULT` + + `C=S*F` where `S` is sparse and `F` is full + + * `shellgui` + + display a seashell + + * `sparseinv` + + sparse inverse subset + + * `spok` + + check if a sparse matrix is valid + + * `spqr_rank` + + SPQR_RANK package. MATLAB toolbox for rank deficient sparse matrices: null + spaces, reliable factorizations, etc. With Leslie Foster, San Jose State + Univ. + + * `SSMULT` + + `C=A*B` where `A` and `B` are both sparse. + This was the basis for the built-in `C=A*B` in MATLAB, until it was + superseded by GraphBLAS in MATLAB R2021a. + + * `SuiteSparseCollection` + + for the SuiteSparse Matrix Collection + + * `waitmex` + + waitbar for use inside a mexFunction + +* `Mongoose` + + graph partitioning. + + authors: Nuri Yeralan, Scott Kolodziej, William Hager, Tim Davis + +* `ParU` + + a parallel unsymmetric pattern multifrontal method. + + Currently a pre-release. + + authors: Mohsen Aznaveh and Tim Davis + +* `RBio` + + read/write sparse matrices in Rutherford/Boeing format + + author: Tim Davis + +* `README.md` + + this file + +* `SPEX` + + solves sparse linear systems in exact arithmetic. + + Requires the GNU GMP and MPRF libraries. + + This will be soon replaced by a more general package, SPEX v3 that includes + this method (exact sparse LU) and others (sparse exact Cholesky, and sparse + exact update/downdate). The API of v3 will be changing significantly. + + authors: Chris Lourenco, Jinhao Chen, Erick Moreno-Centeno, + Lorena Lorena Mejia Domenzain, and Tim Davis. + + See https://github.com/clouren/SPEX for the latest version. + +* `SPQR` + + sparse QR factorization. This the built-in `qr` and `x=A\b` in MATLAB. Also + called SuiteSparseQR. + + Includes two GPU libraries: `SPQR/GPUQREngine` and + `SPQR/SuiteSparse_GPURuntime`. + + author of the CPU code: Tim Davis + + author of GPU modules: Tim Davis, Nuri Yeralan, Wissam Sid-Lakhdar, + Sanjay Ranka + +* `ssget` + + MATLAB interface to the SuiteSparse Matrix Collection + + author: Tim Davis + +* `SuiteSparse_config` + + library with common functions and configuration for all the above packages. + `CSparse`, `GraphBLAS`, `LAGraph`, and `MATLAB_Tools` do not use + `SuiteSparse_config`. + + author: Tim Davis + +* `SuiteSparse_demo.m` + + a demo of SuiteSparse for MATLAB + +* `SuiteSparse_install.m` + + install SuiteSparse for MATLAB + +* `SuiteSparse_paths.m` + + set paths for SuiteSparse MATLAB mexFunctions + +* `SuiteSparse_test.m` + + exhaustive test for SuiteSparse in MATLAB + +* `UMFPACK` + + sparse LU factorization. Requires `AMD` and the `BLAS`. + + This is the built-in `lu` and `x=A\b` in MATLAB. + + author: Tim Davis + + algorithm design collaboration: Iain Duff + +Refer to each package for license, copyright, and author information. All +codes are authored or co-authored by Timothy A. Davis (email: davis@tamu.edu), +except for METIS (by George Karypis), `GraphBLAS/cpu_features` (by Google), +GraphBLAS/lz4, zstd, and xxHash (by Yann Collet, now at Facebook), and +GraphBLAS/CUDA/jitify.hpp (by NVIDIA). Parts of GraphBLAS/CUDA are +Copyright (c) by NVIDIA. Please refer to each of these licenses. + +----------------------------------------------------------------------------- +For distro maintainers (Linux, homebrew, spack, R, Octave, Trilinos, ...): +----------------------------------------------------------------------------- + +Thanks for packaging SuiteSparse! Here are some suggestions: + +* GraphBLAS takes a long time to compile because it creates many fast + "FactoryKernels" at compile-time. If you want to reduce the compile time and + library size, enable the `GRAPHBLAS_COMPACT` mode, but keep the JIT compiler + enabled. Then GraphBLAS will compile the kernels it needs at run-time, via + its JIT compiler. Performance will be the same as the FactoryKernels once + the JIT kernels are compiled. User compiled kernels are placed in + `~/.SuiteSparse`, by default. You do not need to distribute the source for + GraphBLAS to enable the JIT compiler: just `libgraphblas.so` and + `GraphBLAS.h` is enough. + +* GraphBLAS needs OpenMP! It's fundamentally a parallel code so please + distribute it with OpenMP enabled. Performance will suffer otherwise. + +* CUDA acceleration: CHOLMOD and SPQR can benefit from their CUDA kernels. If + you do not have CUDA or do not want to include it in your distro, this + version of SuiteSparse skips the building of the `CHOLMOD_CUDA` and `SPQR_CUDA` + libraries, and does not link against the `GPUQREngine` and + `SuiteSparse_GPURuntime` libraries. ----------------------------------------------------------------------------- How to cite the SuiteSparse meta-package and its component packages: @@ -40,193 +513,180 @@ How to cite the SuiteSparse meta-package and its component packages: SuiteSparse is a meta-package of many packages, each with their own published papers. To cite the whole collection, use the URLs: - * https://github.com/DrTimothyAldenDavis/SuiteSparse - * http://suitesparse.com (which is a forwarding URL +* https://github.com/DrTimothyAldenDavis/SuiteSparse +* http://suitesparse.com (which is a forwarding URL to https://people.engr.tamu.edu/davis/suitesparse.html) Please also cite the specific papers for the packages you use. This is a long list; if you want a shorter list, just cite the most recent "Algorithm XXX:" papers in ACM TOMS, for each package. - * For the MATLAB x=A\b, see below for AMD, COLAMD, CHOLMOD, UMFPACK, - and SuiteSparseQR (SPQR). +* For the MATLAB x=A\b, see below for AMD, COLAMD, CHOLMOD, UMFPACK, + and SuiteSparseQR (SPQR). - * for GraphBLAS, and `C=A*B` in MATLAB (sparse-times-sparse): +* for GraphBLAS, and C=AB in MATLAB (sparse-times-sparse): - T. Davis, Algorithm 10xx: SuiteSparse:GraphBLAS: parallel graph - algorithms in the language of sparse linear algebra, ACM Trans on - Mathematical Software, to appear, 2023. See the pdf in - https://github.com/DrTimothyAldenDavis/GraphBLAS/tree/stable/Doc + T. A. Davis. Algorithm 1037: SuiteSparse:GraphBLAS: Parallel Graph Algorithms + in the Language of Sparse Linear Algebra. ACM Trans. Math. Softw. 49, 3, + Article 28 (September 2023), 30 pages. https://doi.org/10.1145/3577195 - T. Davis, Algorithm 1000: SuiteSparse:GraphBLAS: graph algorithms in - the language of sparse linear algebra, ACM Trans on Mathematical - Software, vol 45, no 4, Dec. 2019, Article No 44. - https://doi.org/10.1145/3322125. + T. Davis, Algorithm 1000: SuiteSparse:GraphBLAS: graph algorithms in the + language of sparse linear algebra, ACM Trans on Mathematical Software, vol + 45, no 4, Dec. 2019, Article No 44. https://doi.org/10.1145/3322125. - * for CSparse/CXSParse: +* for LAGraph: - T. A. Davis, Direct Methods for Sparse Linear Systems, SIAM Series on - the Fundamentals of Algorithms, SIAM, Philadelphia, PA, 2006. - https://doi.org/10.1137/1.9780898718881 + G. Szárnyas et al., "LAGraph: Linear Algebra, Network Analysis Libraries, and + the Study of Graph Algorithms," 2021 IEEE International Parallel and + Distributed Processing Symposium Workshops (IPDPSW), Portland, OR, USA, 2021, + pp. 243-252. https://doi.org/10.1109/IPDPSW52791.2021.00046. - * for SuiteSparseQR (SPQR): (also cite AMD, COLAMD): +* for CSparse/CXSParse: - T. A. Davis, Algorithm 915: SuiteSparseQR: Multifrontal multithreaded - rank-revealing sparse QR factorization, ACM Trans. on Mathematical - Software, 38(1), 2011, pp. 8:1--8:22. - https://doi.org/10.1145/2049662.2049670 + T. A. Davis, Direct Methods for Sparse Linear Systems, SIAM Series on the + Fundamentals of Algorithms, SIAM, Philadelphia, PA, 2006. + https://doi.org/10.1137/1.9780898718881 - * for SuiteSparseQR/GPU: +* for SuiteSparseQR (SPQR): (also cite AMD, COLAMD): - Sencer Nuri Yeralan, T. A. Davis, Wissam M. Sid-Lakhdar, and Sanjay - Ranka. 2017. Algorithm 980: Sparse QR Factorization on the GPU. ACM - Trans. Math. Softw. 44, 2, Article 17 (June 2018), 29 pages. - https://doi.org/10.1145/3065870 + T. A. Davis, Algorithm 915: SuiteSparseQR: Multifrontal multithreaded + rank-revealing sparse QR factorization, ACM Trans. on Mathematical Software, + 38(1), 2011, pp. 8:1--8:22. https://doi.org/10.1145/2049662.2049670 - * for CHOLMOD: (also cite AMD, COLAMD): +* for SuiteSparseQR/GPU: - Y. Chen, T. A. Davis, W. W. Hager, and S. Rajamanickam, Algorithm 887: - CHOLMOD, supernodal sparse Cholesky factorization and update/downdate, - ACM Trans. on Mathematical Software, 35(3), 2008, pp. 22:1--22:14. - https://dl.acm.org/doi/abs/10.1145/1391989.1391995 + Sencer Nuri Yeralan, T. A. Davis, Wissam M. Sid-Lakhdar, and Sanjay Ranka. + 2017. Algorithm 980: Sparse QR Factorization on the GPU. ACM Trans. Math. + Softw. 44, 2, Article 17 (June 2018), 29 pages. + https://doi.org/10.1145/3065870 - T. A. Davis and W. W. Hager, Dynamic supernodes in sparse Cholesky - update/downdate and triangular solves, ACM Trans. on Mathematical - Software, 35(4), 2009, pp. 27:1--27:23. - https://doi.org/10.1145/1462173.1462176 +* for CHOLMOD: (also cite AMD, COLAMD): - * for CHOLMOD/Modify Module: (also cite AMD, COLAMD): + Y. Chen, T. A. Davis, W. W. Hager, and S. Rajamanickam, Algorithm 887: + CHOLMOD, supernodal sparse Cholesky factorization and update/downdate, ACM + Trans. on Mathematical Software, 35(3), 2008, pp. 22:1--22:14. + https://dl.acm.org/doi/abs/10.1145/1391989.1391995 - T. A. Davis and William W. Hager, Row Modifications of a Sparse - Cholesky Factorization SIAM Journal on Matrix Analysis and Applications - 2005 26:3, 621-639 - https://doi.org/10.1137/S089547980343641X + T. A. Davis and W. W. Hager, Dynamic supernodes in sparse Cholesky + update/downdate and triangular solves, ACM Trans. on Mathematical Software, + 35(4), 2009, pp. 27:1--27:23. https://doi.org/10.1145/1462173.1462176 - T. A. Davis and William W. Hager, Multiple-Rank Modifications of a - Sparse Cholesky Factorization SIAM Journal on Matrix Analysis and - Applications 2001 22:4, 997-1013 - https://doi.org/10.1137/S0895479899357346 +* for CHOLMOD/Modify Module: (also cite AMD, COLAMD): - T. A. Davis and William W. Hager, Modifying a Sparse Cholesky - Factorization, SIAM Journal on Matrix Analysis and Applications 1999 - 20:3, 606-627 - https://doi.org/10.1137/S0895479897321076 + T. A. Davis and William W. Hager, Row Modifications of a Sparse Cholesky + Factorization SIAM Journal on Matrix Analysis and Applications 2005 26:3, + 621-639. https://doi.org/10.1137/S089547980343641X - * for CHOLMOD/GPU Modules: + T. A. Davis and William W. Hager, Multiple-Rank Modifications of a Sparse + Cholesky Factorization SIAM Journal on Matrix Analysis and Applications 2001 + 22:4, 997-1013. https://doi.org/10.1137/S0895479899357346 - Steven C. Rennich, Darko Stosic, Timothy A. Davis, Accelerating sparse - Cholesky factorization on GPUs, Parallel Computing, Vol 59, 2016, pp - 140-150. - https://doi.org/10.1016/j.parco.2016.06.004 + T. A. Davis and William W. Hager, Modifying a Sparse Cholesky Factorization, + SIAM Journal on Matrix Analysis and Applications 1999 20:3, 606-627. + https://doi.org/10.1137/S0895479897321076 - * for AMD and CAMD: +* for CHOLMOD/GPU Modules: - P. Amestoy, T. A. Davis, and I. S. Duff, Algorithm 837: An approximate - minimum degree ordering algorithm, ACM Trans. on Mathematical Software, - 30(3), 2004, pp. 381--388. - https://dl.acm.org/doi/abs/10.1145/1024074.1024081 + Steven C. Rennich, Darko Stosic, Timothy A. Davis, Accelerating sparse + Cholesky factorization on GPUs, Parallel Computing, Vol 59, 2016, pp 140-150. + https://doi.org/10.1016/j.parco.2016.06.004 - P. Amestoy, T. A. Davis, and I. S. Duff, An approximate minimum degree - ordering algorithm, SIAM J. Matrix Analysis and Applications, 17(4), - 1996, pp. 886--905. - https://doi.org/10.1137/S0895479894278952 +* for AMD and CAMD: - * for COLAMD, SYMAMD, CCOLAMD, and CSYMAMD: + P. Amestoy, T. A. Davis, and I. S. Duff, Algorithm 837: An approximate + minimum degree ordering algorithm, ACM Trans. on Mathematical Software, + 30(3), 2004, pp. 381--388. + https://dl.acm.org/doi/abs/10.1145/1024074.1024081 - T. A. Davis, J. R. Gilbert, S. Larimore, E. Ng, Algorithm 836: COLAMD, - an approximate column minimum degree ordering algorithm, ACM Trans. on - Mathematical Software, 30(3), 2004, pp. 377--380. - https://doi.org/10.1145/1024074.1024080 + P. Amestoy, T. A. Davis, and I. S. Duff, An approximate minimum degree + ordering algorithm, SIAM J. Matrix Analysis and Applications, 17(4), 1996, + pp. 886--905. https://doi.org/10.1137/S0895479894278952 - T. A. Davis, J. R. Gilbert, S. Larimore, E. Ng, A column approximate - minimum degree ordering algorithm, ACM Trans. on Mathematical Software, - 30(3), 2004, pp. 353--376. - https://doi.org/10.1145/1024074.1024079 +* for COLAMD, SYMAMD, CCOLAMD, and CSYMAMD: - * for UMFPACK: (also cite AMD and COLAMD): + T. A. Davis, J. R. Gilbert, S. Larimore, E. Ng, Algorithm 836: COLAMD, an + approximate column minimum degree ordering algorithm, ACM Trans. on + Mathematical Software, 30(3), 2004, pp. 377--380. + https://doi.org/10.1145/1024074.1024080 - T. A. Davis, Algorithm 832: UMFPACK - an unsymmetric-pattern - multifrontal method with a column pre-ordering strategy, ACM Trans. on - Mathematical Software, 30(2), 2004, pp. 196--199. - https://dl.acm.org/doi/abs/10.1145/992200.992206 + T. A. Davis, J. R. Gilbert, S. Larimore, E. Ng, A column approximate minimum + degree ordering algorithm, ACM Trans. on Mathematical Software, 30(3), 2004, + pp. 353--376. https://doi.org/10.1145/1024074.1024079 - T. A. Davis, A column pre-ordering strategy for the unsymmetric-pattern - multifrontal method, ACM Trans. on Mathematical Software, 30(2), 2004, - pp. 165--195. - https://dl.acm.org/doi/abs/10.1145/992200.992205 +* for UMFPACK: (also cite AMD and COLAMD): - T. A. Davis and I. S. Duff, A combined unifrontal/multifrontal method - for unsymmetric sparse matrices, ACM Trans. on Mathematical Software, - 25(1), 1999, pp. 1--19. - https://doi.org/10.1145/305658.287640 + T. A. Davis, Algorithm 832: UMFPACK - an unsymmetric-pattern multifrontal + method with a column pre-ordering strategy, ACM Trans. on Mathematical + Software, 30(2), 2004, pp. 196--199. + https://dl.acm.org/doi/abs/10.1145/992200.992206 - T. A. Davis and I. S. Duff, An unsymmetric-pattern multifrontal method - for sparse LU factorization, SIAM J. Matrix Analysis and Computations, - 18(1), 1997, pp. 140--158. - https://doi.org/10.1137/S0895479894246905 + T. A. Davis, A column pre-ordering strategy for the unsymmetric-pattern + multifrontal method, ACM Trans. on Mathematical Software, 30(2), 2004, pp. + 165--195. https://dl.acm.org/doi/abs/10.1145/992200.992205 - * for the FACTORIZE m-file: + T. A. Davis and I. S. Duff, A combined unifrontal/multifrontal method for + unsymmetric sparse matrices, ACM Trans. on Mathematical Software, 25(1), + 1999, pp. 1--19. https://doi.org/10.1145/305658.287640 - T. A. Davis, Algorithm 930: FACTORIZE, an object-oriented linear system - solver for MATLAB, ACM Trans. on Mathematical Software, 39(4), 2013, - pp. 28:1-28:18. - https://doi.org/10.1145/2491491.2491498 + T. A. Davis and I. S. Duff, An unsymmetric-pattern multifrontal method for + sparse LU factorization, SIAM J. Matrix Analysis and Computations, 18(1), + 1997, pp. 140--158. https://doi.org/10.1137/S0895479894246905 - * for KLU and BTF (also cite AMD and COLAMD): +* for the FACTORIZE m-file: - T. A. Davis and Ekanathan Palamadai Natarajan. 2010. Algorithm 907: - KLU, A Direct Sparse Solver for Circuit Simulation Problems. ACM Trans. - Math. Softw. 37, 3, Article 36 (September 2010), 17 pages. - https://dl.acm.org/doi/abs/10.1145/1824801.1824814 + T. A. Davis, Algorithm 930: FACTORIZE, an object-oriented linear system + solver for MATLAB, ACM Trans. on Mathematical Software, 39(4), 2013, pp. + 28:1-28:18. https://doi.org/10.1145/2491491.2491498 - * for LDL: +* for KLU and BTF (also cite AMD and COLAMD): - T. A. Davis. Algorithm 849: A concise sparse Cholesky factorization - package. ACM Trans. Math. Softw. 31, 4 (December 2005), 587–591. - https://doi.org/10.1145/1114268.1114277 + T. A. Davis and Ekanathan Palamadai Natarajan. 2010. Algorithm 907: KLU, A + Direct Sparse Solver for Circuit Simulation Problems. ACM Trans. Math. + Softw. 37, 3, Article 36 (September 2010), 17 pages. + https://dl.acm.org/doi/abs/10.1145/1824801.1824814 - * for ssget and the SuiteSparse Matrix Collection: +* for LDL: - T. A. Davis and Yifan Hu. 2011. The University of Florida sparse - matrix collection. ACM Trans. Math. Softw. 38, 1, Article 1 (November - 2011), 25 pages. - https://doi.org/10.1145/2049662.2049663 + T. A. Davis. Algorithm 849: A concise sparse Cholesky factorization package. + ACM Trans. Math. Softw. 31, 4 (December 2005), 587–591. + https://doi.org/10.1145/1114268.1114277 - Kolodziej et al., (2019). The SuiteSparse Matrix Collection Website - Interface. Journal of Open Source Software, 4(35), 1244, - https://doi.org/10.21105/joss.01244 +* for ssget and the SuiteSparse Matrix Collection: - * for `spqr_rank`: + T. A. Davis and Yifan Hu. 2011. The University of Florida sparse matrix + collection. ACM Trans. Math. Softw. 38, 1, Article 1 (November 2011), 25 + pages. https://doi.org/10.1145/2049662.2049663 - Leslie V. Foster and T. A. Davis. 2013. Algorithm 933: Reliable - calculation of numerical rank, null space bases, pseudoinverse - solutions, and basic solutions using suitesparseQR. ACM Trans. Math. - Softw. 40, 1, Article 7 (September 2013), 23 pages. - https://doi.org/10.1145/2513109.2513116 + Kolodziej et al., (2019). The SuiteSparse Matrix Collection Website + Interface. Journal of Open Source Software, 4(35), 1244. + https://doi.org/10.21105/joss.01244 - * for Mongoose: +* for `spqr_rank`: - T. A. Davis, William W. Hager, Scott P. Kolodziej, and S. Nuri Yeralan. - 2020. Algorithm 1003: Mongoose, a Graph Coarsening and Partitioning - Library. ACM Trans. Math. Softw. 46, 1, Article 7 (March 2020), 18 - pages. - https://doi.org/10.1145/3337792 + Leslie V. Foster and T. A. Davis. 2013. Algorithm 933: Reliable calculation + of numerical rank, null space bases, pseudoinverse solutions, and basic + solutions using suitesparseQR. ACM Trans. Math. Softw. 40, 1, Article 7 + (September 2013), 23 pages. https://doi.org/10.1145/2513109.2513116 - * for SPEX: +* for Mongoose: - Christopher Lourenco, Jinhao Chen, Erick Moreno-Centeno, and T. A. - Davis. 2022. Algorithm 1021: SPEX Left LU, Exactly Solving Sparse - Linear Systems via a Sparse Left-Looking Integer-Preserving LU - Factorization. ACM Trans. Math. Softw. June 2022. - https://doi.org/10.1145/3519024 + T. A. Davis, William W. Hager, Scott P. Kolodziej, and S. Nuri Yeralan. + 2020. Algorithm 1003: Mongoose, a Graph Coarsening and Partitioning Library. + ACM Trans. Math. Softw. 46, 1, Article 7 (March 2020), 18 pages. + https://doi.org/10.1145/3337792 + +* for SPEX: + + Christopher Lourenco, Jinhao Chen, Erick Moreno-Centeno, and T. A. Davis. + 2022. Algorithm 1021: SPEX Left LU, Exactly Solving Sparse Linear Systems via + a Sparse Left-Looking Integer-Preserving LU Factorization. ACM Trans. Math. + Softw. June 2022. https://doi.org/10.1145/3519024 ----------------------------------------------------------------------------- About the BLAS and LAPACK libraries ----------------------------------------------------------------------------- -NOTE: Use of the Intel MKL BLAS is strongly recommended. In a 2019 test, -OpenBLAS caused result in severe performance degradation. The reason for this -is being investigated, and this may be resolved in the near future. +NOTE: if you use OpenBLAS, be sure to use version 0.3.27 or later. To select your BLAS/LAPACK, see the instructions in SuiteSparseBLAS.cmake in `SuiteSparse_config/cmake_modules`. If `SuiteSparse_config` finds a BLAS with @@ -234,15 +694,17 @@ To select your BLAS/LAPACK, see the instructions in SuiteSparseBLAS.cmake in `SuiteSparse_config.h` with the `SUITESPARSE_BLAS_INT` defined as `int64_t`. Otherwise, if a 32-bit BLAS is found, this type is defined as `int32_t`. If later on, UMFPACK, CHOLMOD, or SPQR are compiled and linked with a BLAS that -has a different integer size, you must override the definition with -DBLAS64 -(to assert the use of 64-bit integers in the BLAS) or -DBLAS32, (to assert the -use of 32-bit integers in the BLAS). +has a different integer size, you must override the definition with `-DBLAS64` +(to assert the use of 64-bit integers in the BLAS) or `-DBLAS32`, (to assert +the use of 32-bit integers in the BLAS). + +The size of the BLAS integer has nothing to do with `sizeof(void *)`. When distributed in a binary form (such as a Debian, Ubuntu, Spack, or Brew package), SuiteSparse should probably be compiled to expect a 32-bit BLAS, since this is the most common case. The default is to use a 32-bit BLAS, but -this can be changed in SuiteSparseBLAS.cmake or by compiling with -`-DALLOW_64BIT_BLAS=1`. +this can be changed by setting the cmake variable +`SUITESPARSE_USE_64BIT_BLAS` to `ON`. By default, SuiteSparse hunts for a suitable BLAS library. To enforce a particular BLAS library use either: @@ -251,263 +713,140 @@ particular BLAS library use either: cd Package ; cmake -DBLA_VENDOR=OpenBLAS .. make To use the default (hunt for a BLAS), do not set `BLA_VENDOR`, or set it to -ANY. In this case, if `ALLOW_64BIT_BLAS` is set, preference is given to a -64-bit BLAS, but a 32-bit BLAS library will be used if no 64-bit library is -found. +`ANY`. In this case, if `SUITESPARSE_USE_64BIT_BLAS` is ON, preference is +given to a 64-bit BLAS, but a 32-bit BLAS library will be used if no 64-bit +library is found. However, if both `SUITESPARSE_USE_64BIT_BLAS` and +`SUITESPARSE_USE_STRICT` are ON, then only a 64-bit BLAS is considered. -When selecting a particular BLAS library, the `ALLOW_64BIT_BLAS` setting is -strictly followed. If set to true, only a 64-bit BLAS library will be used. -If false (the default), only a 32-bit BLAS library will be used. If no such -BLAS is found, the build will fail. +When selecting a particular BLAS library, the `SUITESPARSE_USE_64BIT_BLAS` +setting is strictly followed. If set to true, only a 64-bit BLAS library will +be used. If false (the default), only a 32-bit BLAS library will be used. If +no such BLAS is found, the build will fail. ------------------- -SuiteSparse/README ------------------- +----------------------------------------------------------------------------- +QUICK START FOR THE C/C++ LIBRARIES: +----------------------------------------------------------------------------- -Packages in SuiteSparse, and files in this directory: +Type the following in this directory (requires system priviledge to do the +`sudo make install`): +``` + mkdir -p build && cd build + cmake .. + cmake --build . + sudo cmake --install . +``` + +All libraries will be created and installed into the default system-wide folder +(/usr/local/lib on Linux). All include files needed by the applications that +use SuiteSparse are installed into /usr/local/include/suitesparse (on Linux). + +To build only a subset of libraries, set `SUITESPARSE_ENABLE_PROJECTS` when +configuring with CMake. E.g., to build and install CHOLMOD and CXSparse +(including their dependencies), use the following commands: +``` + mkdir -p build && cd build + cmake -DSUITESPARSE_ENABLE_PROJECTS="cholmod;cxsparse" .. + cmake --build . + sudo cmake --install . +``` + +For Windows (MSVC), import the `CMakeLists.txt` file into MS Visual Studio. +Be sure to specify the build type as Release; for example, to build SuiteSparse +on Windows in the command window, run: +``` + mkdir -p build && cd build + cmake .. + cmake --build . --config Release + cmake --install . +``` - GraphBLAS graph algorithms in the language of linear algebra. - https://graphblas.org - author: Tim Davis - - SPEX solves sparse linear systems in exact arithmetic. - Requires the GNU GMP and MPRF libraries. - This will be soon replaced by a more general package, SPEX v3 - that includes this method (exact sparse LU) and others (sparse - exact Cholesky, and sparse exact update/downdate). The API - of v3 will be changing significantly. - - AMD approximate minimum degree ordering. This is the built-in AMD - function in MATLAB. - authors: Tim Davis, Patrick Amestoy, Iain Duff - - bin where programs are placed when compiled - - BTF permutation to block triangular form - authors: Tim Davis, Ekanathan Palamadai - - CAMD constrained approximate minimum degree ordering - authors: Tim Davis, Patrick Amestoy, Iain Duff, Yanqing Chen - - CCOLAMD constrained column approximate minimum degree ordering - authors: Tim Davis, Sivasankaran Rajamanickam, Stefan Larimore. - Algorithm design collaborators: Esmond Ng, John Gilbert - (for COLAMD) - - ChangeLog a summary of changes to SuiteSparse. See */Doc/ChangeLog - for details for each package. - - CHOLMOD sparse Cholesky factorization. Requires AMD, COLAMD, CCOLAMD, - the BLAS, and LAPACK. Optionally uses METIS. This is chol and - x=A\b in MATLAB. - author for all modules: Tim Davis - CHOLMOD/Modify module authors: Tim Davis and William W. Hager - - COLAMD column approximate minimum degree ordering. This is the - built-in COLAMD function in MATLAB. - authors (of the code): Tim Davis and Stefan Larimore - Algorithm design collaborators: Esmond Ng, John Gilbert - - Contents.m a list of contents for 'help SuiteSparse' in MATLAB. - - CSparse a concise sparse matrix package, developed for my - book, "Direct Methods for Sparse Linear Systems", - published by SIAM. Intended primarily for teaching. - Note that the code is (c) Tim Davis, as stated in the book. - For production, use CXSparse instead. In particular, both - CSparse and CXSparse have the same include filename: cs.h. - This package is used for the built-in DMPERM in MATLAB. - author: Tim Davis - - CXSparse CSparse Extended. Includes support for complex matrices - and both int or long integers. Use this instead of CSparse - for production use; it creates a libcsparse.so (or *dylib on - the Mac) with the same name as CSparse. It is a superset - of CSparse. Any code that links against CSparse should - also be able to link against CXSparse instead. - author: Tim Davis, David Bateman - - include 'make install' places user-visible include files for each - package here, after 'make local' - - KLU sparse LU factorization, primarily for circuit simulation. - Requires AMD, COLAMD, and BTF. Optionally uses CHOLMOD, - CAMD, CCOLAMD, and METIS. - authors: Tim Davis, Ekanathan Palamadai - - LDL a very concise LDL' factorization package - author: Tim Davis - - lib 'make install' places shared libraries for each package - here, after 'make local' - - Makefile to compile all of SuiteSparse - - make compiles SuiteSparse libraries. - Subsequent "make install" will install - in just CMAKE_INSTALL_PATH (defaults to - /usr/local/lib on Linux or Mac). - - make both compiles SuiteSparse, and then "make install" - will instal in both ./lib and - CMAKE_INSTALL_PATH). - - make local compiles SuiteSparse. - Subsequent "make install will install only - in ./lib, ./include only. - Does not install in CMAKE_INSTALL_PATH. - - make global compiles SuiteSparse libraries. - Subsequent "make install" will install in - just /usr/local/lib (or whatever your - CMAKE_INSTALL_PREFIX is). - Does not install in ./lib and ./include. - - make install installs in the current directory - (./lib, ./include), and/or in - /usr/local/lib and /usr/local/include, - depending on whether "make", "make local", - "make global", or "make both", - etc has been done. - - make uninstall undoes 'make install' - - make distclean removes all files not in distribution, including - ./bin, ./share, ./lib, and ./include. - - make purge same as 'make distclean' - - make clean removes all files not in distribution, but - keeps compiled libraries and demoes, ./lib, - ./share, and ./include. - - Each individual package also has each of the above 'make' - targets. - - Things you don't need to do: - make docs creates user guides from LaTeX files - make cov runs statement coverage tests (Linux only) - - MATLAB_Tools various m-files for use in MATLAB - author: Tim Davis (all parts) - for spqr_rank: author Les Foster and Tim Davis - - Contents.m list of contents - dimacs10 loads matrices for DIMACS10 collection - Factorize object-oriented x=A\b for MATLAB - find_components finds connected components in an image - GEE simple Gaussian elimination - getversion.m determine MATLAB version - gipper.m create MATLAB archive - hprintf.m print hyperlinks in command window - LINFACTOR predecessor to Factorize package - MESHND nested dissection ordering of regular meshes - pagerankdemo.m illustrates how PageRank works - SFMULT C=S*F where S is sparse and F is full - shellgui display a seashell - sparseinv sparse inverse subset - spok check if a sparse matrix is valid - spqr_rank SPQR_RANK package. MATLAB toolbox for rank - deficient sparse matrices: null spaces, - reliable factorizations, etc. With Leslie - Foster, San Jose State Univ. - SSMULT C=A*B where A and B are both sparse - SuiteSparseCollection for the SuiteSparse Matrix Collection - waitmex waitbar for use inside a mexFunction - - The SSMULT and SFMULT functions are the basis for the - built-in C=A*B functions in MATLAB. - - Mongoose graph partitioning. - authors: Nuri Yeralan, Scott Kolodziej, William Hager, Tim Davis - - CHOLMOD/SuiteSparse_metis: a modified version of METIS, embedded into - the CHOLMOD library. See the README.txt files - for details. author: George Karypis. This is a slightly - modified copy included with SuiteSparse via the open-source - license provided by George Karypis. SuiteSparse cannot use - an unmodified copy METIS. - - RBio read/write sparse matrices in Rutherford/Boeing format - author: Tim Davis - - README.txt this file - - SPQR sparse QR factorization. This the built-in qr and x=A\b in - MATLAB. Also called SuiteSparseQR. - author of the CPU code: Tim Davis - author of GPU modules: Tim Davis, Nuri Yeralan, - Wissam Sid-Lakhdar, Sanjay Ranka - - GPUQREngine: GPU support package for SPQR - (not built into MATLAB, however) - authors: Tim Davis, Nuri Yeralan, Sanjay Ranka, - Wissam Sid-Lakhdar - - SuiteSparse_config configuration file for all the above packages. - CSparse and MATLAB_Tools do not use SuiteSparse_config. - author: Tim Davis - - SuiteSparse_GPURuntime GPU support package for SPQR and CHOLMOD - (not builtin to MATLAB, however). - - SuiteSparse_install.m install SuiteSparse for MATLAB - SuiteSparse_paths.m set paths for SuiteSparse MATLAB mexFunctions - - SuiteSparse_test.m exhaustive test for SuiteSparse in MATLAB - - ssget MATLAB interface to the SuiteSparse Matrix Collection - author: Tim Davis - - UMFPACK sparse LU factorization. Requires AMD and the BLAS. - This is the built-in lu and x=A\b in MATLAB. - author: Tim Davis - algorithm design collaboration: Iain Duff - -Some codes optionally use METIS 5.1.0. This package is located in SuiteSparse -in the `CHOLMOD/SuiteSparse_metis` directory. Its use is optional. To compile -CHOLMOD without it, use the CMAKE_OPTIONS="-DNPARTITION=1" setting. The use of -METIS can improve ordering quality for some matrices, particularly large 3D -discretizations. METIS has been slightly modified for use in SuiteSparse; see -the `CHOLMOD/SuiteSparse_metis/README.txt` file for details. +Be sure to first install all required libraries: BLAS and LAPACK for UMFPACK, +CHOLMOD, and SPQR, and GMP and MPFR for SPEX. Be sure to use the latest +libraries; SPEX requires MPFR 4.0.2 and GMP 6.1.2 (these version numbers +do NOT correspond to the X.Y.Z suffix of libgmp.so.X.Y.Z and libmpfr.so.X.Y.Z; +see the SPEX user guide for details). -Refer to each package for license, copyright, and author information. All -codes are authored or co-authored by Timothy A. Davis (email: davis@tamu.edu), -except for METIS (by George Karypis), GraphBLAS/cpu_features (by Google), -GraphBLAS/lz4 and zstd (by Yann Collet, now at Facebook), and -GraphBLAS/CUDA/jitify.hpp (by NVIDIA). Parts of GraphBLAS/CUDA are -Copyright (c) by NVIDIA. Please refer to each of these licenses. +To compile the libraries and install them only in SuiteSparse/lib (not +/usr/local/lib), do this instead in the top-level of SuiteSparse: +``` + mkdir -p build && cd build + cmake -DCMAKE_INSTALL_PREFIX=.. .. + cmake --build . + cmake --install . +``` + +If you add /home/me/SuiteSparse/lib to your library search path +(`LD_LIBRARY_PATH` in Linux), you can do the following (for example): +``` + S = /home/me/SuiteSparse + cc myprogram.c -I$(S)/include/suitesparse -lumfpack -lamd -lcholmod -lsuitesparseconfig -lm +``` + +To change the C and C++ compilers, and to compile in parallel use: +``` + cmake -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER==g++ .. +``` + +for example, which changes the compiler to gcc and g++. + +This will work on Linux/Unix and the Mac. It should automatically detect if +you have the Intel compilers or not, and whether or not you have CUDA. + +See `SuiteSparse_config/cmake_modules/SuiteSparsePolicy.cmake` to select your BLAS. + +You may also need to add SuiteSparse/lib to your path. If your copy of +SuiteSparse is in /home/me/SuiteSparse, for example, then add this to your +`~/.bashrc` file: + +``` +LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/me/SuiteSparse/lib +export LD_LIBRARY_PATH +``` -Licenses for each package are located in the following files, all in -PACKAGENAME/Doc/License.txt, and these files are also concatenated into -the top-level LICENSE.txt file. +For the Mac, use this instead: +``` +DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:/home/me/SuiteSparse/lib +export DYLD_LIBRARY_PATH +``` + +Default install location of files is below, where PACKAGE is one of the +packages in SuiteSparse: + + * `CMAKE_INSTALL_PREFIX/include/suitesparse/`: include files + * `CMAKE_INSTALL_PREFIX/lib/`: compiled libraries + * `CMAKE_INSTALL_PREFIX/lib/cmake/SuiteSparse/`: `*.cmake` scripts + for all of SuiteSparse + * `CMAKE_INSTALL_PREFIX/lib/cmake/PACKAGE/`: `*Config.cmake` scripts for a + specific package + * `CMAKE_INSTALL_PREFIX/lib/pkgconfig/PACKAGE.pc`: `.pc` scripts for + a specific package pkgconfig ----------------------------------------------------------------------------- QUICK START FOR MATLAB USERS (Linux or Mac): ----------------------------------------------------------------------------- -Uncompress the SuiteSparse.zip or SuiteSparse.tar.gz archive file (they contain -the same thing). Suppose you place SuiteSparse in the /home/me/SuiteSparse -folder. - -Add the SuiteSparse/lib folder to your run-time library path. On Linux, add -this to your ~/.bashrc script, assuming /home/me/SuiteSparse is the location of -your copy of SuiteSparse: +Suppose you place SuiteSparse in the `/home/me/SuiteSparse` folder. +Add the `SuiteSparse/lib` folder to your run-time library path. On Linux, add +this to your `~/.bashrc` script, assuming `/home/me/SuiteSparse` is the +location of your copy of SuiteSparse: +``` LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/me/SuiteSparse/lib export LD_LIBRARY_PATH +``` -For the Mac, use this instead, in your ~/.zshrc script, assuming you place -SuiteSparse in /Users/me/SuiteSparse: - +For the Mac, use this instead, in your `~/.zshrc` script, assuming you place +SuiteSparse in `/Users/me/SuiteSparse`: +``` DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:/Users/me/SuiteSparse/lib export DYLD_LIBRARY_PATH +``` -Compile all of SuiteSparse with "make local". +Compile all of SuiteSparse with `make local`. Next, compile the GraphBLAS MATLAB library. In the system shell while in the -SuiteSparse folder, type "make gbmatlab" if you want to install it system-wide -with "make install", or "make gblocal" if you want to use the library in +SuiteSparse folder, type `make gbmatlab` if you want to install it system-wide +with `make install`, or `make gblocal` if you want to use the library in your own SuiteSparse/lib. Then in the MATLAB Command Window, cd to the SuiteSparse directory and type @@ -521,162 +860,360 @@ Documents/MATLAB/startup.m. You can also use the `SuiteSparse_paths` m-file to set all your paths at the start of each MATLAB session. ----------------------------------------------------------------------------- -QUICK START FOR THE C/C++ LIBRARIES: +Compilation options ----------------------------------------------------------------------------- -For Linux and Mac: type the following in this directory (requires system -priviledge to do the `sudo make install`): +You can set specific options for CMake with the command (for example): +``` + cmake -DCHOLMOD_PARTITION=OFF -DBUILD_STATIC_LIBS=OFF -DCMAKE_BUILD_TYPE=Debug .. +``` - make - sudo make install +That command will compile all of SuiteSparse except for CHOLMOD/Partition +Module (because of `-DCHOLMOD_PARTITION=OFF`). Debug mode will be used (the +build type). The static libraries will not be built (since +`-DBUILD_STATIC_LIBS=OFF` is set). -All libraries will be created and copied into SuiteSparse/lib and into -/usr/local/lib. All include files need by the applications that use -SuiteSparse are copied into SuiteSparse/include and into /usr/local/include. +* `SUITESPARSE_ENABLE_PROJECTS`: -For Windows, import each `*/CMakeLists.txt` file into MS Visual Studio. + Semicolon separated list of projects to be built or `all`. + Default: `all` in which case the following projects are built: -Be sure to first install all required libraries: BLAS and LAPACK for UMFPACK, -CHOLMOD, and SPQR, and GMP and MPFR for SPEX. Be sure to use the latest -libraries; SPEX requires MPFR 4.0.2 and GMP 6.1.2 (these version numbers -do NOT correspond to the X.Y.Z suffix of libgmp.so.X.Y.Z and libmpfr.so.X.Y.Z; -see the SPEX user guide for details). + `suitesparse_config;mongoose;amd;btf;camd;ccolamd;colamd;cholmod;cxsparse;ldl;klu;umfpack;paru;rbio;spqr;spex;graphblas;lagraph` -To compile the libraries and install them only in SuiteSparse/lib (not -/usr/local/lib), do this instead in the top-level of SuiteSparse: + Additionally, `csparse` can be included in that list to build CSparse. - make local +* `CMAKE_BUILD_TYPE`: -If you add /home/me/SuiteSparse/lib to your library search path -(`LD_LIBRARY_PATH` in Linux), you can do the following (for example): + Default: `Release`, use `Debug` for debugging. - S = /home/me/SuiteSparse - cc myprogram.c -I$(S)/include -lumfpack -lamd -lcholmod -lsuitesparseconfig -lm +* `SUITESPARSE_USE_STRICT`: -To change the C and C++ compilers, and to compile in parallel use: + SuiteSparse has many user-definable settings of the form `SUITESPARSE_USE_*` + or `(package)_USE_*` for some particular package. In general, these settings + are not strict. For example, if `SUITESPARSE_USE_OPENMP` is `ON` then OpenMP + is preferred, but SuiteSparse can be used without OpenMP so no error is + generated if OpenMP is not found. However, if `SUITESPARSE_USE_STRICT` is + `ON` then all `*_USE_*` settings are treated strictly and an error occurs + if any are set to `ON` but the corresponding package or setting is not + available. The `*_USE_SYSTEM_*` settings are always treated as strict. + Default: `OFF`. - CC=gcc CX=g++ JOBS=32 make +* `SUITESPARSE_USE_CUDA`: -for example, which changes the compiler to gcc and g++, and runs make with -'make -j32', in parallel with 32 jobs. + If set to `ON`, CUDA is enabled for all of SuiteSparse. Default: `ON`, -This will work on Linux/Unix and the Mac. It should automatically detect if -you have the Intel compilers or not, and whether or not you have CUDA. + CUDA on Windows with MSVC appears to be working with this release, but it + should be considered as a prototype and may not be fully functional. I have + limited resources for testing CUDA on Windows. If you encounter issues, + disable CUDA and post this as an issue on GitHub. -NOTE: Use of the Intel MKL BLAS is strongly recommended. The OpenBLAS can -(rarely) result in severe performance degradation, in CHOLMOD in particular. -The reason for this is still under investigation and might already be resolved -in the current version of OpenBLAS. See -`SuiteSparse_config/cmake_modules/SuiteSparsePolicy.cmake` to select your BLAS. +* `CHOLMOD_USE_CUDA`: -You may also need to add SuiteSparse/lib to your path. If your copy of -SuiteSparse is in /home/me/SuiteSparse, for example, then add this to your -~/.bashrc file: + Default: `ON`. Both `SUITESPARSE_USE_CUDA` and `CHOLMOD_USE_CUDA` must be + enabled to use CUDA in CHOLMOD. - LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/me/SuiteSparse/lib - export LD_LIBRARY_PATH +* `SPQR_USE_CUDA`: -For the Mac, use this instead: + Default: `ON`. Both `SUITESPARSE_USE_CUDA` and `SPQR_USE_CUDA` must be + enabled to use CUDA in SPQR. - DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:/home/me/SuiteSparse/lib - export DYLD_LIBRARY_PATH +* `CMAKE_INSTALL_PREFIX`: ------------------------------------------------------------------------------ -Python interface ------------------------------------------------------------------------------ + Defines the install location (default on Linux is `/usr/local`). For example, + this command while in a folder `build` in the top level SuiteSparse folder + will set the install directory to `/stuff`, used by the subsequent + `sudo cmake --install .`: +``` + cmake -DCMAKE_INSTALL_PREFIX=/stuff .. + sudo cmake --install . +``` -See scikit-sparse and scikit-umfpack for the Python interface via SciPy: +* `SUITESPARSE_PKGFILEDIR`: -https://github.com/scikit-sparse/scikit-sparse + Directory where CMake Config and pkg-config files will be installed. By + default, CMake Config files will be installed in the subfolder `cmake` of the + directory where the (static) libraries will be installed (e.g., `lib`). The + `.pc` files for pkg-config will be installed in the subfolder `pkgconfig` of + the directory where the (static) libraries will be installed. -https://github.com/scikit-umfpack/scikit-umfpack + This option allows to install them at a location different from the (static) + libraries. This allows to install multiple configurations of the SuiteSparse + libraries at the same time (e.g., by also setting a different + `CMAKE_RELEASE_POSTFIX` and `CMAKE_INSTALL_LIBDIR` for each of them). To pick + up the respective configuration in downstream projects, set, e.g., + `CMAKE_PREFIX_PATH` (for CMake) or `PKG_CONFIG_PATH` (for build systems using + pkg-config) to the path containing the respective CMake Config files or + pkg-config files. + +* `SUITESPARSE_INCLUDEDIR_POSTFIX`: + + Postfix for installation target of header from SuiteSparse. Default: + suitesparse, so the default include directory is: + `CMAKE_INSTALL_PREFIX/include/suitesparse` + +* `BUILD_SHARED_LIBS`: + + If `ON`, shared libraries are built. + Default: `ON`. + +* `BUILD_STATIC_LIBS`: + + If `ON`, static libraries are built. + Default: `ON`, except for GraphBLAS, which takes a long time to compile so + the default for GraphBLAS is `OFF` unless `BUILD_SHARED_LIBS` is `OFF`. + +* `SUITESPARSE_CUDA_ARCHITECTURES`: + + A string, such as `"all"` or `"35;50;75;80"` that lists the CUDA + architectures to use when compiling CUDA kernels with `nvcc`. The `"all"` + option requires CMake 3.23 or later. Default: `"52;75;80"`. + +* `BLA_VENDOR`: + + A string. Leave unset, or use `"ANY"` to select any BLAS library (the + default). Or set to the name of a `BLA_VENDOR` defined by FindBLAS.cmake. + See: + https://cmake.org/cmake/help/latest/module/FindBLAS.html#blas-lapack-vendors + +* `SUITESPARSE_USE_64BIT_BLAS`: + + If `ON`, look for a 64-bit BLAS. If `OFF`: 32-bit only. Default: `OFF`. + +* `SUITESPARSE_USE_OPENMP`: + + If `ON`, OpenMP is used by default if it is available. Default: `ON`. + + GraphBLAS, LAGraph, and ParU will be vastly slower if OpenMP is not used. + CHOLMOD will be somewhat slower without OpenMP (as long as it still has a + parallel BLAS/LAPACK). Three packages (UMFPACK, CHOLMOD, and SPQR) rely + heavily on parallel BLAS/LAPACK libraries and those libraries may use OpenMP + internally. If you wish to disable OpenMP in an entire application, select a + single-threaded BLAS/LAPACK, or a parallel BLAS/LAPACK that does not use + OpenMP (such as the Apple Accelerate Framework). Using a single-threaded + BLAS/LAPACK library will cause UMFPACK, CHOLMOD, and SPQR to be vastly + slower. + + WARNING: GraphBLAS may not be thread-safe if built without OpenMP or pthreads + (see the GraphBLAS User Guide for details). + +* `SUITESPARSE_CONFIG_USE_OPENMP`: + + If `ON`, `SuiteSparse_config` uses OpenMP if it is available. + Default: `SUITESPARSE_USE_OPENMP`. + It is not essential and only used to let `SuiteSparse_time` call + `omp_get_wtime`. + +* `CHOLMOD_USE_OPENMP`: + + If `ON`, OpenMP is used in CHOLMOD if it is available. + Default: `SUITESPARSE_USE_OPENMP`. + +* `GRAPHBLAS_USE_OPENMP`: + + If `ON`, OpenMP is used in GraphBLAS if it is available. + Default: `SUITESPARSE_USE_OPENMP`. + +* `LAGRAPH_USE_OPENMP`: + + If `ON`, OpenMP is used in LAGraph if it is available. + Default: `SUITESPARSE_USE_OPENMP`. + +* `PARU_USE_OPENMP`: + + If `ON`, OpenMP is used in ParU if it is available. + Default: `SUITESPARSE_USE_OPENMP`. + +* `SUITESPARSE_DEMOS`: + + If `ON`, build the demo programs for each package. Default: `OFF`. + +* `SUITESPARSE_USE_SYSTEM_BTF`: + + If `ON`, use BTF libraries installed on the build system. If `OFF`, + automatically build BTF as dependency if needed. Default: `OFF`. + +* `SUITESPARSE_USE_SYSTEM_CHOLMOD`: + + If `ON`, use CHOLMOD libraries installed on the build system. If `OFF`, + automatically build CHOLMOD as dependency if needed. Default: `OFF`. + +* `SUITESPARSE_USE_SYSTEM_AMD`: + + If `ON`, use AMD libraries installed on the build system. If `OFF`, + automatically build AMD as dependency if needed. Default: `OFF`. + +* `SUITESPARSE_USE_SYSTEM_COLAMD`: + + If `ON`, use COLAMD libraries installed on the build system. If `OFF`, + automatically build COLAMD as dependency if needed. Default: `OFF`. + +* `SUITESPARSE_USE_SYSTEM_CAMD`: + + If `ON`, use CAMD libraries installed on the build system. If `OFF`, + automatically build CAMD as dependency if needed. Default: `OFF`. + +* `SUITESPARSE_USE_SYSTEM_CCOLAMD`: + + If `ON`, use CCOLAMD libraries installed on the build system. If `OFF`, + automatically build CCOLAMD as dependency if needed. Default: `OFF`. + +* `SUITESPARSE_USE_SYSTEM_GRAPHBLAS`: + + If `ON`, use GraphBLAS libraries installed on the build system. If `OFF`, + automatically build GraphBLAS as dependency if needed. Default: `OFF`. + +* `SUITESPARSE_USE_SYSTEM_SUITESPARSE_CONFIG`: + + If `ON`, use `SuiteSparse_config` libraries installed on the build system. If + `OFF`, automatically build `SuiteSparse_config` as dependency if needed. + Default: `OFF`. + +* `SUITESPARSE_USE_FORTRAN` + + If `ON`, use the Fortran compiler to determine how C calls Fortan, and to + build several optional Fortran routines. If `OFF`, use + `SUITESPARSE_C_TO_FORTRAN` to define how C calls Fortran (see + `SuiteSparse_config/cmake_modules/SuiteSparsePolicy.cmake` for details). + Default: `ON`. + +Additional options are available for specific packages: + +* `UMFPACK_USE_CHOLMOD`: + + If `ON`, UMFPACK uses CHOLMOD for additional (optional) + ordering options. Default: `ON`. + +* `KLU_USE_CHOLMOD`: + + If `ON`, KLU uses CHOLMOD for additional (optional) + ordering options. Default: `ON`. + +CHOLMOD is composed of a set of Modules that can be independently selected; +all options default to `ON`: + +* `CHOLMOD_GPL` + + If `OFF`, do not build any GPL-licensed module (MatrixOps, Modify, Supernodal, + and GPU modules) + +* `CHOLMOD_CHECK` + + If `OFF`, do not build the Check module. + +* `CHOLMOD_MATRIXOPS` + + If `OFF`, do not build the MatrixOps module. + +* `CHOLMOD_CHOLESKY` + If `OFF`, do not build the Cholesky module. This also disables the Supernodal + and Modify modules. + +* `CHOLMOD_MODIFY` + + If `OFF`, do not build the Modify module. + +* `CHOLMOD_CAMD` + + If `OFF`, do not link against CAMD and CCOLAMD. This also disables the + Partition module. + +* `CHOLMOD_PARTITION` + + If `OFF`, do not build the Partition module. + +* `CHOLMOD_SUPERNODAL` + + If `OFF`, do not build the Supernodal module. ----------------------------------------------------------------------------- -Compilation options +Possible build/install issues ----------------------------------------------------------------------------- -You can set specific options for CMake with the command (for example): +One common issue can affect all packages: getting the right #include files +that match the current libraries being built. It's possible that your Linux +distro has an older copy of SuiteSparse headers in /usr/include or +/usr/local/include, or that Homebrew has installed its suite-sparse bundle into +/opt/homebrew/include or other places. Old libraries can appear in in +/usr/local/lib, /usr/lib, etc. When building a new copy of SuiteSparse, the +cmake build system is normally (or always?) able to avoid these, and use the +right header for the right version of each library. + +As an additional guard against this possible error, each time one SuiteSparse +package #include's a header from another one, it checks the version number in +the header file, and reports an #error to the compiler if a stale version is +detected. In addition, the Example package checks both the header version and +the library version (by calling a function in each library). If the versions +mismatch in any way, the Example package reports an error at run time. + +For example, CHOLMOD 5.1.0 requires AMD 3.3.0 or later. If it detects an +older one in `amd.h`, it will report an `#error`: + +``` + #include "amd.h" + #if ( ... AMD version is stale ... ) + #error "CHOLMOD 5.1.0 requires AMD 3.3.0 or later" + #endif +``` + +and the compilation will fail. The Example package makes another check, +by calling `amd_version` and comparing it with the versions from the `amd.h` +header file. + +If this error or one like it occurs, check to see if you have an old copy of +SuiteSparse, and uninstall it before compiling your new copy of SuiteSparse. + +There are other many possible build/install issues that are covered by the +corresponding user guides for each package, such as finding the right BLAS, +OpenMP, and other libraries, and how to compile on the Mac when using GraphBLAS +inside MATLAB, and so on. Refer to the User Guides for more details. - CMAKE_OPTIONS="-DNPARTITION=1 -DNSTATIC=1 -DCMAKE_BUILD_TYPE=Debug" make +----------------------------------------------------------------------------- +Interfaces to SuiteSparse +----------------------------------------------------------------------------- -That command will compile all of SuiteSparse except for CHOLMOD/Partition -Module. Debug mode will be used. The static libraries will not be built -(NSTATIC is true). - - CMAKE_BUILD_TYPE: Default: "Release", use "Debug" for debugging. - - ENABLE_CUDA: if set to true, CUDA is enabled for the project. - Default: true for CHOLMOD and SPQR; false otherwise - - LOCAL_INSTALL: if true, "cmake --install" will install - into SuiteSparse/lib and SuiteSparse/include. - if false, "cmake --install" will install into the - default prefix (or the one configured with - CMAKE_INSTALL_PREFIX). - Default: false - - NSTATIC: if true, static libraries are not built. - Default: false, except for GraphBLAS, which - takes a long time to compile so the default for - GraphBLAS is true. For Mongoose, the NSTATIC setting - is treated as if it always false, since the mongoose - program is built with the static library. - - SUITESPARSE_CUDA_ARCHITECTURES: a string, such as "all" or - "35;50;75;80" that lists the CUDA architectures to use - when compiling CUDA kernels with nvcc. The "all" - option requires cmake 3.23 or later. - Default: "52;75;80". - - BLA_VENDOR a string. Leave unset, or use "ANY" to select any BLAS - library (the default). Or set to the name of a - BLA_VENDOR defined by FindBLAS.cmake. See: - https://cmake.org/cmake/help/latest/module/FindBLAS.html#blas-lapack-vendors - - ALLOW_64BIT_BLAS if true: look for a 64-bit BLAS. If false: 32-bit only. - Default: false. - - NOPENMP if true: OpenMP is not used. Default: false. - UMFPACK, CHOLMOD, SPQR, and GraphBLAS will be slow. - Note that BLAS and LAPACK may still use OpenMP - internally; if you wish to disable OpenMP in an entire - application, select a single-threaded BLAS/LAPACK. - WARNING: GraphBLAS may not be thread-safe if built - without OpenMP (see the User Guide for details). - - DEMO if true: build the demo programs for each package. - Default: false. - -Additional options are available within specific packages: - - NCHOLMOD if true, UMFPACK and KLU do not use CHOLMOD for - additional (optional) ordering options +MATLAB/Octave/R/Mathematica interfaces: -CHOLMOD is composed of a set of Modules that can be independently selected; -all options default to false: - - NGL if true: do not build any GPL-licensed module - (MatrixOps, Modify, Supernodal, and GPU modules) - NCHECK if true: do not build the Check module. - NMATRIXOPS if true: do not build the MatrixOps module. - NCHOLESKY if true: do not build the Cholesky module. - This also disables the Supernodal and Modify modules. - NMODIFY if true: do not build the Modify module. - NCAMD if true: do not link against CAMD and CCOLAMD. - This also disables the Partition module. - NPARTITION if true: do not build the Partition module. - NSUPERNODAL if true: do not build the Supernodal module. + Many built-in methods in MATLAB and Octave rely on SuiteSparse, including + `C=A*B` `x=A\b`, `L=chol(A)`, `[L,U,P,Q]=lu(A)`, `R=qr(A)`, `dmperm(A)`, + `p=amd(A)`, `p=colamd(A)`, ... + See also Mathematica, R, and many many more. The list is too long. + +Julia interface: + + https://github.com/JuliaSparse/SparseArrays.jl + +python interface to GraphBLAS by Anaconda and NVIDIA: + + https://pypi.org/project/python-graphblas + +Intel's Go interface to GraphBLAS: + + https://pkg.go.dev/github.com/intel/forGraphBLASGo + +See scikit-sparse and scikit-umfpack for the Python interface via SciPy: + + https://github.com/scikit-sparse/scikit-sparse + https://github.com/scikit-umfpack/scikit-umfpack + +See russell for a Rust interface: + + https://github.com/cpmech/russell ----------------------------------------------------------------------------- Acknowledgements ----------------------------------------------------------------------------- -I would like to thank François Bissey, Sebastien Villemot, Erik Welch, Jim -Kitchen, Markus Mützel, and Fabian Wein for their valuable feedback on the +Markus Mützel contributed the most recent update of the SuiteSparse build +system for all SuiteSparse packages, extensively porting it and modernizing it. + +I would also like to thank François Bissey, Sebastien Villemot, Erik Welch, Jim +Kitchen, and Fabian Wein for their valuable feedback on the SuiteSparse build system and how it works with various Linux / Python distros and other package managers. If you are a maintainer of a SuiteSparse packaging for a Linux distro, conda-forge, R, spack, brew, vcpkg, etc, please feel free to contact me if there's anything I can do to make your life easier. +I would also like to thank Raye Kimmerer for adding support for 32-bit +row/column indices in SPQR v4.2.0. See also the various Acknowledgements within each package. diff --git a/ThirdParty/SuiteSparse/SuiteSparse_config/CMakeLists.txt b/ThirdParty/SuiteSparse/SuiteSparse_config/CMakeLists.txt index 3c25382368..8e5c119b54 100644 --- a/ThirdParty/SuiteSparse/SuiteSparse_config/CMakeLists.txt +++ b/ThirdParty/SuiteSparse/SuiteSparse_config/CMakeLists.txt @@ -10,30 +10,42 @@ # get the version #------------------------------------------------------------------------------- -# cmake 3.22 is required to find the BLAS +# cmake 3.22 is required to find BLAS/LAPACK for UMFPACK, CHOLMOD, SPQR, +# and ParU: cmake_minimum_required ( VERSION 3.22 ) # version of both SuiteSparse and SuiteSparse_config -set ( SUITESPARSE_DATE "Jan 20, 2023" ) +set ( SUITESPARSE_DATE "Jan 20, 2024" ) set ( SUITESPARSE_VERSION_MAJOR 7 ) -set ( SUITESPARSE_VERSION_MINOR 0 ) -set ( SUITESPARSE_VERSION_SUB 1 ) +set ( SUITESPARSE_VERSION_MINOR 6 ) +set ( SUITESPARSE_VERSION_SUB 0 ) +set ( SUITESPARSE_CONFIG_VERSION_MAJOR ${SUITESPARSE_VERSION_MAJOR} CACHE STRING "" FORCE ) +set ( SUITESPARSE_CONFIG_VERSION_MINOR ${SUITESPARSE_VERSION_MINOR} CACHE STRING "" FORCE ) +set ( SUITESPARSE_CONFIG_VERSION_PATCH ${SUITESPARSE_VERSION_SUB} CACHE STRING "" FORCE ) message ( STATUS "Building SuiteSparse_config version: v" ${SUITESPARSE_VERSION_MAJOR}. ${SUITESPARSE_VERSION_MINOR}. ${SUITESPARSE_VERSION_SUB} " (" ${SUITESPARSE_DATE} ")" ) +#------------------------------------------------------------------------------- +# define the project +#------------------------------------------------------------------------------- + +project ( SuiteSparseConfig + VERSION "${SUITESPARSE_VERSION_MAJOR}.${SUITESPARSE_VERSION_MINOR}.${SUITESPARSE_VERSION_SUB}" + LANGUAGES C ) + #------------------------------------------------------------------------------- # SuiteSparse policies #------------------------------------------------------------------------------- set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} - ${CMAKE_SOURCE_DIR}/cmake_modules ) + ${PROJECT_SOURCE_DIR}/cmake_modules ) include ( SuiteSparsePolicy ) -if ( NOT NFORTRAN ) +if ( SUITESPARSE_HAS_FORTRAN ) include ( FortranCInterface ) else ( ) # No Fortran compiler available or enabled, configuration is not automatic. @@ -41,30 +53,74 @@ else ( ) set ( FortranCInterface_GLOBAL__MACRO ${SUITESPARSE_C_TO_FORTRAN} ) endif ( ) +message ( STATUS "C to Fortran calling protocol: ") +message ( STATUS " SUITESPARSE_HAS_FORTRAN : ${SUITESPARSE_HAS_FORTRAN}" ) +message ( STATUS " FortranCInterface_GLOBAL_MACRO : ${FortranCInterface_GLOBAL_MACRO}" ) +message ( STATUS " FortranCInterface_GLOBAL__MACRO : ${FortranCInterface_GLOBAL__MACRO}" ) + #------------------------------------------------------------------------------- -# define the project +# CUDA warning on Windows with MSVC #------------------------------------------------------------------------------- -project ( suitesparseconfig - VERSION "${SUITESPARSE_VERSION_MAJOR}.${SUITESPARSE_VERSION_MINOR}.${SUITESPARSE_VERSION_SUB}" - LANGUAGES C ) +if ( SUITESPARSE_HAS_CUDA AND MSVC ) + message ( WARNING "NOTE: CUDA on MSVC has only recently been revised. It appears to be functional but has not been as rigorously tested as I would like (I have limited resources for testing CUDA on Windows). If you encounter issues, set the cmake option SUITESPARSE_USE_CUDA to OFF and post an issue on GitHub." ) +endif ( ) #------------------------------------------------------------------------------- -# find library dependencies +# find OpenMP #------------------------------------------------------------------------------- -option ( NOPENMP "ON: do not use OpenMP. OFF (default): use OpenMP" off ) -if ( NOPENMP ) +option ( SUITESPARSE_CONFIG_USE_OPENMP "ON: Use OpenMP in SuiteSparse_config if available. OFF: Do not use OpenMP. (Default: SUITESPARSE_USE_OPENMP)" ${SUITESPARSE_USE_OPENMP} ) +if ( SUITESPARSE_CONFIG_USE_OPENMP ) + if ( CMAKE_VERSION VERSION_LESS 3.24 ) + find_package ( OpenMP COMPONENTS C ) + else ( ) + find_package ( OpenMP COMPONENTS C GLOBAL ) + endif ( ) +else ( ) # OpenMP has been disabled - message ( STATUS "OpenMP disabled" ) - set ( OPENMP_FOUND false ) + set ( OpenMP_C_FOUND OFF ) +endif ( ) + +if ( SUITESPARSE_CONFIG_USE_OPENMP AND OpenMP_C_FOUND ) + set ( SUITESPARSE_CONFIG_HAS_OPENMP ON ) else ( ) - find_package ( OpenMP ) + set ( SUITESPARSE_CONFIG_HAS_OPENMP OFF ) endif ( ) +message ( STATUS "SuiteSparse_config has OpenMP: ${SUITESPARSE_CONFIG_HAS_OPENMP}" ) -# AMICI -# include ( SuiteSparseBLAS ) -set(SuiteSparse_BLAS_integer int64_t) +# check for strict usage +if ( SUITESPARSE_USE_STRICT AND SUITESPARSE_CONFIG_USE_OPENMP AND NOT SUITESPARSE_CONFIG_HAS_OPENMP ) + message ( FATAL_ERROR "OpenMP required for SuiteSparse_config but not found" ) +endif ( ) + +# check for librt in case of fallback to "clock_gettime" +include ( CheckSymbolExists ) +check_symbol_exists ( clock_gettime "time.h" NO_RT ) +if ( NO_RT ) + message ( STATUS "Using clock_gettime without librt" ) + set ( SUITESPARSE_HAVE_CLOCK_GETTIME ON ) +else ( ) + # check if we need to link to librt for that function + set ( _orig_CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ) + list ( APPEND CMAKE_REQUIRED_LIBRARIES "rt" ) + check_symbol_exists ( clock_gettime "time.h" WITH_RT ) + set ( CMAKE_REQUIRED_LIBRARIES ${_orig_CMAKE_REQUIRED_LIBRARIES} ) + if ( WITH_RT ) + message ( STATUS "Using clock_gettime with librt" ) + set ( SUITESPARSE_HAVE_CLOCK_GETTIME ON ) + endif ( ) +endif ( ) + +if ( NOT SUITESPARSE_CONFIG_USE_OPENMP AND NOT SUITESPARSE_HAVE_CLOCK_GETTIME ) + message ( STATUS "No OpenMP and no clock_gettime available. Timing functions won't work." ) +endif ( ) + +#------------------------------------------------------------------------------- +# find the BLAS +#------------------------------------------------------------------------------- + +include ( SuiteSparseBLAS ) #------------------------------------------------------------------------------- # configure files @@ -79,36 +135,57 @@ configure_file ( "Config/README.md.in" NEWLINE_STYLE LF ) #------------------------------------------------------------------------------- -# dynamic suitesparseconfig library properties +# dynamic SuiteSparseConfig library properties #------------------------------------------------------------------------------- file ( GLOB SUITESPARSECONFIG_SOURCES "*.c" ) -add_library ( suitesparseconfig SHARED ${SUITESPARSECONFIG_SOURCES} ) -set_target_properties ( suitesparseconfig PROPERTIES - VERSION ${SUITESPARSE_VERSION_MAJOR}.${SUITESPARSE_VERSION_MINOR}.${SUITESPARSE_VERSION_SUB} - C_STANDARD_REQUIRED 11 - SOVERSION ${SUITESPARSE_VERSION_MAJOR} - PUBLIC_HEADER "SuiteSparse_config.h" - WINDOWS_EXPORT_ALL_SYMBOLS ON ) +if ( BUILD_SHARED_LIBS ) + add_library ( SuiteSparseConfig SHARED ${SUITESPARSECONFIG_SOURCES} ) + + set_target_properties ( SuiteSparseConfig PROPERTIES + VERSION ${SUITESPARSE_VERSION_MAJOR}.${SUITESPARSE_VERSION_MINOR}.${SUITESPARSE_VERSION_SUB} + C_STANDARD 11 + C_STANDARD_REQUIRED ON + OUTPUT_NAME suitesparseconfig + SOVERSION ${SUITESPARSE_VERSION_MAJOR} + PUBLIC_HEADER "SuiteSparse_config.h" + WINDOWS_EXPORT_ALL_SYMBOLS ON ) + + if ( ${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.25" ) + set_target_properties ( SuiteSparseConfig PROPERTIES EXPORT_NO_SYSTEM ON ) + endif ( ) + + target_include_directories ( SuiteSparseConfig + INTERFACE $ + $ ) +endif ( ) #------------------------------------------------------------------------------- -# static suitesparseconfig library properties +# static SuiteSparseConfig library properties #------------------------------------------------------------------------------- -if ( NOT NSTATIC ) - add_library ( suitesparseconfig_static STATIC ${SUITESPARSECONFIG_SOURCES} ) +if ( BUILD_STATIC_LIBS ) + add_library ( SuiteSparseConfig_static STATIC ${SUITESPARSECONFIG_SOURCES} ) - set_target_properties ( suitesparseconfig_static PROPERTIES - VERSION ${SUITESPARSE_VERSION_MAJOR}.${SUITESPARSE_VERSION_MINOR}.${SUITESPARSE_VERSION_SUB} - C_STANDARD_REQUIRED 11 + set_target_properties ( SuiteSparseConfig_static PROPERTIES + C_STANDARD 11 + C_STANDARD_REQUIRED ON OUTPUT_NAME suitesparseconfig - SOVERSION ${SUITESPARSE_VERSION_MAJOR} ) + PUBLIC_HEADER "SuiteSparse_config.h" ) if ( MSVC ) - set_target_properties ( suitesparseconfig_static PROPERTIES + set_target_properties ( SuiteSparseConfig_static PROPERTIES OUTPUT_NAME suitesparseconfig_static ) endif ( ) + + if ( ${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.25" ) + set_target_properties ( SuiteSparseConfig_static PROPERTIES EXPORT_NO_SYSTEM ON ) + endif ( ) + + target_include_directories ( SuiteSparseConfig_static + INTERFACE $ + $ ) endif ( ) #------------------------------------------------------------------------------- @@ -116,24 +193,45 @@ endif ( ) #------------------------------------------------------------------------------- # libm: -if ( NOT WIN32 ) - target_link_libraries ( suitesparseconfig PUBLIC m ) - if ( NOT NSTATIC ) - target_link_libraries ( suitesparseconfig_static PUBLIC m ) +include ( CheckSymbolExists ) +check_symbol_exists ( fmax "math.h" NO_LIBM ) +if ( NOT NO_LIBM ) + if ( BUILD_SHARED_LIBS ) + target_link_libraries ( SuiteSparseConfig PRIVATE m ) + endif ( ) + if ( BUILD_STATIC_LIBS ) + target_link_libraries ( SuiteSparseConfig_static PUBLIC m ) + list ( APPEND SUITESPARSE_CONFIG_STATIC_LIBS "m" ) endif ( ) endif ( ) # OpenMP: -if ( OPENMP_FOUND ) +if ( SUITESPARSE_CONFIG_HAS_OPENMP ) message ( STATUS "OpenMP C libraries: ${OpenMP_C_LIBRARIES} ") message ( STATUS "OpenMP C include: ${OpenMP_C_INCLUDE_DIRS} ") message ( STATUS "OpenMP C flags: ${OpenMP_C_FLAGS} ") - target_link_libraries ( suitesparseconfig PUBLIC ${OpenMP_C_LIBRARIES} ) - if ( NOT NSTATIC ) - target_link_libraries ( suitesparseconfig_static PUBLIC ${OpenMP_C_LIBRARIES} ) + if ( BUILD_SHARED_LIBS ) + target_link_libraries ( SuiteSparseConfig PRIVATE OpenMP::OpenMP_C ) + target_include_directories ( SuiteSparseConfig SYSTEM AFTER INTERFACE + "$" ) + endif ( ) + if ( BUILD_STATIC_LIBS ) + target_link_libraries ( SuiteSparseConfig_static PRIVATE OpenMP::OpenMP_C ) + target_include_directories ( SuiteSparseConfig_static SYSTEM AFTER INTERFACE + "$" ) + list ( APPEND SUITESPARSE_CONFIG_STATIC_LIBS ${OpenMP_C_LIBRARIES} ) + endif ( ) +else ( ) + # librt + if ( WITH_RT ) + if ( BUILD_SHARED_LIBS ) + target_link_libraries ( SuiteSparseConfig PRIVATE "rt" ) + endif ( ) + if ( BUILD_STATIC_LIBS ) + target_link_libraries ( SuiteSparseConfig_static PRIVATE "rt" ) + list ( APPEND SUITESPARSE_CONFIG_STATIC_LIBS "rt" ) + endif ( ) endif ( ) - set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS} " ) - include_directories ( ${OpenMP_C_INCLUDE_DIRS} ) endif ( ) # BLAS: @@ -147,23 +245,123 @@ if ( BLAS_FOUND ) endif ( ) #------------------------------------------------------------------------------- -# suitesparseconfig installation location +# SuiteSparseConfig installation location #------------------------------------------------------------------------------- +include ( CMakePackageConfigHelpers ) + file ( GLOB SUITESPARSE_CMAKE_MODULES "cmake_modules/*" ) -install ( TARGETS suitesparseconfig - LIBRARY DESTINATION ${SUITESPARSE_LIBDIR} - ARCHIVE DESTINATION ${SUITESPARSE_LIBDIR} - RUNTIME DESTINATION ${SUITESPARSE_BINDIR} - PUBLIC_HEADER DESTINATION ${SUITESPARSE_INCLUDEDIR} ) +if ( BUILD_SHARED_LIBS ) + install ( TARGETS SuiteSparseConfig + EXPORT SuiteSparse_configTargets + LIBRARY DESTINATION ${SUITESPARSE_LIBDIR} + ARCHIVE DESTINATION ${SUITESPARSE_LIBDIR} + RUNTIME DESTINATION ${SUITESPARSE_BINDIR} + PUBLIC_HEADER DESTINATION ${SUITESPARSE_INCLUDEDIR} ) +endif ( ) +if ( BUILD_STATIC_LIBS ) + install ( TARGETS SuiteSparseConfig_static + EXPORT SuiteSparse_configTargets + ARCHIVE DESTINATION ${SUITESPARSE_LIBDIR} + PUBLIC_HEADER DESTINATION ${SUITESPARSE_INCLUDEDIR} ) +endif ( ) install ( FILES ${SUITESPARSE_CMAKE_MODULES} - DESTINATION ${SUITESPARSE_LIBDIR}/cmake/SuiteSparse + DESTINATION ${SUITESPARSE_PKGFILEDIR}/cmake/SuiteSparse COMPONENT Development ) -if ( NOT NSTATIC ) - install ( TARGETS suitesparseconfig_static - ARCHIVE DESTINATION ${SUITESPARSE_LIBDIR} ) + +# create (temporary) export target file during build +export ( EXPORT SuiteSparse_configTargets + NAMESPACE SuiteSparse:: + FILE ${CMAKE_CURRENT_BINARY_DIR}/SuiteSparse_configTargets.cmake ) + +# install export target and config for find_package +install ( EXPORT SuiteSparse_configTargets + NAMESPACE SuiteSparse:: + DESTINATION ${SUITESPARSE_PKGFILEDIR}/cmake/SuiteSparse_config ) + +configure_package_config_file ( + Config/SuiteSparse_configConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/SuiteSparse_configConfig.cmake + INSTALL_DESTINATION ${SUITESPARSE_PKGFILEDIR}/cmake/SuiteSparse_config ) + +write_basic_package_version_file ( + ${CMAKE_CURRENT_BINARY_DIR}/SuiteSparse_configConfigVersion.cmake + COMPATIBILITY SameMajorVersion ) + +install ( FILES + ${CMAKE_CURRENT_BINARY_DIR}/SuiteSparse_configConfig.cmake + ${CMAKE_CURRENT_BINARY_DIR}/SuiteSparse_configConfigVersion.cmake + DESTINATION ${SUITESPARSE_PKGFILEDIR}/cmake/SuiteSparse_config ) + +#------------------------------------------------------------------------------- +# create pkg-config file +#------------------------------------------------------------------------------- + +if ( NOT MSVC ) + # This might be something like: + # /usr/lib/libgomp.so;/usr/lib/libpthread.a;m + # convert to -l flags for pkg-config, i.e.: "-lgomp -lpthread -lm" + set ( SUITESPARSE_CONFIG_STATIC_LIBS_LIST ${SUITESPARSE_CONFIG_STATIC_LIBS} ) + set ( SUITESPARSE_CONFIG_STATIC_LIBS "" ) + foreach ( _lib ${SUITESPARSE_CONFIG_STATIC_LIBS_LIST} ) + string ( FIND ${_lib} "." _pos REVERSE ) + if ( ${_pos} EQUAL "-1" ) + set ( SUITESPARSE_CONFIG_STATIC_LIBS "${SUITESPARSE_CONFIG_STATIC_LIBS} -l${_lib}" ) + continue () + endif ( ) + set ( _kinds "SHARED" "STATIC" ) + if ( WIN32 ) + list ( PREPEND _kinds "IMPORT" ) + endif ( ) + foreach ( _kind IN LISTS _kinds ) + set ( _regex ".*\\/(lib)?([^\\.]*)(${CMAKE_${_kind}_LIBRARY_SUFFIX})" ) + if ( ${_lib} MATCHES ${_regex} ) + string ( REGEX REPLACE ${_regex} "\\2" _libname ${_lib} ) + if ( NOT "${_libname}" STREQUAL "" ) + set ( SUITESPARSE_CONFIG_STATIC_LIBS "${SUITESPARSE_CONFIG_STATIC_LIBS} -l${_libname}" ) + break () + endif ( ) + endif ( ) + endforeach ( ) + endforeach ( ) + + set ( prefix "${CMAKE_INSTALL_PREFIX}" ) + set ( exec_prefix "\${prefix}" ) + cmake_path ( IS_ABSOLUTE SUITESPARSE_LIBDIR SUITESPARSE_LIBDIR_IS_ABSOLUTE ) + if (SUITESPARSE_LIBDIR_IS_ABSOLUTE) + set ( libdir "${SUITESPARSE_LIBDIR}") + else ( ) + set ( libdir "\${exec_prefix}/${SUITESPARSE_LIBDIR}") + endif ( ) + cmake_path ( IS_ABSOLUTE SUITESPARSE_INCLUDEDIR SUITESPARSE_INCLUDEDIR_IS_ABSOLUTE ) + if (SUITESPARSE_INCLUDEDIR_IS_ABSOLUTE) + set ( includedir "${SUITESPARSE_INCLUDEDIR}") + else ( ) + set ( includedir "\${prefix}/${SUITESPARSE_INCLUDEDIR}") + endif ( ) + if ( BUILD_SHARED_LIBS ) + set ( SUITESPARSE_LIB_BASE_NAME $ ) + else ( ) + set ( SUITESPARSE_LIB_BASE_NAME $ ) + endif ( ) + configure_file ( + Config/SuiteSparse_config.pc.in + SuiteSparse_config.pc.out + @ONLY + NEWLINE_STYLE LF ) + file ( GENERATE + OUTPUT SuiteSparse_config.pc + INPUT ${CMAKE_CURRENT_BINARY_DIR}/SuiteSparse_config.pc.out + NEWLINE_STYLE LF ) + install ( FILES + ${CMAKE_CURRENT_BINARY_DIR}/SuiteSparse_config.pc + DESTINATION ${SUITESPARSE_PKGFILEDIR}/pkgconfig ) endif ( ) +#------------------------------------------------------------------------------- +# report status +#------------------------------------------------------------------------------- + include ( SuiteSparseReport ) diff --git a/ThirdParty/SuiteSparse/SuiteSparse_config/Config/README.md.in b/ThirdParty/SuiteSparse/SuiteSparse_config/Config/README.md.in index e45ae9c08d..bc0c9142fa 100644 --- a/ThirdParty/SuiteSparse/SuiteSparse_config/Config/README.md.in +++ b/ThirdParty/SuiteSparse/SuiteSparse_config/Config/README.md.in @@ -9,11 +9,41 @@ by Tim Davis, available at https://github.com/DrTimothyAldenDavis/SuiteSparse . Primary author of SuiteSparse (codes and algorithms, excl. METIS): Tim Davis -Code co-authors, in alphabetical order (not including METIS): - Patrick Amestoy, David Bateman, Jinhao Chen, Yanqing Chen, Iain Duff, - Les Foster, William Hager, Scott Kolodziej, Chris Lourenco, Stefan - Larimore, Erick Moreno-Centeno, Ekanathan Palamadai, Sivasankaran - Rajamanickam, Sanjay Ranka, Wissam Sid-Lakhdar, Nuri Yeralan. +Code co-authors, in alphabetical order (not including METIS or LAGraph): + Patrick Amestoy, Mohsen Aznaveh, David Bateman, Jinhao Chen, Yanqing Chen, + Iain Duff, Joe Eaton, Les Foster, William Hager, Raye Kimmerer, Scott + Kolodziej, Chris Lourenco, Stefan Larimore, Lorena Mejia Domenzain, Erick + Moreno-Centeno, Markus Mützel, Corey Nolel, Ekanathan Palamadai, + Sivasankaran Rajamanickam, Sanjay Ranka, Wissam Sid-Lakhdar, and + Nuri Yeralan. + +LAGraph has been developed by the highest number of developers of any of +the packages in SuiteSparse and deserves its own list. The list also +appears in LAGraph/Contibutors.txt: + + Janos B. Antal, Budapest University of Technology and Economics, Hungary + Mohsen Aznaveh, Texas A&M University + David A. Bader New Jersey Institute of Technology + Aydin Buluc, Lawrence Berkeley National Lab + Jinhao Chen, Texas A&M University + Tim Davis, Texas A&M University + Florentin Dorre, Technische Univeritat Dresden, Neo4j + Marton Elekes, Budapest University of Technology and Economics, Hungary + Balint Hegyi, Budapest University of Technology and Economics, Hungary + Tanner Hoke, Texas A&M University + James Kitchen, Anaconda + Scott Kolodziej, Texas A&M University + Pranav Konduri, Texas A&M University + Roi Lipman, Redis Labs (now FalkorDB) + Tze Meng Low, Carnegie Mellon University + Tim Mattson, Intel + Scott McMillan, Carnegie Mellon University + Markus Muetzel + Michel Pelletier, Graphegon + Gabor Szarnyas, CWI Amsterdam, The Netherlands + Erik Welch, Anaconda, NVIDIA + Carl Yang, University of California at Davis, Waymo + Yongzhe Zhang, SOKENDAI, Japan METIS is authored by George Karypis. @@ -21,17 +51,460 @@ Additional algorithm designers: Esmond Ng and John Gilbert. Refer to each package for license, copyright, and author information. +----------------------------------------------------------------------------- +Documentation +----------------------------------------------------------------------------- + +Refer to each package for the documentation on each package, typically in the +Doc subfolder. + ----------------------------------------------------------------------------- SuiteSparse branches ----------------------------------------------------------------------------- - * dev: the default branch, with recent updates of features to appear in - the next stable release. The intent is to keep this branch in - fully working order at all times, but the features will not be - finalized at any given time. - * stable: the most recent stable release. - * dev2: working branch. All submitted PRs should made to this branch. - This branch might not always be in working order. +* dev: the default branch, with recent updates of features to appear in + the next stable release. The intent is to keep this branch in + fully working order at all times, but the features will not be + finalized at any given time. +* stable: the most recent stable release. +* dev2: working branch. All submitted PRs should made to this branch. + This branch might not always be in working order. + +----------------------------------------------------------------------------- +SuiteSparse Packages +----------------------------------------------------------------------------- + +Packages in SuiteSparse, and files in this directory: + +* `AMD` + + approximate minimum degree ordering. This is the built-in AMD function in + MATLAB. + + authors: Tim Davis, Patrick Amestoy, Iain Duff + +* `bin` + + where programs are placed when compiled, for `make local` + +* `BTF` + + permutation to block triangular form + + authors: Tim Davis, Ekanathan Palamadai + +* `build` + + folder for default build tree + +* `CAMD` + + constrained approximate minimum degree ordering + + authors: Tim Davis, Patrick Amestoy, Iain Duff, Yanqing Chen + +* `CCOLAMD` + + constrained column approximate minimum degree ordering + + authors: Tim Davis, Sivasankaran Rajamanickam, Stefan Larimore. + + Algorithm design collaborators: Esmond Ng, John Gilbert (for COLAMD) + +* `ChangeLog` + + a summary of changes to SuiteSparse. See `*/Doc/ChangeLog` for details for + each package. + +* `CHOLMOD` + + sparse Cholesky factorization. Requires AMD, COLAMD, CCOLAMD, the BLAS, and + LAPACK. Optionally uses METIS. This is `chol` and `x=A\b` in MATLAB. + + author for all modules: Tim Davis + + CHOLMOD/Modify module authors: Tim Davis and William W. Hager + + CHOLMOD/SuiteSparse_metis: a modified version of METIS, embedded into the + CHOLMOD library. See the README.txt files for details. author: George + Karypis. This is a slightly modified copy included with SuiteSparse via the + open-source license provided by George Karypis. SuiteSparse cannot use an + unmodified copy of METIS. + +* `CITATION.bib` + + citations for SuiteSparse packages, in bibtex format. + +* `CMakeLists.txt` + + optional, to compile all of SuiteSparse. See below. + +* `CODE_OF_CONDUCT.md` + + community guidelines + +* `COLAMD` + + column approximate minimum degree ordering. This is the built-in COLAMD + function in MATLAB. + + authors (of the code): Tim Davis and Stefan Larimore + + Algorithm design collaborators: Esmond Ng, John Gilbert + +* `Contents.m` + + a list of contents for 'help SuiteSparse' in MATLAB. + +* `CONTRIBUTING.md` + + how to contribute to SuiteSparse + +* `CONTRIBUTOR-LICENSE.txt` + + required contributor agreement + +* `CSparse` + + a concise sparse matrix package, developed for my book, "Direct Methods for + Sparse Linear Systems", published by SIAM. Intended primarily for teaching. + Note that the code is (c) Tim Davis, as stated in the book. + + For production, use CXSparse instead. In particular, both CSparse and + CXSparse have the same include filename: `cs.h`. This package is used for + the built-in DMPERM in MATLAB. + + author: Tim Davis + +* `CXSparse` + + CSparse Extended. Includes support for complex matrices and both int or long + integers. Use this instead of CSparse for production use; it creates a + libcsparse.so (or dylib on the Mac) with the same name as CSparse. It is a + superset of CSparse. Any code that links against CSparse should also be able + to link against CXSparse instead. + + author: Tim Davis, David Bateman + +* `Example` + + a simple package that relies on almost all of SuiteSparse + +* `.github` + + workflows for CI testing on GitHub. + +* `GraphBLAS` + + graph algorithms in the language of linear algebra. + + https://graphblas.org + + authors: Tim Davis, Joe Eaton, Corey Nolet + +* `include` + + `make install` places user-visible include files for each package here, after + `make local`. + +* `KLU` + + sparse LU factorization, primarily for circuit simulation. Requires AMD, + COLAMD, and BTF. Optionally uses CHOLMOD, CAMD, CCOLAMD, and METIS. + + authors: Tim Davis, Ekanathan Palamadai + +* `LAGraph` + + a graph algorithms library based on GraphBLAS. See also + https://github.com/GraphBLAS/LAGraph + + Authors: many. + +* `LDL` + + a very concise LDL' factorization package + + author: Tim Davis + +* `lib` + + `make install` places shared libraries for each package here, after + `make local`. + +* `LICENSE.txt` + + collected licenses for each package. + +* `Makefile` + + optional, to compile all of SuiteSparse using `make`, which is used as a + simple wrapper for `cmake` in each subproject. + + * `make` + + compiles SuiteSparse libraries. Subsequent `make install` will install + in `CMAKE_INSTALL_PATH` (might default to `/usr/local/lib` on Linux or Mac). + + * `make local` + + compiles SuiteSparse. Subsequent `make install` will install in `./lib`, + `./include`. Does not install in `CMAKE_INSTALL_PATH`. + + * `make global` + + compiles SuiteSparse libraries. Subsequent `make install` will install in + `/usr/local/lib` (or whatever the configured `CMAKE_INSTALL_PREFIX` is). + Does not install in `./lib` and `./include`. + + * `make install` + + installs in the current directory (`./lib`, `./include`), or in + `/usr/local/lib` and `/usr/local/include`, (the latter defined by + `CMAKE_INSTALL_PREFIX`) depending on whether `make`, `make local`, or + `make global` has been done. + + * `make uninstall` + + undoes `make install`. + + * `make distclean` + + removes all files not in distribution, including `./bin`, `./share`, + `./lib`, and `./include`. + + * `make purge` + + same as `make distclean`. + + * `make clean` + + removes all files not in distribution, but keeps compiled libraries and + demos, `./lib`, `./share`, and `./include`. + + Each individual subproject also has each of the above `make` targets. + + Things you don't need to do: + + * `make docs` + + creates user guides from LaTeX files + + * `make cov` + + runs statement coverage tests (Linux only) + +* `MATLAB_Tools` + + various m-files for use in MATLAB + + author: Tim Davis (all parts) + + for `spqr_rank`: author Les Foster and Tim Davis + + * `Contents.m` + + list of contents + + * `dimacs10` + + loads matrices for DIMACS10 collection + + * `Factorize` + + object-oriented `x=A\b` for MATLAB + + * `find_components` + + finds connected components in an image + + * `GEE` + + simple Gaussian elimination + + * `getversion.m` + + determine MATLAB version + + * `gipper.m` + + create MATLAB archive + + * `hprintf.m` + + print hyperlinks in command window + + * `LINFACTOR` + + predecessor to `Factorize` package + + * `MESHND` + + nested dissection ordering of regular meshes + + * `pagerankdemo.m` + + illustrates how PageRank works + + * `SFMULT` + + `C=S*F` where `S` is sparse and `F` is full + + * `shellgui` + + display a seashell + + * `sparseinv` + + sparse inverse subset + + * `spok` + + check if a sparse matrix is valid + + * `spqr_rank` + + SPQR_RANK package. MATLAB toolbox for rank deficient sparse matrices: null + spaces, reliable factorizations, etc. With Leslie Foster, San Jose State + Univ. + + * `SSMULT` + + `C=A*B` where `A` and `B` are both sparse. + This was the basis for the built-in `C=A*B` in MATLAB, until it was + superseded by GraphBLAS in MATLAB R2021a. + + * `SuiteSparseCollection` + + for the SuiteSparse Matrix Collection + + * `waitmex` + + waitbar for use inside a mexFunction + +* `Mongoose` + + graph partitioning. + + authors: Nuri Yeralan, Scott Kolodziej, William Hager, Tim Davis + +* `ParU` + + a parallel unsymmetric pattern multifrontal method. + + Currently a pre-release. + + authors: Mohsen Aznaveh and Tim Davis + +* `RBio` + + read/write sparse matrices in Rutherford/Boeing format + + author: Tim Davis + +* `README.md` + + this file + +* `SPEX` + + solves sparse linear systems in exact arithmetic. + + Requires the GNU GMP and MPRF libraries. + + This will be soon replaced by a more general package, SPEX v3 that includes + this method (exact sparse LU) and others (sparse exact Cholesky, and sparse + exact update/downdate). The API of v3 will be changing significantly. + + authors: Chris Lourenco, Jinhao Chen, Erick Moreno-Centeno, + Lorena Lorena Mejia Domenzain, and Tim Davis. + + See https://github.com/clouren/SPEX for the latest version. + +* `SPQR` + + sparse QR factorization. This the built-in `qr` and `x=A\b` in MATLAB. Also + called SuiteSparseQR. + + Includes two GPU libraries: `SPQR/GPUQREngine` and + `SPQR/SuiteSparse_GPURuntime`. + + author of the CPU code: Tim Davis + + author of GPU modules: Tim Davis, Nuri Yeralan, Wissam Sid-Lakhdar, + Sanjay Ranka + +* `ssget` + + MATLAB interface to the SuiteSparse Matrix Collection + + author: Tim Davis + +* `SuiteSparse_config` + + library with common functions and configuration for all the above packages. + `CSparse`, `GraphBLAS`, `LAGraph`, and `MATLAB_Tools` do not use + `SuiteSparse_config`. + + author: Tim Davis + +* `SuiteSparse_demo.m` + + a demo of SuiteSparse for MATLAB + +* `SuiteSparse_install.m` + + install SuiteSparse for MATLAB + +* `SuiteSparse_paths.m` + + set paths for SuiteSparse MATLAB mexFunctions + +* `SuiteSparse_test.m` + + exhaustive test for SuiteSparse in MATLAB + +* `UMFPACK` + + sparse LU factorization. Requires `AMD` and the `BLAS`. + + This is the built-in `lu` and `x=A\b` in MATLAB. + + author: Tim Davis + + algorithm design collaboration: Iain Duff + +Refer to each package for license, copyright, and author information. All +codes are authored or co-authored by Timothy A. Davis (email: davis@tamu.edu), +except for METIS (by George Karypis), `GraphBLAS/cpu_features` (by Google), +GraphBLAS/lz4, zstd, and xxHash (by Yann Collet, now at Facebook), and +GraphBLAS/CUDA/jitify.hpp (by NVIDIA). Parts of GraphBLAS/CUDA are +Copyright (c) by NVIDIA. Please refer to each of these licenses. + +----------------------------------------------------------------------------- +For distro maintainers (Linux, homebrew, spack, R, Octave, Trilinos, ...): +----------------------------------------------------------------------------- + +Thanks for packaging SuiteSparse! Here are some suggestions: + +* GraphBLAS takes a long time to compile because it creates many fast + "FactoryKernels" at compile-time. If you want to reduce the compile time and + library size, enable the `GRAPHBLAS_COMPACT` mode, but keep the JIT compiler + enabled. Then GraphBLAS will compile the kernels it needs at run-time, via + its JIT compiler. Performance will be the same as the FactoryKernels once + the JIT kernels are compiled. User compiled kernels are placed in + `~/.SuiteSparse`, by default. You do not need to distribute the source for + GraphBLAS to enable the JIT compiler: just `libgraphblas.so` and + `GraphBLAS.h` is enough. + +* GraphBLAS needs OpenMP! It's fundamentally a parallel code so please + distribute it with OpenMP enabled. Performance will suffer otherwise. + +* CUDA acceleration: CHOLMOD and SPQR can benefit from their CUDA kernels. If + you do not have CUDA or do not want to include it in your distro, this + version of SuiteSparse skips the building of the `CHOLMOD_CUDA` and `SPQR_CUDA` + libraries, and does not link against the `GPUQREngine` and + `SuiteSparse_GPURuntime` libraries. ----------------------------------------------------------------------------- How to cite the SuiteSparse meta-package and its component packages: @@ -40,193 +513,180 @@ How to cite the SuiteSparse meta-package and its component packages: SuiteSparse is a meta-package of many packages, each with their own published papers. To cite the whole collection, use the URLs: - * https://github.com/DrTimothyAldenDavis/SuiteSparse - * http://suitesparse.com (which is a forwarding URL +* https://github.com/DrTimothyAldenDavis/SuiteSparse +* http://suitesparse.com (which is a forwarding URL to https://people.engr.tamu.edu/davis/suitesparse.html) Please also cite the specific papers for the packages you use. This is a long list; if you want a shorter list, just cite the most recent "Algorithm XXX:" papers in ACM TOMS, for each package. - * For the MATLAB x=A\b, see below for AMD, COLAMD, CHOLMOD, UMFPACK, - and SuiteSparseQR (SPQR). +* For the MATLAB x=A\b, see below for AMD, COLAMD, CHOLMOD, UMFPACK, + and SuiteSparseQR (SPQR). - * for GraphBLAS, and `C=A*B` in MATLAB (sparse-times-sparse): +* for GraphBLAS, and C=AB in MATLAB (sparse-times-sparse): - T. Davis, Algorithm 10xx: SuiteSparse:GraphBLAS: parallel graph - algorithms in the language of sparse linear algebra, ACM Trans on - Mathematical Software, to appear, 2023. See the pdf in - https://github.com/DrTimothyAldenDavis/GraphBLAS/tree/stable/Doc + T. A. Davis. Algorithm 1037: SuiteSparse:GraphBLAS: Parallel Graph Algorithms + in the Language of Sparse Linear Algebra. ACM Trans. Math. Softw. 49, 3, + Article 28 (September 2023), 30 pages. https://doi.org/10.1145/3577195 - T. Davis, Algorithm 1000: SuiteSparse:GraphBLAS: graph algorithms in - the language of sparse linear algebra, ACM Trans on Mathematical - Software, vol 45, no 4, Dec. 2019, Article No 44. - https://doi.org/10.1145/3322125. + T. Davis, Algorithm 1000: SuiteSparse:GraphBLAS: graph algorithms in the + language of sparse linear algebra, ACM Trans on Mathematical Software, vol + 45, no 4, Dec. 2019, Article No 44. https://doi.org/10.1145/3322125. - * for CSparse/CXSParse: +* for LAGraph: - T. A. Davis, Direct Methods for Sparse Linear Systems, SIAM Series on - the Fundamentals of Algorithms, SIAM, Philadelphia, PA, 2006. - https://doi.org/10.1137/1.9780898718881 + G. Szárnyas et al., "LAGraph: Linear Algebra, Network Analysis Libraries, and + the Study of Graph Algorithms," 2021 IEEE International Parallel and + Distributed Processing Symposium Workshops (IPDPSW), Portland, OR, USA, 2021, + pp. 243-252. https://doi.org/10.1109/IPDPSW52791.2021.00046. - * for SuiteSparseQR (SPQR): (also cite AMD, COLAMD): +* for CSparse/CXSParse: - T. A. Davis, Algorithm 915: SuiteSparseQR: Multifrontal multithreaded - rank-revealing sparse QR factorization, ACM Trans. on Mathematical - Software, 38(1), 2011, pp. 8:1--8:22. - https://doi.org/10.1145/2049662.2049670 + T. A. Davis, Direct Methods for Sparse Linear Systems, SIAM Series on the + Fundamentals of Algorithms, SIAM, Philadelphia, PA, 2006. + https://doi.org/10.1137/1.9780898718881 - * for SuiteSparseQR/GPU: +* for SuiteSparseQR (SPQR): (also cite AMD, COLAMD): - Sencer Nuri Yeralan, T. A. Davis, Wissam M. Sid-Lakhdar, and Sanjay - Ranka. 2017. Algorithm 980: Sparse QR Factorization on the GPU. ACM - Trans. Math. Softw. 44, 2, Article 17 (June 2018), 29 pages. - https://doi.org/10.1145/3065870 + T. A. Davis, Algorithm 915: SuiteSparseQR: Multifrontal multithreaded + rank-revealing sparse QR factorization, ACM Trans. on Mathematical Software, + 38(1), 2011, pp. 8:1--8:22. https://doi.org/10.1145/2049662.2049670 - * for CHOLMOD: (also cite AMD, COLAMD): +* for SuiteSparseQR/GPU: - Y. Chen, T. A. Davis, W. W. Hager, and S. Rajamanickam, Algorithm 887: - CHOLMOD, supernodal sparse Cholesky factorization and update/downdate, - ACM Trans. on Mathematical Software, 35(3), 2008, pp. 22:1--22:14. - https://dl.acm.org/doi/abs/10.1145/1391989.1391995 + Sencer Nuri Yeralan, T. A. Davis, Wissam M. Sid-Lakhdar, and Sanjay Ranka. + 2017. Algorithm 980: Sparse QR Factorization on the GPU. ACM Trans. Math. + Softw. 44, 2, Article 17 (June 2018), 29 pages. + https://doi.org/10.1145/3065870 - T. A. Davis and W. W. Hager, Dynamic supernodes in sparse Cholesky - update/downdate and triangular solves, ACM Trans. on Mathematical - Software, 35(4), 2009, pp. 27:1--27:23. - https://doi.org/10.1145/1462173.1462176 +* for CHOLMOD: (also cite AMD, COLAMD): - * for CHOLMOD/Modify Module: (also cite AMD, COLAMD): + Y. Chen, T. A. Davis, W. W. Hager, and S. Rajamanickam, Algorithm 887: + CHOLMOD, supernodal sparse Cholesky factorization and update/downdate, ACM + Trans. on Mathematical Software, 35(3), 2008, pp. 22:1--22:14. + https://dl.acm.org/doi/abs/10.1145/1391989.1391995 - T. A. Davis and William W. Hager, Row Modifications of a Sparse - Cholesky Factorization SIAM Journal on Matrix Analysis and Applications - 2005 26:3, 621-639 - https://doi.org/10.1137/S089547980343641X + T. A. Davis and W. W. Hager, Dynamic supernodes in sparse Cholesky + update/downdate and triangular solves, ACM Trans. on Mathematical Software, + 35(4), 2009, pp. 27:1--27:23. https://doi.org/10.1145/1462173.1462176 - T. A. Davis and William W. Hager, Multiple-Rank Modifications of a - Sparse Cholesky Factorization SIAM Journal on Matrix Analysis and - Applications 2001 22:4, 997-1013 - https://doi.org/10.1137/S0895479899357346 +* for CHOLMOD/Modify Module: (also cite AMD, COLAMD): - T. A. Davis and William W. Hager, Modifying a Sparse Cholesky - Factorization, SIAM Journal on Matrix Analysis and Applications 1999 - 20:3, 606-627 - https://doi.org/10.1137/S0895479897321076 + T. A. Davis and William W. Hager, Row Modifications of a Sparse Cholesky + Factorization SIAM Journal on Matrix Analysis and Applications 2005 26:3, + 621-639. https://doi.org/10.1137/S089547980343641X - * for CHOLMOD/GPU Modules: + T. A. Davis and William W. Hager, Multiple-Rank Modifications of a Sparse + Cholesky Factorization SIAM Journal on Matrix Analysis and Applications 2001 + 22:4, 997-1013. https://doi.org/10.1137/S0895479899357346 - Steven C. Rennich, Darko Stosic, Timothy A. Davis, Accelerating sparse - Cholesky factorization on GPUs, Parallel Computing, Vol 59, 2016, pp - 140-150. - https://doi.org/10.1016/j.parco.2016.06.004 + T. A. Davis and William W. Hager, Modifying a Sparse Cholesky Factorization, + SIAM Journal on Matrix Analysis and Applications 1999 20:3, 606-627. + https://doi.org/10.1137/S0895479897321076 - * for AMD and CAMD: +* for CHOLMOD/GPU Modules: - P. Amestoy, T. A. Davis, and I. S. Duff, Algorithm 837: An approximate - minimum degree ordering algorithm, ACM Trans. on Mathematical Software, - 30(3), 2004, pp. 381--388. - https://dl.acm.org/doi/abs/10.1145/1024074.1024081 + Steven C. Rennich, Darko Stosic, Timothy A. Davis, Accelerating sparse + Cholesky factorization on GPUs, Parallel Computing, Vol 59, 2016, pp 140-150. + https://doi.org/10.1016/j.parco.2016.06.004 - P. Amestoy, T. A. Davis, and I. S. Duff, An approximate minimum degree - ordering algorithm, SIAM J. Matrix Analysis and Applications, 17(4), - 1996, pp. 886--905. - https://doi.org/10.1137/S0895479894278952 +* for AMD and CAMD: - * for COLAMD, SYMAMD, CCOLAMD, and CSYMAMD: + P. Amestoy, T. A. Davis, and I. S. Duff, Algorithm 837: An approximate + minimum degree ordering algorithm, ACM Trans. on Mathematical Software, + 30(3), 2004, pp. 381--388. + https://dl.acm.org/doi/abs/10.1145/1024074.1024081 - T. A. Davis, J. R. Gilbert, S. Larimore, E. Ng, Algorithm 836: COLAMD, - an approximate column minimum degree ordering algorithm, ACM Trans. on - Mathematical Software, 30(3), 2004, pp. 377--380. - https://doi.org/10.1145/1024074.1024080 + P. Amestoy, T. A. Davis, and I. S. Duff, An approximate minimum degree + ordering algorithm, SIAM J. Matrix Analysis and Applications, 17(4), 1996, + pp. 886--905. https://doi.org/10.1137/S0895479894278952 - T. A. Davis, J. R. Gilbert, S. Larimore, E. Ng, A column approximate - minimum degree ordering algorithm, ACM Trans. on Mathematical Software, - 30(3), 2004, pp. 353--376. - https://doi.org/10.1145/1024074.1024079 +* for COLAMD, SYMAMD, CCOLAMD, and CSYMAMD: - * for UMFPACK: (also cite AMD and COLAMD): + T. A. Davis, J. R. Gilbert, S. Larimore, E. Ng, Algorithm 836: COLAMD, an + approximate column minimum degree ordering algorithm, ACM Trans. on + Mathematical Software, 30(3), 2004, pp. 377--380. + https://doi.org/10.1145/1024074.1024080 - T. A. Davis, Algorithm 832: UMFPACK - an unsymmetric-pattern - multifrontal method with a column pre-ordering strategy, ACM Trans. on - Mathematical Software, 30(2), 2004, pp. 196--199. - https://dl.acm.org/doi/abs/10.1145/992200.992206 + T. A. Davis, J. R. Gilbert, S. Larimore, E. Ng, A column approximate minimum + degree ordering algorithm, ACM Trans. on Mathematical Software, 30(3), 2004, + pp. 353--376. https://doi.org/10.1145/1024074.1024079 - T. A. Davis, A column pre-ordering strategy for the unsymmetric-pattern - multifrontal method, ACM Trans. on Mathematical Software, 30(2), 2004, - pp. 165--195. - https://dl.acm.org/doi/abs/10.1145/992200.992205 +* for UMFPACK: (also cite AMD and COLAMD): - T. A. Davis and I. S. Duff, A combined unifrontal/multifrontal method - for unsymmetric sparse matrices, ACM Trans. on Mathematical Software, - 25(1), 1999, pp. 1--19. - https://doi.org/10.1145/305658.287640 + T. A. Davis, Algorithm 832: UMFPACK - an unsymmetric-pattern multifrontal + method with a column pre-ordering strategy, ACM Trans. on Mathematical + Software, 30(2), 2004, pp. 196--199. + https://dl.acm.org/doi/abs/10.1145/992200.992206 - T. A. Davis and I. S. Duff, An unsymmetric-pattern multifrontal method - for sparse LU factorization, SIAM J. Matrix Analysis and Computations, - 18(1), 1997, pp. 140--158. - https://doi.org/10.1137/S0895479894246905 + T. A. Davis, A column pre-ordering strategy for the unsymmetric-pattern + multifrontal method, ACM Trans. on Mathematical Software, 30(2), 2004, pp. + 165--195. https://dl.acm.org/doi/abs/10.1145/992200.992205 - * for the FACTORIZE m-file: + T. A. Davis and I. S. Duff, A combined unifrontal/multifrontal method for + unsymmetric sparse matrices, ACM Trans. on Mathematical Software, 25(1), + 1999, pp. 1--19. https://doi.org/10.1145/305658.287640 - T. A. Davis, Algorithm 930: FACTORIZE, an object-oriented linear system - solver for MATLAB, ACM Trans. on Mathematical Software, 39(4), 2013, - pp. 28:1-28:18. - https://doi.org/10.1145/2491491.2491498 + T. A. Davis and I. S. Duff, An unsymmetric-pattern multifrontal method for + sparse LU factorization, SIAM J. Matrix Analysis and Computations, 18(1), + 1997, pp. 140--158. https://doi.org/10.1137/S0895479894246905 - * for KLU and BTF (also cite AMD and COLAMD): +* for the FACTORIZE m-file: - T. A. Davis and Ekanathan Palamadai Natarajan. 2010. Algorithm 907: - KLU, A Direct Sparse Solver for Circuit Simulation Problems. ACM Trans. - Math. Softw. 37, 3, Article 36 (September 2010), 17 pages. - https://dl.acm.org/doi/abs/10.1145/1824801.1824814 + T. A. Davis, Algorithm 930: FACTORIZE, an object-oriented linear system + solver for MATLAB, ACM Trans. on Mathematical Software, 39(4), 2013, pp. + 28:1-28:18. https://doi.org/10.1145/2491491.2491498 - * for LDL: +* for KLU and BTF (also cite AMD and COLAMD): - T. A. Davis. Algorithm 849: A concise sparse Cholesky factorization - package. ACM Trans. Math. Softw. 31, 4 (December 2005), 587–591. - https://doi.org/10.1145/1114268.1114277 + T. A. Davis and Ekanathan Palamadai Natarajan. 2010. Algorithm 907: KLU, A + Direct Sparse Solver for Circuit Simulation Problems. ACM Trans. Math. + Softw. 37, 3, Article 36 (September 2010), 17 pages. + https://dl.acm.org/doi/abs/10.1145/1824801.1824814 - * for ssget and the SuiteSparse Matrix Collection: +* for LDL: - T. A. Davis and Yifan Hu. 2011. The University of Florida sparse - matrix collection. ACM Trans. Math. Softw. 38, 1, Article 1 (November - 2011), 25 pages. - https://doi.org/10.1145/2049662.2049663 + T. A. Davis. Algorithm 849: A concise sparse Cholesky factorization package. + ACM Trans. Math. Softw. 31, 4 (December 2005), 587–591. + https://doi.org/10.1145/1114268.1114277 - Kolodziej et al., (2019). The SuiteSparse Matrix Collection Website - Interface. Journal of Open Source Software, 4(35), 1244, - https://doi.org/10.21105/joss.01244 +* for ssget and the SuiteSparse Matrix Collection: - * for `spqr_rank`: + T. A. Davis and Yifan Hu. 2011. The University of Florida sparse matrix + collection. ACM Trans. Math. Softw. 38, 1, Article 1 (November 2011), 25 + pages. https://doi.org/10.1145/2049662.2049663 - Leslie V. Foster and T. A. Davis. 2013. Algorithm 933: Reliable - calculation of numerical rank, null space bases, pseudoinverse - solutions, and basic solutions using suitesparseQR. ACM Trans. Math. - Softw. 40, 1, Article 7 (September 2013), 23 pages. - https://doi.org/10.1145/2513109.2513116 + Kolodziej et al., (2019). The SuiteSparse Matrix Collection Website + Interface. Journal of Open Source Software, 4(35), 1244. + https://doi.org/10.21105/joss.01244 - * for Mongoose: +* for `spqr_rank`: - T. A. Davis, William W. Hager, Scott P. Kolodziej, and S. Nuri Yeralan. - 2020. Algorithm 1003: Mongoose, a Graph Coarsening and Partitioning - Library. ACM Trans. Math. Softw. 46, 1, Article 7 (March 2020), 18 - pages. - https://doi.org/10.1145/3337792 + Leslie V. Foster and T. A. Davis. 2013. Algorithm 933: Reliable calculation + of numerical rank, null space bases, pseudoinverse solutions, and basic + solutions using suitesparseQR. ACM Trans. Math. Softw. 40, 1, Article 7 + (September 2013), 23 pages. https://doi.org/10.1145/2513109.2513116 - * for SPEX: +* for Mongoose: - Christopher Lourenco, Jinhao Chen, Erick Moreno-Centeno, and T. A. - Davis. 2022. Algorithm 1021: SPEX Left LU, Exactly Solving Sparse - Linear Systems via a Sparse Left-Looking Integer-Preserving LU - Factorization. ACM Trans. Math. Softw. June 2022. - https://doi.org/10.1145/3519024 + T. A. Davis, William W. Hager, Scott P. Kolodziej, and S. Nuri Yeralan. + 2020. Algorithm 1003: Mongoose, a Graph Coarsening and Partitioning Library. + ACM Trans. Math. Softw. 46, 1, Article 7 (March 2020), 18 pages. + https://doi.org/10.1145/3337792 + +* for SPEX: + + Christopher Lourenco, Jinhao Chen, Erick Moreno-Centeno, and T. A. Davis. + 2022. Algorithm 1021: SPEX Left LU, Exactly Solving Sparse Linear Systems via + a Sparse Left-Looking Integer-Preserving LU Factorization. ACM Trans. Math. + Softw. June 2022. https://doi.org/10.1145/3519024 ----------------------------------------------------------------------------- About the BLAS and LAPACK libraries ----------------------------------------------------------------------------- -NOTE: Use of the Intel MKL BLAS is strongly recommended. In a 2019 test, -OpenBLAS caused result in severe performance degradation. The reason for this -is being investigated, and this may be resolved in the near future. +NOTE: if you use OpenBLAS, be sure to use version 0.3.27 or later. To select your BLAS/LAPACK, see the instructions in SuiteSparseBLAS.cmake in `SuiteSparse_config/cmake_modules`. If `SuiteSparse_config` finds a BLAS with @@ -234,15 +694,17 @@ To select your BLAS/LAPACK, see the instructions in SuiteSparseBLAS.cmake in `SuiteSparse_config.h` with the `SUITESPARSE_BLAS_INT` defined as `int64_t`. Otherwise, if a 32-bit BLAS is found, this type is defined as `int32_t`. If later on, UMFPACK, CHOLMOD, or SPQR are compiled and linked with a BLAS that -has a different integer size, you must override the definition with -DBLAS64 -(to assert the use of 64-bit integers in the BLAS) or -DBLAS32, (to assert the -use of 32-bit integers in the BLAS). +has a different integer size, you must override the definition with `-DBLAS64` +(to assert the use of 64-bit integers in the BLAS) or `-DBLAS32`, (to assert +the use of 32-bit integers in the BLAS). + +The size of the BLAS integer has nothing to do with `sizeof(void *)`. When distributed in a binary form (such as a Debian, Ubuntu, Spack, or Brew package), SuiteSparse should probably be compiled to expect a 32-bit BLAS, since this is the most common case. The default is to use a 32-bit BLAS, but -this can be changed in SuiteSparseBLAS.cmake or by compiling with -`-DALLOW_64BIT_BLAS=1`. +this can be changed by setting the cmake variable +`SUITESPARSE_USE_64BIT_BLAS` to `ON`. By default, SuiteSparse hunts for a suitable BLAS library. To enforce a particular BLAS library use either: @@ -251,263 +713,140 @@ particular BLAS library use either: cd Package ; cmake -DBLA_VENDOR=OpenBLAS .. make To use the default (hunt for a BLAS), do not set `BLA_VENDOR`, or set it to -ANY. In this case, if `ALLOW_64BIT_BLAS` is set, preference is given to a -64-bit BLAS, but a 32-bit BLAS library will be used if no 64-bit library is -found. +`ANY`. In this case, if `SUITESPARSE_USE_64BIT_BLAS` is ON, preference is +given to a 64-bit BLAS, but a 32-bit BLAS library will be used if no 64-bit +library is found. However, if both `SUITESPARSE_USE_64BIT_BLAS` and +`SUITESPARSE_USE_STRICT` are ON, then only a 64-bit BLAS is considered. -When selecting a particular BLAS library, the `ALLOW_64BIT_BLAS` setting is -strictly followed. If set to true, only a 64-bit BLAS library will be used. -If false (the default), only a 32-bit BLAS library will be used. If no such -BLAS is found, the build will fail. +When selecting a particular BLAS library, the `SUITESPARSE_USE_64BIT_BLAS` +setting is strictly followed. If set to true, only a 64-bit BLAS library will +be used. If false (the default), only a 32-bit BLAS library will be used. If +no such BLAS is found, the build will fail. ------------------- -SuiteSparse/README ------------------- +----------------------------------------------------------------------------- +QUICK START FOR THE C/C++ LIBRARIES: +----------------------------------------------------------------------------- -Packages in SuiteSparse, and files in this directory: +Type the following in this directory (requires system priviledge to do the +`sudo make install`): +``` + mkdir -p build && cd build + cmake .. + cmake --build . + sudo cmake --install . +``` + +All libraries will be created and installed into the default system-wide folder +(/usr/local/lib on Linux). All include files needed by the applications that +use SuiteSparse are installed into /usr/local/include/suitesparse (on Linux). + +To build only a subset of libraries, set `SUITESPARSE_ENABLE_PROJECTS` when +configuring with CMake. E.g., to build and install CHOLMOD and CXSparse +(including their dependencies), use the following commands: +``` + mkdir -p build && cd build + cmake -DSUITESPARSE_ENABLE_PROJECTS="cholmod;cxsparse" .. + cmake --build . + sudo cmake --install . +``` + +For Windows (MSVC), import the `CMakeLists.txt` file into MS Visual Studio. +Be sure to specify the build type as Release; for example, to build SuiteSparse +on Windows in the command window, run: +``` + mkdir -p build && cd build + cmake .. + cmake --build . --config Release + cmake --install . +``` - GraphBLAS graph algorithms in the language of linear algebra. - https://graphblas.org - author: Tim Davis - - SPEX solves sparse linear systems in exact arithmetic. - Requires the GNU GMP and MPRF libraries. - This will be soon replaced by a more general package, SPEX v3 - that includes this method (exact sparse LU) and others (sparse - exact Cholesky, and sparse exact update/downdate). The API - of v3 will be changing significantly. - - AMD approximate minimum degree ordering. This is the built-in AMD - function in MATLAB. - authors: Tim Davis, Patrick Amestoy, Iain Duff - - bin where programs are placed when compiled - - BTF permutation to block triangular form - authors: Tim Davis, Ekanathan Palamadai - - CAMD constrained approximate minimum degree ordering - authors: Tim Davis, Patrick Amestoy, Iain Duff, Yanqing Chen - - CCOLAMD constrained column approximate minimum degree ordering - authors: Tim Davis, Sivasankaran Rajamanickam, Stefan Larimore. - Algorithm design collaborators: Esmond Ng, John Gilbert - (for COLAMD) - - ChangeLog a summary of changes to SuiteSparse. See */Doc/ChangeLog - for details for each package. - - CHOLMOD sparse Cholesky factorization. Requires AMD, COLAMD, CCOLAMD, - the BLAS, and LAPACK. Optionally uses METIS. This is chol and - x=A\b in MATLAB. - author for all modules: Tim Davis - CHOLMOD/Modify module authors: Tim Davis and William W. Hager - - COLAMD column approximate minimum degree ordering. This is the - built-in COLAMD function in MATLAB. - authors (of the code): Tim Davis and Stefan Larimore - Algorithm design collaborators: Esmond Ng, John Gilbert - - Contents.m a list of contents for 'help SuiteSparse' in MATLAB. - - CSparse a concise sparse matrix package, developed for my - book, "Direct Methods for Sparse Linear Systems", - published by SIAM. Intended primarily for teaching. - Note that the code is (c) Tim Davis, as stated in the book. - For production, use CXSparse instead. In particular, both - CSparse and CXSparse have the same include filename: cs.h. - This package is used for the built-in DMPERM in MATLAB. - author: Tim Davis - - CXSparse CSparse Extended. Includes support for complex matrices - and both int or long integers. Use this instead of CSparse - for production use; it creates a libcsparse.so (or *dylib on - the Mac) with the same name as CSparse. It is a superset - of CSparse. Any code that links against CSparse should - also be able to link against CXSparse instead. - author: Tim Davis, David Bateman - - include 'make install' places user-visible include files for each - package here, after 'make local' - - KLU sparse LU factorization, primarily for circuit simulation. - Requires AMD, COLAMD, and BTF. Optionally uses CHOLMOD, - CAMD, CCOLAMD, and METIS. - authors: Tim Davis, Ekanathan Palamadai - - LDL a very concise LDL' factorization package - author: Tim Davis - - lib 'make install' places shared libraries for each package - here, after 'make local' - - Makefile to compile all of SuiteSparse - - make compiles SuiteSparse libraries. - Subsequent "make install" will install - in just CMAKE_INSTALL_PATH (defaults to - /usr/local/lib on Linux or Mac). - - make both compiles SuiteSparse, and then "make install" - will instal in both ./lib and - CMAKE_INSTALL_PATH). - - make local compiles SuiteSparse. - Subsequent "make install will install only - in ./lib, ./include only. - Does not install in CMAKE_INSTALL_PATH. - - make global compiles SuiteSparse libraries. - Subsequent "make install" will install in - just /usr/local/lib (or whatever your - CMAKE_INSTALL_PREFIX is). - Does not install in ./lib and ./include. - - make install installs in the current directory - (./lib, ./include), and/or in - /usr/local/lib and /usr/local/include, - depending on whether "make", "make local", - "make global", or "make both", - etc has been done. - - make uninstall undoes 'make install' - - make distclean removes all files not in distribution, including - ./bin, ./share, ./lib, and ./include. - - make purge same as 'make distclean' - - make clean removes all files not in distribution, but - keeps compiled libraries and demoes, ./lib, - ./share, and ./include. - - Each individual package also has each of the above 'make' - targets. - - Things you don't need to do: - make docs creates user guides from LaTeX files - make cov runs statement coverage tests (Linux only) - - MATLAB_Tools various m-files for use in MATLAB - author: Tim Davis (all parts) - for spqr_rank: author Les Foster and Tim Davis - - Contents.m list of contents - dimacs10 loads matrices for DIMACS10 collection - Factorize object-oriented x=A\b for MATLAB - find_components finds connected components in an image - GEE simple Gaussian elimination - getversion.m determine MATLAB version - gipper.m create MATLAB archive - hprintf.m print hyperlinks in command window - LINFACTOR predecessor to Factorize package - MESHND nested dissection ordering of regular meshes - pagerankdemo.m illustrates how PageRank works - SFMULT C=S*F where S is sparse and F is full - shellgui display a seashell - sparseinv sparse inverse subset - spok check if a sparse matrix is valid - spqr_rank SPQR_RANK package. MATLAB toolbox for rank - deficient sparse matrices: null spaces, - reliable factorizations, etc. With Leslie - Foster, San Jose State Univ. - SSMULT C=A*B where A and B are both sparse - SuiteSparseCollection for the SuiteSparse Matrix Collection - waitmex waitbar for use inside a mexFunction - - The SSMULT and SFMULT functions are the basis for the - built-in C=A*B functions in MATLAB. - - Mongoose graph partitioning. - authors: Nuri Yeralan, Scott Kolodziej, William Hager, Tim Davis - - CHOLMOD/SuiteSparse_metis: a modified version of METIS, embedded into - the CHOLMOD library. See the README.txt files - for details. author: George Karypis. This is a slightly - modified copy included with SuiteSparse via the open-source - license provided by George Karypis. SuiteSparse cannot use - an unmodified copy METIS. - - RBio read/write sparse matrices in Rutherford/Boeing format - author: Tim Davis - - README.txt this file - - SPQR sparse QR factorization. This the built-in qr and x=A\b in - MATLAB. Also called SuiteSparseQR. - author of the CPU code: Tim Davis - author of GPU modules: Tim Davis, Nuri Yeralan, - Wissam Sid-Lakhdar, Sanjay Ranka - - GPUQREngine: GPU support package for SPQR - (not built into MATLAB, however) - authors: Tim Davis, Nuri Yeralan, Sanjay Ranka, - Wissam Sid-Lakhdar - - SuiteSparse_config configuration file for all the above packages. - CSparse and MATLAB_Tools do not use SuiteSparse_config. - author: Tim Davis - - SuiteSparse_GPURuntime GPU support package for SPQR and CHOLMOD - (not builtin to MATLAB, however). - - SuiteSparse_install.m install SuiteSparse for MATLAB - SuiteSparse_paths.m set paths for SuiteSparse MATLAB mexFunctions - - SuiteSparse_test.m exhaustive test for SuiteSparse in MATLAB - - ssget MATLAB interface to the SuiteSparse Matrix Collection - author: Tim Davis - - UMFPACK sparse LU factorization. Requires AMD and the BLAS. - This is the built-in lu and x=A\b in MATLAB. - author: Tim Davis - algorithm design collaboration: Iain Duff - -Some codes optionally use METIS 5.1.0. This package is located in SuiteSparse -in the `CHOLMOD/SuiteSparse_metis` directory. Its use is optional. To compile -CHOLMOD without it, use the CMAKE_OPTIONS="-DNPARTITION=1" setting. The use of -METIS can improve ordering quality for some matrices, particularly large 3D -discretizations. METIS has been slightly modified for use in SuiteSparse; see -the `CHOLMOD/SuiteSparse_metis/README.txt` file for details. +Be sure to first install all required libraries: BLAS and LAPACK for UMFPACK, +CHOLMOD, and SPQR, and GMP and MPFR for SPEX. Be sure to use the latest +libraries; SPEX requires MPFR 4.0.2 and GMP 6.1.2 (these version numbers +do NOT correspond to the X.Y.Z suffix of libgmp.so.X.Y.Z and libmpfr.so.X.Y.Z; +see the SPEX user guide for details). -Refer to each package for license, copyright, and author information. All -codes are authored or co-authored by Timothy A. Davis (email: davis@tamu.edu), -except for METIS (by George Karypis), GraphBLAS/cpu_features (by Google), -GraphBLAS/lz4 and zstd (by Yann Collet, now at Facebook), and -GraphBLAS/CUDA/jitify.hpp (by NVIDIA). Parts of GraphBLAS/CUDA are -Copyright (c) by NVIDIA. Please refer to each of these licenses. +To compile the libraries and install them only in SuiteSparse/lib (not +/usr/local/lib), do this instead in the top-level of SuiteSparse: +``` + mkdir -p build && cd build + cmake -DCMAKE_INSTALL_PREFIX=.. .. + cmake --build . + cmake --install . +``` + +If you add /home/me/SuiteSparse/lib to your library search path +(`LD_LIBRARY_PATH` in Linux), you can do the following (for example): +``` + S = /home/me/SuiteSparse + cc myprogram.c -I$(S)/include/suitesparse -lumfpack -lamd -lcholmod -lsuitesparseconfig -lm +``` + +To change the C and C++ compilers, and to compile in parallel use: +``` + cmake -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER==g++ .. +``` + +for example, which changes the compiler to gcc and g++. + +This will work on Linux/Unix and the Mac. It should automatically detect if +you have the Intel compilers or not, and whether or not you have CUDA. + +See `SuiteSparse_config/cmake_modules/SuiteSparsePolicy.cmake` to select your BLAS. + +You may also need to add SuiteSparse/lib to your path. If your copy of +SuiteSparse is in /home/me/SuiteSparse, for example, then add this to your +`~/.bashrc` file: + +``` +LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/me/SuiteSparse/lib +export LD_LIBRARY_PATH +``` -Licenses for each package are located in the following files, all in -PACKAGENAME/Doc/License.txt, and these files are also concatenated into -the top-level LICENSE.txt file. +For the Mac, use this instead: +``` +DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:/home/me/SuiteSparse/lib +export DYLD_LIBRARY_PATH +``` + +Default install location of files is below, where PACKAGE is one of the +packages in SuiteSparse: + + * `CMAKE_INSTALL_PREFIX/include/suitesparse/`: include files + * `CMAKE_INSTALL_PREFIX/lib/`: compiled libraries + * `CMAKE_INSTALL_PREFIX/lib/cmake/SuiteSparse/`: `*.cmake` scripts + for all of SuiteSparse + * `CMAKE_INSTALL_PREFIX/lib/cmake/PACKAGE/`: `*Config.cmake` scripts for a + specific package + * `CMAKE_INSTALL_PREFIX/lib/pkgconfig/PACKAGE.pc`: `.pc` scripts for + a specific package pkgconfig ----------------------------------------------------------------------------- QUICK START FOR MATLAB USERS (Linux or Mac): ----------------------------------------------------------------------------- -Uncompress the SuiteSparse.zip or SuiteSparse.tar.gz archive file (they contain -the same thing). Suppose you place SuiteSparse in the /home/me/SuiteSparse -folder. - -Add the SuiteSparse/lib folder to your run-time library path. On Linux, add -this to your ~/.bashrc script, assuming /home/me/SuiteSparse is the location of -your copy of SuiteSparse: +Suppose you place SuiteSparse in the `/home/me/SuiteSparse` folder. +Add the `SuiteSparse/lib` folder to your run-time library path. On Linux, add +this to your `~/.bashrc` script, assuming `/home/me/SuiteSparse` is the +location of your copy of SuiteSparse: +``` LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/me/SuiteSparse/lib export LD_LIBRARY_PATH +``` -For the Mac, use this instead, in your ~/.zshrc script, assuming you place -SuiteSparse in /Users/me/SuiteSparse: - +For the Mac, use this instead, in your `~/.zshrc` script, assuming you place +SuiteSparse in `/Users/me/SuiteSparse`: +``` DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:/Users/me/SuiteSparse/lib export DYLD_LIBRARY_PATH +``` -Compile all of SuiteSparse with "make local". +Compile all of SuiteSparse with `make local`. Next, compile the GraphBLAS MATLAB library. In the system shell while in the -SuiteSparse folder, type "make gbmatlab" if you want to install it system-wide -with "make install", or "make gblocal" if you want to use the library in +SuiteSparse folder, type `make gbmatlab` if you want to install it system-wide +with `make install`, or `make gblocal` if you want to use the library in your own SuiteSparse/lib. Then in the MATLAB Command Window, cd to the SuiteSparse directory and type @@ -521,162 +860,360 @@ Documents/MATLAB/startup.m. You can also use the `SuiteSparse_paths` m-file to set all your paths at the start of each MATLAB session. ----------------------------------------------------------------------------- -QUICK START FOR THE C/C++ LIBRARIES: +Compilation options ----------------------------------------------------------------------------- -For Linux and Mac: type the following in this directory (requires system -priviledge to do the `sudo make install`): +You can set specific options for CMake with the command (for example): +``` + cmake -DCHOLMOD_PARTITION=OFF -DBUILD_STATIC_LIBS=OFF -DCMAKE_BUILD_TYPE=Debug .. +``` - make - sudo make install +That command will compile all of SuiteSparse except for CHOLMOD/Partition +Module (because of `-DCHOLMOD_PARTITION=OFF`). Debug mode will be used (the +build type). The static libraries will not be built (since +`-DBUILD_STATIC_LIBS=OFF` is set). -All libraries will be created and copied into SuiteSparse/lib and into -/usr/local/lib. All include files need by the applications that use -SuiteSparse are copied into SuiteSparse/include and into /usr/local/include. +* `SUITESPARSE_ENABLE_PROJECTS`: -For Windows, import each `*/CMakeLists.txt` file into MS Visual Studio. + Semicolon separated list of projects to be built or `all`. + Default: `all` in which case the following projects are built: -Be sure to first install all required libraries: BLAS and LAPACK for UMFPACK, -CHOLMOD, and SPQR, and GMP and MPFR for SPEX. Be sure to use the latest -libraries; SPEX requires MPFR 4.0.2 and GMP 6.1.2 (these version numbers -do NOT correspond to the X.Y.Z suffix of libgmp.so.X.Y.Z and libmpfr.so.X.Y.Z; -see the SPEX user guide for details). + `suitesparse_config;mongoose;amd;btf;camd;ccolamd;colamd;cholmod;cxsparse;ldl;klu;umfpack;paru;rbio;spqr;spex;graphblas;lagraph` -To compile the libraries and install them only in SuiteSparse/lib (not -/usr/local/lib), do this instead in the top-level of SuiteSparse: + Additionally, `csparse` can be included in that list to build CSparse. - make local +* `CMAKE_BUILD_TYPE`: -If you add /home/me/SuiteSparse/lib to your library search path -(`LD_LIBRARY_PATH` in Linux), you can do the following (for example): + Default: `Release`, use `Debug` for debugging. - S = /home/me/SuiteSparse - cc myprogram.c -I$(S)/include -lumfpack -lamd -lcholmod -lsuitesparseconfig -lm +* `SUITESPARSE_USE_STRICT`: -To change the C and C++ compilers, and to compile in parallel use: + SuiteSparse has many user-definable settings of the form `SUITESPARSE_USE_*` + or `(package)_USE_*` for some particular package. In general, these settings + are not strict. For example, if `SUITESPARSE_USE_OPENMP` is `ON` then OpenMP + is preferred, but SuiteSparse can be used without OpenMP so no error is + generated if OpenMP is not found. However, if `SUITESPARSE_USE_STRICT` is + `ON` then all `*_USE_*` settings are treated strictly and an error occurs + if any are set to `ON` but the corresponding package or setting is not + available. The `*_USE_SYSTEM_*` settings are always treated as strict. + Default: `OFF`. - CC=gcc CX=g++ JOBS=32 make +* `SUITESPARSE_USE_CUDA`: -for example, which changes the compiler to gcc and g++, and runs make with -'make -j32', in parallel with 32 jobs. + If set to `ON`, CUDA is enabled for all of SuiteSparse. Default: `ON`, -This will work on Linux/Unix and the Mac. It should automatically detect if -you have the Intel compilers or not, and whether or not you have CUDA. + CUDA on Windows with MSVC appears to be working with this release, but it + should be considered as a prototype and may not be fully functional. I have + limited resources for testing CUDA on Windows. If you encounter issues, + disable CUDA and post this as an issue on GitHub. -NOTE: Use of the Intel MKL BLAS is strongly recommended. The OpenBLAS can -(rarely) result in severe performance degradation, in CHOLMOD in particular. -The reason for this is still under investigation and might already be resolved -in the current version of OpenBLAS. See -`SuiteSparse_config/cmake_modules/SuiteSparsePolicy.cmake` to select your BLAS. +* `CHOLMOD_USE_CUDA`: -You may also need to add SuiteSparse/lib to your path. If your copy of -SuiteSparse is in /home/me/SuiteSparse, for example, then add this to your -~/.bashrc file: + Default: `ON`. Both `SUITESPARSE_USE_CUDA` and `CHOLMOD_USE_CUDA` must be + enabled to use CUDA in CHOLMOD. - LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/me/SuiteSparse/lib - export LD_LIBRARY_PATH +* `SPQR_USE_CUDA`: -For the Mac, use this instead: + Default: `ON`. Both `SUITESPARSE_USE_CUDA` and `SPQR_USE_CUDA` must be + enabled to use CUDA in SPQR. - DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:/home/me/SuiteSparse/lib - export DYLD_LIBRARY_PATH +* `CMAKE_INSTALL_PREFIX`: ------------------------------------------------------------------------------ -Python interface ------------------------------------------------------------------------------ + Defines the install location (default on Linux is `/usr/local`). For example, + this command while in a folder `build` in the top level SuiteSparse folder + will set the install directory to `/stuff`, used by the subsequent + `sudo cmake --install .`: +``` + cmake -DCMAKE_INSTALL_PREFIX=/stuff .. + sudo cmake --install . +``` -See scikit-sparse and scikit-umfpack for the Python interface via SciPy: +* `SUITESPARSE_PKGFILEDIR`: -https://github.com/scikit-sparse/scikit-sparse + Directory where CMake Config and pkg-config files will be installed. By + default, CMake Config files will be installed in the subfolder `cmake` of the + directory where the (static) libraries will be installed (e.g., `lib`). The + `.pc` files for pkg-config will be installed in the subfolder `pkgconfig` of + the directory where the (static) libraries will be installed. -https://github.com/scikit-umfpack/scikit-umfpack + This option allows to install them at a location different from the (static) + libraries. This allows to install multiple configurations of the SuiteSparse + libraries at the same time (e.g., by also setting a different + `CMAKE_RELEASE_POSTFIX` and `CMAKE_INSTALL_LIBDIR` for each of them). To pick + up the respective configuration in downstream projects, set, e.g., + `CMAKE_PREFIX_PATH` (for CMake) or `PKG_CONFIG_PATH` (for build systems using + pkg-config) to the path containing the respective CMake Config files or + pkg-config files. + +* `SUITESPARSE_INCLUDEDIR_POSTFIX`: + + Postfix for installation target of header from SuiteSparse. Default: + suitesparse, so the default include directory is: + `CMAKE_INSTALL_PREFIX/include/suitesparse` + +* `BUILD_SHARED_LIBS`: + + If `ON`, shared libraries are built. + Default: `ON`. + +* `BUILD_STATIC_LIBS`: + + If `ON`, static libraries are built. + Default: `ON`, except for GraphBLAS, which takes a long time to compile so + the default for GraphBLAS is `OFF` unless `BUILD_SHARED_LIBS` is `OFF`. + +* `SUITESPARSE_CUDA_ARCHITECTURES`: + + A string, such as `"all"` or `"35;50;75;80"` that lists the CUDA + architectures to use when compiling CUDA kernels with `nvcc`. The `"all"` + option requires CMake 3.23 or later. Default: `"52;75;80"`. + +* `BLA_VENDOR`: + + A string. Leave unset, or use `"ANY"` to select any BLAS library (the + default). Or set to the name of a `BLA_VENDOR` defined by FindBLAS.cmake. + See: + https://cmake.org/cmake/help/latest/module/FindBLAS.html#blas-lapack-vendors + +* `SUITESPARSE_USE_64BIT_BLAS`: + + If `ON`, look for a 64-bit BLAS. If `OFF`: 32-bit only. Default: `OFF`. + +* `SUITESPARSE_USE_OPENMP`: + + If `ON`, OpenMP is used by default if it is available. Default: `ON`. + + GraphBLAS, LAGraph, and ParU will be vastly slower if OpenMP is not used. + CHOLMOD will be somewhat slower without OpenMP (as long as it still has a + parallel BLAS/LAPACK). Three packages (UMFPACK, CHOLMOD, and SPQR) rely + heavily on parallel BLAS/LAPACK libraries and those libraries may use OpenMP + internally. If you wish to disable OpenMP in an entire application, select a + single-threaded BLAS/LAPACK, or a parallel BLAS/LAPACK that does not use + OpenMP (such as the Apple Accelerate Framework). Using a single-threaded + BLAS/LAPACK library will cause UMFPACK, CHOLMOD, and SPQR to be vastly + slower. + + WARNING: GraphBLAS may not be thread-safe if built without OpenMP or pthreads + (see the GraphBLAS User Guide for details). + +* `SUITESPARSE_CONFIG_USE_OPENMP`: + + If `ON`, `SuiteSparse_config` uses OpenMP if it is available. + Default: `SUITESPARSE_USE_OPENMP`. + It is not essential and only used to let `SuiteSparse_time` call + `omp_get_wtime`. + +* `CHOLMOD_USE_OPENMP`: + + If `ON`, OpenMP is used in CHOLMOD if it is available. + Default: `SUITESPARSE_USE_OPENMP`. + +* `GRAPHBLAS_USE_OPENMP`: + + If `ON`, OpenMP is used in GraphBLAS if it is available. + Default: `SUITESPARSE_USE_OPENMP`. + +* `LAGRAPH_USE_OPENMP`: + + If `ON`, OpenMP is used in LAGraph if it is available. + Default: `SUITESPARSE_USE_OPENMP`. + +* `PARU_USE_OPENMP`: + + If `ON`, OpenMP is used in ParU if it is available. + Default: `SUITESPARSE_USE_OPENMP`. + +* `SUITESPARSE_DEMOS`: + + If `ON`, build the demo programs for each package. Default: `OFF`. + +* `SUITESPARSE_USE_SYSTEM_BTF`: + + If `ON`, use BTF libraries installed on the build system. If `OFF`, + automatically build BTF as dependency if needed. Default: `OFF`. + +* `SUITESPARSE_USE_SYSTEM_CHOLMOD`: + + If `ON`, use CHOLMOD libraries installed on the build system. If `OFF`, + automatically build CHOLMOD as dependency if needed. Default: `OFF`. + +* `SUITESPARSE_USE_SYSTEM_AMD`: + + If `ON`, use AMD libraries installed on the build system. If `OFF`, + automatically build AMD as dependency if needed. Default: `OFF`. + +* `SUITESPARSE_USE_SYSTEM_COLAMD`: + + If `ON`, use COLAMD libraries installed on the build system. If `OFF`, + automatically build COLAMD as dependency if needed. Default: `OFF`. + +* `SUITESPARSE_USE_SYSTEM_CAMD`: + + If `ON`, use CAMD libraries installed on the build system. If `OFF`, + automatically build CAMD as dependency if needed. Default: `OFF`. + +* `SUITESPARSE_USE_SYSTEM_CCOLAMD`: + + If `ON`, use CCOLAMD libraries installed on the build system. If `OFF`, + automatically build CCOLAMD as dependency if needed. Default: `OFF`. + +* `SUITESPARSE_USE_SYSTEM_GRAPHBLAS`: + + If `ON`, use GraphBLAS libraries installed on the build system. If `OFF`, + automatically build GraphBLAS as dependency if needed. Default: `OFF`. + +* `SUITESPARSE_USE_SYSTEM_SUITESPARSE_CONFIG`: + + If `ON`, use `SuiteSparse_config` libraries installed on the build system. If + `OFF`, automatically build `SuiteSparse_config` as dependency if needed. + Default: `OFF`. + +* `SUITESPARSE_USE_FORTRAN` + + If `ON`, use the Fortran compiler to determine how C calls Fortan, and to + build several optional Fortran routines. If `OFF`, use + `SUITESPARSE_C_TO_FORTRAN` to define how C calls Fortran (see + `SuiteSparse_config/cmake_modules/SuiteSparsePolicy.cmake` for details). + Default: `ON`. + +Additional options are available for specific packages: + +* `UMFPACK_USE_CHOLMOD`: + + If `ON`, UMFPACK uses CHOLMOD for additional (optional) + ordering options. Default: `ON`. + +* `KLU_USE_CHOLMOD`: + + If `ON`, KLU uses CHOLMOD for additional (optional) + ordering options. Default: `ON`. + +CHOLMOD is composed of a set of Modules that can be independently selected; +all options default to `ON`: + +* `CHOLMOD_GPL` + + If `OFF`, do not build any GPL-licensed module (MatrixOps, Modify, Supernodal, + and GPU modules) + +* `CHOLMOD_CHECK` + + If `OFF`, do not build the Check module. + +* `CHOLMOD_MATRIXOPS` + + If `OFF`, do not build the MatrixOps module. + +* `CHOLMOD_CHOLESKY` + If `OFF`, do not build the Cholesky module. This also disables the Supernodal + and Modify modules. + +* `CHOLMOD_MODIFY` + + If `OFF`, do not build the Modify module. + +* `CHOLMOD_CAMD` + + If `OFF`, do not link against CAMD and CCOLAMD. This also disables the + Partition module. + +* `CHOLMOD_PARTITION` + + If `OFF`, do not build the Partition module. + +* `CHOLMOD_SUPERNODAL` + + If `OFF`, do not build the Supernodal module. ----------------------------------------------------------------------------- -Compilation options +Possible build/install issues ----------------------------------------------------------------------------- -You can set specific options for CMake with the command (for example): +One common issue can affect all packages: getting the right #include files +that match the current libraries being built. It's possible that your Linux +distro has an older copy of SuiteSparse headers in /usr/include or +/usr/local/include, or that Homebrew has installed its suite-sparse bundle into +/opt/homebrew/include or other places. Old libraries can appear in in +/usr/local/lib, /usr/lib, etc. When building a new copy of SuiteSparse, the +cmake build system is normally (or always?) able to avoid these, and use the +right header for the right version of each library. + +As an additional guard against this possible error, each time one SuiteSparse +package #include's a header from another one, it checks the version number in +the header file, and reports an #error to the compiler if a stale version is +detected. In addition, the Example package checks both the header version and +the library version (by calling a function in each library). If the versions +mismatch in any way, the Example package reports an error at run time. + +For example, CHOLMOD 5.1.0 requires AMD 3.3.0 or later. If it detects an +older one in `amd.h`, it will report an `#error`: + +``` + #include "amd.h" + #if ( ... AMD version is stale ... ) + #error "CHOLMOD 5.1.0 requires AMD 3.3.0 or later" + #endif +``` + +and the compilation will fail. The Example package makes another check, +by calling `amd_version` and comparing it with the versions from the `amd.h` +header file. + +If this error or one like it occurs, check to see if you have an old copy of +SuiteSparse, and uninstall it before compiling your new copy of SuiteSparse. + +There are other many possible build/install issues that are covered by the +corresponding user guides for each package, such as finding the right BLAS, +OpenMP, and other libraries, and how to compile on the Mac when using GraphBLAS +inside MATLAB, and so on. Refer to the User Guides for more details. - CMAKE_OPTIONS="-DNPARTITION=1 -DNSTATIC=1 -DCMAKE_BUILD_TYPE=Debug" make +----------------------------------------------------------------------------- +Interfaces to SuiteSparse +----------------------------------------------------------------------------- -That command will compile all of SuiteSparse except for CHOLMOD/Partition -Module. Debug mode will be used. The static libraries will not be built -(NSTATIC is true). - - CMAKE_BUILD_TYPE: Default: "Release", use "Debug" for debugging. - - ENABLE_CUDA: if set to true, CUDA is enabled for the project. - Default: true for CHOLMOD and SPQR; false otherwise - - LOCAL_INSTALL: if true, "cmake --install" will install - into SuiteSparse/lib and SuiteSparse/include. - if false, "cmake --install" will install into the - default prefix (or the one configured with - CMAKE_INSTALL_PREFIX). - Default: false - - NSTATIC: if true, static libraries are not built. - Default: false, except for GraphBLAS, which - takes a long time to compile so the default for - GraphBLAS is true. For Mongoose, the NSTATIC setting - is treated as if it always false, since the mongoose - program is built with the static library. - - SUITESPARSE_CUDA_ARCHITECTURES: a string, such as "all" or - "35;50;75;80" that lists the CUDA architectures to use - when compiling CUDA kernels with nvcc. The "all" - option requires cmake 3.23 or later. - Default: "52;75;80". - - BLA_VENDOR a string. Leave unset, or use "ANY" to select any BLAS - library (the default). Or set to the name of a - BLA_VENDOR defined by FindBLAS.cmake. See: - https://cmake.org/cmake/help/latest/module/FindBLAS.html#blas-lapack-vendors - - ALLOW_64BIT_BLAS if true: look for a 64-bit BLAS. If false: 32-bit only. - Default: false. - - NOPENMP if true: OpenMP is not used. Default: false. - UMFPACK, CHOLMOD, SPQR, and GraphBLAS will be slow. - Note that BLAS and LAPACK may still use OpenMP - internally; if you wish to disable OpenMP in an entire - application, select a single-threaded BLAS/LAPACK. - WARNING: GraphBLAS may not be thread-safe if built - without OpenMP (see the User Guide for details). - - DEMO if true: build the demo programs for each package. - Default: false. - -Additional options are available within specific packages: - - NCHOLMOD if true, UMFPACK and KLU do not use CHOLMOD for - additional (optional) ordering options +MATLAB/Octave/R/Mathematica interfaces: -CHOLMOD is composed of a set of Modules that can be independently selected; -all options default to false: - - NGL if true: do not build any GPL-licensed module - (MatrixOps, Modify, Supernodal, and GPU modules) - NCHECK if true: do not build the Check module. - NMATRIXOPS if true: do not build the MatrixOps module. - NCHOLESKY if true: do not build the Cholesky module. - This also disables the Supernodal and Modify modules. - NMODIFY if true: do not build the Modify module. - NCAMD if true: do not link against CAMD and CCOLAMD. - This also disables the Partition module. - NPARTITION if true: do not build the Partition module. - NSUPERNODAL if true: do not build the Supernodal module. + Many built-in methods in MATLAB and Octave rely on SuiteSparse, including + `C=A*B` `x=A\b`, `L=chol(A)`, `[L,U,P,Q]=lu(A)`, `R=qr(A)`, `dmperm(A)`, + `p=amd(A)`, `p=colamd(A)`, ... + See also Mathematica, R, and many many more. The list is too long. + +Julia interface: + + https://github.com/JuliaSparse/SparseArrays.jl + +python interface to GraphBLAS by Anaconda and NVIDIA: + + https://pypi.org/project/python-graphblas + +Intel's Go interface to GraphBLAS: + + https://pkg.go.dev/github.com/intel/forGraphBLASGo + +See scikit-sparse and scikit-umfpack for the Python interface via SciPy: + + https://github.com/scikit-sparse/scikit-sparse + https://github.com/scikit-umfpack/scikit-umfpack + +See russell for a Rust interface: + + https://github.com/cpmech/russell ----------------------------------------------------------------------------- Acknowledgements ----------------------------------------------------------------------------- -I would like to thank François Bissey, Sebastien Villemot, Erik Welch, Jim -Kitchen, Markus Mützel, and Fabian Wein for their valuable feedback on the +Markus Mützel contributed the most recent update of the SuiteSparse build +system for all SuiteSparse packages, extensively porting it and modernizing it. + +I would also like to thank François Bissey, Sebastien Villemot, Erik Welch, Jim +Kitchen, and Fabian Wein for their valuable feedback on the SuiteSparse build system and how it works with various Linux / Python distros and other package managers. If you are a maintainer of a SuiteSparse packaging for a Linux distro, conda-forge, R, spack, brew, vcpkg, etc, please feel free to contact me if there's anything I can do to make your life easier. +I would also like to thank Raye Kimmerer for adding support for 32-bit +row/column indices in SPQR v4.2.0. See also the various Acknowledgements within each package. diff --git a/ThirdParty/SuiteSparse/SuiteSparse_config/Config/SuiteSparse_config.h.in b/ThirdParty/SuiteSparse/SuiteSparse_config/Config/SuiteSparse_config.h.in index 09d05c9222..0d203f55e6 100644 --- a/ThirdParty/SuiteSparse/SuiteSparse_config/Config/SuiteSparse_config.h.in +++ b/ThirdParty/SuiteSparse/SuiteSparse_config/Config/SuiteSparse_config.h.in @@ -368,13 +368,24 @@ int SuiteSparse_divcomplex // determine which timer to use, if any #ifndef NTIMER + // SuiteSparse_config itself can be compiled without OpenMP, + // but other packages can themselves use OpenMP. In this case, + // those packages should use omp_get_wtime() directly. This can + // be done via the SUITESPARSE_TIME macro, defined below: + #cmakedefine SUITESPARSE_HAVE_CLOCK_GETTIME #if defined ( _OPENMP ) #define SUITESPARSE_TIMER_ENABLED - #elif defined ( _POSIX_C_SOURCE ) - #if _POSIX_C_SOURCE >= 199309L + #define SUITESPARSE_TIME (omp_get_wtime ( )) + #elif defined ( SUITESPARSE_HAVE_CLOCK_GETTIME ) #define SUITESPARSE_TIMER_ENABLED - #endif + #define SUITESPARSE_TIME (SuiteSparse_time ( )) + #else + // No timer is available + #define SUITESPARSE_TIME (0) #endif +#else + // The timer is explictly disabled + #define SUITESPARSE_TIME (0) #endif // SuiteSparse printf macro @@ -414,9 +425,14 @@ int SuiteSparse_version // returns SUITESPARSE_VERSION #define SUITESPARSE_SUB_VERSION @SUITESPARSE_VERSION_MINOR@ #define SUITESPARSE_SUBSUB_VERSION @SUITESPARSE_VERSION_SUB@ +// version format x.y #define SUITESPARSE_VER_CODE(main,sub) ((main) * 1000 + (sub)) -#define SUITESPARSE_VERSION \ - SUITESPARSE_VER_CODE(SUITESPARSE_MAIN_VERSION,SUITESPARSE_SUB_VERSION) +#define SUITESPARSE_VERSION SUITESPARSE_VER_CODE(@SUITESPARSE_VERSION_MAJOR@, @SUITESPARSE_VERSION_MINOR@) + +// version format x.y.z +#define SUITESPARSE__VERCODE(main,sub,patch) \ + (((main)*1000ULL + (sub))*1000ULL + (patch)) +#define SUITESPARSE__VERSION SUITESPARSE__VERCODE(@SUITESPARSE_VERSION_MAJOR@,@SUITESPARSE_VERSION_MINOR@,@SUITESPARSE_VERSION_SUB@) //============================================================================== // SuiteSparse interface to the BLAS and LAPACK libraries @@ -469,7 +485,7 @@ int SuiteSparse_version // returns SUITESPARSE_VERSION #elif defined ( BLAS_UNDERSCORE ) - // append an undescore, use lower case + // append an underscore, use lower case #define SUITESPARSE_FORTRAN(name,NAME) name ## _ #define SUITESPARSE__FORTRAN(name,NAME) name ## _ @@ -529,12 +545,12 @@ int SuiteSparse_version // returns SUITESPARSE_VERSION // If the suffix does not contain "_", use (Sun Perf., for example): -// cd build ; cmake -DBLAS64_SUFFIX="64" .. +// cd build && cmake -DBLAS64_SUFFIX="64" .. // If the suffix contains "_" (OpenBLAS in spack for example), use the // following: -// cd build ; cmake -DBLAS64_SUFFIX="_64" .. +// cd build && cmake -DBLAS64_SUFFIX="_64" .. // This setting could be used by the spack packaging of SuiteSparse when linked // with the spack-installed OpenBLAS with 64-bit integers. See @@ -572,6 +588,7 @@ int SuiteSparse_version // returns SUITESPARSE_VERSION // C names of Fortan BLAS and LAPACK functions used by SuiteSparse //------------------------------------------------------------------------------ +// double #define SUITESPARSE_BLAS_DTRSV SUITESPARSE_BLAS ( dtrsv , DTRSV ) #define SUITESPARSE_BLAS_DGEMV SUITESPARSE_BLAS ( dgemv , DGEMV ) #define SUITESPARSE_BLAS_DTRSM SUITESPARSE_BLAS ( dtrsm , DTRSM ) @@ -579,8 +596,15 @@ int SuiteSparse_version // returns SUITESPARSE_VERSION #define SUITESPARSE_BLAS_DSYRK SUITESPARSE_BLAS ( dsyrk , DSYRK ) #define SUITESPARSE_BLAS_DGER SUITESPARSE_BLAS ( dger , DGER ) #define SUITESPARSE_BLAS_DSCAL SUITESPARSE_BLAS ( dscal , DSCAL ) +#define SUITESPARSE_BLAS_DNRM2 SUITESPARSE_BLAS ( dnrm2 , DNRM2 ) + #define SUITESPARSE_LAPACK_DPOTRF SUITESPARSE_BLAS ( dpotrf , DPOTRF ) +#define SUITESPARSE_LAPACK_DLARF SUITESPARSE_BLAS ( dlarf , DLARF ) +#define SUITESPARSE_LAPACK_DLARFG SUITESPARSE_BLAS ( dlarfg , DLARFG ) +#define SUITESPARSE_LAPACK_DLARFT SUITESPARSE_BLAS ( dlarft , DLARFT ) +#define SUITESPARSE_LAPACK_DLARFB SUITESPARSE_BLAS ( dlarfb , DLARFB ) +// double complex #define SUITESPARSE_BLAS_ZTRSV SUITESPARSE_BLAS ( ztrsv , ZTRSV ) #define SUITESPARSE_BLAS_ZGEMV SUITESPARSE_BLAS ( zgemv , ZGEMV ) #define SUITESPARSE_BLAS_ZTRSM SUITESPARSE_BLAS ( ztrsm , ZTRSM ) @@ -588,20 +612,46 @@ int SuiteSparse_version // returns SUITESPARSE_VERSION #define SUITESPARSE_BLAS_ZHERK SUITESPARSE_BLAS ( zherk , ZHERK ) #define SUITESPARSE_BLAS_ZGERU SUITESPARSE_BLAS ( zgeru , ZGERU ) #define SUITESPARSE_BLAS_ZSCAL SUITESPARSE_BLAS ( zscal , ZSCAL ) -#define SUITESPARSE_LAPACK_ZPOTRF SUITESPARSE_BLAS ( zpotrf , ZPOTRF ) - -#define SUITESPARSE_BLAS_DNRM2 SUITESPARSE_BLAS ( dnrm2 , DNRM2 ) -#define SUITESPARSE_LAPACK_DLARF SUITESPARSE_BLAS ( dlarf , DLARF ) -#define SUITESPARSE_LAPACK_DLARFG SUITESPARSE_BLAS ( dlarfg , DLARFG ) -#define SUITESPARSE_LAPACK_DLARFT SUITESPARSE_BLAS ( dlarft , DLARFT ) -#define SUITESPARSE_LAPACK_DLARFB SUITESPARSE_BLAS ( dlarfb , DLARFB ) - #define SUITESPARSE_BLAS_DZNRM2 SUITESPARSE_BLAS ( dznrm2 , DZNRM2 ) + +#define SUITESPARSE_LAPACK_ZPOTRF SUITESPARSE_BLAS ( zpotrf , ZPOTRF ) #define SUITESPARSE_LAPACK_ZLARF SUITESPARSE_BLAS ( zlarf , ZLARF ) #define SUITESPARSE_LAPACK_ZLARFG SUITESPARSE_BLAS ( zlarfg , ZLARFG ) #define SUITESPARSE_LAPACK_ZLARFT SUITESPARSE_BLAS ( zlarft , ZLARFT ) #define SUITESPARSE_LAPACK_ZLARFB SUITESPARSE_BLAS ( zlarfb , ZLARFB ) +// single +#define SUITESPARSE_BLAS_STRSV SUITESPARSE_BLAS ( strsv , STRSV ) +#define SUITESPARSE_BLAS_SGEMV SUITESPARSE_BLAS ( sgemv , SGEMV ) +#define SUITESPARSE_BLAS_STRSM SUITESPARSE_BLAS ( strsm , STRSM ) +#define SUITESPARSE_BLAS_SGEMM SUITESPARSE_BLAS ( sgemm , SGEMM ) +#define SUITESPARSE_BLAS_SSYRK SUITESPARSE_BLAS ( ssyrk , SSYRK ) +#define SUITESPARSE_BLAS_SGER SUITESPARSE_BLAS ( sger , SGER ) +#define SUITESPARSE_BLAS_SSCAL SUITESPARSE_BLAS ( sscal , SSCAL ) +#define SUITESPARSE_BLAS_SNRM2 SUITESPARSE_BLAS ( snrm2 , SNRM2 ) + +#define SUITESPARSE_LAPACK_SPOTRF SUITESPARSE_BLAS ( spotrf , SPOTRF ) +#define SUITESPARSE_LAPACK_SLARF SUITESPARSE_BLAS ( slarf , SLARF ) +#define SUITESPARSE_LAPACK_SLARFG SUITESPARSE_BLAS ( slarfg , SLARFG ) +#define SUITESPARSE_LAPACK_SLARFT SUITESPARSE_BLAS ( slarft , SLARFT ) +#define SUITESPARSE_LAPACK_SLARFB SUITESPARSE_BLAS ( slarfb , SLARFB ) + +// single complex +#define SUITESPARSE_BLAS_CTRSV SUITESPARSE_BLAS ( ctrsv , CTRSV ) +#define SUITESPARSE_BLAS_CGEMV SUITESPARSE_BLAS ( cgemv , CGEMV ) +#define SUITESPARSE_BLAS_CTRSM SUITESPARSE_BLAS ( ctrsm , CTRSM ) +#define SUITESPARSE_BLAS_CGEMM SUITESPARSE_BLAS ( cgemm , CGEMM ) +#define SUITESPARSE_BLAS_CHERK SUITESPARSE_BLAS ( cherk , CHERK ) +#define SUITESPARSE_BLAS_CGERU SUITESPARSE_BLAS ( cgeru , CGERU ) +#define SUITESPARSE_BLAS_CSCAL SUITESPARSE_BLAS ( cscal , CSCAL ) +#define SUITESPARSE_BLAS_SCNRM2 SUITESPARSE_BLAS ( scnrm2 , SCNRM2 ) + +#define SUITESPARSE_LAPACK_CPOTRF SUITESPARSE_BLAS ( cpotrf , CPOTRF ) +#define SUITESPARSE_LAPACK_CLARF SUITESPARSE_BLAS ( clarf , CLARF ) +#define SUITESPARSE_LAPACK_CLARFG SUITESPARSE_BLAS ( clarfg , CLARFG ) +#define SUITESPARSE_LAPACK_CLARFT SUITESPARSE_BLAS ( clarft , CLARFT ) +#define SUITESPARSE_LAPACK_CLARFB SUITESPARSE_BLAS ( clarfb , CLARFB ) + //------------------------------------------------------------------------------ // prototypes of BLAS and SUITESPARSE_LAPACK functions //------------------------------------------------------------------------------ @@ -627,7 +677,11 @@ int SuiteSparse_version // returns SUITESPARSE_VERSION #if defined ( SUITESPARSE_BLAS_DEFINITIONS ) -void SUITESPARSE_BLAS_DGEMV // Y = alpha*A*x + beta*Y +//------------------------------------------------------------------------------ +// gemv: Y = alpha*A*x + beta*Y +//------------------------------------------------------------------------------ + +void SUITESPARSE_BLAS_DGEMV ( // input: const char *trans, @@ -659,7 +713,39 @@ void SUITESPARSE_BLAS_DGEMV // Y = alpha*A*x + beta*Y } \ } -void SUITESPARSE_BLAS_ZGEMV // Y = alpha*A*X + beta*Y +void SUITESPARSE_BLAS_SGEMV +( + // input: + const char *trans, + const SUITESPARSE_BLAS_INT *m, + const SUITESPARSE_BLAS_INT *n, + const float *alpha, + const float *A, + const SUITESPARSE_BLAS_INT *lda, + const float *X, + const SUITESPARSE_BLAS_INT *incx, + const float *beta, + // input/output: + float *Y, + // input: + const SUITESPARSE_BLAS_INT *incy +) ; + +#define SUITESPARSE_BLAS_sgemv(trans,m,n,alpha,A,lda,X,incx,beta,Y,incy,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (M_blas_int, m, ok) ; \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCX_blas_int, incx, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCY_blas_int, incy, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_BLAS_SGEMV (trans, &M_blas_int, &N_blas_int, alpha, A, \ + &LDA_blas_int, X, &INCX_blas_int, beta, Y, &INCY_blas_int) ; \ + } \ +} + +void SUITESPARSE_BLAS_ZGEMV ( // input: const char *trans, @@ -691,7 +777,43 @@ void SUITESPARSE_BLAS_ZGEMV // Y = alpha*A*X + beta*Y } \ } -void SUITESPARSE_BLAS_DTRSV // solve Lx=b, Ux=b, L'x=b, or U'x=b +void SUITESPARSE_BLAS_CGEMV +( + // input: + const char *trans, + const SUITESPARSE_BLAS_INT *m, + const SUITESPARSE_BLAS_INT *n, + const void *alpha, + const void *A, + const SUITESPARSE_BLAS_INT *lda, + const void *X, + const SUITESPARSE_BLAS_INT *incx, + const void *beta, + // input/output: + void *Y, + // input: + const SUITESPARSE_BLAS_INT *incy +) ; + +#define SUITESPARSE_BLAS_cgemv(trans,m,n,alpha,A,lda,X,incx,beta,Y,incy,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (M_blas_int, m, ok) ; \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCX_blas_int, incx, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCY_blas_int, incy, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_BLAS_CGEMV (trans, &M_blas_int, &N_blas_int, alpha, A, \ + &LDA_blas_int, X, &INCX_blas_int, beta, Y, &INCY_blas_int) ; \ + } \ +} + +//------------------------------------------------------------------------------ +// trsv: solve Lx=b, Ux=b, L'x=b, or U'x=b +//------------------------------------------------------------------------------ + +void SUITESPARSE_BLAS_DTRSV ( // input: const char *uplo, @@ -718,7 +840,34 @@ void SUITESPARSE_BLAS_DTRSV // solve Lx=b, Ux=b, L'x=b, or U'x=b } \ } -void SUITESPARSE_BLAS_ZTRSV // solve (L, L', L^H, U, U', or U^H)x=b +void SUITESPARSE_BLAS_STRSV +( + // input: + const char *uplo, + const char *trans, + const char *diag, + const SUITESPARSE_BLAS_INT *n, + const float *A, + const SUITESPARSE_BLAS_INT *lda, + // input/output: + float *X, + // input: + const SUITESPARSE_BLAS_INT *incx +) ; + +#define SUITESPARSE_BLAS_strsv(uplo,trans,diag,n,A,lda,X,incx,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCX_blas_int, incx, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_BLAS_STRSV (uplo, trans, diag, &N_blas_int, A, \ + &LDA_blas_int, X, &INCX_blas_int) ; \ + } \ +} + +void SUITESPARSE_BLAS_ZTRSV ( // input: const char *uplo, @@ -745,7 +894,38 @@ void SUITESPARSE_BLAS_ZTRSV // solve (L, L', L^H, U, U', or U^H)x=b } \ } -void SUITESPARSE_BLAS_DTRSM // solve LX=B, UX=B, L'X=B, or U'X=B +void SUITESPARSE_BLAS_CTRSV +( + // input: + const char *uplo, + const char *trans, + const char *diag, + const SUITESPARSE_BLAS_INT *n, + const void *A, + const SUITESPARSE_BLAS_INT *lda, + // input/output: + void *X, + // input: + const SUITESPARSE_BLAS_INT *incx +) ; + +#define SUITESPARSE_BLAS_ctrsv(uplo,trans,diag,n,A,lda,X,incx,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCX_blas_int, incx, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_BLAS_CTRSV (uplo, trans, diag, &N_blas_int, A, \ + &LDA_blas_int, X, &INCX_blas_int) ; \ + } \ +} + +//------------------------------------------------------------------------------ +// trsm: solve LX=B, UX=B, L'X=B, or U'X=B +//------------------------------------------------------------------------------ + +void SUITESPARSE_BLAS_DTRSM ( // input: const char *side, @@ -776,7 +956,38 @@ void SUITESPARSE_BLAS_DTRSM // solve LX=B, UX=B, L'X=B, or U'X=B } \ } -void SUITESPARSE_BLAS_ZTRSM // solve (L, L', L^H, U, U', or U^H)X=B +void SUITESPARSE_BLAS_STRSM +( + // input: + const char *side, + const char *uplo, + const char *transa, + const char *diag, + const SUITESPARSE_BLAS_INT *m, + const SUITESPARSE_BLAS_INT *n, + const float *alpha, + const float *A, + const SUITESPARSE_BLAS_INT *lda, + // input/output: + float *B, + // input: + const SUITESPARSE_BLAS_INT *ldb +) ; + +#define SUITESPARSE_BLAS_strsm(side,uplo,transa,diag,m,n,alpha,A,lda,B,ldb,ok)\ +{ \ + SUITESPARSE_TO_BLAS_INT (M_blas_int, m, ok) ; \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDB_blas_int, ldb, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_BLAS_STRSM (side, uplo, transa, diag, &M_blas_int, \ + &N_blas_int, alpha, A, &LDA_blas_int, B, &LDB_blas_int) ; \ + } \ +} + +void SUITESPARSE_BLAS_ZTRSM ( // input: const char *side, @@ -807,7 +1018,42 @@ void SUITESPARSE_BLAS_ZTRSM // solve (L, L', L^H, U, U', or U^H)X=B } \ } -void SUITESPARSE_BLAS_DGEMM // C = alpha*A*B + beta*C +void SUITESPARSE_BLAS_CTRSM +( + // input: + const char *side, + const char *uplo, + const char *transa, + const char *diag, + const SUITESPARSE_BLAS_INT *m, + const SUITESPARSE_BLAS_INT *n, + const void *alpha, + const void *A, + const SUITESPARSE_BLAS_INT *lda, + // input/output: + void *B, + // input: + const SUITESPARSE_BLAS_INT *ldb +) ; + +#define SUITESPARSE_BLAS_ctrsm(side,uplo,transa,diag,m,n,alpha,A,lda,B,ldb,ok)\ +{ \ + SUITESPARSE_TO_BLAS_INT (M_blas_int, m, ok) ; \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDB_blas_int, ldb, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_BLAS_CTRSM (side, uplo, transa, diag, &M_blas_int, \ + &N_blas_int, alpha, A, &LDA_blas_int, B, &LDB_blas_int) ; \ + } \ +} + +//------------------------------------------------------------------------------ +// gemm: C = alpha*A*B + beta*C +//------------------------------------------------------------------------------ + +void SUITESPARSE_BLAS_DGEMM ( // input: const char *transa, @@ -844,7 +1090,7 @@ void SUITESPARSE_BLAS_DGEMM // C = alpha*A*B + beta*C } \ } -void SUITESPARSE_BLAS_ZGEMM // C = alpha*A*B + beta*C +void SUITESPARSE_BLAS_SGEMM ( // input: const char *transa, @@ -852,19 +1098,19 @@ void SUITESPARSE_BLAS_ZGEMM // C = alpha*A*B + beta*C const SUITESPARSE_BLAS_INT *m, const SUITESPARSE_BLAS_INT *n, const SUITESPARSE_BLAS_INT *k, - const void *alpha, - const void *A, + const float *alpha, + const float *A, const SUITESPARSE_BLAS_INT *lda, - const void *B, + const float *B, const SUITESPARSE_BLAS_INT *ldb, - const void *beta, + const float *beta, // input/output: - void *C, + float *C, // input: const SUITESPARSE_BLAS_INT *ldc ) ; -#define SUITESPARSE_BLAS_zgemm(transa,transb,m,n,k,alpha,A,lda,B,ldb,beta, \ +#define SUITESPARSE_BLAS_sgemm(transa,transb,m,n,k,alpha,A,lda,B,ldb,beta, \ C,ldc,ok) \ { \ SUITESPARSE_TO_BLAS_INT (M_blas_int, m, ok) ; \ @@ -875,52 +1121,62 @@ void SUITESPARSE_BLAS_ZGEMM // C = alpha*A*B + beta*C SUITESPARSE_TO_BLAS_INT (LDC_blas_int, ldc, ok) ; \ if (ok) \ { \ - SUITESPARSE_BLAS_ZGEMM (transa, transb, &M_blas_int, &N_blas_int, \ + SUITESPARSE_BLAS_SGEMM (transa, transb, &M_blas_int, &N_blas_int, \ &K_blas_int, alpha, A, &LDA_blas_int, B, &LDB_blas_int, beta, C, \ &LDC_blas_int) ; \ } \ } -void SUITESPARSE_BLAS_DSYRK // C = alpha*A*A' + beta*C, or A'A +void SUITESPARSE_BLAS_ZGEMM ( // input: - const char *uplo, - const char *trans, + const char *transa, + const char *transb, + const SUITESPARSE_BLAS_INT *m, const SUITESPARSE_BLAS_INT *n, const SUITESPARSE_BLAS_INT *k, - const double *alpha, - const double *A, + const void *alpha, + const void *A, const SUITESPARSE_BLAS_INT *lda, - const double *beta, + const void *B, + const SUITESPARSE_BLAS_INT *ldb, + const void *beta, // input/output: - double *C, + void *C, // input: const SUITESPARSE_BLAS_INT *ldc ) ; -#define SUITESPARSE_BLAS_dsyrk(uplo,trans,n,k,alpha,A,lda,beta,C,ldc,ok) \ +#define SUITESPARSE_BLAS_zgemm(transa,transb,m,n,k,alpha,A,lda,B,ldb,beta, \ + C,ldc,ok) \ { \ + SUITESPARSE_TO_BLAS_INT (M_blas_int, m, ok) ; \ SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ SUITESPARSE_TO_BLAS_INT (K_blas_int, k, ok) ; \ SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDB_blas_int, ldb, ok) ; \ SUITESPARSE_TO_BLAS_INT (LDC_blas_int, ldc, ok) ; \ if (ok) \ { \ - SUITESPARSE_BLAS_DSYRK (uplo, trans, &N_blas_int, &K_blas_int, alpha, \ - A, &LDA_blas_int, beta, C, &LDC_blas_int) ; \ + SUITESPARSE_BLAS_ZGEMM (transa, transb, &M_blas_int, &N_blas_int, \ + &K_blas_int, alpha, A, &LDA_blas_int, B, &LDB_blas_int, beta, C, \ + &LDC_blas_int) ; \ } \ } -void SUITESPARSE_BLAS_ZHERK // C = alpha*A*A^H + beta*C, or A^H*A +void SUITESPARSE_BLAS_CGEMM ( // input: - const char *uplo, - const char *trans, + const char *transa, + const char *transb, + const SUITESPARSE_BLAS_INT *m, const SUITESPARSE_BLAS_INT *n, const SUITESPARSE_BLAS_INT *k, const void *alpha, const void *A, const SUITESPARSE_BLAS_INT *lda, + const void *B, + const SUITESPARSE_BLAS_INT *ldb, const void *beta, // input/output: void *C, @@ -928,47 +1184,206 @@ void SUITESPARSE_BLAS_ZHERK // C = alpha*A*A^H + beta*C, or A^H*A const SUITESPARSE_BLAS_INT *ldc ) ; -#define SUITESPARSE_BLAS_zherk(uplo,trans,n,k,alpha,A,lda,beta,C,ldc,ok) \ +#define SUITESPARSE_BLAS_cgemm(transa,transb,m,n,k,alpha,A,lda,B,ldb,beta, \ + C,ldc,ok) \ { \ + SUITESPARSE_TO_BLAS_INT (M_blas_int, m, ok) ; \ SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ SUITESPARSE_TO_BLAS_INT (K_blas_int, k, ok) ; \ SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDB_blas_int, ldb, ok) ; \ SUITESPARSE_TO_BLAS_INT (LDC_blas_int, ldc, ok) ; \ if (ok) \ { \ - SUITESPARSE_BLAS_ZHERK (uplo, trans, &N_blas_int, &K_blas_int, alpha, \ - A, &LDA_blas_int, beta, C, &LDC_blas_int) ; \ + SUITESPARSE_BLAS_CGEMM (transa, transb, &M_blas_int, &N_blas_int, \ + &K_blas_int, alpha, A, &LDA_blas_int, B, &LDB_blas_int, beta, C, \ + &LDC_blas_int) ; \ } \ } -void SUITESPARSE_LAPACK_DPOTRF // Cholesky factorization +//------------------------------------------------------------------------------ +// syrk/herk: C = alpha*A*A' + beta*C ; or C = alpha*A'*A + beta*C +//------------------------------------------------------------------------------ + +void SUITESPARSE_BLAS_DSYRK ( // input: const char *uplo, + const char *trans, const SUITESPARSE_BLAS_INT *n, + const SUITESPARSE_BLAS_INT *k, + const double *alpha, + const double *A, + const SUITESPARSE_BLAS_INT *lda, + const double *beta, // input/output: - double *A, + double *C, // input: - const SUITESPARSE_BLAS_INT *lda, - // output: - SUITESPARSE_BLAS_INT *info + const SUITESPARSE_BLAS_INT *ldc ) ; -#define SUITESPARSE_LAPACK_dpotrf(uplo,n,A,lda,info,ok) \ +#define SUITESPARSE_BLAS_dsyrk(uplo,trans,n,k,alpha,A,lda,beta,C,ldc,ok) \ { \ SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (K_blas_int, k, ok) ; \ SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ - info = 1 ; \ + SUITESPARSE_TO_BLAS_INT (LDC_blas_int, ldc, ok) ; \ if (ok) \ { \ - SUITESPARSE_BLAS_INT LAPACK_Info = -999 ; \ - SUITESPARSE_LAPACK_DPOTRF (uplo, &N_blas_int, A, &LDA_blas_int, \ - &LAPACK_Info) ; \ - info = (Int) LAPACK_Info ; \ + SUITESPARSE_BLAS_DSYRK (uplo, trans, &N_blas_int, &K_blas_int, alpha, \ + A, &LDA_blas_int, beta, C, &LDC_blas_int) ; \ } \ } -void SUITESPARSE_LAPACK_ZPOTRF // Cholesky factorization +void SUITESPARSE_BLAS_SSYRK +( + // input: + const char *uplo, + const char *trans, + const SUITESPARSE_BLAS_INT *n, + const SUITESPARSE_BLAS_INT *k, + const float *alpha, + const float *A, + const SUITESPARSE_BLAS_INT *lda, + const float *beta, + // input/output: + float *C, + // input: + const SUITESPARSE_BLAS_INT *ldc +) ; + +#define SUITESPARSE_BLAS_ssyrk(uplo,trans,n,k,alpha,A,lda,beta,C,ldc,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (K_blas_int, k, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDC_blas_int, ldc, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_BLAS_SSYRK (uplo, trans, &N_blas_int, &K_blas_int, alpha, \ + A, &LDA_blas_int, beta, C, &LDC_blas_int) ; \ + } \ +} + +void SUITESPARSE_BLAS_ZHERK +( + // input: + const char *uplo, + const char *trans, + const SUITESPARSE_BLAS_INT *n, + const SUITESPARSE_BLAS_INT *k, + const void *alpha, + const void *A, + const SUITESPARSE_BLAS_INT *lda, + const void *beta, + // input/output: + void *C, + // input: + const SUITESPARSE_BLAS_INT *ldc +) ; + +#define SUITESPARSE_BLAS_zherk(uplo,trans,n,k,alpha,A,lda,beta,C,ldc,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (K_blas_int, k, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDC_blas_int, ldc, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_BLAS_ZHERK (uplo, trans, &N_blas_int, &K_blas_int, alpha, \ + A, &LDA_blas_int, beta, C, &LDC_blas_int) ; \ + } \ +} + +void SUITESPARSE_BLAS_CHERK +( + // input: + const char *uplo, + const char *trans, + const SUITESPARSE_BLAS_INT *n, + const SUITESPARSE_BLAS_INT *k, + const void *alpha, + const void *A, + const SUITESPARSE_BLAS_INT *lda, + const void *beta, + // input/output: + void *C, + // input: + const SUITESPARSE_BLAS_INT *ldc +) ; + +#define SUITESPARSE_BLAS_cherk(uplo,trans,n,k,alpha,A,lda,beta,C,ldc,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (K_blas_int, k, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDC_blas_int, ldc, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_BLAS_CHERK (uplo, trans, &N_blas_int, &K_blas_int, alpha, \ + A, &LDA_blas_int, beta, C, &LDC_blas_int) ; \ + } \ +} + +//------------------------------------------------------------------------------ +// potrf: Cholesky factorization +//------------------------------------------------------------------------------ + +void SUITESPARSE_LAPACK_DPOTRF +( + // input: + const char *uplo, + const SUITESPARSE_BLAS_INT *n, + // input/output: + double *A, + // input: + const SUITESPARSE_BLAS_INT *lda, + // output: + SUITESPARSE_BLAS_INT *info +) ; + +#define SUITESPARSE_LAPACK_dpotrf(uplo,n,A,lda,info,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ + info = 1 ; \ + if (ok) \ + { \ + SUITESPARSE_BLAS_INT LAPACK_Info = -999 ; \ + SUITESPARSE_LAPACK_DPOTRF (uplo, &N_blas_int, A, &LDA_blas_int, \ + &LAPACK_Info) ; \ + info = (Int) LAPACK_Info ; \ + } \ +} + +void SUITESPARSE_LAPACK_SPOTRF +( + // input: + const char *uplo, + const SUITESPARSE_BLAS_INT *n, + // input/output: + float *A, + // input: + const SUITESPARSE_BLAS_INT *lda, + // output: + SUITESPARSE_BLAS_INT *info +) ; + +#define SUITESPARSE_LAPACK_spotrf(uplo,n,A,lda,info,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ + info = 1 ; \ + if (ok) \ + { \ + SUITESPARSE_BLAS_INT LAPACK_Info = -999 ; \ + SUITESPARSE_LAPACK_SPOTRF (uplo, &N_blas_int, A, &LDA_blas_int, \ + &LAPACK_Info) ; \ + info = (Int) LAPACK_Info ; \ + } \ +} + +void SUITESPARSE_LAPACK_ZPOTRF ( // input: const char *uplo, @@ -995,7 +1410,38 @@ void SUITESPARSE_LAPACK_ZPOTRF // Cholesky factorization } \ } -void SUITESPARSE_BLAS_DSCAL // Y = alpha*Y +void SUITESPARSE_LAPACK_CPOTRF +( + // input: + const char *uplo, + const SUITESPARSE_BLAS_INT *n, + // input/output: + void *A, + // input: + const SUITESPARSE_BLAS_INT *lda, + // output: + SUITESPARSE_BLAS_INT *info +) ; + +#define SUITESPARSE_LAPACK_cpotrf(uplo,n,A,lda,info,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ + info = 1 ; \ + if (ok) \ + { \ + SUITESPARSE_BLAS_INT LAPACK_Info = -999 ; \ + SUITESPARSE_LAPACK_CPOTRF (uplo, &N_blas_int, A, &LDA_blas_int, \ + &LAPACK_Info) ; \ + info = LAPACK_Info ; \ + } \ +} + +//------------------------------------------------------------------------------ +// scal: Y = alpha*Y +//------------------------------------------------------------------------------ + +void SUITESPARSE_BLAS_DSCAL ( // input: const SUITESPARSE_BLAS_INT *n, @@ -1016,7 +1462,28 @@ void SUITESPARSE_BLAS_DSCAL // Y = alpha*Y } \ } -void SUITESPARSE_BLAS_ZSCAL // Y = alpha*Y +void SUITESPARSE_BLAS_SSCAL +( + // input: + const SUITESPARSE_BLAS_INT *n, + const float *alpha, + // input/output: + float *Y, + // input: + const SUITESPARSE_BLAS_INT *incy +) ; + +#define SUITESPARSE_BLAS_sscal(n,alpha,Y,incy,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCY_blas_int, incy, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_BLAS_SSCAL (&N_blas_int, alpha, Y, &INCY_blas_int) ; \ + } \ +} + +void SUITESPARSE_BLAS_ZSCAL ( // input: const SUITESPARSE_BLAS_INT *n, @@ -1037,7 +1504,32 @@ void SUITESPARSE_BLAS_ZSCAL // Y = alpha*Y } \ } -void SUITESPARSE_BLAS_DGER // A = alpha*x*y' + A +void SUITESPARSE_BLAS_CSCAL +( + // input: + const SUITESPARSE_BLAS_INT *n, + const void *alpha, + // input/output: + void *Y, + // input: + const SUITESPARSE_BLAS_INT *incy +) ; + +#define SUITESPARSE_BLAS_cscal(n,alpha,Y,incy,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCY_blas_int, incy, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_BLAS_CSCAL (&N_blas_int, alpha, Y, &INCY_blas_int) ; \ + } \ +} + +//------------------------------------------------------------------------------ +// ger/geru: A = alpha*x*y' + A +//------------------------------------------------------------------------------ + +void SUITESPARSE_BLAS_DGER ( // input: const SUITESPARSE_BLAS_INT *m, @@ -1067,7 +1559,37 @@ void SUITESPARSE_BLAS_DGER // A = alpha*x*y' + A } \ } -void SUITESPARSE_BLAS_ZGERU // A = alpha*x*y' + A +void SUITESPARSE_BLAS_SGER +( + // input: + const SUITESPARSE_BLAS_INT *m, + const SUITESPARSE_BLAS_INT *n, + const float *alpha, + const float *X, + const SUITESPARSE_BLAS_INT *incx, + const float *Y, + const SUITESPARSE_BLAS_INT *incy, + // input/output: + float *A, + // input: + const SUITESPARSE_BLAS_INT *lda +) ; + +#define SUITESPARSE_BLAS_sger(m,n,alpha,X,incx,Y,incy,A,lda,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (M_blas_int, m, ok) ; \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCX_blas_int, incx, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCY_blas_int, incy, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_BLAS_SGER (&M_blas_int, &N_blas_int, alpha, X, \ + &INCX_blas_int, Y, &INCY_blas_int, A, &LDA_blas_int) ; \ + } \ +} + +void SUITESPARSE_BLAS_ZGERU ( // input: const SUITESPARSE_BLAS_INT *m, @@ -1097,7 +1619,41 @@ void SUITESPARSE_BLAS_ZGERU // A = alpha*x*y' + A } \ } -void SUITESPARSE_LAPACK_DLARFT // T = block Householder factor +void SUITESPARSE_BLAS_CGERU +( + // input: + const SUITESPARSE_BLAS_INT *m, + const SUITESPARSE_BLAS_INT *n, + const void *alpha, + const void *X, + const SUITESPARSE_BLAS_INT *incx, + const void *Y, + const SUITESPARSE_BLAS_INT *incy, + // input/output: + void *A, + // input: + const SUITESPARSE_BLAS_INT *lda +) ; + +#define SUITESPARSE_BLAS_cgeru(m,n,alpha,X,incx,Y,incy,A,lda,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (M_blas_int, m, ok) ; \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCX_blas_int, incx, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCY_blas_int, incy, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_BLAS_CGERU (&M_blas_int, &N_blas_int, alpha, X, \ + &INCX_blas_int, Y, &INCY_blas_int, A, &LDA_blas_int) ; \ + } \ +} + +//------------------------------------------------------------------------------ +// larft: T = block Householder factor +//------------------------------------------------------------------------------ + +void SUITESPARSE_LAPACK_DLARFT ( // input: const char *direct, @@ -1126,7 +1682,36 @@ void SUITESPARSE_LAPACK_DLARFT // T = block Householder factor } \ } -void SUITESPARSE_LAPACK_ZLARFT // T = block Householder factor +void SUITESPARSE_LAPACK_SLARFT +( + // input: + const char *direct, + const char *storev, + const SUITESPARSE_BLAS_INT *n, + const SUITESPARSE_BLAS_INT *k, + const float *V, + const SUITESPARSE_BLAS_INT *ldv, + const float *Tau, + // output: + float *T, + // input: + const SUITESPARSE_BLAS_INT *ldt +) ; + +#define SUITESPARSE_LAPACK_slarft(direct,storev,n,k,V,ldv,Tau,T,ldt,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (K_blas_int, k, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDV_blas_int, ldv, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDT_blas_int, ldt, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_LAPACK_SLARFT (direct, storev, &N_blas_int, &K_blas_int, \ + V, &LDV_blas_int, Tau, T, &LDT_blas_int) ; \ + } \ +} + +void SUITESPARSE_LAPACK_ZLARFT ( // input: const char *direct, @@ -1155,7 +1740,40 @@ void SUITESPARSE_LAPACK_ZLARFT // T = block Householder factor } \ } -void SUITESPARSE_LAPACK_DLARFB // apply block Householder reflector +void SUITESPARSE_LAPACK_CLARFT +( + // input: + const char *direct, + const char *storev, + const SUITESPARSE_BLAS_INT *n, + const SUITESPARSE_BLAS_INT *k, + const void *V, + const SUITESPARSE_BLAS_INT *ldv, + const void *Tau, + // output: + void *T, + // input: + const SUITESPARSE_BLAS_INT *ldt +) ; + +#define SUITESPARSE_LAPACK_clarft(direct,storev,n,k,V,ldv,Tau,T,ldt,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (K_blas_int, k, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDV_blas_int, ldv, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDT_blas_int, ldt, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_LAPACK_CLARFT (direct, storev, &N_blas_int, &K_blas_int, \ + V, &LDV_blas_int, Tau, T, &LDT_blas_int) ; \ + } \ +} + +//------------------------------------------------------------------------------ +// larfb: apply block Householder reflector +//------------------------------------------------------------------------------ + +void SUITESPARSE_LAPACK_DLARFB ( // input: const char *side, @@ -1197,7 +1815,49 @@ void SUITESPARSE_LAPACK_DLARFB // apply block Householder reflector } \ } -void SUITESPARSE_LAPACK_ZLARFB // apply block Householder reflector +void SUITESPARSE_LAPACK_SLARFB +( + // input: + const char *side, + const char *trans, + const char *direct, + const char *storev, + const SUITESPARSE_BLAS_INT *m, + const SUITESPARSE_BLAS_INT *n, + const SUITESPARSE_BLAS_INT *k, + const float *V, + const SUITESPARSE_BLAS_INT *ldv, + const float *T, + const SUITESPARSE_BLAS_INT *ldt, + // input/output: + float *C, + // input: + const SUITESPARSE_BLAS_INT *ldc, + // workspace: + float *Work, + // input: + const SUITESPARSE_BLAS_INT *ldwork +) ; + +#define SUITESPARSE_LAPACK_slarfb(side,trans,direct,storev,m,n,k,V,ldv,T,ldt, \ + C,ldc,Work,ldwork,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (M_blas_int, m, ok) ; \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (K_blas_int, k, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDV_blas_int, ldv, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDT_blas_int, ldt, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDC_blas_int, ldc, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDWORK_blas_int, ldwork, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_LAPACK_SLARFB (side, trans, direct, storev, &M_blas_int, \ + &N_blas_int, &K_blas_int, V, &LDV_blas_int, T, &LDT_blas_int, C, \ + &LDC_blas_int, Work, &LDWORK_blas_int) ; \ + } \ +} + +void SUITESPARSE_LAPACK_ZLARFB ( // input: const char *side, @@ -1239,7 +1899,53 @@ void SUITESPARSE_LAPACK_ZLARFB // apply block Householder reflector } \ } -double SUITESPARSE_BLAS_DNRM2 // vector 2-norm +void SUITESPARSE_LAPACK_CLARFB +( + // input: + const char *side, + const char *trans, + const char *direct, + const char *storev, + const SUITESPARSE_BLAS_INT *m, + const SUITESPARSE_BLAS_INT *n, + const SUITESPARSE_BLAS_INT *k, + const void *V, + const SUITESPARSE_BLAS_INT *ldv, + const void *T, + const SUITESPARSE_BLAS_INT *ldt, + // input/output: + void *C, + // input: + const SUITESPARSE_BLAS_INT *ldc, + // workspace: + void *Work, + // input: + const SUITESPARSE_BLAS_INT *ldwork +) ; + +#define SUITESPARSE_LAPACK_clarfb(side,trans,direct,storev,m,n,k,V,ldv,T,ldt, \ + C,ldc,Work,ldwork,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (M_blas_int, m, ok) ; \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (K_blas_int, k, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDV_blas_int, ldv, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDT_blas_int, ldt, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDC_blas_int, ldc, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDWORK_blas_int, ldwork, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_LAPACK_CLARFB (side, trans, direct, storev, &M_blas_int, \ + &N_blas_int, &K_blas_int, V, &LDV_blas_int, T, &LDT_blas_int, C, \ + &LDC_blas_int, Work, &LDWORK_blas_int) ; \ + } \ +} + +//------------------------------------------------------------------------------ +// nrm2: vector 2-norm +//------------------------------------------------------------------------------ + +double SUITESPARSE_BLAS_DNRM2 ( // input: const SUITESPARSE_BLAS_INT *n, @@ -1258,7 +1964,26 @@ double SUITESPARSE_BLAS_DNRM2 // vector 2-norm } \ } -double SUITESPARSE_BLAS_DZNRM2 // vector 2-norm +float SUITESPARSE_BLAS_SNRM2 +( + // input: + const SUITESPARSE_BLAS_INT *n, + const float *X, + const SUITESPARSE_BLAS_INT *incx +) ; + +#define SUITESPARSE_BLAS_snrm2(result,n,X,incx,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCX_blas_int, incx, ok) ; \ + result = 0 ; \ + if (ok) \ + { \ + result = SUITESPARSE_BLAS_SNRM2 (&N_blas_int, X, &INCX_blas_int) ; \ + } \ +} + +double SUITESPARSE_BLAS_DZNRM2 ( // input: const SUITESPARSE_BLAS_INT *n, @@ -1277,7 +2002,30 @@ double SUITESPARSE_BLAS_DZNRM2 // vector 2-norm } \ } -void SUITESPARSE_LAPACK_DLARFG // generate Householder reflector +float SUITESPARSE_BLAS_SCNRM2 +( + // input: + const SUITESPARSE_BLAS_INT *n, + const void *X, + const SUITESPARSE_BLAS_INT *incx +) ; + +#define SUITESPARSE_BLAS_scnrm2(result,n,X,incx,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCX_blas_int, incx, ok) ; \ + result = 0 ; \ + if (ok) \ + { \ + result = SUITESPARSE_BLAS_SCNRM2 (&N_blas_int, X, &INCX_blas_int) ; \ + } \ +} + +//------------------------------------------------------------------------------ +// larfg: generate Householder reflector +//------------------------------------------------------------------------------ + +void SUITESPARSE_LAPACK_DLARFG ( // input: const SUITESPARSE_BLAS_INT *n, @@ -1301,7 +2049,31 @@ void SUITESPARSE_LAPACK_DLARFG // generate Householder reflector } \ } -void SUITESPARSE_LAPACK_ZLARFG // generate Householder reflector +void SUITESPARSE_LAPACK_SLARFG +( + // input: + const SUITESPARSE_BLAS_INT *n, + // input/output: + float *alpha, + float *X, + // input: + const SUITESPARSE_BLAS_INT *incx, + // output: + float *tau +) ; + +#define SUITESPARSE_LAPACK_slarfg(n,alpha,X,incx,tau,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCX_blas_int, incx, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_LAPACK_SLARFG (&N_blas_int, alpha, X, &INCX_blas_int, \ + tau) ; \ + } \ +} + +void SUITESPARSE_LAPACK_ZLARFG ( // input: const SUITESPARSE_BLAS_INT *n, @@ -1325,7 +2097,35 @@ void SUITESPARSE_LAPACK_ZLARFG // generate Householder reflector } \ } -void SUITESPARSE_LAPACK_DLARF // apply Householder reflector +void SUITESPARSE_LAPACK_CLARFG +( + // input: + const SUITESPARSE_BLAS_INT *n, + // input/output: + void *alpha, + void *X, + // input: + const SUITESPARSE_BLAS_INT *incx, + // output: + void *tau +) ; + +#define SUITESPARSE_LAPACK_clarfg(n,alpha,X,incx,tau,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCX_blas_int, incx, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_LAPACK_CLARFG (&N_blas_int, alpha, X, &INCX_blas_int, \ + tau) ; \ + } \ +} + +//------------------------------------------------------------------------------ +// larf: apply Householder reflector +//------------------------------------------------------------------------------ + +void SUITESPARSE_LAPACK_DLARF ( // input: const char *side, @@ -1355,7 +2155,37 @@ void SUITESPARSE_LAPACK_DLARF // apply Householder reflector } \ } -void SUITESPARSE_LAPACK_ZLARF // apply Householder reflector +void SUITESPARSE_LAPACK_SLARF +( + // input: + const char *side, + const SUITESPARSE_BLAS_INT *m, + const SUITESPARSE_BLAS_INT *n, + const float *V, + const SUITESPARSE_BLAS_INT *incv, + const float *tau, + // input/output: + float *C, + // input: + const SUITESPARSE_BLAS_INT *ldc, + // workspace: + float *Work +) ; + +#define SUITESPARSE_LAPACK_slarf(side,m,n,V,incv,tau,C,ldc,Work,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (M_blas_int, m, ok) ; \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCV_blas_int, incv, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDC_blas_int, ldc, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_LAPACK_SLARF (side, &M_blas_int, &N_blas_int, V, \ + &INCV_blas_int, tau, C, &LDC_blas_int, Work) ; \ + } \ +} + +void SUITESPARSE_LAPACK_ZLARF ( // input: const char *side, @@ -1385,6 +2215,36 @@ void SUITESPARSE_LAPACK_ZLARF // apply Householder reflector } \ } +void SUITESPARSE_LAPACK_CLARF +( + // input: + const char *side, + const SUITESPARSE_BLAS_INT *m, + const SUITESPARSE_BLAS_INT *n, + const void *V, + const SUITESPARSE_BLAS_INT *incv, + const void *tau, + // input/output: + void *C, + // input: + const SUITESPARSE_BLAS_INT *ldc, + // workspace: + void *Work +) ; + +#define SUITESPARSE_LAPACK_clarf(side,m,n,V,incv,tau,C,ldc,Work,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (M_blas_int, m, ok) ; \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCV_blas_int, incv, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDC_blas_int, ldc, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_LAPACK_CLARF (side, &M_blas_int, &N_blas_int, V, \ + &INCV_blas_int, tau, C, &LDC_blas_int, Work) ; \ + } \ +} + #endif //------------------------------------------------------------------------------ diff --git a/ThirdParty/SuiteSparse/SuiteSparse_config/Config/SuiteSparse_config.pc.in b/ThirdParty/SuiteSparse/SuiteSparse_config/Config/SuiteSparse_config.pc.in new file mode 100644 index 0000000000..f082c22602 --- /dev/null +++ b/ThirdParty/SuiteSparse/SuiteSparse_config/Config/SuiteSparse_config.pc.in @@ -0,0 +1,16 @@ +# SuiteSparse_config, Copyright (c) 2012-2023, Timothy A. Davis. +# All Rights Reserved. +# SPDX-License-Identifier: BSD-3-clause + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: SuiteSparseConfig +URL: https://github.com/DrTimothyAldenDavis/SuiteSparse +Description: Configuration for SuiteSparse +Version: @SUITESPARSE_VERSION_MAJOR@.@SUITESPARSE_VERSION_MINOR@.@SUITESPARSE_VERSION_SUB@ +Libs: -L${libdir} -l@SUITESPARSE_LIB_BASE_NAME@ +Libs.private: @SUITESPARSE_CONFIG_STATIC_LIBS@ +Cflags: -I${includedir} diff --git a/ThirdParty/SuiteSparse/SuiteSparse_config/Config/SuiteSparse_configConfig.cmake.in b/ThirdParty/SuiteSparse/SuiteSparse_config/Config/SuiteSparse_configConfig.cmake.in new file mode 100644 index 0000000000..1831e466ab --- /dev/null +++ b/ThirdParty/SuiteSparse/SuiteSparse_config/Config/SuiteSparse_configConfig.cmake.in @@ -0,0 +1,171 @@ +#------------------------------------------------------------------------------- +# SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparse_configConfig.cmake +#------------------------------------------------------------------------------- + +# The following copyright and license applies to just this file only, not to +# the library itself: +# SuiteSparse_configConfig.cmake, Copyright (c) 2023, Timothy A. Davis. All Rights Reserved. +# SPDX-License-Identifier: BSD-3-clause + +#------------------------------------------------------------------------------- + +# Finds the SuiteSparse_config include file and compiled library. +# The following targets are defined: +# SuiteSparseConfig - for the shared library (if available) +# SuiteSparseConfig_static - for the static library (if available) + +# For backward compatibility the following variables are set: + +# SUITESPARSE_CONFIG_INCLUDE_DIR - where to find SuiteSparse_config.h +# SUITESPARSE_CONFIG_LIBRARY - dynamic SuiteSparse_config library +# SUITESPARSE_CONFIG_STATIC - static SuiteSparse_config library +# SUITESPARSE_CONFIG_LIBRARIES - libraries when using SuiteSparse_config +# SUITESPARSE_CONFIG_FOUND - true if SuiteSparse_config found + +# Set ``CMAKE_MODULE_PATH`` to the parent folder where this module file is +# installed. + +#------------------------------------------------------------------------------- + +@PACKAGE_INIT@ + +set ( SUITESPARSE_DATE "@SUITESPARSE_DATE@" ) +set ( SUITESPARSE_CONFIG_VERSION_MAJOR @SUITESPARSE_VERSION_MAJOR@ ) +set ( SUITESPARSE_CONFIG_VERSION_MINOR @SUITESPARSE_VERSION_MINOR@ ) +set ( SUITESPARSE_CONFIG_VERSION_PATCH @SUITESPARSE_VERSION_SUB@ ) +set ( SUITESPARSE_CONFIG_VERSION "@SUITESPARSE_VERSION_MAJOR@.@SUITESPARSE_VERSION_MINOR@.@SUITESPARSE_VERSION_SUB@" ) + +# Check for dependent targets +include ( CMakeFindDependencyMacro ) +set ( _dependencies_found ON ) + +# Look for OpenMP +if ( @SUITESPARSE_CONFIG_HAS_OPENMP@ AND NOT OpenMP_C_FOUND ) + find_dependency ( OpenMP COMPONENTS C ) + if ( NOT OpenMP_C_FOUND ) + set ( _dependencies_found OFF ) + endif ( ) +endif ( ) + +if ( NOT _dependencies_found ) + set ( SuiteSparse_config_FOUND OFF ) + return ( ) +endif ( ) + + +# Import target +include ( ${CMAKE_CURRENT_LIST_DIR}/SuiteSparse_configTargets.cmake ) + +if ( @SUITESPARSE_CONFIG_HAS_OPENMP@ ) + if ( TARGET SuiteSparse::SuiteSparseConfig ) + get_property ( _suitesparse_config_aliased TARGET SuiteSparse::SuiteSparseConfig + PROPERTY ALIASED_TARGET ) + if ( "${_suitesparse_config_aliased}" STREQUAL "" ) + target_include_directories ( SuiteSparse::SuiteSparseConfig SYSTEM AFTER INTERFACE + "$" ) + else ( ) + target_include_directories ( ${_suitesparse_config_aliased} SYSTEM AFTER INTERFACE + "$" ) + endif ( ) + endif ( ) + if ( TARGET SuiteSparse::SuiteSparseConfig_static ) + get_property ( _suitesparse_config_aliased TARGET SuiteSparse::SuiteSparseConfig_static + PROPERTY ALIASED_TARGET ) + if ( "${_suitesparse_config_aliased}" STREQUAL "" ) + target_include_directories ( SuiteSparse::SuiteSparseConfig_static SYSTEM AFTER INTERFACE + "$" ) + else ( ) + target_include_directories ( ${_suitesparse_config_aliased} SYSTEM AFTER INTERFACE + "$" ) + endif ( ) + endif ( ) +endif ( ) + + +# The following is only for backward compatibility with FindSuiteSparse_config. + +set ( _target_shared SuiteSparse::SuiteSparseConfig ) +set ( _target_static SuiteSparse::SuiteSparseConfig_static ) +set ( _var_prefix "SUITESPARSE_CONFIG" ) + +if ( NOT @BUILD_SHARED_LIBS@ AND NOT TARGET ${_target_shared} ) + # make sure there is always an import target without suffix ) + add_library ( ${_target_shared} ALIAS ${_target_static} ) +endif ( ) + +get_target_property ( ${_var_prefix}_INCLUDE_DIR ${_target_shared} INTERFACE_INCLUDE_DIRECTORIES ) +if ( ${_var_prefix}_INCLUDE_DIR ) + # First item in SuiteSparse targets contains the "main" header directory. + list ( GET ${_var_prefix}_INCLUDE_DIR 0 ${_var_prefix}_INCLUDE_DIR ) +endif ( ) +get_target_property ( ${_var_prefix}_LIBRARY ${_target_shared} IMPORTED_IMPLIB ) +if ( NOT ${_var_prefix}_LIBRARY ) + get_target_property ( _library_chk ${_target_shared} IMPORTED_LOCATION ) + if ( EXISTS ${_library_chk} ) + set ( ${_var_prefix}_LIBRARY ${_library_chk} ) + endif ( ) +endif ( ) +if ( TARGET ${_target_static} ) + get_target_property ( ${_var_prefix}_STATIC ${_target_static} IMPORTED_LOCATION ) +endif ( ) + +# Check for most common build types +set ( _config_types "Debug" "Release" "RelWithDebInfo" "MinSizeRel" "None" ) + +get_property ( _isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG ) +if ( _isMultiConfig ) + # For multi-configuration generators (e.g., Visual Studio), prefer those + # configurations. + list ( PREPEND _config_types ${CMAKE_CONFIGURATION_TYPES} ) +else ( ) + # For single-configuration generators, prefer the current configuration. + list ( PREPEND _config_types ${CMAKE_BUILD_TYPE} ) +endif ( ) + +list ( REMOVE_DUPLICATES _config_types ) + +foreach ( _config ${_config_types} ) + string ( TOUPPER ${_config} _uc_config ) + if ( NOT ${_var_prefix}_LIBRARY ) + get_target_property ( _library_chk ${_target_shared} + IMPORTED_IMPLIB_${_uc_config} ) + if ( EXISTS ${_library_chk} ) + set ( ${_var_prefix}_LIBRARY ${_library_chk} ) + endif ( ) + endif ( ) + if ( NOT ${_var_prefix}_LIBRARY ) + get_target_property ( _library_chk ${_target_shared} + IMPORTED_LOCATION_${_uc_config} ) + if ( EXISTS ${_library_chk} ) + set ( ${_var_prefix}_LIBRARY ${_library_chk} ) + endif ( ) + endif ( ) + if ( TARGET ${_target_static} AND NOT ${_var_prefix}_STATIC ) + get_target_property ( _library_chk ${_target_static} + IMPORTED_LOCATION_${_uc_config} ) + if ( EXISTS ${_library_chk} ) + set ( ${_var_prefix}_STATIC ${_library_chk} ) + endif ( ) + endif ( ) +endforeach ( ) + +set ( SUITESPARSE_CONFIG_LIBRARIES ${SUITESPARSE_CONFIG_LIBRARY} ) + +macro ( suitesparse_check_exist _var _files ) + # ignore generator expressions + string ( GENEX_STRIP "${_files}" _files2 ) + + foreach ( _file ${_files2} ) + if ( NOT EXISTS "${_file}" ) + message ( FATAL_ERROR "File or directory ${_file} referenced by variable ${_var} does not exist!" ) + endif ( ) + endforeach () +endmacro ( ) + +suitesparse_check_exist ( SUITESPARSE_CONFIG_INCLUDE_DIR ${SUITESPARSE_CONFIG_INCLUDE_DIR} ) +suitesparse_check_exist ( SUITESPARSE_CONFIG_LIBRARY ${SUITESPARSE_CONFIG_LIBRARY} ) + +message ( STATUS "SuiteSparse_config version: ${SUITESPARSE_CONFIG_VERSION}" ) +message ( STATUS "SuiteSparse_config include: ${SUITESPARSE_CONFIG_INCLUDE_DIR}" ) +message ( STATUS "SuiteSparse_config library: ${SUITESPARSE_CONFIG_LIBRARY}" ) +message ( STATUS "SuiteSparse_config static: ${SUITESPARSE_CONFIG_STATIC}" ) diff --git a/ThirdParty/SuiteSparse/SuiteSparse_config/Makefile b/ThirdParty/SuiteSparse/SuiteSparse_config/Makefile index 9893afe789..a14ac2fdf4 100644 --- a/ThirdParty/SuiteSparse/SuiteSparse_config/Makefile +++ b/ThirdParty/SuiteSparse/SuiteSparse_config/Makefile @@ -37,18 +37,18 @@ default: library # default is to install only in /usr/local library: - ( cd build && cmake $(CMAKE_OPTIONS) .. && cmake --build . -j${JOBS} ) + ( cd build && cmake $(CMAKE_OPTIONS) .. && cmake --build . --config Release -j${JOBS} ) # install only in SuiteSparse/lib and SuiteSparse/include local: - ( cd build && cmake $(CMAKE_OPTIONS) -DLOCAL_INSTALL=1 .. && cmake --build . -j${JOBS} ) + ( cd build && cmake $(CMAKE_OPTIONS) -USUITESPARSE_PKGFILEDIR -DSUITESPARSE_LOCAL_INSTALL=1 .. && cmake --build . --config Release -j${JOBS} ) # install only in /usr/local (default) global: - ( cd build && cmake $(CMAKE_OPTIONS) -DLOCAL_INSTALL=0 .. && cmake --build . -j${JOBS} ) + ( cd build && cmake $(CMAKE_OPTIONS) -USUITESPARSE_PKGFILEDIR -DSUITESPARSE_LOCAL_INSTALL=0 .. && cmake --build . --config Release -j${JOBS} ) debug: - ( cd build ; cmake $(CMAKE_OPTIONS) -DCMAKE_BUILD_TYPE=Debug .. ; cmake --build . ) + ( cd build && cmake $(CMAKE_OPTIONS) -DCMAKE_BUILD_TYPE=Debug .. ; cmake --build . --config Debug ) all: library @@ -56,14 +56,14 @@ demos: library # just compile after running cmake; do not run cmake again remake: - ( cd build ; cmake --build . ) + ( cd build && cmake --build . ) # just run cmake to set things up setup: - ( cd build ; cmake $(CMAKE_OPTIONS) .. ) + ( cd build && cmake $(CMAKE_OPTIONS) .. ) install: - ( cd build ; cmake --install . ) + ( cd build && cmake --install . ) # remove any installed libraries and #include files uninstall: diff --git a/ThirdParty/SuiteSparse/SuiteSparse_config/README.txt b/ThirdParty/SuiteSparse/SuiteSparse_config/README.txt index 4ff01953f8..9d306c9e2a 100644 --- a/ThirdParty/SuiteSparse/SuiteSparse_config/README.txt +++ b/ThirdParty/SuiteSparse/SuiteSparse_config/README.txt @@ -1,4 +1,4 @@ -SuiteSparse_config, Copyright (c) 2012-2023, Timothy A. Davis. +SuiteSparse_config, Copyright (c) 2012-2024, Timothy A. Davis. All Rights Reserved. SPDX-License-Identifier: BSD-3-clause diff --git a/ThirdParty/SuiteSparse/SuiteSparse_config/SuiteSparse_config.c b/ThirdParty/SuiteSparse/SuiteSparse_config/SuiteSparse_config.c index 079093716a..ee220a77f9 100644 --- a/ThirdParty/SuiteSparse/SuiteSparse_config/SuiteSparse_config.c +++ b/ThirdParty/SuiteSparse/SuiteSparse_config/SuiteSparse_config.c @@ -11,7 +11,6 @@ /* SuiteSparse configuration : memory manager and printf functions. */ -#define SUITESPARSE_LIBRARY #include "SuiteSparse_config.h" /* -------------------------------------------------------------------------- */ @@ -496,7 +495,7 @@ void *SuiteSparse_free /* always returns NULL */ tic [1] = 0 ; } -#else +#else /* ---------------------------------------------------------------------- */ /* POSIX timer */ @@ -617,7 +616,7 @@ double SuiteSparse_hypot (double x, double y) r = x / y ; s = y * sqrt (1.0 + r*r) ; } - } + } return (s) ; } @@ -760,6 +759,10 @@ const char *SuiteSparse_BLAS_library ( void ) return ((sizeof (SUITESPARSE_BLAS_INT) == 8) ? "OpenBLAS (64-bit integers)" : "OpenBLAS (32-bit integers)") ; + #elif defined ( BLAS_FLAME ) + return ((sizeof (SUITESPARSE_BLAS_INT) == 8) ? + "FLAME (64-bit integers)" : + "FLAME (32-bit integers)") ; #elif defined ( BLAS_Generic ) return ((sizeof (SUITESPARSE_BLAS_INT) == 8) ? "Reference BLAS (64-bit integers)" : diff --git a/ThirdParty/SuiteSparse/SuiteSparse_config/SuiteSparse_config.h b/ThirdParty/SuiteSparse/SuiteSparse_config/SuiteSparse_config.h index 2b917bdeda..d2300dcfc6 100644 --- a/ThirdParty/SuiteSparse/SuiteSparse_config/SuiteSparse_config.h +++ b/ThirdParty/SuiteSparse/SuiteSparse_config/SuiteSparse_config.h @@ -368,13 +368,24 @@ int SuiteSparse_divcomplex // determine which timer to use, if any #ifndef NTIMER + // SuiteSparse_config itself can be compiled without OpenMP, + // but other packages can themselves use OpenMP. In this case, + // those packages should use omp_get_wtime() directly. This can + // be done via the SUITESPARSE_TIME macro, defined below: + #define SUITESPARSE_HAVE_CLOCK_GETTIME #if defined ( _OPENMP ) #define SUITESPARSE_TIMER_ENABLED - #elif defined ( _POSIX_C_SOURCE ) - #if _POSIX_C_SOURCE >= 199309L + #define SUITESPARSE_TIME (omp_get_wtime ( )) + #elif defined ( SUITESPARSE_HAVE_CLOCK_GETTIME ) #define SUITESPARSE_TIMER_ENABLED - #endif + #define SUITESPARSE_TIME (SuiteSparse_time ( )) + #else + // No timer is available + #define SUITESPARSE_TIME (0) #endif +#else + // The timer is explictly disabled + #define SUITESPARSE_TIME (0) #endif // SuiteSparse printf macro @@ -409,14 +420,19 @@ int SuiteSparse_version // returns SUITESPARSE_VERSION #define SUITESPARSE_HAS_VERSION_FUNCTION -#define SUITESPARSE_DATE "Jan 20, 2023" +#define SUITESPARSE_DATE "Jan 20, 2024" #define SUITESPARSE_MAIN_VERSION 7 -#define SUITESPARSE_SUB_VERSION 0 -#define SUITESPARSE_SUBSUB_VERSION 1 +#define SUITESPARSE_SUB_VERSION 6 +#define SUITESPARSE_SUBSUB_VERSION 0 +// version format x.y #define SUITESPARSE_VER_CODE(main,sub) ((main) * 1000 + (sub)) -#define SUITESPARSE_VERSION \ - SUITESPARSE_VER_CODE(SUITESPARSE_MAIN_VERSION,SUITESPARSE_SUB_VERSION) +#define SUITESPARSE_VERSION SUITESPARSE_VER_CODE(7, 6) + +// version format x.y.z +#define SUITESPARSE__VERCODE(main,sub,patch) \ + (((main)*1000ULL + (sub))*1000ULL + (patch)) +#define SUITESPARSE__VERSION SUITESPARSE__VERCODE(7,6,0) //============================================================================== // SuiteSparse interface to the BLAS and LAPACK libraries @@ -469,7 +485,7 @@ int SuiteSparse_version // returns SUITESPARSE_VERSION #elif defined ( BLAS_UNDERSCORE ) - // append an undescore, use lower case + // append an underscore, use lower case #define SUITESPARSE_FORTRAN(name,NAME) name ## _ #define SUITESPARSE__FORTRAN(name,NAME) name ## _ @@ -529,12 +545,12 @@ int SuiteSparse_version // returns SUITESPARSE_VERSION // If the suffix does not contain "_", use (Sun Perf., for example): -// cd build ; cmake -DBLAS64_SUFFIX="64" .. +// cd build && cmake -DBLAS64_SUFFIX="64" .. // If the suffix contains "_" (OpenBLAS in spack for example), use the // following: -// cd build ; cmake -DBLAS64_SUFFIX="_64" .. +// cd build && cmake -DBLAS64_SUFFIX="_64" .. // This setting could be used by the spack packaging of SuiteSparse when linked // with the spack-installed OpenBLAS with 64-bit integers. See @@ -572,6 +588,7 @@ int SuiteSparse_version // returns SUITESPARSE_VERSION // C names of Fortan BLAS and LAPACK functions used by SuiteSparse //------------------------------------------------------------------------------ +// double #define SUITESPARSE_BLAS_DTRSV SUITESPARSE_BLAS ( dtrsv , DTRSV ) #define SUITESPARSE_BLAS_DGEMV SUITESPARSE_BLAS ( dgemv , DGEMV ) #define SUITESPARSE_BLAS_DTRSM SUITESPARSE_BLAS ( dtrsm , DTRSM ) @@ -579,8 +596,15 @@ int SuiteSparse_version // returns SUITESPARSE_VERSION #define SUITESPARSE_BLAS_DSYRK SUITESPARSE_BLAS ( dsyrk , DSYRK ) #define SUITESPARSE_BLAS_DGER SUITESPARSE_BLAS ( dger , DGER ) #define SUITESPARSE_BLAS_DSCAL SUITESPARSE_BLAS ( dscal , DSCAL ) +#define SUITESPARSE_BLAS_DNRM2 SUITESPARSE_BLAS ( dnrm2 , DNRM2 ) + #define SUITESPARSE_LAPACK_DPOTRF SUITESPARSE_BLAS ( dpotrf , DPOTRF ) +#define SUITESPARSE_LAPACK_DLARF SUITESPARSE_BLAS ( dlarf , DLARF ) +#define SUITESPARSE_LAPACK_DLARFG SUITESPARSE_BLAS ( dlarfg , DLARFG ) +#define SUITESPARSE_LAPACK_DLARFT SUITESPARSE_BLAS ( dlarft , DLARFT ) +#define SUITESPARSE_LAPACK_DLARFB SUITESPARSE_BLAS ( dlarfb , DLARFB ) +// double complex #define SUITESPARSE_BLAS_ZTRSV SUITESPARSE_BLAS ( ztrsv , ZTRSV ) #define SUITESPARSE_BLAS_ZGEMV SUITESPARSE_BLAS ( zgemv , ZGEMV ) #define SUITESPARSE_BLAS_ZTRSM SUITESPARSE_BLAS ( ztrsm , ZTRSM ) @@ -588,20 +612,46 @@ int SuiteSparse_version // returns SUITESPARSE_VERSION #define SUITESPARSE_BLAS_ZHERK SUITESPARSE_BLAS ( zherk , ZHERK ) #define SUITESPARSE_BLAS_ZGERU SUITESPARSE_BLAS ( zgeru , ZGERU ) #define SUITESPARSE_BLAS_ZSCAL SUITESPARSE_BLAS ( zscal , ZSCAL ) -#define SUITESPARSE_LAPACK_ZPOTRF SUITESPARSE_BLAS ( zpotrf , ZPOTRF ) - -#define SUITESPARSE_BLAS_DNRM2 SUITESPARSE_BLAS ( dnrm2 , DNRM2 ) -#define SUITESPARSE_LAPACK_DLARF SUITESPARSE_BLAS ( dlarf , DLARF ) -#define SUITESPARSE_LAPACK_DLARFG SUITESPARSE_BLAS ( dlarfg , DLARFG ) -#define SUITESPARSE_LAPACK_DLARFT SUITESPARSE_BLAS ( dlarft , DLARFT ) -#define SUITESPARSE_LAPACK_DLARFB SUITESPARSE_BLAS ( dlarfb , DLARFB ) - #define SUITESPARSE_BLAS_DZNRM2 SUITESPARSE_BLAS ( dznrm2 , DZNRM2 ) + +#define SUITESPARSE_LAPACK_ZPOTRF SUITESPARSE_BLAS ( zpotrf , ZPOTRF ) #define SUITESPARSE_LAPACK_ZLARF SUITESPARSE_BLAS ( zlarf , ZLARF ) #define SUITESPARSE_LAPACK_ZLARFG SUITESPARSE_BLAS ( zlarfg , ZLARFG ) #define SUITESPARSE_LAPACK_ZLARFT SUITESPARSE_BLAS ( zlarft , ZLARFT ) #define SUITESPARSE_LAPACK_ZLARFB SUITESPARSE_BLAS ( zlarfb , ZLARFB ) +// single +#define SUITESPARSE_BLAS_STRSV SUITESPARSE_BLAS ( strsv , STRSV ) +#define SUITESPARSE_BLAS_SGEMV SUITESPARSE_BLAS ( sgemv , SGEMV ) +#define SUITESPARSE_BLAS_STRSM SUITESPARSE_BLAS ( strsm , STRSM ) +#define SUITESPARSE_BLAS_SGEMM SUITESPARSE_BLAS ( sgemm , SGEMM ) +#define SUITESPARSE_BLAS_SSYRK SUITESPARSE_BLAS ( ssyrk , SSYRK ) +#define SUITESPARSE_BLAS_SGER SUITESPARSE_BLAS ( sger , SGER ) +#define SUITESPARSE_BLAS_SSCAL SUITESPARSE_BLAS ( sscal , SSCAL ) +#define SUITESPARSE_BLAS_SNRM2 SUITESPARSE_BLAS ( snrm2 , SNRM2 ) + +#define SUITESPARSE_LAPACK_SPOTRF SUITESPARSE_BLAS ( spotrf , SPOTRF ) +#define SUITESPARSE_LAPACK_SLARF SUITESPARSE_BLAS ( slarf , SLARF ) +#define SUITESPARSE_LAPACK_SLARFG SUITESPARSE_BLAS ( slarfg , SLARFG ) +#define SUITESPARSE_LAPACK_SLARFT SUITESPARSE_BLAS ( slarft , SLARFT ) +#define SUITESPARSE_LAPACK_SLARFB SUITESPARSE_BLAS ( slarfb , SLARFB ) + +// single complex +#define SUITESPARSE_BLAS_CTRSV SUITESPARSE_BLAS ( ctrsv , CTRSV ) +#define SUITESPARSE_BLAS_CGEMV SUITESPARSE_BLAS ( cgemv , CGEMV ) +#define SUITESPARSE_BLAS_CTRSM SUITESPARSE_BLAS ( ctrsm , CTRSM ) +#define SUITESPARSE_BLAS_CGEMM SUITESPARSE_BLAS ( cgemm , CGEMM ) +#define SUITESPARSE_BLAS_CHERK SUITESPARSE_BLAS ( cherk , CHERK ) +#define SUITESPARSE_BLAS_CGERU SUITESPARSE_BLAS ( cgeru , CGERU ) +#define SUITESPARSE_BLAS_CSCAL SUITESPARSE_BLAS ( cscal , CSCAL ) +#define SUITESPARSE_BLAS_SCNRM2 SUITESPARSE_BLAS ( scnrm2 , SCNRM2 ) + +#define SUITESPARSE_LAPACK_CPOTRF SUITESPARSE_BLAS ( cpotrf , CPOTRF ) +#define SUITESPARSE_LAPACK_CLARF SUITESPARSE_BLAS ( clarf , CLARF ) +#define SUITESPARSE_LAPACK_CLARFG SUITESPARSE_BLAS ( clarfg , CLARFG ) +#define SUITESPARSE_LAPACK_CLARFT SUITESPARSE_BLAS ( clarft , CLARFT ) +#define SUITESPARSE_LAPACK_CLARFB SUITESPARSE_BLAS ( clarfb , CLARFB ) + //------------------------------------------------------------------------------ // prototypes of BLAS and SUITESPARSE_LAPACK functions //------------------------------------------------------------------------------ @@ -627,7 +677,11 @@ int SuiteSparse_version // returns SUITESPARSE_VERSION #if defined ( SUITESPARSE_BLAS_DEFINITIONS ) -void SUITESPARSE_BLAS_DGEMV // Y = alpha*A*x + beta*Y +//------------------------------------------------------------------------------ +// gemv: Y = alpha*A*x + beta*Y +//------------------------------------------------------------------------------ + +void SUITESPARSE_BLAS_DGEMV ( // input: const char *trans, @@ -659,7 +713,39 @@ void SUITESPARSE_BLAS_DGEMV // Y = alpha*A*x + beta*Y } \ } -void SUITESPARSE_BLAS_ZGEMV // Y = alpha*A*X + beta*Y +void SUITESPARSE_BLAS_SGEMV +( + // input: + const char *trans, + const SUITESPARSE_BLAS_INT *m, + const SUITESPARSE_BLAS_INT *n, + const float *alpha, + const float *A, + const SUITESPARSE_BLAS_INT *lda, + const float *X, + const SUITESPARSE_BLAS_INT *incx, + const float *beta, + // input/output: + float *Y, + // input: + const SUITESPARSE_BLAS_INT *incy +) ; + +#define SUITESPARSE_BLAS_sgemv(trans,m,n,alpha,A,lda,X,incx,beta,Y,incy,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (M_blas_int, m, ok) ; \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCX_blas_int, incx, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCY_blas_int, incy, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_BLAS_SGEMV (trans, &M_blas_int, &N_blas_int, alpha, A, \ + &LDA_blas_int, X, &INCX_blas_int, beta, Y, &INCY_blas_int) ; \ + } \ +} + +void SUITESPARSE_BLAS_ZGEMV ( // input: const char *trans, @@ -691,7 +777,43 @@ void SUITESPARSE_BLAS_ZGEMV // Y = alpha*A*X + beta*Y } \ } -void SUITESPARSE_BLAS_DTRSV // solve Lx=b, Ux=b, L'x=b, or U'x=b +void SUITESPARSE_BLAS_CGEMV +( + // input: + const char *trans, + const SUITESPARSE_BLAS_INT *m, + const SUITESPARSE_BLAS_INT *n, + const void *alpha, + const void *A, + const SUITESPARSE_BLAS_INT *lda, + const void *X, + const SUITESPARSE_BLAS_INT *incx, + const void *beta, + // input/output: + void *Y, + // input: + const SUITESPARSE_BLAS_INT *incy +) ; + +#define SUITESPARSE_BLAS_cgemv(trans,m,n,alpha,A,lda,X,incx,beta,Y,incy,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (M_blas_int, m, ok) ; \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCX_blas_int, incx, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCY_blas_int, incy, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_BLAS_CGEMV (trans, &M_blas_int, &N_blas_int, alpha, A, \ + &LDA_blas_int, X, &INCX_blas_int, beta, Y, &INCY_blas_int) ; \ + } \ +} + +//------------------------------------------------------------------------------ +// trsv: solve Lx=b, Ux=b, L'x=b, or U'x=b +//------------------------------------------------------------------------------ + +void SUITESPARSE_BLAS_DTRSV ( // input: const char *uplo, @@ -718,7 +840,34 @@ void SUITESPARSE_BLAS_DTRSV // solve Lx=b, Ux=b, L'x=b, or U'x=b } \ } -void SUITESPARSE_BLAS_ZTRSV // solve (L, L', L^H, U, U', or U^H)x=b +void SUITESPARSE_BLAS_STRSV +( + // input: + const char *uplo, + const char *trans, + const char *diag, + const SUITESPARSE_BLAS_INT *n, + const float *A, + const SUITESPARSE_BLAS_INT *lda, + // input/output: + float *X, + // input: + const SUITESPARSE_BLAS_INT *incx +) ; + +#define SUITESPARSE_BLAS_strsv(uplo,trans,diag,n,A,lda,X,incx,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCX_blas_int, incx, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_BLAS_STRSV (uplo, trans, diag, &N_blas_int, A, \ + &LDA_blas_int, X, &INCX_blas_int) ; \ + } \ +} + +void SUITESPARSE_BLAS_ZTRSV ( // input: const char *uplo, @@ -745,7 +894,38 @@ void SUITESPARSE_BLAS_ZTRSV // solve (L, L', L^H, U, U', or U^H)x=b } \ } -void SUITESPARSE_BLAS_DTRSM // solve LX=B, UX=B, L'X=B, or U'X=B +void SUITESPARSE_BLAS_CTRSV +( + // input: + const char *uplo, + const char *trans, + const char *diag, + const SUITESPARSE_BLAS_INT *n, + const void *A, + const SUITESPARSE_BLAS_INT *lda, + // input/output: + void *X, + // input: + const SUITESPARSE_BLAS_INT *incx +) ; + +#define SUITESPARSE_BLAS_ctrsv(uplo,trans,diag,n,A,lda,X,incx,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCX_blas_int, incx, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_BLAS_CTRSV (uplo, trans, diag, &N_blas_int, A, \ + &LDA_blas_int, X, &INCX_blas_int) ; \ + } \ +} + +//------------------------------------------------------------------------------ +// trsm: solve LX=B, UX=B, L'X=B, or U'X=B +//------------------------------------------------------------------------------ + +void SUITESPARSE_BLAS_DTRSM ( // input: const char *side, @@ -776,7 +956,38 @@ void SUITESPARSE_BLAS_DTRSM // solve LX=B, UX=B, L'X=B, or U'X=B } \ } -void SUITESPARSE_BLAS_ZTRSM // solve (L, L', L^H, U, U', or U^H)X=B +void SUITESPARSE_BLAS_STRSM +( + // input: + const char *side, + const char *uplo, + const char *transa, + const char *diag, + const SUITESPARSE_BLAS_INT *m, + const SUITESPARSE_BLAS_INT *n, + const float *alpha, + const float *A, + const SUITESPARSE_BLAS_INT *lda, + // input/output: + float *B, + // input: + const SUITESPARSE_BLAS_INT *ldb +) ; + +#define SUITESPARSE_BLAS_strsm(side,uplo,transa,diag,m,n,alpha,A,lda,B,ldb,ok)\ +{ \ + SUITESPARSE_TO_BLAS_INT (M_blas_int, m, ok) ; \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDB_blas_int, ldb, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_BLAS_STRSM (side, uplo, transa, diag, &M_blas_int, \ + &N_blas_int, alpha, A, &LDA_blas_int, B, &LDB_blas_int) ; \ + } \ +} + +void SUITESPARSE_BLAS_ZTRSM ( // input: const char *side, @@ -807,7 +1018,42 @@ void SUITESPARSE_BLAS_ZTRSM // solve (L, L', L^H, U, U', or U^H)X=B } \ } -void SUITESPARSE_BLAS_DGEMM // C = alpha*A*B + beta*C +void SUITESPARSE_BLAS_CTRSM +( + // input: + const char *side, + const char *uplo, + const char *transa, + const char *diag, + const SUITESPARSE_BLAS_INT *m, + const SUITESPARSE_BLAS_INT *n, + const void *alpha, + const void *A, + const SUITESPARSE_BLAS_INT *lda, + // input/output: + void *B, + // input: + const SUITESPARSE_BLAS_INT *ldb +) ; + +#define SUITESPARSE_BLAS_ctrsm(side,uplo,transa,diag,m,n,alpha,A,lda,B,ldb,ok)\ +{ \ + SUITESPARSE_TO_BLAS_INT (M_blas_int, m, ok) ; \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDB_blas_int, ldb, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_BLAS_CTRSM (side, uplo, transa, diag, &M_blas_int, \ + &N_blas_int, alpha, A, &LDA_blas_int, B, &LDB_blas_int) ; \ + } \ +} + +//------------------------------------------------------------------------------ +// gemm: C = alpha*A*B + beta*C +//------------------------------------------------------------------------------ + +void SUITESPARSE_BLAS_DGEMM ( // input: const char *transa, @@ -844,7 +1090,7 @@ void SUITESPARSE_BLAS_DGEMM // C = alpha*A*B + beta*C } \ } -void SUITESPARSE_BLAS_ZGEMM // C = alpha*A*B + beta*C +void SUITESPARSE_BLAS_SGEMM ( // input: const char *transa, @@ -852,19 +1098,19 @@ void SUITESPARSE_BLAS_ZGEMM // C = alpha*A*B + beta*C const SUITESPARSE_BLAS_INT *m, const SUITESPARSE_BLAS_INT *n, const SUITESPARSE_BLAS_INT *k, - const void *alpha, - const void *A, + const float *alpha, + const float *A, const SUITESPARSE_BLAS_INT *lda, - const void *B, + const float *B, const SUITESPARSE_BLAS_INT *ldb, - const void *beta, + const float *beta, // input/output: - void *C, + float *C, // input: const SUITESPARSE_BLAS_INT *ldc ) ; -#define SUITESPARSE_BLAS_zgemm(transa,transb,m,n,k,alpha,A,lda,B,ldb,beta, \ +#define SUITESPARSE_BLAS_sgemm(transa,transb,m,n,k,alpha,A,lda,B,ldb,beta, \ C,ldc,ok) \ { \ SUITESPARSE_TO_BLAS_INT (M_blas_int, m, ok) ; \ @@ -875,52 +1121,62 @@ void SUITESPARSE_BLAS_ZGEMM // C = alpha*A*B + beta*C SUITESPARSE_TO_BLAS_INT (LDC_blas_int, ldc, ok) ; \ if (ok) \ { \ - SUITESPARSE_BLAS_ZGEMM (transa, transb, &M_blas_int, &N_blas_int, \ + SUITESPARSE_BLAS_SGEMM (transa, transb, &M_blas_int, &N_blas_int, \ &K_blas_int, alpha, A, &LDA_blas_int, B, &LDB_blas_int, beta, C, \ &LDC_blas_int) ; \ } \ } -void SUITESPARSE_BLAS_DSYRK // C = alpha*A*A' + beta*C, or A'A +void SUITESPARSE_BLAS_ZGEMM ( // input: - const char *uplo, - const char *trans, + const char *transa, + const char *transb, + const SUITESPARSE_BLAS_INT *m, const SUITESPARSE_BLAS_INT *n, const SUITESPARSE_BLAS_INT *k, - const double *alpha, - const double *A, + const void *alpha, + const void *A, const SUITESPARSE_BLAS_INT *lda, - const double *beta, + const void *B, + const SUITESPARSE_BLAS_INT *ldb, + const void *beta, // input/output: - double *C, + void *C, // input: const SUITESPARSE_BLAS_INT *ldc ) ; -#define SUITESPARSE_BLAS_dsyrk(uplo,trans,n,k,alpha,A,lda,beta,C,ldc,ok) \ +#define SUITESPARSE_BLAS_zgemm(transa,transb,m,n,k,alpha,A,lda,B,ldb,beta, \ + C,ldc,ok) \ { \ + SUITESPARSE_TO_BLAS_INT (M_blas_int, m, ok) ; \ SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ SUITESPARSE_TO_BLAS_INT (K_blas_int, k, ok) ; \ SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDB_blas_int, ldb, ok) ; \ SUITESPARSE_TO_BLAS_INT (LDC_blas_int, ldc, ok) ; \ if (ok) \ { \ - SUITESPARSE_BLAS_DSYRK (uplo, trans, &N_blas_int, &K_blas_int, alpha, \ - A, &LDA_blas_int, beta, C, &LDC_blas_int) ; \ + SUITESPARSE_BLAS_ZGEMM (transa, transb, &M_blas_int, &N_blas_int, \ + &K_blas_int, alpha, A, &LDA_blas_int, B, &LDB_blas_int, beta, C, \ + &LDC_blas_int) ; \ } \ } -void SUITESPARSE_BLAS_ZHERK // C = alpha*A*A^H + beta*C, or A^H*A +void SUITESPARSE_BLAS_CGEMM ( // input: - const char *uplo, - const char *trans, + const char *transa, + const char *transb, + const SUITESPARSE_BLAS_INT *m, const SUITESPARSE_BLAS_INT *n, const SUITESPARSE_BLAS_INT *k, const void *alpha, const void *A, const SUITESPARSE_BLAS_INT *lda, + const void *B, + const SUITESPARSE_BLAS_INT *ldb, const void *beta, // input/output: void *C, @@ -928,47 +1184,206 @@ void SUITESPARSE_BLAS_ZHERK // C = alpha*A*A^H + beta*C, or A^H*A const SUITESPARSE_BLAS_INT *ldc ) ; -#define SUITESPARSE_BLAS_zherk(uplo,trans,n,k,alpha,A,lda,beta,C,ldc,ok) \ +#define SUITESPARSE_BLAS_cgemm(transa,transb,m,n,k,alpha,A,lda,B,ldb,beta, \ + C,ldc,ok) \ { \ + SUITESPARSE_TO_BLAS_INT (M_blas_int, m, ok) ; \ SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ SUITESPARSE_TO_BLAS_INT (K_blas_int, k, ok) ; \ SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDB_blas_int, ldb, ok) ; \ SUITESPARSE_TO_BLAS_INT (LDC_blas_int, ldc, ok) ; \ if (ok) \ { \ - SUITESPARSE_BLAS_ZHERK (uplo, trans, &N_blas_int, &K_blas_int, alpha, \ - A, &LDA_blas_int, beta, C, &LDC_blas_int) ; \ + SUITESPARSE_BLAS_CGEMM (transa, transb, &M_blas_int, &N_blas_int, \ + &K_blas_int, alpha, A, &LDA_blas_int, B, &LDB_blas_int, beta, C, \ + &LDC_blas_int) ; \ } \ } -void SUITESPARSE_LAPACK_DPOTRF // Cholesky factorization +//------------------------------------------------------------------------------ +// syrk/herk: C = alpha*A*A' + beta*C ; or C = alpha*A'*A + beta*C +//------------------------------------------------------------------------------ + +void SUITESPARSE_BLAS_DSYRK ( // input: const char *uplo, + const char *trans, const SUITESPARSE_BLAS_INT *n, + const SUITESPARSE_BLAS_INT *k, + const double *alpha, + const double *A, + const SUITESPARSE_BLAS_INT *lda, + const double *beta, // input/output: - double *A, + double *C, // input: - const SUITESPARSE_BLAS_INT *lda, - // output: - SUITESPARSE_BLAS_INT *info + const SUITESPARSE_BLAS_INT *ldc ) ; -#define SUITESPARSE_LAPACK_dpotrf(uplo,n,A,lda,info,ok) \ +#define SUITESPARSE_BLAS_dsyrk(uplo,trans,n,k,alpha,A,lda,beta,C,ldc,ok) \ { \ SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (K_blas_int, k, ok) ; \ SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ - info = 1 ; \ + SUITESPARSE_TO_BLAS_INT (LDC_blas_int, ldc, ok) ; \ if (ok) \ { \ - SUITESPARSE_BLAS_INT LAPACK_Info = -999 ; \ - SUITESPARSE_LAPACK_DPOTRF (uplo, &N_blas_int, A, &LDA_blas_int, \ - &LAPACK_Info) ; \ - info = (Int) LAPACK_Info ; \ + SUITESPARSE_BLAS_DSYRK (uplo, trans, &N_blas_int, &K_blas_int, alpha, \ + A, &LDA_blas_int, beta, C, &LDC_blas_int) ; \ } \ } -void SUITESPARSE_LAPACK_ZPOTRF // Cholesky factorization +void SUITESPARSE_BLAS_SSYRK +( + // input: + const char *uplo, + const char *trans, + const SUITESPARSE_BLAS_INT *n, + const SUITESPARSE_BLAS_INT *k, + const float *alpha, + const float *A, + const SUITESPARSE_BLAS_INT *lda, + const float *beta, + // input/output: + float *C, + // input: + const SUITESPARSE_BLAS_INT *ldc +) ; + +#define SUITESPARSE_BLAS_ssyrk(uplo,trans,n,k,alpha,A,lda,beta,C,ldc,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (K_blas_int, k, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDC_blas_int, ldc, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_BLAS_SSYRK (uplo, trans, &N_blas_int, &K_blas_int, alpha, \ + A, &LDA_blas_int, beta, C, &LDC_blas_int) ; \ + } \ +} + +void SUITESPARSE_BLAS_ZHERK +( + // input: + const char *uplo, + const char *trans, + const SUITESPARSE_BLAS_INT *n, + const SUITESPARSE_BLAS_INT *k, + const void *alpha, + const void *A, + const SUITESPARSE_BLAS_INT *lda, + const void *beta, + // input/output: + void *C, + // input: + const SUITESPARSE_BLAS_INT *ldc +) ; + +#define SUITESPARSE_BLAS_zherk(uplo,trans,n,k,alpha,A,lda,beta,C,ldc,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (K_blas_int, k, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDC_blas_int, ldc, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_BLAS_ZHERK (uplo, trans, &N_blas_int, &K_blas_int, alpha, \ + A, &LDA_blas_int, beta, C, &LDC_blas_int) ; \ + } \ +} + +void SUITESPARSE_BLAS_CHERK +( + // input: + const char *uplo, + const char *trans, + const SUITESPARSE_BLAS_INT *n, + const SUITESPARSE_BLAS_INT *k, + const void *alpha, + const void *A, + const SUITESPARSE_BLAS_INT *lda, + const void *beta, + // input/output: + void *C, + // input: + const SUITESPARSE_BLAS_INT *ldc +) ; + +#define SUITESPARSE_BLAS_cherk(uplo,trans,n,k,alpha,A,lda,beta,C,ldc,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (K_blas_int, k, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDC_blas_int, ldc, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_BLAS_CHERK (uplo, trans, &N_blas_int, &K_blas_int, alpha, \ + A, &LDA_blas_int, beta, C, &LDC_blas_int) ; \ + } \ +} + +//------------------------------------------------------------------------------ +// potrf: Cholesky factorization +//------------------------------------------------------------------------------ + +void SUITESPARSE_LAPACK_DPOTRF +( + // input: + const char *uplo, + const SUITESPARSE_BLAS_INT *n, + // input/output: + double *A, + // input: + const SUITESPARSE_BLAS_INT *lda, + // output: + SUITESPARSE_BLAS_INT *info +) ; + +#define SUITESPARSE_LAPACK_dpotrf(uplo,n,A,lda,info,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ + info = 1 ; \ + if (ok) \ + { \ + SUITESPARSE_BLAS_INT LAPACK_Info = -999 ; \ + SUITESPARSE_LAPACK_DPOTRF (uplo, &N_blas_int, A, &LDA_blas_int, \ + &LAPACK_Info) ; \ + info = (Int) LAPACK_Info ; \ + } \ +} + +void SUITESPARSE_LAPACK_SPOTRF +( + // input: + const char *uplo, + const SUITESPARSE_BLAS_INT *n, + // input/output: + float *A, + // input: + const SUITESPARSE_BLAS_INT *lda, + // output: + SUITESPARSE_BLAS_INT *info +) ; + +#define SUITESPARSE_LAPACK_spotrf(uplo,n,A,lda,info,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ + info = 1 ; \ + if (ok) \ + { \ + SUITESPARSE_BLAS_INT LAPACK_Info = -999 ; \ + SUITESPARSE_LAPACK_SPOTRF (uplo, &N_blas_int, A, &LDA_blas_int, \ + &LAPACK_Info) ; \ + info = (Int) LAPACK_Info ; \ + } \ +} + +void SUITESPARSE_LAPACK_ZPOTRF ( // input: const char *uplo, @@ -995,7 +1410,38 @@ void SUITESPARSE_LAPACK_ZPOTRF // Cholesky factorization } \ } -void SUITESPARSE_BLAS_DSCAL // Y = alpha*Y +void SUITESPARSE_LAPACK_CPOTRF +( + // input: + const char *uplo, + const SUITESPARSE_BLAS_INT *n, + // input/output: + void *A, + // input: + const SUITESPARSE_BLAS_INT *lda, + // output: + SUITESPARSE_BLAS_INT *info +) ; + +#define SUITESPARSE_LAPACK_cpotrf(uplo,n,A,lda,info,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ + info = 1 ; \ + if (ok) \ + { \ + SUITESPARSE_BLAS_INT LAPACK_Info = -999 ; \ + SUITESPARSE_LAPACK_CPOTRF (uplo, &N_blas_int, A, &LDA_blas_int, \ + &LAPACK_Info) ; \ + info = LAPACK_Info ; \ + } \ +} + +//------------------------------------------------------------------------------ +// scal: Y = alpha*Y +//------------------------------------------------------------------------------ + +void SUITESPARSE_BLAS_DSCAL ( // input: const SUITESPARSE_BLAS_INT *n, @@ -1016,7 +1462,28 @@ void SUITESPARSE_BLAS_DSCAL // Y = alpha*Y } \ } -void SUITESPARSE_BLAS_ZSCAL // Y = alpha*Y +void SUITESPARSE_BLAS_SSCAL +( + // input: + const SUITESPARSE_BLAS_INT *n, + const float *alpha, + // input/output: + float *Y, + // input: + const SUITESPARSE_BLAS_INT *incy +) ; + +#define SUITESPARSE_BLAS_sscal(n,alpha,Y,incy,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCY_blas_int, incy, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_BLAS_SSCAL (&N_blas_int, alpha, Y, &INCY_blas_int) ; \ + } \ +} + +void SUITESPARSE_BLAS_ZSCAL ( // input: const SUITESPARSE_BLAS_INT *n, @@ -1037,7 +1504,32 @@ void SUITESPARSE_BLAS_ZSCAL // Y = alpha*Y } \ } -void SUITESPARSE_BLAS_DGER // A = alpha*x*y' + A +void SUITESPARSE_BLAS_CSCAL +( + // input: + const SUITESPARSE_BLAS_INT *n, + const void *alpha, + // input/output: + void *Y, + // input: + const SUITESPARSE_BLAS_INT *incy +) ; + +#define SUITESPARSE_BLAS_cscal(n,alpha,Y,incy,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCY_blas_int, incy, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_BLAS_CSCAL (&N_blas_int, alpha, Y, &INCY_blas_int) ; \ + } \ +} + +//------------------------------------------------------------------------------ +// ger/geru: A = alpha*x*y' + A +//------------------------------------------------------------------------------ + +void SUITESPARSE_BLAS_DGER ( // input: const SUITESPARSE_BLAS_INT *m, @@ -1067,7 +1559,37 @@ void SUITESPARSE_BLAS_DGER // A = alpha*x*y' + A } \ } -void SUITESPARSE_BLAS_ZGERU // A = alpha*x*y' + A +void SUITESPARSE_BLAS_SGER +( + // input: + const SUITESPARSE_BLAS_INT *m, + const SUITESPARSE_BLAS_INT *n, + const float *alpha, + const float *X, + const SUITESPARSE_BLAS_INT *incx, + const float *Y, + const SUITESPARSE_BLAS_INT *incy, + // input/output: + float *A, + // input: + const SUITESPARSE_BLAS_INT *lda +) ; + +#define SUITESPARSE_BLAS_sger(m,n,alpha,X,incx,Y,incy,A,lda,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (M_blas_int, m, ok) ; \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCX_blas_int, incx, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCY_blas_int, incy, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_BLAS_SGER (&M_blas_int, &N_blas_int, alpha, X, \ + &INCX_blas_int, Y, &INCY_blas_int, A, &LDA_blas_int) ; \ + } \ +} + +void SUITESPARSE_BLAS_ZGERU ( // input: const SUITESPARSE_BLAS_INT *m, @@ -1097,7 +1619,41 @@ void SUITESPARSE_BLAS_ZGERU // A = alpha*x*y' + A } \ } -void SUITESPARSE_LAPACK_DLARFT // T = block Householder factor +void SUITESPARSE_BLAS_CGERU +( + // input: + const SUITESPARSE_BLAS_INT *m, + const SUITESPARSE_BLAS_INT *n, + const void *alpha, + const void *X, + const SUITESPARSE_BLAS_INT *incx, + const void *Y, + const SUITESPARSE_BLAS_INT *incy, + // input/output: + void *A, + // input: + const SUITESPARSE_BLAS_INT *lda +) ; + +#define SUITESPARSE_BLAS_cgeru(m,n,alpha,X,incx,Y,incy,A,lda,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (M_blas_int, m, ok) ; \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCX_blas_int, incx, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCY_blas_int, incy, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDA_blas_int, lda, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_BLAS_CGERU (&M_blas_int, &N_blas_int, alpha, X, \ + &INCX_blas_int, Y, &INCY_blas_int, A, &LDA_blas_int) ; \ + } \ +} + +//------------------------------------------------------------------------------ +// larft: T = block Householder factor +//------------------------------------------------------------------------------ + +void SUITESPARSE_LAPACK_DLARFT ( // input: const char *direct, @@ -1126,7 +1682,36 @@ void SUITESPARSE_LAPACK_DLARFT // T = block Householder factor } \ } -void SUITESPARSE_LAPACK_ZLARFT // T = block Householder factor +void SUITESPARSE_LAPACK_SLARFT +( + // input: + const char *direct, + const char *storev, + const SUITESPARSE_BLAS_INT *n, + const SUITESPARSE_BLAS_INT *k, + const float *V, + const SUITESPARSE_BLAS_INT *ldv, + const float *Tau, + // output: + float *T, + // input: + const SUITESPARSE_BLAS_INT *ldt +) ; + +#define SUITESPARSE_LAPACK_slarft(direct,storev,n,k,V,ldv,Tau,T,ldt,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (K_blas_int, k, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDV_blas_int, ldv, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDT_blas_int, ldt, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_LAPACK_SLARFT (direct, storev, &N_blas_int, &K_blas_int, \ + V, &LDV_blas_int, Tau, T, &LDT_blas_int) ; \ + } \ +} + +void SUITESPARSE_LAPACK_ZLARFT ( // input: const char *direct, @@ -1155,7 +1740,40 @@ void SUITESPARSE_LAPACK_ZLARFT // T = block Householder factor } \ } -void SUITESPARSE_LAPACK_DLARFB // apply block Householder reflector +void SUITESPARSE_LAPACK_CLARFT +( + // input: + const char *direct, + const char *storev, + const SUITESPARSE_BLAS_INT *n, + const SUITESPARSE_BLAS_INT *k, + const void *V, + const SUITESPARSE_BLAS_INT *ldv, + const void *Tau, + // output: + void *T, + // input: + const SUITESPARSE_BLAS_INT *ldt +) ; + +#define SUITESPARSE_LAPACK_clarft(direct,storev,n,k,V,ldv,Tau,T,ldt,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (K_blas_int, k, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDV_blas_int, ldv, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDT_blas_int, ldt, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_LAPACK_CLARFT (direct, storev, &N_blas_int, &K_blas_int, \ + V, &LDV_blas_int, Tau, T, &LDT_blas_int) ; \ + } \ +} + +//------------------------------------------------------------------------------ +// larfb: apply block Householder reflector +//------------------------------------------------------------------------------ + +void SUITESPARSE_LAPACK_DLARFB ( // input: const char *side, @@ -1197,7 +1815,49 @@ void SUITESPARSE_LAPACK_DLARFB // apply block Householder reflector } \ } -void SUITESPARSE_LAPACK_ZLARFB // apply block Householder reflector +void SUITESPARSE_LAPACK_SLARFB +( + // input: + const char *side, + const char *trans, + const char *direct, + const char *storev, + const SUITESPARSE_BLAS_INT *m, + const SUITESPARSE_BLAS_INT *n, + const SUITESPARSE_BLAS_INT *k, + const float *V, + const SUITESPARSE_BLAS_INT *ldv, + const float *T, + const SUITESPARSE_BLAS_INT *ldt, + // input/output: + float *C, + // input: + const SUITESPARSE_BLAS_INT *ldc, + // workspace: + float *Work, + // input: + const SUITESPARSE_BLAS_INT *ldwork +) ; + +#define SUITESPARSE_LAPACK_slarfb(side,trans,direct,storev,m,n,k,V,ldv,T,ldt, \ + C,ldc,Work,ldwork,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (M_blas_int, m, ok) ; \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (K_blas_int, k, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDV_blas_int, ldv, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDT_blas_int, ldt, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDC_blas_int, ldc, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDWORK_blas_int, ldwork, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_LAPACK_SLARFB (side, trans, direct, storev, &M_blas_int, \ + &N_blas_int, &K_blas_int, V, &LDV_blas_int, T, &LDT_blas_int, C, \ + &LDC_blas_int, Work, &LDWORK_blas_int) ; \ + } \ +} + +void SUITESPARSE_LAPACK_ZLARFB ( // input: const char *side, @@ -1239,7 +1899,53 @@ void SUITESPARSE_LAPACK_ZLARFB // apply block Householder reflector } \ } -double SUITESPARSE_BLAS_DNRM2 // vector 2-norm +void SUITESPARSE_LAPACK_CLARFB +( + // input: + const char *side, + const char *trans, + const char *direct, + const char *storev, + const SUITESPARSE_BLAS_INT *m, + const SUITESPARSE_BLAS_INT *n, + const SUITESPARSE_BLAS_INT *k, + const void *V, + const SUITESPARSE_BLAS_INT *ldv, + const void *T, + const SUITESPARSE_BLAS_INT *ldt, + // input/output: + void *C, + // input: + const SUITESPARSE_BLAS_INT *ldc, + // workspace: + void *Work, + // input: + const SUITESPARSE_BLAS_INT *ldwork +) ; + +#define SUITESPARSE_LAPACK_clarfb(side,trans,direct,storev,m,n,k,V,ldv,T,ldt, \ + C,ldc,Work,ldwork,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (M_blas_int, m, ok) ; \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (K_blas_int, k, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDV_blas_int, ldv, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDT_blas_int, ldt, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDC_blas_int, ldc, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDWORK_blas_int, ldwork, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_LAPACK_CLARFB (side, trans, direct, storev, &M_blas_int, \ + &N_blas_int, &K_blas_int, V, &LDV_blas_int, T, &LDT_blas_int, C, \ + &LDC_blas_int, Work, &LDWORK_blas_int) ; \ + } \ +} + +//------------------------------------------------------------------------------ +// nrm2: vector 2-norm +//------------------------------------------------------------------------------ + +double SUITESPARSE_BLAS_DNRM2 ( // input: const SUITESPARSE_BLAS_INT *n, @@ -1258,7 +1964,26 @@ double SUITESPARSE_BLAS_DNRM2 // vector 2-norm } \ } -double SUITESPARSE_BLAS_DZNRM2 // vector 2-norm +float SUITESPARSE_BLAS_SNRM2 +( + // input: + const SUITESPARSE_BLAS_INT *n, + const float *X, + const SUITESPARSE_BLAS_INT *incx +) ; + +#define SUITESPARSE_BLAS_snrm2(result,n,X,incx,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCX_blas_int, incx, ok) ; \ + result = 0 ; \ + if (ok) \ + { \ + result = SUITESPARSE_BLAS_SNRM2 (&N_blas_int, X, &INCX_blas_int) ; \ + } \ +} + +double SUITESPARSE_BLAS_DZNRM2 ( // input: const SUITESPARSE_BLAS_INT *n, @@ -1277,7 +2002,30 @@ double SUITESPARSE_BLAS_DZNRM2 // vector 2-norm } \ } -void SUITESPARSE_LAPACK_DLARFG // generate Householder reflector +float SUITESPARSE_BLAS_SCNRM2 +( + // input: + const SUITESPARSE_BLAS_INT *n, + const void *X, + const SUITESPARSE_BLAS_INT *incx +) ; + +#define SUITESPARSE_BLAS_scnrm2(result,n,X,incx,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCX_blas_int, incx, ok) ; \ + result = 0 ; \ + if (ok) \ + { \ + result = SUITESPARSE_BLAS_SCNRM2 (&N_blas_int, X, &INCX_blas_int) ; \ + } \ +} + +//------------------------------------------------------------------------------ +// larfg: generate Householder reflector +//------------------------------------------------------------------------------ + +void SUITESPARSE_LAPACK_DLARFG ( // input: const SUITESPARSE_BLAS_INT *n, @@ -1301,7 +2049,31 @@ void SUITESPARSE_LAPACK_DLARFG // generate Householder reflector } \ } -void SUITESPARSE_LAPACK_ZLARFG // generate Householder reflector +void SUITESPARSE_LAPACK_SLARFG +( + // input: + const SUITESPARSE_BLAS_INT *n, + // input/output: + float *alpha, + float *X, + // input: + const SUITESPARSE_BLAS_INT *incx, + // output: + float *tau +) ; + +#define SUITESPARSE_LAPACK_slarfg(n,alpha,X,incx,tau,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCX_blas_int, incx, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_LAPACK_SLARFG (&N_blas_int, alpha, X, &INCX_blas_int, \ + tau) ; \ + } \ +} + +void SUITESPARSE_LAPACK_ZLARFG ( // input: const SUITESPARSE_BLAS_INT *n, @@ -1325,7 +2097,35 @@ void SUITESPARSE_LAPACK_ZLARFG // generate Householder reflector } \ } -void SUITESPARSE_LAPACK_DLARF // apply Householder reflector +void SUITESPARSE_LAPACK_CLARFG +( + // input: + const SUITESPARSE_BLAS_INT *n, + // input/output: + void *alpha, + void *X, + // input: + const SUITESPARSE_BLAS_INT *incx, + // output: + void *tau +) ; + +#define SUITESPARSE_LAPACK_clarfg(n,alpha,X,incx,tau,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCX_blas_int, incx, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_LAPACK_CLARFG (&N_blas_int, alpha, X, &INCX_blas_int, \ + tau) ; \ + } \ +} + +//------------------------------------------------------------------------------ +// larf: apply Householder reflector +//------------------------------------------------------------------------------ + +void SUITESPARSE_LAPACK_DLARF ( // input: const char *side, @@ -1355,7 +2155,37 @@ void SUITESPARSE_LAPACK_DLARF // apply Householder reflector } \ } -void SUITESPARSE_LAPACK_ZLARF // apply Householder reflector +void SUITESPARSE_LAPACK_SLARF +( + // input: + const char *side, + const SUITESPARSE_BLAS_INT *m, + const SUITESPARSE_BLAS_INT *n, + const float *V, + const SUITESPARSE_BLAS_INT *incv, + const float *tau, + // input/output: + float *C, + // input: + const SUITESPARSE_BLAS_INT *ldc, + // workspace: + float *Work +) ; + +#define SUITESPARSE_LAPACK_slarf(side,m,n,V,incv,tau,C,ldc,Work,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (M_blas_int, m, ok) ; \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCV_blas_int, incv, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDC_blas_int, ldc, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_LAPACK_SLARF (side, &M_blas_int, &N_blas_int, V, \ + &INCV_blas_int, tau, C, &LDC_blas_int, Work) ; \ + } \ +} + +void SUITESPARSE_LAPACK_ZLARF ( // input: const char *side, @@ -1385,6 +2215,36 @@ void SUITESPARSE_LAPACK_ZLARF // apply Householder reflector } \ } +void SUITESPARSE_LAPACK_CLARF +( + // input: + const char *side, + const SUITESPARSE_BLAS_INT *m, + const SUITESPARSE_BLAS_INT *n, + const void *V, + const SUITESPARSE_BLAS_INT *incv, + const void *tau, + // input/output: + void *C, + // input: + const SUITESPARSE_BLAS_INT *ldc, + // workspace: + void *Work +) ; + +#define SUITESPARSE_LAPACK_clarf(side,m,n,V,incv,tau,C,ldc,Work,ok) \ +{ \ + SUITESPARSE_TO_BLAS_INT (M_blas_int, m, ok) ; \ + SUITESPARSE_TO_BLAS_INT (N_blas_int, n, ok) ; \ + SUITESPARSE_TO_BLAS_INT (INCV_blas_int, incv, ok) ; \ + SUITESPARSE_TO_BLAS_INT (LDC_blas_int, ldc, ok) ; \ + if (ok) \ + { \ + SUITESPARSE_LAPACK_CLARF (side, &M_blas_int, &N_blas_int, V, \ + &INCV_blas_int, tau, C, &LDC_blas_int, Work) ; \ + } \ +} + #endif //------------------------------------------------------------------------------ diff --git a/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/FindSuiteSparse_config.cmake b/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/FindSuiteSparse_config.cmake deleted file mode 100644 index 2b75390cc9..0000000000 --- a/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/FindSuiteSparse_config.cmake +++ /dev/null @@ -1,141 +0,0 @@ -#------------------------------------------------------------------------------- -# SuiteSparse/SuiteSparse_config/cmake_modules/FindSuiteSparse_config.cmake -#------------------------------------------------------------------------------- - -# The following copyright and license applies to just this file only, not to -# the library itself: -# FindSuiteSparse_config.cmake, Copyright (c) 2022-2023, Timothy A. Davis. All Rights Reserved. -# SPDX-License-Identifier: BSD-3-clause - -#------------------------------------------------------------------------------- - -# Finds the SuiteSparse_config include file and compiled library and sets: - -# SUITESPARSE_CONFIG_INCLUDE_DIR - where to find SuiteSparse_config.h -# SUITESPARSE_CONFIG_LIBRARY - dynamic SuiteSparse_config library -# SUITESPARSE_CONFIG_STATIC - static SuiteSparse_config library -# SUITESPARSE_CONFIG_LIBRARIES - libraries when using SuiteSparse_config -# SUITESPARSE_CONFIG_FOUND - true if SuiteSparse_config found - -# set ``SUITESPARSE_CONFIG_ROOT`` or ``SuiteSparse_config_ROOT`` to a -# SuiteSparse_config installation root to tell this module where to look. - -# All the Find*.cmake files in SuiteSparse are installed by 'make install' into -# /usr/local/lib/cmake/SuiteSparse (where '/usr/local' is the -# ${CMAKE_INSTALL_PREFIX}). To access this file, place the following commands -# in your CMakeLists.txt file. See also SuiteSparse/Example/CMakeLists.txt: -# -# set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} -# ${CMAKE_INSTALL_PREFIX}/lib/cmake/SuiteSparse ) - -#------------------------------------------------------------------------------- - -# include files for SuiteSparse_config -find_path ( SUITESPARSE_CONFIG_INCLUDE_DIR - NAMES SuiteSparse_config.h - HINTS ${SUITESPARSE_CONFIG_ROOT} - HINTS ENV SUITESPARSE_CONFIG_ROOT - HINTS ${CMAKE_SOURCE_DIR}/.. - HINTS ${CMAKE_SOURCE_DIR}/../SuiteSparse/SuiteSparse_config - HINTS ${CMAKE_SOURCE_DIR}/../SuiteSparse_config - PATH_SUFFIXES include Include -) - -# dynamic SuiteSparse_config (or static if no dynamic library was built) -find_library ( SUITESPARSE_CONFIG_LIBRARY - NAMES suitesparseconfig suitesparseconfig_static - HINTS ${SUITESPARSE_CONFIG_ROOT} - HINTS ENV SUITESPARSE_CONFIG_ROOT - HINTS ${CMAKE_SOURCE_DIR}/.. - HINTS ${CMAKE_SOURCE_DIR}/../SuiteSparse/SuiteSparse_config - HINTS ${CMAKE_SOURCE_DIR}/../SuiteSparse_config - PATH_SUFFIXES lib build build/Release build/Debug -) - -if ( MSVC ) - set ( STATIC_NAME suitesparseconfig_static ) -else ( ) - set ( STATIC_NAME suitesparseconfig ) - set ( save ${CMAKE_FIND_LIBRARY_SUFFIXES} ) - message ( STATUS "original library suffixes: ${CMAKE_FIND_LIBRARY_SUFFIXES}" ) - set ( CMAKE_FIND_LIBRARY_SUFFIXES - ${CMAKE_STATIC_LIBRARY_SUFFIX} ${CMAKE_FIND_LIBRARY_SUFFIXES} ) - message ( STATUS "revised for static search: ${CMAKE_FIND_LIBRARY_SUFFIXES}" ) -endif ( ) - -# static libraries for SuiteSparse_config -find_library ( SUITESPARSE_CONFIG_STATIC - NAMES ${STATIC_NAME} - HINTS ${SUITESPARSE_CONFIG_ROOT} - HINTS ENV SUITESPARSE_CONFIG_ROOT - HINTS ${CMAKE_SOURCE_DIR}/.. - HINTS ${CMAKE_SOURCE_DIR}/../SuiteSparse/SuiteSparse_config - HINTS ${CMAKE_SOURCE_DIR}/../SuiteSparse_config - PATH_SUFFIXES lib build build/Release build/Debug -) - -if ( NOT MSVC ) - # restore the CMAKE_FIND_LIBRARY_SUFFIXES variable - set ( CMAKE_FIND_LIBRARY_SUFFIXES ${save} ) -endif ( ) - -# get version of the library from the dynamic library filename, if present -get_filename_component ( SUITESPARSE_CONFIG_LIBRARY ${SUITESPARSE_CONFIG_LIBRARY} REALPATH ) -get_filename_component ( SUITESPARSE_CONFIG_FILENAME ${SUITESPARSE_CONFIG_LIBRARY} NAME ) -string ( - REGEX MATCH "[0-9]+.[0-9]+.[0-9]+" - SUITESPARSE_CONFIG_VERSION - ${SUITESPARSE_CONFIG_FILENAME} -) - -# set ( SUITESPARSE_CONFIG_VERSION "" ) -if ( EXISTS "${SUITESPARSE_CONFIG_INCLUDE_DIR}" AND NOT SUITESPARSE_CONFIG_VERSION ) - # if the version does not appear in the filename, read the include file - file ( STRINGS ${SUITESPARSE_CONFIG_INCLUDE_DIR}/SuiteSparse_config.h SUITESPARSE_CONFIG_MAJOR_STR - REGEX "define SUITESPARSE_MAIN_VERSION" ) - file ( STRINGS ${SUITESPARSE_CONFIG_INCLUDE_DIR}/SuiteSparse_config.h SUITESPARSE_CONFIG_MINOR_STR - REGEX "define SUITESPARSE_SUB_VERSION" ) - file ( STRINGS ${SUITESPARSE_CONFIG_INCLUDE_DIR}/SuiteSparse_config.h SUITESPARSE_CONFIG_PATCH_STR - REGEX "define SUITESPARSE_SUBSUB_VERSION" ) - message ( STATUS "major: ${SUITESPARSE_CONFIG_MAJOR_STR}" ) - message ( STATUS "minor: ${SUITESPARSE_CONFIG_MINOR_STR}" ) - message ( STATUS "patch: ${SUITESPARSE_CONFIG_PATCH_STR}" ) - string ( REGEX MATCH "[0-9]+" SUITESPARSE_CONFIG_MAJOR ${SUITESPARSE_CONFIG_MAJOR_STR} ) - string ( REGEX MATCH "[0-9]+" SUITESPARSE_CONFIG_MINOR ${SUITESPARSE_CONFIG_MINOR_STR} ) - string ( REGEX MATCH "[0-9]+" SUITESPARSE_CONFIG_PATCH ${SUITESPARSE_CONFIG_PATCH_STR} ) - set (SUITESPARSE_CONFIG_VERSION "${SUITESPARSE_CONFIG_MAJOR}.${SUITESPARSE_CONFIG_MINOR}.${SUITESPARSE_CONFIG_PATCH}") -endif ( ) - -# libaries when using SuiteSparse_config -set (SUITESPARSE_CONFIG_LIBRARIES ${SUITESPARSE_CONFIG_LIBRARY}) - -include ( FindPackageHandleStandardArgs ) - -find_package_handle_standard_args ( SuiteSparse_config - REQUIRED_VARS SUITESPARSE_CONFIG_LIBRARY SUITESPARSE_CONFIG_INCLUDE_DIR - VERSION_VAR SUITESPARSE_CONFIG_VERSION - REASON_FAILURE_MESSAGE result -) - -message (STATUS "result: ${result}") - -mark_as_advanced ( - SUITESPARSE_CONFIG_INCLUDE_DIR - SUITESPARSE_CONFIG_LIBRARY - SUITESPARSE_CONFIG_STATIC - SUITESPARSE_CONFIG_LIBRARIES -) - -if ( SUITESPARSE_CONFIG_FOUND ) - message ( STATUS "SuiteSparse_config version: ${SUITESPARSE_CONFIG_VERSION}" ) - message ( STATUS "SuiteSparse_config include: ${SUITESPARSE_CONFIG_INCLUDE_DIR}" ) - message ( STATUS "SuiteSparse_config library: ${SUITESPARSE_CONFIG_LIBRARY}" ) - message ( STATUS "SuiteSparse_config static: ${SUITESPARSE_CONFIG_STATIC}" ) -else ( ) - message ( STATUS "SuiteSparse_config not found" ) - set ( SUITESPARSE_CONFIG_INCLUDE_DIR "" ) - set ( SUITESPARSE_CONFIG_LIBRARIES "" ) - set ( SUITESPARSE_CONFIG_LIBRARY "" ) - set ( SUITESPARSE_CONFIG_STATIC "" ) -endif ( ) - diff --git a/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseBLAS.cmake b/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseBLAS.cmake index 9e4b779acb..2225f53731 100644 --- a/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseBLAS.cmake +++ b/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseBLAS.cmake @@ -24,13 +24,27 @@ endif ( ) set ( BLA_VENDOR "ANY" CACHE STRING "if ANY (default): searches for any BLAS. Otherwise: search for a specific BLAS" ) -# To allow the use of a BLAS with 64-bit integers, set this to true -option ( ALLOW_64BIT_BLAS - "OFF (default): use only 32-bit BLAS. ON: look for 32 or 64-bit BLAS" off ) +# To allow the use of a BLAS with 64-bit integers, set this to ON +option ( SUITESPARSE_USE_64BIT_BLAS + "OFF (default): use only 32-bit BLAS. ON: look for 32 or 64-bit BLAS" OFF ) # dynamic/static linking with BLAS option ( BLA_STATIC - "OFF (default): dynamic linking of BLAS. ON: static linking of BLAS" off ) + "OFF (default): dynamic linking of BLAS. ON: static linking of BLAS" OFF ) + +if ( DEFINED BLAS_LIBRARIES OR DEFINED BLAS_INCLUDE_DIRS ) + # AMICI -- silence cmake "Manually-specified variables were not used by the project" + set(amici_ignore "${BLAS_LIBRARIES} ${BLAS_INCLUDE_DIRS}") + + # User supplied variables for libraries and/or include directories. + # Use them as-is. + if ( SUITESPARSE_USE_64BIT_BLAS ) + include ( SuiteSparseBLAS64 ) + else ( ) + include ( SuiteSparseBLAS32 ) + endif ( ) + return ( ) +endif ( ) #------------------------------------------------------------------------------- # look for a specific BLAS library @@ -39,32 +53,34 @@ option ( BLA_STATIC # To request specific BLAS, use either (for example): # # CMAKE_OPTIONS="-DBLA_VENDOR=Apple" make -# cd build ; cmake -DBLA_VENDOR=Apple .. ; make +# cd build && cmake -DBLA_VENDOR=Apple .. ; make # -# Use the ALLOW_64BIT_BLAS to select 64-bit or 32-bit BLAS. This setting is -# strictly enforced. If set to true, then only a 64-bit BLAS is allowed. -# If this is not found, no 32-bit BLAS is considered, and the build will fail. +# Use SUITESPARSE_USE_64BIT_BLAS to select 64-bit or 32-bit BLAS. If +# BLA_VENDOR is also defined, this setting is strictly enforced. If set to +# ON, then only a 64-bit BLAS is allowed. If this is not found, no 32-bit +# BLAS is considered, and the build will fail. # -# If the BLA_VENDOR string implies a 64-bit BLAS, then ALLOW_64BIT_BLAS is set -# to true, ignoring the setting from the user (Intel10_64ilp* and Arm_64ilp*). +# If the BLA_VENDOR string implies a 64-bit BLAS, then +# SUITESPARSE_USE_64BIT_BLAS is set to ON, ignoring the setting of this value +# from the user (Intel10_64ilp* and Arm_64ilp*). # -# The default for ALLOW_64BIT_BLAS is false. +# The default for SUITESPARSE_USE_64BIT_BLAS is OFF. if ( NOT (BLA_VENDOR STREQUAL "ANY" ) ) # only look for the BLAS from a single vendor if ( ( BLA_VENDOR MATCHES "64ilp" ) OR ( BLA_VENDOR MATCHES "ilp64" ) ) # Intel10_64ilp* or Arm_ilp64* - set ( ALLOW_64BIT_BLAS true ) + set ( SUITESPARSE_USE_64BIT_BLAS ON ) # OK; overidden by BLA_VENDOR endif ( ) - if ( ALLOW_64BIT_BLAS ) + if ( SUITESPARSE_USE_64BIT_BLAS ) # only look for 64-bit BLAS set ( BLA_SIZEOF_INTEGER 8 ) message ( STATUS "Looking for 64-BLAS: " ${BLA_VENDOR} ) else ( ) # only look for 32-bit BLAS - message ( STATUS "Looking for 32-BLAS: " ${BLA_VENDOR} ) set ( BLA_SIZEOF_INTEGER 4 ) + message ( STATUS "Looking for 32-BLAS: " ${BLA_VENDOR} ) endif ( ) find_package ( BLAS REQUIRED ) if ( BLA_SIZEOF_INTEGER EQUAL 8 ) @@ -76,6 +92,7 @@ if ( NOT (BLA_VENDOR STREQUAL "ANY" ) ) include ( SuiteSparseBLAS32 ) endif ( ) message ( STATUS "Specific BLAS: ${BLA_VENDOR} found: ${BLAS_FOUND}" ) + message ( STATUS "BLAS integer size: ${BLA_SIZEOF_INTEGER}" ) return ( ) endif ( ) @@ -83,10 +100,19 @@ endif ( ) # Look for any 64-bit BLAS, if allowed #------------------------------------------------------------------------------- -# If ALLOW_64BIT_BLAS is true, then a 64-bit BLAS is preferred. -# If not found, a 32-bit BLAS is sought (below) +# If SUITESPARSE_USE_64BIT_BLAS is ON and SUITESPARSE_USE_STRICT is OFF, then a +# 64-bit BLAS is preferred. If not found, a 32-bit BLAS is sought. +# The setting of SUITESPARSE_USE_64BIT_BLAS not strict by default. + +# If SUITESPARSE_USE_64BIT_BLAS is ON and SUITESPARSE_USE_STRICT is ON, then a +# only a 64-bit BLAS is considered. An error occurs if a 64-bit BLAS is not +# found. -if ( ALLOW_64BIT_BLAS ) +# If SUITESPARSE_USE_64BIT_BLAS is OFF, only a 32-bit BLAS is considered. An +# error occurs if a 32-bit BLAS is not found (the SUITESPARSE_USE_STRICT +# setting is ignored). + +if ( SUITESPARSE_USE_64BIT_BLAS ) # Look for Intel MKL BLAS with 64-bit integers message ( STATUS "Looking for Intel 64-bit BLAS" ) @@ -139,6 +165,11 @@ if ( ALLOW_64BIT_BLAS ) return ( ) endif ( ) + # report an error if strict + if ( SUITESPARSE_USE_STRICT ) + message ( FATAL_ERROR "64-bit BLAS required, but not found" ) + endif ( ) + endif ( ) #------------------------------------------------------------------------------- diff --git a/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseBLAS64.cmake b/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseBLAS64.cmake index 744aaef910..72efcd5ce0 100644 --- a/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseBLAS64.cmake +++ b/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseBLAS64.cmake @@ -25,19 +25,19 @@ set ( SuiteSparse_BLAS_integer "int64_t" ) # If the suffix does not contain "_", use (Sun Perf., for example): -# cd build ; cmake -DBLAS64_SUFFIX="64" .. +# cd build && cmake -DBLAS64_SUFFIX="64" .. # If the suffix contains "_" (OpenBLAS in spack for example), use the # following: -# cd build ; cmake -DBLAS64_SUFFIX="_64" .. +# cd build && cmake -DBLAS64_SUFFIX="_64" .. # This setting could be used by the spack packaging of SuiteSparse when linked # with the spack-installed OpenBLAS with 64-bit integers. See # https://github.com/spack/spack/blob/develop/var/spack/repos/builtin/packages/suite-sparse/package.py if ( DEFINED BLAS64_SUFFIX ) - # append BLAS64_SUFFIX to each BLAS and LAPACK name + # append BLAS64_SUFFIX to each BLAS and LAPACK function name string ( FIND ${BLAS64_SUFFIX} "_" HAS_UNDERSCORE ) message ( STATUS "BLAS64_suffix: ${BLAS64_SUFFIX}" ) if ( HAS_UNDERSCORE EQUAL -1 ) diff --git a/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseLAPACK.cmake b/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseLAPACK.cmake index 27ff73cadd..82366e0aa5 100644 --- a/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseLAPACK.cmake +++ b/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseLAPACK.cmake @@ -20,18 +20,19 @@ cmake_minimum_required ( VERSION 3.22 ) -if ( BLA_VENDOR STREQUAL "FLAME" ) - # FLAME has the BLAS but not LAPACK +if ( DEFINED LAPACK_LIBRARIES OR DEFINED LAPACK_INCLUDE_DIRS ) + # User supplied variables for libraries and/or include directories. + # Use them as-is. + return ( ) +endif ( ) - set ( BLA_VENDOR "Generic" ) - message ( STATUS "Looking for generic LAPACK to use with BLIS/FLAME BLAS" ) +if ( BLA_VENDOR STREQUAL "FLAME" ) - # look for the generic dynamic LAPACK library (usually liblagraph.so) find_library ( LAPACK_LIBRARY - NAMES lapack + NAMES flame PATH_SUFFIXES lib build ) - # look for the static LAPACK library (usually liblagraph.a) + # look for the static LAPACK library (usually liblapack.a) if ( MSVC ) set ( STATIC_SUFFIX .lib ) else ( ) diff --git a/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparsePolicy.cmake b/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparsePolicy.cmake index 8c18fa7581..b6b195407c 100644 --- a/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparsePolicy.cmake +++ b/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparsePolicy.cmake @@ -14,24 +14,25 @@ # To use the "Debug" policy, precede this with # set ( CMAKE_BUILD_TYPE Debug ) # -# ENABLE_CUDA: if set to true, CUDA is enabled for the project. -# Default: true for CHOLMOD and SPQR, false for GraphBLAS -# (for which CUDA is in progress and not ready for -# production use). +# SUITESPARSE_USE_CUDA: if OFF, CUDA is disabled. if ON, CUDA is enabled, +# if available. Default: ON. # -# LOCAL_INSTALL: if true, "cmake --install" will install +# SUITESPARSE_LOCAL_INSTALL: if true, "cmake --install" will install # into SuiteSparse/lib and SuiteSparse/include. # if false, "cmake --install" will install into the # default prefix (or the one configured with -# CMAKE_INSTALL_PREFIX). -# Default: false +# CMAKE_INSTALL_PREFIX). Requires cmake 3.19. +# This is ignored when using the root CMakeLists.txt. +# Set CMAKE_INSTALL_PREFIX instead. +# Default: OFF # -# NSTATIC: if true, static libraries are not built. -# Default: false, except for GraphBLAS, which +# BUILD_SHARED_LIBS: if true, shared libraries are built. +# Default: ON. +# +# BUILD_STATIC_LIBS: if true, static libraries are built. +# Default: ON, except for GraphBLAS, which # takes a long time to compile so the default for -# GraphBLAS is true. For Mongoose, the NSTATIC setting -# is treated as if it always false, since the mongoose -# program is built with the static library. +# GraphBLAS is false. # # SUITESPARSE_CUDA_ARCHITECTURES: a string, such as "all" or # "35;50;75;80" that lists the CUDA architectures to use @@ -47,12 +48,12 @@ # Both settings must appear, or neither. # Default: neither are defined. # -# BLA_STATIC: if true, use static linkage for BLAS and LAPACK. -# Default: false +# BLA_STATIC: if ON, use static linkage for BLAS and LAPACK. +# Default: not set (that is, the same as OFF) # -# ALLOW_64BIT_BLAS if true, SuiteSparse will search for both 32-bit and -# 64-bit BLAS. If false, only 32-bit BLAS will be -# searched for. Ignored if BLA_VENDOR and +# SUITESPARSE_USE_64BIT_BLAS if true, SuiteSparse will search for both +# 32-bit and 64-bit BLAS. If false, only 32-bit BLAS +# will be searched for. Ignored if BLA_VENDOR and # BLA_SIZEOF_INTEGER are defined. # # SUITESPARSE_C_TO_FORTRAN: a string that defines how C calls Fortran. @@ -63,33 +64,64 @@ # This setting is only used if no Fortran compiler is # found. # -# NFORTRAN: if true, no Fortan files are compiled, and the Fortran -# language is not enabled in any cmake scripts. The -# built-in cmake script FortranCInterface is skipped. -# This will require SUITESPARSE_C_TO_FORTRAN to be defined -# explicitly, if the defaults are not appropriate for your -# system. -# Default: false - -cmake_minimum_required ( VERSION 3.19 ) - -message ( STATUS "Source: ${CMAKE_SOURCE_DIR} ") -message ( STATUS "Build: ${CMAKE_BINARY_DIR} ") - -cmake_policy ( SET CMP0042 NEW ) # enable MACOSX_RPATH by default -cmake_policy ( SET CMP0048 NEW ) # VERSION variable policy -cmake_policy ( SET CMP0054 NEW ) # if ( expression ) handling policy -cmake_policy ( SET CMP0104 NEW ) # initialize CUDA architectures +# SUITESPARSE_USE_FORTRAN: if OFF, no Fortan files are compiled, and the +# Fortran language is not enabled in any cmake scripts. +# The built-in cmake script FortranCInterface is skipped. +# This will require SUITESPARSE_C_TO_FORTRAN to be +# defined explicitly, if the defaults are not appropriate +# for your system. +# Default: ON +# +# SUITESPARSE_PKGFILEDIR: Directory where CMake Config and pkg-config files +# will be installed. By default, CMake Config files will +# be installed in the subfolder `cmake` of the directory +# where the (static) libraries will be installed (e.g., +# `lib`). The `.pc` files for pkg-config will be +# installed in the subfolder `pkgconfig` of the directory +# where the (static) libraries will be installed. +# Default: CMAKE_INSTALL_PREFIX, or SuiteSparse/lib if +# SUITESPARSE_LOCAL_INSTALL is enabled. +# +# SUITESPARSE_INCLUDEDIR_POSTFIX : Postfix for installation target of +# header from SuiteSparse. Default: suitesparse, so the +# default include directory is: +# CMAKE_INSTALL_PREFIX/include/suitesparse +# +# SUITESPARSE_USE_STRICT: SuiteSparse has many user-definable settings of the +# form SUITESPARSE_USE_* or (package)_USE_* for some +# particular package. In general, these settings are not +# strict. For example, if SUITESPARSE_USE_OPENMP is +# ON then OpenMP is preferred, but SuiteSparse can be +# used without OpenMP so no error is generated if OpenMP +# is not found. However, if SUITESPARSE_USE_STRICT is +# ON then all *_USE_* settings are treated strictly +# and an error occurs if any are set to ON but the +# corresponding package or setting is not available. The +# *_USE_SYSTEM_* settings are always treated as strict. +# Default: OFF. + +message ( STATUS "Source: ${CMAKE_SOURCE_DIR} ") +message ( STATUS "Build: ${CMAKE_BINARY_DIR} ") + +if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.18.0" ) + cmake_policy ( SET CMP0104 NEW ) # initialize CUDA architectures +endif ( ) -# AMICI -set(CMAKE_POSITION_INDEPENDENT_CODE ON) +# SuiteSparse packages have many intentional extra semicolons, for code +# readability (such as "/* do nothing */ ;" in SuiteSparse_config.c). Disable +# the clang warning for these statements: +if ( CMAKE_C_COMPILER_ID STREQUAL "Clang" ) + set ( CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Wno-extra-semi-stmt" ) +endif ( ) +if ( CMAKE_CXX_COMPILER_ID STREQUAL "Clang" ) + set ( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wno-extra-semi-stmt" ) +endif ( ) if ( WIN32 ) set ( CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS true ) add_compile_definitions ( _CRT_SECURE_NO_WARNINGS ) endif ( ) -set ( CMAKE_MACOSX_RPATH TRUE ) enable_language ( C ) include ( GNUInstallDirs ) @@ -97,15 +129,40 @@ include ( GNUInstallDirs ) set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake_modules ) -# NSTATIC option -if ( NSTATIC_DEFAULT_ON ) - option ( NSTATIC "ON (default): do not build static libraries. OFF: build static libraries" on ) +# Use OpenMP +option ( SUITESPARSE_USE_OPENMP "ON (default): Use OpenMP in libraries by default if available. OFF: Do not use OpenMP by default." ON ) + +# strict usage +option ( SUITESPARSE_USE_STRICT "ON: treat all _USE__ settings as strict if they are ON. OFF (default): consider *_USE_* as preferences, not strict" OFF ) + +# build the demos +option ( SUITESPARSE_DEMOS "ON: Build the demo programs. OFF (default): do not build the demo programs." OFF ) + +# BUILD_SHARED_LIBS and BUILD_STATIC_LIBS options +option ( BUILD_SHARED_LIBS "OFF: do not build shared libraries. ON (default): build shared libraries" ON ) + +if ( BUILD_STATIC_LIBS_DEFAULT_OFF ) + option ( BUILD_STATIC_LIBS "OFF (default): do not build static libraries. ON: build static libraries" OFF ) else ( ) - option ( NSTATIC "ON: do not build static libraries. OFF (default): build static libraries" off ) + # For backwards compatibility, use NSTATIC if it is set. + if ( NSTATIC ) + option ( BUILD_STATIC_LIBS "OFF: do not build static libraries. ON (default): build static libraries" OFF ) + else ( ) + option ( BUILD_STATIC_LIBS "OFF: do not build static libraries. ON (default): build static libraries" ON ) + endif ( ) +endif ( ) + +if ( NOT BUILD_SHARED_LIBS AND NOT BUILD_STATIC_LIBS ) + message ( FATAL_ERROR "At least one of BUILD_SHARED_LIBS or BUILD_STATIC_LIBS must be set to ON." ) endif ( ) # installation options -option ( LOCAL_INSTALL "Install in SuiteSparse/lib" off ) +if ( NOT SUITESPARSE_ROOT_CMAKELISTS AND ${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.19.0" ) + # the SUITESPARSE_LOCAL_INSTALL option requires cmake 3.19.0 or later + option ( SUITESPARSE_LOCAL_INSTALL "Install in SuiteSparse/lib" OFF ) +else ( ) + set ( SUITESPARSE_LOCAL_INSTALL OFF ) +endif ( ) if ( SUITESPARSE_SECOND_LEVEL ) # some packages in SuiteSparse are in SuiteSparse/Package/Package @@ -117,94 +174,127 @@ else ( ) ${CMAKE_SOURCE_DIR}/../lib/cmake ) endif ( ) -# add the ./build folder to the runpath so other SuiteSparse packages can -# find this one without "make install" -set ( CMAKE_BUILD_RPATH ${CMAKE_BUILD_RPATH} ${CMAKE_BINARY_DIR} ) - -# determine if this Package is inside the SuiteSparse folder -set ( INSIDE_SUITESPARSE false ) -if ( LOCAL_INSTALL ) - # if you do not want to install local copies of SuiteSparse - # packages in SuiteSparse/lib and SuiteSparse/, set - # LOCAL_INSTALL to false in your CMake options. - if ( SUITESPARSE_SECOND_LEVEL ) - # the package is normally located at the 2nd level inside SuiteSparse - # (SuiteSparse/GraphBLAS/GraphBLAS/ for example) - if ( EXISTS ${CMAKE_SOURCE_DIR}/../../SuiteSparse_config ) - set ( INSIDE_SUITESPARSE true ) +# allow libraries to "see" each other if they are installed in a non-default LIBRARY_PATH +list ( FIND CMAKE_INSTALL_RPATH "$ORIGIN" _idx ) +if ( _idx LESS 0 ) + # "$ORIGIN" not yet included in CMAKE_INSTALL_RPATH + list ( PREPEND CMAKE_INSTALL_RPATH "$ORIGIN" ) +endif ( ) + +set ( INSIDE_SUITESPARSE OFF ) +if ( NOT SUITESPARSE_ROOT_CMAKELISTS ) + # determine if this Package is inside the SuiteSparse folder + if ( SUITESPARSE_LOCAL_INSTALL ) + # if you do not want to install local copies of SuiteSparse + # packages in SuiteSparse/lib and SuiteSparse/, set + # SUITESPARSE_LOCAL_INSTALL to false in your CMake options. + if ( SUITESPARSE_SECOND_LEVEL ) + # the package is normally located at the 2nd level inside SuiteSparse + # (SuiteSparse/GraphBLAS/GraphBLAS/ for example) + if ( EXISTS ${CMAKE_SOURCE_DIR}/../../SuiteSparse_config ) + set ( INSIDE_SUITESPARSE true ) + endif ( ) + else ( ) + # typical case, the package is at the 1st level inside SuiteSparse + # (SuiteSparse/AMD for example) + if ( EXISTS ${CMAKE_SOURCE_DIR}/../SuiteSparse_config ) + set ( INSIDE_SUITESPARSE true ) + endif ( ) endif ( ) - else ( ) - # typical case, the package is at the 1st level inside SuiteSparse - # (SuiteSparse/AMD for example) - if ( EXISTS ${CMAKE_SOURCE_DIR}/../SuiteSparse_config ) - set ( INSIDE_SUITESPARSE true ) + + if ( NOT INSIDE_SUITESPARSE ) + message ( FATAL_ERROR "Unsupported layout for local installation. Correct the directory layout or unset SUITESPARSE_LOCAL_INSTALL." ) endif ( ) - endif ( ) - if ( NOT INSIDE_SUITESPARSE ) - message ( FATAL_ERROR "Unsupported layout for local installation. Correct the directory layout or unset LOCAL_INSTALL." ) endif ( ) - endif ( ) -if ( INSIDE_SUITESPARSE ) - # ../lib and ../include exist: the package is inside SuiteSparse. - # find ( REAL_PATH ...) requires cmake 3.19. - if ( SUITESPARSE_SECOND_LEVEL ) - file ( REAL_PATH ${CMAKE_SOURCE_DIR}/../.. SUITESPARSE_LOCAL_PREFIX ) - else ( ) - file ( REAL_PATH ${CMAKE_SOURCE_DIR}/.. SUITESPARSE_LOCAL_PREFIX ) - endif ( ) -endif ( ) +set ( SUITESPARSE_INCLUDEDIR_POSTFIX "suitesparse" CACHE STRING + "Postfix for installation target of header from SuiteSparse (default: \"suitesparse\")" ) -if ( LOCAL_INSTALL ) +if ( SUITESPARSE_LOCAL_INSTALL ) + if ( INSIDE_SUITESPARSE ) + # ../lib and ../include exist: the package is inside SuiteSparse. + # find ( REAL_PATH ...) requires cmake 3.19. + if ( SUITESPARSE_SECOND_LEVEL ) + file ( REAL_PATH ${CMAKE_SOURCE_DIR}/../.. + SUITESPARSE_LOCAL_PREFIX ) + else ( ) + file ( REAL_PATH ${CMAKE_SOURCE_DIR}/.. + SUITESPARSE_LOCAL_PREFIX ) + endif ( ) + endif ( ) set ( SUITESPARSE_LIBDIR ${SUITESPARSE_LOCAL_PREFIX}/lib ) - set ( SUITESPARSE_INCLUDEDIR ${SUITESPARSE_LOCAL_PREFIX}/include ) + set ( SUITESPARSE_INCLUDEDIR ${SUITESPARSE_LOCAL_PREFIX}/include/${SUITESPARSE_INCLUDEDIR_POSTFIX} ) set ( SUITESPARSE_BINDIR ${SUITESPARSE_LOCAL_PREFIX}/bin ) else ( ) set ( SUITESPARSE_LIBDIR ${CMAKE_INSTALL_LIBDIR} ) - set ( SUITESPARSE_INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR} ) + set ( SUITESPARSE_INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR}/${SUITESPARSE_INCLUDEDIR_POSTFIX} ) set ( SUITESPARSE_BINDIR ${CMAKE_INSTALL_BINDIR} ) endif ( ) if ( INSIDE_SUITESPARSE ) # append ../lib to the install and build runpaths - set ( CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_RPATH} ${SUITESPARSE_LIBDIR} ) - set ( CMAKE_BUILD_RPATH ${CMAKE_BUILD_RPATH} ${SUITESPARSE_LIBDIR} ) + list ( APPEND CMAKE_INSTALL_RPATH ${SUITESPARSE_LIBDIR} ) + list ( APPEND CMAKE_BUILD_RPATH ${SUITESPARSE_LIBDIR} ) endif ( ) -message ( STATUS "Install lib: ${SUITESPARSE_LIBDIR}" ) -message ( STATUS "Install include: ${SUITESPARSE_INCLUDEDIR}" ) -message ( STATUS "Install bin: ${SUITESPARSE_BINDIR}" ) -message ( STATUS "Install rpath: ${CMAKE_INSTALL_RPATH}" ) -message ( STATUS "Build rpath: ${CMAKE_BUILD_RPATH}" ) +set ( SUITESPARSE_PKGFILEDIR ${SUITESPARSE_LIBDIR} CACHE STRING + "Directory where CMake Config and pkg-config files will be installed" ) + +message ( STATUS "Install lib: ${SUITESPARSE_LIBDIR}" ) +message ( STATUS "Install include: ${SUITESPARSE_INCLUDEDIR}" ) +message ( STATUS "Install bin: ${SUITESPARSE_BINDIR}" ) +message ( STATUS "Install pkg-file: ${SUITESPARSE_PKGFILEDIR}" ) +message ( STATUS "Install rpath: ${CMAKE_INSTALL_RPATH}" ) +message ( STATUS "Build rpath: ${CMAKE_BUILD_RPATH}" ) if ( NOT CMAKE_BUILD_TYPE ) set ( CMAKE_BUILD_TYPE Release ) endif ( ) -message ( STATUS "Build type: ${CMAKE_BUILD_TYPE} ") +message ( STATUS "Build type: ${CMAKE_BUILD_TYPE} ") set ( CMAKE_INCLUDE_CURRENT_DIR ON ) +#------------------------------------------------------------------------------- +# Workaround for math.h on old macOS +#------------------------------------------------------------------------------- + +if ( APPLE AND CMAKE_SYSTEM_VERSION VERSION_LESS 11 ) + # Older versions of math.h on the Mac define Real and Imag, which + # conflict with the internal use of those symbols in CHOLMOD, KLU, SPQR, + # UMFPACK, and ParU. + # This can be disabled with -D__NOEXTENSIONS__ + message ( STATUS "MacOS: disable extensions in math.h" ) + add_compile_definitions ( "__NOEXTENSIONS__" ) +endif ( ) + #------------------------------------------------------------------------------- # check if Fortran is available and enabled #------------------------------------------------------------------------------- include ( CheckLanguage ) -option ( NFORTRAN "ON: do not try to use Fortran. OFF (default): try Fortran" off ) -if ( NFORTRAN ) - message ( STATUS "Fortran: not enabled" ) -else ( ) +option ( SUITESPARSE_USE_FORTRAN "ON (default): use Fortran. OFF: do not use Fortran" ON ) +if ( SUITESPARSE_USE_FORTRAN ) check_language ( Fortran ) if ( CMAKE_Fortran_COMPILER ) enable_language ( Fortran ) - message ( STATUS "Fortran: ${CMAKE_Fortran_COMPILER}" ) + message ( STATUS "Fortran: ${CMAKE_Fortran_COMPILER}" ) + set ( SUITESPARSE_HAS_FORTRAN ON ) else ( ) # Fortran not available: - set ( NFORTRAN true ) - message ( STATUS "Fortran: not available" ) + set ( SUITESPARSE_HAS_FORTRAN OFF ) + message ( STATUS "Fortran: not available" ) endif ( ) +else ( ) + message ( STATUS "Fortran: not enabled" ) + set ( SUITESPARSE_HAS_FORTRAN OFF ) +endif ( ) + +# check for strict usage +if ( SUITESPARSE_USE_STRICT AND SUITESPARSE_USE_FORTRAN AND NOT SUITESPARSE_HAS_FORTRAN ) + message ( FATAL_ERROR "Fortran required for SuiteSparse but not found" ) endif ( ) # default C-to-Fortran name mangling if Fortran compiler not found @@ -222,48 +312,54 @@ endif ( ) # find CUDA #------------------------------------------------------------------------------- -if ( ENABLE_CUDA ) +option ( SUITESPARSE_USE_CUDA "ON (default): enable CUDA acceleration for SuiteSparse, OFF: do not use CUDA" ON ) + +if ( SUITESPARSE_USE_CUDA ) # try finding CUDA check_language ( CUDA ) - message ( STATUS "Looking for CUDA" ) + # message ( STATUS "Looking for CUDA" ) if ( CMAKE_CUDA_COMPILER ) # with CUDA: - message ( STATUS "Find CUDA tool kit:" ) + # message ( STATUS "Find CUDA tool kit:" ) # FindCUDAToolKit needs to have C or CXX enabled first (see above) - include ( FindCUDAToolkit ) - message ( STATUS "CUDA toolkit found: " ${CUDAToolkit_FOUND} ) - message ( STATUS "CUDA toolkit version: " ${CUDAToolkit_VERSION} ) - message ( STATUS "CUDA toolkit include: " ${CUDAToolkit_INCLUDE_DIRS} ) - message ( STATUS "CUDA toolkit lib dir: " ${CUDAToolkit_LIBRARY_DIR} ) + find_package ( CUDAToolkit ) + message ( STATUS "CUDA toolkit : " ${CUDAToolkit_FOUND} ) + message ( STATUS "CUDA toolkit ver: " ${CUDAToolkit_VERSION} ) + message ( STATUS "CUDA toolkit inc: " ${CUDAToolkit_INCLUDE_DIRS} ) + message ( STATUS "CUDA toolkit lib: " ${CUDAToolkit_LIBRARY_DIR} ) if ( CUDAToolkit_VERSION VERSION_LESS "11.2" ) # CUDA is present but too old - message ( STATUS "CUDA: not enabled (CUDA 11.2 or later required)" ) - set ( SUITESPARSE_CUDA off ) + message ( STATUS "CUDA: not enabled (CUDA 11.2 or later required)" ) + set ( SUITESPARSE_HAS_CUDA OFF ) else ( ) # CUDA 11.2 or later present enable_language ( CUDA ) - set ( SUITESPARSE_CUDA on ) + set ( SUITESPARSE_HAS_CUDA ON ) endif ( ) else ( ) # without CUDA: - message ( STATUS "CUDA: not found" ) - set ( SUITESPARSE_CUDA off ) + message ( STATUS "CUDA: not found" ) + set ( SUITESPARSE_HAS_CUDA OFF ) endif ( ) else ( ) # CUDA is disabled - set ( SUITESPARSE_CUDA off ) + set ( SUITESPARSE_HAS_CUDA OFF ) endif ( ) -if ( SUITESPARSE_CUDA ) - message ( STATUS "CUDA: enabled" ) - add_compile_definitions ( SUITESPARSE_CUDA ) +if ( SUITESPARSE_HAS_CUDA ) + message ( STATUS "CUDA: enabled" ) set ( SUITESPARSE_CUDA_ARCHITECTURES "52;75;80" CACHE STRING "CUDA architectures" ) set ( CMAKE_CUDA_ARCHITECTURES ${SUITESPARSE_CUDA_ARCHITECTURES} ) else ( ) - message ( STATUS "CUDA: not enabled" ) + message ( STATUS "CUDA: not enabled" ) +endif ( ) + +# check for strict usage +if ( SUITESPARSE_USE_STRICT AND SUITESPARSE_USE_CUDA AND NOT SUITESPARSE_HAS_CUDA ) + message ( FATAL_ERROR "CUDA required for SuiteSparse but not found" ) endif ( ) diff --git a/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseReport.cmake b/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseReport.cmake index 9271c4a920..0b47f34c13 100644 --- a/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseReport.cmake +++ b/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseReport.cmake @@ -10,21 +10,15 @@ #------------------------------------------------------------------------------- message ( STATUS "------------------------------------------------------------------------" ) -message ( STATUS "SuiteSparse CMAKE report for: ${CMAKE_PROJECT_NAME}" ) +message ( STATUS "SuiteSparse CMAKE report for: ${PROJECT_NAME}" ) message ( STATUS "------------------------------------------------------------------------" ) -message ( STATUS "inside common SuiteSparse root: ${INSIDE_SUITESPARSE}" ) -message ( STATUS "install in SuiteSparse/lib and SuiteSparse/include: ${LOCAL_INSTALL}" ) -message ( STATUS "build type: ${CMAKE_BUILD_TYPE}" ) -if ( NSTATIC ) - message ( STATUS "NSTATIC: true (do not build static library)" ) -else ( ) - message ( STATUS "NSTATIC: false (build static library)" ) -endif ( ) -if ( OPENMP_FOUND ) - message ( STATUS "use OpenMP: yes ") -else ( ) - message ( STATUS "use OpenMP: no ") +if ( NOT SUITESPARSE_ROOT_CMAKELISTS ) + message ( STATUS "inside common SuiteSparse root: ${INSIDE_SUITESPARSE}" ) + message ( STATUS "install in SuiteSparse/lib and SuiteSparse/include: ${SUITESPARSE_LOCAL_INSTALL}" ) endif ( ) +message ( STATUS "build type: ${CMAKE_BUILD_TYPE}" ) +message ( STATUS "BUILD_SHARED_LIBS: ${BUILD_SHARED_LIBS}" ) +message ( STATUS "BUILD_STATIC_LIBS: ${BUILD_STATIC_LIBS}" ) message ( STATUS "C compiler: ${CMAKE_C_COMPILER} ") message ( STATUS "C flags: ${CMAKE_C_FLAGS}" ) message ( STATUS "C++ compiler: ${CMAKE_CXX_COMPILER}" ) @@ -36,10 +30,10 @@ else ( ) message ( STATUS "C Flags release: ${CMAKE_C_FLAGS_RELEASE} ") message ( STATUS "C++ Flags release: ${CMAKE_CXX_FLAGS_RELEASE} ") endif ( ) -if ( NFORTRAN ) - message ( STATUS "Fortran compiler: none" ) -else ( ) +if ( SUITESPARSE_HAS_FORTRAN ) message ( STATUS "Fortran compiler: ${CMAKE_Fortran_COMPILER} " ) +else ( ) + message ( STATUS "Fortran compiler: none" ) endif ( ) get_property ( CDEFN DIRECTORY PROPERTY COMPILE_DEFINITIONS ) message ( STATUS "compile definitions: ${CDEFN}") @@ -49,7 +43,4 @@ endif ( ) if ( DEFINED CMAKE_CUDA_ARCHITECTURES ) message ( STATUS "CUDA architectures: ${CMAKE_CUDA_ARCHITECTURES}" ) endif ( ) -if ( NPARTITION ) - message ( STATUS "NPARTITION: do not use METIS" ) -endif ( ) message ( STATUS "------------------------------------------------------------------------" ) diff --git a/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparse__thread.cmake b/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparse__thread.cmake new file mode 100644 index 0000000000..1c52185b7a --- /dev/null +++ b/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparse__thread.cmake @@ -0,0 +1,72 @@ +#------------------------------------------------------------------------------- +# GraphBLAS/cmake_modules/SuiteSparse__thread.cmake +#------------------------------------------------------------------------------- + +# Copyright (c) 2017-2023, Timothy A. Davis. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +#------------------------------------------------------------------------------- + +# determine if the __thread keyword is available, which is an extension by +# gcc but supported by many compilers, to indicate thread-local-storage. + +include ( CheckCSourceCompiles ) + +set ( thread_src +" #include + __thread int x = 1 ; + int main (void) + { + x = 0 ; + return (x) ; + } +" ) + +set ( declspec_thread_src +" #include + __declspec ( thread ) int x = 1 ; + int main (void) + { + x = 0 ; + return (x) ; + } +" ) + +set ( thread_local_src +" #include + #include + _Thread_local int x = 1 ; + int main (void) + { + x = 0 ; + return (x) ; + } +" ) + +check_c_source_compiles ( "${declspec_thread_src}" HAVE_KEYWORD__DECLSPEC_THREAD ) + +check_c_source_compiles ( "${thread_src}" HAVE_KEYWORD__THREAD ) + +check_c_source_compiles ( "${thread_local_src}" HAVE_KEYWORD__THREAD_LOCAL ) + +if ( HAVE_KEYWORD__DECLSPEC_THREAD ) + add_compile_definitions ( HAVE_KEYWORD__DECLSPEC_THREAD ) + message ( STATUS "__declspec(thread) keyword: available" ) +else ( ) + message ( STATUS "__declspec(thread) keyword: not available" ) +endif ( ) + +if ( HAVE_KEYWORD__THREAD ) + add_compile_definitions ( HAVE_KEYWORD__THREAD ) + message ( STATUS "__thread keyword: available" ) +else ( ) + message ( STATUS "__thread keyword: not available" ) +endif ( ) + +if ( HAVE_KEYWORD__THREAD_LOCAL ) + add_compile_definitions ( HAVE_KEYWORD__THREAD_LOCAL ) + message ( STATUS "_Thread_local keyword: available" ) +else ( ) + message ( STATUS "_Thread_local_thread keyword: not available" ) +endif ( ) + diff --git a/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparse_ssize_t.cmake b/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparse_ssize_t.cmake deleted file mode 100644 index 2b74bae83b..0000000000 --- a/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparse_ssize_t.cmake +++ /dev/null @@ -1,32 +0,0 @@ -#------------------------------------------------------------------------------- -# SuiteSparse/cmake_modules/SuiteSparse_ssize_t.cmake -#------------------------------------------------------------------------------- - -# Copyright (c) 2023, Timothy A. Davis. All Rights Reserved. -# SPDX-License-Identifier: BSD-3-clause - -#------------------------------------------------------------------------------- - -# determine if the compiler defines ssize_t - -include ( CheckCSourceCompiles ) - -set ( ssize_t_source -" #include - int main (void) - { - ssize_t x = 0 ; - return (0) ; - } -" ) - -check_c_source_compiles ( "${ssize_t_source}" TEST_FOR_SSIZE_T ) - -if ( TEST_FOR_SSIZE_T ) - set ( HAVE_SSIZE_T true ) - message ( STATUS "#include and ssize_t: OK" ) -else ( ) - set ( HAVE_SSIZE_T false ) - message ( STATUS "#include and ssize_t: not found" ) -endif ( ) - diff --git a/ThirdParty/SuiteSparse/build/.gitignore b/ThirdParty/SuiteSparse/build/.gitignore new file mode 100644 index 0000000000..52e15321b7 --- /dev/null +++ b/ThirdParty/SuiteSparse/build/.gitignore @@ -0,0 +1,4 @@ +# Ignore all files except this file. +* +*/ +!.gitignore diff --git a/ThirdParty/SuiteSparse/lib/.gitignore b/ThirdParty/SuiteSparse/lib/.gitignore new file mode 100644 index 0000000000..5e7d2734cf --- /dev/null +++ b/ThirdParty/SuiteSparse/lib/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore diff --git a/cmake/AmiciConfig.cmake b/cmake/AmiciConfig.cmake index cb7d57d51a..ebef45d6e0 100644 --- a/cmake/AmiciConfig.cmake +++ b/cmake/AmiciConfig.cmake @@ -1,5 +1,12 @@ @PACKAGE_INIT@ +# TODO remove after cmake files for test models have been regenerated +# cmake >=3.27 +if(POLICY CMP0144) + cmake_policy(SET CMP0144 NEW) +endif(POLICY CMP0144) + + include(CMakeFindDependencyMacro) find_package(OpenMP) diff --git a/python/sdist/setup.py b/python/sdist/setup.py index ed65127f7a..0da5ac9878 100755 --- a/python/sdist/setup.py +++ b/python/sdist/setup.py @@ -50,9 +50,15 @@ def get_extensions(): source_dir="amici/ThirdParty/SuiteSparse/SuiteSparse_config", cmake_configure_options=[ *global_cmake_configure_options, - "-DBLA_VENDOR=All", - "-DENABLE_CUDA=FALSE", - "-DNFORTRAN=TRUE", + "-DCMAKE_POSITION_INDEPENDENT_CODE=ON", + # Building SuiteSparse_config does not require a BLAS + # we just set BLAS_LIBRARIES to skip the search, + # the value is not used + # "-DBLA_VENDOR=All", + "-DBLAS_LIBRARIES=dummy", + "-DSUITESPARSE_USE_64BIT_BLAS=ON", + "-DSUITESPARSE_USE_CUDA=OFF", + "-DSUITESPARSE_USE_FORTRAN=OFF", ], ) # SuiteSparse AMD @@ -62,7 +68,7 @@ def get_extensions(): source_dir="amici/ThirdParty/SuiteSparse/AMD", cmake_configure_options=[ *global_cmake_configure_options, - "-DNFORTRAN=TRUE", + "-DSUITESPARSE_USE_FORTRAN=OFF", ], ) # SuiteSparse BTF @@ -72,7 +78,7 @@ def get_extensions(): source_dir="amici/ThirdParty/SuiteSparse/BTF", cmake_configure_options=[ *global_cmake_configure_options, - "-DNFORTRAN=TRUE", + "-DSUITESPARSE_USE_FORTRAN=OFF", ], ) # SuiteSparse COLAMD @@ -82,7 +88,7 @@ def get_extensions(): source_dir="amici/ThirdParty/SuiteSparse/COLAMD", cmake_configure_options=[ *global_cmake_configure_options, - "-DNFORTRAN=TRUE", + "-DSUITESPARSE_USE_FORTRAN=OFF", ], ) # SuiteSparse KLU @@ -92,9 +98,9 @@ def get_extensions(): source_dir="amici/ThirdParty/SuiteSparse/KLU", cmake_configure_options=[ *global_cmake_configure_options, - "-DNCHOLMOD=ON", - "-DENABLE_CUDA=FALSE", - "-DNFORTRAN=TRUE", + "-DKLU_USE_CHOLMOD=OFF", + "-DSUITESPARSE_USE_CUDA=OFF", + "-DSUITESPARSE_USE_FORTRAN=OFF", ], ) # SUNDIALS @@ -120,7 +126,7 @@ def get_extensions(): # be replaced by the actual path by `AmiciBuildCMakeExtension` # before being passed to CMake. "-DKLU_LIBRARY_DIR='${build_dir}/amici/lib'", - "-DKLU_INCLUDE_DIR='${build_dir}/amici/include'", + "-DKLU_INCLUDE_DIR='${build_dir}/amici/include/suitesparse'", ], ) # AMICI diff --git a/scripts/buildSuiteSparse.sh b/scripts/buildSuiteSparse.sh index e916530de6..93341810f8 100755 --- a/scripts/buildSuiteSparse.sh +++ b/scripts/buildSuiteSparse.sh @@ -8,7 +8,14 @@ script_path=$(dirname "$BASH_SOURCE") amici_path=$(cd "$script_path/.." && pwd) suitesparse_root="${amici_path}/ThirdParty/SuiteSparse" -export CMAKE_OPTIONS="-DBLA_VENDOR=All -DENABLE_CUDA=FALSE -DNFORTRAN=TRUE -DNCHOLMOD=TRUE" -for subdir in SuiteSparse_config BTF AMD COLAMD KLU - do cd "${suitesparse_root}/${subdir}" && make local install +for subdir in SuiteSparse_config BTF AMD COLAMD KLU; do + export CMAKE_OPTIONS="-DSUITESPARSE_USE_CUDA=OFF -DSUITESPARSE_USE_FORTRAN=OFF" + + if [ $subdir = "SuiteSparse_config" ]; then + export CMAKE_OPTIONS="$CMAKE_OPTIONS -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DBLA_VENDOR=All -DSUITESPARSE_USE_64BIT_BLAS=ON -DBLAS_LIBRARIES=dummy" + elif [ $subdir = "KLU" ]; then + export CMAKE_OPTIONS="$CMAKE_OPTIONS -DKLU_USE_CHOLMOD=OFF" + fi + + cd "${suitesparse_root}/${subdir}" && make local install done diff --git a/scripts/buildSundials.sh b/scripts/buildSundials.sh index c898a17d05..f3410ef31a 100755 --- a/scripts/buildSundials.sh +++ b/scripts/buildSundials.sh @@ -48,7 +48,7 @@ ${cmake} -DCMAKE_INSTALL_PREFIX="${sundials_build_path}" \ -DEXAMPLES_INSTALL=OFF \ -DENABLE_KLU=ON \ -DKLU_LIBRARY_DIR="${suitesparse_root}/lib" \ - -DKLU_INCLUDE_DIR="${suitesparse_root}/include" \ + -DKLU_INCLUDE_DIR="${suitesparse_root}/include/suitesparse" \ ${SuperLUMT} \ .. From 5e386ee7bab32b4d53f8277e2ce8099f36e9f220 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Tue, 27 Feb 2024 09:00:43 +0100 Subject: [PATCH 039/188] Don't eliminate parameters that are initial assignment targets (pt2) (#2305) Currently, parameters that are targets of initial assignments don't show up as parameters or expressions in the amici model. This is rather not what most users would expect. Therefore, treat all SBML parameters that are initial assignment targets and whose initial assignment does not evaluate to a number (for those that do, see #2304) as amici expressions. Those static expressions will be handled more efficiently after #2303. Related to #2150. See also #2304. --- python/sdist/amici/sbml_import.py | 17 +++++++++-------- tests/testSBMLSuite.py | 4 +++- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/python/sdist/amici/sbml_import.py b/python/sdist/amici/sbml_import.py index 8fc2ab9fd9..11ffb16599 100644 --- a/python/sdist/amici/sbml_import.py +++ b/python/sdist/amici/sbml_import.py @@ -722,7 +722,7 @@ def _gather_base_locals( "INF": sp.oo, "NaN": sp.nan, "rem": sp.Mod, - "time": symbol_with_assumptions("time"), + "time": sbml_time_symbol, # SBML L3 explicitly defines this value, which is not equal # to the most recent SI definition. "avogadro": sp.Float(6.02214179e23), @@ -1108,11 +1108,13 @@ def _process_parameters( } # Parameters that need to be turned into expressions - # so far, this concerns parameters with initial assignments containing rateOf(.) - # (those have been skipped above) + # so far, this concerns parameters with symbolic initial assignments + # (those have been skipped above) that are not rate rule targets for par in self.sbml.getListOfParameters(): - if (ia := par_id_to_ia.get(par.getId())) is not None and ia.find( - sp.core.function.UndefinedFunction("rateOf") + if ( + (ia := par_id_to_ia.get(par.getId())) is not None + and not ia.is_Number + and not self.is_rate_rule_target(par) ): self.symbols[SymbolId.EXPRESSION][ _get_identifier_symbol(par) @@ -1890,6 +1892,7 @@ def _process_initial_assignments(self): if identifier in itt.chain( self.symbols[SymbolId.SPECIES], self.compartments, + self.symbols[SymbolId.EXPRESSION], self.symbols[SymbolId.PARAMETER], self.symbols[SymbolId.FIXED_PARAMETER], ): @@ -1965,9 +1968,7 @@ def _make_initial( if "init" in species: sym_math = smart_subs(sym_math, species_id, species["init"]) - sym_math = smart_subs( - sym_math, self._local_symbols["time"], sp.Float(0) - ) + sym_math = smart_subs(sym_math, sbml_time_symbol, sp.Float(0)) sym_math = _dummy_to_rateof(sym_math, rateof_to_dummy) diff --git a/tests/testSBMLSuite.py b/tests/testSBMLSuite.py index cfad477ac4..2feb3ea7e8 100755 --- a/tests/testSBMLSuite.py +++ b/tests/testSBMLSuite.py @@ -130,10 +130,12 @@ def verify_results(settings, rdata, expected, wrapper, model, atol, rtol): # collect parameters for par in model.getParameterIds(): simulated[par] = rdata["ts"] * 0 + model.getParameterById(par) - # collect fluxes + # collect fluxes and other expressions for expr_idx, expr_id in enumerate(model.getExpressionIds()): if expr_id.startswith("flux_"): simulated[expr_id.removeprefix("flux_")] = rdata.w[:, expr_idx] + elif expr_id.removeprefix("amici_") not in simulated.columns: + simulated[expr_id] = rdata.w[:, expr_idx] # handle renamed reserved symbols simulated.rename( columns={c: c.replace("amici_", "") for c in simulated.columns}, From 6a1cc3c14aa2542474304a4fe4afbdb96492dd25 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Tue, 27 Feb 2024 12:37:11 +0100 Subject: [PATCH 040/188] Refactor DEModel, remove SbmlImporter dependency (#2312) `SbmlImporter` should construct a `DEModel`, instead of `DEModel` importing an `SbmlImporter`. Will allow moving `DEModel` to a separate module. Closes #2308. --- python/sdist/amici/de_export.py | 296 +++++------------------------- python/sdist/amici/de_model.py | 20 ++ python/sdist/amici/sbml_import.py | 172 ++++++++++++++++- python/sdist/amici/splines.py | 6 +- 4 files changed, 237 insertions(+), 257 deletions(-) diff --git a/python/sdist/amici/de_export.py b/python/sdist/amici/de_export.py index 37958edf97..be5a70e5cc 100644 --- a/python/sdist/amici/de_export.py +++ b/python/sdist/amici/de_export.py @@ -1,13 +1,15 @@ """ C++ Export ---------- -This module provides all necessary functionality specify a DE model and -generate executable C++ simulation code. The user generally won't have to -directly call any function from this module as this will be done by +This module provides all necessary functionality to specify a differential +equation model and generate executable C++ simulation code. +The user generally won't have to directly call any function from this module +as this will be done by :py:func:`amici.pysb_import.pysb2amici`, :py:func:`amici.sbml_import.SbmlImporter.sbml2amici` and :py:func:`amici.petab_import.import_model`. """ +from __future__ import annotations import contextlib import copy import itertools @@ -22,8 +24,6 @@ TYPE_CHECKING, Callable, Literal, - Optional, - Union, ) from collections.abc import Sequence import numpy as np @@ -40,7 +40,6 @@ splines, ) from ._codegen.template import apply_template -from .constants import SymbolId from .cxxcodeprinter import ( AmiciCxxCodePrinter, get_switch_statement, @@ -51,7 +50,6 @@ ObservableTransformation, SBMLException, amici_time_symbol, - generate_flux_symbol, smart_subs_dict, strip_pysb, toposort_symbols, @@ -70,7 +68,7 @@ ) if TYPE_CHECKING: - from . import sbml_import + from .splines import AbstractSpline # Template for model simulation main.cpp file @@ -497,25 +495,6 @@ def var_in_function_signature(name: str, varname: str, ode: bool) -> bool: ) -# defines the type of some attributes in DEModel -symbol_to_type = { - SymbolId.SPECIES: DifferentialState, - SymbolId.ALGEBRAIC_STATE: AlgebraicState, - SymbolId.ALGEBRAIC_EQUATION: AlgebraicEquation, - SymbolId.PARAMETER: Parameter, - SymbolId.FIXED_PARAMETER: Constant, - SymbolId.OBSERVABLE: Observable, - SymbolId.EVENT_OBSERVABLE: EventObservable, - SymbolId.SIGMAY: SigmaY, - SymbolId.SIGMAZ: SigmaZ, - SymbolId.LLHY: LogLikelihoodY, - SymbolId.LLHZ: LogLikelihoodZ, - SymbolId.LLHRZ: LogLikelihoodRZ, - SymbolId.EXPRESSION: Expression, - SymbolId.EVENT: Event, -} - - class DEModel: """ Defines a Differential Equation as set of ModelQuantities. @@ -639,8 +618,8 @@ class DEModel: def __init__( self, - verbose: Optional[Union[bool, int]] = False, - simplify: Optional[Callable] = _default_simplify, + verbose: bool | int | None = False, + simplify: Callable | None = _default_simplify, cache_simplify: bool = False, ): """ @@ -672,7 +651,7 @@ def __init__( self._expressions: list[Expression] = [] self._conservation_laws: list[ConservationLaw] = [] self._events: list[Event] = [] - self._splines = [] + self._splines: list[AbstractSpline] = [] self._symboldim_funs: dict[str, Callable[[], int]] = { "sx": self.num_states_solver, "v": self.num_states_solver, @@ -683,19 +662,15 @@ def __init__( } self._eqs: dict[ str, - Union[ - sp.Matrix, - sp.SparseMatrix, - list[Union[sp.Matrix, sp.SparseMatrix]], - ], + (sp.Matrix | sp.SparseMatrix | list[sp.Matrix | sp.SparseMatrix]), ] = dict() - self._sparseeqs: dict[str, Union[sp.Matrix, list[sp.Matrix]]] = dict() + self._sparseeqs: dict[str, sp.Matrix | list[sp.Matrix]] = dict() self._vals: dict[str, list[sp.Expr]] = dict() self._names: dict[str, list[str]] = dict() - self._syms: dict[str, Union[sp.Matrix, list[sp.Matrix]]] = dict() - self._sparsesyms: dict[str, Union[list[str], list[list[str]]]] = dict() - self._colptrs: dict[str, Union[list[int], list[list[int]]]] = dict() - self._rowvals: dict[str, Union[list[int], list[list[int]]]] = dict() + self._syms: dict[str, sp.Matrix | list[sp.Matrix]] = dict() + self._sparsesyms: dict[str, list[str] | list[list[str]]] = dict() + self._colptrs: dict[str, list[int] | list[list[int]]] = dict() + self._rowvals: dict[str, list[int] | list[list[int]]] = dict() self._equation_prototype: dict[str, Callable] = { "total_cl": self.conservation_laws, @@ -726,7 +701,7 @@ def __init__( "k": self.constants, } self._total_derivative_prototypes: dict[ - str, dict[str, Union[str, list[str]]] + str, dict[str, str | list[str]] ] = { "sroot": { "eq": "root", @@ -742,7 +717,7 @@ def __init__( def cached_simplify( expr: sp.Expr, - _simplified: dict[str, sp.Expr] = {}, + _simplified: dict[str, sp.Expr] = {}, # noqa B006 _simplify: Callable = simplify, ) -> sp.Expr: """Speed up expression simplification with caching. @@ -769,7 +744,7 @@ def cached_simplify( return _simplified[expr_str] self._simplify = cached_simplify - self._x0_fixedParameters_idx: Union[None, Sequence[int]] + self._x0_fixedParameters_idx: None | Sequence[int] self._w_recursion_depth: int = 0 self._has_quadratic_nllh: bool = True set_log_level(logger, verbose) @@ -838,188 +813,6 @@ def states(self) -> list[State]: """Get all states.""" return self._differential_states + self._algebraic_states - @log_execution_time("importing SbmlImporter", logger) - def import_from_sbml_importer( - self, - si: "sbml_import.SbmlImporter", - compute_cls: Optional[bool] = True, - ) -> None: - """ - Imports a model specification from a - :class:`amici.sbml_import.SbmlImporter` instance. - - :param si: - imported SBML model - :param compute_cls: - whether to compute conservation laws - """ - - # add splines as expressions to the model - # saved for later substituting into the fluxes - spline_subs = {} - - for ispl, spl in enumerate(si.splines): - spline_expr = spl.ode_model_symbol(si) - spline_subs[spl.sbml_id] = spline_expr - self.add_component( - Expression( - identifier=spl.sbml_id, - name=str(spl.sbml_id), - value=spline_expr, - ) - ) - self._splines = si.splines - - # get symbolic expression from SBML importers - symbols = copy.copy(si.symbols) - - # assemble fluxes and add them as expressions to the model - assert len(si.flux_ids) == len(si.flux_vector) - fluxes = [ - generate_flux_symbol(ir, name=flux_id) - for ir, flux_id in enumerate(si.flux_ids) - ] - - # correct time derivatives for compartment changes - def transform_dxdt_to_concentration(species_id, dxdt): - """ - Produces the appropriate expression for the first derivative of a - species with respect to time, for species that reside in - compartments with a constant volume, or a volume that is defined by - an assignment or rate rule. - - :param species_id: - The identifier of the species (generated in "sbml_import.py"). - - :param dxdt: - The element-wise product of the row in the stoichiometric - matrix that corresponds to the species (row x_index) and the - flux (kinetic laws) vector. Ignored in the case of rate rules. - """ - # The derivation of the below return expressions can be found in - # the documentation. They are found by rearranging - # $\frac{d}{dt} (vx) = Sw$ for $\frac{dx}{dt}$, where $v$ is the - # vector of species compartment volumes, $x$ is the vector of - # species concentrations, $S$ is the stoichiometric matrix, and $w$ - # is the flux vector. The conditional below handles the cases of - # species in (i) compartments with a rate rule, (ii) compartments - # with an assignment rule, and (iii) compartments with a constant - # volume, respectively. - species = si.symbols[SymbolId.SPECIES][species_id] - - comp = species["compartment"] - if comp in si.symbols[SymbolId.SPECIES]: - dv_dt = si.symbols[SymbolId.SPECIES][comp]["dt"] - xdot = (dxdt - dv_dt * species_id) / comp - return xdot - elif comp in si.compartment_assignment_rules: - v = si.compartment_assignment_rules[comp] - - # we need to flatten out assignments in the compartment in - # order to ensure that we catch all species dependencies - v = smart_subs_dict( - v, si.symbols[SymbolId.EXPRESSION], "value" - ) - dv_dt = v.diff(amici_time_symbol) - # we may end up with a time derivative of the compartment - # volume due to parameter rate rules - comp_rate_vars = [ - p - for p in v.free_symbols - if p in si.symbols[SymbolId.SPECIES] - ] - for var in comp_rate_vars: - dv_dt += ( - v.diff(var) * si.symbols[SymbolId.SPECIES][var]["dt"] - ) - dv_dx = v.diff(species_id) - xdot = (dxdt - dv_dt * species_id) / (dv_dx * species_id + v) - return xdot - elif comp in si.symbols[SymbolId.ALGEBRAIC_STATE]: - raise SBMLException( - f"Species {species_id} is in a compartment {comp} that is" - f" defined by an algebraic equation. This is not" - f" supported." - ) - else: - v = si.compartments[comp] - - if v == 1.0: - return dxdt - - return dxdt / v - - # create dynamics without respecting conservation laws first - dxdt = smart_multiply( - si.stoichiometric_matrix, MutableDenseMatrix(fluxes) - ) - for ix, ((species_id, species), formula) in enumerate( - zip(symbols[SymbolId.SPECIES].items(), dxdt) - ): - # rate rules and amount species don't need to be updated - if "dt" in species: - continue - if species["amount"]: - species["dt"] = formula - else: - species["dt"] = transform_dxdt_to_concentration( - species_id, formula - ) - - # create all basic components of the DE model and add them. - for symbol_name in symbols: - # transform dict of lists into a list of dicts - args = ["name", "identifier"] - - if symbol_name == SymbolId.SPECIES: - args += ["dt", "init"] - elif symbol_name == SymbolId.ALGEBRAIC_STATE: - args += ["init"] - else: - args += ["value"] - - if symbol_name == SymbolId.EVENT: - args += ["state_update", "initial_value"] - elif symbol_name == SymbolId.OBSERVABLE: - args += ["transformation"] - elif symbol_name == SymbolId.EVENT_OBSERVABLE: - args += ["event"] - - comp_kwargs = [ - { - "identifier": var_id, - **{k: v for k, v in var.items() if k in args}, - } - for var_id, var in symbols[symbol_name].items() - ] - - for comp_kwarg in comp_kwargs: - self.add_component(symbol_to_type[symbol_name](**comp_kwarg)) - - # add fluxes as expressions, this needs to happen after base - # expressions from symbols have been parsed - for flux_id, flux in zip(fluxes, si.flux_vector): - # replace splines inside fluxes - flux = flux.subs(spline_subs) - self.add_component( - Expression(identifier=flux_id, name=str(flux_id), value=flux) - ) - - # process conservation laws - if compute_cls: - si.process_conservation_laws(self) - - # fill in 'self._sym' based on prototypes and components in ode_model - self.generate_basic_variables() - self._has_quadratic_nllh = all( - llh["dist"] - in ["normal", "lin-normal", "log-normal", "log10-normal"] - for llh in si.symbols[SymbolId.LLHY].values() - ) - - # substitute SBML-rateOf constructs - self._process_sbml_rate_of() - def _process_sbml_rate_of(self) -> None: """Substitute any SBML-rateOf constructs in the model equations""" rate_of_func = sp.core.function.UndefinedFunction("rateOf") @@ -1160,7 +953,7 @@ def get_rate(symbol: sp.Symbol): # ) def add_component( - self, component: ModelQuantity, insert_first: Optional[bool] = False + self, component: ModelQuantity, insert_first: bool | None = False ) -> None: """ Adds a new ModelQuantity to the model. @@ -1276,6 +1069,24 @@ def add_conservation_law( self.add_component(cl) self._differential_states[ix].set_conservation_law(cl) + def add_spline(self, spline: AbstractSpline, spline_expr: sp.Expr) -> None: + """Add a spline to the model. + + :param spline: + Spline instance to be added + :param spline_expr: + Sympy function representation of `spline` from + ``spline.ode_model_symbol()``. + """ + self._splines.append(spline) + self.add_component( + Expression( + identifier=spline.sbml_id, + name=str(spline.sbml_id), + value=spline_expr, + ) + ) + def get_observable_transformations(self) -> list[ObservableTransformation]: """ List of observable transformations @@ -1459,9 +1270,7 @@ def sparseeq(self, name) -> sp.Matrix: self._generate_sparse_symbol(name) return self._sparseeqs[name] - def colptrs( - self, name: str - ) -> Union[list[sp.Number], list[list[sp.Number]]]: + def colptrs(self, name: str) -> list[sp.Number] | list[list[sp.Number]]: """ Returns (and constructs if necessary) the column pointers for a sparsified symbolic variable. @@ -1478,9 +1287,7 @@ def colptrs( self._generate_sparse_symbol(name) return self._colptrs[name] - def rowvals( - self, name: str - ) -> Union[list[sp.Number], list[list[sp.Number]]]: + def rowvals(self, name: str) -> list[sp.Number] | list[list[sp.Number]]: """ Returns (and constructs if necessary) the row values for a sparsified symbolic variable. @@ -1534,8 +1341,7 @@ def free_symbols(self) -> set[sp.Basic]: """ return set( chain.from_iterable( - state.get_free_symbols() - for state in self.states() + self.algebraic_equations() + state.get_free_symbols() for state in self.states() ) ) @@ -2426,8 +2232,8 @@ def _multiplication( name: str, x: str, y: str, - transpose_x: Optional[bool] = False, - sign: Optional[int] = 1, + transpose_x: bool | None = False, + sign: int | None = 1, ): """ Creates a new symbolic variable according to a multiplication @@ -2629,7 +2435,7 @@ def _get_unique_root( self, root_found: sp.Expr, roots: list[Event], - ) -> Union[sp.Symbol, None]: + ) -> sp.Symbol | None: """ Collects roots of Heaviside functions and events and stores them in the roots list. It checks for redundancy to not store symbolically @@ -2803,13 +2609,13 @@ class DEExporter: def __init__( self, de_model: DEModel, - outdir: Optional[Union[Path, str]] = None, - verbose: Optional[Union[bool, int]] = False, - assume_pow_positivity: Optional[bool] = False, - compiler: Optional[str] = None, - allow_reinit_fixpar_initcond: Optional[bool] = True, - generate_sensitivity_code: Optional[bool] = True, - model_name: Optional[str] = "model", + outdir: Path | str | None = None, + verbose: bool | int | None = False, + assume_pow_positivity: bool | None = False, + compiler: str | None = None, + allow_reinit_fixpar_initcond: bool | None = True, + generate_sensitivity_code: bool | None = True, + model_name: str | None = "model", ): """ Generate AMICI C++ files for the DE provided to the constructor. @@ -3901,7 +3707,7 @@ def _write_module_setup(self) -> None: template_data, ) - def set_paths(self, output_dir: Optional[Union[str, Path]] = None) -> None: + def set_paths(self, output_dir: str | Path | None = None) -> None: """ Set output paths for the model and create if necessary diff --git a/python/sdist/amici/de_model.py b/python/sdist/amici/de_model.py index c20509407a..cb0c066e4d 100644 --- a/python/sdist/amici/de_model.py +++ b/python/sdist/amici/de_model.py @@ -13,6 +13,7 @@ generate_measurement_symbol, generate_regularization_symbol, ) +from .constants import SymbolId __all__ = [ "ConservationLaw", @@ -732,3 +733,22 @@ def get_trigger_time(self) -> sp.Float: "This event does not trigger at a fixed timepoint." ) return self._t_root[0] + + +# defines the type of some attributes in DEModel +symbol_to_type = { + SymbolId.SPECIES: DifferentialState, + SymbolId.ALGEBRAIC_STATE: AlgebraicState, + SymbolId.ALGEBRAIC_EQUATION: AlgebraicEquation, + SymbolId.PARAMETER: Parameter, + SymbolId.FIXED_PARAMETER: Constant, + SymbolId.OBSERVABLE: Observable, + SymbolId.EVENT_OBSERVABLE: EventObservable, + SymbolId.SIGMAY: SigmaY, + SymbolId.SIGMAZ: SigmaZ, + SymbolId.LLHY: LogLikelihoodY, + SymbolId.LLHZ: LogLikelihoodZ, + SymbolId.LLHRZ: LogLikelihoodRZ, + SymbolId.EXPRESSION: Expression, + SymbolId.EVENT: Event, +} diff --git a/python/sdist/amici/sbml_import.py b/python/sdist/amici/sbml_import.py index 11ffb16599..eb0b739186 100644 --- a/python/sdist/amici/sbml_import.py +++ b/python/sdist/amici/sbml_import.py @@ -32,7 +32,8 @@ DEExporter, DEModel, ) -from .sympy_utils import smart_is_zero_matrix +from .de_model import symbol_to_type, Expression +from .sympy_utils import smart_is_zero_matrix, smart_multiply from .import_utils import ( RESERVED_SYMBOLS, _check_unsupported_functions, @@ -50,10 +51,12 @@ symbol_with_assumptions, toposort_symbols, _default_simplify, + generate_flux_symbol, ) from .logging import get_logger, log_execution_time, set_log_level from .sbml_utils import SBMLException, _parse_logical_operators from .splines import AbstractSpline +from sympy.matrices.dense import MutableDenseMatrix SymbolicFormula = dict[sp.Symbol, sp.Expr] @@ -180,7 +183,7 @@ def __init__( self.species_assignment_rules: SymbolicFormula = {} self.parameter_assignment_rules: SymbolicFormula = {} self.initial_assignments: SymbolicFormula = {} - self.splines = [] + self.splines: list[AbstractSpline] = [] self._reset_symbols() @@ -534,9 +537,96 @@ def _build_ode_model( simplify=simplify, cache_simplify=cache_simplify, ) - ode_model.import_from_sbml_importer( - self, compute_cls=compute_conservation_laws + + ode_model._has_quadratic_nllh = all( + llh["dist"] + in ["normal", "lin-normal", "log-normal", "log10-normal"] + for llh in self.symbols[SymbolId.LLHY].values() + ) + + # add splines as expressions to the model + # saved for later substituting into the fluxes + spline_subs = {} + for ispl, spl in enumerate(self.splines): + spline_expr = spl.ode_model_symbol(self) + spline_subs[spl.sbml_id] = spline_expr + ode_model.add_spline(spl, spline_expr) + + # assemble fluxes and add them as expressions to the model + assert len(self.flux_ids) == len(self.flux_vector) + fluxes = [ + generate_flux_symbol(ir, name=flux_id) + for ir, flux_id in enumerate(self.flux_ids) + ] + + # create dynamics without respecting conservation laws first + dxdt = smart_multiply( + self.stoichiometric_matrix, MutableDenseMatrix(fluxes) ) + # correct time derivatives for compartment changes + for ix, ((species_id, species), formula) in enumerate( + zip(self.symbols[SymbolId.SPECIES].items(), dxdt) + ): + # rate rules and amount species don't need to be updated + if "dt" in species: + continue + if species["amount"]: + species["dt"] = formula + else: + species["dt"] = self._transform_dxdt_to_concentration( + species_id, formula + ) + + # create all basic components of the DE model and add them. + for symbol_name in self.symbols: + # transform dict of lists into a list of dicts + args = ["name", "identifier"] + + if symbol_name == SymbolId.SPECIES: + args += ["dt", "init"] + elif symbol_name == SymbolId.ALGEBRAIC_STATE: + args += ["init"] + else: + args += ["value"] + + if symbol_name == SymbolId.EVENT: + args += ["state_update", "initial_value"] + elif symbol_name == SymbolId.OBSERVABLE: + args += ["transformation"] + elif symbol_name == SymbolId.EVENT_OBSERVABLE: + args += ["event"] + + comp_kwargs = [ + { + "identifier": var_id, + **{k: v for k, v in var.items() if k in args}, + } + for var_id, var in self.symbols[symbol_name].items() + ] + + for comp_kwarg in comp_kwargs: + ode_model.add_component( + symbol_to_type[symbol_name](**comp_kwarg) + ) + + # add fluxes as expressions, this needs to happen after base + # expressions from symbols have been parsed + for flux_id, flux in zip(fluxes, self.flux_vector): + # replace splines inside fluxes + flux = flux.subs(spline_subs) + ode_model.add_component( + Expression(identifier=flux_id, name=str(flux_id), value=flux) + ) + + if compute_conservation_laws: + self._process_conservation_laws(ode_model) + + # fill in 'self._sym' based on prototypes and components in ode_model + ode_model.generate_basic_variables() + + # substitute SBML-rateOf constructs + ode_model._process_sbml_rate_of() + return ode_model @log_execution_time("importing SBML", logger) @@ -550,7 +640,7 @@ def _process_sbml( :param constant_parameters: SBML Ids identifying constant parameters - :param hardcode_parameters: + :param hardcode_symbols: Parameter IDs to be replaced by their values in the generated model. """ if not self._discard_annotations: @@ -1974,12 +2064,12 @@ def _make_initial( return sym_math - def process_conservation_laws(self, ode_model) -> None: + def _process_conservation_laws(self, ode_model: DEModel) -> None: """ Find conservation laws in reactions and species. :param ode_model: - ODEModel object with basic definitions + :class:`DEModel` object with basic definitions """ conservation_laws = [] @@ -2526,6 +2616,74 @@ def is_rate_rule_target(self, element: sbml.SBase) -> bool: a = self.sbml.getRateRuleByVariable(element.getId()) return a is not None and self._sympy_from_sbml_math(a) is not None + def _transform_dxdt_to_concentration( + self, species_id: sp.Symbol, dxdt: sp.Expr + ) -> sp.Expr: + """ + Produces the appropriate expression for the first derivative of a + species with respect to time, for species that reside in + compartments with a constant volume, or a volume that is defined by + an assignment or rate rule. + + :param species_id: + The identifier of the species (generated in "sbml_import.py"). + + :param dxdt: + The element-wise product of the row in the stoichiometric + matrix that corresponds to the species (row x_index) and the + flux (kinetic laws) vector. Ignored in the case of rate rules. + """ + # The derivation of the below return expressions can be found in + # the documentation. They are found by rearranging + # $\frac{d}{dt} (vx) = Sw$ for $\frac{dx}{dt}$, where $v$ is the + # vector of species compartment volumes, $x$ is the vector of + # species concentrations, $S$ is the stoichiometric matrix, and $w$ + # is the flux vector. The conditional below handles the cases of + # species in (i) compartments with a rate rule, (ii) compartments + # with an assignment rule, and (iii) compartments with a constant + # volume, respectively. + species = self.symbols[SymbolId.SPECIES][species_id] + + comp = species["compartment"] + if comp in self.symbols[SymbolId.SPECIES]: + dv_dt = self.symbols[SymbolId.SPECIES][comp]["dt"] + xdot = (dxdt - dv_dt * species_id) / comp + return xdot + elif comp in self.compartment_assignment_rules: + v = self.compartment_assignment_rules[comp] + + # we need to flatten out assignments in the compartment in + # order to ensure that we catch all species dependencies + v = smart_subs_dict(v, self.symbols[SymbolId.EXPRESSION], "value") + dv_dt = v.diff(amici_time_symbol) + # we may end up with a time derivative of the compartment + # volume due to parameter rate rules + comp_rate_vars = [ + p + for p in v.free_symbols + if p in self.symbols[SymbolId.SPECIES] + ] + for var in comp_rate_vars: + dv_dt += ( + v.diff(var) * self.symbols[SymbolId.SPECIES][var]["dt"] + ) + dv_dx = v.diff(species_id) + xdot = (dxdt - dv_dt * species_id) / (dv_dx * species_id + v) + return xdot + elif comp in self.symbols[SymbolId.ALGEBRAIC_STATE]: + raise SBMLException( + f"Species {species_id} is in a compartment {comp} that is" + f" defined by an algebraic equation. This is not" + f" supported." + ) + else: + v = self.compartments[comp] + + if v == 1.0: + return dxdt + + return dxdt / v + def _check_lib_sbml_errors( sbml_doc: sbml.SBMLDocument, show_warnings: bool = False diff --git a/python/sdist/amici/splines.py b/python/sdist/amici/splines.py index d55a78137b..ea0cd0e06d 100644 --- a/python/sdist/amici/splines.py +++ b/python/sdist/amici/splines.py @@ -52,6 +52,7 @@ pretty_xml, sbml_mathml, ) +from .constants import SymbolId logger = get_logger(__name__, logging.WARNING) @@ -569,8 +570,6 @@ def check_if_valid(self, importer: sbml_import.SbmlImporter) -> None: # the AMICI spline implementation. # If found, they should be checked for here # until (if at all) they are accounted for. - from .de_export import SymbolId - fixed_parameters: list[sp.Symbol] = list( importer.symbols[SymbolId.FIXED_PARAMETER].keys() ) @@ -1345,8 +1344,6 @@ def _from_annotation( def parameters(self, importer: sbml_import.SbmlImporter) -> set[sp.Symbol]: """Returns the SBML parameters used by this spline""" - from .de_export import SymbolId - return self._parameters().intersection( set(importer.symbols[SymbolId.PARAMETER].keys()) ) @@ -1659,7 +1656,6 @@ def check_if_valid(self, importer: sbml_import.SbmlImporter) -> None: for spline grid points, values, ... contain species symbols. """ # TODO this is very much a draft - from .de_export import SymbolId species: list[sp.Symbol] = list(importer.symbols[SymbolId.SPECIES]) for d in self.derivatives_at_nodes: From eb3fd4a45c59444d45e1a7b3c39de4c202906332 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Tue, 27 Feb 2024 13:40:23 +0100 Subject: [PATCH 041/188] Split expressions into static and dynamic (#2303) Split expressions in `w` and its derivatives into dynamic (explicitly or implicitly time-dependent) and static ones. Evaluate static ones only when needed, i.e. after (re)initializing x_rdata or parameters. See #1269 --- .github/actions/setup-amici-cpp/action.yml | 3 + include/amici/abstract_model.h | 46 ++-- include/amici/model.h | 36 ++- matlab/@amifun/getArgs.m | 6 +- matlab/@amimodel/generateC.m | 3 +- models/model_calvetti/CMakeLists.txt | 37 ++- models/model_calvetti/dwdx.cpp | 2 +- models/model_calvetti/main.cpp | 59 +++-- models/model_calvetti/model_calvetti.h | 18 +- models/model_calvetti/w.cpp | 2 +- models/model_dirac/CMakeLists.txt | 37 ++- models/model_dirac/main.cpp | 59 +++-- models/model_dirac/model_dirac.h | 10 +- models/model_events/CMakeLists.txt | 37 ++- models/model_events/main.cpp | 59 +++-- models/model_events/model_events.h | 10 +- models/model_jakstat_adjoint/CMakeLists.txt | 37 ++- models/model_jakstat_adjoint/dwdp.cpp | 2 +- models/model_jakstat_adjoint/dwdx.cpp | 2 +- models/model_jakstat_adjoint/main.cpp | 59 +++-- .../model_jakstat_adjoint.h | 22 +- models/model_jakstat_adjoint/w.cpp | 2 +- .../model_jakstat_adjoint_o2/CMakeLists.txt | 37 ++- models/model_jakstat_adjoint_o2/dwdp.cpp | 2 +- models/model_jakstat_adjoint_o2/dwdx.cpp | 2 +- models/model_jakstat_adjoint_o2/main.cpp | 59 +++-- .../model_jakstat_adjoint_o2.h | 22 +- models/model_jakstat_adjoint_o2/w.cpp | 2 +- models/model_nested_events/CMakeLists.txt | 37 ++- models/model_nested_events/main.cpp | 59 +++-- .../model_nested_events/model_nested_events.h | 10 +- models/model_neuron/CMakeLists.txt | 37 ++- models/model_neuron/main.cpp | 59 +++-- models/model_neuron/model_neuron.h | 10 +- models/model_neuron_o2/CMakeLists.txt | 37 ++- models/model_neuron_o2/dwdx.cpp | 2 +- models/model_neuron_o2/main.cpp | 59 +++-- models/model_neuron_o2/model_neuron_o2.h | 18 +- models/model_neuron_o2/w.cpp | 2 +- models/model_robertson/CMakeLists.txt | 37 ++- models/model_robertson/dwdp.cpp | 2 +- models/model_robertson/dwdx.cpp | 2 +- models/model_robertson/main.cpp | 59 +++-- models/model_robertson/model_robertson.h | 22 +- models/model_robertson/w.cpp | 2 +- models/model_steadystate/CMakeLists.txt | 37 ++- models/model_steadystate/dwdp.cpp | 2 +- models/model_steadystate/dwdx.cpp | 2 +- models/model_steadystate/main.cpp | 59 +++-- models/model_steadystate/model_steadystate.h | 22 +- models/model_steadystate/w.cpp | 2 +- python/sdist/amici/cxxcodeprinter.py | 18 +- python/sdist/amici/de_export.py | 224 +++++++++++++++++- src/abstract_model.cpp | 29 +-- src/model.cpp | 99 +++++--- src/model_dae.cpp | 6 +- src/model_ode.cpp | 4 +- src/solver.cpp | 13 +- swig/model.i | 1 + 59 files changed, 999 insertions(+), 645 deletions(-) diff --git a/.github/actions/setup-amici-cpp/action.yml b/.github/actions/setup-amici-cpp/action.yml index 09ec9311bf..b5ee4808b8 100644 --- a/.github/actions/setup-amici-cpp/action.yml +++ b/.github/actions/setup-amici-cpp/action.yml @@ -18,6 +18,9 @@ runs: - run: echo "ENABLE_GCOV_COVERAGE=TRUE" >> $GITHUB_ENV shell: bash + - run: echo "PYTHONFAULTHANDLER=1" >> $GITHUB_ENV + shell: bash + - name: Set up Sonar tools uses: ./.github/actions/setup-sonar-tools diff --git a/include/amici/abstract_model.h b/include/amici/abstract_model.h index d88347078d..f340214e7e 100644 --- a/include/amici/abstract_model.h +++ b/include/amici/abstract_model.h @@ -820,11 +820,15 @@ class AbstractModel { * @param h Heaviside vector * @param tcl total abundances for conservation laws * @param spl spline value vector + * @param include_static Whether to (re-)evaluate only dynamic expressions + * (false) or also static expressions (true). + * Dynamic expressions are those that depend directly or indirectly on time, + * static expressions are those that don't. */ virtual void fw(realtype* w, realtype const t, realtype const* x, realtype const* p, realtype const* k, realtype const* h, realtype const* tcl, - realtype const* spl); + realtype const* spl, bool include_static = true); /** * @brief Model-specific sparse implementation of dwdp @@ -840,12 +844,16 @@ class AbstractModel { * @param spl spline value vector * @param sspl sensitivities of spline values vector w.r.t. parameters \f$ p * \f$ + * @param include_static Whether to (re-)evaluate only dynamic expressions + * (false) or also static expressions (true). + * Dynamic expressions are those that depend directly or indirectly on time, + * static expressions are those that don't. */ virtual void fdwdp( realtype* dwdp, realtype const t, realtype const* x, realtype const* p, realtype const* k, realtype const* h, realtype const* w, realtype const* tcl, realtype const* stcl, realtype const* spl, - realtype const* sspl + realtype const* sspl, bool include_static = true ); /** @@ -860,28 +868,6 @@ class AbstractModel { */ virtual void fdwdp_rowvals(SUNMatrixWrapper& dwdp); - /** - * @brief Model-specific sensitivity implementation of dwdp - * @param dwdp Recurring terms in xdot, parameter derivative - * @param t timepoint - * @param x vector with the states - * @param p parameter vector - * @param k constants vector - * @param h Heaviside vector - * @param w vector with helper variables - * @param tcl total abundances for conservation laws - * @param stcl sensitivities of total abundances for conservation laws - * @param spl spline value vector - * @param sspl sensitivities of spline values vector - * @param ip sensitivity parameter index - */ - virtual void fdwdp( - realtype* dwdp, realtype const t, realtype const* x, realtype const* p, - realtype const* k, realtype const* h, realtype const* w, - realtype const* tcl, realtype const* stcl, realtype const* spl, - realtype const* sspl, int ip - ); - /** * @brief Model-specific implementation of dwdx, data part * @param dwdx Recurring terms in xdot, state derivative @@ -893,11 +879,15 @@ class AbstractModel { * @param w vector with helper variables * @param tcl total abundances for conservation laws * @param spl spline value vector + * @param include_static Whether to (re-)evaluate only dynamic expressions + * (false) or also static expressions (true). + * Dynamic expressions are those that depend directly or indirectly on time, + * static expressions are those that don't. */ virtual void fdwdx( realtype* dwdx, realtype const t, realtype const* x, realtype const* p, realtype const* k, realtype const* h, realtype const* w, - realtype const* tcl, realtype const* spl + realtype const* tcl, realtype const* spl, bool include_static = true ); /** @@ -922,11 +912,15 @@ class AbstractModel { * @param h Heaviside vector * @param w vector with helper variables * @param tcl Total abundances for conservation laws + * @param include_static Whether to (re-)evaluate only dynamic expressions + * (false) or also static expressions (true). + * Dynamic expressions are those that depend directly or indirectly on time, + * static expressions are those that don't. */ virtual void fdwdw( realtype* dwdw, realtype t, realtype const* x, realtype const* p, realtype const* k, realtype const* h, realtype const* w, - realtype const* tcl + realtype const* tcl, bool include_static = true ); /** diff --git a/include/amici/model.h b/include/amici/model.h index 23f6ac63c8..13de007e00 100644 --- a/include/amici/model.h +++ b/include/amici/model.h @@ -239,6 +239,18 @@ class Model : public AbstractModel, public ModelDimensions { bool computeSensitivities, std::vector& roots_found ); + /** + * @brief Re-initialize model properties after changing simulation context. + * @param t Timepoint + * @param x Reference to state variables + * @param sx Reference to state variable sensitivities + * @param computeSensitivities Flag indicating whether sensitivities are to + * be computed + */ + void reinitialize( + realtype t, AmiVector& x, AmiVectorArray& sx, bool computeSensitivities + ); + /** * @brief Initialize model properties. * @param xB Adjoint state variables @@ -1828,29 +1840,45 @@ class Model : public AbstractModel, public ModelDimensions { * @brief Compute recurring terms in xdot. * @param t Timepoint * @param x Array with the states + * @param include_static Whether to (re-)evaluate only dynamic expressions + * (false) or also static expressions (true). + * Dynamic expressions are those that depend directly or indirectly on time, + * static expressions are those that don't. */ - void fw(realtype t, realtype const* x); + void fw(realtype t, realtype const* x, bool include_static = true); /** * @brief Compute parameter derivative for recurring terms in xdot. * @param t Timepoint * @param x Array with the states + * @param include_static Whether to (re-)evaluate only dynamic expressions + * (false) or also static expressions (true). + * Dynamic expressions are those that depend directly or indirectly on time, + * static expressions are those that don't. */ - void fdwdp(realtype t, realtype const* x); + void fdwdp(realtype t, realtype const* x, bool include_static = true); /** * @brief Compute state derivative for recurring terms in xdot. * @param t Timepoint * @param x Array with the states + * @param include_static Whether to (re-)evaluate only dynamic expressions + * (false) or also static expressions (true). + * Dynamic expressions are those that depend directly or indirectly on time, + * static expressions are those that don't. */ - void fdwdx(realtype t, realtype const* x); + void fdwdx(realtype t, realtype const* x, bool include_static = true); /** * @brief Compute self derivative for recurring terms in xdot. * @param t Timepoint * @param x Array with the states + * @param include_static Whether to (re-)evaluate only dynamic expressions + * (false) or also static expressions (true). + * Dynamic expressions are those that depend directly or indirectly on time, + * static expressions are those that don't. */ - void fdwdw(realtype t, realtype const* x); + void fdwdw(realtype t, realtype const* x, bool include_static = true); /** * @brief Compute fx_rdata. diff --git a/matlab/@amifun/getArgs.m b/matlab/@amifun/getArgs.m index afb50802e0..a34c658ee5 100644 --- a/matlab/@amifun/getArgs.m +++ b/matlab/@amifun/getArgs.m @@ -108,11 +108,11 @@ case 'dJrzdsigma' this.argstr = '(double *dJrzdsigma, const int iz, const realtype *p, const realtype *k, const double *rz, const double *sigmaz)'; case 'w' - this.argstr = '(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl)'; + this.argstr = '(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl, bool include_static)'; case 'dwdp' - this.argstr = '(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl)'; + this.argstr = '(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl, bool include_static)'; case 'dwdx' - this.argstr = '(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl)'; + this.argstr = '(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl, bool include_static)'; case 'M' this.argstr = '(realtype *M, const realtype t, const realtype *x, const realtype *p, const realtype *k)'; otherwise diff --git a/matlab/@amimodel/generateC.m b/matlab/@amimodel/generateC.m index 869f386baf..2f09831b24 100644 --- a/matlab/@amimodel/generateC.m +++ b/matlab/@amimodel/generateC.m @@ -209,7 +209,7 @@ function generateC(this) end fprintf(fid,'};\n\n'); fprintf(fid,['} // namespace model_' this.modelname '\n\n']); -fprintf(fid,'} // namespace amici \n\n'); +fprintf(fid,'} // namespace amici\n\n'); fprintf(fid,['#endif /* _amici_' this.modelname '_h */\n']); fclose(fid); @@ -253,6 +253,7 @@ function generateC(this) argstr = strrep(argstr,'realtype',''); argstr = strrep(argstr,'int',''); +argstr = strrep(argstr,'bool',''); argstr = strrep(argstr,'const',''); argstr = strrep(argstr,'double',''); argstr = strrep(argstr,'SUNMatrixContent_Sparse',''); diff --git a/models/model_calvetti/CMakeLists.txt b/models/model_calvetti/CMakeLists.txt index 2d1347b553..30262f7de3 100644 --- a/models/model_calvetti/CMakeLists.txt +++ b/models/model_calvetti/CMakeLists.txt @@ -1,5 +1,11 @@ # Build AMICI model cmake_minimum_required(VERSION 3.15) +cmake_policy(VERSION 3.15...3.27) + +# cmake >=3.27 +if(POLICY CMP0144) + cmake_policy(SET CMP0144 NEW) +endif(POLICY CMP0144) project(model_calvetti) @@ -14,7 +20,7 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") endif() foreach(flag ${MY_CXX_FLAGS}) unset(CUR_FLAG_SUPPORTED CACHE) - check_cxx_compiler_flag(-Werror ${flag} CUR_FLAG_SUPPORTED) + check_cxx_compiler_flag(${flag} CUR_FLAG_SUPPORTED) if(${CUR_FLAG_SUPPORTED}) string(APPEND CMAKE_CXX_FLAGS " ${flag}") endif() @@ -33,6 +39,23 @@ find_package(Amici REQUIRED HINTS ${CMAKE_CURRENT_LIST_DIR}/../../build) message(STATUS "Found AMICI ${Amici_DIR}") +# Debug build? +if("$ENV{ENABLE_AMICI_DEBUGGING}" OR "$ENV{ENABLE_GCOV_COVERAGE}") + add_compile_options(-UNDEBUG) + if(MSVC) + add_compile_options(-DEBUG) + else() + add_compile_options(-O0 -g) + endif() + set(CMAKE_BUILD_TYPE "Debug") +endif() + +# coverage options +if($ENV{ENABLE_GCOV_COVERAGE}) + string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage") + string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " --coverage") +endif() + set(MODEL_DIR ${CMAKE_CURRENT_LIST_DIR}) set(SRC_LIST_LIB ${MODEL_DIR}/JSparse.cpp @@ -73,18 +96,6 @@ if(NOT "${AMICI_PYTHON_BUILD_EXT_ONLY}") target_link_libraries(simulate_${PROJECT_NAME} ${PROJECT_NAME}) endif() -# Debug build? -if("$ENV{ENABLE_AMICI_DEBUGGING}" OR "$ENV{ENABLE_GCOV_COVERAGE}") - add_compile_options(-UNDEBUG -O0 -g) - set(CMAKE_BUILD_TYPE "Debug") -endif() - -# coverage options -if($ENV{ENABLE_GCOV_COVERAGE}) - string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage") - string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " --coverage") -endif() - # SWIG option(ENABLE_SWIG "Build swig/python library?" ON) if(ENABLE_SWIG) diff --git a/models/model_calvetti/dwdx.cpp b/models/model_calvetti/dwdx.cpp index 9066d0e953..f185abd31f 100644 --- a/models/model_calvetti/dwdx.cpp +++ b/models/model_calvetti/dwdx.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_calvetti{ -void dwdx_model_calvetti(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl) { +void dwdx_model_calvetti(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl, bool include_static) { dwdx[0] = 1.0/(x[0]*x[0]*x[0])*-2.0; dwdx[1] = k[1]*w[15]*dwdx[0]; dwdx[2] = dwdx[1]; diff --git a/models/model_calvetti/main.cpp b/models/model_calvetti/main.cpp index 00a5e6b448..ecdff85a46 100644 --- a/models/model_calvetti/main.cpp +++ b/models/model_calvetti/main.cpp @@ -1,14 +1,13 @@ #include -#include /* AMICI base functions */ -#include "wrapfunctions.h" /* model-provided functions */ +#include "wrapfunctions.h" /* model-provided functions */ +#include /* AMICI base functions */ -template < class T > -std::ostream& operator << (std::ostream& os, const std::vector& v) -{ +template +std::ostream& operator<<(std::ostream& os, std::vector const& v) { os << "["; - for (typename std::vector::const_iterator ii = v.begin(); ii != v.end(); ++ii) - { + for (typename std::vector::const_iterator ii = v.begin(); ii != v.end(); + ++ii) { os << " " << *ii; } os << "]"; @@ -21,9 +20,9 @@ std::ostream& operator << (std::ostream& os, const std::vector& v) */ int main() { - std::cout<<"********************************"<getObservableIds(); - std::cout<<"Simulated observables for timepoints "<ts<<"\n\n"; - for(int i_observable = 0; i_observable < rdata->ny; ++i_observable) { - std::cout<nt; ++i_time) { + std::cout << "Simulated observables for timepoints " << rdata->ts << "\n\n"; + for (int i_observable = 0; i_observable < rdata->ny; ++i_observable) { + std::cout << observable_ids[i_observable] << ":\n\t"; + for (int i_time = 0; i_time < rdata->nt; ++i_time) { // rdata->y is a flat 2D array in row-major ordering - std::cout<y[i_time * rdata->ny + i_observable]<<" "; + std::cout << rdata->y[i_time * rdata->ny + i_observable] << " "; } - std::cout<setSensitivityOrder(amici::SensitivityOrder::first); @@ -78,18 +76,17 @@ int main() { auto state_ids = model->getStateIds(); auto parameter_ids = model->getParameterIds(); - std::cout<<"State sensitivities for timepoint " - <ts[i_time] - <nx; ++i_state) { - std::cout<<"\td("<plist(i_nplist)]<<") = "; + std::cout << "State sensitivities for timepoint " << rdata->ts[i_time] + << std::endl; // nt x nplist x nx + for (int i_state = 0; i_state < rdata->nx; ++i_state) { + std::cout << "\td(" << state_ids[i_state] << ")/d(" + << parameter_ids[model->plist(i_nplist)] << ") = "; // rdata->sx is a flat 3D array in row-major ordering - std::cout<sx[i_time * rdata->nplist * rdata->nx - + i_nplist * rdata->nx - + i_state]; - std::cout<sx + [i_time * rdata->nplist * rdata->nx + + i_nplist * rdata->nx + i_state]; + std::cout << std::endl; } return 0; diff --git a/models/model_calvetti/model_calvetti.h b/models/model_calvetti/model_calvetti.h index c8144bdf5e..c189ae8d74 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) 223c5075608273b17f304556d0e8ccb41233bd21 */ +/* Generated by amiwrap (R2017b) 8b324bca5b796a93094195d22a023e5f8e945ef1 */ #include #include #include "amici/defines.h" @@ -19,11 +19,11 @@ extern void Jy_model_calvetti(double *nllh, const int iy, const realtype *p, con extern void M_model_calvetti(realtype *M, const realtype t, const realtype *x, const realtype *p, const realtype *k); extern void dJydsigma_model_calvetti(double *dJydsigma, const int iy, const realtype *p, const realtype *k, const double *y, const double *sigmay, const double *my); extern void dJydy_model_calvetti(double *dJydy, const int iy, const realtype *p, const realtype *k, const double *y, const double *sigmay, const double *my); -extern void dwdx_model_calvetti(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl); +extern void dwdx_model_calvetti(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl, bool include_static); extern void dydx_model_calvetti(double *dydx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *dwdx); extern void root_model_calvetti(realtype *root, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *dx); extern void sigmay_model_calvetti(double *sigmay, const realtype t, const realtype *p, const realtype *k, const realtype *y); -extern void w_model_calvetti(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl); +extern void w_model_calvetti(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl, bool include_static); extern void x0_model_calvetti(realtype *x0, const realtype t, const realtype *p, const realtype *k); extern void xdot_model_calvetti(realtype *xdot, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *dx, const realtype *w); extern void y_model_calvetti(double *y, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w); @@ -72,7 +72,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 "223c5075608273b17f304556d0e8ccb41233bd21"; }; + std::string getAmiciCommit() const override { return "8b324bca5b796a93094195d22a023e5f8e945ef1"; }; 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); @@ -136,11 +136,11 @@ class Model_model_calvetti : public amici::Model_DAE { void fdsigmazdp(double *dsigmazdp, const realtype t, const realtype *p, const realtype *k, const int ip) override { } - void fdwdp(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl) override { + void fdwdp(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl, bool include_static) override { } - void fdwdx(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl) override { - dwdx_model_calvetti(dwdx, t, x, p, k, h, w, tcl, spl); + void fdwdx(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl, bool include_static) override { + dwdx_model_calvetti(dwdx, t, x, p, k, h, w, tcl, spl, include_static); } void fdxdotdp(realtype *dxdotdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const int ip, const realtype *dx, const realtype *w, const realtype *dwdp) override { @@ -185,8 +185,8 @@ class Model_model_calvetti : public amici::Model_DAE { void fsz(double *sz, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *sx, const int ip) override { } - void fw(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl) override { - w_model_calvetti(w, t, x, p, k, h, tcl, spl); + void fw(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl, bool include_static) override { + w_model_calvetti(w, t, x, p, k, h, tcl, spl, include_static); } void fx0(realtype *x0, const realtype t, const realtype *p, const realtype *k) override { diff --git a/models/model_calvetti/w.cpp b/models/model_calvetti/w.cpp index fb1aaef8e2..38773562d3 100644 --- a/models/model_calvetti/w.cpp +++ b/models/model_calvetti/w.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_calvetti{ -void w_model_calvetti(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl) { +void w_model_calvetti(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl, bool include_static) { w[0] = 1.0/k[0]; w[1] = k[2]*k[2]; w[2] = 1.0/(x[1]*x[1]); diff --git a/models/model_dirac/CMakeLists.txt b/models/model_dirac/CMakeLists.txt index 64f02dca9b..d96169b04a 100644 --- a/models/model_dirac/CMakeLists.txt +++ b/models/model_dirac/CMakeLists.txt @@ -1,5 +1,11 @@ # Build AMICI model cmake_minimum_required(VERSION 3.15) +cmake_policy(VERSION 3.15...3.27) + +# cmake >=3.27 +if(POLICY CMP0144) + cmake_policy(SET CMP0144 NEW) +endif(POLICY CMP0144) project(model_dirac) @@ -14,7 +20,7 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") endif() foreach(flag ${MY_CXX_FLAGS}) unset(CUR_FLAG_SUPPORTED CACHE) - check_cxx_compiler_flag(-Werror ${flag} CUR_FLAG_SUPPORTED) + check_cxx_compiler_flag(${flag} CUR_FLAG_SUPPORTED) if(${CUR_FLAG_SUPPORTED}) string(APPEND CMAKE_CXX_FLAGS " ${flag}") endif() @@ -33,6 +39,23 @@ find_package(Amici REQUIRED HINTS ${CMAKE_CURRENT_LIST_DIR}/../../build) message(STATUS "Found AMICI ${Amici_DIR}") +# Debug build? +if("$ENV{ENABLE_AMICI_DEBUGGING}" OR "$ENV{ENABLE_GCOV_COVERAGE}") + add_compile_options(-UNDEBUG) + if(MSVC) + add_compile_options(-DEBUG) + else() + add_compile_options(-O0 -g) + endif() + set(CMAKE_BUILD_TYPE "Debug") +endif() + +# coverage options +if($ENV{ENABLE_GCOV_COVERAGE}) + string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage") + string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " --coverage") +endif() + set(MODEL_DIR ${CMAKE_CURRENT_LIST_DIR}) set(SRC_LIST_LIB ${MODEL_DIR}/JSparse.cpp @@ -73,18 +96,6 @@ if(NOT "${AMICI_PYTHON_BUILD_EXT_ONLY}") target_link_libraries(simulate_${PROJECT_NAME} ${PROJECT_NAME}) endif() -# Debug build? -if("$ENV{ENABLE_AMICI_DEBUGGING}" OR "$ENV{ENABLE_GCOV_COVERAGE}") - add_compile_options(-UNDEBUG -O0 -g) - set(CMAKE_BUILD_TYPE "Debug") -endif() - -# coverage options -if($ENV{ENABLE_GCOV_COVERAGE}) - string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage") - string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " --coverage") -endif() - # SWIG option(ENABLE_SWIG "Build swig/python library?" ON) if(ENABLE_SWIG) diff --git a/models/model_dirac/main.cpp b/models/model_dirac/main.cpp index 00a5e6b448..ecdff85a46 100644 --- a/models/model_dirac/main.cpp +++ b/models/model_dirac/main.cpp @@ -1,14 +1,13 @@ #include -#include /* AMICI base functions */ -#include "wrapfunctions.h" /* model-provided functions */ +#include "wrapfunctions.h" /* model-provided functions */ +#include /* AMICI base functions */ -template < class T > -std::ostream& operator << (std::ostream& os, const std::vector& v) -{ +template +std::ostream& operator<<(std::ostream& os, std::vector const& v) { os << "["; - for (typename std::vector::const_iterator ii = v.begin(); ii != v.end(); ++ii) - { + for (typename std::vector::const_iterator ii = v.begin(); ii != v.end(); + ++ii) { os << " " << *ii; } os << "]"; @@ -21,9 +20,9 @@ std::ostream& operator << (std::ostream& os, const std::vector& v) */ int main() { - std::cout<<"********************************"<getObservableIds(); - std::cout<<"Simulated observables for timepoints "<ts<<"\n\n"; - for(int i_observable = 0; i_observable < rdata->ny; ++i_observable) { - std::cout<nt; ++i_time) { + std::cout << "Simulated observables for timepoints " << rdata->ts << "\n\n"; + for (int i_observable = 0; i_observable < rdata->ny; ++i_observable) { + std::cout << observable_ids[i_observable] << ":\n\t"; + for (int i_time = 0; i_time < rdata->nt; ++i_time) { // rdata->y is a flat 2D array in row-major ordering - std::cout<y[i_time * rdata->ny + i_observable]<<" "; + std::cout << rdata->y[i_time * rdata->ny + i_observable] << " "; } - std::cout<setSensitivityOrder(amici::SensitivityOrder::first); @@ -78,18 +76,17 @@ int main() { auto state_ids = model->getStateIds(); auto parameter_ids = model->getParameterIds(); - std::cout<<"State sensitivities for timepoint " - <ts[i_time] - <nx; ++i_state) { - std::cout<<"\td("<plist(i_nplist)]<<") = "; + std::cout << "State sensitivities for timepoint " << rdata->ts[i_time] + << std::endl; // nt x nplist x nx + for (int i_state = 0; i_state < rdata->nx; ++i_state) { + std::cout << "\td(" << state_ids[i_state] << ")/d(" + << parameter_ids[model->plist(i_nplist)] << ") = "; // rdata->sx is a flat 3D array in row-major ordering - std::cout<sx[i_time * rdata->nplist * rdata->nx - + i_nplist * rdata->nx - + i_state]; - std::cout<sx + [i_time * rdata->nplist * rdata->nx + + i_nplist * rdata->nx + i_state]; + std::cout << std::endl; } return 0; diff --git a/models/model_dirac/model_dirac.h b/models/model_dirac/model_dirac.h index cfd943e456..0425320765 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) 223c5075608273b17f304556d0e8ccb41233bd21 */ +/* Generated by amiwrap (R2017b) 8b324bca5b796a93094195d22a023e5f8e945ef1 */ #include #include #include "amici/defines.h" @@ -72,7 +72,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 "223c5075608273b17f304556d0e8ccb41233bd21"; }; + std::string getAmiciCommit() const override { return "8b324bca5b796a93094195d22a023e5f8e945ef1"; }; 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); @@ -134,10 +134,10 @@ class Model_model_dirac : public amici::Model_ODE { void fdsigmazdp(double *dsigmazdp, const realtype t, const realtype *p, const realtype *k, const int ip) override { } - void fdwdp(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl) override { + void fdwdp(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl, bool include_static) override { } - void fdwdx(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl) override { + void fdwdx(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl, bool include_static) override { } void fdxdotdp(realtype *dxdotdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const int ip, const realtype *w, const realtype *dwdp) override { @@ -184,7 +184,7 @@ class Model_model_dirac : public amici::Model_ODE { void fsz(double *sz, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *sx, const int ip) override { } - void fw(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl) override { + void fw(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl, bool include_static) override { } void fx0(realtype *x0, const realtype t, const realtype *p, const realtype *k) override { diff --git a/models/model_events/CMakeLists.txt b/models/model_events/CMakeLists.txt index 277573426d..53e3a335b8 100644 --- a/models/model_events/CMakeLists.txt +++ b/models/model_events/CMakeLists.txt @@ -1,5 +1,11 @@ # Build AMICI model cmake_minimum_required(VERSION 3.15) +cmake_policy(VERSION 3.15...3.27) + +# cmake >=3.27 +if(POLICY CMP0144) + cmake_policy(SET CMP0144 NEW) +endif(POLICY CMP0144) project(model_events) @@ -14,7 +20,7 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") endif() foreach(flag ${MY_CXX_FLAGS}) unset(CUR_FLAG_SUPPORTED CACHE) - check_cxx_compiler_flag(-Werror ${flag} CUR_FLAG_SUPPORTED) + check_cxx_compiler_flag(${flag} CUR_FLAG_SUPPORTED) if(${CUR_FLAG_SUPPORTED}) string(APPEND CMAKE_CXX_FLAGS " ${flag}") endif() @@ -33,6 +39,23 @@ find_package(Amici REQUIRED HINTS ${CMAKE_CURRENT_LIST_DIR}/../../build) message(STATUS "Found AMICI ${Amici_DIR}") +# Debug build? +if("$ENV{ENABLE_AMICI_DEBUGGING}" OR "$ENV{ENABLE_GCOV_COVERAGE}") + add_compile_options(-UNDEBUG) + if(MSVC) + add_compile_options(-DEBUG) + else() + add_compile_options(-O0 -g) + endif() + set(CMAKE_BUILD_TYPE "Debug") +endif() + +# coverage options +if($ENV{ENABLE_GCOV_COVERAGE}) + string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage") + string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " --coverage") +endif() + set(MODEL_DIR ${CMAKE_CURRENT_LIST_DIR}) set(SRC_LIST_LIB ${MODEL_DIR}/JSparse.cpp @@ -87,18 +110,6 @@ if(NOT "${AMICI_PYTHON_BUILD_EXT_ONLY}") target_link_libraries(simulate_${PROJECT_NAME} ${PROJECT_NAME}) endif() -# Debug build? -if("$ENV{ENABLE_AMICI_DEBUGGING}" OR "$ENV{ENABLE_GCOV_COVERAGE}") - add_compile_options(-UNDEBUG -O0 -g) - set(CMAKE_BUILD_TYPE "Debug") -endif() - -# coverage options -if($ENV{ENABLE_GCOV_COVERAGE}) - string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage") - string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " --coverage") -endif() - # SWIG option(ENABLE_SWIG "Build swig/python library?" ON) if(ENABLE_SWIG) diff --git a/models/model_events/main.cpp b/models/model_events/main.cpp index 00a5e6b448..ecdff85a46 100644 --- a/models/model_events/main.cpp +++ b/models/model_events/main.cpp @@ -1,14 +1,13 @@ #include -#include /* AMICI base functions */ -#include "wrapfunctions.h" /* model-provided functions */ +#include "wrapfunctions.h" /* model-provided functions */ +#include /* AMICI base functions */ -template < class T > -std::ostream& operator << (std::ostream& os, const std::vector& v) -{ +template +std::ostream& operator<<(std::ostream& os, std::vector const& v) { os << "["; - for (typename std::vector::const_iterator ii = v.begin(); ii != v.end(); ++ii) - { + for (typename std::vector::const_iterator ii = v.begin(); ii != v.end(); + ++ii) { os << " " << *ii; } os << "]"; @@ -21,9 +20,9 @@ std::ostream& operator << (std::ostream& os, const std::vector& v) */ int main() { - std::cout<<"********************************"<getObservableIds(); - std::cout<<"Simulated observables for timepoints "<ts<<"\n\n"; - for(int i_observable = 0; i_observable < rdata->ny; ++i_observable) { - std::cout<nt; ++i_time) { + std::cout << "Simulated observables for timepoints " << rdata->ts << "\n\n"; + for (int i_observable = 0; i_observable < rdata->ny; ++i_observable) { + std::cout << observable_ids[i_observable] << ":\n\t"; + for (int i_time = 0; i_time < rdata->nt; ++i_time) { // rdata->y is a flat 2D array in row-major ordering - std::cout<y[i_time * rdata->ny + i_observable]<<" "; + std::cout << rdata->y[i_time * rdata->ny + i_observable] << " "; } - std::cout<setSensitivityOrder(amici::SensitivityOrder::first); @@ -78,18 +76,17 @@ int main() { auto state_ids = model->getStateIds(); auto parameter_ids = model->getParameterIds(); - std::cout<<"State sensitivities for timepoint " - <ts[i_time] - <nx; ++i_state) { - std::cout<<"\td("<plist(i_nplist)]<<") = "; + std::cout << "State sensitivities for timepoint " << rdata->ts[i_time] + << std::endl; // nt x nplist x nx + for (int i_state = 0; i_state < rdata->nx; ++i_state) { + std::cout << "\td(" << state_ids[i_state] << ")/d(" + << parameter_ids[model->plist(i_nplist)] << ") = "; // rdata->sx is a flat 3D array in row-major ordering - std::cout<sx[i_time * rdata->nplist * rdata->nx - + i_nplist * rdata->nx - + i_state]; - std::cout<sx + [i_time * rdata->nplist * rdata->nx + + i_nplist * rdata->nx + i_state]; + std::cout << std::endl; } return 0; diff --git a/models/model_events/model_events.h b/models/model_events/model_events.h index ad6c976419..bc2b1b6157 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) 223c5075608273b17f304556d0e8ccb41233bd21 */ +/* Generated by amiwrap (R2017b) 8b324bca5b796a93094195d22a023e5f8e945ef1 */ #include #include #include "amici/defines.h" @@ -86,7 +86,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 "223c5075608273b17f304556d0e8ccb41233bd21"; }; + std::string getAmiciCommit() const override { return "8b324bca5b796a93094195d22a023e5f8e945ef1"; }; 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); @@ -154,10 +154,10 @@ class Model_model_events : public amici::Model_ODE { void fdsigmazdp(double *dsigmazdp, const realtype t, const realtype *p, const realtype *k, const int ip) override { } - void fdwdp(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl) override { + void fdwdp(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl, bool include_static) override { } - void fdwdx(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl) override { + void fdwdx(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl, bool include_static) override { } void fdxdotdp(realtype *dxdotdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const int ip, const realtype *w, const realtype *dwdp) override { @@ -210,7 +210,7 @@ class Model_model_events : public amici::Model_ODE { sz_model_events(sz, ie, t, x, p, k, h, sx, ip); } - void fw(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl) override { + void fw(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl, bool include_static) override { } void fx0(realtype *x0, const realtype t, const realtype *p, const realtype *k) override { diff --git a/models/model_jakstat_adjoint/CMakeLists.txt b/models/model_jakstat_adjoint/CMakeLists.txt index 1670a68ee9..75cc527694 100644 --- a/models/model_jakstat_adjoint/CMakeLists.txt +++ b/models/model_jakstat_adjoint/CMakeLists.txt @@ -1,5 +1,11 @@ # Build AMICI model cmake_minimum_required(VERSION 3.15) +cmake_policy(VERSION 3.15...3.27) + +# cmake >=3.27 +if(POLICY CMP0144) + cmake_policy(SET CMP0144 NEW) +endif(POLICY CMP0144) project(model_jakstat_adjoint) @@ -14,7 +20,7 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") endif() foreach(flag ${MY_CXX_FLAGS}) unset(CUR_FLAG_SUPPORTED CACHE) - check_cxx_compiler_flag(-Werror ${flag} CUR_FLAG_SUPPORTED) + check_cxx_compiler_flag(${flag} CUR_FLAG_SUPPORTED) if(${CUR_FLAG_SUPPORTED}) string(APPEND CMAKE_CXX_FLAGS " ${flag}") endif() @@ -33,6 +39,23 @@ find_package(Amici REQUIRED HINTS ${CMAKE_CURRENT_LIST_DIR}/../../build) message(STATUS "Found AMICI ${Amici_DIR}") +# Debug build? +if("$ENV{ENABLE_AMICI_DEBUGGING}" OR "$ENV{ENABLE_GCOV_COVERAGE}") + add_compile_options(-UNDEBUG) + if(MSVC) + add_compile_options(-DEBUG) + else() + add_compile_options(-O0 -g) + endif() + set(CMAKE_BUILD_TYPE "Debug") +endif() + +# coverage options +if($ENV{ENABLE_GCOV_COVERAGE}) + string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage") + string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " --coverage") +endif() + set(MODEL_DIR ${CMAKE_CURRENT_LIST_DIR}) set(SRC_LIST_LIB ${MODEL_DIR}/JSparse.cpp @@ -76,18 +99,6 @@ if(NOT "${AMICI_PYTHON_BUILD_EXT_ONLY}") target_link_libraries(simulate_${PROJECT_NAME} ${PROJECT_NAME}) endif() -# Debug build? -if("$ENV{ENABLE_AMICI_DEBUGGING}" OR "$ENV{ENABLE_GCOV_COVERAGE}") - add_compile_options(-UNDEBUG -O0 -g) - set(CMAKE_BUILD_TYPE "Debug") -endif() - -# coverage options -if($ENV{ENABLE_GCOV_COVERAGE}) - string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage") - string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " --coverage") -endif() - # SWIG option(ENABLE_SWIG "Build swig/python library?" ON) if(ENABLE_SWIG) diff --git a/models/model_jakstat_adjoint/dwdp.cpp b/models/model_jakstat_adjoint/dwdp.cpp index 3213a319d9..092466e87b 100644 --- a/models/model_jakstat_adjoint/dwdp.cpp +++ b/models/model_jakstat_adjoint/dwdp.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_jakstat_adjoint{ -void dwdp_model_jakstat_adjoint(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl) { +void dwdp_model_jakstat_adjoint(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl, bool include_static) { dwdp[0] = amici::Dspline_pos(4,t,5,0.0,p[5],5.0,p[6],1.0E1,p[7],2.0E1,p[8],6.0E1,p[9],0.0,0.0); dwdp[1] = amici::Dspline_pos(6,t,5,0.0,p[5],5.0,p[6],1.0E1,p[7],2.0E1,p[8],6.0E1,p[9],0.0,0.0); dwdp[2] = amici::Dspline_pos(8,t,5,0.0,p[5],5.0,p[6],1.0E1,p[7],2.0E1,p[8],6.0E1,p[9],0.0,0.0); diff --git a/models/model_jakstat_adjoint/dwdx.cpp b/models/model_jakstat_adjoint/dwdx.cpp index 70a26b8a2c..354b72f246 100644 --- a/models/model_jakstat_adjoint/dwdx.cpp +++ b/models/model_jakstat_adjoint/dwdx.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_jakstat_adjoint{ -void dwdx_model_jakstat_adjoint(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl) { +void dwdx_model_jakstat_adjoint(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl, bool include_static) { dwdx[0] = x[1]*2.0; } diff --git a/models/model_jakstat_adjoint/main.cpp b/models/model_jakstat_adjoint/main.cpp index 00a5e6b448..ecdff85a46 100644 --- a/models/model_jakstat_adjoint/main.cpp +++ b/models/model_jakstat_adjoint/main.cpp @@ -1,14 +1,13 @@ #include -#include /* AMICI base functions */ -#include "wrapfunctions.h" /* model-provided functions */ +#include "wrapfunctions.h" /* model-provided functions */ +#include /* AMICI base functions */ -template < class T > -std::ostream& operator << (std::ostream& os, const std::vector& v) -{ +template +std::ostream& operator<<(std::ostream& os, std::vector const& v) { os << "["; - for (typename std::vector::const_iterator ii = v.begin(); ii != v.end(); ++ii) - { + for (typename std::vector::const_iterator ii = v.begin(); ii != v.end(); + ++ii) { os << " " << *ii; } os << "]"; @@ -21,9 +20,9 @@ std::ostream& operator << (std::ostream& os, const std::vector& v) */ int main() { - std::cout<<"********************************"<getObservableIds(); - std::cout<<"Simulated observables for timepoints "<ts<<"\n\n"; - for(int i_observable = 0; i_observable < rdata->ny; ++i_observable) { - std::cout<nt; ++i_time) { + std::cout << "Simulated observables for timepoints " << rdata->ts << "\n\n"; + for (int i_observable = 0; i_observable < rdata->ny; ++i_observable) { + std::cout << observable_ids[i_observable] << ":\n\t"; + for (int i_time = 0; i_time < rdata->nt; ++i_time) { // rdata->y is a flat 2D array in row-major ordering - std::cout<y[i_time * rdata->ny + i_observable]<<" "; + std::cout << rdata->y[i_time * rdata->ny + i_observable] << " "; } - std::cout<setSensitivityOrder(amici::SensitivityOrder::first); @@ -78,18 +76,17 @@ int main() { auto state_ids = model->getStateIds(); auto parameter_ids = model->getParameterIds(); - std::cout<<"State sensitivities for timepoint " - <ts[i_time] - <nx; ++i_state) { - std::cout<<"\td("<plist(i_nplist)]<<") = "; + std::cout << "State sensitivities for timepoint " << rdata->ts[i_time] + << std::endl; // nt x nplist x nx + for (int i_state = 0; i_state < rdata->nx; ++i_state) { + std::cout << "\td(" << state_ids[i_state] << ")/d(" + << parameter_ids[model->plist(i_nplist)] << ") = "; // rdata->sx is a flat 3D array in row-major ordering - std::cout<sx[i_time * rdata->nplist * rdata->nx - + i_nplist * rdata->nx - + i_state]; - std::cout<sx + [i_time * rdata->nplist * rdata->nx + + i_nplist * rdata->nx + i_state]; + std::cout << std::endl; } return 0; diff --git a/models/model_jakstat_adjoint/model_jakstat_adjoint.h b/models/model_jakstat_adjoint/model_jakstat_adjoint.h index 6d7601947a..aad482c26c 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) 223c5075608273b17f304556d0e8ccb41233bd21 */ +/* Generated by amiwrap (R2017b) 8b324bca5b796a93094195d22a023e5f8e945ef1 */ #include #include #include "amici/defines.h" @@ -19,14 +19,14 @@ extern void Jy_model_jakstat_adjoint(double *nllh, const int iy, const realtype extern void dJydsigma_model_jakstat_adjoint(double *dJydsigma, const int iy, const realtype *p, const realtype *k, const double *y, const double *sigmay, const double *my); extern void dJydy_model_jakstat_adjoint(double *dJydy, const int iy, const realtype *p, const realtype *k, const double *y, const double *sigmay, const double *my); extern void dsigmaydp_model_jakstat_adjoint(double *dsigmaydp, const realtype t, const realtype *p, const realtype *k, const realtype *y, const int ip); -extern void dwdp_model_jakstat_adjoint(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl); -extern void dwdx_model_jakstat_adjoint(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl); +extern void dwdp_model_jakstat_adjoint(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl, bool include_static); +extern void dwdx_model_jakstat_adjoint(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl, bool include_static); extern void dxdotdp_model_jakstat_adjoint(realtype *dxdotdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const int ip, const realtype *w, const realtype *dwdp); extern void dydp_model_jakstat_adjoint(double *dydp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const int ip, const realtype *w, const realtype *dwdp); extern void dydx_model_jakstat_adjoint(double *dydx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *dwdx); extern void sigmay_model_jakstat_adjoint(double *sigmay, const realtype t, const realtype *p, const realtype *k, const realtype *y); extern void sx0_model_jakstat_adjoint(realtype *sx0, const realtype t,const realtype *x0, const realtype *p, const realtype *k, const int ip); -extern void w_model_jakstat_adjoint(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl); +extern void w_model_jakstat_adjoint(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl, bool include_static); extern void x0_model_jakstat_adjoint(realtype *x0, const realtype t, const realtype *p, const realtype *k); extern void xdot_model_jakstat_adjoint(realtype *xdot, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w); extern void y_model_jakstat_adjoint(double *y, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w); @@ -75,7 +75,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 "223c5075608273b17f304556d0e8ccb41233bd21"; }; + std::string getAmiciCommit() const override { return "8b324bca5b796a93094195d22a023e5f8e945ef1"; }; 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); @@ -136,12 +136,12 @@ class Model_model_jakstat_adjoint : public amici::Model_ODE { void fdsigmazdp(double *dsigmazdp, const realtype t, const realtype *p, const realtype *k, const int ip) override { } - void fdwdp(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl) override { - dwdp_model_jakstat_adjoint(dwdp, t, x, p, k, h, w, tcl, stcl, spl, sspl); + void fdwdp(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl, bool include_static) override { + dwdp_model_jakstat_adjoint(dwdp, t, x, p, k, h, w, tcl, stcl, spl, sspl, include_static); } - void fdwdx(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl) override { - dwdx_model_jakstat_adjoint(dwdx, t, x, p, k, h, w, tcl, spl); + void fdwdx(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl, bool include_static) override { + dwdx_model_jakstat_adjoint(dwdx, t, x, p, k, h, w, tcl, spl, include_static); } void fdxdotdp(realtype *dxdotdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const int ip, const realtype *w, const realtype *dwdp) override { @@ -188,8 +188,8 @@ class Model_model_jakstat_adjoint : public amici::Model_ODE { void fsz(double *sz, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *sx, const int ip) override { } - void fw(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl) override { - w_model_jakstat_adjoint(w, t, x, p, k, h, tcl, spl); + void fw(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl, bool include_static) override { + w_model_jakstat_adjoint(w, t, x, p, k, h, tcl, spl, include_static); } void fx0(realtype *x0, const realtype t, const realtype *p, const realtype *k) override { diff --git a/models/model_jakstat_adjoint/w.cpp b/models/model_jakstat_adjoint/w.cpp index 06238238ca..430b96de3a 100644 --- a/models/model_jakstat_adjoint/w.cpp +++ b/models/model_jakstat_adjoint/w.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_jakstat_adjoint{ -void w_model_jakstat_adjoint(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl) { +void w_model_jakstat_adjoint(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl, bool include_static) { w[0] = amici::spline_pos(t,5,0.0,p[5],5.0,p[6],1.0E1,p[7],2.0E1,p[8],6.0E1,p[9],0.0,0.0); w[1] = x[1]*x[1]; } diff --git a/models/model_jakstat_adjoint_o2/CMakeLists.txt b/models/model_jakstat_adjoint_o2/CMakeLists.txt index b4b9cc03ca..4b2b35e223 100644 --- a/models/model_jakstat_adjoint_o2/CMakeLists.txt +++ b/models/model_jakstat_adjoint_o2/CMakeLists.txt @@ -1,5 +1,11 @@ # Build AMICI model cmake_minimum_required(VERSION 3.15) +cmake_policy(VERSION 3.15...3.27) + +# cmake >=3.27 +if(POLICY CMP0144) + cmake_policy(SET CMP0144 NEW) +endif(POLICY CMP0144) project(model_jakstat_adjoint_o2) @@ -14,7 +20,7 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") endif() foreach(flag ${MY_CXX_FLAGS}) unset(CUR_FLAG_SUPPORTED CACHE) - check_cxx_compiler_flag(-Werror ${flag} CUR_FLAG_SUPPORTED) + check_cxx_compiler_flag(${flag} CUR_FLAG_SUPPORTED) if(${CUR_FLAG_SUPPORTED}) string(APPEND CMAKE_CXX_FLAGS " ${flag}") endif() @@ -33,6 +39,23 @@ find_package(Amici REQUIRED HINTS ${CMAKE_CURRENT_LIST_DIR}/../../build) message(STATUS "Found AMICI ${Amici_DIR}") +# Debug build? +if("$ENV{ENABLE_AMICI_DEBUGGING}" OR "$ENV{ENABLE_GCOV_COVERAGE}") + add_compile_options(-UNDEBUG) + if(MSVC) + add_compile_options(-DEBUG) + else() + add_compile_options(-O0 -g) + endif() + set(CMAKE_BUILD_TYPE "Debug") +endif() + +# coverage options +if($ENV{ENABLE_GCOV_COVERAGE}) + string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage") + string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " --coverage") +endif() + set(MODEL_DIR ${CMAKE_CURRENT_LIST_DIR}) set(SRC_LIST_LIB ${MODEL_DIR}/JSparse.cpp @@ -76,18 +99,6 @@ if(NOT "${AMICI_PYTHON_BUILD_EXT_ONLY}") target_link_libraries(simulate_${PROJECT_NAME} ${PROJECT_NAME}) endif() -# Debug build? -if("$ENV{ENABLE_AMICI_DEBUGGING}" OR "$ENV{ENABLE_GCOV_COVERAGE}") - add_compile_options(-UNDEBUG -O0 -g) - set(CMAKE_BUILD_TYPE "Debug") -endif() - -# coverage options -if($ENV{ENABLE_GCOV_COVERAGE}) - string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage") - string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " --coverage") -endif() - # SWIG option(ENABLE_SWIG "Build swig/python library?" ON) if(ENABLE_SWIG) diff --git a/models/model_jakstat_adjoint_o2/dwdp.cpp b/models/model_jakstat_adjoint_o2/dwdp.cpp index b3e591fcba..a936666e39 100644 --- a/models/model_jakstat_adjoint_o2/dwdp.cpp +++ b/models/model_jakstat_adjoint_o2/dwdp.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_jakstat_adjoint_o2{ -void dwdp_model_jakstat_adjoint_o2(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl) { +void dwdp_model_jakstat_adjoint_o2(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl, bool include_static) { dwdp[0] = amici::Dspline_pos(4,t,5,0.0,p[5],5.0,p[6],1.0E1,p[7],2.0E1,p[8],6.0E1,p[9],0.0,0.0); dwdp[1] = amici::DDspline_pos(4,4,t,5,0.0,p[5],5.0,p[6],1.0E1,p[7],2.0E1,p[8],6.0E1,p[9],0.0,0.0); dwdp[2] = amici::DDspline_pos(6,4,t,5,0.0,p[5],5.0,p[6],1.0E1,p[7],2.0E1,p[8],6.0E1,p[9],0.0,0.0); diff --git a/models/model_jakstat_adjoint_o2/dwdx.cpp b/models/model_jakstat_adjoint_o2/dwdx.cpp index 3226a7535b..81ad6343f1 100644 --- a/models/model_jakstat_adjoint_o2/dwdx.cpp +++ b/models/model_jakstat_adjoint_o2/dwdx.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_jakstat_adjoint_o2{ -void dwdx_model_jakstat_adjoint_o2(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl) { +void dwdx_model_jakstat_adjoint_o2(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl, bool include_static) { dwdx[0] = x[1]*2.0; dwdx[1] = 2.0; } diff --git a/models/model_jakstat_adjoint_o2/main.cpp b/models/model_jakstat_adjoint_o2/main.cpp index 00a5e6b448..ecdff85a46 100644 --- a/models/model_jakstat_adjoint_o2/main.cpp +++ b/models/model_jakstat_adjoint_o2/main.cpp @@ -1,14 +1,13 @@ #include -#include /* AMICI base functions */ -#include "wrapfunctions.h" /* model-provided functions */ +#include "wrapfunctions.h" /* model-provided functions */ +#include /* AMICI base functions */ -template < class T > -std::ostream& operator << (std::ostream& os, const std::vector& v) -{ +template +std::ostream& operator<<(std::ostream& os, std::vector const& v) { os << "["; - for (typename std::vector::const_iterator ii = v.begin(); ii != v.end(); ++ii) - { + for (typename std::vector::const_iterator ii = v.begin(); ii != v.end(); + ++ii) { os << " " << *ii; } os << "]"; @@ -21,9 +20,9 @@ std::ostream& operator << (std::ostream& os, const std::vector& v) */ int main() { - std::cout<<"********************************"<getObservableIds(); - std::cout<<"Simulated observables for timepoints "<ts<<"\n\n"; - for(int i_observable = 0; i_observable < rdata->ny; ++i_observable) { - std::cout<nt; ++i_time) { + std::cout << "Simulated observables for timepoints " << rdata->ts << "\n\n"; + for (int i_observable = 0; i_observable < rdata->ny; ++i_observable) { + std::cout << observable_ids[i_observable] << ":\n\t"; + for (int i_time = 0; i_time < rdata->nt; ++i_time) { // rdata->y is a flat 2D array in row-major ordering - std::cout<y[i_time * rdata->ny + i_observable]<<" "; + std::cout << rdata->y[i_time * rdata->ny + i_observable] << " "; } - std::cout<setSensitivityOrder(amici::SensitivityOrder::first); @@ -78,18 +76,17 @@ int main() { auto state_ids = model->getStateIds(); auto parameter_ids = model->getParameterIds(); - std::cout<<"State sensitivities for timepoint " - <ts[i_time] - <nx; ++i_state) { - std::cout<<"\td("<plist(i_nplist)]<<") = "; + std::cout << "State sensitivities for timepoint " << rdata->ts[i_time] + << std::endl; // nt x nplist x nx + for (int i_state = 0; i_state < rdata->nx; ++i_state) { + std::cout << "\td(" << state_ids[i_state] << ")/d(" + << parameter_ids[model->plist(i_nplist)] << ") = "; // rdata->sx is a flat 3D array in row-major ordering - std::cout<sx[i_time * rdata->nplist * rdata->nx - + i_nplist * rdata->nx - + i_state]; - std::cout<sx + [i_time * rdata->nplist * rdata->nx + + i_nplist * rdata->nx + i_state]; + std::cout << std::endl; } return 0; 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 bfac0b3267..e44c31b8d5 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) 223c5075608273b17f304556d0e8ccb41233bd21 */ +/* Generated by amiwrap (R2017b) 8b324bca5b796a93094195d22a023e5f8e945ef1 */ #include #include #include "amici/defines.h" @@ -19,14 +19,14 @@ extern void Jy_model_jakstat_adjoint_o2(double *nllh, const int iy, const realty extern void dJydsigma_model_jakstat_adjoint_o2(double *dJydsigma, const int iy, const realtype *p, const realtype *k, const double *y, const double *sigmay, const double *my); extern void dJydy_model_jakstat_adjoint_o2(double *dJydy, const int iy, const realtype *p, const realtype *k, const double *y, const double *sigmay, const double *my); extern void dsigmaydp_model_jakstat_adjoint_o2(double *dsigmaydp, const realtype t, const realtype *p, const realtype *k, const realtype *y, const int ip); -extern void dwdp_model_jakstat_adjoint_o2(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl); -extern void dwdx_model_jakstat_adjoint_o2(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl); +extern void dwdp_model_jakstat_adjoint_o2(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl, bool include_static); +extern void dwdx_model_jakstat_adjoint_o2(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl, bool include_static); extern void dxdotdp_model_jakstat_adjoint_o2(realtype *dxdotdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const int ip, const realtype *w, const realtype *dwdp); extern void dydp_model_jakstat_adjoint_o2(double *dydp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const int ip, const realtype *w, const realtype *dwdp); extern void dydx_model_jakstat_adjoint_o2(double *dydx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *dwdx); extern void sigmay_model_jakstat_adjoint_o2(double *sigmay, const realtype t, const realtype *p, const realtype *k, const realtype *y); extern void sx0_model_jakstat_adjoint_o2(realtype *sx0, const realtype t,const realtype *x0, const realtype *p, const realtype *k, const int ip); -extern void w_model_jakstat_adjoint_o2(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl); +extern void w_model_jakstat_adjoint_o2(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl, bool include_static); extern void x0_model_jakstat_adjoint_o2(realtype *x0, const realtype t, const realtype *p, const realtype *k); extern void xdot_model_jakstat_adjoint_o2(realtype *xdot, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w); extern void y_model_jakstat_adjoint_o2(double *y, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w); @@ -75,7 +75,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 "223c5075608273b17f304556d0e8ccb41233bd21"; }; + std::string getAmiciCommit() const override { return "8b324bca5b796a93094195d22a023e5f8e945ef1"; }; 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); @@ -136,12 +136,12 @@ class Model_model_jakstat_adjoint_o2 : public amici::Model_ODE { void fdsigmazdp(double *dsigmazdp, const realtype t, const realtype *p, const realtype *k, const int ip) override { } - void fdwdp(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl) override { - dwdp_model_jakstat_adjoint_o2(dwdp, t, x, p, k, h, w, tcl, stcl, spl, sspl); + void fdwdp(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl, bool include_static) override { + dwdp_model_jakstat_adjoint_o2(dwdp, t, x, p, k, h, w, tcl, stcl, spl, sspl, include_static); } - void fdwdx(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl) override { - dwdx_model_jakstat_adjoint_o2(dwdx, t, x, p, k, h, w, tcl, spl); + void fdwdx(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl, bool include_static) override { + dwdx_model_jakstat_adjoint_o2(dwdx, t, x, p, k, h, w, tcl, spl, include_static); } void fdxdotdp(realtype *dxdotdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const int ip, const realtype *w, const realtype *dwdp) override { @@ -188,8 +188,8 @@ class Model_model_jakstat_adjoint_o2 : public amici::Model_ODE { void fsz(double *sz, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *sx, const int ip) override { } - void fw(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl) override { - w_model_jakstat_adjoint_o2(w, t, x, p, k, h, tcl, spl); + void fw(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl, bool include_static) override { + w_model_jakstat_adjoint_o2(w, t, x, p, k, h, tcl, spl, include_static); } void fx0(realtype *x0, const realtype t, const realtype *p, const realtype *k) override { diff --git a/models/model_jakstat_adjoint_o2/w.cpp b/models/model_jakstat_adjoint_o2/w.cpp index 766860cfa0..827202e139 100644 --- a/models/model_jakstat_adjoint_o2/w.cpp +++ b/models/model_jakstat_adjoint_o2/w.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_jakstat_adjoint_o2{ -void w_model_jakstat_adjoint_o2(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl) { +void w_model_jakstat_adjoint_o2(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl, bool include_static) { w[0] = amici::spline_pos(t,5,0.0,p[5],5.0,p[6],1.0E1,p[7],2.0E1,p[8],6.0E1,p[9],0.0,0.0); w[1] = x[1]*x[1]; w[2] = 1.0/k[0]; diff --git a/models/model_nested_events/CMakeLists.txt b/models/model_nested_events/CMakeLists.txt index 1a67d0a2c2..c609531e1d 100644 --- a/models/model_nested_events/CMakeLists.txt +++ b/models/model_nested_events/CMakeLists.txt @@ -1,5 +1,11 @@ # Build AMICI model cmake_minimum_required(VERSION 3.15) +cmake_policy(VERSION 3.15...3.27) + +# cmake >=3.27 +if(POLICY CMP0144) + cmake_policy(SET CMP0144 NEW) +endif(POLICY CMP0144) project(model_nested_events) @@ -14,7 +20,7 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") endif() foreach(flag ${MY_CXX_FLAGS}) unset(CUR_FLAG_SUPPORTED CACHE) - check_cxx_compiler_flag(-Werror ${flag} CUR_FLAG_SUPPORTED) + check_cxx_compiler_flag(${flag} CUR_FLAG_SUPPORTED) if(${CUR_FLAG_SUPPORTED}) string(APPEND CMAKE_CXX_FLAGS " ${flag}") endif() @@ -33,6 +39,23 @@ find_package(Amici REQUIRED HINTS ${CMAKE_CURRENT_LIST_DIR}/../../build) message(STATUS "Found AMICI ${Amici_DIR}") +# Debug build? +if("$ENV{ENABLE_AMICI_DEBUGGING}" OR "$ENV{ENABLE_GCOV_COVERAGE}") + add_compile_options(-UNDEBUG) + if(MSVC) + add_compile_options(-DEBUG) + else() + add_compile_options(-O0 -g) + endif() + set(CMAKE_BUILD_TYPE "Debug") +endif() + +# coverage options +if($ENV{ENABLE_GCOV_COVERAGE}) + string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage") + string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " --coverage") +endif() + set(MODEL_DIR ${CMAKE_CURRENT_LIST_DIR}) set(SRC_LIST_LIB ${MODEL_DIR}/JSparse.cpp @@ -76,18 +99,6 @@ if(NOT "${AMICI_PYTHON_BUILD_EXT_ONLY}") target_link_libraries(simulate_${PROJECT_NAME} ${PROJECT_NAME}) endif() -# Debug build? -if("$ENV{ENABLE_AMICI_DEBUGGING}" OR "$ENV{ENABLE_GCOV_COVERAGE}") - add_compile_options(-UNDEBUG -O0 -g) - set(CMAKE_BUILD_TYPE "Debug") -endif() - -# coverage options -if($ENV{ENABLE_GCOV_COVERAGE}) - string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage") - string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " --coverage") -endif() - # SWIG option(ENABLE_SWIG "Build swig/python library?" ON) if(ENABLE_SWIG) diff --git a/models/model_nested_events/main.cpp b/models/model_nested_events/main.cpp index 00a5e6b448..ecdff85a46 100644 --- a/models/model_nested_events/main.cpp +++ b/models/model_nested_events/main.cpp @@ -1,14 +1,13 @@ #include -#include /* AMICI base functions */ -#include "wrapfunctions.h" /* model-provided functions */ +#include "wrapfunctions.h" /* model-provided functions */ +#include /* AMICI base functions */ -template < class T > -std::ostream& operator << (std::ostream& os, const std::vector& v) -{ +template +std::ostream& operator<<(std::ostream& os, std::vector const& v) { os << "["; - for (typename std::vector::const_iterator ii = v.begin(); ii != v.end(); ++ii) - { + for (typename std::vector::const_iterator ii = v.begin(); ii != v.end(); + ++ii) { os << " " << *ii; } os << "]"; @@ -21,9 +20,9 @@ std::ostream& operator << (std::ostream& os, const std::vector& v) */ int main() { - std::cout<<"********************************"<getObservableIds(); - std::cout<<"Simulated observables for timepoints "<ts<<"\n\n"; - for(int i_observable = 0; i_observable < rdata->ny; ++i_observable) { - std::cout<nt; ++i_time) { + std::cout << "Simulated observables for timepoints " << rdata->ts << "\n\n"; + for (int i_observable = 0; i_observable < rdata->ny; ++i_observable) { + std::cout << observable_ids[i_observable] << ":\n\t"; + for (int i_time = 0; i_time < rdata->nt; ++i_time) { // rdata->y is a flat 2D array in row-major ordering - std::cout<y[i_time * rdata->ny + i_observable]<<" "; + std::cout << rdata->y[i_time * rdata->ny + i_observable] << " "; } - std::cout<setSensitivityOrder(amici::SensitivityOrder::first); @@ -78,18 +76,17 @@ int main() { auto state_ids = model->getStateIds(); auto parameter_ids = model->getParameterIds(); - std::cout<<"State sensitivities for timepoint " - <ts[i_time] - <nx; ++i_state) { - std::cout<<"\td("<plist(i_nplist)]<<") = "; + std::cout << "State sensitivities for timepoint " << rdata->ts[i_time] + << std::endl; // nt x nplist x nx + for (int i_state = 0; i_state < rdata->nx; ++i_state) { + std::cout << "\td(" << state_ids[i_state] << ")/d(" + << parameter_ids[model->plist(i_nplist)] << ") = "; // rdata->sx is a flat 3D array in row-major ordering - std::cout<sx[i_time * rdata->nplist * rdata->nx - + i_nplist * rdata->nx - + i_state]; - std::cout<sx + [i_time * rdata->nplist * rdata->nx + + i_nplist * rdata->nx + i_state]; + std::cout << std::endl; } return 0; diff --git a/models/model_nested_events/model_nested_events.h b/models/model_nested_events/model_nested_events.h index 0ed43eedbd..5cad2049a9 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) 223c5075608273b17f304556d0e8ccb41233bd21 */ +/* Generated by amiwrap (R2017b) 8b324bca5b796a93094195d22a023e5f8e945ef1 */ #include #include #include "amici/defines.h" @@ -75,7 +75,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 "223c5075608273b17f304556d0e8ccb41233bd21"; }; + std::string getAmiciCommit() const override { return "8b324bca5b796a93094195d22a023e5f8e945ef1"; }; 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); @@ -138,10 +138,10 @@ class Model_model_nested_events : public amici::Model_ODE { void fdsigmazdp(double *dsigmazdp, const realtype t, const realtype *p, const realtype *k, const int ip) override { } - void fdwdp(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl) override { + void fdwdp(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl, bool include_static) override { } - void fdwdx(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl) override { + void fdwdx(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl, bool include_static) override { } void fdxdotdp(realtype *dxdotdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const int ip, const realtype *w, const realtype *dwdp) override { @@ -189,7 +189,7 @@ class Model_model_nested_events : public amici::Model_ODE { void fsz(double *sz, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *sx, const int ip) override { } - void fw(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl) override { + void fw(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl, bool include_static) override { } void fx0(realtype *x0, const realtype t, const realtype *p, const realtype *k) override { diff --git a/models/model_neuron/CMakeLists.txt b/models/model_neuron/CMakeLists.txt index 3234271696..4b580c036a 100644 --- a/models/model_neuron/CMakeLists.txt +++ b/models/model_neuron/CMakeLists.txt @@ -1,5 +1,11 @@ # Build AMICI model cmake_minimum_required(VERSION 3.15) +cmake_policy(VERSION 3.15...3.27) + +# cmake >=3.27 +if(POLICY CMP0144) + cmake_policy(SET CMP0144 NEW) +endif(POLICY CMP0144) project(model_neuron) @@ -14,7 +20,7 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") endif() foreach(flag ${MY_CXX_FLAGS}) unset(CUR_FLAG_SUPPORTED CACHE) - check_cxx_compiler_flag(-Werror ${flag} CUR_FLAG_SUPPORTED) + check_cxx_compiler_flag(${flag} CUR_FLAG_SUPPORTED) if(${CUR_FLAG_SUPPORTED}) string(APPEND CMAKE_CXX_FLAGS " ${flag}") endif() @@ -33,6 +39,23 @@ find_package(Amici REQUIRED HINTS ${CMAKE_CURRENT_LIST_DIR}/../../build) message(STATUS "Found AMICI ${Amici_DIR}") +# Debug build? +if("$ENV{ENABLE_AMICI_DEBUGGING}" OR "$ENV{ENABLE_GCOV_COVERAGE}") + add_compile_options(-UNDEBUG) + if(MSVC) + add_compile_options(-DEBUG) + else() + add_compile_options(-O0 -g) + endif() + set(CMAKE_BUILD_TYPE "Debug") +endif() + +# coverage options +if($ENV{ENABLE_GCOV_COVERAGE}) + string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage") + string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " --coverage") +endif() + set(MODEL_DIR ${CMAKE_CURRENT_LIST_DIR}) set(SRC_LIST_LIB ${MODEL_DIR}/JSparse.cpp @@ -90,18 +113,6 @@ if(NOT "${AMICI_PYTHON_BUILD_EXT_ONLY}") target_link_libraries(simulate_${PROJECT_NAME} ${PROJECT_NAME}) endif() -# Debug build? -if("$ENV{ENABLE_AMICI_DEBUGGING}" OR "$ENV{ENABLE_GCOV_COVERAGE}") - add_compile_options(-UNDEBUG -O0 -g) - set(CMAKE_BUILD_TYPE "Debug") -endif() - -# coverage options -if($ENV{ENABLE_GCOV_COVERAGE}) - string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage") - string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " --coverage") -endif() - # SWIG option(ENABLE_SWIG "Build swig/python library?" ON) if(ENABLE_SWIG) diff --git a/models/model_neuron/main.cpp b/models/model_neuron/main.cpp index 00a5e6b448..ecdff85a46 100644 --- a/models/model_neuron/main.cpp +++ b/models/model_neuron/main.cpp @@ -1,14 +1,13 @@ #include -#include /* AMICI base functions */ -#include "wrapfunctions.h" /* model-provided functions */ +#include "wrapfunctions.h" /* model-provided functions */ +#include /* AMICI base functions */ -template < class T > -std::ostream& operator << (std::ostream& os, const std::vector& v) -{ +template +std::ostream& operator<<(std::ostream& os, std::vector const& v) { os << "["; - for (typename std::vector::const_iterator ii = v.begin(); ii != v.end(); ++ii) - { + for (typename std::vector::const_iterator ii = v.begin(); ii != v.end(); + ++ii) { os << " " << *ii; } os << "]"; @@ -21,9 +20,9 @@ std::ostream& operator << (std::ostream& os, const std::vector& v) */ int main() { - std::cout<<"********************************"<getObservableIds(); - std::cout<<"Simulated observables for timepoints "<ts<<"\n\n"; - for(int i_observable = 0; i_observable < rdata->ny; ++i_observable) { - std::cout<nt; ++i_time) { + std::cout << "Simulated observables for timepoints " << rdata->ts << "\n\n"; + for (int i_observable = 0; i_observable < rdata->ny; ++i_observable) { + std::cout << observable_ids[i_observable] << ":\n\t"; + for (int i_time = 0; i_time < rdata->nt; ++i_time) { // rdata->y is a flat 2D array in row-major ordering - std::cout<y[i_time * rdata->ny + i_observable]<<" "; + std::cout << rdata->y[i_time * rdata->ny + i_observable] << " "; } - std::cout<setSensitivityOrder(amici::SensitivityOrder::first); @@ -78,18 +76,17 @@ int main() { auto state_ids = model->getStateIds(); auto parameter_ids = model->getParameterIds(); - std::cout<<"State sensitivities for timepoint " - <ts[i_time] - <nx; ++i_state) { - std::cout<<"\td("<plist(i_nplist)]<<") = "; + std::cout << "State sensitivities for timepoint " << rdata->ts[i_time] + << std::endl; // nt x nplist x nx + for (int i_state = 0; i_state < rdata->nx; ++i_state) { + std::cout << "\td(" << state_ids[i_state] << ")/d(" + << parameter_ids[model->plist(i_nplist)] << ") = "; // rdata->sx is a flat 3D array in row-major ordering - std::cout<sx[i_time * rdata->nplist * rdata->nx - + i_nplist * rdata->nx - + i_state]; - std::cout<sx + [i_time * rdata->nplist * rdata->nx + + i_nplist * rdata->nx + i_state]; + std::cout << std::endl; } return 0; diff --git a/models/model_neuron/model_neuron.h b/models/model_neuron/model_neuron.h index e744f57f65..387a3cadbd 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) 223c5075608273b17f304556d0e8ccb41233bd21 */ +/* Generated by amiwrap (R2017b) 8b324bca5b796a93094195d22a023e5f8e945ef1 */ #include #include #include "amici/defines.h" @@ -89,7 +89,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 "223c5075608273b17f304556d0e8ccb41233bd21"; }; + std::string getAmiciCommit() const override { return "8b324bca5b796a93094195d22a023e5f8e945ef1"; }; 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); @@ -160,10 +160,10 @@ class Model_model_neuron : public amici::Model_ODE { void fdsigmazdp(double *dsigmazdp, const realtype t, const realtype *p, const realtype *k, const int ip) override { } - void fdwdp(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl) override { + void fdwdp(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl, bool include_static) override { } - void fdwdx(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl) override { + void fdwdx(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl, bool include_static) override { } void fdxdotdp(realtype *dxdotdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const int ip, const realtype *w, const realtype *dwdp) override { @@ -216,7 +216,7 @@ class Model_model_neuron : public amici::Model_ODE { sz_model_neuron(sz, ie, t, x, p, k, h, sx, ip); } - void fw(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl) override { + void fw(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl, bool include_static) override { } void fx0(realtype *x0, const realtype t, const realtype *p, const realtype *k) override { diff --git a/models/model_neuron_o2/CMakeLists.txt b/models/model_neuron_o2/CMakeLists.txt index 42f5da90af..161fd4e9ce 100644 --- a/models/model_neuron_o2/CMakeLists.txt +++ b/models/model_neuron_o2/CMakeLists.txt @@ -1,5 +1,11 @@ # Build AMICI model cmake_minimum_required(VERSION 3.15) +cmake_policy(VERSION 3.15...3.27) + +# cmake >=3.27 +if(POLICY CMP0144) + cmake_policy(SET CMP0144 NEW) +endif(POLICY CMP0144) project(model_neuron_o2) @@ -14,7 +20,7 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") endif() foreach(flag ${MY_CXX_FLAGS}) unset(CUR_FLAG_SUPPORTED CACHE) - check_cxx_compiler_flag(-Werror ${flag} CUR_FLAG_SUPPORTED) + check_cxx_compiler_flag(${flag} CUR_FLAG_SUPPORTED) if(${CUR_FLAG_SUPPORTED}) string(APPEND CMAKE_CXX_FLAGS " ${flag}") endif() @@ -33,6 +39,23 @@ find_package(Amici REQUIRED HINTS ${CMAKE_CURRENT_LIST_DIR}/../../build) message(STATUS "Found AMICI ${Amici_DIR}") +# Debug build? +if("$ENV{ENABLE_AMICI_DEBUGGING}" OR "$ENV{ENABLE_GCOV_COVERAGE}") + add_compile_options(-UNDEBUG) + if(MSVC) + add_compile_options(-DEBUG) + else() + add_compile_options(-O0 -g) + endif() + set(CMAKE_BUILD_TYPE "Debug") +endif() + +# coverage options +if($ENV{ENABLE_GCOV_COVERAGE}) + string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage") + string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " --coverage") +endif() + set(MODEL_DIR ${CMAKE_CURRENT_LIST_DIR}) set(SRC_LIST_LIB ${MODEL_DIR}/JSparse.cpp @@ -92,18 +115,6 @@ if(NOT "${AMICI_PYTHON_BUILD_EXT_ONLY}") target_link_libraries(simulate_${PROJECT_NAME} ${PROJECT_NAME}) endif() -# Debug build? -if("$ENV{ENABLE_AMICI_DEBUGGING}" OR "$ENV{ENABLE_GCOV_COVERAGE}") - add_compile_options(-UNDEBUG -O0 -g) - set(CMAKE_BUILD_TYPE "Debug") -endif() - -# coverage options -if($ENV{ENABLE_GCOV_COVERAGE}) - string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage") - string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " --coverage") -endif() - # SWIG option(ENABLE_SWIG "Build swig/python library?" ON) if(ENABLE_SWIG) diff --git a/models/model_neuron_o2/dwdx.cpp b/models/model_neuron_o2/dwdx.cpp index a746d7549a..3a2036c023 100644 --- a/models/model_neuron_o2/dwdx.cpp +++ b/models/model_neuron_o2/dwdx.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_neuron_o2{ -void dwdx_model_neuron_o2(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl) { +void dwdx_model_neuron_o2(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl, bool include_static) { dwdx[0] = 2.0/2.5E1; dwdx[1] = dwdx[0]; } diff --git a/models/model_neuron_o2/main.cpp b/models/model_neuron_o2/main.cpp index 00a5e6b448..ecdff85a46 100644 --- a/models/model_neuron_o2/main.cpp +++ b/models/model_neuron_o2/main.cpp @@ -1,14 +1,13 @@ #include -#include /* AMICI base functions */ -#include "wrapfunctions.h" /* model-provided functions */ +#include "wrapfunctions.h" /* model-provided functions */ +#include /* AMICI base functions */ -template < class T > -std::ostream& operator << (std::ostream& os, const std::vector& v) -{ +template +std::ostream& operator<<(std::ostream& os, std::vector const& v) { os << "["; - for (typename std::vector::const_iterator ii = v.begin(); ii != v.end(); ++ii) - { + for (typename std::vector::const_iterator ii = v.begin(); ii != v.end(); + ++ii) { os << " " << *ii; } os << "]"; @@ -21,9 +20,9 @@ std::ostream& operator << (std::ostream& os, const std::vector& v) */ int main() { - std::cout<<"********************************"<getObservableIds(); - std::cout<<"Simulated observables for timepoints "<ts<<"\n\n"; - for(int i_observable = 0; i_observable < rdata->ny; ++i_observable) { - std::cout<nt; ++i_time) { + std::cout << "Simulated observables for timepoints " << rdata->ts << "\n\n"; + for (int i_observable = 0; i_observable < rdata->ny; ++i_observable) { + std::cout << observable_ids[i_observable] << ":\n\t"; + for (int i_time = 0; i_time < rdata->nt; ++i_time) { // rdata->y is a flat 2D array in row-major ordering - std::cout<y[i_time * rdata->ny + i_observable]<<" "; + std::cout << rdata->y[i_time * rdata->ny + i_observable] << " "; } - std::cout<setSensitivityOrder(amici::SensitivityOrder::first); @@ -78,18 +76,17 @@ int main() { auto state_ids = model->getStateIds(); auto parameter_ids = model->getParameterIds(); - std::cout<<"State sensitivities for timepoint " - <ts[i_time] - <nx; ++i_state) { - std::cout<<"\td("<plist(i_nplist)]<<") = "; + std::cout << "State sensitivities for timepoint " << rdata->ts[i_time] + << std::endl; // nt x nplist x nx + for (int i_state = 0; i_state < rdata->nx; ++i_state) { + std::cout << "\td(" << state_ids[i_state] << ")/d(" + << parameter_ids[model->plist(i_nplist)] << ") = "; // rdata->sx is a flat 3D array in row-major ordering - std::cout<sx[i_time * rdata->nplist * rdata->nx - + i_nplist * rdata->nx - + i_state]; - std::cout<sx + [i_time * rdata->nplist * rdata->nx + + i_nplist * rdata->nx + i_state]; + std::cout << std::endl; } return 0; diff --git a/models/model_neuron_o2/model_neuron_o2.h b/models/model_neuron_o2/model_neuron_o2.h index a108e6284b..4ae613ee84 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) 223c5075608273b17f304556d0e8ccb41233bd21 */ +/* Generated by amiwrap (R2017b) 8b324bca5b796a93094195d22a023e5f8e945ef1 */ #include #include #include "amici/defines.h" @@ -29,7 +29,7 @@ extern void deltasx_model_neuron_o2(double *deltasx, const realtype t, const rea extern void deltax_model_neuron_o2(double *deltax, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const int ie, const realtype *xdot, const realtype *xdot_old); extern void deltaxB_model_neuron_o2(double *deltaxB, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const int ie, const realtype *xdot, const realtype *xdot_old, const realtype *xB); extern void drzdx_model_neuron_o2(double *drzdx, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h); -extern void dwdx_model_neuron_o2(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl); +extern void dwdx_model_neuron_o2(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl, bool include_static); extern void dxdotdp_model_neuron_o2(realtype *dxdotdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const int ip, const realtype *w, const realtype *dwdp); extern void dydx_model_neuron_o2(double *dydx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *dwdx); extern void dzdx_model_neuron_o2(double *dzdx, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h); @@ -41,7 +41,7 @@ extern void srz_model_neuron_o2(double *srz, const int ie, const realtype t, con extern void stau_model_neuron_o2(double *stau, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *sx, const int ip, const int ie); extern void sx0_model_neuron_o2(realtype *sx0, const realtype t,const realtype *x0, const realtype *p, const realtype *k, const int ip); extern void sz_model_neuron_o2(double *sz, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *sx, const int ip); -extern void w_model_neuron_o2(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl); +extern void w_model_neuron_o2(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl, bool include_static); extern void x0_model_neuron_o2(realtype *x0, const realtype t, const realtype *p, const realtype *k); extern void xdot_model_neuron_o2(realtype *xdot, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w); extern void y_model_neuron_o2(double *y, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w); @@ -91,7 +91,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 "223c5075608273b17f304556d0e8ccb41233bd21"; }; + std::string getAmiciCommit() const override { return "8b324bca5b796a93094195d22a023e5f8e945ef1"; }; 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); @@ -162,11 +162,11 @@ class Model_model_neuron_o2 : public amici::Model_ODE { void fdsigmazdp(double *dsigmazdp, const realtype t, const realtype *p, const realtype *k, const int ip) override { } - void fdwdp(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl) override { + void fdwdp(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl, bool include_static) override { } - void fdwdx(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl) override { - dwdx_model_neuron_o2(dwdx, t, x, p, k, h, w, tcl, spl); + void fdwdx(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl, bool include_static) override { + dwdx_model_neuron_o2(dwdx, t, x, p, k, h, w, tcl, spl, include_static); } void fdxdotdp(realtype *dxdotdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const int ip, const realtype *w, const realtype *dwdp) override { @@ -219,8 +219,8 @@ class Model_model_neuron_o2 : public amici::Model_ODE { sz_model_neuron_o2(sz, ie, t, x, p, k, h, sx, ip); } - void fw(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl) override { - w_model_neuron_o2(w, t, x, p, k, h, tcl, spl); + void fw(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl, bool include_static) override { + w_model_neuron_o2(w, t, x, p, k, h, tcl, spl, include_static); } void fx0(realtype *x0, const realtype t, const realtype *p, const realtype *k) override { diff --git a/models/model_neuron_o2/w.cpp b/models/model_neuron_o2/w.cpp index cbd2f0a25f..fca88b9e36 100644 --- a/models/model_neuron_o2/w.cpp +++ b/models/model_neuron_o2/w.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_neuron_o2{ -void w_model_neuron_o2(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl) { +void w_model_neuron_o2(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl, bool include_static) { w[0] = x[0]*(2.0/2.5E1); w[1] = w[0]+5.0; } diff --git a/models/model_robertson/CMakeLists.txt b/models/model_robertson/CMakeLists.txt index 9b27e3daaf..1a4c57353a 100644 --- a/models/model_robertson/CMakeLists.txt +++ b/models/model_robertson/CMakeLists.txt @@ -1,5 +1,11 @@ # Build AMICI model cmake_minimum_required(VERSION 3.15) +cmake_policy(VERSION 3.15...3.27) + +# cmake >=3.27 +if(POLICY CMP0144) + cmake_policy(SET CMP0144 NEW) +endif(POLICY CMP0144) project(model_robertson) @@ -14,7 +20,7 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") endif() foreach(flag ${MY_CXX_FLAGS}) unset(CUR_FLAG_SUPPORTED CACHE) - check_cxx_compiler_flag(-Werror ${flag} CUR_FLAG_SUPPORTED) + check_cxx_compiler_flag(${flag} CUR_FLAG_SUPPORTED) if(${CUR_FLAG_SUPPORTED}) string(APPEND CMAKE_CXX_FLAGS " ${flag}") endif() @@ -33,6 +39,23 @@ find_package(Amici REQUIRED HINTS ${CMAKE_CURRENT_LIST_DIR}/../../build) message(STATUS "Found AMICI ${Amici_DIR}") +# Debug build? +if("$ENV{ENABLE_AMICI_DEBUGGING}" OR "$ENV{ENABLE_GCOV_COVERAGE}") + add_compile_options(-UNDEBUG) + if(MSVC) + add_compile_options(-DEBUG) + else() + add_compile_options(-O0 -g) + endif() + set(CMAKE_BUILD_TYPE "Debug") +endif() + +# coverage options +if($ENV{ENABLE_GCOV_COVERAGE}) + string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage") + string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " --coverage") +endif() + set(MODEL_DIR ${CMAKE_CURRENT_LIST_DIR}) set(SRC_LIST_LIB ${MODEL_DIR}/JSparse.cpp @@ -74,18 +97,6 @@ if(NOT "${AMICI_PYTHON_BUILD_EXT_ONLY}") target_link_libraries(simulate_${PROJECT_NAME} ${PROJECT_NAME}) endif() -# Debug build? -if("$ENV{ENABLE_AMICI_DEBUGGING}" OR "$ENV{ENABLE_GCOV_COVERAGE}") - add_compile_options(-UNDEBUG -O0 -g) - set(CMAKE_BUILD_TYPE "Debug") -endif() - -# coverage options -if($ENV{ENABLE_GCOV_COVERAGE}) - string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage") - string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " --coverage") -endif() - # SWIG option(ENABLE_SWIG "Build swig/python library?" ON) if(ENABLE_SWIG) diff --git a/models/model_robertson/dwdp.cpp b/models/model_robertson/dwdp.cpp index 831c448cad..5911b99078 100644 --- a/models/model_robertson/dwdp.cpp +++ b/models/model_robertson/dwdp.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_robertson{ -void dwdp_model_robertson(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl) { +void dwdp_model_robertson(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl, bool include_static) { dwdp[0] = x[1]*x[2]; } diff --git a/models/model_robertson/dwdx.cpp b/models/model_robertson/dwdx.cpp index 5c300a54ec..1e75c29246 100644 --- a/models/model_robertson/dwdx.cpp +++ b/models/model_robertson/dwdx.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_robertson{ -void dwdx_model_robertson(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl) { +void dwdx_model_robertson(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl, bool include_static) { dwdx[0] = p[1]*x[2]; dwdx[1] = p[1]*x[1]; } diff --git a/models/model_robertson/main.cpp b/models/model_robertson/main.cpp index 00a5e6b448..ecdff85a46 100644 --- a/models/model_robertson/main.cpp +++ b/models/model_robertson/main.cpp @@ -1,14 +1,13 @@ #include -#include /* AMICI base functions */ -#include "wrapfunctions.h" /* model-provided functions */ +#include "wrapfunctions.h" /* model-provided functions */ +#include /* AMICI base functions */ -template < class T > -std::ostream& operator << (std::ostream& os, const std::vector& v) -{ +template +std::ostream& operator<<(std::ostream& os, std::vector const& v) { os << "["; - for (typename std::vector::const_iterator ii = v.begin(); ii != v.end(); ++ii) - { + for (typename std::vector::const_iterator ii = v.begin(); ii != v.end(); + ++ii) { os << " " << *ii; } os << "]"; @@ -21,9 +20,9 @@ std::ostream& operator << (std::ostream& os, const std::vector& v) */ int main() { - std::cout<<"********************************"<getObservableIds(); - std::cout<<"Simulated observables for timepoints "<ts<<"\n\n"; - for(int i_observable = 0; i_observable < rdata->ny; ++i_observable) { - std::cout<nt; ++i_time) { + std::cout << "Simulated observables for timepoints " << rdata->ts << "\n\n"; + for (int i_observable = 0; i_observable < rdata->ny; ++i_observable) { + std::cout << observable_ids[i_observable] << ":\n\t"; + for (int i_time = 0; i_time < rdata->nt; ++i_time) { // rdata->y is a flat 2D array in row-major ordering - std::cout<y[i_time * rdata->ny + i_observable]<<" "; + std::cout << rdata->y[i_time * rdata->ny + i_observable] << " "; } - std::cout<setSensitivityOrder(amici::SensitivityOrder::first); @@ -78,18 +76,17 @@ int main() { auto state_ids = model->getStateIds(); auto parameter_ids = model->getParameterIds(); - std::cout<<"State sensitivities for timepoint " - <ts[i_time] - <nx; ++i_state) { - std::cout<<"\td("<plist(i_nplist)]<<") = "; + std::cout << "State sensitivities for timepoint " << rdata->ts[i_time] + << std::endl; // nt x nplist x nx + for (int i_state = 0; i_state < rdata->nx; ++i_state) { + std::cout << "\td(" << state_ids[i_state] << ")/d(" + << parameter_ids[model->plist(i_nplist)] << ") = "; // rdata->sx is a flat 3D array in row-major ordering - std::cout<sx[i_time * rdata->nplist * rdata->nx - + i_nplist * rdata->nx - + i_state]; - std::cout<sx + [i_time * rdata->nplist * rdata->nx + + i_nplist * rdata->nx + i_state]; + std::cout << std::endl; } return 0; diff --git a/models/model_robertson/model_robertson.h b/models/model_robertson/model_robertson.h index 816dd2db32..4f25f4d9bd 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) 223c5075608273b17f304556d0e8ccb41233bd21 */ +/* Generated by amiwrap (R2017b) 8b324bca5b796a93094195d22a023e5f8e945ef1 */ #include #include #include "amici/defines.h" @@ -19,12 +19,12 @@ extern void Jy_model_robertson(double *nllh, const int iy, const realtype *p, co extern void M_model_robertson(realtype *M, const realtype t, const realtype *x, const realtype *p, const realtype *k); extern void dJydsigma_model_robertson(double *dJydsigma, const int iy, const realtype *p, const realtype *k, const double *y, const double *sigmay, const double *my); extern void dJydy_model_robertson(double *dJydy, const int iy, const realtype *p, const realtype *k, const double *y, const double *sigmay, const double *my); -extern void dwdp_model_robertson(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl); -extern void dwdx_model_robertson(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl); +extern void dwdp_model_robertson(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl, bool include_static); +extern void dwdx_model_robertson(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl, bool include_static); extern void dxdotdp_model_robertson(realtype *dxdotdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const int ip, const realtype *dx, const realtype *w, const realtype *dwdp); extern void dydx_model_robertson(double *dydx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *dwdx); extern void sigmay_model_robertson(double *sigmay, const realtype t, const realtype *p, const realtype *k, const realtype *y); -extern void w_model_robertson(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl); +extern void w_model_robertson(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl, bool include_static); extern void x0_model_robertson(realtype *x0, const realtype t, const realtype *p, const realtype *k); extern void xdot_model_robertson(realtype *xdot, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *dx, const realtype *w); extern void y_model_robertson(double *y, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w); @@ -73,7 +73,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 "223c5075608273b17f304556d0e8ccb41233bd21"; }; + std::string getAmiciCommit() const override { return "8b324bca5b796a93094195d22a023e5f8e945ef1"; }; 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); @@ -137,12 +137,12 @@ class Model_model_robertson : public amici::Model_DAE { void fdsigmazdp(double *dsigmazdp, const realtype t, const realtype *p, const realtype *k, const int ip) override { } - void fdwdp(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl) override { - dwdp_model_robertson(dwdp, t, x, p, k, h, w, tcl, stcl, spl, sspl); + void fdwdp(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl, bool include_static) override { + dwdp_model_robertson(dwdp, t, x, p, k, h, w, tcl, stcl, spl, sspl, include_static); } - void fdwdx(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl) override { - dwdx_model_robertson(dwdx, t, x, p, k, h, w, tcl, spl); + void fdwdx(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl, bool include_static) override { + dwdx_model_robertson(dwdx, t, x, p, k, h, w, tcl, spl, include_static); } void fdxdotdp(realtype *dxdotdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const int ip, const realtype *dx, const realtype *w, const realtype *dwdp) override { @@ -187,8 +187,8 @@ class Model_model_robertson : public amici::Model_DAE { void fsz(double *sz, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *sx, const int ip) override { } - void fw(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl) override { - w_model_robertson(w, t, x, p, k, h, tcl, spl); + void fw(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl, bool include_static) override { + w_model_robertson(w, t, x, p, k, h, tcl, spl, include_static); } void fx0(realtype *x0, const realtype t, const realtype *p, const realtype *k) override { diff --git a/models/model_robertson/w.cpp b/models/model_robertson/w.cpp index 6905b49c0e..ae4145ba6b 100644 --- a/models/model_robertson/w.cpp +++ b/models/model_robertson/w.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_robertson{ -void w_model_robertson(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl) { +void w_model_robertson(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl, bool include_static) { w[0] = p[1]*x[1]*x[2]; } diff --git a/models/model_steadystate/CMakeLists.txt b/models/model_steadystate/CMakeLists.txt index 9b699da5b0..3d0dacaf83 100644 --- a/models/model_steadystate/CMakeLists.txt +++ b/models/model_steadystate/CMakeLists.txt @@ -1,5 +1,11 @@ # Build AMICI model cmake_minimum_required(VERSION 3.15) +cmake_policy(VERSION 3.15...3.27) + +# cmake >=3.27 +if(POLICY CMP0144) + cmake_policy(SET CMP0144 NEW) +endif(POLICY CMP0144) project(model_steadystate) @@ -14,7 +20,7 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") endif() foreach(flag ${MY_CXX_FLAGS}) unset(CUR_FLAG_SUPPORTED CACHE) - check_cxx_compiler_flag(-Werror ${flag} CUR_FLAG_SUPPORTED) + check_cxx_compiler_flag(${flag} CUR_FLAG_SUPPORTED) if(${CUR_FLAG_SUPPORTED}) string(APPEND CMAKE_CXX_FLAGS " ${flag}") endif() @@ -33,6 +39,23 @@ find_package(Amici REQUIRED HINTS ${CMAKE_CURRENT_LIST_DIR}/../../build) message(STATUS "Found AMICI ${Amici_DIR}") +# Debug build? +if("$ENV{ENABLE_AMICI_DEBUGGING}" OR "$ENV{ENABLE_GCOV_COVERAGE}") + add_compile_options(-UNDEBUG) + if(MSVC) + add_compile_options(-DEBUG) + else() + add_compile_options(-O0 -g) + endif() + set(CMAKE_BUILD_TYPE "Debug") +endif() + +# coverage options +if($ENV{ENABLE_GCOV_COVERAGE}) + string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage") + string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " --coverage") +endif() + set(MODEL_DIR ${CMAKE_CURRENT_LIST_DIR}) set(SRC_LIST_LIB ${MODEL_DIR}/JSparse.cpp @@ -73,18 +96,6 @@ if(NOT "${AMICI_PYTHON_BUILD_EXT_ONLY}") target_link_libraries(simulate_${PROJECT_NAME} ${PROJECT_NAME}) endif() -# Debug build? -if("$ENV{ENABLE_AMICI_DEBUGGING}" OR "$ENV{ENABLE_GCOV_COVERAGE}") - add_compile_options(-UNDEBUG -O0 -g) - set(CMAKE_BUILD_TYPE "Debug") -endif() - -# coverage options -if($ENV{ENABLE_GCOV_COVERAGE}) - string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage") - string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " --coverage") -endif() - # SWIG option(ENABLE_SWIG "Build swig/python library?" ON) if(ENABLE_SWIG) diff --git a/models/model_steadystate/dwdp.cpp b/models/model_steadystate/dwdp.cpp index 154db2a72f..d31310d341 100644 --- a/models/model_steadystate/dwdp.cpp +++ b/models/model_steadystate/dwdp.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_steadystate{ -void dwdp_model_steadystate(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl) { +void dwdp_model_steadystate(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl, bool include_static) { dwdp[0] = x[2]; } diff --git a/models/model_steadystate/dwdx.cpp b/models/model_steadystate/dwdx.cpp index d447f2140d..dcd5f5e49e 100644 --- a/models/model_steadystate/dwdx.cpp +++ b/models/model_steadystate/dwdx.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_steadystate{ -void dwdx_model_steadystate(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl) { +void dwdx_model_steadystate(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl, bool include_static) { dwdx[0] = x[0]*2.0; dwdx[1] = p[3]; } diff --git a/models/model_steadystate/main.cpp b/models/model_steadystate/main.cpp index 00a5e6b448..ecdff85a46 100644 --- a/models/model_steadystate/main.cpp +++ b/models/model_steadystate/main.cpp @@ -1,14 +1,13 @@ #include -#include /* AMICI base functions */ -#include "wrapfunctions.h" /* model-provided functions */ +#include "wrapfunctions.h" /* model-provided functions */ +#include /* AMICI base functions */ -template < class T > -std::ostream& operator << (std::ostream& os, const std::vector& v) -{ +template +std::ostream& operator<<(std::ostream& os, std::vector const& v) { os << "["; - for (typename std::vector::const_iterator ii = v.begin(); ii != v.end(); ++ii) - { + for (typename std::vector::const_iterator ii = v.begin(); ii != v.end(); + ++ii) { os << " " << *ii; } os << "]"; @@ -21,9 +20,9 @@ std::ostream& operator << (std::ostream& os, const std::vector& v) */ int main() { - std::cout<<"********************************"<getObservableIds(); - std::cout<<"Simulated observables for timepoints "<ts<<"\n\n"; - for(int i_observable = 0; i_observable < rdata->ny; ++i_observable) { - std::cout<nt; ++i_time) { + std::cout << "Simulated observables for timepoints " << rdata->ts << "\n\n"; + for (int i_observable = 0; i_observable < rdata->ny; ++i_observable) { + std::cout << observable_ids[i_observable] << ":\n\t"; + for (int i_time = 0; i_time < rdata->nt; ++i_time) { // rdata->y is a flat 2D array in row-major ordering - std::cout<y[i_time * rdata->ny + i_observable]<<" "; + std::cout << rdata->y[i_time * rdata->ny + i_observable] << " "; } - std::cout<setSensitivityOrder(amici::SensitivityOrder::first); @@ -78,18 +76,17 @@ int main() { auto state_ids = model->getStateIds(); auto parameter_ids = model->getParameterIds(); - std::cout<<"State sensitivities for timepoint " - <ts[i_time] - <nx; ++i_state) { - std::cout<<"\td("<plist(i_nplist)]<<") = "; + std::cout << "State sensitivities for timepoint " << rdata->ts[i_time] + << std::endl; // nt x nplist x nx + for (int i_state = 0; i_state < rdata->nx; ++i_state) { + std::cout << "\td(" << state_ids[i_state] << ")/d(" + << parameter_ids[model->plist(i_nplist)] << ") = "; // rdata->sx is a flat 3D array in row-major ordering - std::cout<sx[i_time * rdata->nplist * rdata->nx - + i_nplist * rdata->nx - + i_state]; - std::cout<sx + [i_time * rdata->nplist * rdata->nx + + i_nplist * rdata->nx + i_state]; + std::cout << std::endl; } return 0; diff --git a/models/model_steadystate/model_steadystate.h b/models/model_steadystate/model_steadystate.h index 776b754b08..ca02261ef8 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) 223c5075608273b17f304556d0e8ccb41233bd21 */ +/* Generated by amiwrap (R2017b) 8b324bca5b796a93094195d22a023e5f8e945ef1 */ #include #include #include "amici/defines.h" @@ -18,12 +18,12 @@ extern void JSparse_model_steadystate(SUNMatrixContent_Sparse JSparse, const rea extern void Jy_model_steadystate(double *nllh, const int iy, const realtype *p, const realtype *k, const double *y, const double *sigmay, const double *my); extern void dJydsigma_model_steadystate(double *dJydsigma, const int iy, const realtype *p, const realtype *k, const double *y, const double *sigmay, const double *my); extern void dJydy_model_steadystate(double *dJydy, const int iy, const realtype *p, const realtype *k, const double *y, const double *sigmay, const double *my); -extern void dwdp_model_steadystate(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl); -extern void dwdx_model_steadystate(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl); +extern void dwdp_model_steadystate(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl, bool include_static); +extern void dwdx_model_steadystate(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl, bool include_static); extern void dxdotdp_model_steadystate(realtype *dxdotdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const int ip, const realtype *w, const realtype *dwdp); extern void dydx_model_steadystate(double *dydx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *dwdx); extern void sigmay_model_steadystate(double *sigmay, const realtype t, const realtype *p, const realtype *k, const realtype *y); -extern void w_model_steadystate(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl); +extern void w_model_steadystate(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl, bool include_static); extern void x0_model_steadystate(realtype *x0, const realtype t, const realtype *p, const realtype *k); extern void xdot_model_steadystate(realtype *xdot, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w); extern void y_model_steadystate(double *y, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w); @@ -72,7 +72,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 "223c5075608273b17f304556d0e8ccb41233bd21"; }; + std::string getAmiciCommit() const override { return "8b324bca5b796a93094195d22a023e5f8e945ef1"; }; 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); @@ -132,12 +132,12 @@ class Model_model_steadystate : public amici::Model_ODE { void fdsigmazdp(double *dsigmazdp, const realtype t, const realtype *p, const realtype *k, const int ip) override { } - void fdwdp(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl) override { - dwdp_model_steadystate(dwdp, t, x, p, k, h, w, tcl, stcl, spl, sspl); + void fdwdp(realtype *dwdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *stcl, const realtype *spl, const realtype *sspl, bool include_static) override { + dwdp_model_steadystate(dwdp, t, x, p, k, h, w, tcl, stcl, spl, sspl, include_static); } - void fdwdx(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl) override { - dwdx_model_steadystate(dwdx, t, x, p, k, h, w, tcl, spl); + void fdwdx(realtype *dwdx, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *w, const realtype *tcl, const realtype *spl, bool include_static) override { + dwdx_model_steadystate(dwdx, t, x, p, k, h, w, tcl, spl, include_static); } void fdxdotdp(realtype *dxdotdp, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const int ip, const realtype *w, const realtype *dwdp) override { @@ -182,8 +182,8 @@ class Model_model_steadystate : public amici::Model_ODE { void fsz(double *sz, const int ie, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *sx, const int ip) override { } - void fw(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl) override { - w_model_steadystate(w, t, x, p, k, h, tcl, spl); + void fw(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl, bool include_static) override { + w_model_steadystate(w, t, x, p, k, h, tcl, spl, include_static); } void fx0(realtype *x0, const realtype t, const realtype *p, const realtype *k) override { diff --git a/models/model_steadystate/w.cpp b/models/model_steadystate/w.cpp index 948d4529c2..5a0acafc83 100644 --- a/models/model_steadystate/w.cpp +++ b/models/model_steadystate/w.cpp @@ -10,7 +10,7 @@ namespace amici { namespace model_model_steadystate{ -void w_model_steadystate(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl) { +void w_model_steadystate(realtype *w, const realtype t, const realtype *x, const realtype *p, const realtype *k, const realtype *h, const realtype *tcl, const realtype *spl, bool include_static) { w[0] = p[3]*x[2]; w[1] = x[0]*x[0]; } diff --git a/python/sdist/amici/cxxcodeprinter.py b/python/sdist/amici/cxxcodeprinter.py index 032089393d..adc4c25454 100644 --- a/python/sdist/amici/cxxcodeprinter.py +++ b/python/sdist/amici/cxxcodeprinter.py @@ -3,6 +3,7 @@ import os import re from typing import Optional +from collections.abc import Sequence from collections.abc import Iterable import sympy as sp @@ -123,6 +124,7 @@ def _get_sym_lines_symbols( equations: sp.Matrix, variable: str, indent_level: int, + indices: Optional[Sequence[int]] = None, ) -> list[str]: """ Generate C++ code for where array elements are directly replaced with @@ -140,9 +142,19 @@ def _get_sym_lines_symbols( :param indent_level: indentation level (number of leading blanks) + :param indices: + Optional custom indices corresponding to entries in `symbols`. + Only used for comments. + :return: C++ code as list of lines """ + assert len(symbols) == len(equations) + if indices is None: + indices = range(len(symbols)) + else: + assert len(indices) == len(symbols) + indent = " " * indent_level def format_regular_line(symbol, math, index): @@ -179,7 +191,9 @@ def format_regular_line(symbol, math, index): for (identifier, definition) in expr_dict.items() } ) - symbol_to_idx = {sym: idx for idx, sym in enumerate(symbols)} + symbol_to_idx = { + sym: idx for idx, sym in zip(indices, symbols) + } def format_line(symbol: sp.Symbol): math = expr_dict[symbol] @@ -203,7 +217,7 @@ def format_line(symbol: sp.Symbol): return [ format_regular_line(sym, math, index) - for index, (sym, math) in enumerate(zip(symbols, equations)) + for index, sym, math in zip(indices, symbols, equations) if math not in [0, 0.0] ] diff --git a/python/sdist/amici/de_export.py b/python/sdist/amici/de_export.py index be5a70e5cc..b853dd6ad0 100644 --- a/python/sdist/amici/de_export.py +++ b/python/sdist/amici/de_export.py @@ -179,14 +179,15 @@ def arguments(self, ode: bool = True) -> str: "realtype *dwdp, const realtype t, const realtype *x, " "const realtype *p, const realtype *k, const realtype *h, " "const realtype *w, const realtype *tcl, const realtype *dtcldp, " - "const realtype *spl, const realtype *sspl", + "const realtype *spl, const realtype *sspl, bool include_static", assume_pow_positivity=True, sparse=True, ), "dwdx": _FunctionInfo( "realtype *dwdx, const realtype t, const realtype *x, " "const realtype *p, const realtype *k, const realtype *h, " - "const realtype *w, const realtype *tcl, const realtype *spl", + "const realtype *w, const realtype *tcl, const realtype *spl, " + "bool include_static", assume_pow_positivity=True, sparse=True, ), @@ -211,7 +212,7 @@ def arguments(self, ode: bool = True) -> str: "dwdw": _FunctionInfo( "realtype *dwdw, const realtype t, const realtype *x, " "const realtype *p, const realtype *k, const realtype *h, " - "const realtype *w, const realtype *tcl", + "const realtype *w, const realtype *tcl, bool include_static", assume_pow_positivity=True, sparse=True, ), @@ -331,7 +332,8 @@ def arguments(self, ode: bool = True) -> str: "w": _FunctionInfo( "realtype *w, const realtype t, const realtype *x, " "const realtype *p, const realtype *k, " - "const realtype *h, const realtype *tcl, const realtype *spl", + "const realtype *h, const realtype *tcl, const realtype *spl, " + "bool include_static", assume_pow_positivity=True, ), "x0": _FunctionInfo( @@ -612,6 +614,10 @@ class DEModel: whether all observables have a gaussian noise model, i.e. whether res and FIM make sense. + :ivar _static_indices: + dict of lists list of indices of static variables for different + model entities. + :ivar _z2event: list of event indices for each event observable """ @@ -749,6 +755,8 @@ def cached_simplify( self._has_quadratic_nllh: bool = True set_log_level(logger, verbose) + self._static_indices: dict[str, list[int]] = {} + def differential_states(self) -> list[DifferentialState]: """Get all differential states.""" return self._differential_states @@ -1345,6 +1353,153 @@ def free_symbols(self) -> set[sp.Basic]: ) ) + def static_indices(self, name: str) -> list[int]: + """ + Returns the indices of static expressions in the given model entity. + + Static expressions are those that do not depend on time, + neither directly nor indirectly. + + :param name: Name of the model entity. + :return: List of indices of static expressions. + """ + # already computed? + if (res := self._static_indices.get(name)) is not None: + return res + + if name == "w": + dwdx = self.sym("dwdx") + dwdw = self.sym("dwdw") + w = self.eq("w") + + # Check for direct (via `t`) or indirect (via `x`, `h`, or splines) + # time dependency. + # To avoid lengthy symbolic computations, we only check if we have + # any non-zeros in hierarchy. We currently neglect the case where + # different hierarchy levels may cancel out. Treating a static + # expression as dynamic in such rare cases shouldn't be a problem. + dynamic_dependency = np.asarray( + dwdx.applyfunc(lambda x: int(not x.is_zero)) + ).astype(np.int64) + # to check for other time-dependence, we add a column to the dwdx + # matrix + dynamic_syms = [ + # FIXME: see spline comment below + # *self.sym("spl"), + *self.sym("h"), + amici_time_symbol, + ] + dynamic_dependency = np.hstack( + ( + dynamic_dependency, + np.array( + [ + expr.has(*dynamic_syms) + # FIXME: the current spline implementation is a giant pita + # currently, the splines occur in the form of sympy functions, e.g.: + # AmiciSpline(y0, time, y0_3, y0_1) + # AmiciSplineSensitivity(y0, time, y0_1, y0_3, y0_1) + # until it uses the proper self.sym("spl") / self.sym("sspl") + # symbols, which will require quite some refactoring, + # we just do dumb string checks :| + or ( + bool(self._splines) + and "AmiciSpline" in str(expr) + ) + for expr in w + ] + )[:, np.newaxis], + ) + ) + + nonzero_dwdw = np.asarray( + dwdw.applyfunc(lambda x: int(not x.is_zero)) + ).astype(np.int64) + + # `w` is made up an expression hierarchy. Any given entry is only + # static if all its dependencies are static. Here, we unravel + # the hierarchical structure of `w`. + # If for an entry in `w`, the row sum of the intermediate products + # is 0 across all levels, the expression is static. + tmp = dynamic_dependency + res = np.sum(tmp, axis=1) + while np.any(tmp != 0): + tmp = nonzero_dwdw.dot(tmp) + res += np.sum(tmp, axis=1) + self._static_indices[name] = ( + np.argwhere(res == 0).flatten().tolist() + ) + + return self._static_indices[name] + + if name in ("dwdw", "dwdx", "dwdp"): + static_indices_w = set(self.static_indices("w")) + dynamic_syms = [ + *( + sym + for i, sym in enumerate(self.sym("w")) + if i not in static_indices_w + ), + amici_time_symbol, + *self.sym("x"), + *self.sym("h"), + # FIXME see spline comment above + # *(self.sym("spl") if name in ("dwdw", "dwdx") else ()), + # *(self.sym("sspl") if name == "dwdp" else ()), + ] + dynamic_syms = sp.Matrix(dynamic_syms) + rowvals = self.rowvals(name) + sparseeq = self.sparseeq(name) + + # collect the indices of static expressions of dwd* from the list + # of non-zeros entries of the sparse matrix + self._static_indices[name] = [ + i + for i, (expr, row_idx) in enumerate(zip(sparseeq, rowvals)) + # derivative of a static expression is static + if row_idx in static_indices_w + # constant expressions + or expr.is_Number + # check for dependencies on non-static entities + or ( + # FIXME see spline comment above + # (check str before diff, as diff will fail on spline functions) + ( + # splines: non-static + not self._splines or "AmiciSpline" not in str(expr) + ) + and ( + # If the expression contains dynamic symbols, it might + # still be static. However, checking the derivative + # is currently too expensive, and we rather accept + # false negatives. + not expr.has(*dynamic_syms) + # or all( + # expr.diff(dyn_sym).is_zero + # for dyn_sym in dynamic_syms + # ) + ) + ) + ] + return self._static_indices[name] + + raise NotImplementedError(name) + + def dynamic_indices(self, name: str) -> list[int]: + """ + Return the indices of dynamic expressions in the given model entity. + + :param name: Name of the model entity. + :return: List of indices of dynamic expressions. + """ + static_idxs = set(self.static_indices(name)) + length = len( + self.sparsesym(name) + if name in sparse_functions + else self.sym(name) + ) + return [i for i in range(length) if i not in static_idxs] + def _generate_symbol(self, name: str) -> None: """ Generates the symbolic identifiers for a symbolic variable @@ -3261,9 +3416,57 @@ def _get_function_body( symbols = list(map(sp.Symbol, self.model.sparsesym(function))) else: symbols = self.model.sym(function) - lines += self._code_printer._get_sym_lines_symbols( - symbols, equations, function, 4 - ) + + if function in ("w", "dwdw", "dwdx", "dwdp"): + # Split into a block of static and dynamic expressions. + if len(static_idxs := self.model.static_indices(function)) > 0: + tmp_symbols = sp.Matrix( + [[symbols[i]] for i in static_idxs] + ) + tmp_equations = sp.Matrix( + [equations[i] for i in static_idxs] + ) + tmp_lines = self._code_printer._get_sym_lines_symbols( + tmp_symbols, + tmp_equations, + function, + 8, + static_idxs, + ) + if tmp_lines: + lines.extend( + [ + " // static expressions", + " if (include_static) {", + *tmp_lines, + " }", + ] + ) + + # dynamic expressions + if len(dynamic_idxs := self.model.dynamic_indices(function)): + tmp_symbols = sp.Matrix( + [[symbols[i]] for i in dynamic_idxs] + ) + tmp_equations = sp.Matrix( + [equations[i] for i in dynamic_idxs] + ) + + tmp_lines = self._code_printer._get_sym_lines_symbols( + tmp_symbols, + tmp_equations, + function, + 4, + dynamic_idxs, + ) + if tmp_lines: + lines.append("\n // dynamic expressions") + lines.extend(tmp_lines) + + else: + lines += self._code_printer._get_sym_lines_symbols( + symbols, equations, function, 4 + ) else: lines += self._code_printer._get_sym_lines_array( @@ -3808,7 +4011,7 @@ def get_model_override_implementation( body = ( "" if nobody - else "\n{ind8}{maybe_return}{fun}_{name}({eval_signature});{ind4}\n".format( + else "\n{ind8}{maybe_return}{fun}_{name}({eval_signature});\n{ind4}".format( ind4=" " * 4, ind8=" " * 8, maybe_return="" if func_info.return_type == "void" else "return ", @@ -3855,7 +4058,9 @@ def get_sunindex_override_implementation( if nobody: impl += "}}\n" else: - impl += "{ind8}{fun}_{indextype}_{name}({eval_signature});\n{ind4}}}\n" + impl += ( + "\n{ind8}{fun}_{indextype}_{name}({eval_signature});\n{ind4}}}\n" + ) return impl.format( ind4=" " * 4, @@ -3892,6 +4097,7 @@ def remove_argument_types(signature: str) -> str: "realtype *", "const int ", "int ", + "bool ", "SUNMatrixContent_Sparse ", "gsl::span", ] diff --git a/src/abstract_model.cpp b/src/abstract_model.cpp index 041d06b361..dfa0780535 100644 --- a/src/abstract_model.cpp +++ b/src/abstract_model.cpp @@ -437,10 +437,11 @@ void AbstractModel::fdJrzdsigma( ); } -void AbstractModel:: - fw(realtype* /*w*/, realtype const /*t*/, realtype const* /*x*/, - realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, - realtype const* /*tcl*/, realtype const* /*spl*/) { +void AbstractModel::fw( + realtype* /*w*/, realtype const /*t*/, realtype const* /*x*/, + realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, + realtype const* /*tcl*/, realtype const* /*spl*/, bool /*include_static*/ +) { throw AmiException( "Requested functionality is not supported as %s is " "not implemented for this model!", @@ -452,7 +453,7 @@ void AbstractModel::fdwdp( realtype* /*dwdp*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, realtype const* /*w*/, realtype const* /*tcl*/, realtype const* /*stcl*/, - realtype const* /*spl*/, realtype const* /*sspl*/ + realtype const* /*spl*/, realtype const* /*sspl*/, bool /*include_static*/ ) { throw AmiException( "Requested functionality is not supported as %s is " @@ -477,23 +478,11 @@ void AbstractModel::fdwdp_rowvals(SUNMatrixWrapper& /*dwdp*/) { ); } -void AbstractModel::fdwdp( - realtype* /*dwdp*/, realtype const /*t*/, realtype const* /*x*/, - realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, - realtype const* /*w*/, realtype const* /*tcl*/, realtype const* /*stcl*/, - realtype const* /*spl*/, realtype const* /*sspl*/, int const /*ip*/ -) { - throw AmiException( - "Requested functionality is not supported as %s is " - "not implemented for this model!", - __func__ - ); -} - void AbstractModel::fdwdx( realtype* /*dwdx*/, realtype const /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, - realtype const* /*w*/, realtype const* /*tcl*/, realtype const* /*spl*/ + realtype const* /*w*/, realtype const* /*tcl*/, realtype const* /*spl*/, + bool /*include_static*/ ) { throw AmiException( "Requested functionality is not supported as %s is " @@ -521,7 +510,7 @@ void AbstractModel::fdwdx_rowvals(SUNMatrixWrapper& /*dwdx*/) { void AbstractModel::fdwdw( realtype* /*dwdw*/, realtype /*t*/, realtype const* /*x*/, realtype const* /*p*/, realtype const* /*k*/, realtype const* /*h*/, - realtype const* /*w*/, realtype const* /*tcl*/ + realtype const* /*w*/, realtype const* /*tcl*/, bool /*include_static*/ ) { throw AmiException( "Requested functionality is not supported as %s " diff --git a/src/model.cpp b/src/model.cpp index 3478610bbe..54363d8d1b 100644 --- a/src/model.cpp +++ b/src/model.cpp @@ -323,6 +323,31 @@ void Model::initialize( if (ne) initEvents(x, dx, roots_found); + + // evaluate static expressions once + auto x_pos = computeX_pos(x); + fw(t0(), x_pos, true); + fdwdw(t0(), x_pos, true); + fdwdx(t0(), x_pos, true); + if (computeSensitivities) { + fdwdp(t0(), x_pos, true); + } +} + +void Model::reinitialize( + realtype t, AmiVector& x, AmiVectorArray& sx, bool computeSensitivities +) { + fx0_fixedParameters(x); + + // re-evaluate static expressions once + auto x_pos = computeX_pos(x); + fw(t, x_pos, true); + fdwdw(t, x_pos, true); + fdwdx(t, x_pos, true); + if (computeSensitivities) { + fsx0_fixedParameters(sx, x); + fdwdp(t, x_pos, true); + } } void Model::initializeB( @@ -1048,7 +1073,7 @@ void Model::requireSensitivitiesForAllParameters() { void Model::getExpression( gsl::span w, realtype const t, AmiVector const& x ) { - fw(t, computeX_pos(x)); + fw(t, computeX_pos(x), false); writeSlice(derived_state_.w_, w); } @@ -1469,7 +1494,7 @@ void Model::addStateSensitivityEventUpdate( AmiVector const& xdot, AmiVector const& xdot_old, std::vector const& stau ) { - fw(t, x_old.data()); + fw(t, x_old.data(), false); for (int ip = 0; ip < nplist(); ip++) { @@ -2040,7 +2065,7 @@ void Model::fy(realtype const t, AmiVector const& x) { derived_state_.y_.assign(ny, 0.0); - fw(t, x_pos); + fw(t, x_pos, false); fy(derived_state_.y_.data(), t, x_pos, state_.unscaledParameters.data(), state_.fixedParameters.data(), state_.h.data(), derived_state_.w_.data()); @@ -2059,8 +2084,8 @@ void Model::fdydp(realtype const t, AmiVector const& x) { auto x_pos = computeX_pos(x); derived_state_.dydp_.assign(ny * nplist(), 0.0); - fw(t, x_pos); - fdwdp(t, x_pos); + fw(t, x_pos, false); + fdwdp(t, x_pos, false); /* get dydp slice (ny) for current time and parameter */ for (int ip = 0; ip < nplist(); ip++) @@ -2094,8 +2119,8 @@ void Model::fdydx(realtype const t, AmiVector const& x) { derived_state_.dydx_.assign(ny * nx_solver, 0.0); - fw(t, x_pos); - fdwdx(t, x_pos); + fw(t, x_pos, false); + fdwdx(t, x_pos, false); fdydx( derived_state_.dydx_.data(), t, x_pos, state_.unscaledParameters.data(), state_.fixedParameters.data(), state_.h.data(), @@ -2840,38 +2865,42 @@ void Model::fsspl(realtype const t) { } } -void Model::fw(realtype const t, realtype const* x) { - std::fill(derived_state_.w_.begin(), derived_state_.w_.end(), 0.0); +void Model::fw(realtype const t, realtype const* x, bool include_static) { + if (include_static) { + std::fill(derived_state_.w_.begin(), derived_state_.w_.end(), 0.0); + } fspl(t); fw(derived_state_.w_.data(), t, x, state_.unscaledParameters.data(), state_.fixedParameters.data(), state_.h.data(), state_.total_cl.data(), - state_.spl_.data()); + state_.spl_.data(), include_static); if (always_check_finite_) { checkFinite(derived_state_.w_, ModelQuantity::w); } } -void Model::fdwdp(realtype const t, realtype const* x) { +void Model::fdwdp(realtype const t, realtype const* x, bool include_static) { if (!nw) return; - fw(t, x); + fw(t, x, include_static); derived_state_.dwdp_.zero(); if (pythonGenerated) { - if (!dwdp_hierarchical_.at(0).capacity()) + if (!ndwdp) return; fsspl(t); - fdwdw(t, x); - dwdp_hierarchical_.at(0).zero(); - fdwdp_colptrs(dwdp_hierarchical_.at(0)); - fdwdp_rowvals(dwdp_hierarchical_.at(0)); + fdwdw(t, x, include_static); + if (include_static) { + dwdp_hierarchical_.at(0).zero(); + fdwdp_colptrs(dwdp_hierarchical_.at(0)); + fdwdp_rowvals(dwdp_hierarchical_.at(0)); + } fdwdp( dwdp_hierarchical_.at(0).data(), t, x, state_.unscaledParameters.data(), state_.fixedParameters.data(), state_.h.data(), derived_state_.w_.data(), state_.total_cl.data(), state_.stotal_cl.data(), state_.spl_.data(), - derived_state_.sspl_.data() + derived_state_.sspl_.data(), include_static ); for (int irecursion = 1; irecursion <= w_recursion_depth_; @@ -2901,25 +2930,29 @@ void Model::fdwdp(realtype const t, realtype const* x) { } } -void Model::fdwdx(realtype const t, realtype const* x) { +void Model::fdwdx(realtype const t, realtype const* x, bool include_static) { if (!nw) return; - fw(t, x); + fw(t, x, include_static); derived_state_.dwdx_.zero(); if (pythonGenerated) { if (!dwdx_hierarchical_.at(0).capacity()) return; - fdwdw(t, x); - dwdx_hierarchical_.at(0).zero(); - fdwdx_colptrs(dwdx_hierarchical_.at(0)); - fdwdx_rowvals(dwdx_hierarchical_.at(0)); + + fdwdw(t, x, include_static); + + if (include_static) { + dwdx_hierarchical_.at(0).zero(); + fdwdx_colptrs(dwdx_hierarchical_.at(0)); + fdwdx_rowvals(dwdx_hierarchical_.at(0)); + } fdwdx( dwdx_hierarchical_.at(0).data(), t, x, state_.unscaledParameters.data(), state_.fixedParameters.data(), state_.h.data(), derived_state_.w_.data(), state_.total_cl.data(), - state_.spl_.data() + state_.spl_.data(), include_static ); for (int irecursion = 1; irecursion <= w_recursion_depth_; @@ -2947,16 +2980,20 @@ void Model::fdwdx(realtype const t, realtype const* x) { } } -void Model::fdwdw(realtype const t, realtype const* x) { - if (!nw || !dwdw_.capacity()) +void Model::fdwdw(realtype const t, realtype const* x, bool include_static) { + if (!ndwdw) return; - dwdw_.zero(); - fdwdw_colptrs(dwdw_); - fdwdw_rowvals(dwdw_); + + if (include_static) { + dwdw_.zero(); + fdwdw_colptrs(dwdw_); + fdwdw_rowvals(dwdw_); + } + fdwdw( dwdw_.data(), t, x, state_.unscaledParameters.data(), state_.fixedParameters.data(), state_.h.data(), - derived_state_.w_.data(), state_.total_cl.data() + derived_state_.w_.data(), state_.total_cl.data(), include_static ); if (always_check_finite_) { diff --git a/src/model_dae.cpp b/src/model_dae.cpp index a3a8aaea26..ad397bc82f 100644 --- a/src/model_dae.cpp +++ b/src/model_dae.cpp @@ -31,7 +31,7 @@ void Model_DAE::fJSparse( realtype t, realtype cj, const_N_Vector x, const_N_Vector dx, SUNMatrix J ) { auto x_pos = computeX_pos(x); - fdwdx(t, N_VGetArrayPointerConst(x_pos)); + fdwdx(t, N_VGetArrayPointerConst(x_pos), false); if (pythonGenerated) { auto JSparse = SUNMatrixWrapper(J); // python generated @@ -122,7 +122,7 @@ void Model_DAE::fxdot( realtype t, const_N_Vector x, const_N_Vector dx, N_Vector xdot ) { auto x_pos = computeX_pos(x); - fw(t, N_VGetArrayPointerConst(x)); + fw(t, N_VGetArrayPointerConst(x), false); N_VConst(0.0, xdot); fxdot( N_VGetArrayPointer(xdot), t, N_VGetArrayPointerConst(x_pos), @@ -171,7 +171,7 @@ void Model_DAE::fdxdotdp( ); } else { // matlab generated - fdwdp(t, N_VGetArrayPointerConst(x_pos)); + fdwdp(t, N_VGetArrayPointerConst(x_pos), false); for (int ip = 0; ip < nplist(); ip++) { N_VConst(0.0, derived_state_.dxdotdp.getNVector(ip)); diff --git a/src/model_ode.cpp b/src/model_ode.cpp index 257ef45289..e3e401e1bf 100644 --- a/src/model_ode.cpp +++ b/src/model_ode.cpp @@ -29,7 +29,7 @@ void Model_ODE::fJSparse( void Model_ODE::fJSparse(realtype t, const_N_Vector x, SUNMatrix J) { auto x_pos = computeX_pos(x); - fdwdx(t, N_VGetArrayPointerConst(x_pos)); + fdwdx(t, N_VGetArrayPointerConst(x_pos), false); if (pythonGenerated) { auto JSparse = SUNMatrixWrapper(J); // python generated @@ -143,7 +143,7 @@ void Model_ODE::fdxdotdw(realtype const t, const_N_Vector x) { void Model_ODE::fdxdotdp(realtype const t, const_N_Vector x) { auto x_pos = computeX_pos(x); - fdwdp(t, N_VGetArrayPointerConst(x_pos)); + fdwdp(t, N_VGetArrayPointerConst(x_pos), false); if (pythonGenerated) { // python generated diff --git a/src/solver.cpp b/src/solver.cpp index 1a74e8b44b..2caa9d8bc0 100644 --- a/src/solver.cpp +++ b/src/solver.cpp @@ -251,13 +251,14 @@ void Solver::setupSteadystate( } void Solver::updateAndReinitStatesAndSensitivities(Model* model) const { - model->fx0_fixedParameters(x_); - reInit(t_, x_, dx_); + model->reinitialize( + t_, x_, sx_, getSensitivityOrder() >= SensitivityOrder::first + ); - if (getSensitivityOrder() >= SensitivityOrder::first) { - model->fsx0_fixedParameters(sx_, x_); - if (getSensitivityMethod() == SensitivityMethod::forward) - sensReInit(sx_, sdx_); + reInit(t_, x_, dx_); + if (getSensitivityOrder() >= SensitivityOrder::first + && getSensitivityMethod() == SensitivityMethod::forward) { + sensReInit(sx_, sdx_); } } diff --git a/swig/model.i b/swig/model.i index ee3286e1a4..fdb8ad9c85 100644 --- a/swig/model.i +++ b/swig/model.i @@ -37,6 +37,7 @@ using namespace amici; %ignore initializeB; %ignore initializeStateSensitivities; %ignore initializeStates; +%ignore reinitialize; %ignore ModelState; %ignore getModelState; %ignore setModelState; From 504f4bc25150580b0751feb1fb550b0be747eb8a Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Tue, 27 Feb 2024 17:41:55 +0100 Subject: [PATCH 042/188] Refactor: Move amici.de_model to amici.de_model_components (#2323) ... to make space to move `amici.de_export.DEModel` to `amici.de_model` Related to #2306. --- .github/workflows/test_sbml_semantic_test_suite.yml | 2 +- documentation/python_modules.rst | 2 +- python/sdist/amici/de_export.py | 2 +- python/sdist/amici/{de_model.py => de_model_components.py} | 2 +- python/sdist/amici/sbml_import.py | 2 +- python/tests/test_de_model.py | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) rename python/sdist/amici/{de_model.py => de_model_components.py} (99%) diff --git a/.github/workflows/test_sbml_semantic_test_suite.yml b/.github/workflows/test_sbml_semantic_test_suite.yml index ddc78e1b89..3c0b3bd149 100644 --- a/.github/workflows/test_sbml_semantic_test_suite.yml +++ b/.github/workflows/test_sbml_semantic_test_suite.yml @@ -9,7 +9,7 @@ on: paths: - .github/workflows/test_sbml_semantic_test_suite.yml - python/sdist/amici/de_export.py - - python/sdist/amici/de_model.py + - python/sdist/amici/de_model_components.py - python/sdist/amici/sbml_import.py - python/sdist/amici/import_utils.py - scripts/run-SBMLTestsuite.sh diff --git a/documentation/python_modules.rst b/documentation/python_modules.rst index 237a0a021f..03ae7a937e 100644 --- a/documentation/python_modules.rst +++ b/documentation/python_modules.rst @@ -25,7 +25,7 @@ AMICI Python API amici.petab_simulate amici.import_utils amici.de_export - amici.de_model + amici.de_model_components amici.plotting amici.pandas amici.logging diff --git a/python/sdist/amici/de_export.py b/python/sdist/amici/de_export.py index b853dd6ad0..126132911d 100644 --- a/python/sdist/amici/de_export.py +++ b/python/sdist/amici/de_export.py @@ -45,7 +45,7 @@ get_switch_statement, csc_matrix, ) -from .de_model import * +from .de_model_components import * from .import_utils import ( ObservableTransformation, SBMLException, diff --git a/python/sdist/amici/de_model.py b/python/sdist/amici/de_model_components.py similarity index 99% rename from python/sdist/amici/de_model.py rename to python/sdist/amici/de_model_components.py index cb0c066e4d..eeeabb35db 100644 --- a/python/sdist/amici/de_model.py +++ b/python/sdist/amici/de_model_components.py @@ -199,7 +199,7 @@ def __init__(self, identifier: str, value: sp.Expr): Create a new AlgebraicEquation instance. :param value: - formula of the algebraic equation, solution is given by + Formula of the algebraic equation, the solution is given by ``formula == 0`` """ super().__init__(sp.Symbol(identifier), identifier, value) diff --git a/python/sdist/amici/sbml_import.py b/python/sdist/amici/sbml_import.py index eb0b739186..5a0e1669ad 100644 --- a/python/sdist/amici/sbml_import.py +++ b/python/sdist/amici/sbml_import.py @@ -32,7 +32,7 @@ DEExporter, DEModel, ) -from .de_model import symbol_to_type, Expression +from .de_model_components import symbol_to_type, Expression from .sympy_utils import smart_is_zero_matrix, smart_multiply from .import_utils import ( RESERVED_SYMBOLS, diff --git a/python/tests/test_de_model.py b/python/tests/test_de_model.py index 8bd750443a..07c8328ed1 100644 --- a/python/tests/test_de_model.py +++ b/python/tests/test_de_model.py @@ -1,5 +1,5 @@ import sympy as sp -from amici.de_model import Event +from amici.de_model_components import Event from amici.import_utils import amici_time_symbol from amici.testing import skip_on_valgrind From c4bcb9dd255ac947d0762bc79b34ca67338d0d87 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Tue, 27 Feb 2024 17:42:33 +0100 Subject: [PATCH 043/188] Refactor de_export.py, extract C++ function info to cxx_functions.py (#2321) Move everything related to information on C++ model functions to a separate module. Related to #2306. No changes in functionality. --- python/sdist/amici/_codegen/cxx_functions.py | 385 ++++++++++++++++++ python/sdist/amici/de_export.py | 388 +------------------ 2 files changed, 399 insertions(+), 374 deletions(-) create mode 100644 python/sdist/amici/_codegen/cxx_functions.py diff --git a/python/sdist/amici/_codegen/cxx_functions.py b/python/sdist/amici/_codegen/cxx_functions.py new file mode 100644 index 0000000000..25a8af3c2c --- /dev/null +++ b/python/sdist/amici/_codegen/cxx_functions.py @@ -0,0 +1,385 @@ +"""Info about C++ functions in the generated model code.""" +from __future__ import annotations + +from dataclasses import dataclass + + +@dataclass +class _FunctionInfo: + """Information on a model-specific generated C++ function + + :ivar ode_arguments: argument list of the ODE function. + input variables should be ``const``. + :ivar dae_arguments: argument list of the DAE function, if different from + ODE function. input variables should be ``const``. + :ivar return_type: the return type of the function + :ivar assume_pow_positivity: + identifies the functions on which ``assume_pow_positivity`` will have + an effect when specified during model generation. generally these are + functions that are used for solving the ODE, where negative values may + negatively affect convergence of the integration algorithm + :ivar sparse: + specifies whether the result of this function will be stored in sparse + format. sparse format means that the function will only return an + array of nonzero values and not a full matrix. + :ivar generate_body: + indicates whether a model-specific implementation is to be generated + :ivar body: + the actual function body. will be filled later + """ + + ode_arguments: str = "" + dae_arguments: str = "" + return_type: str = "void" + assume_pow_positivity: bool = False + sparse: bool = False + generate_body: bool = True + body: str = "" + + def arguments(self, ode: bool = True) -> str: + """Get the arguments for the ODE or DAE function""" + if ode or not self.dae_arguments: + return self.ode_arguments + return self.dae_arguments + + +# Information on a model-specific generated C++ function +# prototype for generated C++ functions, keys are the names of functions +functions = { + "Jy": _FunctionInfo( + "realtype *Jy, const int iy, const realtype *p, " + "const realtype *k, const realtype *y, const realtype *sigmay, " + "const realtype *my" + ), + "dJydsigma": _FunctionInfo( + "realtype *dJydsigma, const int iy, const realtype *p, " + "const realtype *k, const realtype *y, const realtype *sigmay, " + "const realtype *my" + ), + "dJydy": _FunctionInfo( + "realtype *dJydy, const int iy, const realtype *p, " + "const realtype *k, const realtype *y, " + "const realtype *sigmay, const realtype *my", + sparse=True, + ), + "Jz": _FunctionInfo( + "realtype *Jz, const int iz, const realtype *p, const realtype *k, " + "const realtype *z, const realtype *sigmaz, const realtype *mz" + ), + "dJzdsigma": _FunctionInfo( + "realtype *dJzdsigma, const int iz, const realtype *p, " + "const realtype *k, const realtype *z, const realtype *sigmaz, " + "const realtype *mz" + ), + "dJzdz": _FunctionInfo( + "realtype *dJzdz, const int iz, const realtype *p, " + "const realtype *k, const realtype *z, const realtype *sigmaz, " + "const double *mz", + ), + "Jrz": _FunctionInfo( + "realtype *Jrz, const int iz, const realtype *p, " + "const realtype *k, const realtype *rz, const realtype *sigmaz" + ), + "dJrzdsigma": _FunctionInfo( + "realtype *dJrzdsigma, const int iz, const realtype *p, " + "const realtype *k, const realtype *rz, const realtype *sigmaz" + ), + "dJrzdz": _FunctionInfo( + "realtype *dJrzdz, const int iz, const realtype *p, " + "const realtype *k, const realtype *rz, const realtype *sigmaz", + ), + "root": _FunctionInfo( + "realtype *root, const realtype t, const realtype *x, " + "const realtype *p, const realtype *k, const realtype *h, " + "const realtype *tcl" + ), + "dwdp": _FunctionInfo( + "realtype *dwdp, const realtype t, const realtype *x, " + "const realtype *p, const realtype *k, const realtype *h, " + "const realtype *w, const realtype *tcl, const realtype *dtcldp, " + "const realtype *spl, const realtype *sspl, bool include_static", + assume_pow_positivity=True, + sparse=True, + ), + "dwdx": _FunctionInfo( + "realtype *dwdx, const realtype t, const realtype *x, " + "const realtype *p, const realtype *k, const realtype *h, " + "const realtype *w, const realtype *tcl, const realtype *spl, " + "bool include_static", + assume_pow_positivity=True, + sparse=True, + ), + "create_splines": _FunctionInfo( + "const realtype *p, const realtype *k", + return_type="std::vector", + ), + "spl": _FunctionInfo(generate_body=False), + "sspl": _FunctionInfo(generate_body=False), + "spline_values": _FunctionInfo( + "const realtype *p, const realtype *k", generate_body=False + ), + "spline_slopes": _FunctionInfo( + "const realtype *p, const realtype *k", generate_body=False + ), + "dspline_valuesdp": _FunctionInfo( + "realtype *dspline_valuesdp, const realtype *p, const realtype *k, " + "const int ip" + ), + "dspline_slopesdp": _FunctionInfo( + "realtype *dspline_slopesdp, const realtype *p, const realtype *k, " + "const int ip" + ), + "dwdw": _FunctionInfo( + "realtype *dwdw, const realtype t, const realtype *x, " + "const realtype *p, const realtype *k, const realtype *h, " + "const realtype *w, const realtype *tcl, bool include_static", + assume_pow_positivity=True, + sparse=True, + ), + "dxdotdw": _FunctionInfo( + "realtype *dxdotdw, const realtype t, const realtype *x, " + "const realtype *p, const realtype *k, const realtype *h, " + "const realtype *w", + "realtype *dxdotdw, const realtype t, const realtype *x, " + "const realtype *p, const realtype *k, const realtype *h, " + "const realtype *dx, const realtype *w", + assume_pow_positivity=True, + sparse=True, + ), + "dxdotdx_explicit": _FunctionInfo( + "realtype *dxdotdx_explicit, const realtype t, " + "const realtype *x, const realtype *p, const realtype *k, " + "const realtype *h, const realtype *w", + "realtype *dxdotdx_explicit, const realtype t, " + "const realtype *x, const realtype *p, const realtype *k, " + "const realtype *h, const realtype *dx, const realtype *w", + assume_pow_positivity=True, + sparse=True, + ), + "dxdotdp_explicit": _FunctionInfo( + "realtype *dxdotdp_explicit, const realtype t, " + "const realtype *x, const realtype *p, const realtype *k, " + "const realtype *h, const realtype *w", + "realtype *dxdotdp_explicit, const realtype t, " + "const realtype *x, const realtype *p, const realtype *k, " + "const realtype *h, const realtype *dx, const realtype *w", + assume_pow_positivity=True, + sparse=True, + ), + "dydx": _FunctionInfo( + "realtype *dydx, const realtype t, const realtype *x, " + "const realtype *p, const realtype *k, const realtype *h, " + "const realtype *w, const realtype *dwdx", + ), + "dydp": _FunctionInfo( + "realtype *dydp, const realtype t, const realtype *x, " + "const realtype *p, const realtype *k, const realtype *h, " + "const int ip, const realtype *w, const realtype *tcl, " + "const realtype *dtcldp, const realtype *spl, const realtype *sspl" + ), + "dzdx": _FunctionInfo( + "realtype *dzdx, const int ie, const realtype t, " + "const realtype *x, const realtype *p, const realtype *k, " + "const realtype *h", + ), + "dzdp": _FunctionInfo( + "realtype *dzdp, const int ie, const realtype t, " + "const realtype *x, const realtype *p, const realtype *k, " + "const realtype *h, const int ip", + ), + "drzdx": _FunctionInfo( + "realtype *drzdx, const int ie, const realtype t, " + "const realtype *x, const realtype *p, const realtype *k, " + "const realtype *h", + ), + "drzdp": _FunctionInfo( + "realtype *drzdp, const int ie, const realtype t, " + "const realtype *x, const realtype *p, const realtype *k, " + "const realtype *h, const int ip", + ), + "dsigmaydy": _FunctionInfo( + "realtype *dsigmaydy, const realtype t, const realtype *p, " + "const realtype *k, const realtype *y" + ), + "dsigmaydp": _FunctionInfo( + "realtype *dsigmaydp, const realtype t, const realtype *p, " + "const realtype *k, const realtype *y, const int ip", + ), + "sigmay": _FunctionInfo( + "realtype *sigmay, const realtype t, const realtype *p, " + "const realtype *k, const realtype *y", + ), + "dsigmazdp": _FunctionInfo( + "realtype *dsigmazdp, const realtype t, const realtype *p," + " const realtype *k, const int ip", + ), + "sigmaz": _FunctionInfo( + "realtype *sigmaz, const realtype t, const realtype *p, " + "const realtype *k", + ), + "sroot": _FunctionInfo( + "realtype *stau, const realtype t, const realtype *x, " + "const realtype *p, const realtype *k, const realtype *h, " + "const realtype *sx, const int ip, const int ie, " + "const realtype *tcl", + generate_body=False, + ), + "drootdt": _FunctionInfo(generate_body=False), + "drootdt_total": _FunctionInfo(generate_body=False), + "drootdp": _FunctionInfo(generate_body=False), + "drootdx": _FunctionInfo(generate_body=False), + "stau": _FunctionInfo( + "realtype *stau, const realtype t, const realtype *x, " + "const realtype *p, const realtype *k, const realtype *h, " + "const realtype *tcl, const realtype *sx, const int ip, " + "const int ie" + ), + "deltax": _FunctionInfo( + "double *deltax, const realtype t, const realtype *x, " + "const realtype *p, const realtype *k, const realtype *h, " + "const int ie, const realtype *xdot, const realtype *xdot_old" + ), + "ddeltaxdx": _FunctionInfo(generate_body=False), + "ddeltaxdt": _FunctionInfo(generate_body=False), + "ddeltaxdp": _FunctionInfo(generate_body=False), + "deltasx": _FunctionInfo( + "realtype *deltasx, const realtype t, const realtype *x, " + "const realtype *p, const realtype *k, const realtype *h, " + "const realtype *w, const int ip, const int ie, " + "const realtype *xdot, const realtype *xdot_old, " + "const realtype *sx, const realtype *stau, const realtype *tcl" + ), + "w": _FunctionInfo( + "realtype *w, const realtype t, const realtype *x, " + "const realtype *p, const realtype *k, " + "const realtype *h, const realtype *tcl, const realtype *spl, " + "bool include_static", + assume_pow_positivity=True, + ), + "x0": _FunctionInfo( + "realtype *x0, const realtype t, const realtype *p, " + "const realtype *k" + ), + "x0_fixedParameters": _FunctionInfo( + "realtype *x0_fixedParameters, const realtype t, " + "const realtype *p, const realtype *k, " + "gsl::span reinitialization_state_idxs", + ), + "sx0": _FunctionInfo( + "realtype *sx0, const realtype t, const realtype *x, " + "const realtype *p, const realtype *k, const int ip", + ), + "sx0_fixedParameters": _FunctionInfo( + "realtype *sx0_fixedParameters, const realtype t, " + "const realtype *x0, const realtype *p, const realtype *k, " + "const int ip, gsl::span reinitialization_state_idxs", + ), + "xdot": _FunctionInfo( + "realtype *xdot, const realtype t, const realtype *x, " + "const realtype *p, const realtype *k, const realtype *h, " + "const realtype *w", + "realtype *xdot, const realtype t, const realtype *x, " + "const realtype *p, const realtype *k, const realtype *h, " + "const realtype *dx, const realtype *w", + assume_pow_positivity=True, + ), + "xdot_old": _FunctionInfo(generate_body=False), + "y": _FunctionInfo( + "realtype *y, const realtype t, const realtype *x, " + "const realtype *p, const realtype *k, " + "const realtype *h, const realtype *w", + ), + "x_rdata": _FunctionInfo( + "realtype *x_rdata, const realtype *x, const realtype *tcl, " + "const realtype *p, const realtype *k" + ), + "total_cl": _FunctionInfo( + "realtype *total_cl, const realtype *x_rdata, " + "const realtype *p, const realtype *k" + ), + "dtotal_cldp": _FunctionInfo( + "realtype *dtotal_cldp, const realtype *x_rdata, " + "const realtype *p, const realtype *k, const int ip" + ), + "dtotal_cldx_rdata": _FunctionInfo( + "realtype *dtotal_cldx_rdata, const realtype *x_rdata, " + "const realtype *p, const realtype *k, const realtype *tcl", + sparse=True, + ), + "x_solver": _FunctionInfo("realtype *x_solver, const realtype *x_rdata"), + "dx_rdatadx_solver": _FunctionInfo( + "realtype *dx_rdatadx_solver, const realtype *x, " + "const realtype *tcl, const realtype *p, const realtype *k", + sparse=True, + ), + "dx_rdatadp": _FunctionInfo( + "realtype *dx_rdatadp, const realtype *x, " + "const realtype *tcl, const realtype *p, const realtype *k, " + "const int ip" + ), + "dx_rdatadtcl": _FunctionInfo( + "realtype *dx_rdatadtcl, const realtype *x, " + "const realtype *tcl, const realtype *p, const realtype *k", + sparse=True, + ), + "z": _FunctionInfo( + "realtype *z, const int ie, const realtype t, const realtype *x, " + "const realtype *p, const realtype *k, const realtype *h" + ), + "rz": _FunctionInfo( + "realtype *rz, const int ie, const realtype t, const realtype *x, " + "const realtype *p, const realtype *k, const realtype *h" + ), +} + +#: list of sparse functions +sparse_functions = [ + func_name for func_name, func_info in functions.items() if func_info.sparse +] + +#: list of nobody functions +nobody_functions = [ + func_name + for func_name, func_info in functions.items() + if not func_info.generate_body +] + +#: list of sensitivity functions +sensi_functions = [ + func_name + for func_name, func_info in functions.items() + if "const int ip" in func_info.arguments() +] + +#: list of sparse sensitivity functions +sparse_sensi_functions = [ + func_name + for func_name, func_info in functions.items() + if "const int ip" not in func_info.arguments() + and func_name.endswith("dp") + or func_name.endswith("dp_explicit") +] + +#: list of event functions +event_functions = [ + func_name + for func_name, func_info in functions.items() + if "const int ie" in func_info.arguments() + and "const int ip" not in func_info.arguments() +] + +#: list of event sensitivity functions +event_sensi_functions = [ + func_name + for func_name, func_info in functions.items() + if "const int ie" in func_info.arguments() + and "const int ip" in func_info.arguments() +] + +#: list of multiobs functions +multiobs_functions = [ + func_name + for func_name, func_info in functions.items() + if "const int iy" in func_info.arguments() + or "const int iz" in func_info.arguments() +] diff --git a/python/sdist/amici/de_export.py b/python/sdist/amici/de_export.py index 126132911d..014096aaf8 100644 --- a/python/sdist/amici/de_export.py +++ b/python/sdist/amici/de_export.py @@ -17,7 +17,6 @@ import os import re import shutil -from dataclasses import dataclass from itertools import chain from pathlib import Path from typing import ( @@ -39,6 +38,17 @@ amiciSwigPath, splines, ) +from ._codegen.cxx_functions import ( + _FunctionInfo, + functions, + sparse_functions, + nobody_functions, + sensi_functions, + sparse_sensi_functions, + event_functions, + event_sensi_functions, + multiobs_functions, +) from ._codegen.template import apply_template from .cxxcodeprinter import ( AmiciCxxCodePrinter, @@ -86,380 +96,10 @@ DERIVATIVE_PATTERN = re.compile(r"^d(x_rdata|xdot|\w+?)d(\w+?)(?:_explicit)?$") -@dataclass -class _FunctionInfo: - """Information on a model-specific generated C++ function - - :ivar ode_arguments: argument list of the ODE function. input variables should be - ``const``. - :ivar dae_arguments: argument list of the DAE function, if different from ODE - function. input variables should be ``const``. - :ivar return_type: the return type of the function - :ivar assume_pow_positivity: - identifies the functions on which ``assume_pow_positivity`` will have - an effect when specified during model generation. generally these are - functions that are used for solving the ODE, where negative values may - negatively affect convergence of the integration algorithm - :ivar sparse: - specifies whether the result of this function will be stored in sparse - format. sparse format means that the function will only return an - array of nonzero values and not a full matrix. - :ivar generate_body: - indicates whether a model-specific implementation is to be generated - :ivar body: - the actual function body. will be filled later - """ - - ode_arguments: str = "" - dae_arguments: str = "" - return_type: str = "void" - assume_pow_positivity: bool = False - sparse: bool = False - generate_body: bool = True - body: str = "" - - def arguments(self, ode: bool = True) -> str: - """Get the arguments for the ODE or DAE function""" - if ode or not self.dae_arguments: - return self.ode_arguments - return self.dae_arguments - - -# Information on a model-specific generated C++ function -# prototype for generated C++ functions, keys are the names of functions -functions = { - "Jy": _FunctionInfo( - "realtype *Jy, const int iy, const realtype *p, " - "const realtype *k, const realtype *y, const realtype *sigmay, " - "const realtype *my" - ), - "dJydsigma": _FunctionInfo( - "realtype *dJydsigma, const int iy, const realtype *p, " - "const realtype *k, const realtype *y, const realtype *sigmay, " - "const realtype *my" - ), - "dJydy": _FunctionInfo( - "realtype *dJydy, const int iy, const realtype *p, " - "const realtype *k, const realtype *y, " - "const realtype *sigmay, const realtype *my", - sparse=True, - ), - "Jz": _FunctionInfo( - "realtype *Jz, const int iz, const realtype *p, const realtype *k, " - "const realtype *z, const realtype *sigmaz, const realtype *mz" - ), - "dJzdsigma": _FunctionInfo( - "realtype *dJzdsigma, const int iz, const realtype *p, " - "const realtype *k, const realtype *z, const realtype *sigmaz, " - "const realtype *mz" - ), - "dJzdz": _FunctionInfo( - "realtype *dJzdz, const int iz, const realtype *p, " - "const realtype *k, const realtype *z, const realtype *sigmaz, " - "const double *mz", - ), - "Jrz": _FunctionInfo( - "realtype *Jrz, const int iz, const realtype *p, " - "const realtype *k, const realtype *rz, const realtype *sigmaz" - ), - "dJrzdsigma": _FunctionInfo( - "realtype *dJrzdsigma, const int iz, const realtype *p, " - "const realtype *k, const realtype *rz, const realtype *sigmaz" - ), - "dJrzdz": _FunctionInfo( - "realtype *dJrzdz, const int iz, const realtype *p, " - "const realtype *k, const realtype *rz, const realtype *sigmaz", - ), - "root": _FunctionInfo( - "realtype *root, const realtype t, const realtype *x, " - "const realtype *p, const realtype *k, const realtype *h, " - "const realtype *tcl" - ), - "dwdp": _FunctionInfo( - "realtype *dwdp, const realtype t, const realtype *x, " - "const realtype *p, const realtype *k, const realtype *h, " - "const realtype *w, const realtype *tcl, const realtype *dtcldp, " - "const realtype *spl, const realtype *sspl, bool include_static", - assume_pow_positivity=True, - sparse=True, - ), - "dwdx": _FunctionInfo( - "realtype *dwdx, const realtype t, const realtype *x, " - "const realtype *p, const realtype *k, const realtype *h, " - "const realtype *w, const realtype *tcl, const realtype *spl, " - "bool include_static", - assume_pow_positivity=True, - sparse=True, - ), - "create_splines": _FunctionInfo( - "const realtype *p, const realtype *k", - return_type="std::vector", - ), - "spl": _FunctionInfo(generate_body=False), - "sspl": _FunctionInfo(generate_body=False), - "spline_values": _FunctionInfo( - "const realtype *p, const realtype *k", generate_body=False - ), - "spline_slopes": _FunctionInfo( - "const realtype *p, const realtype *k", generate_body=False - ), - "dspline_valuesdp": _FunctionInfo( - "realtype *dspline_valuesdp, const realtype *p, const realtype *k, const int ip" - ), - "dspline_slopesdp": _FunctionInfo( - "realtype *dspline_slopesdp, const realtype *p, const realtype *k, const int ip" - ), - "dwdw": _FunctionInfo( - "realtype *dwdw, const realtype t, const realtype *x, " - "const realtype *p, const realtype *k, const realtype *h, " - "const realtype *w, const realtype *tcl, bool include_static", - assume_pow_positivity=True, - sparse=True, - ), - "dxdotdw": _FunctionInfo( - "realtype *dxdotdw, const realtype t, const realtype *x, " - "const realtype *p, const realtype *k, const realtype *h, " - "const realtype *w", - "realtype *dxdotdw, const realtype t, const realtype *x, " - "const realtype *p, const realtype *k, const realtype *h, " - "const realtype *dx, const realtype *w", - assume_pow_positivity=True, - sparse=True, - ), - "dxdotdx_explicit": _FunctionInfo( - "realtype *dxdotdx_explicit, const realtype t, " - "const realtype *x, const realtype *p, const realtype *k, " - "const realtype *h, const realtype *w", - "realtype *dxdotdx_explicit, const realtype t, " - "const realtype *x, const realtype *p, const realtype *k, " - "const realtype *h, const realtype *dx, const realtype *w", - assume_pow_positivity=True, - sparse=True, - ), - "dxdotdp_explicit": _FunctionInfo( - "realtype *dxdotdp_explicit, const realtype t, " - "const realtype *x, const realtype *p, const realtype *k, " - "const realtype *h, const realtype *w", - "realtype *dxdotdp_explicit, const realtype t, " - "const realtype *x, const realtype *p, const realtype *k, " - "const realtype *h, const realtype *dx, const realtype *w", - assume_pow_positivity=True, - sparse=True, - ), - "dydx": _FunctionInfo( - "realtype *dydx, const realtype t, const realtype *x, " - "const realtype *p, const realtype *k, const realtype *h, " - "const realtype *w, const realtype *dwdx", - ), - "dydp": _FunctionInfo( - "realtype *dydp, const realtype t, const realtype *x, " - "const realtype *p, const realtype *k, const realtype *h, " - "const int ip, const realtype *w, const realtype *tcl, " - "const realtype *dtcldp, const realtype *spl, const realtype *sspl" - ), - "dzdx": _FunctionInfo( - "realtype *dzdx, const int ie, const realtype t, " - "const realtype *x, const realtype *p, const realtype *k, " - "const realtype *h", - ), - "dzdp": _FunctionInfo( - "realtype *dzdp, const int ie, const realtype t, " - "const realtype *x, const realtype *p, const realtype *k, " - "const realtype *h, const int ip", - ), - "drzdx": _FunctionInfo( - "realtype *drzdx, const int ie, const realtype t, " - "const realtype *x, const realtype *p, const realtype *k, " - "const realtype *h", - ), - "drzdp": _FunctionInfo( - "realtype *drzdp, const int ie, const realtype t, " - "const realtype *x, const realtype *p, const realtype *k, " - "const realtype *h, const int ip", - ), - "dsigmaydy": _FunctionInfo( - "realtype *dsigmaydy, const realtype t, const realtype *p, " - "const realtype *k, const realtype *y" - ), - "dsigmaydp": _FunctionInfo( - "realtype *dsigmaydp, const realtype t, const realtype *p, " - "const realtype *k, const realtype *y, const int ip", - ), - "sigmay": _FunctionInfo( - "realtype *sigmay, const realtype t, const realtype *p, " - "const realtype *k, const realtype *y", - ), - "dsigmazdp": _FunctionInfo( - "realtype *dsigmazdp, const realtype t, const realtype *p," - " const realtype *k, const int ip", - ), - "sigmaz": _FunctionInfo( - "realtype *sigmaz, const realtype t, const realtype *p, " - "const realtype *k", - ), - "sroot": _FunctionInfo( - "realtype *stau, const realtype t, const realtype *x, " - "const realtype *p, const realtype *k, const realtype *h, " - "const realtype *sx, const int ip, const int ie, " - "const realtype *tcl", - generate_body=False, - ), - "drootdt": _FunctionInfo(generate_body=False), - "drootdt_total": _FunctionInfo(generate_body=False), - "drootdp": _FunctionInfo(generate_body=False), - "drootdx": _FunctionInfo(generate_body=False), - "stau": _FunctionInfo( - "realtype *stau, const realtype t, const realtype *x, " - "const realtype *p, const realtype *k, const realtype *h, " - "const realtype *tcl, const realtype *sx, const int ip, " - "const int ie" - ), - "deltax": _FunctionInfo( - "double *deltax, const realtype t, const realtype *x, " - "const realtype *p, const realtype *k, const realtype *h, " - "const int ie, const realtype *xdot, const realtype *xdot_old" - ), - "ddeltaxdx": _FunctionInfo(generate_body=False), - "ddeltaxdt": _FunctionInfo(generate_body=False), - "ddeltaxdp": _FunctionInfo(generate_body=False), - "deltasx": _FunctionInfo( - "realtype *deltasx, const realtype t, const realtype *x, " - "const realtype *p, const realtype *k, const realtype *h, " - "const realtype *w, const int ip, const int ie, " - "const realtype *xdot, const realtype *xdot_old, " - "const realtype *sx, const realtype *stau, const realtype *tcl" - ), - "w": _FunctionInfo( - "realtype *w, const realtype t, const realtype *x, " - "const realtype *p, const realtype *k, " - "const realtype *h, const realtype *tcl, const realtype *spl, " - "bool include_static", - assume_pow_positivity=True, - ), - "x0": _FunctionInfo( - "realtype *x0, const realtype t, const realtype *p, " - "const realtype *k" - ), - "x0_fixedParameters": _FunctionInfo( - "realtype *x0_fixedParameters, const realtype t, " - "const realtype *p, const realtype *k, " - "gsl::span reinitialization_state_idxs", - ), - "sx0": _FunctionInfo( - "realtype *sx0, const realtype t, const realtype *x, " - "const realtype *p, const realtype *k, const int ip", - ), - "sx0_fixedParameters": _FunctionInfo( - "realtype *sx0_fixedParameters, const realtype t, " - "const realtype *x0, const realtype *p, const realtype *k, " - "const int ip, gsl::span reinitialization_state_idxs", - ), - "xdot": _FunctionInfo( - "realtype *xdot, const realtype t, const realtype *x, " - "const realtype *p, const realtype *k, const realtype *h, " - "const realtype *w", - "realtype *xdot, const realtype t, const realtype *x, " - "const realtype *p, const realtype *k, const realtype *h, " - "const realtype *dx, const realtype *w", - assume_pow_positivity=True, - ), - "xdot_old": _FunctionInfo(generate_body=False), - "y": _FunctionInfo( - "realtype *y, const realtype t, const realtype *x, " - "const realtype *p, const realtype *k, " - "const realtype *h, const realtype *w", - ), - "x_rdata": _FunctionInfo( - "realtype *x_rdata, const realtype *x, const realtype *tcl, " - "const realtype *p, const realtype *k" - ), - "total_cl": _FunctionInfo( - "realtype *total_cl, const realtype *x_rdata, " - "const realtype *p, const realtype *k" - ), - "dtotal_cldp": _FunctionInfo( - "realtype *dtotal_cldp, const realtype *x_rdata, " - "const realtype *p, const realtype *k, const int ip" - ), - "dtotal_cldx_rdata": _FunctionInfo( - "realtype *dtotal_cldx_rdata, const realtype *x_rdata, " - "const realtype *p, const realtype *k, const realtype *tcl", - sparse=True, - ), - "x_solver": _FunctionInfo("realtype *x_solver, const realtype *x_rdata"), - "dx_rdatadx_solver": _FunctionInfo( - "realtype *dx_rdatadx_solver, const realtype *x, " - "const realtype *tcl, const realtype *p, const realtype *k", - sparse=True, - ), - "dx_rdatadp": _FunctionInfo( - "realtype *dx_rdatadp, const realtype *x, " - "const realtype *tcl, const realtype *p, const realtype *k, " - "const int ip" - ), - "dx_rdatadtcl": _FunctionInfo( - "realtype *dx_rdatadtcl, const realtype *x, " - "const realtype *tcl, const realtype *p, const realtype *k", - sparse=True, - ), - "z": _FunctionInfo( - "realtype *z, const int ie, const realtype t, const realtype *x, " - "const realtype *p, const realtype *k, const realtype *h" - ), - "rz": _FunctionInfo( - "realtype *rz, const int ie, const realtype t, const realtype *x, " - "const realtype *p, const realtype *k, const realtype *h" - ), -} - -# list of sparse functions -sparse_functions = [ - func_name for func_name, func_info in functions.items() if func_info.sparse -] -# list of nobody functions -nobody_functions = [ - func_name - for func_name, func_info in functions.items() - if not func_info.generate_body -] -# list of sensitivity functions -sensi_functions = [ - func_name - for func_name, func_info in functions.items() - if "const int ip" in func_info.arguments() -] -# list of sensitivity functions -sparse_sensi_functions = [ - func_name - for func_name, func_info in functions.items() - if "const int ip" not in func_info.arguments() - and func_name.endswith("dp") - or func_name.endswith("dp_explicit") -] -# list of event functions -event_functions = [ - func_name - for func_name, func_info in functions.items() - if "const int ie" in func_info.arguments() - and "const int ip" not in func_info.arguments() -] -event_sensi_functions = [ - func_name - for func_name, func_info in functions.items() - if "const int ie" in func_info.arguments() - and "const int ip" in func_info.arguments() -] -# list of multiobs functions -multiobs_functions = [ - func_name - for func_name, func_info in functions.items() - if "const int iy" in func_info.arguments() - or "const int iz" in func_info.arguments() -] -# list of equations that have ids which may not be unique +#: list of equations that have ids which may not be unique non_unique_id_symbols = ["x_rdata", "y"] -# custom c++ function replacements +#: custom c++ function replacements CUSTOM_FUNCTIONS = [ { "sympy": "polygamma", @@ -471,7 +111,7 @@ def arguments(self, ode: bool = True) -> str: {"sympy": "DiracDelta", "c++": "amici::dirac"}, ] -# python log manager +#: python log manager logger = get_logger(__name__, logging.ERROR) From b4ab8a24176b4286cf3fede8d359a256e6e8904f Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Tue, 27 Feb 2024 17:43:04 +0100 Subject: [PATCH 044/188] Deterministic ordering of source files in generated CMakeLists.txt (#2322) Simpler model diffs. --- python/sdist/amici/de_export.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/python/sdist/amici/de_export.py b/python/sdist/amici/de_export.py index 014096aaf8..d956056a2e 100644 --- a/python/sdist/amici/de_export.py +++ b/python/sdist/amici/de_export.py @@ -3488,12 +3488,14 @@ def vector_initializer(v): def _write_c_make_file(self): """Write CMake ``CMakeLists.txt`` file for this model.""" sources = "\n".join( - f + " " - for f in os.listdir(self.model_path) - if f.endswith( - (".cpp", ".h"), + sorted( + f + " " + for f in os.listdir(self.model_path) + if f.endswith( + (".cpp", ".h"), + ) + and f != "main.cpp" ) - and f != "main.cpp" ) template_data = { From 85e35ae5082da3fc96a4e088d18adefae3753135 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Wed, 28 Feb 2024 10:25:19 +0100 Subject: [PATCH 045/188] Refactor de_export.py, extract _codegen.model_class (#2319) Move functionality for generating the ``amici::Model`` subclass code to a separate file. Related to #2306 --- python/sdist/amici/_codegen/model_class.py | 191 ++++++++++++++++++++ python/sdist/amici/de_export.py | 195 ++------------------- 2 files changed, 201 insertions(+), 185 deletions(-) create mode 100644 python/sdist/amici/_codegen/model_class.py diff --git a/python/sdist/amici/_codegen/model_class.py b/python/sdist/amici/_codegen/model_class.py new file mode 100644 index 0000000000..e6366c1dfd --- /dev/null +++ b/python/sdist/amici/_codegen/model_class.py @@ -0,0 +1,191 @@ +"""Function for generating the ``amici::Model`` subclass for an amici model.""" +from __future__ import annotations + +from .cxx_functions import functions, multiobs_functions + +from ..de_model_components import Event + + +def get_function_extern_declaration(fun: str, name: str, ode: bool) -> str: + """ + Constructs the extern function declaration for a given function + + :param fun: + function name + :param name: + model name + :param ode: + whether to generate declaration for DAE or ODE + + :return: + C++ function definition string + """ + f = functions[fun] + return f"extern {f.return_type} {fun}_{name}({f.arguments(ode)});" + + +def get_sunindex_extern_declaration( + fun: str, name: str, indextype: str +) -> str: + """ + Constructs the function declaration for an index function of a given + function + + :param fun: + function name + + :param name: + model name + + :param indextype: + index function {'colptrs', 'rowvals'} + + :return: + C++ function declaration string + """ + index_arg = ", int index" if fun in multiobs_functions else "" + return ( + f"extern void {fun}_{indextype}_{name}" + f"(SUNMatrixWrapper &{indextype}{index_arg});" + ) + + +def get_model_override_implementation( + fun: str, name: str, ode: bool, nobody: bool = False +) -> str: + """ + Constructs ``amici::Model::*`` override implementation for a given function + + :param fun: + function name + + :param name: + model name + + :param nobody: + whether the function has a nontrivial implementation + + :return: + C++ function implementation string + """ + func_info = functions[fun] + body = ( + "" + if nobody + else "\n{ind8}{maybe_return}{fun}_{name}({eval_signature});\n{ind4}".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(ode)), + ) + ) + return "{return_type} f{fun}({signature}) override {{{body}}}\n".format( + return_type=func_info.return_type, + fun=fun, + signature=func_info.arguments(ode), + body=body, + ) + + +def get_sunindex_override_implementation( + fun: str, name: str, indextype: str, nobody: bool = False +) -> str: + """ + Constructs the ``amici::Model`` function implementation for an index + function of a given function + + :param fun: + function name + + :param name: + model name + + :param indextype: + index function {'colptrs', 'rowvals'} + + :param nobody: + whether the corresponding function has a nontrivial implementation + + :return: + C++ function implementation string + """ + index_arg = ", int index" if fun in multiobs_functions else "" + index_arg_eval = ", index" if fun in multiobs_functions else "" + + impl = "void f{fun}_{indextype}({signature}) override {{" + + if nobody: + impl += "}}\n" + else: + impl += ( + "\n{ind8}{fun}_{indextype}_{name}({eval_signature});\n{ind4}}}\n" + ) + + return impl.format( + ind4=" " * 4, + ind8=" " * 8, + fun=fun, + indextype=indextype, + name=name, + signature=f"SUNMatrixWrapper &{indextype}{index_arg}", + eval_signature=f"{indextype}{index_arg_eval}", + ) + + +def remove_argument_types(signature: str) -> str: + """ + Strips argument types from a function signature + + :param signature: + function signature + + :return: + string that can be used to construct function calls with the same + variable names and ordering as in the function signature + """ + # remove * prefix for pointers (pointer must always be removed before + # values otherwise we will inadvertently dereference values, + # same applies for const specifications) + # + # always add whitespace after type definition for cosmetic reasons + known_types = [ + "const realtype *", + "const double *", + "const realtype ", + "double *", + "realtype *", + "const int ", + "int ", + "bool ", + "SUNMatrixContent_Sparse ", + "gsl::span", + ] + + for type_str in known_types: + signature = signature.replace(type_str, "") + + return signature + + +def get_state_independent_event_intializer(events: list[Event]) -> str: + """Get initializer list for state independent events in amici::Model.""" + map_time_to_event_idx = {} + for event_idx, event in enumerate(events): + if not event.triggers_at_fixed_timepoint(): + continue + trigger_time = float(event.get_trigger_time()) + try: + map_time_to_event_idx[trigger_time].append(event_idx) + except KeyError: + map_time_to_event_idx[trigger_time] = [event_idx] + + def vector_initializer(v): + """std::vector initializer list with elements from `v`""" + return f"{{{', '.join(map(str, v))}}}" + + return ", ".join( + f"{{{trigger_time}, {vector_initializer(event_idxs)}}}" + for trigger_time, event_idxs in map_time_to_event_idx.items() + ) diff --git a/python/sdist/amici/de_export.py b/python/sdist/amici/de_export.py index d956056a2e..cdf5d6ac6a 100644 --- a/python/sdist/amici/de_export.py +++ b/python/sdist/amici/de_export.py @@ -49,6 +49,13 @@ event_sensi_functions, multiobs_functions, ) +from ._codegen.model_class import ( + get_function_extern_declaration, + get_sunindex_extern_declaration, + get_model_override_implementation, + get_sunindex_override_implementation, + get_state_independent_event_intializer, +) from ._codegen.template import apply_template from .cxxcodeprinter import ( AmiciCxxCodePrinter, @@ -3330,7 +3337,9 @@ def _write_model_header_cpp(self) -> None: ) ), "Z2EVENT": ", ".join(map(str, self.model._z2event)), - "STATE_INDEPENDENT_EVENTS": self._get_state_independent_event_intializer(), + "STATE_INDEPENDENT_EVENTS": get_state_independent_event_intializer( + self.model.events() + ), "ID": ", ".join( str(float(isinstance(s, DifferentialState))) for s in self.model.states() @@ -3464,27 +3473,6 @@ def _get_symbol_id_initializer_list(self, name: str) -> str: for idx, symbol in enumerate(self.model.sym(name)) ) - def _get_state_independent_event_intializer(self) -> str: - """Get initializer list for state independent events in amici::Model.""" - map_time_to_event_idx = {} - for event_idx, event in enumerate(self.model.events()): - if not event.triggers_at_fixed_timepoint(): - continue - trigger_time = float(event.get_trigger_time()) - try: - map_time_to_event_idx[trigger_time].append(event_idx) - except KeyError: - map_time_to_event_idx[trigger_time] = [event_idx] - - def vector_initializer(v): - """std::vector initializer list with elements from `v`""" - return f"{{{', '.join(map(str, v))}}}" - - return ", ".join( - f"{{{trigger_time}, {vector_initializer(event_idxs)}}}" - for trigger_time, event_idxs in map_time_to_event_idx.items() - ) - def _write_c_make_file(self): """Write CMake ``CMakeLists.txt`` file for this model.""" sources = "\n".join( @@ -3587,169 +3575,6 @@ def set_name(self, model_name: str) -> None: self.model_name = model_name -def get_function_extern_declaration(fun: str, name: str, ode: bool) -> str: - """ - Constructs the extern function declaration for a given function - - :param fun: - function name - :param name: - model name - :param ode: - whether to generate declaration for DAE or ODE - - :return: - C++ function definition string - """ - f = functions[fun] - return f"extern {f.return_type} {fun}_{name}({f.arguments(ode)});" - - -def get_sunindex_extern_declaration( - fun: str, name: str, indextype: str -) -> str: - """ - Constructs the function declaration for an index function of a given - function - - :param fun: - function name - - :param name: - model name - - :param indextype: - index function {'colptrs', 'rowvals'} - - :return: - C++ function declaration string - """ - index_arg = ", int index" if fun in multiobs_functions else "" - return ( - f"extern void {fun}_{indextype}_{name}" - f"(SUNMatrixWrapper &{indextype}{index_arg});" - ) - - -def get_model_override_implementation( - fun: str, name: str, ode: bool, nobody: bool = False -) -> str: - """ - Constructs ``amici::Model::*`` override implementation for a given function - - :param fun: - function name - - :param name: - model name - - :param nobody: - whether the function has a nontrivial implementation - - :return: - C++ function implementation string - """ - func_info = functions[fun] - body = ( - "" - if nobody - else "\n{ind8}{maybe_return}{fun}_{name}({eval_signature});\n{ind4}".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(ode)), - ) - ) - return "{return_type} f{fun}({signature}) override {{{body}}}\n".format( - return_type=func_info.return_type, - fun=fun, - signature=func_info.arguments(ode), - body=body, - ) - - -def get_sunindex_override_implementation( - fun: str, name: str, indextype: str, nobody: bool = False -) -> str: - """ - Constructs the ``amici::Model`` function implementation for an index - function of a given function - - :param fun: - function name - - :param name: - model name - - :param indextype: - index function {'colptrs', 'rowvals'} - - :param nobody: - whether the corresponding function has a nontrivial implementation - - :return: - C++ function implementation string - """ - index_arg = ", int index" if fun in multiobs_functions else "" - index_arg_eval = ", index" if fun in multiobs_functions else "" - - impl = "void f{fun}_{indextype}({signature}) override {{" - - if nobody: - impl += "}}\n" - else: - impl += ( - "\n{ind8}{fun}_{indextype}_{name}({eval_signature});\n{ind4}}}\n" - ) - - return impl.format( - ind4=" " * 4, - ind8=" " * 8, - fun=fun, - indextype=indextype, - name=name, - signature=f"SUNMatrixWrapper &{indextype}{index_arg}", - eval_signature=f"{indextype}{index_arg_eval}", - ) - - -def remove_argument_types(signature: str) -> str: - """ - Strips argument types from a function signature - - :param signature: - function signature - - :return: - string that can be used to construct function calls with the same - variable names and ordering as in the function signature - """ - # remove * prefix for pointers (pointer must always be removed before - # values otherwise we will inadvertently dereference values, - # same applies for const specifications) - # - # always add whitespace after type definition for cosmetic reasons - known_types = [ - "const realtype *", - "const double *", - "const realtype ", - "double *", - "realtype *", - "const int ", - "int ", - "bool ", - "SUNMatrixContent_Sparse ", - "gsl::span", - ] - - for type_str in known_types: - signature = signature.replace(type_str, "") - - return signature - - def is_valid_identifier(x: str) -> bool: """ Check whether `x` is a valid identifier for conditions, parameters, From 4782cadc9c408475c90488a540578ddd3caad259 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Wed, 28 Feb 2024 13:17:11 +0100 Subject: [PATCH 046/188] Refactor de_export, extract DEModel (#2324) * Refactor de_export, extract DEModel Move `de_export.DEModel` to `de_model`. Closes #2306. de_export is now down to 1.5K lines, from previously 4K+. Some further cleanup would be good, but it's a start. * .. * .. --- documentation/python_modules.rst | 1 + python/sdist/amici/__init__.py | 2 +- python/sdist/amici/_codegen/cxx_functions.py | 23 + python/sdist/amici/de_export.py | 2255 +---------------- python/sdist/amici/de_model.py | 2276 ++++++++++++++++++ python/sdist/amici/pysb_import.py | 2 +- python/sdist/amici/sbml_import.py | 2 +- python/sdist/amici/sympy_utils.py | 3 +- python/tests/test_misc.py | 4 +- python/tests/test_ode_export.py | 2 +- 10 files changed, 2308 insertions(+), 2262 deletions(-) create mode 100644 python/sdist/amici/de_model.py diff --git a/documentation/python_modules.rst b/documentation/python_modules.rst index 03ae7a937e..2ec565c5fa 100644 --- a/documentation/python_modules.rst +++ b/documentation/python_modules.rst @@ -25,6 +25,7 @@ AMICI Python API amici.petab_simulate amici.import_utils amici.de_export + amici.de_model amici.de_model_components amici.plotting amici.pandas diff --git a/python/sdist/amici/__init__.py b/python/sdist/amici/__init__.py index 204933565f..cd7bcb0500 100644 --- a/python/sdist/amici/__init__.py +++ b/python/sdist/amici/__init__.py @@ -114,7 +114,7 @@ def _imported_from_setup() -> bool: # These modules don't require the swig interface from typing import Protocol, runtime_checkable - from .de_export import DEExporter, DEModel # noqa: F401 + from .de_export import DEExporter # noqa: F401 from .sbml_import import ( # noqa: F401 SbmlImporter, assignmentRules2observables, diff --git a/python/sdist/amici/_codegen/cxx_functions.py b/python/sdist/amici/_codegen/cxx_functions.py index 25a8af3c2c..7831ed97c2 100644 --- a/python/sdist/amici/_codegen/cxx_functions.py +++ b/python/sdist/amici/_codegen/cxx_functions.py @@ -2,6 +2,7 @@ from __future__ import annotations from dataclasses import dataclass +import re @dataclass @@ -383,3 +384,25 @@ def arguments(self, ode: bool = True) -> str: if "const int iy" in func_info.arguments() or "const int iz" in func_info.arguments() ] + + +def var_in_function_signature(name: str, varname: str, ode: bool) -> bool: + """ + Checks if the values for a symbolic variable is passed in the signature + of a function + + :param name: + name of the function + :param varname: + name of the symbolic variable + :param ode: + whether to check the ODE or DAE signature + + :return: + boolean indicating whether the variable occurs in the function + signature + """ + return name in functions and re.search( + rf"const (realtype|double) \*{varname}[0]*(,|$)+", + functions[name].arguments(ode=ode), + ) diff --git a/python/sdist/amici/de_export.py b/python/sdist/amici/de_export.py index cdf5d6ac6a..fea9325ab2 100644 --- a/python/sdist/amici/de_export.py +++ b/python/sdist/amici/de_export.py @@ -10,25 +10,17 @@ :py:func:`amici.petab_import.import_model`. """ from __future__ import annotations -import contextlib import copy -import itertools import logging import os import re import shutil -from itertools import chain from pathlib import Path from typing import ( TYPE_CHECKING, - Callable, Literal, ) -from collections.abc import Sequence -import numpy as np import sympy as sp -from sympy.matrices.dense import MutableDenseMatrix -from sympy.matrices.immutable import ImmutableDenseMatrix from . import ( __commit__, @@ -60,32 +52,22 @@ from .cxxcodeprinter import ( AmiciCxxCodePrinter, get_switch_statement, - csc_matrix, ) +from .de_model import DEModel from .de_model_components import * from .import_utils import ( - ObservableTransformation, - SBMLException, - amici_time_symbol, - smart_subs_dict, strip_pysb, - toposort_symbols, - unique_preserve_order, - _default_simplify, ) from .logging import get_logger, log_execution_time, set_log_level from .compile import build_model_extension from .sympy_utils import ( _custom_pow_eval_derivative, _monkeypatched, - smart_jacobian, - smart_multiply, smart_is_zero_matrix, - _parallel_applyfunc, ) if TYPE_CHECKING: - from .splines import AbstractSpline + pass # Template for model simulation main.cpp file @@ -100,8 +82,6 @@ ) IDENTIFIER_PATTERN = re.compile(r"^[a-zA-Z_]\w*$") -DERIVATIVE_PATTERN = re.compile(r"^d(x_rdata|xdot|\w+?)d(\w+?)(?:_explicit)?$") - #: list of equations that have ids which may not be unique non_unique_id_symbols = ["x_rdata", "y"] @@ -122,2237 +102,6 @@ logger = get_logger(__name__, logging.ERROR) -def var_in_function_signature(name: str, varname: str, ode: bool) -> bool: - """ - Checks if the values for a symbolic variable is passed in the signature - of a function - - :param name: - name of the function - :param varname: - name of the symbolic variable - :param ode: - whether to check the ODE or DAE signature - - :return: - boolean indicating whether the variable occurs in the function - signature - """ - return name in functions and re.search( - rf"const (realtype|double) \*{varname}[0]*(,|$)+", - functions[name].arguments(ode=ode), - ) - - -class DEModel: - """ - Defines a Differential Equation as set of ModelQuantities. - This class provides general purpose interfaces to compute arbitrary - symbolic derivatives that are necessary for model simulation or - sensitivity computation. - - :ivar _differential_states: - list of differential state variables - - :ivar _algebraic_states: - list of algebraic state variables - - :ivar _observables: - list of observables - - :ivar _event_observables: - list of event observables - - :ivar _sigma_ys: - list of sigmas for observables - - :ivar _sigma_zs: - list of sigmas for event observables - - :ivar _parameters: - list of parameters - - :ivar _log_likelihood_ys: - list of loglikelihoods for observables - - :ivar _log_likelihood_zs: - list of loglikelihoods for event observables - - :ivar _log_likelihood_rzs: - list of loglikelihoods for event observable regularizations - - :ivar _expressions: - list of expressions instances - - :ivar _conservation_laws: - list of conservation laws - - :ivar _symboldim_funs: - define functions that compute model dimensions, these - are functions as the underlying symbolic expressions have not been - populated at compile time - - :ivar _eqs: - carries symbolic formulas of the symbolic variables of the model - - :ivar _sparseeqs: - carries linear list of all symbolic formulas for sparsified - variables - - :ivar _vals: - carries numeric values of symbolic identifiers of the symbolic - variables of the model - - :ivar _names: - carries the names of symbolic identifiers of the symbolic variables - of the model - - :ivar _syms: - carries symbolic identifiers of the symbolic variables of the - model - - :ivar _sparsesyms: - carries linear list of all symbolic identifiers for sparsified - variables - - :ivar _colptrs: - carries column pointers for sparsified variables. See - SUNMatrixContent_Sparse definition in ``sunmatrix/sunmatrix_sparse.h`` - - :ivar _rowvals: - carries row values for sparsified variables. See - SUNMatrixContent_Sparse definition in ``sunmatrix/sunmatrix_sparse.h`` - - :ivar _equation_prototype: - defines the attribute from which an equation should be generated via - list comprehension (see :meth:`OEModel._generate_equation`) - - :ivar _variable_prototype: - defines the attribute from which a variable should be generated via - list comprehension (see :meth:`DEModel._generate_symbol`) - - :ivar _value_prototype: - defines the attribute from which a value should be generated via - list comprehension (see :meth:`DEModel._generate_value`) - - :ivar _total_derivative_prototypes: - defines how a total derivative equation is computed for an equation, - key defines the name and values should be arguments for - :meth:`DEModel.totalDerivative` - - :ivar _lock_total_derivative: - add chainvariables to this set when computing total derivative from - a partial derivative call to enforce a partial derivative in the - next recursion. prevents infinite recursion - - :ivar _simplify: - If not None, this function will be used to simplify symbolic - derivative expressions. Receives sympy expressions as only argument. - To apply multiple simplifications, wrap them in a lambda expression. - - :ivar _x0_fixedParameters_idx: - Index list of subset of states for which x0_fixedParameters was - computed - - :ivar _w_recursion_depth: - recursion depth in w, quantified as nilpotency of dwdw - - :ivar _has_quadratic_nllh: - whether all observables have a gaussian noise model, i.e. whether - res and FIM make sense. - - :ivar _static_indices: - dict of lists list of indices of static variables for different - model entities. - - :ivar _z2event: - list of event indices for each event observable - """ - - def __init__( - self, - verbose: bool | int | None = False, - simplify: Callable | None = _default_simplify, - cache_simplify: bool = False, - ): - """ - Create a new DEModel instance. - - :param verbose: - verbosity level for logging, True/False default to - ``logging.DEBUG``/``logging.ERROR`` - - :param simplify: - see :meth:`DEModel._simplify` - - :param cache_simplify: - Whether to cache calls to the simplify method. Can e.g. decrease - import times for models with events. - """ - self._differential_states: list[DifferentialState] = [] - self._algebraic_states: list[AlgebraicState] = [] - self._algebraic_equations: list[AlgebraicEquation] = [] - self._observables: list[Observable] = [] - self._event_observables: list[EventObservable] = [] - self._sigma_ys: list[SigmaY] = [] - self._sigma_zs: list[SigmaZ] = [] - self._parameters: list[Parameter] = [] - self._constants: list[Constant] = [] - self._log_likelihood_ys: list[LogLikelihoodY] = [] - self._log_likelihood_zs: list[LogLikelihoodZ] = [] - self._log_likelihood_rzs: list[LogLikelihoodRZ] = [] - self._expressions: list[Expression] = [] - self._conservation_laws: list[ConservationLaw] = [] - self._events: list[Event] = [] - self._splines: list[AbstractSpline] = [] - self._symboldim_funs: dict[str, Callable[[], int]] = { - "sx": self.num_states_solver, - "v": self.num_states_solver, - "vB": self.num_states_solver, - "xB": self.num_states_solver, - "sigmay": self.num_obs, - "sigmaz": self.num_eventobs, - } - self._eqs: dict[ - str, - (sp.Matrix | sp.SparseMatrix | list[sp.Matrix | sp.SparseMatrix]), - ] = dict() - self._sparseeqs: dict[str, sp.Matrix | list[sp.Matrix]] = dict() - self._vals: dict[str, list[sp.Expr]] = dict() - self._names: dict[str, list[str]] = dict() - self._syms: dict[str, sp.Matrix | list[sp.Matrix]] = dict() - self._sparsesyms: dict[str, list[str] | list[list[str]]] = dict() - self._colptrs: dict[str, list[int] | list[list[int]]] = dict() - self._rowvals: dict[str, list[int] | list[list[int]]] = dict() - - self._equation_prototype: dict[str, Callable] = { - "total_cl": self.conservation_laws, - "x0": self.states, - "y": self.observables, - "Jy": self.log_likelihood_ys, - "Jz": self.log_likelihood_zs, - "Jrz": self.log_likelihood_rzs, - "w": self.expressions, - "root": self.events, - "sigmay": self.sigma_ys, - "sigmaz": self.sigma_zs, - } - self._variable_prototype: dict[str, Callable] = { - "tcl": self.conservation_laws, - "x_rdata": self.states, - "y": self.observables, - "z": self.event_observables, - "p": self.parameters, - "k": self.constants, - "w": self.expressions, - "sigmay": self.sigma_ys, - "sigmaz": self.sigma_zs, - "h": self.events, - } - self._value_prototype: dict[str, Callable] = { - "p": self.parameters, - "k": self.constants, - } - self._total_derivative_prototypes: dict[ - str, dict[str, str | list[str]] - ] = { - "sroot": { - "eq": "root", - "chainvars": ["x"], - "var": "p", - "dxdz_name": "sx", - }, - } - - self._lock_total_derivative: list[str] = list() - self._simplify: Callable = simplify - if cache_simplify and simplify is not None: - - def cached_simplify( - expr: sp.Expr, - _simplified: dict[str, sp.Expr] = {}, # noqa B006 - _simplify: Callable = simplify, - ) -> sp.Expr: - """Speed up expression simplification with caching. - - NB: This can decrease model import times for models that have - many repeated expressions during C++ file generation. - For example, this can be useful for models with events. - However, for other models, this may increase model import - times. - - :param expr: - The SymPy expression. - :param _simplified: - The cache. - :param _simplify: - The simplification method. - - :return: - The simplified expression. - """ - expr_str = repr(expr) - if expr_str not in _simplified: - _simplified[expr_str] = _simplify(expr) - return _simplified[expr_str] - - self._simplify = cached_simplify - self._x0_fixedParameters_idx: None | Sequence[int] - self._w_recursion_depth: int = 0 - self._has_quadratic_nllh: bool = True - set_log_level(logger, verbose) - - self._static_indices: dict[str, list[int]] = {} - - def differential_states(self) -> list[DifferentialState]: - """Get all differential states.""" - return self._differential_states - - def algebraic_states(self) -> list[AlgebraicState]: - """Get all algebraic states.""" - return self._algebraic_states - - def observables(self) -> list[Observable]: - """Get all observables.""" - return self._observables - - def parameters(self) -> list[Parameter]: - """Get all parameters.""" - return self._parameters - - def constants(self) -> list[Constant]: - """Get all constants.""" - return self._constants - - def expressions(self) -> list[Expression]: - """Get all expressions.""" - return self._expressions - - def events(self) -> list[Event]: - """Get all events.""" - return self._events - - def event_observables(self) -> list[EventObservable]: - """Get all event observables.""" - return self._event_observables - - def sigma_ys(self) -> list[SigmaY]: - """Get all observable sigmas.""" - return self._sigma_ys - - def sigma_zs(self) -> list[SigmaZ]: - """Get all event observable sigmas.""" - return self._sigma_zs - - def conservation_laws(self) -> list[ConservationLaw]: - """Get all conservation laws.""" - return self._conservation_laws - - def log_likelihood_ys(self) -> list[LogLikelihoodY]: - """Get all observable log likelihoodss.""" - return self._log_likelihood_ys - - def log_likelihood_zs(self) -> list[LogLikelihoodZ]: - """Get all event observable log likelihoods.""" - return self._log_likelihood_zs - - def log_likelihood_rzs(self) -> list[LogLikelihoodRZ]: - """Get all event observable regularization log likelihoods.""" - return self._log_likelihood_rzs - - def is_ode(self) -> bool: - """Check if model is ODE model.""" - return len(self._algebraic_equations) == 0 - - def states(self) -> list[State]: - """Get all states.""" - return self._differential_states + self._algebraic_states - - def _process_sbml_rate_of(self) -> None: - """Substitute any SBML-rateOf constructs in the model equations""" - rate_of_func = sp.core.function.UndefinedFunction("rateOf") - species_sym_to_xdot = dict(zip(self.sym("x"), self.sym("xdot"))) - species_sym_to_idx = {x: i for i, x in enumerate(self.sym("x"))} - - def get_rate(symbol: sp.Symbol): - """Get rate of change of the given symbol""" - if symbol.find(rate_of_func): - raise SBMLException("Nesting rateOf() is not allowed.") - - # Replace all rateOf(some_species) by their respective xdot equation - with contextlib.suppress(KeyError): - return self._eqs["xdot"][species_sym_to_idx[symbol]] - - # For anything other than a state, rateOf(.) is 0 or invalid - return 0 - - # replace rateOf-instances in xdot by xdot symbols - made_substitutions = False - for i_state in range(len(self.eq("xdot"))): - if rate_ofs := self._eqs["xdot"][i_state].find(rate_of_func): - self._eqs["xdot"][i_state] = self._eqs["xdot"][i_state].subs( - { - # either the rateOf argument is a state, or it's 0 - rate_of: species_sym_to_xdot.get(rate_of.args[0], 0) - for rate_of in rate_ofs - } - ) - made_substitutions = True - - if made_substitutions: - # substitute in topological order - subs = toposort_symbols( - dict(zip(self.sym("xdot"), self.eq("xdot"))) - ) - self._eqs["xdot"] = smart_subs_dict(self.eq("xdot"), subs) - - # replace rateOf-instances in x0 by xdot equation - for i_state in range(len(self.eq("x0"))): - if rate_ofs := self._eqs["x0"][i_state].find(rate_of_func): - self._eqs["x0"][i_state] = self._eqs["x0"][i_state].subs( - { - rate_of: get_rate(rate_of.args[0]) - for rate_of in rate_ofs - } - ) - - # replace rateOf-instances in w by xdot equation - # here we may need toposort, as xdot may depend on w - made_substitutions = False - for i_expr in range(len(self.eq("w"))): - if rate_ofs := self._eqs["w"][i_expr].find(rate_of_func): - self._eqs["w"][i_expr] = self._eqs["w"][i_expr].subs( - { - rate_of: get_rate(rate_of.args[0]) - for rate_of in rate_ofs - } - ) - made_substitutions = True - - if made_substitutions: - # Sort expressions in self._expressions, w symbols, and w equations - # in topological order. Ideally, this would already happen before - # adding the expressions to the model, but at that point, we don't - # have access to xdot yet. - # NOTE: elsewhere, conservations law expressions are expected to - # occur before any other w expressions, so we must maintain their - # position - # toposort everything but conservation law expressions, - # then prepend conservation laws - w_sorted = toposort_symbols( - dict( - zip( - self.sym("w")[self.num_cons_law() :, :], - self.eq("w")[self.num_cons_law() :, :], - ) - ) - ) - w_sorted = ( - dict( - zip( - self.sym("w")[: self.num_cons_law(), :], - self.eq("w")[: self.num_cons_law(), :], - ) - ) - | w_sorted - ) - old_syms = tuple(self._syms["w"]) - topo_expr_syms = tuple(w_sorted.keys()) - new_order = [old_syms.index(s) for s in topo_expr_syms] - self._expressions = [self._expressions[i] for i in new_order] - self._syms["w"] = sp.Matrix(topo_expr_syms) - self._eqs["w"] = sp.Matrix(list(w_sorted.values())) - - for component in chain( - self.observables(), - self.events(), - self._algebraic_equations, - ): - if rate_ofs := component.get_val().find(rate_of_func): - if isinstance(component, Event): - # TODO froot(...) can currently not depend on `w`, so this substitution fails for non-zero rates - # see, e.g., sbml test case 01293 - raise SBMLException( - "AMICI does currently not support rateOf(.) inside event trigger functions." - ) - - if isinstance(component, AlgebraicEquation): - # TODO IDACalcIC fails with - # "The linesearch algorithm failed: step too small or too many backtracks." - # see, e.g., sbml test case 01482 - raise SBMLException( - "AMICI does currently not support rateOf(.) inside AlgebraicRules." - ) - - component.set_val( - component.get_val().subs( - { - rate_of: get_rate(rate_of.args[0]) - for rate_of in rate_ofs - } - ) - ) - - for event in self.events(): - if event._state_update is None: - continue - - for i_state in range(len(event._state_update)): - if rate_ofs := event._state_update[i_state].find(rate_of_func): - raise SBMLException( - "AMICI does currently not support rateOf(.) inside event state updates." - ) - # TODO here we need xdot sym, not eqs - # event._state_update[i_state] = event._state_update[i_state].subs( - # {rate_of: get_rate(rate_of.args[0]) for rate_of in rate_ofs} - # ) - - def add_component( - self, component: ModelQuantity, insert_first: bool | None = False - ) -> None: - """ - Adds a new ModelQuantity to the model. - - :param component: - model quantity to be added - - :param insert_first: - whether to add quantity first or last, relevant when components - may refer to other components of the same type. - """ - if type(component) not in { - Observable, - Expression, - Parameter, - Constant, - DifferentialState, - AlgebraicState, - AlgebraicEquation, - LogLikelihoodY, - LogLikelihoodZ, - LogLikelihoodRZ, - SigmaY, - SigmaZ, - ConservationLaw, - Event, - EventObservable, - }: - raise ValueError(f"Invalid component type {type(component)}") - - component_list = getattr( - self, - "_" - + "_".join( - s.lower() - for s in re.split(r"([A-Z][^A-Z]+)", type(component).__name__) - if s - ) - + "s", - ) - if insert_first: - component_list.insert(0, component) - else: - component_list.append(component) - - def add_conservation_law( - self, - state: sp.Symbol, - total_abundance: sp.Symbol, - coefficients: dict[sp.Symbol, sp.Expr], - ) -> None: - r""" - Adds a new conservation law to the model. A conservation law is defined - by the conserved quantity :math:`T = \sum_i(a_i * x_i)`, where - :math:`a_i` are coefficients and :math:`x_i` are different state - variables. - - :param state: - symbolic identifier of the state that should be replaced by - the conservation law (:math:`x_j`) - - :param total_abundance: - symbolic identifier of the total abundance (:math:`T/a_j`) - - :param coefficients: - Dictionary of coefficients {x_i: a_i} - """ - try: - ix = next( - filter( - lambda is_s: is_s[1].get_id() == state, - enumerate(self._differential_states), - ) - )[0] - except StopIteration: - raise ValueError( - f"Specified state {state} was not found in the " - f"model states." - ) - - state_id = self._differential_states[ix].get_id() - - # \sum_{i≠j}(a_i * x_i)/a_j - target_expression = ( - sp.Add( - *( - c_i * x_i - for x_i, c_i in coefficients.items() - if x_i != state - ) - ) - / coefficients[state] - ) - - # x_j = T/a_j - \sum_{i≠j}(a_i * x_i)/a_j - state_expr = total_abundance - target_expression - - # T/a_j = \sum_{i≠j}(a_i * x_i)/a_j + x_j - abundance_expr = target_expression + state_id - - self.add_component( - Expression(state_id, str(state_id), state_expr), insert_first=True - ) - - cl = ConservationLaw( - total_abundance, - f"total_{state_id}", - abundance_expr, - coefficients, - state_id, - ) - - self.add_component(cl) - self._differential_states[ix].set_conservation_law(cl) - - def add_spline(self, spline: AbstractSpline, spline_expr: sp.Expr) -> None: - """Add a spline to the model. - - :param spline: - Spline instance to be added - :param spline_expr: - Sympy function representation of `spline` from - ``spline.ode_model_symbol()``. - """ - self._splines.append(spline) - self.add_component( - Expression( - identifier=spline.sbml_id, - name=str(spline.sbml_id), - value=spline_expr, - ) - ) - - def get_observable_transformations(self) -> list[ObservableTransformation]: - """ - List of observable transformations - - :return: - list of transformations - """ - return [obs.trafo for obs in self._observables] - - def num_states_rdata(self) -> int: - """ - Number of states. - - :return: - number of state variable symbols - """ - return len(self.sym("x_rdata")) - - def num_states_solver(self) -> int: - """ - Number of states after applying conservation laws. - - :return: - number of state variable symbols - """ - return len(self.sym("x")) - - def num_cons_law(self) -> int: - """ - Number of conservation laws. - - :return: - number of conservation laws - """ - return self.num_states_rdata() - self.num_states_solver() - - def num_state_reinits(self) -> int: - """ - Number of solver states which would be reinitialized after - preequilibration - - :return: - number of state variable symbols with reinitialization - """ - reinit_states = self.eq("x0_fixedParameters") - solver_states = self.eq("x_solver") - return sum(ix in solver_states for ix in reinit_states) - - def num_obs(self) -> int: - """ - Number of Observables. - - :return: - number of observable symbols - """ - return len(self.sym("y")) - - def num_eventobs(self) -> int: - """ - Number of Event Observables. - - :return: - number of event observable symbols - """ - return len(self.sym("z")) - - def num_const(self) -> int: - """ - Number of Constants. - - :return: - number of constant symbols - """ - return len(self.sym("k")) - - def num_par(self) -> int: - """ - Number of Parameters. - - :return: - number of parameter symbols - """ - return len(self.sym("p")) - - def num_expr(self) -> int: - """ - Number of Expressions. - - :return: - number of expression symbols - """ - return len(self.sym("w")) - - def num_events(self) -> int: - """ - Total number of Events (those for which root-functions are added and those without). - - :return: - number of events - """ - return len(self.sym("h")) - - def num_events_solver(self) -> int: - """ - Number of Events. - - :return: - number of event symbols (length of the root vector in AMICI) - """ - return sum( - not event.triggers_at_fixed_timepoint() for event in self.events() - ) - - def sym(self, name: str) -> sp.Matrix: - """ - Returns (and constructs if necessary) the identifiers for a symbolic - entity. - - :param name: - name of the symbolic variable - - :return: - matrix of symbolic identifiers - """ - if name not in self._syms: - self._generate_symbol(name) - - return self._syms[name] - - def sparsesym(self, name: str, force_generate: bool = True) -> list[str]: - """ - Returns (and constructs if necessary) the sparsified identifiers for - a sparsified symbolic variable. - - :param name: - name of the symbolic variable - - :param force_generate: - whether the symbols should be generated if not available - - :return: - linearized Matrix containing the symbolic identifiers - """ - if name not in sparse_functions: - raise ValueError(f"{name} is not marked as sparse") - if name not in self._sparsesyms and force_generate: - self._generate_sparse_symbol(name) - return self._sparsesyms.get(name, []) - - def eq(self, name: str) -> sp.Matrix: - """ - Returns (and constructs if necessary) the formulas for a symbolic - entity. - - :param name: - name of the symbolic variable - - :return: - matrix of symbolic formulas - """ - - if name not in self._eqs: - dec = log_execution_time(f"computing {name}", logger) - dec(self._compute_equation)(name) - return self._eqs[name] - - def sparseeq(self, name) -> sp.Matrix: - """ - Returns (and constructs if necessary) the sparsified formulas for a - sparsified symbolic variable. - - :param name: - name of the symbolic variable - - :return: - linearized matrix containing the symbolic formulas - """ - if name not in sparse_functions: - raise ValueError(f"{name} is not marked as sparse") - if name not in self._sparseeqs: - self._generate_sparse_symbol(name) - return self._sparseeqs[name] - - def colptrs(self, name: str) -> list[sp.Number] | list[list[sp.Number]]: - """ - Returns (and constructs if necessary) the column pointers for - a sparsified symbolic variable. - - :param name: - name of the symbolic variable - - :return: - list containing the column pointers - """ - if name not in sparse_functions: - raise ValueError(f"{name} is not marked as sparse") - if name not in self._sparseeqs: - self._generate_sparse_symbol(name) - return self._colptrs[name] - - def rowvals(self, name: str) -> list[sp.Number] | list[list[sp.Number]]: - """ - Returns (and constructs if necessary) the row values for a - sparsified symbolic variable. - - :param name: - name of the symbolic variable - - :return: - list containing the row values - """ - if name not in sparse_functions: - raise ValueError(f"{name} is not marked as sparse") - if name not in self._sparseeqs: - self._generate_sparse_symbol(name) - return self._rowvals[name] - - def val(self, name: str) -> list[sp.Number]: - """ - Returns (and constructs if necessary) the numeric values of a - symbolic entity - - :param name: - name of the symbolic variable - - :return: - list containing the numeric values - """ - if name not in self._vals: - self._generate_value(name) - return self._vals[name] - - def name(self, name: str) -> list[str]: - """ - Returns (and constructs if necessary) the names of a symbolic - variable - - :param name: - name of the symbolic variable - - :return: - list of names - """ - if name not in self._names: - self._generate_name(name) - return self._names[name] - - def free_symbols(self) -> set[sp.Basic]: - """ - Returns list of free symbols that appear in RHS and initial - conditions. - """ - return set( - chain.from_iterable( - state.get_free_symbols() for state in self.states() - ) - ) - - def static_indices(self, name: str) -> list[int]: - """ - Returns the indices of static expressions in the given model entity. - - Static expressions are those that do not depend on time, - neither directly nor indirectly. - - :param name: Name of the model entity. - :return: List of indices of static expressions. - """ - # already computed? - if (res := self._static_indices.get(name)) is not None: - return res - - if name == "w": - dwdx = self.sym("dwdx") - dwdw = self.sym("dwdw") - w = self.eq("w") - - # Check for direct (via `t`) or indirect (via `x`, `h`, or splines) - # time dependency. - # To avoid lengthy symbolic computations, we only check if we have - # any non-zeros in hierarchy. We currently neglect the case where - # different hierarchy levels may cancel out. Treating a static - # expression as dynamic in such rare cases shouldn't be a problem. - dynamic_dependency = np.asarray( - dwdx.applyfunc(lambda x: int(not x.is_zero)) - ).astype(np.int64) - # to check for other time-dependence, we add a column to the dwdx - # matrix - dynamic_syms = [ - # FIXME: see spline comment below - # *self.sym("spl"), - *self.sym("h"), - amici_time_symbol, - ] - dynamic_dependency = np.hstack( - ( - dynamic_dependency, - np.array( - [ - expr.has(*dynamic_syms) - # FIXME: the current spline implementation is a giant pita - # currently, the splines occur in the form of sympy functions, e.g.: - # AmiciSpline(y0, time, y0_3, y0_1) - # AmiciSplineSensitivity(y0, time, y0_1, y0_3, y0_1) - # until it uses the proper self.sym("spl") / self.sym("sspl") - # symbols, which will require quite some refactoring, - # we just do dumb string checks :| - or ( - bool(self._splines) - and "AmiciSpline" in str(expr) - ) - for expr in w - ] - )[:, np.newaxis], - ) - ) - - nonzero_dwdw = np.asarray( - dwdw.applyfunc(lambda x: int(not x.is_zero)) - ).astype(np.int64) - - # `w` is made up an expression hierarchy. Any given entry is only - # static if all its dependencies are static. Here, we unravel - # the hierarchical structure of `w`. - # If for an entry in `w`, the row sum of the intermediate products - # is 0 across all levels, the expression is static. - tmp = dynamic_dependency - res = np.sum(tmp, axis=1) - while np.any(tmp != 0): - tmp = nonzero_dwdw.dot(tmp) - res += np.sum(tmp, axis=1) - self._static_indices[name] = ( - np.argwhere(res == 0).flatten().tolist() - ) - - return self._static_indices[name] - - if name in ("dwdw", "dwdx", "dwdp"): - static_indices_w = set(self.static_indices("w")) - dynamic_syms = [ - *( - sym - for i, sym in enumerate(self.sym("w")) - if i not in static_indices_w - ), - amici_time_symbol, - *self.sym("x"), - *self.sym("h"), - # FIXME see spline comment above - # *(self.sym("spl") if name in ("dwdw", "dwdx") else ()), - # *(self.sym("sspl") if name == "dwdp" else ()), - ] - dynamic_syms = sp.Matrix(dynamic_syms) - rowvals = self.rowvals(name) - sparseeq = self.sparseeq(name) - - # collect the indices of static expressions of dwd* from the list - # of non-zeros entries of the sparse matrix - self._static_indices[name] = [ - i - for i, (expr, row_idx) in enumerate(zip(sparseeq, rowvals)) - # derivative of a static expression is static - if row_idx in static_indices_w - # constant expressions - or expr.is_Number - # check for dependencies on non-static entities - or ( - # FIXME see spline comment above - # (check str before diff, as diff will fail on spline functions) - ( - # splines: non-static - not self._splines or "AmiciSpline" not in str(expr) - ) - and ( - # If the expression contains dynamic symbols, it might - # still be static. However, checking the derivative - # is currently too expensive, and we rather accept - # false negatives. - not expr.has(*dynamic_syms) - # or all( - # expr.diff(dyn_sym).is_zero - # for dyn_sym in dynamic_syms - # ) - ) - ) - ] - return self._static_indices[name] - - raise NotImplementedError(name) - - def dynamic_indices(self, name: str) -> list[int]: - """ - Return the indices of dynamic expressions in the given model entity. - - :param name: Name of the model entity. - :return: List of indices of dynamic expressions. - """ - static_idxs = set(self.static_indices(name)) - length = len( - self.sparsesym(name) - if name in sparse_functions - else self.sym(name) - ) - return [i for i in range(length) if i not in static_idxs] - - def _generate_symbol(self, name: str) -> None: - """ - Generates the symbolic identifiers for a symbolic variable - - :param name: - name of the symbolic variable - """ - if name in self._variable_prototype: - components = self._variable_prototype[name]() - self._syms[name] = sp.Matrix( - [comp.get_id() for comp in components] - ) - if name == "y": - self._syms["my"] = sp.Matrix( - [comp.get_measurement_symbol() for comp in components] - ) - if name == "z": - self._syms["mz"] = sp.Matrix( - [comp.get_measurement_symbol() for comp in components] - ) - self._syms["rz"] = sp.Matrix( - [comp.get_regularization_symbol() for comp in components] - ) - return - elif name == "x": - self._syms[name] = sp.Matrix( - [ - state.get_id() - for state in self.states() - if not state.has_conservation_law() - ] - ) - return - elif name == "xdot": - self._syms[name] = sp.Matrix( - [ - f"d{x.get_id()}dt" if self.is_ode() else f"de_{ix}" - for ix, x in enumerate(self._differential_states) - if not x.has_conservation_law() - ] - + [f"ae_{ix}" for ix in range(len(self._algebraic_equations))] - ) - return - elif name == "dx": - self._syms[name] = sp.Matrix( - [ - f"d{state.get_id()}dt" - for state in self.states() - if not state.has_conservation_law() - ] - ) - return - elif name == "sx0": - self._syms[name] = sp.Matrix( - [ - f"s{state.get_id()}_0" - for state in self.states() - if not state.has_conservation_law() - ] - ) - return - elif name == "sx_rdata": - self._syms[name] = sp.Matrix( - [f"sx_rdata_{i}" for i in range(len(self.states()))] - ) - return - elif name == "dtcldp": - # check, whether the CL consists of only one state. Then, - # sensitivities drop out, otherwise generate symbols - self._syms[name] = sp.Matrix( - [ - [ - sp.Symbol( - f"s{strip_pysb(tcl.get_id())}__" - f"{strip_pysb(par.get_id())}", - real=True, - ) - for par in self._parameters - ] - if self.conservation_law_has_multispecies(tcl) - else [0] * self.num_par() - for tcl in self._conservation_laws - ] - ) - return - elif name == "xdot_old": - length = len(self.eq("xdot")) - elif name in sparse_functions: - self._generate_sparse_symbol(name) - return - elif name in self._symboldim_funs: - length = self._symboldim_funs[name]() - elif name == "stau": - length = self.eq(name)[0].shape[1] - elif name in sensi_functions: - length = self.eq(name).shape[0] - elif name == "spl": - # placeholders for the numeric spline values. - # Need to create symbols - self._syms[name] = sp.Matrix( - [[f"spl_{isp}" for isp in range(len(self._splines))]] - ) - return - elif name == "sspl": - # placeholders for spline sensitivities. Need to create symbols - self._syms[name] = sp.Matrix( - [ - [f"sspl_{isp}_{ip}" for ip in range(len(self._syms["p"]))] - for isp in range(len(self._splines)) - ] - ) - return - else: - length = len(self.eq(name)) - self._syms[name] = sp.Matrix( - [ - sp.Symbol(f'{name}{0 if name == "stau" else i}', real=True) - for i in range(length) - ] - ) - - def generate_basic_variables(self) -> None: - """ - Generates the symbolic identifiers for all variables in - ``DEModel._variable_prototype`` - """ - # We need to process events and Heaviside functions in the ``DEModel`, - # before adding it to DEExporter - self.parse_events() - - for var in self._variable_prototype: - if var not in self._syms: - self._generate_symbol(var) - # symbols for spline values need to be created in addition - for var in ["spl", "sspl"]: - self._generate_symbol(var) - - self._generate_symbol("x") - - def parse_events(self) -> None: - """ - This function checks the right-hand side for roots of Heaviside - functions or events, collects the roots, removes redundant roots, - and replaces the formulae of the found roots by identifiers of AMICI's - Heaviside function implementation in the right-hand side - """ - # Track all roots functions in the right-hand side - roots = copy.deepcopy(self._events) - for state in self._differential_states: - state.set_dt(self._process_heavisides(state.get_dt(), roots)) - - for expr in self._expressions: - expr.set_val(self._process_heavisides(expr.get_val(), roots)) - - # remove all possible Heavisides from roots, which may arise from - # the substitution of `'w'` in `_collect_heaviside_roots` - for root in roots: - root.set_val(self._process_heavisides(root.get_val(), roots)) - - # Now add the found roots to the model components - for root in roots: - # skip roots of SBML events, as these have already been added - if root in self._events: - continue - # add roots of heaviside functions - self.add_component(root) - - # re-order events - first those that require root tracking, then the others - self._events = list( - chain( - itertools.filterfalse( - Event.triggers_at_fixed_timepoint, self._events - ), - filter(Event.triggers_at_fixed_timepoint, self._events), - ) - ) - - def get_appearance_counts(self, idxs: list[int]) -> list[int]: - """ - Counts how often a state appears in the time derivative of - another state and expressions for a subset of states - - :param idxs: - list of state indices for which counts are to be computed - - :return: - list of counts for the states ordered according to the provided - indices - """ - free_symbols_dt = list( - itertools.chain.from_iterable( - [str(symbol) for symbol in state.get_dt().free_symbols] - for state in self.states() - ) - ) - - free_symbols_expr = list( - itertools.chain.from_iterable( - [str(symbol) for symbol in expr.get_val().free_symbols] - for expr in self._expressions - ) - ) - - return [ - free_symbols_dt.count(str(self._differential_states[idx].get_id())) - + free_symbols_expr.count( - str(self._differential_states[idx].get_id()) - ) - for idx in idxs - ] - - def _generate_sparse_symbol(self, name: str) -> None: - """ - Generates the sparse symbolic identifiers, symbolic identifiers, - sparse equations, column pointers and row values for a symbolic - variable - - :param name: - name of the symbolic variable - """ - matrix = self.eq(name) - - if match_deriv := DERIVATIVE_PATTERN.match(name): - eq = match_deriv[1] - var = match_deriv[2] - - rownames = self.sym(eq) - colnames = self.sym(var) - - if name == "dJydy": - # One entry per y-slice - self._colptrs[name] = [] - self._rowvals[name] = [] - self._sparseeqs[name] = [] - self._sparsesyms[name] = [] - self._syms[name] = [] - - for iy in range(self.num_obs()): - ( - symbol_col_ptrs, - symbol_row_vals, - sparse_list, - symbol_list, - sparse_matrix, - ) = csc_matrix( - matrix[iy, :], - rownames=rownames, - colnames=colnames, - identifier=iy, - ) - self._colptrs[name].append(symbol_col_ptrs) - self._rowvals[name].append(symbol_row_vals) - self._sparseeqs[name].append(sparse_list) - self._sparsesyms[name].append(symbol_list) - self._syms[name].append(sparse_matrix) - else: - ( - symbol_col_ptrs, - symbol_row_vals, - sparse_list, - symbol_list, - sparse_matrix, - ) = csc_matrix( - matrix, - rownames=rownames, - colnames=colnames, - pattern_only=name in nobody_functions, - ) - - self._colptrs[name] = symbol_col_ptrs - self._rowvals[name] = symbol_row_vals - self._sparseeqs[name] = sparse_list - self._sparsesyms[name] = symbol_list - self._syms[name] = sparse_matrix - - def _compute_equation(self, name: str) -> None: - """ - Computes the symbolic formula for a symbolic variable - - :param name: - name of the symbolic variable - """ - # replacement ensures that we don't have to adapt name in abstract - # model and keep backwards compatibility with matlab - match_deriv = DERIVATIVE_PATTERN.match( - re.sub(r"dJ(y|z|rz)dsigma", r"dJ\1dsigma\1", name) - .replace("sigmarz", "sigmaz") - .replace("dJrzdz", "dJrzdrz") - ) - time_symbol = sp.Matrix([amici_time_symbol]) - - if name in self._equation_prototype: - self._equation_from_components( - name, self._equation_prototype[name]() - ) - - elif name in self._total_derivative_prototypes: - args = self._total_derivative_prototypes[name] - args["name"] = name - self._lock_total_derivative += args["chainvars"] - self._total_derivative(**args) - for cv in args["chainvars"]: - self._lock_total_derivative.remove(cv) - - elif name == "xdot": - if self.is_ode(): - self._eqs[name] = sp.Matrix( - [ - state.get_dt() - for state in self._differential_states - if not state.has_conservation_law() - ] - ) - else: - self._eqs[name] = sp.Matrix( - [ - x.get_dt() - dx - for x, dx in zip( - ( - s - for s in self._differential_states - if not s.has_conservation_law() - ), - self.sym("dx"), - ) - ] - + [eq.get_val() for eq in self._algebraic_equations] - ) - - elif name == "x_rdata": - self._eqs[name] = sp.Matrix( - [state.get_x_rdata() for state in self.states()] - ) - - elif name == "x_solver": - self._eqs[name] = sp.Matrix( - [ - state.get_id() - for state in self.states() - if not state.has_conservation_law() - ] - ) - - elif name == "sx_solver": - self._eqs[name] = sp.Matrix( - [ - self.sym("sx_rdata")[ix] - for ix, state in enumerate(self.states()) - if not state.has_conservation_law() - ] - ) - - elif name == "sx0": - self._derivative(name[1:], "p", name=name) - - elif name == "sx0_fixedParameters": - # deltax = -x+x0_fixedParameters if x0_fixedParameters>0 else 0 - # deltasx = -sx+dx0_fixed_parametersdx*sx+dx0_fixedParametersdp - # if x0_fixedParameters>0 else 0 - # sx0_fixedParameters = sx+deltasx = - # dx0_fixed_parametersdx*sx+dx0_fixedParametersdp - self._eqs[name] = smart_jacobian( - self.eq("x0_fixedParameters"), self.sym("p") - ) - - dx0_fixed_parametersdx = smart_jacobian( - self.eq("x0_fixedParameters"), self.sym("x") - ) - - if not smart_is_zero_matrix(dx0_fixed_parametersdx): - if isinstance(self._eqs[name], ImmutableDenseMatrix): - self._eqs[name] = MutableDenseMatrix(self._eqs[name]) - tmp = smart_multiply(dx0_fixed_parametersdx, self.sym("sx0")) - for ip in range(self._eqs[name].shape[1]): - self._eqs[name][:, ip] += tmp - - elif name == "x0_fixedParameters": - k = self.sym("k") - self._x0_fixedParameters_idx = [ - ix - for ix, eq in enumerate(self.eq("x0")) - if any(sym in eq.free_symbols for sym in k) - ] - eq = self.eq("x0") - self._eqs[name] = sp.Matrix( - [eq[ix] for ix in self._x0_fixedParameters_idx] - ) - - elif name == "dtotal_cldx_rdata": - x_rdata = self.sym("x_rdata") - self._eqs[name] = sp.Matrix( - [ - [cl.get_ncoeff(xr) for xr in x_rdata] - for cl in self._conservation_laws - ] - ) - - elif name == "dtcldx": - # this is always zero - self._eqs[name] = sp.zeros( - self.num_cons_law(), self.num_states_solver() - ) - - elif name == "dtcldp": - # force symbols - self._eqs[name] = self.sym(name) - - elif name == "dx_rdatadx_solver": - if self.num_cons_law(): - x_solver = self.sym("x") - self._eqs[name] = sp.Matrix( - [ - [state.get_dx_rdata_dx_solver(xs) for xs in x_solver] - for state in self.states() - ] - ) - else: - # so far, dx_rdatadx_solver is only required for sx_rdata - # in case of no conservation laws, C++ code will directly use - # sx, we don't need this - self._eqs[name] = sp.zeros( - self.num_states_rdata(), self.num_states_solver() - ) - - elif name == "dx_rdatadp": - if self.num_cons_law(): - self._eqs[name] = smart_jacobian( - self.eq("x_rdata"), self.sym("p") - ) - else: - # so far, dx_rdatadp is only required for sx_rdata - # in case of no conservation laws, C++ code will directly use - # sx, we don't need this - self._eqs[name] = sp.zeros( - self.num_states_rdata(), self.num_par() - ) - - elif name == "dx_rdatadtcl": - self._eqs[name] = smart_jacobian( - self.eq("x_rdata"), self.sym("tcl") - ) - - elif name == "dxdotdx_explicit": - # force symbols - self._derivative("xdot", "x", name=name) - - elif name == "dxdotdp_explicit": - # force symbols - self._derivative("xdot", "p", name=name) - - elif name == "spl": - self._eqs[name] = self.sym(name) - - elif name == "sspl": - # force symbols - self._eqs[name] = self.sym(name) - - elif name == "spline_values": - # force symbols - self._eqs[name] = sp.Matrix( - [y for spline in self._splines for y in spline.values_at_nodes] - ) - - elif name == "spline_slopes": - # force symbols - self._eqs[name] = sp.Matrix( - [ - d - for spline in self._splines - for d in ( - sp.zeros(len(spline.derivatives_at_nodes), 1) - if spline.derivatives_by_fd - else spline.derivatives_at_nodes - ) - ] - ) - - elif name == "drootdt": - self._eqs[name] = smart_jacobian(self.eq("root"), time_symbol) - - elif name == "drootdt_total": - # backsubstitution of optimized right-hand side terms into RHS - # calling subs() is costly. Due to looping over events though, the - # following lines are only evaluated if a model has events - w_sorted = toposort_symbols(dict(zip(self.sym("w"), self.eq("w")))) - tmp_xdot = smart_subs_dict(self.eq("xdot"), w_sorted) - self._eqs[name] = self.eq("drootdt") - if self.num_states_solver(): - self._eqs[name] += smart_multiply(self.eq("drootdx"), tmp_xdot) - - elif name == "deltax": - # fill boluses for Heaviside functions, as empty state updates - # would cause problems when writing the function file later - event_eqs = [] - for event in self._events: - if event._state_update is None: - event_eqs.append(sp.zeros(self.num_states_solver(), 1)) - else: - event_eqs.append(event._state_update) - - self._eqs[name] = event_eqs - - elif name == "z": - event_observables = [ - sp.zeros(self.num_eventobs(), 1) for _ in self._events - ] - event_ids = [e.get_id() for e in self._events] - # TODO: get rid of this stupid 1-based indexing as soon as we can - # the matlab interface - z2event = [ - event_ids.index(event_obs.get_event()) + 1 - for event_obs in self._event_observables - ] - for (iz, ie), event_obs in zip( - enumerate(z2event), self._event_observables - ): - event_observables[ie - 1][iz] = event_obs.get_val() - - self._eqs[name] = event_observables - self._z2event = z2event - - elif name in ["ddeltaxdx", "ddeltaxdp", "ddeltaxdt", "dzdp", "dzdx"]: - if match_deriv[2] == "t": - var = time_symbol - else: - var = self.sym(match_deriv[2]) - - self._eqs[name] = [ - smart_jacobian(self.eq(match_deriv[1])[ie], var) - for ie in range(self.num_events()) - ] - if name == "dzdx": - for ie in range(self.num_events()): - dtaudx = ( - -self.eq("drootdx")[ie, :] - / self.eq("drootdt_total")[ie] - ) - for iz in range(self.num_eventobs()): - if ie != self._z2event[iz] - 1: - continue - dzdt = sp.diff(self.eq("z")[ie][iz], time_symbol) - self._eqs[name][ie][iz, :] += dzdt * dtaudx - - elif name in ["rz", "drzdx", "drzdp"]: - eq_events = [] - for ie in range(self.num_events()): - val = sp.zeros( - self.num_eventobs(), - 1 if name == "rz" else len(self.sym(match_deriv[2])), - ) - # match event observables to root function - for iz in range(self.num_eventobs()): - if ie == self._z2event[iz] - 1: - val[iz, :] = self.eq(name.replace("rz", "root"))[ie, :] - eq_events.append(val) - - self._eqs[name] = eq_events - - elif name == "stau": - self._eqs[name] = [ - -self.eq("sroot")[ie, :] / self.eq("drootdt_total")[ie] - if not self.eq("drootdt_total")[ie].is_zero - else sp.zeros(*self.eq("sroot")[ie, :].shape) - for ie in range(self.num_events()) - ] - - elif name == "deltasx": - if self.num_states_solver() * self.num_par() == 0: - self._eqs[name] = [] - return - - event_eqs = [] - for ie, event in enumerate(self._events): - tmp_eq = sp.zeros(self.num_states_solver(), self.num_par()) - - # need to check if equations are zero since we are using - # symbols - if not smart_is_zero_matrix( - self.eq("stau")[ie] - ) and not smart_is_zero_matrix(self.eq("xdot")): - tmp_eq += smart_multiply( - self.sym("xdot_old") - self.sym("xdot"), - self.sym("stau").T, - ) - - # only add deltax part if there is state update - if event._state_update is not None: - # partial derivative for the parameters - tmp_eq += self.eq("ddeltaxdp")[ie] - - # initial part of chain rule state variables - tmp_dxdp = self.sym("sx") * sp.ones(1, self.num_par()) - - # need to check if equations are zero since we are using - # symbols - if not smart_is_zero_matrix(self.eq("stau")[ie]): - # chain rule for the time point - tmp_eq += smart_multiply( - self.eq("ddeltaxdt")[ie], self.sym("stau").T - ) - - # additional part of chain rule state variables - tmp_dxdp += smart_multiply( - self.sym("xdot_old"), self.sym("stau").T - ) - - # finish chain rule for the state variables - tmp_eq += smart_multiply( - self.eq("ddeltaxdx")[ie], tmp_dxdp - ) - - event_eqs.append(tmp_eq) - - self._eqs[name] = event_eqs - - elif name == "xdot_old": - # force symbols - self._eqs[name] = self.sym(name) - - elif name == "dwdx": - if ( - expected := list( - map( - ConservationLaw.get_x_rdata, - reversed(self.conservation_laws()), - ) - ) - ) != (actual := self.eq("w")[: self.num_cons_law()]): - raise AssertionError( - "Conservation laws are not at the beginning of 'w'. " - f"Got {actual}, expected {expected}." - ) - x = self.sym("x") - self._eqs[name] = sp.Matrix( - [ - [-cl.get_ncoeff(xs) for xs in x] - # the insert first in ode_model._add_conservation_law() means - # that we need to reverse the order here - for cl in reversed(self._conservation_laws) - ] - ).col_join( - smart_jacobian(self.eq("w")[self.num_cons_law() :, :], x) - ) - - elif match_deriv: - self._derivative(match_deriv[1], match_deriv[2], name) - - else: - raise ValueError(f"Unknown equation {name}") - - if name in ("sigmay", "sigmaz"): - # check for states in sigma{y,z}, which is currently not supported - syms_x = self.sym("x") - syms_yz = self.sym(name.removeprefix("sigma")) - xs_in_sigma = {} - for sym_yz, eq_yz in zip(syms_yz, self._eqs[name]): - yz_free_syms = eq_yz.free_symbols - if tmp := {x for x in syms_x if x in yz_free_syms}: - xs_in_sigma[sym_yz] = tmp - if xs_in_sigma: - msg = ", ".join( - [f"{yz} depends on {xs}" for yz, xs in xs_in_sigma.items()] - ) - raise NotImplementedError( - f"State-dependent observables are not supported, but {msg}." - ) - - if name == "root": - # Events are processed after the model has been set up. - # Equations are there, but symbols for roots must be added - self.sym("h") - - if name in {"Jy", "dydx"}: - # do not transpose if we compute the partial derivative as part of - # a total derivative - if not len(self._lock_total_derivative): - self._eqs[name] = self._eqs[name].transpose() - - if name in {"dzdx", "drzdx"}: - self._eqs[name] = [e.T for e in self._eqs[name]] - - if self._simplify: - dec = log_execution_time(f"simplifying {name}", logger) - if isinstance(self._eqs[name], list): - self._eqs[name] = [ - dec(_parallel_applyfunc)(sub_eq, self._simplify) - for sub_eq in self._eqs[name] - ] - else: - self._eqs[name] = dec(_parallel_applyfunc)( - self._eqs[name], self._simplify - ) - - def sym_names(self) -> list[str]: - """ - Returns a list of names of generated symbolic variables - - :return: - list of names - """ - return list(self._syms.keys()) - - def _derivative(self, eq: str, var: str, name: str = None) -> None: - """ - Creates a new symbolic variable according to a derivative - - :param eq: - name of the symbolic variable that defines the formula - - :param var: - name of the symbolic variable that defines the identifiers - with respect to which the derivatives are to be computed - - :param name: - name of resulting symbolic variable, default is ``d{eq}d{var}`` - """ - if not name: - name = f"d{eq}d{var}" - - ignore_chainrule = { - ("xdot", "p"): "w", # has generic implementation in c++ code - ("xdot", "x"): "w", # has generic implementation in c++ code - ("w", "w"): "tcl", # dtcldw = 0 - ("w", "x"): "tcl", # dtcldx = 0 - } - # automatically detect chainrule - chainvars = [ - cv - for cv in ["w", "tcl"] - if var_in_function_signature(eq, cv, self.is_ode()) - and cv not in self._lock_total_derivative - and var != cv - and min(self.sym(cv).shape) - and ( - (eq, var) not in ignore_chainrule - or ignore_chainrule[(eq, var)] != cv - ) - ] - if len(chainvars): - self._lock_total_derivative += chainvars - self._total_derivative(name, eq, chainvars, var) - for cv in chainvars: - self._lock_total_derivative.remove(cv) - return - - # partial derivative - sym_eq = self.eq(eq).transpose() if eq == "Jy" else self.eq(eq) - - sym_var = self.sym(var) - - derivative = smart_jacobian(sym_eq, sym_var) - - self._eqs[name] = derivative - - # compute recursion depth based on nilpotency of jacobian. computing - # nilpotency can be done more efficiently on numerical sparsity pattern - if name == "dwdw": - nonzeros = np.asarray( - derivative.applyfunc(lambda x: int(not x.is_zero)) - ).astype(np.int64) - recursion = nonzeros.copy() - if max(recursion.shape): - while recursion.max(): - recursion = recursion.dot(nonzeros) - self._w_recursion_depth += 1 - if self._w_recursion_depth > len(sym_eq): - raise RuntimeError( - "dwdw is not nilpotent. Something, somewhere went " - "terribly wrong. Please file a bug report at " - "https://github.com/AMICI-dev/AMICI/issues and " - "attach this model." - ) - - if name == "dydw" and not smart_is_zero_matrix(derivative): - dwdw = self.eq("dwdw") - # h(k) = d{eq}dw*dwdw^k* (k=1) - h = smart_multiply(derivative, dwdw) - while not smart_is_zero_matrix(h): - self._eqs[name] += h - # h(k+1) = d{eq}dw*dwdw^(k+1) = h(k)*dwdw - h = smart_multiply(h, dwdw) - - def _total_derivative( - self, - name: str, - eq: str, - chainvars: list[str], - var: str, - dydx_name: str = None, - dxdz_name: str = None, - ) -> None: - """ - Creates a new symbolic variable according to a total derivative - using the chain rule - - :param name: - name of resulting symbolic variable - - :param eq: - name of the symbolic variable that defines the formula - - :param chainvars: - names of the symbolic variable that define the - identifiers with respect to which the chain rules are applied - - :param var: - name of the symbolic variable that defines the identifiers - with respect to which the derivatives are to be computed - - :param dydx_name: - defines the name of the symbolic variable that - defines the derivative of the ``eq`` with respect to ``chainvar``, - default is ``d{eq}d{chainvar}`` - - :param dxdz_name: - defines the name of the symbolic variable that - defines the derivative of the ``chainvar`` with respect to ``var``, - default is d{chainvar}d{var} - """ - # compute total derivative according to chainrule - # Dydz = dydx*dxdz + dydz - - # initialize with partial derivative dydz without chain rule - self._eqs[name] = self.sym_or_eq(name, f"d{eq}d{var}") - if not isinstance(self._eqs[name], sp.Symbol): - # if not a Symbol, create a copy using sympy API - # NB deepcopy does not work safely, see sympy issue #7672 - self._eqs[name] = self._eqs[name].copy() - - for chainvar in chainvars: - if dydx_name is None: - dydx_name = f"d{eq}d{chainvar}" - if dxdz_name is None: - dxdz_name = f"d{chainvar}d{var}" - - dydx = self.sym_or_eq(name, dydx_name) - dxdz = self.sym_or_eq(name, dxdz_name) - # Save time for large models if one multiplicand is zero, - # which is not checked for by sympy - if not smart_is_zero_matrix(dydx) and not smart_is_zero_matrix( - dxdz - ): - dydx_times_dxdz = smart_multiply(dydx, dxdz) - if ( - dxdz.shape[1] == 1 - and self._eqs[name].shape[1] != dxdz.shape[1] - ): - for iz in range(self._eqs[name].shape[1]): - self._eqs[name][:, iz] += dydx_times_dxdz - else: - self._eqs[name] += dydx_times_dxdz - - def sym_or_eq(self, name: str, varname: str) -> sp.Matrix: - """ - Returns symbols or equations depending on whether a given - variable appears in the function signature or not. - - :param name: - name of function for which the signature should be checked - - :param varname: - name of the variable which should be contained in the - function signature - - :return: - the variable symbols if the variable is part of the signature and - the variable equations otherwise. - """ - # dwdx and dwdp will be dynamically computed and their ordering - # within a column may differ from the initialization of symbols here, - # so those are not safe to use. Not removing them from signature as - # this would break backwards compatibility. - if var_in_function_signature( - name, varname, self.is_ode() - ) and varname not in [ - "dwdx", - "dwdp", - ]: - return self.sym(varname) - else: - return self.eq(varname) - - def _multiplication( - self, - name: str, - x: str, - y: str, - transpose_x: bool | None = False, - sign: int | None = 1, - ): - """ - Creates a new symbolic variable according to a multiplication - - :param name: - name of resulting symbolic variable, default is ``d{eq}d{var}`` - - :param x: - name of the symbolic variable that defines the first factor - - :param y: - name of the symbolic variable that defines the second factor - - :param transpose_x: - indicates whether the first factor should be - transposed before multiplication - - :param sign: - defines the sign of the product, should be +1 or -1 - """ - if sign not in [-1, 1]: - raise TypeError(f"sign must be +1 or -1, was {sign}") - - variables = { - varname: self.sym(varname) - if var_in_function_signature(name, varname, self.is_ode()) - else self.eq(varname) - for varname in [x, y] - } - - xx = variables[x].transpose() if transpose_x else variables[x] - yy = variables[y] - - self._eqs[name] = sign * smart_multiply(xx, yy) - - def _equation_from_components( - self, name: str, components: list[ModelQuantity] - ) -> None: - """ - Generates the formulas of a symbolic variable from the attributes - - :param name: - name of resulting symbolic variable - - :param component: - name of the attribute - """ - self._eqs[name] = sp.Matrix([comp.get_val() for comp in components]) - - def get_conservation_laws(self) -> list[tuple[sp.Symbol, sp.Expr]]: - """Returns a list of states with conservation law set - - :return: - list of state identifiers - """ - return [ - (state.get_id(), state.get_x_rdata()) - for state in self.states() - if state.has_conservation_law() - ] - - def _generate_value(self, name: str) -> None: - """ - Generates the numeric values of a symbolic variable from value - prototypes - - :param name: - name of resulting symbolic variable - """ - if name in self._value_prototype: - components = self._value_prototype[name]() - else: - raise ValueError(f"No values for {name}") - - self._vals[name] = [comp.get_val() for comp in components] - - def _generate_name(self, name: str) -> None: - """ - Generates the names of a symbolic variable from variable prototypes or - equation prototypes - - :param name: - name of resulting symbolic variable - """ - if name in self._variable_prototype: - components = self._variable_prototype[name]() - elif name in self._equation_prototype: - components = self._equation_prototype[name]() - else: - raise ValueError(f"No names for {name}") - - self._names[name] = [comp.get_name() for comp in components] - - def state_has_fixed_parameter_initial_condition(self, ix: int) -> bool: - """ - Checks whether the state at specified index has a fixed parameter - initial condition - - :param ix: - state index - - :return: - boolean indicating if any of the initial condition free - variables is contained in the model constants - """ - ic = self.states()[ix].get_val() - if not isinstance(ic, sp.Basic): - return False - return any( - fp in (c.get_id() for c in self._constants) - for fp in ic.free_symbols - ) - - def state_has_conservation_law(self, ix: int) -> bool: - """ - Checks whether the state at specified index has a conservation - law set - - :param ix: - state index - - :return: - boolean indicating if conservation_law is not None - """ - return self.states()[ix].has_conservation_law() - - def get_solver_indices(self) -> dict[int, int]: - """ - Returns a mapping that maps rdata species indices to solver indices - - :return: - dictionary mapping rdata species indices to solver indices - """ - solver_index = {} - ix_solver = 0 - for ix in range(len(self.states())): - if self.state_has_conservation_law(ix): - continue - solver_index[ix] = ix_solver - ix_solver += 1 - return solver_index - - def state_is_constant(self, ix: int) -> bool: - """ - Checks whether the temporal derivative of the state is zero - - :param ix: - state index - - :return: - boolean indicating if constant over time - """ - state = self.states()[ix] - if isinstance(state, AlgebraicState): - return False - - return state.get_dt() == 0.0 - - def conservation_law_has_multispecies(self, tcl: ConservationLaw) -> bool: - """ - Checks whether a conservation law has multiple species or it just - defines one constant species - - :param tcl: - conservation law - - :return: - boolean indicating if conservation_law is not None - """ - state_set = set(self.sym("x_rdata")) - n_species = len(state_set.intersection(tcl.get_val().free_symbols)) - return n_species > 1 - - def _expr_is_time_dependent(self, expr: sp.Expr) -> bool: - """Determine whether an expression is time-dependent. - - :param expr: - The expression. - - :returns: - Whether the expression is time-dependent. - """ - # `expr.free_symbols` will be different to `self._states.keys()`, so - # it's easier to compare as `str`. - expr_syms = {str(sym) for sym in expr.free_symbols} - - # Check if the time variable is in the expression. - if "t" in expr_syms: - return True - - # Check if any time-dependent states are in the expression. - state_syms = [str(sym) for sym in self.states()] - return any( - not self.state_is_constant(state_syms.index(state)) - for state in expr_syms.intersection(state_syms) - ) - - def _get_unique_root( - self, - root_found: sp.Expr, - roots: list[Event], - ) -> sp.Symbol | None: - """ - Collects roots of Heaviside functions and events and stores them in - the roots list. It checks for redundancy to not store symbolically - equivalent root functions more than once. - - :param root_found: - equation of the root function - :param roots: - list of already known root functions with identifier - - :returns: - unique identifier for root, or ``None`` if the root is not - time-dependent - """ - if not self._expr_is_time_dependent(root_found): - return None - - for root in roots: - if sp.simplify(root_found - root.get_val()) == 0: - return root.get_id() - - # create an event for a new root function - root_symstr = f"Heaviside_{len(roots)}" - roots.append( - Event( - identifier=sp.Symbol(root_symstr), - name=root_symstr, - value=root_found, - state_update=None, - ) - ) - return roots[-1].get_id() - - def _collect_heaviside_roots( - self, - args: Sequence[sp.Expr], - ) -> list[sp.Expr]: - """ - Recursively checks an expression for the occurrence of Heaviside - functions and return all roots found - - :param args: - args attribute of the expanded expression - - :returns: - root functions that were extracted from Heaviside function - arguments - """ - root_funs = [] - for arg in args: - if arg.func == sp.Heaviside: - root_funs.append(arg.args[0]) - elif arg.has(sp.Heaviside): - root_funs.extend(self._collect_heaviside_roots(arg.args)) - - if not root_funs: - return [] - - # substitute 'w' expressions into root expressions now, to avoid - # rewriting 'root.cpp' and 'stau.cpp' headers - # to include 'w.h' - w_sorted = toposort_symbols( - dict( - zip( - [expr.get_id() for expr in self._expressions], - [expr.get_val() for expr in self._expressions], - ) - ) - ) - root_funs = [r.subs(w_sorted) for r in root_funs] - - return root_funs - - def _process_heavisides( - self, - dxdt: sp.Expr, - roots: list[Event], - ) -> sp.Expr: - """ - Parses the RHS of a state variable, checks for Heaviside functions, - collects unique roots functions that can be tracked by SUNDIALS and - replaces Heaviside Functions by amici helper variables that will be - updated based on SUNDIALS root tracking. - - :param dxdt: - right-hand side of state variable - :param roots: - list of known root functions with identifier - - :returns: - dxdt with Heaviside functions replaced by amici helper variables - """ - # track all the old Heaviside expressions in tmp_roots_old - # replace them later by the new expressions - heavisides = [] - # run through the expression tree and get the roots - tmp_roots_old = self._collect_heaviside_roots(dxdt.args) - for tmp_old in unique_preserve_order(tmp_roots_old): - # we want unique identifiers for the roots - tmp_new = self._get_unique_root(tmp_old, roots) - # `tmp_new` is None if the root is not time-dependent. - if tmp_new is None: - continue - # For Heavisides, we need to add the negative function as well - self._get_unique_root(sp.sympify(-tmp_old), roots) - heavisides.append((sp.Heaviside(tmp_old), tmp_new)) - - if heavisides: - # only apply subs if necessary - for heaviside_sympy, heaviside_amici in heavisides: - dxdt = dxdt.subs(heaviside_sympy, heaviside_amici) - - return dxdt - - class DEExporter: """ The DEExporter class generates AMICI C++ files for a model as diff --git a/python/sdist/amici/de_model.py b/python/sdist/amici/de_model.py new file mode 100644 index 0000000000..b69cc6ba68 --- /dev/null +++ b/python/sdist/amici/de_model.py @@ -0,0 +1,2276 @@ +"""Symbolic differential equation model.""" +from __future__ import annotations + +import contextlib +import copy +import itertools +import re +from itertools import chain +from typing import Callable, TYPE_CHECKING +from collections.abc import Sequence + +import numpy as np +import sympy as sp +from sympy import ImmutableDenseMatrix, MutableDenseMatrix + +from ._codegen.cxx_functions import ( + sparse_functions, + sensi_functions, + nobody_functions, + var_in_function_signature, +) +from .cxxcodeprinter import csc_matrix +from .de_model_components import ( + DifferentialState, + AlgebraicState, + AlgebraicEquation, + Observable, + EventObservable, + SigmaY, + SigmaZ, + Parameter, + Constant, + LogLikelihoodY, + LogLikelihoodZ, + LogLikelihoodRZ, + Expression, + ConservationLaw, + Event, + State, + ModelQuantity, +) +from .import_utils import ( + _default_simplify, + SBMLException, + toposort_symbols, + smart_subs_dict, + ObservableTransformation, + amici_time_symbol, + strip_pysb, + unique_preserve_order, +) +from .sympy_utils import ( + smart_jacobian, + smart_is_zero_matrix, + smart_multiply, + _parallel_applyfunc, +) +from .logging import get_logger, log_execution_time, set_log_level +import logging + +if TYPE_CHECKING: + from .splines import AbstractSpline + +logger = get_logger(__name__, logging.ERROR) + + +DERIVATIVE_PATTERN = re.compile(r"^d(x_rdata|xdot|\w+?)d(\w+?)(?:_explicit)?$") + + +class DEModel: + """ + Defines a Differential Equation as set of ModelQuantities. + This class provides general purpose interfaces to compute arbitrary + symbolic derivatives that are necessary for model simulation or + sensitivity computation. + + :ivar _differential_states: + list of differential state variables + + :ivar _algebraic_states: + list of algebraic state variables + + :ivar _observables: + list of observables + + :ivar _event_observables: + list of event observables + + :ivar _sigma_ys: + list of sigmas for observables + + :ivar _sigma_zs: + list of sigmas for event observables + + :ivar _parameters: + list of parameters + + :ivar _log_likelihood_ys: + list of loglikelihoods for observables + + :ivar _log_likelihood_zs: + list of loglikelihoods for event observables + + :ivar _log_likelihood_rzs: + list of loglikelihoods for event observable regularizations + + :ivar _expressions: + list of expressions instances + + :ivar _conservation_laws: + list of conservation laws + + :ivar _symboldim_funs: + define functions that compute model dimensions, these + are functions as the underlying symbolic expressions have not been + populated at compile time + + :ivar _eqs: + carries symbolic formulas of the symbolic variables of the model + + :ivar _sparseeqs: + carries linear list of all symbolic formulas for sparsified + variables + + :ivar _vals: + carries numeric values of symbolic identifiers of the symbolic + variables of the model + + :ivar _names: + carries the names of symbolic identifiers of the symbolic variables + of the model + + :ivar _syms: + carries symbolic identifiers of the symbolic variables of the + model + + :ivar _sparsesyms: + carries linear list of all symbolic identifiers for sparsified + variables + + :ivar _colptrs: + carries column pointers for sparsified variables. See + SUNMatrixContent_Sparse definition in ``sunmatrix/sunmatrix_sparse.h`` + + :ivar _rowvals: + carries row values for sparsified variables. See + SUNMatrixContent_Sparse definition in ``sunmatrix/sunmatrix_sparse.h`` + + :ivar _equation_prototype: + defines the attribute from which an equation should be generated via + list comprehension (see :meth:`OEModel._generate_equation`) + + :ivar _variable_prototype: + defines the attribute from which a variable should be generated via + list comprehension (see :meth:`DEModel._generate_symbol`) + + :ivar _value_prototype: + defines the attribute from which a value should be generated via + list comprehension (see :meth:`DEModel._generate_value`) + + :ivar _total_derivative_prototypes: + defines how a total derivative equation is computed for an equation, + key defines the name and values should be arguments for + :meth:`DEModel.totalDerivative` + + :ivar _lock_total_derivative: + add chainvariables to this set when computing total derivative from + a partial derivative call to enforce a partial derivative in the + next recursion. prevents infinite recursion + + :ivar _simplify: + If not None, this function will be used to simplify symbolic + derivative expressions. Receives sympy expressions as only argument. + To apply multiple simplifications, wrap them in a lambda expression. + + :ivar _x0_fixedParameters_idx: + Index list of subset of states for which x0_fixedParameters was + computed + + :ivar _w_recursion_depth: + recursion depth in w, quantified as nilpotency of dwdw + + :ivar _has_quadratic_nllh: + whether all observables have a gaussian noise model, i.e. whether + res and FIM make sense. + + :ivar _static_indices: + dict of lists list of indices of static variables for different + model entities. + + :ivar _z2event: + list of event indices for each event observable + """ + + def __init__( + self, + verbose: bool | int | None = False, + simplify: Callable | None = _default_simplify, + cache_simplify: bool = False, + ): + """ + Create a new DEModel instance. + + :param verbose: + verbosity level for logging, True/False default to + ``logging.DEBUG``/``logging.ERROR`` + + :param simplify: + see :meth:`DEModel._simplify` + + :param cache_simplify: + Whether to cache calls to the simplify method. Can e.g. decrease + import times for models with events. + """ + self._differential_states: list[DifferentialState] = [] + self._algebraic_states: list[AlgebraicState] = [] + self._algebraic_equations: list[AlgebraicEquation] = [] + self._observables: list[Observable] = [] + self._event_observables: list[EventObservable] = [] + self._sigma_ys: list[SigmaY] = [] + self._sigma_zs: list[SigmaZ] = [] + self._parameters: list[Parameter] = [] + self._constants: list[Constant] = [] + self._log_likelihood_ys: list[LogLikelihoodY] = [] + self._log_likelihood_zs: list[LogLikelihoodZ] = [] + self._log_likelihood_rzs: list[LogLikelihoodRZ] = [] + self._expressions: list[Expression] = [] + self._conservation_laws: list[ConservationLaw] = [] + self._events: list[Event] = [] + self._splines: list[AbstractSpline] = [] + self._symboldim_funs: dict[str, Callable[[], int]] = { + "sx": self.num_states_solver, + "v": self.num_states_solver, + "vB": self.num_states_solver, + "xB": self.num_states_solver, + "sigmay": self.num_obs, + "sigmaz": self.num_eventobs, + } + self._eqs: dict[ + str, + (sp.Matrix | sp.SparseMatrix | list[sp.Matrix | sp.SparseMatrix]), + ] = dict() + self._sparseeqs: dict[str, sp.Matrix | list[sp.Matrix]] = dict() + self._vals: dict[str, list[sp.Expr]] = dict() + self._names: dict[str, list[str]] = dict() + self._syms: dict[str, sp.Matrix | list[sp.Matrix]] = dict() + self._sparsesyms: dict[str, list[str] | list[list[str]]] = dict() + self._colptrs: dict[str, list[int] | list[list[int]]] = dict() + self._rowvals: dict[str, list[int] | list[list[int]]] = dict() + + self._equation_prototype: dict[str, Callable] = { + "total_cl": self.conservation_laws, + "x0": self.states, + "y": self.observables, + "Jy": self.log_likelihood_ys, + "Jz": self.log_likelihood_zs, + "Jrz": self.log_likelihood_rzs, + "w": self.expressions, + "root": self.events, + "sigmay": self.sigma_ys, + "sigmaz": self.sigma_zs, + } + self._variable_prototype: dict[str, Callable] = { + "tcl": self.conservation_laws, + "x_rdata": self.states, + "y": self.observables, + "z": self.event_observables, + "p": self.parameters, + "k": self.constants, + "w": self.expressions, + "sigmay": self.sigma_ys, + "sigmaz": self.sigma_zs, + "h": self.events, + } + self._value_prototype: dict[str, Callable] = { + "p": self.parameters, + "k": self.constants, + } + self._total_derivative_prototypes: dict[ + str, dict[str, str | list[str]] + ] = { + "sroot": { + "eq": "root", + "chainvars": ["x"], + "var": "p", + "dxdz_name": "sx", + }, + } + + self._lock_total_derivative: list[str] = list() + self._simplify: Callable = simplify + if cache_simplify and simplify is not None: + + def cached_simplify( + expr: sp.Expr, + _simplified: dict[str, sp.Expr] = {}, # noqa B006 + _simplify: Callable = simplify, + ) -> sp.Expr: + """Speed up expression simplification with caching. + + NB: This can decrease model import times for models that have + many repeated expressions during C++ file generation. + For example, this can be useful for models with events. + However, for other models, this may increase model import + times. + + :param expr: + The SymPy expression. + :param _simplified: + The cache. + :param _simplify: + The simplification method. + + :return: + The simplified expression. + """ + expr_str = repr(expr) + if expr_str not in _simplified: + _simplified[expr_str] = _simplify(expr) + return _simplified[expr_str] + + self._simplify = cached_simplify + self._x0_fixedParameters_idx: None | Sequence[int] + self._w_recursion_depth: int = 0 + self._has_quadratic_nllh: bool = True + set_log_level(logger, verbose) + + self._static_indices: dict[str, list[int]] = {} + + def differential_states(self) -> list[DifferentialState]: + """Get all differential states.""" + return self._differential_states + + def algebraic_states(self) -> list[AlgebraicState]: + """Get all algebraic states.""" + return self._algebraic_states + + def observables(self) -> list[Observable]: + """Get all observables.""" + return self._observables + + def parameters(self) -> list[Parameter]: + """Get all parameters.""" + return self._parameters + + def constants(self) -> list[Constant]: + """Get all constants.""" + return self._constants + + def expressions(self) -> list[Expression]: + """Get all expressions.""" + return self._expressions + + def events(self) -> list[Event]: + """Get all events.""" + return self._events + + def event_observables(self) -> list[EventObservable]: + """Get all event observables.""" + return self._event_observables + + def sigma_ys(self) -> list[SigmaY]: + """Get all observable sigmas.""" + return self._sigma_ys + + def sigma_zs(self) -> list[SigmaZ]: + """Get all event observable sigmas.""" + return self._sigma_zs + + def conservation_laws(self) -> list[ConservationLaw]: + """Get all conservation laws.""" + return self._conservation_laws + + def log_likelihood_ys(self) -> list[LogLikelihoodY]: + """Get all observable log likelihoodss.""" + return self._log_likelihood_ys + + def log_likelihood_zs(self) -> list[LogLikelihoodZ]: + """Get all event observable log likelihoods.""" + return self._log_likelihood_zs + + def log_likelihood_rzs(self) -> list[LogLikelihoodRZ]: + """Get all event observable regularization log likelihoods.""" + return self._log_likelihood_rzs + + def is_ode(self) -> bool: + """Check if model is ODE model.""" + return len(self._algebraic_equations) == 0 + + def states(self) -> list[State]: + """Get all states.""" + return self._differential_states + self._algebraic_states + + def _process_sbml_rate_of(self) -> None: + """Substitute any SBML-rateOf constructs in the model equations""" + rate_of_func = sp.core.function.UndefinedFunction("rateOf") + species_sym_to_xdot = dict(zip(self.sym("x"), self.sym("xdot"))) + species_sym_to_idx = {x: i for i, x in enumerate(self.sym("x"))} + + def get_rate(symbol: sp.Symbol): + """Get rate of change of the given symbol""" + if symbol.find(rate_of_func): + raise SBMLException("Nesting rateOf() is not allowed.") + + # Replace all rateOf(some_species) by their respective xdot equation + with contextlib.suppress(KeyError): + return self._eqs["xdot"][species_sym_to_idx[symbol]] + + # For anything other than a state, rateOf(.) is 0 or invalid + return 0 + + # replace rateOf-instances in xdot by xdot symbols + made_substitutions = False + for i_state in range(len(self.eq("xdot"))): + if rate_ofs := self._eqs["xdot"][i_state].find(rate_of_func): + self._eqs["xdot"][i_state] = self._eqs["xdot"][i_state].subs( + { + # either the rateOf argument is a state, or it's 0 + rate_of: species_sym_to_xdot.get(rate_of.args[0], 0) + for rate_of in rate_ofs + } + ) + made_substitutions = True + + if made_substitutions: + # substitute in topological order + subs = toposort_symbols( + dict(zip(self.sym("xdot"), self.eq("xdot"))) + ) + self._eqs["xdot"] = smart_subs_dict(self.eq("xdot"), subs) + + # replace rateOf-instances in x0 by xdot equation + for i_state in range(len(self.eq("x0"))): + if rate_ofs := self._eqs["x0"][i_state].find(rate_of_func): + self._eqs["x0"][i_state] = self._eqs["x0"][i_state].subs( + { + rate_of: get_rate(rate_of.args[0]) + for rate_of in rate_ofs + } + ) + + # replace rateOf-instances in w by xdot equation + # here we may need toposort, as xdot may depend on w + made_substitutions = False + for i_expr in range(len(self.eq("w"))): + if rate_ofs := self._eqs["w"][i_expr].find(rate_of_func): + self._eqs["w"][i_expr] = self._eqs["w"][i_expr].subs( + { + rate_of: get_rate(rate_of.args[0]) + for rate_of in rate_ofs + } + ) + made_substitutions = True + + if made_substitutions: + # Sort expressions in self._expressions, w symbols, and w equations + # in topological order. Ideally, this would already happen before + # adding the expressions to the model, but at that point, we don't + # have access to xdot yet. + # NOTE: elsewhere, conservations law expressions are expected to + # occur before any other w expressions, so we must maintain their + # position + # toposort everything but conservation law expressions, + # then prepend conservation laws + w_sorted = toposort_symbols( + dict( + zip( + self.sym("w")[self.num_cons_law() :, :], + self.eq("w")[self.num_cons_law() :, :], + ) + ) + ) + w_sorted = ( + dict( + zip( + self.sym("w")[: self.num_cons_law(), :], + self.eq("w")[: self.num_cons_law(), :], + ) + ) + | w_sorted + ) + old_syms = tuple(self._syms["w"]) + topo_expr_syms = tuple(w_sorted.keys()) + new_order = [old_syms.index(s) for s in topo_expr_syms] + self._expressions = [self._expressions[i] for i in new_order] + self._syms["w"] = sp.Matrix(topo_expr_syms) + self._eqs["w"] = sp.Matrix(list(w_sorted.values())) + + for component in chain( + self.observables(), + self.events(), + self._algebraic_equations, + ): + if rate_ofs := component.get_val().find(rate_of_func): + if isinstance(component, Event): + # TODO froot(...) can currently not depend on `w`, so this substitution fails for non-zero rates + # see, e.g., sbml test case 01293 + raise SBMLException( + "AMICI does currently not support rateOf(.) inside event trigger functions." + ) + + if isinstance(component, AlgebraicEquation): + # TODO IDACalcIC fails with + # "The linesearch algorithm failed: step too small or too many backtracks." + # see, e.g., sbml test case 01482 + raise SBMLException( + "AMICI does currently not support rateOf(.) inside AlgebraicRules." + ) + + component.set_val( + component.get_val().subs( + { + rate_of: get_rate(rate_of.args[0]) + for rate_of in rate_ofs + } + ) + ) + + for event in self.events(): + if event._state_update is None: + continue + + for i_state in range(len(event._state_update)): + if rate_ofs := event._state_update[i_state].find(rate_of_func): + raise SBMLException( + "AMICI does currently not support rateOf(.) inside event state updates." + ) + # TODO here we need xdot sym, not eqs + # event._state_update[i_state] = event._state_update[i_state].subs( + # {rate_of: get_rate(rate_of.args[0]) for rate_of in rate_ofs} + # ) + + def add_component( + self, component: ModelQuantity, insert_first: bool | None = False + ) -> None: + """ + Adds a new ModelQuantity to the model. + + :param component: + model quantity to be added + + :param insert_first: + whether to add quantity first or last, relevant when components + may refer to other components of the same type. + """ + if type(component) not in { + Observable, + Expression, + Parameter, + Constant, + DifferentialState, + AlgebraicState, + AlgebraicEquation, + LogLikelihoodY, + LogLikelihoodZ, + LogLikelihoodRZ, + SigmaY, + SigmaZ, + ConservationLaw, + Event, + EventObservable, + }: + raise ValueError(f"Invalid component type {type(component)}") + + component_list = getattr( + self, + "_" + + "_".join( + s.lower() + for s in re.split(r"([A-Z][^A-Z]+)", type(component).__name__) + if s + ) + + "s", + ) + if insert_first: + component_list.insert(0, component) + else: + component_list.append(component) + + def add_conservation_law( + self, + state: sp.Symbol, + total_abundance: sp.Symbol, + coefficients: dict[sp.Symbol, sp.Expr], + ) -> None: + r""" + Adds a new conservation law to the model. A conservation law is defined + by the conserved quantity :math:`T = \sum_i(a_i * x_i)`, where + :math:`a_i` are coefficients and :math:`x_i` are different state + variables. + + :param state: + symbolic identifier of the state that should be replaced by + the conservation law (:math:`x_j`) + + :param total_abundance: + symbolic identifier of the total abundance (:math:`T/a_j`) + + :param coefficients: + Dictionary of coefficients {x_i: a_i} + """ + try: + ix = next( + filter( + lambda is_s: is_s[1].get_id() == state, + enumerate(self._differential_states), + ) + )[0] + except StopIteration: + raise ValueError( + f"Specified state {state} was not found in the " + f"model states." + ) + + state_id = self._differential_states[ix].get_id() + + # \sum_{i≠j}(a_i * x_i)/a_j + target_expression = ( + sp.Add( + *( + c_i * x_i + for x_i, c_i in coefficients.items() + if x_i != state + ) + ) + / coefficients[state] + ) + + # x_j = T/a_j - \sum_{i≠j}(a_i * x_i)/a_j + state_expr = total_abundance - target_expression + + # T/a_j = \sum_{i≠j}(a_i * x_i)/a_j + x_j + abundance_expr = target_expression + state_id + + self.add_component( + Expression(state_id, str(state_id), state_expr), insert_first=True + ) + + cl = ConservationLaw( + total_abundance, + f"total_{state_id}", + abundance_expr, + coefficients, + state_id, + ) + + self.add_component(cl) + self._differential_states[ix].set_conservation_law(cl) + + def add_spline(self, spline: AbstractSpline, spline_expr: sp.Expr) -> None: + """Add a spline to the model. + + :param spline: + Spline instance to be added + :param spline_expr: + Sympy function representation of `spline` from + ``spline.ode_model_symbol()``. + """ + self._splines.append(spline) + self.add_component( + Expression( + identifier=spline.sbml_id, + name=str(spline.sbml_id), + value=spline_expr, + ) + ) + + def get_observable_transformations(self) -> list[ObservableTransformation]: + """ + List of observable transformations + + :return: + list of transformations + """ + return [obs.trafo for obs in self._observables] + + def num_states_rdata(self) -> int: + """ + Number of states. + + :return: + number of state variable symbols + """ + return len(self.sym("x_rdata")) + + def num_states_solver(self) -> int: + """ + Number of states after applying conservation laws. + + :return: + number of state variable symbols + """ + return len(self.sym("x")) + + def num_cons_law(self) -> int: + """ + Number of conservation laws. + + :return: + number of conservation laws + """ + return self.num_states_rdata() - self.num_states_solver() + + def num_state_reinits(self) -> int: + """ + Number of solver states which would be reinitialized after + preequilibration + + :return: + number of state variable symbols with reinitialization + """ + reinit_states = self.eq("x0_fixedParameters") + solver_states = self.eq("x_solver") + return sum(ix in solver_states for ix in reinit_states) + + def num_obs(self) -> int: + """ + Number of Observables. + + :return: + number of observable symbols + """ + return len(self.sym("y")) + + def num_eventobs(self) -> int: + """ + Number of Event Observables. + + :return: + number of event observable symbols + """ + return len(self.sym("z")) + + def num_const(self) -> int: + """ + Number of Constants. + + :return: + number of constant symbols + """ + return len(self.sym("k")) + + def num_par(self) -> int: + """ + Number of Parameters. + + :return: + number of parameter symbols + """ + return len(self.sym("p")) + + def num_expr(self) -> int: + """ + Number of Expressions. + + :return: + number of expression symbols + """ + return len(self.sym("w")) + + def num_events(self) -> int: + """ + Total number of Events (those for which root-functions are added and those without). + + :return: + number of events + """ + return len(self.sym("h")) + + def num_events_solver(self) -> int: + """ + Number of Events. + + :return: + number of event symbols (length of the root vector in AMICI) + """ + return sum( + not event.triggers_at_fixed_timepoint() for event in self.events() + ) + + def sym(self, name: str) -> sp.Matrix: + """ + Returns (and constructs if necessary) the identifiers for a symbolic + entity. + + :param name: + name of the symbolic variable + + :return: + matrix of symbolic identifiers + """ + if name not in self._syms: + self._generate_symbol(name) + + return self._syms[name] + + def sparsesym(self, name: str, force_generate: bool = True) -> list[str]: + """ + Returns (and constructs if necessary) the sparsified identifiers for + a sparsified symbolic variable. + + :param name: + name of the symbolic variable + + :param force_generate: + whether the symbols should be generated if not available + + :return: + linearized Matrix containing the symbolic identifiers + """ + if name not in sparse_functions: + raise ValueError(f"{name} is not marked as sparse") + if name not in self._sparsesyms and force_generate: + self._generate_sparse_symbol(name) + return self._sparsesyms.get(name, []) + + def eq(self, name: str) -> sp.Matrix: + """ + Returns (and constructs if necessary) the formulas for a symbolic + entity. + + :param name: + name of the symbolic variable + + :return: + matrix of symbolic formulas + """ + + if name not in self._eqs: + dec = log_execution_time(f"computing {name}", logger) + dec(self._compute_equation)(name) + return self._eqs[name] + + def sparseeq(self, name) -> sp.Matrix: + """ + Returns (and constructs if necessary) the sparsified formulas for a + sparsified symbolic variable. + + :param name: + name of the symbolic variable + + :return: + linearized matrix containing the symbolic formulas + """ + if name not in sparse_functions: + raise ValueError(f"{name} is not marked as sparse") + if name not in self._sparseeqs: + self._generate_sparse_symbol(name) + return self._sparseeqs[name] + + def colptrs(self, name: str) -> list[sp.Number] | list[list[sp.Number]]: + """ + Returns (and constructs if necessary) the column pointers for + a sparsified symbolic variable. + + :param name: + name of the symbolic variable + + :return: + list containing the column pointers + """ + if name not in sparse_functions: + raise ValueError(f"{name} is not marked as sparse") + if name not in self._sparseeqs: + self._generate_sparse_symbol(name) + return self._colptrs[name] + + def rowvals(self, name: str) -> list[sp.Number] | list[list[sp.Number]]: + """ + Returns (and constructs if necessary) the row values for a + sparsified symbolic variable. + + :param name: + name of the symbolic variable + + :return: + list containing the row values + """ + if name not in sparse_functions: + raise ValueError(f"{name} is not marked as sparse") + if name not in self._sparseeqs: + self._generate_sparse_symbol(name) + return self._rowvals[name] + + def val(self, name: str) -> list[sp.Number]: + """ + Returns (and constructs if necessary) the numeric values of a + symbolic entity + + :param name: + name of the symbolic variable + + :return: + list containing the numeric values + """ + if name not in self._vals: + self._generate_value(name) + return self._vals[name] + + def name(self, name: str) -> list[str]: + """ + Returns (and constructs if necessary) the names of a symbolic + variable + + :param name: + name of the symbolic variable + + :return: + list of names + """ + if name not in self._names: + self._generate_name(name) + return self._names[name] + + def free_symbols(self) -> set[sp.Basic]: + """ + Returns list of free symbols that appear in RHS and initial + conditions. + """ + return set( + chain.from_iterable( + state.get_free_symbols() for state in self.states() + ) + ) + + def static_indices(self, name: str) -> list[int]: + """ + Returns the indices of static expressions in the given model entity. + + Static expressions are those that do not depend on time, + neither directly nor indirectly. + + :param name: Name of the model entity. + :return: List of indices of static expressions. + """ + # already computed? + if (res := self._static_indices.get(name)) is not None: + return res + + if name == "w": + dwdx = self.sym("dwdx") + dwdw = self.sym("dwdw") + w = self.eq("w") + + # Check for direct (via `t`) or indirect (via `x`, `h`, or splines) + # time dependency. + # To avoid lengthy symbolic computations, we only check if we have + # any non-zeros in hierarchy. We currently neglect the case where + # different hierarchy levels may cancel out. Treating a static + # expression as dynamic in such rare cases shouldn't be a problem. + dynamic_dependency = np.asarray( + dwdx.applyfunc(lambda x: int(not x.is_zero)) + ).astype(np.int64) + # to check for other time-dependence, we add a column to the dwdx + # matrix + dynamic_syms = [ + # FIXME: see spline comment below + # *self.sym("spl"), + *self.sym("h"), + amici_time_symbol, + ] + dynamic_dependency = np.hstack( + ( + dynamic_dependency, + np.array( + [ + expr.has(*dynamic_syms) + # FIXME: the current spline implementation is a giant pita + # currently, the splines occur in the form of sympy functions, e.g.: + # AmiciSpline(y0, time, y0_3, y0_1) + # AmiciSplineSensitivity(y0, time, y0_1, y0_3, y0_1) + # until it uses the proper self.sym("spl") / self.sym("sspl") + # symbols, which will require quite some refactoring, + # we just do dumb string checks :| + or ( + bool(self._splines) + and "AmiciSpline" in str(expr) + ) + for expr in w + ] + )[:, np.newaxis], + ) + ) + + nonzero_dwdw = np.asarray( + dwdw.applyfunc(lambda x: int(not x.is_zero)) + ).astype(np.int64) + + # `w` is made up an expression hierarchy. Any given entry is only + # static if all its dependencies are static. Here, we unravel + # the hierarchical structure of `w`. + # If for an entry in `w`, the row sum of the intermediate products + # is 0 across all levels, the expression is static. + tmp = dynamic_dependency + res = np.sum(tmp, axis=1) + while np.any(tmp != 0): + tmp = nonzero_dwdw.dot(tmp) + res += np.sum(tmp, axis=1) + self._static_indices[name] = ( + np.argwhere(res == 0).flatten().tolist() + ) + + return self._static_indices[name] + + if name in ("dwdw", "dwdx", "dwdp"): + static_indices_w = set(self.static_indices("w")) + dynamic_syms = [ + *( + sym + for i, sym in enumerate(self.sym("w")) + if i not in static_indices_w + ), + amici_time_symbol, + *self.sym("x"), + *self.sym("h"), + # FIXME see spline comment above + # *(self.sym("spl") if name in ("dwdw", "dwdx") else ()), + # *(self.sym("sspl") if name == "dwdp" else ()), + ] + dynamic_syms = sp.Matrix(dynamic_syms) + rowvals = self.rowvals(name) + sparseeq = self.sparseeq(name) + + # collect the indices of static expressions of dwd* from the list + # of non-zeros entries of the sparse matrix + self._static_indices[name] = [ + i + for i, (expr, row_idx) in enumerate(zip(sparseeq, rowvals)) + # derivative of a static expression is static + if row_idx in static_indices_w + # constant expressions + or expr.is_Number + # check for dependencies on non-static entities + or ( + # FIXME see spline comment above + # (check str before diff, as diff will fail on spline functions) + ( + # splines: non-static + not self._splines or "AmiciSpline" not in str(expr) + ) + and ( + # If the expression contains dynamic symbols, it might + # still be static. However, checking the derivative + # is currently too expensive, and we rather accept + # false negatives. + not expr.has(*dynamic_syms) + # or all( + # expr.diff(dyn_sym).is_zero + # for dyn_sym in dynamic_syms + # ) + ) + ) + ] + return self._static_indices[name] + + raise NotImplementedError(name) + + def dynamic_indices(self, name: str) -> list[int]: + """ + Return the indices of dynamic expressions in the given model entity. + + :param name: Name of the model entity. + :return: List of indices of dynamic expressions. + """ + static_idxs = set(self.static_indices(name)) + length = len( + self.sparsesym(name) + if name in sparse_functions + else self.sym(name) + ) + return [i for i in range(length) if i not in static_idxs] + + def _generate_symbol(self, name: str) -> None: + """ + Generates the symbolic identifiers for a symbolic variable + + :param name: + name of the symbolic variable + """ + if name in self._variable_prototype: + components = self._variable_prototype[name]() + self._syms[name] = sp.Matrix( + [comp.get_id() for comp in components] + ) + if name == "y": + self._syms["my"] = sp.Matrix( + [comp.get_measurement_symbol() for comp in components] + ) + if name == "z": + self._syms["mz"] = sp.Matrix( + [comp.get_measurement_symbol() for comp in components] + ) + self._syms["rz"] = sp.Matrix( + [comp.get_regularization_symbol() for comp in components] + ) + return + elif name == "x": + self._syms[name] = sp.Matrix( + [ + state.get_id() + for state in self.states() + if not state.has_conservation_law() + ] + ) + return + elif name == "xdot": + self._syms[name] = sp.Matrix( + [ + f"d{x.get_id()}dt" if self.is_ode() else f"de_{ix}" + for ix, x in enumerate(self._differential_states) + if not x.has_conservation_law() + ] + + [f"ae_{ix}" for ix in range(len(self._algebraic_equations))] + ) + return + elif name == "dx": + self._syms[name] = sp.Matrix( + [ + f"d{state.get_id()}dt" + for state in self.states() + if not state.has_conservation_law() + ] + ) + return + elif name == "sx0": + self._syms[name] = sp.Matrix( + [ + f"s{state.get_id()}_0" + for state in self.states() + if not state.has_conservation_law() + ] + ) + return + elif name == "sx_rdata": + self._syms[name] = sp.Matrix( + [f"sx_rdata_{i}" for i in range(len(self.states()))] + ) + return + elif name == "dtcldp": + # check, whether the CL consists of only one state. Then, + # sensitivities drop out, otherwise generate symbols + self._syms[name] = sp.Matrix( + [ + [ + sp.Symbol( + f"s{strip_pysb(tcl.get_id())}__" + f"{strip_pysb(par.get_id())}", + real=True, + ) + for par in self._parameters + ] + if self.conservation_law_has_multispecies(tcl) + else [0] * self.num_par() + for tcl in self._conservation_laws + ] + ) + return + elif name == "xdot_old": + length = len(self.eq("xdot")) + elif name in sparse_functions: + self._generate_sparse_symbol(name) + return + elif name in self._symboldim_funs: + length = self._symboldim_funs[name]() + elif name == "stau": + length = self.eq(name)[0].shape[1] + elif name in sensi_functions: + length = self.eq(name).shape[0] + elif name == "spl": + # placeholders for the numeric spline values. + # Need to create symbols + self._syms[name] = sp.Matrix( + [[f"spl_{isp}" for isp in range(len(self._splines))]] + ) + return + elif name == "sspl": + # placeholders for spline sensitivities. Need to create symbols + self._syms[name] = sp.Matrix( + [ + [f"sspl_{isp}_{ip}" for ip in range(len(self._syms["p"]))] + for isp in range(len(self._splines)) + ] + ) + return + else: + length = len(self.eq(name)) + self._syms[name] = sp.Matrix( + [ + sp.Symbol(f'{name}{0 if name == "stau" else i}', real=True) + for i in range(length) + ] + ) + + def generate_basic_variables(self) -> None: + """ + Generates the symbolic identifiers for all variables in + ``DEModel._variable_prototype`` + """ + # We need to process events and Heaviside functions in the ``DEModel`, + # before adding it to DEExporter + self.parse_events() + + for var in self._variable_prototype: + if var not in self._syms: + self._generate_symbol(var) + # symbols for spline values need to be created in addition + for var in ["spl", "sspl"]: + self._generate_symbol(var) + + self._generate_symbol("x") + + def parse_events(self) -> None: + """ + This function checks the right-hand side for roots of Heaviside + functions or events, collects the roots, removes redundant roots, + and replaces the formulae of the found roots by identifiers of AMICI's + Heaviside function implementation in the right-hand side + """ + # Track all roots functions in the right-hand side + roots = copy.deepcopy(self._events) + for state in self._differential_states: + state.set_dt(self._process_heavisides(state.get_dt(), roots)) + + for expr in self._expressions: + expr.set_val(self._process_heavisides(expr.get_val(), roots)) + + # remove all possible Heavisides from roots, which may arise from + # the substitution of `'w'` in `_collect_heaviside_roots` + for root in roots: + root.set_val(self._process_heavisides(root.get_val(), roots)) + + # Now add the found roots to the model components + for root in roots: + # skip roots of SBML events, as these have already been added + if root in self._events: + continue + # add roots of heaviside functions + self.add_component(root) + + # re-order events - first those that require root tracking, then the others + self._events = list( + chain( + itertools.filterfalse( + Event.triggers_at_fixed_timepoint, self._events + ), + filter(Event.triggers_at_fixed_timepoint, self._events), + ) + ) + + def get_appearance_counts(self, idxs: list[int]) -> list[int]: + """ + Counts how often a state appears in the time derivative of + another state and expressions for a subset of states + + :param idxs: + list of state indices for which counts are to be computed + + :return: + list of counts for the states ordered according to the provided + indices + """ + free_symbols_dt = list( + itertools.chain.from_iterable( + [str(symbol) for symbol in state.get_dt().free_symbols] + for state in self.states() + ) + ) + + free_symbols_expr = list( + itertools.chain.from_iterable( + [str(symbol) for symbol in expr.get_val().free_symbols] + for expr in self._expressions + ) + ) + + return [ + free_symbols_dt.count(str(self._differential_states[idx].get_id())) + + free_symbols_expr.count( + str(self._differential_states[idx].get_id()) + ) + for idx in idxs + ] + + def _generate_sparse_symbol(self, name: str) -> None: + """ + Generates the sparse symbolic identifiers, symbolic identifiers, + sparse equations, column pointers and row values for a symbolic + variable + + :param name: + name of the symbolic variable + """ + matrix = self.eq(name) + + if match_deriv := DERIVATIVE_PATTERN.match(name): + eq = match_deriv[1] + var = match_deriv[2] + + rownames = self.sym(eq) + colnames = self.sym(var) + + if name == "dJydy": + # One entry per y-slice + self._colptrs[name] = [] + self._rowvals[name] = [] + self._sparseeqs[name] = [] + self._sparsesyms[name] = [] + self._syms[name] = [] + + for iy in range(self.num_obs()): + ( + symbol_col_ptrs, + symbol_row_vals, + sparse_list, + symbol_list, + sparse_matrix, + ) = csc_matrix( + matrix[iy, :], + rownames=rownames, + colnames=colnames, + identifier=iy, + ) + self._colptrs[name].append(symbol_col_ptrs) + self._rowvals[name].append(symbol_row_vals) + self._sparseeqs[name].append(sparse_list) + self._sparsesyms[name].append(symbol_list) + self._syms[name].append(sparse_matrix) + else: + ( + symbol_col_ptrs, + symbol_row_vals, + sparse_list, + symbol_list, + sparse_matrix, + ) = csc_matrix( + matrix, + rownames=rownames, + colnames=colnames, + pattern_only=name in nobody_functions, + ) + + self._colptrs[name] = symbol_col_ptrs + self._rowvals[name] = symbol_row_vals + self._sparseeqs[name] = sparse_list + self._sparsesyms[name] = symbol_list + self._syms[name] = sparse_matrix + + def _compute_equation(self, name: str) -> None: + """ + Computes the symbolic formula for a symbolic variable + + :param name: + name of the symbolic variable + """ + # replacement ensures that we don't have to adapt name in abstract + # model and keep backwards compatibility with matlab + match_deriv = DERIVATIVE_PATTERN.match( + re.sub(r"dJ(y|z|rz)dsigma", r"dJ\1dsigma\1", name) + .replace("sigmarz", "sigmaz") + .replace("dJrzdz", "dJrzdrz") + ) + time_symbol = sp.Matrix([amici_time_symbol]) + + if name in self._equation_prototype: + self._equation_from_components( + name, self._equation_prototype[name]() + ) + + elif name in self._total_derivative_prototypes: + args = self._total_derivative_prototypes[name] + args["name"] = name + self._lock_total_derivative += args["chainvars"] + self._total_derivative(**args) + for cv in args["chainvars"]: + self._lock_total_derivative.remove(cv) + + elif name == "xdot": + if self.is_ode(): + self._eqs[name] = sp.Matrix( + [ + state.get_dt() + for state in self._differential_states + if not state.has_conservation_law() + ] + ) + else: + self._eqs[name] = sp.Matrix( + [ + x.get_dt() - dx + for x, dx in zip( + ( + s + for s in self._differential_states + if not s.has_conservation_law() + ), + self.sym("dx"), + ) + ] + + [eq.get_val() for eq in self._algebraic_equations] + ) + + elif name == "x_rdata": + self._eqs[name] = sp.Matrix( + [state.get_x_rdata() for state in self.states()] + ) + + elif name == "x_solver": + self._eqs[name] = sp.Matrix( + [ + state.get_id() + for state in self.states() + if not state.has_conservation_law() + ] + ) + + elif name == "sx_solver": + self._eqs[name] = sp.Matrix( + [ + self.sym("sx_rdata")[ix] + for ix, state in enumerate(self.states()) + if not state.has_conservation_law() + ] + ) + + elif name == "sx0": + self._derivative(name[1:], "p", name=name) + + elif name == "sx0_fixedParameters": + # deltax = -x+x0_fixedParameters if x0_fixedParameters>0 else 0 + # deltasx = -sx+dx0_fixed_parametersdx*sx+dx0_fixedParametersdp + # if x0_fixedParameters>0 else 0 + # sx0_fixedParameters = sx+deltasx = + # dx0_fixed_parametersdx*sx+dx0_fixedParametersdp + self._eqs[name] = smart_jacobian( + self.eq("x0_fixedParameters"), self.sym("p") + ) + + dx0_fixed_parametersdx = smart_jacobian( + self.eq("x0_fixedParameters"), self.sym("x") + ) + + if not smart_is_zero_matrix(dx0_fixed_parametersdx): + if isinstance(self._eqs[name], ImmutableDenseMatrix): + self._eqs[name] = MutableDenseMatrix(self._eqs[name]) + tmp = smart_multiply(dx0_fixed_parametersdx, self.sym("sx0")) + for ip in range(self._eqs[name].shape[1]): + self._eqs[name][:, ip] += tmp + + elif name == "x0_fixedParameters": + k = self.sym("k") + self._x0_fixedParameters_idx = [ + ix + for ix, eq in enumerate(self.eq("x0")) + if any(sym in eq.free_symbols for sym in k) + ] + eq = self.eq("x0") + self._eqs[name] = sp.Matrix( + [eq[ix] for ix in self._x0_fixedParameters_idx] + ) + + elif name == "dtotal_cldx_rdata": + x_rdata = self.sym("x_rdata") + self._eqs[name] = sp.Matrix( + [ + [cl.get_ncoeff(xr) for xr in x_rdata] + for cl in self._conservation_laws + ] + ) + + elif name == "dtcldx": + # this is always zero + self._eqs[name] = sp.zeros( + self.num_cons_law(), self.num_states_solver() + ) + + elif name == "dtcldp": + # force symbols + self._eqs[name] = self.sym(name) + + elif name == "dx_rdatadx_solver": + if self.num_cons_law(): + x_solver = self.sym("x") + self._eqs[name] = sp.Matrix( + [ + [state.get_dx_rdata_dx_solver(xs) for xs in x_solver] + for state in self.states() + ] + ) + else: + # so far, dx_rdatadx_solver is only required for sx_rdata + # in case of no conservation laws, C++ code will directly use + # sx, we don't need this + self._eqs[name] = sp.zeros( + self.num_states_rdata(), self.num_states_solver() + ) + + elif name == "dx_rdatadp": + if self.num_cons_law(): + self._eqs[name] = smart_jacobian( + self.eq("x_rdata"), self.sym("p") + ) + else: + # so far, dx_rdatadp is only required for sx_rdata + # in case of no conservation laws, C++ code will directly use + # sx, we don't need this + self._eqs[name] = sp.zeros( + self.num_states_rdata(), self.num_par() + ) + + elif name == "dx_rdatadtcl": + self._eqs[name] = smart_jacobian( + self.eq("x_rdata"), self.sym("tcl") + ) + + elif name == "dxdotdx_explicit": + # force symbols + self._derivative("xdot", "x", name=name) + + elif name == "dxdotdp_explicit": + # force symbols + self._derivative("xdot", "p", name=name) + + elif name == "spl": + self._eqs[name] = self.sym(name) + + elif name == "sspl": + # force symbols + self._eqs[name] = self.sym(name) + + elif name == "spline_values": + # force symbols + self._eqs[name] = sp.Matrix( + [y for spline in self._splines for y in spline.values_at_nodes] + ) + + elif name == "spline_slopes": + # force symbols + self._eqs[name] = sp.Matrix( + [ + d + for spline in self._splines + for d in ( + sp.zeros(len(spline.derivatives_at_nodes), 1) + if spline.derivatives_by_fd + else spline.derivatives_at_nodes + ) + ] + ) + + elif name == "drootdt": + self._eqs[name] = smart_jacobian(self.eq("root"), time_symbol) + + elif name == "drootdt_total": + # backsubstitution of optimized right-hand side terms into RHS + # calling subs() is costly. Due to looping over events though, the + # following lines are only evaluated if a model has events + w_sorted = toposort_symbols(dict(zip(self.sym("w"), self.eq("w")))) + tmp_xdot = smart_subs_dict(self.eq("xdot"), w_sorted) + self._eqs[name] = self.eq("drootdt") + if self.num_states_solver(): + self._eqs[name] += smart_multiply(self.eq("drootdx"), tmp_xdot) + + elif name == "deltax": + # fill boluses for Heaviside functions, as empty state updates + # would cause problems when writing the function file later + event_eqs = [] + for event in self._events: + if event._state_update is None: + event_eqs.append(sp.zeros(self.num_states_solver(), 1)) + else: + event_eqs.append(event._state_update) + + self._eqs[name] = event_eqs + + elif name == "z": + event_observables = [ + sp.zeros(self.num_eventobs(), 1) for _ in self._events + ] + event_ids = [e.get_id() for e in self._events] + # TODO: get rid of this stupid 1-based indexing as soon as we can + # the matlab interface + z2event = [ + event_ids.index(event_obs.get_event()) + 1 + for event_obs in self._event_observables + ] + for (iz, ie), event_obs in zip( + enumerate(z2event), self._event_observables + ): + event_observables[ie - 1][iz] = event_obs.get_val() + + self._eqs[name] = event_observables + self._z2event = z2event + + elif name in ["ddeltaxdx", "ddeltaxdp", "ddeltaxdt", "dzdp", "dzdx"]: + if match_deriv[2] == "t": + var = time_symbol + else: + var = self.sym(match_deriv[2]) + + self._eqs[name] = [ + smart_jacobian(self.eq(match_deriv[1])[ie], var) + for ie in range(self.num_events()) + ] + if name == "dzdx": + for ie in range(self.num_events()): + dtaudx = ( + -self.eq("drootdx")[ie, :] + / self.eq("drootdt_total")[ie] + ) + for iz in range(self.num_eventobs()): + if ie != self._z2event[iz] - 1: + continue + dzdt = sp.diff(self.eq("z")[ie][iz], time_symbol) + self._eqs[name][ie][iz, :] += dzdt * dtaudx + + elif name in ["rz", "drzdx", "drzdp"]: + eq_events = [] + for ie in range(self.num_events()): + val = sp.zeros( + self.num_eventobs(), + 1 if name == "rz" else len(self.sym(match_deriv[2])), + ) + # match event observables to root function + for iz in range(self.num_eventobs()): + if ie == self._z2event[iz] - 1: + val[iz, :] = self.eq(name.replace("rz", "root"))[ie, :] + eq_events.append(val) + + self._eqs[name] = eq_events + + elif name == "stau": + self._eqs[name] = [ + -self.eq("sroot")[ie, :] / self.eq("drootdt_total")[ie] + if not self.eq("drootdt_total")[ie].is_zero + else sp.zeros(*self.eq("sroot")[ie, :].shape) + for ie in range(self.num_events()) + ] + + elif name == "deltasx": + if self.num_states_solver() * self.num_par() == 0: + self._eqs[name] = [] + return + + event_eqs = [] + for ie, event in enumerate(self._events): + tmp_eq = sp.zeros(self.num_states_solver(), self.num_par()) + + # need to check if equations are zero since we are using + # symbols + if not smart_is_zero_matrix( + self.eq("stau")[ie] + ) and not smart_is_zero_matrix(self.eq("xdot")): + tmp_eq += smart_multiply( + self.sym("xdot_old") - self.sym("xdot"), + self.sym("stau").T, + ) + + # only add deltax part if there is state update + if event._state_update is not None: + # partial derivative for the parameters + tmp_eq += self.eq("ddeltaxdp")[ie] + + # initial part of chain rule state variables + tmp_dxdp = self.sym("sx") * sp.ones(1, self.num_par()) + + # need to check if equations are zero since we are using + # symbols + if not smart_is_zero_matrix(self.eq("stau")[ie]): + # chain rule for the time point + tmp_eq += smart_multiply( + self.eq("ddeltaxdt")[ie], self.sym("stau").T + ) + + # additional part of chain rule state variables + tmp_dxdp += smart_multiply( + self.sym("xdot_old"), self.sym("stau").T + ) + + # finish chain rule for the state variables + tmp_eq += smart_multiply( + self.eq("ddeltaxdx")[ie], tmp_dxdp + ) + + event_eqs.append(tmp_eq) + + self._eqs[name] = event_eqs + + elif name == "xdot_old": + # force symbols + self._eqs[name] = self.sym(name) + + elif name == "dwdx": + if ( + expected := list( + map( + ConservationLaw.get_x_rdata, + reversed(self.conservation_laws()), + ) + ) + ) != (actual := self.eq("w")[: self.num_cons_law()]): + raise AssertionError( + "Conservation laws are not at the beginning of 'w'. " + f"Got {actual}, expected {expected}." + ) + x = self.sym("x") + self._eqs[name] = sp.Matrix( + [ + [-cl.get_ncoeff(xs) for xs in x] + # the insert first in ode_model._add_conservation_law() means + # that we need to reverse the order here + for cl in reversed(self._conservation_laws) + ] + ).col_join( + smart_jacobian(self.eq("w")[self.num_cons_law() :, :], x) + ) + + elif match_deriv: + self._derivative(match_deriv[1], match_deriv[2], name) + + else: + raise ValueError(f"Unknown equation {name}") + + if name in ("sigmay", "sigmaz"): + # check for states in sigma{y,z}, which is currently not supported + syms_x = self.sym("x") + syms_yz = self.sym(name.removeprefix("sigma")) + xs_in_sigma = {} + for sym_yz, eq_yz in zip(syms_yz, self._eqs[name]): + yz_free_syms = eq_yz.free_symbols + if tmp := {x for x in syms_x if x in yz_free_syms}: + xs_in_sigma[sym_yz] = tmp + if xs_in_sigma: + msg = ", ".join( + [f"{yz} depends on {xs}" for yz, xs in xs_in_sigma.items()] + ) + raise NotImplementedError( + f"State-dependent observables are not supported, but {msg}." + ) + + if name == "root": + # Events are processed after the model has been set up. + # Equations are there, but symbols for roots must be added + self.sym("h") + + if name in {"Jy", "dydx"}: + # do not transpose if we compute the partial derivative as part of + # a total derivative + if not len(self._lock_total_derivative): + self._eqs[name] = self._eqs[name].transpose() + + if name in {"dzdx", "drzdx"}: + self._eqs[name] = [e.T for e in self._eqs[name]] + + if self._simplify: + dec = log_execution_time(f"simplifying {name}", logger) + if isinstance(self._eqs[name], list): + self._eqs[name] = [ + dec(_parallel_applyfunc)(sub_eq, self._simplify) + for sub_eq in self._eqs[name] + ] + else: + self._eqs[name] = dec(_parallel_applyfunc)( + self._eqs[name], self._simplify + ) + + def sym_names(self) -> list[str]: + """ + Returns a list of names of generated symbolic variables + + :return: + list of names + """ + return list(self._syms.keys()) + + def _derivative(self, eq: str, var: str, name: str = None) -> None: + """ + Creates a new symbolic variable according to a derivative + + :param eq: + name of the symbolic variable that defines the formula + + :param var: + name of the symbolic variable that defines the identifiers + with respect to which the derivatives are to be computed + + :param name: + name of resulting symbolic variable, default is ``d{eq}d{var}`` + """ + if not name: + name = f"d{eq}d{var}" + + ignore_chainrule = { + ("xdot", "p"): "w", # has generic implementation in c++ code + ("xdot", "x"): "w", # has generic implementation in c++ code + ("w", "w"): "tcl", # dtcldw = 0 + ("w", "x"): "tcl", # dtcldx = 0 + } + # automatically detect chainrule + chainvars = [ + cv + for cv in ["w", "tcl"] + if var_in_function_signature(eq, cv, self.is_ode()) + and cv not in self._lock_total_derivative + and var != cv + and min(self.sym(cv).shape) + and ( + (eq, var) not in ignore_chainrule + or ignore_chainrule[(eq, var)] != cv + ) + ] + if len(chainvars): + self._lock_total_derivative += chainvars + self._total_derivative(name, eq, chainvars, var) + for cv in chainvars: + self._lock_total_derivative.remove(cv) + return + + # partial derivative + sym_eq = self.eq(eq).transpose() if eq == "Jy" else self.eq(eq) + + sym_var = self.sym(var) + + derivative = smart_jacobian(sym_eq, sym_var) + + self._eqs[name] = derivative + + # compute recursion depth based on nilpotency of jacobian. computing + # nilpotency can be done more efficiently on numerical sparsity pattern + if name == "dwdw": + nonzeros = np.asarray( + derivative.applyfunc(lambda x: int(not x.is_zero)) + ).astype(np.int64) + recursion = nonzeros.copy() + if max(recursion.shape): + while recursion.max(): + recursion = recursion.dot(nonzeros) + self._w_recursion_depth += 1 + if self._w_recursion_depth > len(sym_eq): + raise RuntimeError( + "dwdw is not nilpotent. Something, somewhere went " + "terribly wrong. Please file a bug report at " + "https://github.com/AMICI-dev/AMICI/issues and " + "attach this model." + ) + + if name == "dydw" and not smart_is_zero_matrix(derivative): + dwdw = self.eq("dwdw") + # h(k) = d{eq}dw*dwdw^k* (k=1) + h = smart_multiply(derivative, dwdw) + while not smart_is_zero_matrix(h): + self._eqs[name] += h + # h(k+1) = d{eq}dw*dwdw^(k+1) = h(k)*dwdw + h = smart_multiply(h, dwdw) + + def _total_derivative( + self, + name: str, + eq: str, + chainvars: list[str], + var: str, + dydx_name: str = None, + dxdz_name: str = None, + ) -> None: + """ + Creates a new symbolic variable according to a total derivative + using the chain rule + + :param name: + name of resulting symbolic variable + + :param eq: + name of the symbolic variable that defines the formula + + :param chainvars: + names of the symbolic variable that define the + identifiers with respect to which the chain rules are applied + + :param var: + name of the symbolic variable that defines the identifiers + with respect to which the derivatives are to be computed + + :param dydx_name: + defines the name of the symbolic variable that + defines the derivative of the ``eq`` with respect to ``chainvar``, + default is ``d{eq}d{chainvar}`` + + :param dxdz_name: + defines the name of the symbolic variable that + defines the derivative of the ``chainvar`` with respect to ``var``, + default is d{chainvar}d{var} + """ + # compute total derivative according to chainrule + # Dydz = dydx*dxdz + dydz + + # initialize with partial derivative dydz without chain rule + self._eqs[name] = self.sym_or_eq(name, f"d{eq}d{var}") + if not isinstance(self._eqs[name], sp.Symbol): + # if not a Symbol, create a copy using sympy API + # NB deepcopy does not work safely, see sympy issue #7672 + self._eqs[name] = self._eqs[name].copy() + + for chainvar in chainvars: + if dydx_name is None: + dydx_name = f"d{eq}d{chainvar}" + if dxdz_name is None: + dxdz_name = f"d{chainvar}d{var}" + + dydx = self.sym_or_eq(name, dydx_name) + dxdz = self.sym_or_eq(name, dxdz_name) + # Save time for large models if one multiplicand is zero, + # which is not checked for by sympy + if not smart_is_zero_matrix(dydx) and not smart_is_zero_matrix( + dxdz + ): + dydx_times_dxdz = smart_multiply(dydx, dxdz) + if ( + dxdz.shape[1] == 1 + and self._eqs[name].shape[1] != dxdz.shape[1] + ): + for iz in range(self._eqs[name].shape[1]): + self._eqs[name][:, iz] += dydx_times_dxdz + else: + self._eqs[name] += dydx_times_dxdz + + def sym_or_eq(self, name: str, varname: str) -> sp.Matrix: + """ + Returns symbols or equations depending on whether a given + variable appears in the function signature or not. + + :param name: + name of function for which the signature should be checked + + :param varname: + name of the variable which should be contained in the + function signature + + :return: + the variable symbols if the variable is part of the signature and + the variable equations otherwise. + """ + # dwdx and dwdp will be dynamically computed and their ordering + # within a column may differ from the initialization of symbols here, + # so those are not safe to use. Not removing them from signature as + # this would break backwards compatibility. + if var_in_function_signature( + name, varname, self.is_ode() + ) and varname not in [ + "dwdx", + "dwdp", + ]: + return self.sym(varname) + else: + return self.eq(varname) + + def _multiplication( + self, + name: str, + x: str, + y: str, + transpose_x: bool | None = False, + sign: int | None = 1, + ): + """ + Creates a new symbolic variable according to a multiplication + + :param name: + name of resulting symbolic variable, default is ``d{eq}d{var}`` + + :param x: + name of the symbolic variable that defines the first factor + + :param y: + name of the symbolic variable that defines the second factor + + :param transpose_x: + indicates whether the first factor should be + transposed before multiplication + + :param sign: + defines the sign of the product, should be +1 or -1 + """ + if sign not in [-1, 1]: + raise TypeError(f"sign must be +1 or -1, was {sign}") + + variables = { + varname: self.sym(varname) + if var_in_function_signature(name, varname, self.is_ode()) + else self.eq(varname) + for varname in [x, y] + } + + xx = variables[x].transpose() if transpose_x else variables[x] + yy = variables[y] + + self._eqs[name] = sign * smart_multiply(xx, yy) + + def _equation_from_components( + self, name: str, components: list[ModelQuantity] + ) -> None: + """ + Generates the formulas of a symbolic variable from the attributes + + :param name: + name of resulting symbolic variable + + :param component: + name of the attribute + """ + self._eqs[name] = sp.Matrix([comp.get_val() for comp in components]) + + def get_conservation_laws(self) -> list[tuple[sp.Symbol, sp.Expr]]: + """Returns a list of states with conservation law set + + :return: + list of state identifiers + """ + return [ + (state.get_id(), state.get_x_rdata()) + for state in self.states() + if state.has_conservation_law() + ] + + def _generate_value(self, name: str) -> None: + """ + Generates the numeric values of a symbolic variable from value + prototypes + + :param name: + name of resulting symbolic variable + """ + if name in self._value_prototype: + components = self._value_prototype[name]() + else: + raise ValueError(f"No values for {name}") + + self._vals[name] = [comp.get_val() for comp in components] + + def _generate_name(self, name: str) -> None: + """ + Generates the names of a symbolic variable from variable prototypes or + equation prototypes + + :param name: + name of resulting symbolic variable + """ + if name in self._variable_prototype: + components = self._variable_prototype[name]() + elif name in self._equation_prototype: + components = self._equation_prototype[name]() + else: + raise ValueError(f"No names for {name}") + + self._names[name] = [comp.get_name() for comp in components] + + def state_has_fixed_parameter_initial_condition(self, ix: int) -> bool: + """ + Checks whether the state at specified index has a fixed parameter + initial condition + + :param ix: + state index + + :return: + boolean indicating if any of the initial condition free + variables is contained in the model constants + """ + ic = self.states()[ix].get_val() + if not isinstance(ic, sp.Basic): + return False + return any( + fp in (c.get_id() for c in self._constants) + for fp in ic.free_symbols + ) + + def state_has_conservation_law(self, ix: int) -> bool: + """ + Checks whether the state at specified index has a conservation + law set + + :param ix: + state index + + :return: + boolean indicating if conservation_law is not None + """ + return self.states()[ix].has_conservation_law() + + def get_solver_indices(self) -> dict[int, int]: + """ + Returns a mapping that maps rdata species indices to solver indices + + :return: + dictionary mapping rdata species indices to solver indices + """ + solver_index = {} + ix_solver = 0 + for ix in range(len(self.states())): + if self.state_has_conservation_law(ix): + continue + solver_index[ix] = ix_solver + ix_solver += 1 + return solver_index + + def state_is_constant(self, ix: int) -> bool: + """ + Checks whether the temporal derivative of the state is zero + + :param ix: + state index + + :return: + boolean indicating if constant over time + """ + state = self.states()[ix] + if isinstance(state, AlgebraicState): + return False + + return state.get_dt() == 0.0 + + def conservation_law_has_multispecies(self, tcl: ConservationLaw) -> bool: + """ + Checks whether a conservation law has multiple species or it just + defines one constant species + + :param tcl: + conservation law + + :return: + boolean indicating if conservation_law is not None + """ + state_set = set(self.sym("x_rdata")) + n_species = len(state_set.intersection(tcl.get_val().free_symbols)) + return n_species > 1 + + def _expr_is_time_dependent(self, expr: sp.Expr) -> bool: + """Determine whether an expression is time-dependent. + + :param expr: + The expression. + + :returns: + Whether the expression is time-dependent. + """ + # `expr.free_symbols` will be different to `self._states.keys()`, so + # it's easier to compare as `str`. + expr_syms = {str(sym) for sym in expr.free_symbols} + + # Check if the time variable is in the expression. + if "t" in expr_syms: + return True + + # Check if any time-dependent states are in the expression. + state_syms = [str(sym) for sym in self.states()] + return any( + not self.state_is_constant(state_syms.index(state)) + for state in expr_syms.intersection(state_syms) + ) + + def _get_unique_root( + self, + root_found: sp.Expr, + roots: list[Event], + ) -> sp.Symbol | None: + """ + Collects roots of Heaviside functions and events and stores them in + the roots list. It checks for redundancy to not store symbolically + equivalent root functions more than once. + + :param root_found: + equation of the root function + :param roots: + list of already known root functions with identifier + + :returns: + unique identifier for root, or ``None`` if the root is not + time-dependent + """ + if not self._expr_is_time_dependent(root_found): + return None + + for root in roots: + if sp.simplify(root_found - root.get_val()) == 0: + return root.get_id() + + # create an event for a new root function + root_symstr = f"Heaviside_{len(roots)}" + roots.append( + Event( + identifier=sp.Symbol(root_symstr), + name=root_symstr, + value=root_found, + state_update=None, + ) + ) + return roots[-1].get_id() + + def _collect_heaviside_roots( + self, + args: Sequence[sp.Expr], + ) -> list[sp.Expr]: + """ + Recursively checks an expression for the occurrence of Heaviside + functions and return all roots found + + :param args: + args attribute of the expanded expression + + :returns: + root functions that were extracted from Heaviside function + arguments + """ + root_funs = [] + for arg in args: + if arg.func == sp.Heaviside: + root_funs.append(arg.args[0]) + elif arg.has(sp.Heaviside): + root_funs.extend(self._collect_heaviside_roots(arg.args)) + + if not root_funs: + return [] + + # substitute 'w' expressions into root expressions now, to avoid + # rewriting 'root.cpp' and 'stau.cpp' headers + # to include 'w.h' + w_sorted = toposort_symbols( + dict( + zip( + [expr.get_id() for expr in self._expressions], + [expr.get_val() for expr in self._expressions], + ) + ) + ) + root_funs = [r.subs(w_sorted) for r in root_funs] + + return root_funs + + def _process_heavisides( + self, + dxdt: sp.Expr, + roots: list[Event], + ) -> sp.Expr: + """ + Parses the RHS of a state variable, checks for Heaviside functions, + collects unique roots functions that can be tracked by SUNDIALS and + replaces Heaviside Functions by amici helper variables that will be + updated based on SUNDIALS root tracking. + + :param dxdt: + right-hand side of state variable + :param roots: + list of known root functions with identifier + + :returns: + dxdt with Heaviside functions replaced by amici helper variables + """ + # track all the old Heaviside expressions in tmp_roots_old + # replace them later by the new expressions + heavisides = [] + # run through the expression tree and get the roots + tmp_roots_old = self._collect_heaviside_roots(dxdt.args) + for tmp_old in unique_preserve_order(tmp_roots_old): + # we want unique identifiers for the roots + tmp_new = self._get_unique_root(tmp_old, roots) + # `tmp_new` is None if the root is not time-dependent. + if tmp_new is None: + continue + # For Heavisides, we need to add the negative function as well + self._get_unique_root(sp.sympify(-tmp_old), roots) + heavisides.append((sp.Heaviside(tmp_old), tmp_new)) + + if heavisides: + # only apply subs if necessary + for heaviside_sympy, heaviside_amici in heavisides: + dxdt = dxdt.subs(heaviside_sympy, heaviside_amici) + + return dxdt diff --git a/python/sdist/amici/pysb_import.py b/python/sdist/amici/pysb_import.py index 05f9ed9b28..94aad595d9 100644 --- a/python/sdist/amici/pysb_import.py +++ b/python/sdist/amici/pysb_import.py @@ -27,7 +27,6 @@ from .de_export import ( Constant, DEExporter, - DEModel, DifferentialState, Expression, LogLikelihoodY, @@ -35,6 +34,7 @@ Parameter, SigmaY, ) +from .de_model import DEModel from .import_utils import ( _get_str_symbol_identifiers, _parse_special_functions, diff --git a/python/sdist/amici/sbml_import.py b/python/sdist/amici/sbml_import.py index 5a0e1669ad..61cb79043e 100644 --- a/python/sdist/amici/sbml_import.py +++ b/python/sdist/amici/sbml_import.py @@ -27,10 +27,10 @@ from sympy.logic.boolalg import BooleanFalse, BooleanTrue from . import has_clibs +from .de_model import DEModel from .constants import SymbolId from .de_export import ( DEExporter, - DEModel, ) from .de_model_components import symbol_to_type, Expression from .sympy_utils import smart_is_zero_matrix, smart_multiply diff --git a/python/sdist/amici/sympy_utils.py b/python/sdist/amici/sympy_utils.py index 863fc57f14..bc20c49dc4 100644 --- a/python/sdist/amici/sympy_utils.py +++ b/python/sdist/amici/sympy_utils.py @@ -5,8 +5,7 @@ import contextlib import sympy as sp import logging -from amici.de_export import get_logger -from amici.logging import log_execution_time +from .logging import log_execution_time, get_logger logger = get_logger(__name__, logging.ERROR) diff --git a/python/tests/test_misc.py b/python/tests/test_misc.py index 1ddeb3f760..b68e96f1a0 100644 --- a/python/tests/test_misc.py +++ b/python/tests/test_misc.py @@ -7,9 +7,7 @@ import amici import pytest import sympy as sp -from amici.de_export import ( - smart_subs_dict, -) +from amici.import_utils import smart_subs_dict from amici.testing import skip_on_valgrind diff --git a/python/tests/test_ode_export.py b/python/tests/test_ode_export.py index 65af2935bb..6dc4d85f05 100644 --- a/python/tests/test_ode_export.py +++ b/python/tests/test_ode_export.py @@ -92,7 +92,7 @@ def test_csc_matrix_vector(): @skip_on_valgrind def test_match_deriv(): - from amici.de_export import DERIVATIVE_PATTERN as pat + from amici.de_model import DERIVATIVE_PATTERN as pat def check(str, out1, out2): match = pat.match(str) From 239ce2c3998e98dc71209b7837cd451c94bb5ac7 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Mon, 4 Mar 2024 07:45:35 +0100 Subject: [PATCH 047/188] Allow setting MaxConvFails and MaxNonlinIters on solvers (#2335) See [CVodeSetMaxConvFails](https://sundials.readthedocs.io/en/latest/cvodes/Usage/SIM.html#c.CVodeSetMaxConvFails) and [CVodeSetMaxNonlinIters](https://sundials.readthedocs.io/en/latest/cvodes/Usage/SIM.html#c.CVodeSetMaxNonlinIters). Closes #2333. --- include/amici/serialization.h | 2 ++ include/amici/solver.h | 47 +++++++++++++++++++++++++++++++++++ include/amici/solver_cvodes.h | 4 +++ include/amici/solver_idas.h | 4 +++ src/hdf5.cpp | 22 ++++++++++++++++ src/newton_solver.cpp | 4 +-- src/solver.cpp | 27 +++++++++++++++++++- src/solver_cvodes.cpp | 13 ++++++++++ src/solver_idas.cpp | 13 ++++++++++ 9 files changed, 132 insertions(+), 4 deletions(-) diff --git a/include/amici/serialization.h b/include/amici/serialization.h index 6cab4f0353..b5bfe466fc 100644 --- a/include/amici/serialization.h +++ b/include/amici/serialization.h @@ -85,6 +85,8 @@ void serialize(Archive& ar, amici::Solver& s, unsigned int const /*version*/) { ar & s.check_sensi_steadystate_conv_; ar & s.rdata_mode_; ar & s.maxtime_; + ar & s.max_conv_fails_; + ar & s.max_nonlin_iters_; } /** diff --git a/include/amici/solver.h b/include/amici/solver.h index 92ae4c47fb..ab4f8ef427 100644 --- a/include/amici/solver.h +++ b/include/amici/solver.h @@ -936,6 +936,34 @@ class Solver { check_sensi_steadystate_conv_ = flag; } + /** + * @brief Set the maximum number of nonlinear solver iterations permitted + * per step. + * @param max_nonlin_iters maximum number of nonlinear solver iterations + */ + void setMaxNonlinIters(int max_nonlin_iters); + + /** + * @brief Get the maximum number of nonlinear solver iterations permitted + * per step. + * @return maximum number of nonlinear solver iterations + */ + int getMaxNonlinIters() const; + + /** + * @brief Set the maximum number of nonlinear solver convergence failures + * permitted per step. + * @param max_conv_fails maximum number of nonlinear solver convergence + */ + void setMaxConvFails(int max_conv_fails); + + /** + * @brief Get the maximum number of nonlinear solver convergence failures + * permitted per step. + * @return maximum number of nonlinear solver convergence + */ + int getMaxConvFails() const; + /** * @brief Serialize Solver (see boost::serialization::serialize) * @param ar Archive to serialize to @@ -1712,6 +1740,18 @@ class Solver { SensitivityMethod const sensi_meth, bool preequilibration ) const; + /** + * @brief Apply the maximum number of nonlinear solver iterations permitted + * per step. + */ + virtual void apply_max_nonlin_iters() const = 0; + + /** + * @brief Apply the maximum number of nonlinear solver convergence failures + * permitted per step. + */ + virtual void apply_max_conv_fails() const = 0; + /** state (dimension: nx_solver) */ mutable AmiVector x_{0}; @@ -1844,6 +1884,13 @@ class Solver { */ bool check_sensi_steadystate_conv_{true}; + /** Maximum number of nonlinear solver iterations permitted per step */ + int max_nonlin_iters_{3}; + + /** Maximum number of nonlinear solver convergence failures permitted per + * step */ + int max_conv_fails_{10}; + /** CPU time, forward solve */ mutable realtype cpu_time_{0.0}; diff --git a/include/amici/solver_cvodes.h b/include/amici/solver_cvodes.h index f98fb34c1a..592298b7ec 100644 --- a/include/amici/solver_cvodes.h +++ b/include/amici/solver_cvodes.h @@ -249,6 +249,10 @@ class CVodeSolver : public Solver { void setJacTimesVecFnB(int which) const override; void setSparseJacFn_ss() const override; + + void apply_max_nonlin_iters() const override; + + void apply_max_conv_fails() const override; }; } // namespace amici diff --git a/include/amici/solver_idas.h b/include/amici/solver_idas.h index b985a090af..079cee6399 100644 --- a/include/amici/solver_idas.h +++ b/include/amici/solver_idas.h @@ -229,6 +229,10 @@ class IDASolver : public Solver { void setJacTimesVecFnB(int which) const override; void setSparseJacFn_ss() const override; + + void apply_max_nonlin_iters() const override; + + void apply_max_conv_fails() const override; }; } // namespace amici diff --git a/src/hdf5.cpp b/src/hdf5.cpp index e459eee138..4efed799a8 100644 --- a/src/hdf5.cpp +++ b/src/hdf5.cpp @@ -895,6 +895,16 @@ void writeSolverSettingsToHDF5( file.getId(), hdf5Location.c_str(), "check_sensi_steadystate_conv", &ibuffer, 1 ); + + ibuffer = static_cast(solver.getMaxNonlinIters()); + H5LTset_attribute_int( + file.getId(), hdf5Location.c_str(), "max_nonlin_iters", &ibuffer, 1 + ); + + ibuffer = static_cast(solver.getMaxConvFails()); + H5LTset_attribute_int( + file.getId(), hdf5Location.c_str(), "max_conv_fails", &ibuffer, 1 + ); } void readSolverSettingsFromHDF5( @@ -1106,6 +1116,18 @@ void readSolverSettingsFromHDF5( file, datasetPath, "check_sensi_steadystate_conv" )); } + + if (attributeExists(file, datasetPath, "max_nonlin_iters")) { + solver.setMaxNonlinIters( + getIntScalarAttribute(file, datasetPath, "max_nonlin_iters") + ); + } + + if (attributeExists(file, datasetPath, "max_conv_fails")) { + solver.setMaxConvFails( + getIntScalarAttribute(file, datasetPath, "max_conv_fails") + ); + } } void readSolverSettingsFromHDF5( diff --git a/src/newton_solver.cpp b/src/newton_solver.cpp index 9f011bac1e..c14b05c7f2 100644 --- a/src/newton_solver.cpp +++ b/src/newton_solver.cpp @@ -188,9 +188,7 @@ void NewtonSolverSparse::prepareLinearSystemB( Model& model, SimulationState const& state ) { /* Get sparse Jacobian */ - model.fJSparseB( - state.t, 0.0, state.x, state.dx, xB_, dxB_, xdot_, Jtmp_ - ); + model.fJSparseB(state.t, 0.0, state.x, state.dx, xB_, dxB_, xdot_, Jtmp_); Jtmp_.refresh(); auto status = SUNLinSolSetup_KLU(linsol_, Jtmp_); if (status != SUNLS_SUCCESS) diff --git a/src/solver.cpp b/src/solver.cpp index 2caa9d8bc0..88185817dd 100644 --- a/src/solver.cpp +++ b/src/solver.cpp @@ -44,6 +44,8 @@ Solver::Solver(Solver const& other) , rdata_mode_(other.rdata_mode_) , newton_step_steadystate_conv_(other.newton_step_steadystate_conv_) , check_sensi_steadystate_conv_(other.check_sensi_steadystate_conv_) + , max_nonlin_iters_(other.max_nonlin_iters_) + , max_conv_fails_(other.max_conv_fails_) , maxstepsB_(other.maxstepsB_) , sensi_(other.sensi_) {} @@ -191,6 +193,9 @@ void Solver::setup( if (model->nt() > 1) calcIC(model->getTimepoint(1)); + apply_max_nonlin_iters(); + apply_max_conv_fails(); + cpu_time_ = 0.0; cpu_timeB_ = 0.0; } @@ -553,7 +558,9 @@ bool operator==(Solver const& a, Solver const& b) { == b.newton_step_steadystate_conv_) && (a.check_sensi_steadystate_conv_ == b.check_sensi_steadystate_conv_) - && (a.rdata_mode_ == b.rdata_mode_); + && (a.rdata_mode_ == b.rdata_mode_) + && (a.max_conv_fails_ == b.max_conv_fails_) + && (a.max_nonlin_iters_ == b.max_nonlin_iters_); } void Solver::applyTolerances() const { @@ -666,6 +673,24 @@ void Solver::checkSensitivityMethod( resetMutableMemory(nx(), nplist(), nquad()); } +void Solver::setMaxNonlinIters(int max_nonlin_iters) { + if (max_nonlin_iters < 0) + throw AmiException("max_nonlin_iters must be a non-negative number"); + + max_nonlin_iters_ = max_nonlin_iters; +} + +int Solver::getMaxNonlinIters() const { return max_nonlin_iters_; } + +void Solver::setMaxConvFails(int max_conv_fails) { + if (max_conv_fails < 0) + throw AmiException("max_conv_fails must be a non-negative number"); + + max_conv_fails_ = max_conv_fails; +} + +int Solver::getMaxConvFails() const { return max_conv_fails_; } + int Solver::getNewtonMaxSteps() const { return newton_maxsteps_; } void Solver::setNewtonMaxSteps(int const newton_maxsteps) { diff --git a/src/solver_cvodes.cpp b/src/solver_cvodes.cpp index f5150a87c7..4fe97720d8 100644 --- a/src/solver_cvodes.cpp +++ b/src/solver_cvodes.cpp @@ -257,6 +257,19 @@ void CVodeSolver::setSparseJacFn_ss() const { throw CvodeException(status, "CVodeSetJacFn"); } +void CVodeSolver::apply_max_nonlin_iters() const { + int status + = CVodeSetMaxNonlinIters(solver_memory_.get(), getMaxNonlinIters()); + if (status != CV_SUCCESS) + throw CvodeException(status, "CVodeSetMaxNonlinIters"); +} + +void CVodeSolver::apply_max_conv_fails() const { + int status = CVodeSetMaxConvFails(solver_memory_.get(), getMaxConvFails()); + if (status != CV_SUCCESS) + throw CvodeException(status, "CVodeSetMaxConvFails"); +} + Solver* CVodeSolver::clone() const { return new CVodeSolver(*this); } void CVodeSolver::allocateSolver() const { diff --git a/src/solver_idas.cpp b/src/solver_idas.cpp index dac085e98d..1c69a041db 100644 --- a/src/solver_idas.cpp +++ b/src/solver_idas.cpp @@ -254,6 +254,19 @@ void IDASolver::setSparseJacFn_ss() const { throw IDAException(status, "IDASetJacFn"); } +void IDASolver::apply_max_nonlin_iters() const { + int status + = IDASetMaxNonlinIters(solver_memory_.get(), getMaxNonlinIters()); + if (status != IDA_SUCCESS) + throw IDAException(status, "IDASetMaxNonlinIters"); +} + +void IDASolver::apply_max_conv_fails() const { + int status = IDASetMaxConvFails(solver_memory_.get(), getMaxConvFails()); + if (status != IDA_SUCCESS) + throw IDAException(status, "IDASetMaxConvFails"); +} + Solver* IDASolver::clone() const { return new IDASolver(*this); } void IDASolver::allocateSolver() const { From 4a065e62f8a73a813fb464b00bc5d6cee64c1d09 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Mon, 4 Mar 2024 07:46:27 +0100 Subject: [PATCH 048/188] Fix size check in `Model::setStateIsNonNegative` (#2332) Previously, the wrong array was checked :see_no_evil:: ```python amici_model.setAllStatesNonNegative() amici_model.setStateIsNonNegative([]) amici_model.setAllStatesNonNegative() Traceback (most recent call last): File "", line 1, in amici_model.setAllStatesNonNegative() File "python/sdist/amici/amici.py", line 2243, in setAllStatesNonNegative return _amici.ModelPtr_setAllStatesNonNegative(self) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RuntimeError: Dimension of input stateIsNonNegative (0) does not agree with number of state variables (2) ``` --- src/model.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/model.cpp b/src/model.cpp index 54363d8d1b..b671de3c73 100644 --- a/src/model.cpp +++ b/src/model.cpp @@ -883,11 +883,11 @@ void Model::setStateIsNonNegative(std::vector const& nonNegative) { // in case of conservation laws return; } - if (state_is_non_negative_.size() != gsl::narrow(nx_rdata)) { + if (nonNegative.size() != gsl::narrow(nx_rdata)) { throw AmiException( "Dimension of input stateIsNonNegative (%u) does " "not agree with number of state variables (%d)", - state_is_non_negative_.size(), nx_rdata + nonNegative.size(), nx_rdata ); } state_is_non_negative_ = nonNegative; From 61d17fa1f970debc7c246f011eb0b1608cef702c Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Mon, 4 Mar 2024 09:53:26 +0100 Subject: [PATCH 049/188] GHA: use dateutil from pypi (#2337) Finally works with Python3.12 without DeprecationWarnings --- .github/workflows/test_python_ver_matrix.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/test_python_ver_matrix.yml b/.github/workflows/test_python_ver_matrix.yml index 5f9d96d295..28b86d1c01 100644 --- a/.github/workflows/test_python_ver_matrix.yml +++ b/.github/workflows/test_python_ver_matrix.yml @@ -50,10 +50,6 @@ jobs: - name: Install python package run: scripts/installAmiciSource.sh - # until https://github.com/dateutil/dateutil >2.8.2 is released https://github.com/dateutil/dateutil/issues/1314 - - run: source build/venv/bin/activate && pip3 install git+https://github.com/dateutil/dateutil.git@296d419fe6bf3b22897f8f210735ac9c4e1cb796 - if: matrix.python-version == '3.12' - # install pysb before sympy to allow for sympy>=1.12 (https://github.com/pysb/pysb/commit/e83937cb8c74afc9b2fa96595b68464946745f33) - run: source build/venv/bin/activate && pip3 install git+https://github.com/pysb/pysb From 3a6b0df34fc18cd7d8aa9b4793d35485ef110f2d Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Mon, 4 Mar 2024 09:54:11 +0100 Subject: [PATCH 050/188] Fix uncaught C++ exception in runAmiciSimulation (#2338) Previously, an exception during `ReturnData::processSimulationObjects` would result in program termination. Fixes #1882. --- src/amici.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/amici.cpp b/src/amici.cpp index 6e85dde857..ecff1ea786 100644 --- a/src/amici.cpp +++ b/src/amici.cpp @@ -220,10 +220,20 @@ std::unique_ptr runAmiciSimulation( ); } - rdata->processSimulationObjects( - preeq.get(), fwd.get(), bwd_success ? bwd.get() : nullptr, posteq.get(), - model, solver, edata - ); + try { + rdata->processSimulationObjects( + preeq.get(), fwd.get(), bwd_success ? bwd.get() : nullptr, posteq.get(), + model, solver, edata + ); + } catch (std::exception const& ex) { + rdata->status = AMICI_ERROR; + if (rethrow) + throw; + logger.log( + LogSeverity::error, "OTHER", "AMICI simulation failed: %s", + ex.what() + ); + } rdata->cpu_time_total = cpu_timer.elapsed_milliseconds(); From 0f961d676cbda261c32360463cc2245e957ff600 Mon Sep 17 00:00:00 2001 From: Polina Lakrisenko Date: Mon, 4 Mar 2024 14:14:34 +0100 Subject: [PATCH 051/188] Fix missing import in `amici/petab/petab_import.py` (#2342) fixes ``` Traceback (most recent call last): File "//./mnt/src/optimize.py", line 557, in model, model_petab_problem = _model_import( File "/mnt/src/util.py", line 44, in _model_import model = amici.petab_import.import_petab_problem( File "/usr/local/lib/python3.10/dist-packages/amici/petab/petab_import.py", line 79, in import_petab_problem warn( NameError: name 'warn' is not defined ``` --- python/sdist/amici/petab/petab_import.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python/sdist/amici/petab/petab_import.py b/python/sdist/amici/petab/petab_import.py index 902bf837ef..cb896b39e3 100644 --- a/python/sdist/amici/petab/petab_import.py +++ b/python/sdist/amici/petab/petab_import.py @@ -10,6 +10,7 @@ import shutil from pathlib import Path from typing import Union +from warnings import warn import amici import petab From 1ab1d555feb1162b9e5be9876ecd14bede374019 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Mon, 4 Mar 2024 15:22:08 +0100 Subject: [PATCH 052/188] Fix ReturnDataView AttributeError: messages (#2341) * Make log messages from ReturnData available through ReturnDataView. * Add `LogItem.__repr__` Closes #2331. --- python/sdist/amici/numpy.py | 9 +++++++-- python/tests/test_sbml_import.py | 2 +- swig/amici.i | 8 ++++++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/python/sdist/amici/numpy.py b/python/sdist/amici/numpy.py index 6e2966ba4b..f75d927b7b 100644 --- a/python/sdist/amici/numpy.py +++ b/python/sdist/amici/numpy.py @@ -9,7 +9,7 @@ import itertools from typing import Literal, Union from collections.abc import Iterator - +from numbers import Number import amici import numpy as np import sympy as sp @@ -238,6 +238,7 @@ class ReturnDataView(SwigPtrView): "numnonlinsolvconvfailsB", "cpu_timeB", "cpu_time_total", + "messages", ] def __init__(self, rdata: Union[ReturnDataPtr, ReturnData]): @@ -440,7 +441,11 @@ def _field_as_numpy( attr = getattr(data, field) if field_dim := field_dimensions.get(field, None): return None if len(attr) == 0 else np.array(attr).reshape(field_dim) - return float(attr) + + if isinstance(attr, Number): + return float(attr) + + return attr def _entity_type_from_id( diff --git a/python/tests/test_sbml_import.py b/python/tests/test_sbml_import.py index 74a51d020a..ecf21f1f95 100644 --- a/python/tests/test_sbml_import.py +++ b/python/tests/test_sbml_import.py @@ -368,7 +368,7 @@ def test_solver_reuse(model_steadystate_module): assert rdata1.status == amici.AMICI_SUCCESS for attr in rdata1: - if "time" in attr: + if "time" in attr or attr == "messages": continue val1 = getattr(rdata1, attr) diff --git a/swig/amici.i b/swig/amici.i index 3518b296fe..1ef076fe33 100644 --- a/swig/amici.i +++ b/swig/amici.i @@ -235,6 +235,14 @@ def __repr__(self): %} }; +%extend amici::LogItem { +%pythoncode %{ +def __repr__(self): + return (f"{self.__class__.__name__}(severity={self.severity}, " + f"identifier={self.identifier!r}, message={self.message!r})") +%} +}; + // Convert integer values to enum class // defeats the purpose of enum class, but didn't find a better way to allow for From e8852be21a25b9fa79fc2fa2bbd44a4c83edabb6 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Mon, 4 Mar 2024 15:23:00 +0100 Subject: [PATCH 053/188] Fix swig/std_unique_ptr.i (#2343) Fixes some `used without template arguments` errors when running swig with `-builtin` or `-keyword`. --- swig/std_unique_ptr.i | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swig/std_unique_ptr.i b/swig/std_unique_ptr.i index c44513bcee..f73babfd9e 100644 --- a/swig/std_unique_ptr.i +++ b/swig/std_unique_ptr.i @@ -17,7 +17,7 @@ namespace std { pointer operator-> () const; pointer release (); - void reset (pointer __p=pointer()); + void reset (pointer __p=std::unique_ptr::pointer()); void swap (unique_ptr &__u); pointer get () const; operator bool () const; From 2906611fda0488a95fde73a8312313bf91686e42 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Tue, 5 Mar 2024 16:47:42 +0100 Subject: [PATCH 054/188] Add new return code constant LSETUP_FAIL (#2353) --- include/amici/defines.h | 1 + src/amici.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/include/amici/defines.h b/include/amici/defines.h index f5ed1e62bb..210710b7ba 100644 --- a/include/amici/defines.h +++ b/include/amici/defines.h @@ -68,6 +68,7 @@ constexpr int AMICI_TOO_MUCH_WORK= -1; constexpr int AMICI_TOO_MUCH_ACC= -2; constexpr int AMICI_ERR_FAILURE= -3; constexpr int AMICI_CONV_FAILURE= -4; +constexpr int AMICI_LSETUP_FAIL= -6; constexpr int AMICI_RHSFUNC_FAIL= -8; constexpr int AMICI_FIRST_RHSFUNC_ERR= -9; constexpr int AMICI_ILL_INPUT= -22; diff --git a/src/amici.cpp b/src/amici.cpp index ecff1ea786..ef6d640e13 100644 --- a/src/amici.cpp +++ b/src/amici.cpp @@ -69,6 +69,7 @@ std::map simulation_status_to_str_map = { {AMICI_MAX_TIME_EXCEEDED, "AMICI_MAX_TIME_EXCEEDED"}, {AMICI_SUCCESS, "AMICI_SUCCESS"}, {AMICI_NOT_RUN, "AMICI_NOT_RUN"}, + {AMICI_LSETUP_FAIL, "AMICI_LSETUP_FAIL"}, }; std::unique_ptr runAmiciSimulation( From 2235ab7d7d4943bb88b071e4da090a4e0b9b37a3 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Tue, 5 Mar 2024 16:48:50 +0100 Subject: [PATCH 055/188] Fix in-place building of model wheels (#2352) Fix `python -m build -n` for generated model packages. Fixes half of #2285 --- python/sdist/amici/MANIFEST.template.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/python/sdist/amici/MANIFEST.template.in b/python/sdist/amici/MANIFEST.template.in index eb3b1b450f..fd78129853 100644 --- a/python/sdist/amici/MANIFEST.template.in +++ b/python/sdist/amici/MANIFEST.template.in @@ -1 +1,3 @@ include *.cpp *.h +include CMakeLists.txt +recursive-include swig/ * From 777deb0a1a3e50d39b6f6e1e172cf754514d62f3 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Tue, 5 Mar 2024 18:49:36 +0100 Subject: [PATCH 056/188] Always include timepoints in NaN/Inf warnings (#2347) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Always include timepoints in NaN/Inf warnings Closes #2328 --------- Co-authored-by: Fabian Fröhlich --- include/amici/model.h | 7 +++-- src/amici.cpp | 4 +-- src/model.cpp | 69 +++++++++++++++++++++++++------------------ src/model_dae.cpp | 3 +- src/model_ode.cpp | 3 +- src/solver_cvodes.cpp | 34 +++++++++++---------- src/solver_idas.cpp | 30 +++++++++++-------- 7 files changed, 89 insertions(+), 61 deletions(-) diff --git a/include/amici/model.h b/include/amici/model.h index 13de007e00..29b98aa913 100644 --- a/include/amici/model.h +++ b/include/amici/model.h @@ -1334,10 +1334,12 @@ class Model : public AbstractModel, public ModelDimensions { * * @param array * @param model_quantity The model quantity `array` corresponds to + * @param t Current timepoint * @return */ int checkFinite( - gsl::span array, ModelQuantity model_quantity + gsl::span array, ModelQuantity model_quantity, + realtype t ) const; /** * @brief Check if the given array has only finite elements. @@ -1347,11 +1349,12 @@ class Model : public AbstractModel, public ModelDimensions { * @param array Flattened matrix * @param model_quantity The model quantity `array` corresponds to * @param num_cols Number of columns of the non-flattened matrix + * @param t Current timepoint * @return */ int checkFinite( gsl::span array, ModelQuantity model_quantity, - size_t num_cols + size_t num_cols, realtype t ) const; /** diff --git a/src/amici.cpp b/src/amici.cpp index ef6d640e13..cc8c2f00ab 100644 --- a/src/amici.cpp +++ b/src/amici.cpp @@ -223,8 +223,8 @@ std::unique_ptr runAmiciSimulation( try { rdata->processSimulationObjects( - preeq.get(), fwd.get(), bwd_success ? bwd.get() : nullptr, posteq.get(), - model, solver, edata + preeq.get(), fwd.get(), bwd_success ? bwd.get() : nullptr, + posteq.get(), model, solver, edata ); } catch (std::exception const& ex) { rdata->status = AMICI_ERROR; diff --git a/src/model.cpp b/src/model.cpp index b671de3c73..cefdf1ac97 100644 --- a/src/model.cpp +++ b/src/model.cpp @@ -372,7 +372,7 @@ void Model::initializeStates(AmiVector& x) { std::copy(x0_solver.cbegin(), x0_solver.cend(), x.data()); } - checkFinite(x.getVector(), ModelQuantity::x0); + checkFinite(x.getVector(), ModelQuantity::x0, t0()); } void Model::initializeSplines() { @@ -1482,7 +1482,7 @@ void Model::addStateEventUpdate( ); if (always_check_finite_) { - checkFinite(derived_state_.deltax_, ModelQuantity::deltax); + checkFinite(derived_state_.deltax_, ModelQuantity::deltax, t); } // update @@ -1536,7 +1536,7 @@ void Model::addAdjointStateEventUpdate( ); if (always_check_finite_) { - checkFinite(derived_state_.deltaxB_, ModelQuantity::deltaxB); + checkFinite(derived_state_.deltaxB_, ModelQuantity::deltaxB, t); } // apply update @@ -1582,7 +1582,7 @@ void Model::updateHeavisideB(int const* rootsfound) { } int Model::checkFinite( - gsl::span array, ModelQuantity model_quantity + gsl::span array, ModelQuantity model_quantity, realtype t ) const { auto it = std::find_if(array.begin(), array.end(), [](realtype x) { return !std::isfinite(x); @@ -1654,23 +1654,27 @@ int Model::checkFinite( gsl_ExpectsDebug(false); model_quantity_str = std::to_string(static_cast(model_quantity)); } - if (logger) + if (logger) { + auto t_msg = std::isfinite(t) + ? std::string(" at t=" + std::to_string(t) + " ") + : std::string(); + logger->log( LogSeverity::warning, msg_id, - "AMICI encountered a %s value for %s[%i] (%s)", + "AMICI encountered a %s value for %s[%i] (%s)%s", non_finite_type.c_str(), model_quantity_str.c_str(), - gsl::narrow(flat_index), element_id.c_str() + gsl::narrow(flat_index), element_id.c_str(), t_msg.c_str() ); - + } // check upstream, without infinite recursion if (model_quantity != ModelQuantity::k && model_quantity != ModelQuantity::p && model_quantity != ModelQuantity::ts) { - checkFinite(state_.fixedParameters, ModelQuantity::k); - checkFinite(state_.unscaledParameters, ModelQuantity::p); - checkFinite(simulation_parameters_.ts_, ModelQuantity::ts); + checkFinite(state_.fixedParameters, ModelQuantity::k, t); + checkFinite(state_.unscaledParameters, ModelQuantity::p, t); + checkFinite(simulation_parameters_.ts_, ModelQuantity::ts, t); if (!always_check_finite_ && model_quantity != ModelQuantity::w) { // don't check twice if always_check_finite_ is true - checkFinite(derived_state_.w_, ModelQuantity::w); + checkFinite(derived_state_.w_, ModelQuantity::w, t); } } return AMICI_RECOVERABLE_ERROR; @@ -1678,7 +1682,7 @@ int Model::checkFinite( int Model::checkFinite( gsl::span array, ModelQuantity model_quantity, - size_t num_cols + size_t num_cols, realtype t ) const { auto it = std::find_if(array.begin(), array.end(), [](realtype x) { return !std::isfinite(x); @@ -1768,19 +1772,25 @@ int Model::checkFinite( model_quantity_str = std::to_string(static_cast(model_quantity)); } - if (logger) + if (logger) { + auto t_msg = std::isfinite(t) + ? std::string(" at t=" + std::to_string(t) + " ") + : std::string(); + logger->log( LogSeverity::warning, msg_id, - "AMICI encountered a %s value for %s[%i] (%s, %s)", + "AMICI encountered a %s value for %s[%i] (%s, %s)%s", non_finite_type.c_str(), model_quantity_str.c_str(), - gsl::narrow(flat_index), row_id.c_str(), col_id.c_str() + gsl::narrow(flat_index), row_id.c_str(), col_id.c_str(), + t_msg.c_str() ); + } // check upstream - checkFinite(state_.fixedParameters, ModelQuantity::k); - checkFinite(state_.unscaledParameters, ModelQuantity::p); - checkFinite(simulation_parameters_.ts_, ModelQuantity::ts); - checkFinite(derived_state_.w_, ModelQuantity::w); + checkFinite(state_.fixedParameters, ModelQuantity::k, t); + checkFinite(state_.unscaledParameters, ModelQuantity::p, t); + checkFinite(simulation_parameters_.ts_, ModelQuantity::ts, t); + checkFinite(derived_state_.w_, ModelQuantity::w, t); return AMICI_RECOVERABLE_ERROR; } @@ -1868,10 +1878,10 @@ int Model::checkFinite(SUNMatrix m, ModelQuantity model_quantity, realtype t) ); // check upstream - checkFinite(state_.fixedParameters, ModelQuantity::k); - checkFinite(state_.unscaledParameters, ModelQuantity::p); - checkFinite(simulation_parameters_.ts_, ModelQuantity::ts); - checkFinite(derived_state_.w_, ModelQuantity::w); + checkFinite(state_.fixedParameters, ModelQuantity::k, t); + checkFinite(state_.unscaledParameters, ModelQuantity::p, t); + checkFinite(simulation_parameters_.ts_, ModelQuantity::ts, t); + checkFinite(derived_state_.w_, ModelQuantity::w, t); return AMICI_RECOVERABLE_ERROR; } @@ -1895,7 +1905,7 @@ void Model::fx0(AmiVector& x) { state_.unscaledParameters.data(), state_.fixedParameters.data() ); - checkFinite(derived_state_.x_rdata_, ModelQuantity::x0_rdata); + checkFinite(derived_state_.x_rdata_, ModelQuantity::x0_rdata, t0()); } void Model::fx0_fixedParameters(AmiVector& x) { @@ -1982,7 +1992,10 @@ void Model::fx_rdata(AmiVector& x_rdata, AmiVector const& x) { state_.unscaledParameters.data(), state_.fixedParameters.data() ); if (always_check_finite_) - checkFinite(x_rdata.getVector(), ModelQuantity::x_rdata); + checkFinite( + x_rdata.getVector(), ModelQuantity::x_rdata, + std::numeric_limits::quiet_NaN() + ); } void Model::fsx_rdata( @@ -2072,7 +2085,7 @@ void Model::fy(realtype const t, AmiVector const& x) { if (always_check_finite_) { checkFinite( - gsl::make_span(derived_state_.y_.data(), ny), ModelQuantity::y + gsl::make_span(derived_state_.y_.data(), ny), ModelQuantity::y, t ); } } @@ -2875,7 +2888,7 @@ void Model::fw(realtype const t, realtype const* x, bool include_static) { state_.spl_.data(), include_static); if (always_check_finite_) { - checkFinite(derived_state_.w_, ModelQuantity::w); + checkFinite(derived_state_.w_, ModelQuantity::w, t); } } diff --git a/src/model_dae.cpp b/src/model_dae.cpp index ad397bc82f..22025e1181 100644 --- a/src/model_dae.cpp +++ b/src/model_dae.cpp @@ -138,7 +138,8 @@ void Model_DAE::fJDiag( fJSparse(t, 0.0, x.getNVector(), dx.getNVector(), derived_state_.J_); derived_state_.J_.refresh(); derived_state_.J_.to_diag(JDiag.getNVector()); - if (checkFinite(JDiag.getVector(), ModelQuantity::JDiag) != AMICI_SUCCESS) + if (checkFinite(JDiag.getVector(), ModelQuantity::JDiag, t) + != AMICI_SUCCESS) throw AmiException("Evaluation of fJDiag failed!"); } diff --git a/src/model_ode.cpp b/src/model_ode.cpp index e3e401e1bf..787df210ef 100644 --- a/src/model_ode.cpp +++ b/src/model_ode.cpp @@ -122,7 +122,8 @@ void Model_ODE::fJDiag( AmiVector const& x, AmiVector const& /*dx*/ ) { fJDiag(t, JDiag.getNVector(), x.getNVector()); - if (checkFinite(JDiag.getVector(), ModelQuantity::JDiag) != AMICI_SUCCESS) + if (checkFinite(JDiag.getVector(), ModelQuantity::JDiag, t) + != AMICI_SUCCESS) throw AmiException("Evaluation of fJDiag failed!"); } diff --git a/src/solver_cvodes.cpp b/src/solver_cvodes.cpp index 4fe97720d8..0952724457 100644 --- a/src/solver_cvodes.cpp +++ b/src/solver_cvodes.cpp @@ -927,7 +927,7 @@ fJB(realtype t, N_Vector x, N_Vector xB, N_Vector xBdot, SUNMatrix JB, Expects(model); model->fJB(t, x, xB, xBdot, JB); - return model->checkFinite(gsl::make_span(JB), ModelQuantity::JB); + return model->checkFinite(gsl::make_span(JB), ModelQuantity::JB, t); } /** @@ -978,7 +978,7 @@ static int fJSparseB( Expects(model); model->fJSparseB(t, x, xB, xBdot, JB); - return model->checkFinite(gsl::make_span(JB), ModelQuantity::JB); + return model->checkFinite(gsl::make_span(JB), ModelQuantity::JB, t); } /** @@ -1041,7 +1041,7 @@ fJv(N_Vector v, N_Vector Jv, realtype t, N_Vector x, N_Vector /*xdot*/, Expects(model); model->fJv(v, Jv, t, x); - return model->checkFinite(gsl::make_span(Jv), ModelQuantity::Jv); + return model->checkFinite(gsl::make_span(Jv), ModelQuantity::Jv, t); } /** @@ -1067,7 +1067,7 @@ static int fJvB( Expects(model); model->fJvB(vB, JvB, t, x, xB); - return model->checkFinite(gsl::make_span(JvB), ModelQuantity::JvB); + return model->checkFinite(gsl::make_span(JvB), ModelQuantity::JvB, t); } /** @@ -1094,7 +1094,7 @@ static int froot(realtype t, N_Vector x, realtype* root, void* user_data) { model->froot(t, x, gsl::make_span(root, model->ne_solver)); } return model->checkFinite( - gsl::make_span(root, model->ne_solver), ModelQuantity::root + gsl::make_span(root, model->ne_solver), ModelQuantity::root, t ); } @@ -1119,7 +1119,7 @@ static int fxdot(realtype t, N_Vector x, N_Vector xdot, void* user_data) { } if (t > 1e200 - && !model->checkFinite(gsl::make_span(x), ModelQuantity::xdot)) { + && !model->checkFinite(gsl::make_span(x), ModelQuantity::xdot, t)) { /* when t is large (typically ~1e300), CVODES may pass all NaN x to fxdot from which we typically cannot recover. To save time on normal execution, we do not always want to check finiteness @@ -1128,7 +1128,7 @@ static int fxdot(realtype t, N_Vector x, N_Vector xdot, void* user_data) { } model->fxdot(t, x, xdot); - return model->checkFinite(gsl::make_span(xdot), ModelQuantity::xdot); + return model->checkFinite(gsl::make_span(xdot), ModelQuantity::xdot, t); } /** @@ -1154,7 +1154,7 @@ fxBdot(realtype t, N_Vector x, N_Vector xB, N_Vector xBdot, void* user_data) { } model->fxBdot(t, x, xB, xBdot); - return model->checkFinite(gsl::make_span(xBdot), ModelQuantity::xBdot); + return model->checkFinite(gsl::make_span(xBdot), ModelQuantity::xBdot, t); } /** @@ -1174,7 +1174,7 @@ fqBdot(realtype t, N_Vector x, N_Vector xB, N_Vector qBdot, void* user_data) { Expects(model); model->fqBdot(t, x, xB, qBdot); - return model->checkFinite(gsl::make_span(qBdot), ModelQuantity::qBdot); + return model->checkFinite(gsl::make_span(qBdot), ModelQuantity::qBdot, t); } /** @@ -1193,7 +1193,9 @@ static int fxBdot_ss(realtype t, N_Vector xB, N_Vector xBdot, void* user_data) { Expects(model); model->fxBdot_ss(t, xB, xBdot); - return model->checkFinite(gsl::make_span(xBdot), ModelQuantity::xBdot_ss); + return model->checkFinite( + gsl::make_span(xBdot), ModelQuantity::xBdot_ss, t + ); } /** @@ -1212,7 +1214,9 @@ static int fqBdot_ss(realtype t, N_Vector xB, N_Vector qBdot, void* user_data) { Expects(model); model->fqBdot_ss(t, xB, qBdot); - return model->checkFinite(gsl::make_span(qBdot), ModelQuantity::qBdot_ss); + return model->checkFinite( + gsl::make_span(qBdot), ModelQuantity::qBdot_ss, t + ); } /** @@ -1228,8 +1232,8 @@ static int fqBdot_ss(realtype t, N_Vector xB, N_Vector qBdot, void* user_data) { * @return status flag indicating successful execution */ static int fJSparseB_ss( - realtype /*t*/, N_Vector /*x*/, N_Vector xBdot, SUNMatrix JB, - void* user_data, N_Vector /*tmp1*/, N_Vector /*tmp2*/, N_Vector /*tmp3*/ + realtype t, N_Vector /*x*/, N_Vector xBdot, SUNMatrix JB, void* user_data, + N_Vector /*tmp1*/, N_Vector /*tmp2*/, N_Vector /*tmp3*/ ) { auto typed_udata = static_cast(user_data); Expects(typed_udata); @@ -1238,7 +1242,7 @@ static int fJSparseB_ss( model->fJSparseB_ss(JB); return model->checkFinite( - gsl::make_span(xBdot), ModelQuantity::JSparseB_ss + gsl::make_span(xBdot), ModelQuantity::JSparseB_ss, t ); } @@ -1267,7 +1271,7 @@ static int fsxdot( Expects(model); model->fsxdot(t, x, ip, sx, sxdot); - return model->checkFinite(gsl::make_span(sxdot), ModelQuantity::sxdot); + return model->checkFinite(gsl::make_span(sxdot), ModelQuantity::sxdot, t); } bool operator==(CVodeSolver const& a, CVodeSolver const& b) { diff --git a/src/solver_idas.cpp b/src/solver_idas.cpp index 1c69a041db..fa08bc29f9 100644 --- a/src/solver_idas.cpp +++ b/src/solver_idas.cpp @@ -1065,7 +1065,7 @@ int fJv( Expects(model); model->fJv(t, x, dx, v, Jv, cj); - return model->checkFinite(gsl::make_span(Jv), ModelQuantity::Jv); + return model->checkFinite(gsl::make_span(Jv), ModelQuantity::Jv, t); } /** @@ -1096,7 +1096,7 @@ int fJvB( Expects(model); model->fJvB(t, x, dx, xB, dxB, vB, JvB, cj); - return model->checkFinite(gsl::make_span(JvB), ModelQuantity::JvB); + return model->checkFinite(gsl::make_span(JvB), ModelQuantity::JvB, t); } /** @@ -1118,7 +1118,7 @@ int froot( model->froot(t, x, dx, gsl::make_span(root, model->ne)); return model->checkFinite( - gsl::make_span(root, model->ne), ModelQuantity::root + gsl::make_span(root, model->ne), ModelQuantity::root, t ); } @@ -1144,7 +1144,7 @@ int fxdot(realtype t, N_Vector x, N_Vector dx, N_Vector xdot, void* user_data) { } if (t > 1e200 - && !model->checkFinite(gsl::make_span(x), ModelQuantity::xdot)) { + && !model->checkFinite(gsl::make_span(x), ModelQuantity::xdot, t)) { /* when t is large (typically ~1e300), CVODES may pass all NaN x to fxdot from which we typically cannot recover. To save time on normal execution, we do not always want to check finiteness @@ -1153,7 +1153,7 @@ int fxdot(realtype t, N_Vector x, N_Vector dx, N_Vector xdot, void* user_data) { } model->fxdot(t, x, dx, xdot); - return model->checkFinite(gsl::make_span(xdot), ModelQuantity::xdot); + return model->checkFinite(gsl::make_span(xdot), ModelQuantity::xdot, t); } /** @@ -1183,7 +1183,7 @@ int fxBdot( } model->fxBdot(t, x, dx, xB, dxB, xBdot); - return model->checkFinite(gsl::make_span(xBdot), ModelQuantity::xBdot); + return model->checkFinite(gsl::make_span(xBdot), ModelQuantity::xBdot, t); } /** @@ -1208,7 +1208,7 @@ int fqBdot( Expects(model); model->fqBdot(t, x, dx, xB, dxB, qBdot); - return model->checkFinite(gsl::make_span(qBdot), ModelQuantity::qBdot); + return model->checkFinite(gsl::make_span(qBdot), ModelQuantity::qBdot, t); } /** @@ -1230,7 +1230,9 @@ static int fxBdot_ss( Expects(model); model->fxBdot_ss(t, xB, dxB, xBdot); - return model->checkFinite(gsl::make_span(xBdot), ModelQuantity::xBdot_ss); + return model->checkFinite( + gsl::make_span(xBdot), ModelQuantity::xBdot_ss, t + ); } /** @@ -1252,7 +1254,9 @@ static int fqBdot_ss( Expects(model); model->fqBdot_ss(t, xB, dxB, qBdot); - return model->checkFinite(gsl::make_span(qBdot), ModelQuantity::qBdot_ss); + return model->checkFinite( + gsl::make_span(qBdot), ModelQuantity::qBdot_ss, t + ); } /** @@ -1270,7 +1274,7 @@ static int fqBdot_ss( * @return status flag indicating successful execution */ static int fJSparseB_ss( - realtype /*t*/, realtype /*cj*/, N_Vector /*x*/, N_Vector /*dx*/, + realtype t, realtype /*cj*/, N_Vector /*x*/, N_Vector /*dx*/, N_Vector xBdot, SUNMatrix JB, void* user_data, N_Vector /*tmp1*/, N_Vector /*tmp2*/, N_Vector /*tmp3*/ ) { @@ -1281,7 +1285,7 @@ static int fJSparseB_ss( model->fJSparseB_ss(JB); return model->checkFinite( - gsl::make_span(xBdot), ModelQuantity::JSparseB_ss + gsl::make_span(xBdot), ModelQuantity::JSparseB_ss, t ); } @@ -1314,7 +1318,9 @@ int fsxdot( for (int ip = 0; ip < model->nplist(); ip++) { model->fsxdot(t, x, dx, ip, sx[ip], sdx[ip], sxdot[ip]); - if (model->checkFinite(gsl::make_span(sxdot[ip]), ModelQuantity::sxdot) + if (model->checkFinite( + gsl::make_span(sxdot[ip]), ModelQuantity::sxdot, t + ) != AMICI_SUCCESS) return AMICI_RECOVERABLE_ERROR; } From 89b00cf2758d85ac5eb920cf9dfb170656af3234 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Tue, 5 Mar 2024 18:59:08 +0100 Subject: [PATCH 057/188] Make is-zero-checks compatible to the upcoming sympy>1.12 (#2350) Current sympy master (tested: 0ededfcfc033ddca2d1d54a0eda85bf53c63761c): ```python IPython console for SymPy 1.13.dev (Python 3.12.2-64-bit) (ground types: python) In [1]: Float(0) == 0 Out[1]: False In [2]: Float(0).is_zero Out[2]: True ``` vs 1.12: ```python Python console for SymPy 1.12 (Python 3.12.2-64-bit) (ground types: python) >>> Float(0) == 0 True >>> Float(0).is_zero True ``` Therefore, replace `== 0` by `.is_zero`. Amongst other potential issues, this fixes zeros in sparse matrices. Closes #2320. --- python/sdist/amici/cxxcodeprinter.py | 2 +- python/sdist/amici/de_model.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/python/sdist/amici/cxxcodeprinter.py b/python/sdist/amici/cxxcodeprinter.py index adc4c25454..fb98b0aca0 100644 --- a/python/sdist/amici/cxxcodeprinter.py +++ b/python/sdist/amici/cxxcodeprinter.py @@ -339,7 +339,7 @@ def csc_matrix( for col in range(ncols): symbol_col_ptrs.append(idx) for row in range(nrows): - if matrix[row, col] == 0: + if matrix[row, col].is_zero: continue symbol_row_vals.append(row) diff --git a/python/sdist/amici/de_model.py b/python/sdist/amici/de_model.py index b69cc6ba68..ea2807df52 100644 --- a/python/sdist/amici/de_model.py +++ b/python/sdist/amici/de_model.py @@ -2115,7 +2115,7 @@ def state_is_constant(self, ix: int) -> bool: if isinstance(state, AlgebraicState): return False - return state.get_dt() == 0.0 + return state.get_dt().is_zero def conservation_law_has_multispecies(self, tcl: ConservationLaw) -> bool: """ @@ -2179,7 +2179,7 @@ def _get_unique_root( return None for root in roots: - if sp.simplify(root_found - root.get_val()) == 0: + if sp.simplify(root_found - root.get_val()).is_zero: return root.get_id() # create an event for a new root function From b8be5a0788b44ec5245ba0bbb2dd331d1713f061 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Wed, 6 Mar 2024 08:34:16 +0100 Subject: [PATCH 058/188] Fix type annotations in swig-wrappers (#2344) Depending on swig options/versions, swig may or may not generate type annotations. With these changes, type annotations will be extracted from the docstrings where available, independent of the swig-generated type annotations. Closes #2336. --- python/sdist/amici/swig.py | 43 +++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/python/sdist/amici/swig.py b/python/sdist/amici/swig.py index 902145ff3e..81a030ba3d 100644 --- a/python/sdist/amici/swig.py +++ b/python/sdist/amici/swig.py @@ -15,6 +15,7 @@ class TypeHintFixer(ast.NodeTransformer): "ptrdiff_t": ast.Name("int"), "size_t": ast.Name("int"), "bool": ast.Name("bool"), + "boolean": ast.Name("bool"), "std::unique_ptr< amici::Solver >": ast.Constant("Solver"), "amici::InternalSensitivityMethod": ast.Constant( "InternalSensitivityMethod" @@ -40,8 +41,10 @@ class TypeHintFixer(ast.NodeTransformer): "SteadyStateSensitivityMode" ), "amici::realtype": ast.Name("float"), - "DoubleVector": ast.Constant("Sequence[float]"), + "DoubleVector": ast.Name("Sequence[float]"), + "BoolVector": ast.Name("Sequence[bool]"), "IntVector": ast.Name("Sequence[int]"), + "StringVector": ast.Name("Sequence[str]"), "std::string": ast.Name("str"), "std::string const &": ast.Name("str"), "std::unique_ptr< amici::ExpData >": ast.Constant("ExpData"), @@ -53,6 +56,8 @@ class TypeHintFixer(ast.NodeTransformer): } def visit_FunctionDef(self, node): + self._annotation_from_docstring(node) + # Has a return type annotation? if node.returns: node.returns = self._new_annot(node.returns.value) @@ -103,6 +108,42 @@ def _new_annot(self, old_annot: str): return ast.Constant(old_annot) + def _annotation_from_docstring(self, node: ast.FunctionDef): + """Add annotations based on docstring. + + If any argument or return type of the function is not annotated, but + the corresponding docstring contains a type hint, the type hint is used + as the annotation. + """ + docstring = ast.get_docstring(node, clean=False) + if not docstring or "*Overload 1:*" in docstring: + # skip overloaded methods + return + + docstring = docstring.split("\n") + lines_to_remove = set() + + for line_no, line in enumerate(docstring): + if match := re.match(r"\W*:rtype:\W*(.+)", line): + node.returns = ast.Constant(match.group(1)) + lines_to_remove.add(line_no) + + if match := re.match(r"\W*:type:\W*(\w+):\W*(.+)", line): + for arg in node.args.args: + if arg.arg == match.group(1): + arg.annotation = ast.Constant(match.group(2)) + lines_to_remove.add(line_no) + + if lines_to_remove: + # Update docstring with type annotations removed + assert isinstance(node.body[0].value, ast.Constant) + new_docstring = "\n".join( + line + for line_no, line in enumerate(docstring) + if line_no not in lines_to_remove + ) + node.body[0].value = ast.Str(new_docstring) + def fix_typehints(infilename, outfilename): """Change SWIG-generated C++ typehints to Python typehints""" From 46d9d887991c15b8bb315e5d12dfd4f7330d5f04 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Wed, 6 Mar 2024 14:25:14 +0100 Subject: [PATCH 059/188] CMake: Fix/cleanup BLAS linking (#2357) No matter how we found the BLAS, make sure there is always BLAS::BLAS. Closes #2354 --- CMakeLists.txt | 97 ++++----------------------------- cmake/AmiciConfig.cmake | 25 ++++----- cmake/AmiciFindBLAS.cmake | 110 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 130 insertions(+), 102 deletions(-) create mode 100644 cmake/AmiciFindBLAS.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 324426f2a7..744847930d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -119,6 +119,7 @@ endif() # find dependencies include(GNUInstallDirs) +include(AmiciFindBLAS) find_package(OpenMP) find_package(Boost COMPONENTS chrono) @@ -142,75 +143,6 @@ message(STATUS "Found SUNDIALS: ${SUNDIALS_DIR}") set(GSL_LITE_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ThirdParty/gsl") -# AMICI requires BLAS, currently Intel MKL, CBLAS or MATLAB BLAS can be used. -# The latter is not supported via CMake yet. -set(BLAS - "CBLAS" - CACHE STRING "BLAS library to use") -set_property(CACHE BLAS PROPERTY STRINGS "CBLAS" "MKL" "ACCELERATE") -if(${BLAS} STREQUAL "MKL" OR DEFINED ENV{MKLROOT}) - if(DEFINED ENV{MKLROOT}) - # This is set by Environment Modules - message(STATUS "Using MKL_INCDIR and MKL_LIB from environment module") - set(BLAS - "MKL" - CACHE STRING "BLAS library to use" FORCE) - set(BLAS_INCLUDE_DIRS - "$ENV{MKL_INCDIR}" - CACHE STRING "" FORCE) - set(BLAS_LIBRARIES - "$ENV{MKL_LIB}" - CACHE STRING "" FORCE) - else() - set(BLAS_INCLUDE_DIRS - "" - CACHE STRING "") - set(BLAS_LIBRARIES - -lmkl - CACHE STRING "") - endif() -elseif( - NOT DEFINED ENV{BLAS_LIBS} - AND NOT DEFINED ENV{BLAS_CFLAGS} - AND NOT BLAS_FOUND) - # if nothing is specified via environment variables, let's try FindBLAS - if($(CMAKE_VERSION) VERSION_GREATER_EQUAL 3.22) - set(BLA_SIZEOF_INTEGER 8) - endif() - - if(APPLE AND (NOT DEFINED BLA_VENDOR OR BLA_VENDOR STREQUAL "All")) - set(BLA_VENDOR Apple) - find_package(BLAS) - if(BLAS_FOUND) - set_property( - TARGET BLAS::BLAS - APPEND - PROPERTY INTERFACE_COMPILE_DEFINITIONS ACCELERATE_NEW_LAPACK) - set_property( - TARGET BLAS::BLAS - APPEND - PROPERTY INTERFACE_COMPILE_DEFINITIONS ACCELERATE_LAPACK_ILP64) - else() - set(BLA_VENDOR "All") - endif() - - endif() - if(NOT BLAS_FOUND) - find_package(BLAS) - endif() - if(NOT BLAS_FOUND) - # Nothing specified by the user and FindBLAS didn't find anything; let's try - # if cblas is available on the system paths. - set(BLAS_INCLUDE_DIRS - "" - CACHE STRING "") - set(BLAS_LIBRARIES - -lcblas - CACHE STRING "") - endif() -endif() -add_compile_definitions(AMICI_BLAS_${BLAS}) - # Add target to create version file add_custom_target( version @@ -285,16 +217,6 @@ set(AMICI_SRC_LIST add_library(${PROJECT_NAME} ${AMICI_SRC_LIST}) -# legacy python package environment variables: -if(DEFINED ENV{BLAS_CFLAGS}) - target_compile_options(${PROJECT_NAME} PRIVATE "$ENV{BLAS_CFLAGS}") -endif() -if(DEFINED ENV{BLAS_LIBS}) - # Note that, on Windows, at least for ninja, this will only work with dashes - # instead of slashes in any linker options - target_link_libraries(${PROJECT_NAME} PUBLIC "$ENV{BLAS_LIBS}") -endif() - set(AMICI_CXX_OPTIONS "" CACHE STRING "C++ options for libamici (semicolon-separated)") @@ -315,10 +237,6 @@ target_include_directories( $ PRIVATE ${SUNDIALS_PRIVATE_INCLUDE_DIRS}) -if(NOT "${BLAS_INCLUDE_DIRS}" STREQUAL "") - target_include_directories(${PROJECT_NAME} PUBLIC ${BLAS_INCLUDE_DIRS}) -endif() - if("$ENV{ENABLE_AMICI_DEBUGGING}" OR "$ENV{ENABLE_GCOV_COVERAGE}" OR CMAKE_BUILD_TYPE MATCHES "Debug") @@ -354,11 +272,11 @@ target_link_libraries( SUNDIALS::sunnonlinsolfixedpoint_static SUNDIALS::cvodes_static SUNDIALS::idas_static - ${BLAS_LIBRARIES} $<$:Boost::chrono> $<$:OpenMP::OpenMP_CXX> ${CMAKE_DL_LIBS} PRIVATE + BLAS::BLAS $<$:SUNDIALS::sundials_sunlinsolsuperlumt> ) @@ -457,9 +375,11 @@ install( EXPORT AmiciTargets DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Amici" NAMESPACE Upstream::) -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/AmiciConfig.cmake - ${CMAKE_CURRENT_BINARY_DIR}/AmiciConfigVersion.cmake - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Amici) +install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/AmiciConfig.cmake + ${CMAKE_CURRENT_BINARY_DIR}/AmiciConfigVersion.cmake + ${CMAKE_CURRENT_LIST_DIR}/cmake/AmiciFindBLAS.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Amici) install(DIRECTORY ThirdParty/gsl/gsl TYPE INCLUDE) # When running from setup.py, this is a symlink we need to dereference @@ -467,6 +387,9 @@ get_filename_component(_swig_realpath "swig" REALPATH) install(DIRECTORY "${_swig_realpath}" DESTINATION ${CMAKE_INSTALL_DATADIR}/amici) +configure_file(cmake/AmiciFindBLAS.cmake + ${CMAKE_CURRENT_BINARY_DIR}/AmiciFindBLAS.cmake COPYONLY) + # Register package? if(EXPORT_PACKAGE) export(PACKAGE Amici) diff --git a/cmake/AmiciConfig.cmake b/cmake/AmiciConfig.cmake index ebef45d6e0..d1e86c151e 100644 --- a/cmake/AmiciConfig.cmake +++ b/cmake/AmiciConfig.cmake @@ -1,12 +1,5 @@ @PACKAGE_INIT@ -# TODO remove after cmake files for test models have been regenerated -# cmake >=3.27 -if(POLICY CMP0144) - cmake_policy(SET CMP0144 NEW) -endif(POLICY CMP0144) - - include(CMakeFindDependencyMacro) find_package(OpenMP) @@ -31,11 +24,13 @@ endif() if(NOT DEFINED KLU_ROOT) set(KLU_ROOT @CMAKE_SOURCE_DIR@/ThirdParty/SuiteSparse/) endif() -find_package(SuiteSparse_config REQUIRED) -find_package(AMD REQUIRED) -find_package(BTF REQUIRED) -find_package(COLAMD REQUIRED) -find_package(KLU REQUIRED) +find_dependency(SuiteSparse_config REQUIRED) +find_dependency(AMD REQUIRED) +find_dependency(BTF REQUIRED) +find_dependency(COLAMD REQUIRED) +find_dependency(KLU REQUIRED) + +include("${CMAKE_CURRENT_LIST_DIR}/AmiciFindBLAS.cmake") add_library(SUNDIALS::KLU INTERFACE IMPORTED) target_link_libraries( @@ -48,15 +43,15 @@ target_link_libraries( set_target_properties(SUNDIALS::KLU PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${KLU_INCLUDE_DIR}") -find_package(SUNDIALS REQUIRED PATHS +find_dependency(SUNDIALS REQUIRED PATHS "@CMAKE_SOURCE_DIR@/ThirdParty/sundials/build/@CMAKE_INSTALL_LIBDIR@/cmake/sundials/") if(@Boost_CHRONO_FOUND@) - find_package(Boost COMPONENTS chrono REQUIRED) + find_dependency(Boost COMPONENTS chrono REQUIRED) endif() if(@HDF5_FOUND@) - find_package( + find_dependency( HDF5 COMPONENTS C HL CXX REQUIRED) diff --git a/cmake/AmiciFindBLAS.cmake b/cmake/AmiciFindBLAS.cmake new file mode 100644 index 0000000000..6b2f9cf5f9 --- /dev/null +++ b/cmake/AmiciFindBLAS.cmake @@ -0,0 +1,110 @@ +# Find a BLAS +# +# AMICI requires BLAS, currently Intel MKL, CBLAS or MATLAB BLAS can be used. +# The latter is not supported via CMake yet. +# +# This module defines the BLAS::BLAS IMPORTED target. + +set(BLAS + "CBLAS" + CACHE STRING "BLAS library to use") +set_property(CACHE BLAS PROPERTY STRINGS "CBLAS" "MKL" "ACCELERATE") + +if(${BLAS} STREQUAL "MKL" OR DEFINED ENV{MKLROOT}) + if(DEFINED ENV{MKLROOT}) + # This is set by Environment Modules + message(STATUS "Using MKL_INCDIR and MKL_LIB from environment module") + set(BLAS + "MKL" + CACHE STRING "BLAS library to use" FORCE) + set(BLAS_INCLUDE_DIRS + "$ENV{MKL_INCDIR}" + CACHE STRING "" FORCE) + set(BLAS_LIBRARIES + "$ENV{MKL_LIB}" + CACHE STRING "" FORCE) + else() + set(BLAS_INCLUDE_DIRS + "" + CACHE STRING "") + set(BLAS_LIBRARIES + -lmkl + CACHE STRING "") + endif() +elseif( + NOT DEFINED ENV{BLAS_LIBS} + AND NOT DEFINED ENV{BLAS_CFLAGS} + AND NOT BLAS_FOUND) + # if nothing is specified via environment variables, let's try FindBLAS + if($(CMAKE_VERSION) VERSION_GREATER_EQUAL 3.22) + set(BLA_SIZEOF_INTEGER 8) + endif() + + if(APPLE AND (NOT DEFINED BLA_VENDOR OR BLA_VENDOR STREQUAL "All")) + set(BLA_VENDOR Apple) + find_package(BLAS) + if(BLAS_FOUND) + message(STATUS "Found Apple Accelerate BLAS") + set_property( + TARGET BLAS::BLAS + APPEND + PROPERTY INTERFACE_COMPILE_DEFINITIONS ACCELERATE_NEW_LAPACK) + set_property( + TARGET BLAS::BLAS + APPEND + PROPERTY INTERFACE_COMPILE_DEFINITIONS ACCELERATE_LAPACK_ILP64) + else() + set(BLA_VENDOR "All") + endif() + + endif() + if(NOT BLAS_FOUND) + find_package(BLAS) + if(BLAS_FOUND) + message(STATUS "Found BLAS via FindBLAS") + endif() + endif() + if(NOT BLAS_FOUND) + # Nothing specified by the user and FindBLAS didn't find anything; let's try + # if cblas is available on the system paths. + set(BLAS_INCLUDE_DIRS + "" + CACHE STRING "") + set(BLAS_LIBRARIES + -lcblas + CACHE STRING "") + endif() +endif() + +# Create an imported target +if(NOT TARGET BLAS::BLAS) + add_library(BLAS INTERFACE) + set_target_properties(BLAS PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${BLAS_INCLUDE_DIRS}" + INTERFACE_LINK_LIBRARIES "${BLAS_LIBRARIES}") + add_library(BLAS::BLAS ALIAS BLAS) + install(TARGETS BLAS EXPORT BLAS) + export(EXPORT BLAS NAMESPACE BLAS::) + install( + EXPORT BLAS + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Amici" + NAMESPACE BLAS::) + + + # legacy python package environment variables: + if(DEFINED ENV{BLAS_CFLAGS}) + target_compile_options(BLAS INTERFACE "$ENV{BLAS_CFLAGS}") + endif() + if(DEFINED ENV{BLAS_LIBS}) + # Note that, on Windows, at least for ninja, this will only work with dashes + # instead of slashes in any linker options + target_link_libraries(BLAS INTERFACE "$ENV{BLAS_LIBS}") + endif() + + if(NOT "${BLAS_INCLUDE_DIRS}" STREQUAL "") + target_include_directories(BLAS INTERFACE ${BLAS_INCLUDE_DIRS}) + endif() + target_compile_definitions(BLAS INTERFACE "AMICI_BLAS_${BLAS}") +else() + target_compile_definitions(BLAS::BLAS INTERFACE "AMICI_BLAS_${BLAS}") +endif() From 536a68e1182b1005622e7539a7f4a31fc9a0ab94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20Fr=C3=B6hlich?= Date: Wed, 6 Mar 2024 17:59:47 +0000 Subject: [PATCH 060/188] Fix venv related installation issues (#2356) * fix venv and BLAS * fixup * fixup? * Update runNotebook.sh * revert BLAS * remove virtualenv * remove venv creation from installAmiciSource * clear venv in buildInstall.sh * cleanup * doh * add venv back to installAmiciSource * readd numpy * export python executable in source install --- .github/workflows/test_petab_test_suite.yml | 8 +++---- .github/workflows/test_python_cplusplus.yml | 14 ++++++------ .github/workflows/test_python_ver_matrix.yml | 6 ++--- .gitignore | 1 + scripts/buildAmici.sh | 24 +++++++++++++++++++- scripts/installAmiciArchive.sh | 8 +++---- scripts/installAmiciSource.sh | 7 ++++-- scripts/run-SBMLTestsuite.sh | 2 +- scripts/run-codecov.sh | 2 +- scripts/run-python-tests.sh | 2 +- scripts/run-valgrind-py.sh | 2 +- scripts/runNotebook.sh | 2 +- 12 files changed, 52 insertions(+), 26 deletions(-) diff --git a/.github/workflows/test_petab_test_suite.yml b/.github/workflows/test_petab_test_suite.yml index 6301269e03..b6465d554b 100644 --- a/.github/workflows/test_petab_test_suite.yml +++ b/.github/workflows/test_petab_test_suite.yml @@ -57,7 +57,7 @@ jobs: - name: Install petab run: | - source ./build/venv/bin/activate \ + source ./venv/bin/activate \ && pip3 install wheel pytest shyaml pytest-cov pysb # retrieve test models @@ -65,19 +65,19 @@ jobs: run: | git clone --depth 1 --branch main \ https://github.com/PEtab-dev/petab_test_suite \ - && source ./build/venv/bin/activate \ + && source ./venv/bin/activate \ && cd petab_test_suite && pip3 install -e . - name: Run PEtab-related unit tests run: | - source ./build/venv/bin/activate \ + source ./venv/bin/activate \ && pytest --cov-report=xml:coverage.xml \ --cov=./ python/tests/test_*petab*.py python/tests/petab/ # run test models - name: Run PEtab test suite run: | - source ./build/venv/bin/activate \ + source ./venv/bin/activate \ && AMICI_PARALLEL_COMPILE="" pytest -v \ --cov-report=xml:coverage.xml \ --cov-append \ diff --git a/.github/workflows/test_python_cplusplus.yml b/.github/workflows/test_python_cplusplus.yml index 58dd5480e5..f729d47867 100644 --- a/.github/workflows/test_python_cplusplus.yml +++ b/.github/workflows/test_python_cplusplus.yml @@ -45,11 +45,11 @@ jobs: run: scripts/installAmiciSource.sh - name: Check OpenMP support - run: source build/venv/bin/activate && python -c "import amici; import sys; sys.exit(not amici.compiledWithOpenMP())" + run: source venv/bin/activate && python -c "import amici; import sys; sys.exit(not amici.compiledWithOpenMP())" - name: Python tests (part 1) run: | - source build/venv/bin/activate \ + source venv/bin/activate \ && pytest \ --ignore-glob=*petab* \ --ignore-glob=*test_splines.py \ @@ -64,7 +64,7 @@ jobs: - name: Python tests splines if: ${{ github.base_ref == 'master' || github.event.merge_group.base_ref == 'master'}} run: | - source build/venv/bin/activate \ + source venv/bin/activate \ && pytest \ --cov=amici \ --cov-report=xml:"${AMICI_DIR}/build/coverage_py.xml" \ @@ -138,7 +138,7 @@ jobs: - name: Python tests run: | - source build/venv/bin/activate \ + source venv/bin/activate \ && pytest \ --cov=amici \ --cov-report=xml:"${AMICI_DIR}/build/coverage_py.xml" \ @@ -214,7 +214,7 @@ jobs: - name: Install notebook dependencies run: | - source build/venv/bin/activate \ + source venv/bin/activate \ && pip install jax[cpu] - name: example notebooks @@ -251,7 +251,7 @@ jobs: run: scripts/installAmiciSource.sh - name: Check OpenMP support - run: source build/venv/bin/activate && python -c "import amici; import sys; sys.exit(not amici.compiledWithOpenMP())" + run: source venv/bin/activate && python -c "import amici; import sys; sys.exit(not amici.compiledWithOpenMP())" - name: cppcheck run: scripts/run-cppcheck.sh @@ -296,7 +296,7 @@ jobs: scripts/installAmiciSource.sh - name: Check OpenMP support - run: source build/venv/bin/activate && python -c "import amici; import sys; sys.exit(not amici.compiledWithOpenMP())" + run: source venv/bin/activate && python -c "import amici; import sys; sys.exit(not amici.compiledWithOpenMP())" - name: Get BioNetGen run: scripts/buildBNGL.sh diff --git a/.github/workflows/test_python_ver_matrix.yml b/.github/workflows/test_python_ver_matrix.yml index 28b86d1c01..414daadccc 100644 --- a/.github/workflows/test_python_ver_matrix.yml +++ b/.github/workflows/test_python_ver_matrix.yml @@ -51,15 +51,15 @@ jobs: run: scripts/installAmiciSource.sh # install pysb before sympy to allow for sympy>=1.12 (https://github.com/pysb/pysb/commit/e83937cb8c74afc9b2fa96595b68464946745f33) - - run: source build/venv/bin/activate && pip3 install git+https://github.com/pysb/pysb + - run: source venv/bin/activate && pip3 install git+https://github.com/pysb/pysb # until sympy>1.12 is released - - run: source build/venv/bin/activate && pip3 install git+https://github.com/sympy/sympy.git@master + - run: source venv/bin/activate && pip3 install git+https://github.com/sympy/sympy.git@master if: matrix.python-version == '3.12' - name: Python tests run: | - source build/venv/bin/activate \ + source venv/bin/activate \ && python3 -m pytest \ --durations=10 \ --ignore-glob=*petab* \ diff --git a/.gitignore b/.gitignore index 60b9ff5031..4dc2884415 100644 --- a/.gitignore +++ b/.gitignore @@ -202,3 +202,4 @@ CS_Signalling_ERBB_RAS_AKT/ cache_fiddy/* debug/* tests/benchmark-models/cache_fiddy/* +venv/* diff --git a/scripts/buildAmici.sh b/scripts/buildAmici.sh index 507a391621..2a3e7c7531 100755 --- a/scripts/buildAmici.sh +++ b/scripts/buildAmici.sh @@ -25,7 +25,29 @@ else fi # required for build swig interface -pip show numpy > /dev/null || python3 -m pip install numpy +venv_dir="${amici_path}/venv" +set +e +mkdir -p "${venv_dir}" +python3 -m venv "${venv_dir}" --clear +# in case this fails (usually due to missing ensurepip, try getting pip +# manually +if [[ $? ]]; then + set -e + python3 -m venv "${venv_dir}" --clear --without-pip + source "${venv_dir}/bin/activate" + get_pip=${amici_path}/get-pip.py + curl "https://bootstrap.pypa.io/get-pip.py" -o "${get_pip}" + python3 "${get_pip}" + rm "${get_pip}" +else + set -e + source "${venv_dir}/bin/activate" +fi + +# set python executable for cmake +export PYTHON_EXECUTABLE="${amici_path}/venv/bin/python" +# install numpy +python3 -m pip install numpy ${cmake} \ -Wdev -DAMICI_CXX_OPTIONS="-Wall;-Wextra${extra_cxx_flags}" \ diff --git a/scripts/installAmiciArchive.sh b/scripts/installAmiciArchive.sh index 25de4f9f93..f427a91256 100755 --- a/scripts/installAmiciArchive.sh +++ b/scripts/installAmiciArchive.sh @@ -18,18 +18,18 @@ rm -f ${AMICI_PATH}/python/sdist/amici/amici_without_hdf5.py # test install from archive set +e -python3 -m venv ${AMICI_PATH}/build/venvArchive --clear +python3 -m venv ${AMICI_PATH}/venvArchive --clear # in case this fails (usually due to missing ensurepip, try getting pip # manually if [[ $? ]]; then set -e - python3 -m venv ${AMICI_PATH}/build/venvArchive --clear --without-pip - source ${AMICI_PATH}/build/venvArchive/bin/activate + python3 -m venv ${AMICI_PATH}/venvArchive --clear --without-pip + source ${AMICI_PATH}/venvArchive/bin/activate curl https://bootstrap.pypa.io/get-pip.py -o ${AMICI_PATH}/build/get-pip.py python ${AMICI_PATH}/build/get-pip.py else set -e - source ${AMICI_PATH}/build/venvArchive/bin/activate + source ${AMICI_PATH}/venvArchive/bin/activate fi pip install $(ls -t ${AMICI_PATH}/build/python/amici-*.tar.gz | head -1) diff --git a/scripts/installAmiciSource.sh b/scripts/installAmiciSource.sh index bbb4bf4a83..c074c89d46 100755 --- a/scripts/installAmiciSource.sh +++ b/scripts/installAmiciSource.sh @@ -5,12 +5,12 @@ set -e SCRIPT_PATH=$(dirname $BASH_SOURCE) AMICI_PATH=$(cd "$SCRIPT_PATH/.." && pwd) -venv_dir="${AMICI_PATH}/build/venv" # Disabled until cmake package is made compatible with updated setup.py #make python-wheel #pip3 install --user --prefix= `ls -t ${AMICI_PATH}/build/python/amici-*.whl | head -1` # test install from setup.py +venv_dir="${AMICI_PATH}/venv" set +e mkdir -p "${venv_dir}" python3 -m venv "${venv_dir}" --clear @@ -20,7 +20,7 @@ if [[ $? ]]; then set -e python3 -m venv "${venv_dir}" --clear --without-pip source "${venv_dir}/bin/activate" - get_pip=${AMICI_PATH}/build/get-pip.py + get_pip=${AMICI_PATH}/get-pip.py curl "https://bootstrap.pypa.io/get-pip.py" -o "${get_pip}" python3 "${get_pip}" rm "${get_pip}" @@ -29,6 +29,9 @@ else source "${venv_dir}/bin/activate" fi +# set python executable for cmake +export PYTHON_EXECUTABLE="${AMICI_PATH}/venv/bin/python" + python -m pip install --upgrade pip wheel python -m pip install --upgrade pip setuptools cmake_build_extension numpy python -m pip install git+https://github.com/FFroehlich/pysb@fix_pattern_matching # pin to PR for SPM with compartments diff --git a/scripts/run-SBMLTestsuite.sh b/scripts/run-SBMLTestsuite.sh index 97c2033751..a3dcee1681 100755 --- a/scripts/run-SBMLTestsuite.sh +++ b/scripts/run-SBMLTestsuite.sh @@ -9,7 +9,7 @@ if [[ ! -d "tests/sbml-test-suite" ]]; then mv -f ./sbml-test-suite ./tests/sbml-test-suite fi -source build/venv/bin/activate +source venv/bin/activate pip show pytest-xdist > /dev/null 2>&1 || pip install pytest-xdist pip install coverage pytest-cov diff --git a/scripts/run-codecov.sh b/scripts/run-codecov.sh index 42d76f7a0e..f5f253b5c9 100755 --- a/scripts/run-codecov.sh +++ b/scripts/run-codecov.sh @@ -4,7 +4,7 @@ script_path=$(dirname $BASH_SOURCE) amici_path=$(cd "$script_path"/.. && pwd) -source "${amici_path}"/build/venv/bin/activate +source "${amici_path}"/venv/bin/activate pip install coverage pytest pytest-cov if [[ -z "${BNGPATH}" ]]; then diff --git a/scripts/run-python-tests.sh b/scripts/run-python-tests.sh index d58a1c8dec..0fed628fce 100755 --- a/scripts/run-python-tests.sh +++ b/scripts/run-python-tests.sh @@ -12,7 +12,7 @@ if [[ -z "${BNGPATH}" ]]; then fi cd "${amici_path}"/python/tests -source "${amici_path}"/build/venv/bin/activate +source "${amici_path}"/venv/bin/activate # PEtab tests are run separately pytest \ diff --git a/scripts/run-valgrind-py.sh b/scripts/run-valgrind-py.sh index c2a6239ad4..9621c24d31 100755 --- a/scripts/run-valgrind-py.sh +++ b/scripts/run-valgrind-py.sh @@ -15,7 +15,7 @@ if [ $# -eq 0 ] then # No arguments supplied, run all tests cd "${amici_path}"/python/tests - source "${amici_path}"/build/venv/bin/activate + source "${amici_path}"/venv/bin/activate command=(python -m pytest -vv --ignore-glob=*petab* -W 'ignore:Signature ') # ^ ignores the following warning that occurs only under valgrind, # e.g. `valgrind python -c "import h5py"`: diff --git a/scripts/runNotebook.sh b/scripts/runNotebook.sh index bf1ef8d5e0..ee62b59cfc 100755 --- a/scripts/runNotebook.sh +++ b/scripts/runNotebook.sh @@ -25,7 +25,7 @@ if [ $# -eq 0 ]; then exit 1 fi -source ${AMICI_PATH}/build/venv/bin/activate +source ${AMICI_PATH}/venv/bin/activate pip3 show nbconvert || pip3 install --upgrade nbconvert pip3 show ipykernel || (pip3 install --upgrade ipykernel && python3 -m ipykernel install --user --name amici --display-name "Python (amici)") From 19f7bafe9dfd83cea8fc982661d294b8296c296f Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Wed, 6 Mar 2024 20:10:23 +0100 Subject: [PATCH 061/188] SBML import: Evaluate initial assignments (#2359) Evaluate initial assignments to parameters to determine whether the targets are amici parameters or expressions. Related to https://github.com/AMICI-dev/AMICI/pull/2304. For example, it the initial assignment is `log(10)`, this should still be a parameter instead of an expression, but `sympy.log(10).is_Number` will be `False`. --- python/sdist/amici/sbml_import.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/python/sdist/amici/sbml_import.py b/python/sdist/amici/sbml_import.py index 61cb79043e..55d6dad903 100644 --- a/python/sdist/amici/sbml_import.py +++ b/python/sdist/amici/sbml_import.py @@ -1143,7 +1143,12 @@ def _process_parameters( # parameter ID => initial assignment sympy expression par_id_to_ia = { - par.getId(): ia + par.getId(): ia.subs( + { + BooleanTrue(): sp.Float(1.0), + BooleanFalse(): sp.Float(0.0), + } + ).evalf() for par in self.sbml.getListOfParameters() if (ia := self._get_element_initial_assignment(par.getId())) is not None From dcc81d154bd05ea759c881ee33442871f2898403 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Wed, 6 Mar 2024 21:07:31 +0100 Subject: [PATCH 062/188] Fix issues with paths containing blanks for sundials (#2361) ... --- ThirdParty/sundials/CMakeLists.txt | 6 +++--- ThirdParty/sundials/cmake/tpl/FindKLU.cmake | 2 +- ThirdParty/sundials/cmake/tpl/SundialsKLU.cmake | 17 +++++++++-------- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/ThirdParty/sundials/CMakeLists.txt b/ThirdParty/sundials/CMakeLists.txt index f535def9d0..e23332cf31 100644 --- a/ThirdParty/sundials/CMakeLists.txt +++ b/ThirdParty/sundials/CMakeLists.txt @@ -27,9 +27,9 @@ project(SUNDIALS C) # Specify the location of additional CMAKE modules set(CMAKE_MODULE_PATH - ${PROJECT_SOURCE_DIR}/cmake - ${PROJECT_SOURCE_DIR}/cmake/macros - ${PROJECT_SOURCE_DIR}/cmake/tpl + "${PROJECT_SOURCE_DIR}/cmake" + "${PROJECT_SOURCE_DIR}/cmake/macros" + "${PROJECT_SOURCE_DIR}/cmake/tpl" ) # MACRO definitions diff --git a/ThirdParty/sundials/cmake/tpl/FindKLU.cmake b/ThirdParty/sundials/cmake/tpl/FindKLU.cmake index 9e25883c4f..98465aa180 100644 --- a/ThirdParty/sundials/cmake/tpl/FindKLU.cmake +++ b/ThirdParty/sundials/cmake/tpl/FindKLU.cmake @@ -101,7 +101,7 @@ if (NOT SUITESPARSECONFIG_LIBRARY) mark_as_advanced(SUITESPARSECONFIG_LIBRARY) endif () -set(KLU_LIBRARIES ${KLU_LIBRARY} ${AMD_LIBRARY} ${COLAMD_LIBRARY} ${BTF_LIBRARY} ${SUITESPARSECONFIG_LIBRARY}) +set(KLU_LIBRARIES "${KLU_LIBRARY}" "${AMD_LIBRARY}" "${COLAMD_LIBRARY}" "${BTF_LIBRARY}" "${SUITESPARSECONFIG_LIBRARY}") # set package variables including KLU_FOUND include(FindPackageHandleStandardArgs) diff --git a/ThirdParty/sundials/cmake/tpl/SundialsKLU.cmake b/ThirdParty/sundials/cmake/tpl/SundialsKLU.cmake index 8a9e111c6c..fd7947cac4 100644 --- a/ThirdParty/sundials/cmake/tpl/SundialsKLU.cmake +++ b/ThirdParty/sundials/cmake/tpl/SundialsKLU.cmake @@ -49,7 +49,8 @@ find_package(KLU REQUIRED) message(STATUS "KLU_LIBRARIES: ${KLU_LIBRARIES}") message(STATUS "KLU_INCLUDE_DIR: ${KLU_INCLUDE_DIR}") - +list(JOIN KLU_LIBRARIES "\" \"" KLU_LIBRARIES_TMP) +set(KLU_LIBRARIES_TMP \"${KLU_LIBRARIES_TMP}\") # ----------------------------------------------------------------------------- # Section 4: Test the TPL # ----------------------------------------------------------------------------- @@ -58,11 +59,11 @@ if(KLU_FOUND AND (NOT KLU_WORKS)) # Do any checks which don't require compilation first. # Create the KLU_TEST directory - set(KLU_TEST_DIR ${PROJECT_BINARY_DIR}/KLU_TEST) + set(KLU_TEST_DIR "${PROJECT_BINARY_DIR}/KLU_TEST") file(MAKE_DIRECTORY ${KLU_TEST_DIR}) # Create a CMakeLists.txt file - file(WRITE ${KLU_TEST_DIR}/CMakeLists.txt + file(WRITE "${KLU_TEST_DIR}/CMakeLists.txt" "CMAKE_MINIMUM_REQUIRED(VERSION 3.1.3)\n" "PROJECT(ltest C)\n" "SET(CMAKE_VERBOSE_MAKEFILE ON)\n" @@ -72,12 +73,12 @@ if(KLU_FOUND AND (NOT KLU_WORKS)) "SET(CMAKE_C_FLAGS_DEBUG \"${CMAKE_C_FLAGS_DEBUG}\")\n" "SET(CMAKE_C_FLAGS_RELWITHDEBUGINFO \"${CMAKE_C_FLAGS_RELWITHDEBUGINFO}\")\n" "SET(CMAKE_C_FLAGS_MINSIZE \"${CMAKE_C_FLAGS_MINSIZE}\")\n" - "INCLUDE_DIRECTORIES(${KLU_INCLUDE_DIR})\n" + "INCLUDE_DIRECTORIES(\"${KLU_INCLUDE_DIR}\")\n" "ADD_EXECUTABLE(ltest ltest.c)\n" - "TARGET_LINK_LIBRARIES(ltest ${KLU_LIBRARIES})\n") + "TARGET_LINK_LIBRARIES(ltest ${KLU_LIBRARIES_TMP})\n") # Create a C source file which calls a KLU function - file(WRITE ${KLU_TEST_DIR}/ltest.c + file(WRITE "${KLU_TEST_DIR}/ltest.c" "\#include \"klu.h\"\n" "int main(){\n" "klu_common Common;\n" @@ -87,10 +88,10 @@ if(KLU_FOUND AND (NOT KLU_WORKS)) # To ensure we do not use stuff from the previous attempts, # we must remove the CMakeFiles directory. - file(REMOVE_RECURSE ${KLU_TEST_DIR}/CMakeFiles) + file(REMOVE_RECURSE "${KLU_TEST_DIR}/CMakeFiles") # Attempt to build and link the "ltest" executable - try_compile(COMPILE_OK ${KLU_TEST_DIR} ${KLU_TEST_DIR} ltest + try_compile(COMPILE_OK "${KLU_TEST_DIR}" "${KLU_TEST_DIR}" ltest OUTPUT_VARIABLE COMPILE_OUTPUT) # Process test result From ddffa93f707eb4f129ef773c00ba6e0ddebcb963 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Wed, 6 Mar 2024 21:40:40 +0100 Subject: [PATCH 063/188] Enable specifying maximum solver step size (#2360) Expose [CVodeSetMaxStep](https://sundials.readthedocs.io/en/latest/cvodes/Usage/SIM.html#c.CVodeSetMaxStep). Closes #2358. --- include/amici/serialization.h | 1 + include/amici/solver.h | 20 ++++++++++++++++++++ include/amici/solver_cvodes.h | 2 ++ include/amici/solver_idas.h | 2 ++ src/hdf5.cpp | 11 +++++++++++ src/solver.cpp | 10 ++++++++++ src/solver_cvodes.cpp | 6 ++++++ src/solver_idas.cpp | 6 ++++++ 8 files changed, 58 insertions(+) diff --git a/include/amici/serialization.h b/include/amici/serialization.h index b5bfe466fc..97172e8375 100644 --- a/include/amici/serialization.h +++ b/include/amici/serialization.h @@ -87,6 +87,7 @@ void serialize(Archive& ar, amici::Solver& s, unsigned int const /*version*/) { ar & s.maxtime_; ar & s.max_conv_fails_; ar & s.max_nonlin_iters_; + ar & s.max_step_size_; } /** diff --git a/include/amici/solver.h b/include/amici/solver.h index ab4f8ef427..a52e80e82d 100644 --- a/include/amici/solver.h +++ b/include/amici/solver.h @@ -964,6 +964,18 @@ class Solver { */ int getMaxConvFails() const; + /** + * @brief Set the maximum step size + * @param max_step_size maximum step size. `0.0` means no limit. + */ + void setMaxStepSize(realtype max_step_size); + + /** + * @brief Get the maximum step size + * @return maximum step size + */ + realtype getMaxStepSize() const; + /** * @brief Serialize Solver (see boost::serialization::serialize) * @param ar Archive to serialize to @@ -1752,6 +1764,11 @@ class Solver { */ virtual void apply_max_conv_fails() const = 0; + /** + * @brief Apply the allowed maximum stepsize to the solver. + */ + virtual void apply_max_step_size() const = 0; + /** state (dimension: nx_solver) */ mutable AmiVector x_{0}; @@ -1891,6 +1908,9 @@ class Solver { * step */ int max_conv_fails_{10}; + /** Maximum allowed step size */ + realtype max_step_size_{0.0}; + /** CPU time, forward solve */ mutable realtype cpu_time_{0.0}; diff --git a/include/amici/solver_cvodes.h b/include/amici/solver_cvodes.h index 592298b7ec..8abf407f1d 100644 --- a/include/amici/solver_cvodes.h +++ b/include/amici/solver_cvodes.h @@ -253,6 +253,8 @@ class CVodeSolver : public Solver { void apply_max_nonlin_iters() const override; void apply_max_conv_fails() const override; + + void apply_max_step_size() const override; }; } // namespace amici diff --git a/include/amici/solver_idas.h b/include/amici/solver_idas.h index 079cee6399..3d8a9df485 100644 --- a/include/amici/solver_idas.h +++ b/include/amici/solver_idas.h @@ -233,6 +233,8 @@ class IDASolver : public Solver { void apply_max_nonlin_iters() const override; void apply_max_conv_fails() const override; + + void apply_max_step_size() const override; }; } // namespace amici diff --git a/src/hdf5.cpp b/src/hdf5.cpp index 4efed799a8..e980619e5f 100644 --- a/src/hdf5.cpp +++ b/src/hdf5.cpp @@ -802,6 +802,11 @@ void writeSolverSettingsToHDF5( file.getId(), hdf5Location.c_str(), "maxtime", &dbuffer, 1 ); + dbuffer = solver.getMaxStepSize(); + H5LTset_attribute_double( + file.getId(), hdf5Location.c_str(), "max_step_size", &dbuffer, 1 + ); + ibuffer = gsl::narrow(solver.getMaxSteps()); H5LTset_attribute_int( file.getId(), hdf5Location.c_str(), "maxsteps", &ibuffer, 1 @@ -1000,6 +1005,12 @@ void readSolverSettingsFromHDF5( ); } + if (attributeExists(file, datasetPath, "max_step_size")) { + solver.setMaxStepSize( + getDoubleScalarAttribute(file, datasetPath, "max_step_size") + ); + } + if (attributeExists(file, datasetPath, "maxsteps")) { solver.setMaxSteps(getIntScalarAttribute(file, datasetPath, "maxsteps") ); diff --git a/src/solver.cpp b/src/solver.cpp index 88185817dd..352017a6ee 100644 --- a/src/solver.cpp +++ b/src/solver.cpp @@ -46,6 +46,7 @@ Solver::Solver(Solver const& other) , check_sensi_steadystate_conv_(other.check_sensi_steadystate_conv_) , max_nonlin_iters_(other.max_nonlin_iters_) , max_conv_fails_(other.max_conv_fails_) + , max_step_size_(other.max_step_size_) , maxstepsB_(other.maxstepsB_) , sensi_(other.sensi_) {} @@ -195,6 +196,7 @@ void Solver::setup( apply_max_nonlin_iters(); apply_max_conv_fails(); + apply_max_step_size(); cpu_time_ = 0.0; cpu_timeB_ = 0.0; @@ -691,6 +693,14 @@ void Solver::setMaxConvFails(int max_conv_fails) { int Solver::getMaxConvFails() const { return max_conv_fails_; } +void Solver::setMaxStepSize(realtype max_step_size) { + if (max_step_size < 0) + throw AmiException("max_step_size must be non-negative."); + max_step_size_ = max_step_size; +} + +realtype Solver::getMaxStepSize() const { return max_step_size_; } + int Solver::getNewtonMaxSteps() const { return newton_maxsteps_; } void Solver::setNewtonMaxSteps(int const newton_maxsteps) { diff --git a/src/solver_cvodes.cpp b/src/solver_cvodes.cpp index 0952724457..626b4cc050 100644 --- a/src/solver_cvodes.cpp +++ b/src/solver_cvodes.cpp @@ -270,6 +270,12 @@ void CVodeSolver::apply_max_conv_fails() const { throw CvodeException(status, "CVodeSetMaxConvFails"); } +void CVodeSolver::apply_max_step_size() const { + int status = CVodeSetMaxStep(solver_memory_.get(), getMaxStepSize()); + if (status != CV_SUCCESS) + throw CvodeException(status, "CVodeSetMaxStep"); +} + Solver* CVodeSolver::clone() const { return new CVodeSolver(*this); } void CVodeSolver::allocateSolver() const { diff --git a/src/solver_idas.cpp b/src/solver_idas.cpp index fa08bc29f9..d97d6b3911 100644 --- a/src/solver_idas.cpp +++ b/src/solver_idas.cpp @@ -267,6 +267,12 @@ void IDASolver::apply_max_conv_fails() const { throw IDAException(status, "IDASetMaxConvFails"); } +void IDASolver::apply_max_step_size() const { + int status = IDASetMaxStep(solver_memory_.get(), getMaxStepSize()); + if (status != IDA_SUCCESS) + throw IDAException(status, "IDASetMaxStep"); +} + Solver* IDASolver::clone() const { return new IDASolver(*this); } void IDASolver::allocateSolver() const { From d7603b10d0837e75a15949db3b8f810fe7a6b7f7 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Wed, 6 Mar 2024 22:05:31 +0100 Subject: [PATCH 064/188] Fix Solver::dky_ initialization (#2362) `Solver::dky_` is always empty. Any call to `CVodeGetDky` should fail. Seems like it's never used. --- src/solver.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/solver.cpp b/src/solver.cpp index 352017a6ee..c1c048e4cc 100644 --- a/src/solver.cpp +++ b/src/solver.cpp @@ -1186,6 +1186,8 @@ void Solver::resetMutableMemory(int const nx, int const nplist, int const nquad) sx_ = AmiVectorArray(nx, nplist); sdx_ = AmiVectorArray(nx, nplist); + dky_ = AmiVector(nx); + xB_ = AmiVector(nx); dxB_ = AmiVector(nx); xQB_ = AmiVector(nquad); From 2316f2ad0ab0a13d07358b328f519ff01b11af58 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Wed, 6 Mar 2024 23:01:51 +0100 Subject: [PATCH 065/188] Fix petab import: initial assignment targets as constant parameters (#2345) During PEtab import, parameters that are targets of initial assignments have so far not been turned into constant parameters, because they didn't exist in the amici model (see #2304). Now that those parameters remain in the model, they should be turned into constant parameters, unless specified otherwise. Requires https://github.com/PEtab-dev/libpetab-python/pull/248, otherwise PEtab parameter mapping will provide potentially incorrect values for those parameters. --- python/sdist/amici/petab/sbml_import.py | 20 ++++++++++++--- python/sdist/amici/sbml_import.py | 15 +++-------- python/sdist/setup.cfg | 2 +- python/tests/test_petab_import.py | 34 ++++++++++++++++++++++--- 4 files changed, 52 insertions(+), 19 deletions(-) diff --git a/python/sdist/amici/petab/sbml_import.py b/python/sdist/amici/petab/sbml_import.py index 6388d6f8b0..e349683505 100644 --- a/python/sdist/amici/petab/sbml_import.py +++ b/python/sdist/amici/petab/sbml_import.py @@ -518,14 +518,26 @@ def _get_fixed_parameters_sbml( petab_problem, non_estimated_parameters_as_constants ) - # exclude targets of rules or initial assignments + # exclude targets of rules or initial assignments that are not numbers sbml_model = petab_problem.model.sbml_model + parser_settings = libsbml.L3ParserSettings() + parser_settings.setModel(sbml_model) + parser_settings.setParseUnits(libsbml.L3P_NO_UNITS) + for fixed_parameter in fixed_parameters.copy(): # check global parameters - if sbml_model.getInitialAssignmentBySymbol( - fixed_parameter - ) or sbml_model.getRuleByVariable(fixed_parameter): + if sbml_model.getRuleByVariable(fixed_parameter): fixed_parameters.remove(fixed_parameter) + continue + if ia := sbml_model.getInitialAssignmentBySymbol(fixed_parameter): + sym_math = sp.sympify( + libsbml.formulaToL3StringWithSettings( + ia.getMath(), parser_settings + ) + ) + if not sym_math.is_Number: + fixed_parameters.remove(fixed_parameter) + continue return list(sorted(fixed_parameters)) diff --git a/python/sdist/amici/sbml_import.py b/python/sdist/amici/sbml_import.py index 55d6dad903..8f7f67b02f 100644 --- a/python/sdist/amici/sbml_import.py +++ b/python/sdist/amici/sbml_import.py @@ -187,18 +187,11 @@ def __init__( self._reset_symbols() - # http://sbml.org/Software/libSBML/5.18.0/docs/python-api/classlibsbml_1_1_l3_parser_settings.html#abcfedd34efd3cae2081ba8f42ea43f52 + # https://sbml.org/software/libsbml/5.18.0/docs/formatted/python-api/classlibsbml_1_1_l3_parser_settings.html#ab30d7ed52ca24cbb842d0a7fed7f4bfd # all defaults except disable unit parsing - self.sbml_parser_settings = sbml.L3ParserSettings( - self.sbml, - sbml.L3P_PARSE_LOG_AS_LOG10, - sbml.L3P_EXPAND_UNARY_MINUS, - sbml.L3P_NO_UNITS, - sbml.L3P_AVOGADRO_IS_CSYMBOL, - sbml.L3P_COMPARE_BUILTINS_CASE_INSENSITIVE, - None, - sbml.L3P_MODULO_IS_PIECEWISE, - ) + self.sbml_parser_settings = sbml.L3ParserSettings() + self.sbml_parser_settings.setModel(self.sbml) + self.sbml_parser_settings.setParseUnits(sbml.L3P_NO_UNITS) self._discard_annotations: bool = discard_annotations diff --git a/python/sdist/setup.cfg b/python/sdist/setup.cfg index 0d27a0918e..d34d42f98f 100644 --- a/python/sdist/setup.cfg +++ b/python/sdist/setup.cfg @@ -47,7 +47,7 @@ zip_safe = False # Don't include any URLs here - they are not supported by PyPI: # HTTPError: 400 Bad Request from https://upload.pypi.org/legacy/ # Invalid value for requires_dist. Error: Can't have direct dependency: ... -petab = petab>=0.2.1 +petab = petab>=0.2.9 pysb = pysb>=1.13.1 test = benchmark_models_petab @ git+https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab.git@master#subdirectory=src/python diff --git a/python/tests/test_petab_import.py b/python/tests/test_petab_import.py index 7a476f272d..fca319e11f 100644 --- a/python/tests/test_petab_import.py +++ b/python/tests/test_petab_import.py @@ -36,8 +36,30 @@ def simple_sbml_model(): return document, model +@pytest.fixture() +def get_fixed_parameters_model(): + """Create test SBML model for test_get_fixed_parameters""" + ant_model = """ + p1 = 1 + p2 = 2 + p3 = 3 + p4 = 4 + p5 = 5 + p6 = 3^2 + p7 = p6 + p8 = 8 + p8' = 1 + p9 := p8 + """ + from amici.antimony_import import antimony2sbml + + sbml_str = antimony2sbml(ant_model) + sbml_doc = libsbml.SBMLReader().readSBMLFromString(sbml_str) + return sbml_doc, sbml_doc.getModel() + + @skip_on_valgrind -def test_get_fixed_parameters(simple_sbml_model): +def test_get_fixed_parameters(get_fixed_parameters_model): """Check for correct identification of fixed parameters: p1: fixed (via condition table) @@ -45,13 +67,18 @@ def test_get_fixed_parameters(simple_sbml_model): p3: fixed (via parameter table `estimate=0`) p4: not fixed (via parameter table `estimate=1`) p5: fixed (implicitly, because not listed as estimated) + p6: fixed (implicitly, because not listed as estimated + initial assignment is a number) + p7: not fixed (initial assignment is not a number) + p8: not fixed (rate rule target) + p9: not fixed (assignment rule target) """ from amici.petab.sbml_import import ( _get_fixed_parameters_sbml as get_fixed_parameters, ) from petab.models.sbml_model import SbmlModel - sbml_doc, sbml_model = simple_sbml_model + sbml_doc, sbml_model = get_fixed_parameters_model condition_df = petab.get_condition_df( pd.DataFrame( { @@ -77,13 +104,14 @@ def test_get_fixed_parameters(simple_sbml_model): "p1", "p3", "p5", + "p6", } assert set( get_fixed_parameters( petab_problem, non_estimated_parameters_as_constants=False ) - ) == {"p1", "p5"} + ) == {"p1", "p5", "p6"} @skip_on_valgrind From 9824abad241c5acc97ce3d8ece65e3486c630f92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20Fr=C3=B6hlich?= Date: Thu, 7 Mar 2024 08:38:23 +0000 Subject: [PATCH 066/188] Update dataframes import/export (#2351) * Update pandas.py * add doc + fix scales * fix venv and BLAS * fix tests * revert BLAS changes * Update scripts/installAmiciArchive.sh --------- Co-authored-by: Daniel Weindl --- python/sdist/amici/pandas.py | 57 ++++++++++++++++++++++++++++++++++-- scripts/buildAmici.sh | 1 - 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/python/sdist/amici/pandas.py b/python/sdist/amici/pandas.py index 745cbfb767..b776d2d5ef 100644 --- a/python/sdist/amici/pandas.py +++ b/python/sdist/amici/pandas.py @@ -410,6 +410,20 @@ def _fill_conditions_dict( ] else: datadict[par + "_presim"] = np.nan + + for i_par, par in enumerate( + _get_names_or_ids(model, "Parameter", by_id=by_id) + ): + if len(edata.parameters): + datadict[par] = edata.parameters[i_par] + else: + datadict[par] = model.getParameters()[i_par] + + if len(edata.pscale): + datadict[par + "_scale"] = edata.pscale[i_par] + else: + datadict[par + "_scale"] = model.getParameterScale()[i_par] + return datadict @@ -438,6 +452,11 @@ def _get_extended_observable_cols(model: AmiciModel, by_id: bool) -> list[str]: name + "_presim" for name in _get_names_or_ids(model, "FixedParameter", by_id=by_id) ] + + _get_names_or_ids(model, "Parameter", by_id=by_id) + + [ + name + "_scale" + for name in _get_names_or_ids(model, "Parameter", by_id=by_id) + ] + _get_names_or_ids(model, "Observable", by_id=by_id) + [ name + "_std" @@ -471,6 +490,11 @@ def _get_observable_cols(model: AmiciModel, by_id: bool) -> list[str]: name + "_presim" for name in _get_names_or_ids(model, "FixedParameter", by_id=by_id) ] + + _get_names_or_ids(model, "Parameter", by_id=by_id) + + [ + name + "_scale" + for name in _get_names_or_ids(model, "Parameter", by_id=by_id) + ] + _get_names_or_ids(model, "Observable", by_id=by_id) ) @@ -500,6 +524,11 @@ def _get_state_cols(model: AmiciModel, by_id: bool) -> list[str]: name + "_presim" for name in _get_names_or_ids(model, "FixedParameter", by_id=by_id) ] + + _get_names_or_ids(model, "Parameter", by_id=by_id) + + [ + name + "_scale" + for name in _get_names_or_ids(model, "Parameter", by_id=by_id) + ] + _get_names_or_ids(model, "State", by_id=by_id) ) @@ -528,6 +557,11 @@ def _get_expression_cols(model: AmiciModel, by_id: bool) -> list[str]: name + "_presim" for name in _get_names_or_ids(model, "FixedParameter", by_id=by_id) ] + + _get_names_or_ids(model, "Parameter", by_id=by_id) + + [ + name + "_scale" + for name in _get_names_or_ids(model, "Parameter", by_id=by_id) + ] + _get_names_or_ids(model, "Expression", by_id=by_id) ) @@ -641,10 +675,11 @@ def constructEdataFromDataFrame( Model instance. :param condition: - pd.Series with FixedParameter Names/Ids as columns. + pd.Series with (Fixed)Parameter Names/Ids as columns. Preequilibration conditions may be specified by appending '_preeq' as suffix. Presimulation conditions may be specified by - appending '_presim' as suffix. + appending '_presim' as suffix. Parameter scales may be specified by + appending '_scale' as suffix. :param by_id: Indicate whether in the arguments, column headers are based on ids or @@ -681,6 +716,20 @@ def constructEdataFromDataFrame( .values ) + # fill in parameters + edata.parameters = ( + condition[_get_names_or_ids(model, "Parameter", by_id=by_id)] + .astype(float) + .values + ) + + edata.pscale = amici.parameterScalingFromIntVector( + [ + amici.ParameterScaling(condition[par + "_scale"].astype(int)) + for par in list(_get_names_or_ids(model, "Parameter", by_id=by_id)) + ] + ) + # fill in preequilibration parameters if any( [overwrite_preeq[key] != condition[key] for key in overwrite_preeq] @@ -767,6 +816,10 @@ def getEdataFromDataFrame( condition_parameters.append(par + "_preeq") if par + "_presim" in df.columns: condition_parameters.append(par + "_presim") + # parameters & scales + for par in _get_names_or_ids(model, "Parameter", by_id=by_id): + condition_parameters.append(par) + condition_parameters.append(par + "_scale") # presimulation time if "t_presim" in df.columns: condition_parameters.append("t_presim") diff --git a/scripts/buildAmici.sh b/scripts/buildAmici.sh index 2a3e7c7531..80b724c8c3 100755 --- a/scripts/buildAmici.sh +++ b/scripts/buildAmici.sh @@ -46,7 +46,6 @@ fi # set python executable for cmake export PYTHON_EXECUTABLE="${amici_path}/venv/bin/python" -# install numpy python3 -m pip install numpy ${cmake} \ From 2dfd2bb3d2e74186abc8f7bfcf9f8c43c08ad9a4 Mon Sep 17 00:00:00 2001 From: Paul Jonas Jost <70631928+PaulJonasJost@users.noreply.github.com> Date: Thu, 7 Mar 2024 12:09:57 +0100 Subject: [PATCH 067/188] Added amici.petab.conditions to readthedocs (#2364) --- documentation/python_modules.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/documentation/python_modules.rst b/documentation/python_modules.rst index 2ec565c5fa..2607447f0d 100644 --- a/documentation/python_modules.rst +++ b/documentation/python_modules.rst @@ -12,6 +12,7 @@ AMICI Python API amici.pysb_import amici.bngl_import amici.petab + amici.petab.conditions amici.petab.import_helpers amici.petab.parameter_mapping amici.petab.petab_import From 8d655240d9eea295ecaa113e105eb360244be52c Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Thu, 7 Mar 2024 12:10:28 +0100 Subject: [PATCH 068/188] Improve debugging info in ReturnData (#2349) * Improve debugging info in ReturnData * Add `ReturnData::t_last` that holds the last solver timepoint (Closes #2246) * `ReturnData::J` is now the evaluted at `t_last` instead of the last successfully reached output timepoint (Closes #2334) * `ReturnData::xdot` is now the evaluted at `t_last` instead of the last successfully reached output timepoint * skip xdot/J checks in case of failures --- include/amici/forwardproblem.h | 2 +- include/amici/rdata.h | 7 +++++-- python/sdist/amici/numpy.py | 1 + src/amici.cpp | 2 ++ src/forwardproblem.cpp | 5 ++++- src/hdf5.cpp | 4 ++++ tests/cpp/testfunctions.cpp | 23 +++++++++++++---------- 7 files changed, 30 insertions(+), 14 deletions(-) diff --git a/include/amici/forwardproblem.h b/include/amici/forwardproblem.h index 8c500bf73c..67658ebee4 100644 --- a/include/amici/forwardproblem.h +++ b/include/amici/forwardproblem.h @@ -316,7 +316,7 @@ class ForwardProblem { * @brief Creates a carbon copy of the current simulation state variables * @return state */ - SimulationState getSimulationState() const; + SimulationState getSimulationState(); /** array of index vectors (dimension ne) indicating whether the respective * root has been detected for all so far encountered discontinuities, diff --git a/include/amici/rdata.h b/include/amici/rdata.h index 6357d24748..b5b4c269b4 100644 --- a/include/amici/rdata.h +++ b/include/amici/rdata.h @@ -104,12 +104,12 @@ class ReturnData : public ModelDimensions { */ std::vector ts; - /** time derivative (shape `nx`) */ + /** time derivative (shape `nx`) evaluated at `t_last`. */ std::vector xdot; /** * Jacobian of differential equation right hand side (shape `nx` x `nx`, - * row-major) + * row-major) evaluated at `t_last`. */ std::vector J; @@ -456,6 +456,9 @@ class ReturnData : public ModelDimensions { /** log messages */ std::vector messages; + /** The final internal time of the solver. */ + realtype t_last{std::numeric_limits::quiet_NaN()}; + protected: /** offset for sigma_residuals */ realtype sigma_offset; diff --git a/python/sdist/amici/numpy.py b/python/sdist/amici/numpy.py index f75d927b7b..c1aef949c6 100644 --- a/python/sdist/amici/numpy.py +++ b/python/sdist/amici/numpy.py @@ -239,6 +239,7 @@ class ReturnDataView(SwigPtrView): "cpu_timeB", "cpu_time_total", "messages", + "t_last", ] def __init__(self, rdata: Union[ReturnDataPtr, ReturnData]): diff --git a/src/amici.cpp b/src/amici.cpp index cc8c2f00ab..6166ab9352 100644 --- a/src/amici.cpp +++ b/src/amici.cpp @@ -236,6 +236,8 @@ std::unique_ptr runAmiciSimulation( ); } + rdata->t_last = solver.gett(); + rdata->cpu_time_total = cpu_timer.elapsed_milliseconds(); // verify that reported CPU times are plausible diff --git a/src/forwardproblem.cpp b/src/forwardproblem.cpp index b4bcd9740f..ef4585f9e9 100644 --- a/src/forwardproblem.cpp +++ b/src/forwardproblem.cpp @@ -416,7 +416,10 @@ void ForwardProblem::getAdjointUpdates(Model& model, ExpData const& edata) { } } -SimulationState ForwardProblem::getSimulationState() const { +SimulationState ForwardProblem::getSimulationState() { + if (std::isfinite(solver->gett())) { + solver->writeSolution(&t_, x_, dx_, sx_, dx_); + } auto state = SimulationState(); state.t = t_; state.x = x_; diff --git a/src/hdf5.cpp b/src/hdf5.cpp index e980619e5f..303de576fb 100644 --- a/src/hdf5.cpp +++ b/src/hdf5.cpp @@ -532,6 +532,10 @@ void writeReturnDataDiagnosis( &rdata.cpu_time_total, 1 ); + H5LTset_attribute_double( + file.getId(), hdf5Location.c_str(), "t_last", &rdata.t_last, 1 + ); + if (!rdata.J.empty()) createAndWriteDouble2DDataset( file, hdf5Location + "/J", rdata.J, rdata.nx, rdata.nx diff --git a/tests/cpp/testfunctions.cpp b/tests/cpp/testfunctions.cpp index 3e03eedfc4..8a8054f147 100644 --- a/tests/cpp/testfunctions.cpp +++ b/tests/cpp/testfunctions.cpp @@ -192,13 +192,6 @@ void verifyReturnData(std::string const& hdffile, std::string const& resultPath, // CHECK_EQUAL(AMICI_O2MODE_FULL, udata->o2mode); - if(hdf5::locationExists(file, resultPath + "/diagnosis/J")) { - expected = hdf5::getDoubleDataset2D(file, resultPath + "/diagnosis/J", m, n); - checkEqualArray(expected, rdata->J, atol, rtol, "J"); - } else { - ASSERT_TRUE(rdata->J.empty()); - } - if(hdf5::locationExists(file, resultPath + "/y")) { expected = hdf5::getDoubleDataset2D(file, resultPath + "/y", m, n); checkEqualArray(expected, rdata->y, atol, rtol, "y"); @@ -234,12 +227,22 @@ void verifyReturnData(std::string const& hdffile, std::string const& resultPath, ASSERT_TRUE(rdata->sigmaz.empty()); } - expected = hdf5::getDoubleDataset1D(file, resultPath + "/diagnosis/xdot"); - checkEqualArray(expected, rdata->xdot, atol, rtol, "xdot"); - expected = hdf5::getDoubleDataset1D(file, resultPath + "/x0"); checkEqualArray(expected, rdata->x0, atol, rtol, "x0"); + if(rdata->status == AMICI_SUCCESS) { + // for the failed cases, the stored results don't match + // since https://github.com/AMICI-dev/AMICI/pull/2349 + expected = hdf5::getDoubleDataset1D(file, resultPath + "/diagnosis/xdot"); + checkEqualArray(expected, rdata->xdot, atol, rtol, "xdot"); + + if(hdf5::locationExists(file, resultPath + "/diagnosis/J")) { + expected = hdf5::getDoubleDataset2D(file, resultPath + "/diagnosis/J", m, n); + checkEqualArray(expected, rdata->J, atol, rtol, "J"); + } else { + ASSERT_TRUE(rdata->J.empty()); + } + } if(rdata->sensi >= SensitivityOrder::first) { verifyReturnDataSensitivities(file, resultPath, rdata, model, atol, rtol); } else { From 3dded5ca4e1f1202bef5d3b6865f5bf63eead43c Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Thu, 7 Mar 2024 14:18:39 +0100 Subject: [PATCH 069/188] Skip recreating N_Vector in AmiVector::copy (#2363) We only copy if the sizes match. Since the std::vector is not reallocated, there is no need to recreate the N_Vector. --- src/vector.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vector.cpp b/src/vector.cpp index 78b66d7954..460b92c71c 100644 --- a/src/vector.cpp +++ b/src/vector.cpp @@ -53,7 +53,6 @@ void AmiVector::copy(AmiVector const& other) { getLength(), other.getLength() ); std::copy(other.vec_.begin(), other.vec_.end(), vec_.begin()); - synchroniseNVector(); } void AmiVector::synchroniseNVector() { From 7a920e25c14bba59a6eac7a43c602ede147a98ec Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Thu, 7 Mar 2024 15:18:41 +0100 Subject: [PATCH 070/188] Enable setting constraints on state variables (#2340) In addition to the current `Model.setStateIsNonNegative`, this adds the option to set additional (non)negativity/positivity constraints to be enforced by the solver. See [CVodeSetConstraints](https://sundials.readthedocs.io/en/latest/cvode/Usage/index.html#c.CVodeSetConstraints) for details. Related to #2327. --- include/amici/defines.h | 9 ++++++ include/amici/serialization.h | 21 ++++++++++++++ include/amici/solver.h | 28 +++++++++++++++++- include/amici/solver_cvodes.h | 2 ++ include/amici/solver_idas.h | 2 ++ include/amici/vector.h | 35 +++++++++++++++++++++- python/tests/test_hdf5.py | 2 ++ python/tests/test_sbml_import.py | 50 ++++++++++++++++++++++++++++++++ src/hdf5.cpp | 10 +++++++ src/solver.cpp | 26 +++++++++++++++++ src/solver_cvodes.cpp | 12 ++++++++ src/solver_idas.cpp | 12 ++++++++ swig/amici.i | 1 + 13 files changed, 208 insertions(+), 2 deletions(-) diff --git a/include/amici/defines.h b/include/amici/defines.h index 210710b7ba..bd97d9a0b2 100644 --- a/include/amici/defines.h +++ b/include/amici/defines.h @@ -255,6 +255,15 @@ enum class SplineExtrapolation { periodic = 3, }; +/** Constraints on state variables */ +enum class Constraint { + none = 0, + non_negative = 1, + non_positive = -1, + positive = 2, + negative = -2, +}; + // clang-format on } // namespace amici diff --git a/include/amici/serialization.h b/include/amici/serialization.h index 97172e8375..e4b1e3afe5 100644 --- a/include/amici/serialization.h +++ b/include/amici/serialization.h @@ -5,6 +5,7 @@ #include "amici/rdata.h" #include "amici/solver.h" #include "amici/solver_cvodes.h" +#include "amici/vector.h" #include @@ -87,6 +88,7 @@ void serialize(Archive& ar, amici::Solver& s, unsigned int const /*version*/) { ar & s.maxtime_; ar & s.max_conv_fails_; ar & s.max_nonlin_iters_; + ar & s.constraints_; ar & s.max_step_size_; } @@ -278,6 +280,25 @@ void serialize( ar & m.ubw; ar & m.lbw; } + +/** + * @brief Serialize AmiVector to a boost archive + * @param ar archive + * @param v AmiVector + */ +template +void serialize( + Archive& ar, amici::AmiVector& v, unsigned int const /*version*/ +) { + if (Archive::is_loading::value) { + std::vector tmp; + ar & tmp; + v = amici::AmiVector(tmp); + } else { + auto tmp = v.getVector(); + ar & tmp; + } +} #endif } // namespace serialization } // namespace boost diff --git a/include/amici/solver.h b/include/amici/solver.h index a52e80e82d..661a9fac25 100644 --- a/include/amici/solver.h +++ b/include/amici/solver.h @@ -964,6 +964,24 @@ class Solver { */ int getMaxConvFails() const; + /** + * @brief Set constraints on the model state. + * + * See + * https://sundials.readthedocs.io/en/latest/cvode/Usage/index.html#c.CVodeSetConstraints. + * + * @param constraints + */ + void setConstraints(std::vector const& constraints); + + /** + * @brief Get constraints on the model state. + * @return constraints + */ + std::vector getConstraints() const { + return constraints_.getVector(); + } + /** * @brief Set the maximum step size * @param max_step_size maximum step size. `0.0` means no limit. @@ -1130,7 +1148,7 @@ class Solver { virtual void rootInit(int ne) const = 0; /** - * @brief Initalize non-linear solver for sensitivities + * @brief Initialize non-linear solver for sensitivities * @param model Model instance */ void initializeNonLinearSolverSens(Model const* model) const; @@ -1648,6 +1666,11 @@ class Solver { */ void applySensitivityTolerances() const; + /** + * @brief Apply the constraints to the solver. + */ + virtual void apply_constraints() const; + /** pointer to solver memory block */ mutable std::unique_ptr solver_memory_; @@ -1809,6 +1832,9 @@ class Solver { /** flag indicating whether sensInit1 was called */ mutable bool sens_initialized_{false}; + /** Vector of constraints on the solution */ + mutable AmiVector constraints_; + private: /** * @brief applies total number of steps for next solver call diff --git a/include/amici/solver_cvodes.h b/include/amici/solver_cvodes.h index 8abf407f1d..8d04b22a34 100644 --- a/include/amici/solver_cvodes.h +++ b/include/amici/solver_cvodes.h @@ -254,6 +254,8 @@ class CVodeSolver : public Solver { void apply_max_conv_fails() const override; + void apply_constraints() const override; + void apply_max_step_size() const override; }; diff --git a/include/amici/solver_idas.h b/include/amici/solver_idas.h index 3d8a9df485..c8efc69c0c 100644 --- a/include/amici/solver_idas.h +++ b/include/amici/solver_idas.h @@ -234,6 +234,8 @@ class IDASolver : public Solver { void apply_max_conv_fails() const override; + void apply_constraints() const override; + void apply_max_step_size() const override; }; diff --git a/include/amici/vector.h b/include/amici/vector.h index b1b496c26e..486ea77682 100644 --- a/include/amici/vector.h +++ b/include/amici/vector.h @@ -10,6 +10,18 @@ #include +namespace amici { +class AmiVector; +} + +// for serialization friend +namespace boost { +namespace serialization { +template +void serialize(Archive& ar, amici::AmiVector& s, unsigned int version); +} +} // namespace boost + namespace amici { /** Since const N_Vector is not what we want */ @@ -54,7 +66,7 @@ class AmiVector { * @brief constructor from gsl::span, * @param rvec vector from which the data will be copied */ - explicit AmiVector(gsl::span rvec) + explicit AmiVector(gsl::span rvec) : AmiVector(std::vector(rvec.begin(), rvec.end())) {} /** @@ -213,6 +225,17 @@ class AmiVector { */ void abs() { N_VAbs(getNVector(), getNVector()); }; + /** + * @brief Serialize AmiVector (see boost::serialization::serialize) + * @param ar Archive to serialize to + * @param s Data to serialize + * @param version Version number + */ + template + friend void boost::serialization::serialize( + Archive& ar, AmiVector& s, unsigned int version + ); + private: /** main data storage */ std::vector vec_; @@ -405,6 +428,16 @@ namespace gsl { inline span make_span(N_Vector nv) { return span(N_VGetArrayPointer(nv), N_VGetLength_Serial(nv)); } + +/** + * @brief Create span from N_Vector + * @param nv + * + */ +inline span make_span(amici::AmiVector const& av) { + return make_span(av.getVector()); +} + } // namespace gsl #endif /* AMICI_VECTOR_H */ diff --git a/python/tests/test_hdf5.py b/python/tests/test_hdf5.py index 232f22be8c..ee9d5a3e3b 100644 --- a/python/tests/test_hdf5.py +++ b/python/tests/test_hdf5.py @@ -24,6 +24,8 @@ def _modify_solver_attrs(solver): elif attr == "setMaxTime": # default value is the maximum, must not add to that cval = random.random() + elif attr == "setConstraints": + cval = [1.0, 1.0] elif isinstance(val, int): cval = val + 1 else: diff --git a/python/tests/test_sbml_import.py b/python/tests/test_sbml_import.py index ecf21f1f95..aaab5688cc 100644 --- a/python/tests/test_sbml_import.py +++ b/python/tests/test_sbml_import.py @@ -722,3 +722,53 @@ def test_hardcode_parameters(simple_sbml_model): constant_parameters=["p1"], hardcode_symbols=["p1"], ) + + +def test_constraints(): + """Test non-negativity constraint handling.""" + from amici.antimony_import import antimony2amici + from amici import Constraint + + ant_model = """ + model test_non_negative_species + species A = 10 + species B = 0 + # R1: A => B; k1f * sqrt(A) + R1: A => B; k1f * max(0, A) + k1f = 1e10 + end + """ + module_name = "test_non_negative_species" + with TemporaryDirectory(prefix=module_name) as outdir: + antimony2amici( + ant_model, + model_name=module_name, + output_dir=outdir, + compute_conservation_laws=False, + ) + model_module = amici.import_model_module( + module_name=module_name, module_path=outdir + ) + amici_model = model_module.getModel() + amici_model.setTimepoints(np.linspace(0, 100, 200)) + amici_solver = amici_model.getSolver() + rdata = amici.runAmiciSimulation(amici_model, amici_solver) + assert rdata.status == amici.AMICI_SUCCESS + # should be non-negative in theory, but is expected to become negative + # in practice + assert np.any(rdata.x < 0) + + amici_solver.setRelativeTolerance(1e-14) + amici_solver.setConstraints( + [Constraint.non_negative, Constraint.non_negative] + ) + rdata = amici.runAmiciSimulation(amici_model, amici_solver) + assert rdata.status == amici.AMICI_SUCCESS + assert np.all(rdata.x >= 0) + assert np.all( + np.sum(rdata.x, axis=1) - np.sum(rdata.x[0]) + < max( + np.sum(rdata.x[0]) * amici_solver.getRelativeTolerance(), + amici_solver.getAbsoluteTolerance(), + ) + ) diff --git a/src/hdf5.cpp b/src/hdf5.cpp index 303de576fb..0454b634f1 100644 --- a/src/hdf5.cpp +++ b/src/hdf5.cpp @@ -914,6 +914,10 @@ void writeSolverSettingsToHDF5( H5LTset_attribute_int( file.getId(), hdf5Location.c_str(), "max_conv_fails", &ibuffer, 1 ); + + createAndWriteDouble1DDataset( + file, hdf5Location + "/constraints", solver.getConstraints() + ); } void readSolverSettingsFromHDF5( @@ -1143,6 +1147,12 @@ void readSolverSettingsFromHDF5( getIntScalarAttribute(file, datasetPath, "max_conv_fails") ); } + + if (locationExists(file, datasetPath + "/constraints")) { + solver.setConstraints( + getDoubleDataset1D(file, datasetPath + "/constraints") + ); + } } void readSolverSettingsFromHDF5( diff --git a/src/solver.cpp b/src/solver.cpp index c1c048e4cc..6fc1132c4d 100644 --- a/src/solver.cpp +++ b/src/solver.cpp @@ -200,6 +200,8 @@ void Solver::setup( cpu_time_ = 0.0; cpu_timeB_ = 0.0; + + apply_constraints(); } void Solver::setupB( @@ -646,6 +648,15 @@ void Solver::applySensitivityTolerances() const { } } +void Solver::apply_constraints() const { + if (constraints_.getLength() != 0 + && gsl::narrow(constraints_.getLength()) != nx()) { + throw std::invalid_argument( + "Constraints must have the same size as the state vector." + ); + } +} + SensitivityMethod Solver::getSensitivityMethod() const { return sensi_meth_; } SensitivityMethod Solver::getSensitivityMethodPreequilibration() const { @@ -693,6 +704,21 @@ void Solver::setMaxConvFails(int max_conv_fails) { int Solver::getMaxConvFails() const { return max_conv_fails_; } +void Solver::setConstraints(std::vector const& constraints) { + auto any_constraint + = std::any_of(constraints.begin(), constraints.end(), [](bool x) { + return x != 0.0; + }); + + if (!any_constraint) { + // all-0 must be converted to empty, otherwise sundials will fail + constraints_ = AmiVector(); + return; + } + + constraints_ = AmiVector(constraints); +} + void Solver::setMaxStepSize(realtype max_step_size) { if (max_step_size < 0) throw AmiException("max_step_size must be non-negative."); diff --git a/src/solver_cvodes.cpp b/src/solver_cvodes.cpp index 626b4cc050..e5771ff391 100644 --- a/src/solver_cvodes.cpp +++ b/src/solver_cvodes.cpp @@ -270,6 +270,18 @@ void CVodeSolver::apply_max_conv_fails() const { throw CvodeException(status, "CVodeSetMaxConvFails"); } +void CVodeSolver::apply_constraints() const { + Solver::apply_constraints(); + + int status = CVodeSetConstraints( + solver_memory_.get(), + constraints_.getLength() > 0 ? constraints_.getNVector() : nullptr + ); + if (status != CV_SUCCESS) { + throw CvodeException(status, "CVodeSetConstraints"); + } +} + void CVodeSolver::apply_max_step_size() const { int status = CVodeSetMaxStep(solver_memory_.get(), getMaxStepSize()); if (status != CV_SUCCESS) diff --git a/src/solver_idas.cpp b/src/solver_idas.cpp index d97d6b3911..4f96c95f86 100644 --- a/src/solver_idas.cpp +++ b/src/solver_idas.cpp @@ -267,6 +267,18 @@ void IDASolver::apply_max_conv_fails() const { throw IDAException(status, "IDASetMaxConvFails"); } +void IDASolver::apply_constraints() const { + Solver::apply_constraints(); + + int status = IDASetConstraints( + solver_memory_.get(), + constraints_.getLength() > 0 ? constraints_.getNVector() : nullptr + ); + if (status != IDA_SUCCESS) { + throw IDAException(status, "IDASetConstraints"); + } +} + void IDASolver::apply_max_step_size() const { int status = IDASetMaxStep(solver_memory_.get(), getMaxStepSize()); if (status != IDA_SUCCESS) diff --git a/swig/amici.i b/swig/amici.i index 1ef076fe33..46a58f8365 100644 --- a/swig/amici.i +++ b/swig/amici.i @@ -327,6 +327,7 @@ SteadyStateStatus = enum('SteadyStateStatus') NewtonDampingFactorMode = enum('NewtonDampingFactorMode') FixedParameterContext = enum('FixedParameterContext') RDataReporting = enum('RDataReporting') +Constraint = enum('Constraint') %} %template(SteadyStateStatusVector) std::vector; From c43ce3b360a447a61c605ce487f013f0558dd061 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Thu, 7 Mar 2024 21:17:36 +0100 Subject: [PATCH 071/188] Log stacktraces only in debug mode (#2366) Log stacktraces only in debug mode Closes #2186 --- python/tests/test_preequilibration.py | 4 ---- src/amici.cpp | 3 ++- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/python/tests/test_preequilibration.py b/python/tests/test_preequilibration.py index be447b0c54..d003507199 100644 --- a/python/tests/test_preequilibration.py +++ b/python/tests/test_preequilibration.py @@ -608,8 +608,6 @@ def test_simulation_errors(preeq_fixture): ) assert rdata._swigptr.messages[1].severity == amici.LogSeverity_error assert rdata._swigptr.messages[1].identifier == "OTHER" - assert rdata._swigptr.messages[2].severity == amici.LogSeverity_debug - assert rdata._swigptr.messages[2].identifier == "BACKTRACE" # too long simulations solver.setMaxSteps(int(1e4)) @@ -632,8 +630,6 @@ def test_simulation_errors(preeq_fixture): ) assert rdata._swigptr.messages[2].severity == amici.LogSeverity_error assert rdata._swigptr.messages[2].identifier == "OTHER" - assert rdata._swigptr.messages[3].severity == amici.LogSeverity_debug - assert rdata._swigptr.messages[3].identifier == "BACKTRACE" def test_get_model_for_preeq(preeq_fixture): diff --git a/src/amici.cpp b/src/amici.cpp index 6166ab9352..2f60a8e12a 100644 --- a/src/amici.cpp +++ b/src/amici.cpp @@ -206,11 +206,12 @@ std::unique_ptr runAmiciSimulation( LogSeverity::error, "OTHER", "AMICI simulation failed: %s", ex.what() ); +#ifndef NDEBUG logger.log( LogSeverity::debug, "BACKTRACE", "The previous error occurred at:\n%s", ex.getBacktrace() ); - +#endif } catch (std::exception const& ex) { rdata->status = AMICI_ERROR; if (rethrow) From 6f12f85ae59657790dc9b19faabe7d79f95b333e Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Thu, 7 Mar 2024 21:18:31 +0100 Subject: [PATCH 072/188] Fix type annotations in swig-wrappers (again) (#2365) Some things were missing in https://github.com/AMICI-dev/AMICI/pull/2344 --- python/sdist/amici/swig.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/python/sdist/amici/swig.py b/python/sdist/amici/swig.py index 81a030ba3d..fbc486c301 100644 --- a/python/sdist/amici/swig.py +++ b/python/sdist/amici/swig.py @@ -59,7 +59,7 @@ def visit_FunctionDef(self, node): self._annotation_from_docstring(node) # Has a return type annotation? - if node.returns: + if node.returns and isinstance(node.returns, ast.Constant): node.returns = self._new_annot(node.returns.value) # Has arguments? @@ -112,8 +112,11 @@ def _annotation_from_docstring(self, node: ast.FunctionDef): """Add annotations based on docstring. If any argument or return type of the function is not annotated, but - the corresponding docstring contains a type hint, the type hint is used - as the annotation. + the corresponding docstring contains a type hint (``:rtype:`` or + ``:type:``), the type hint is used as the annotation. + + Swig sometimes generates ``:type solver: :py:class:`Solver`` instead of + ``:type solver: Solver``. Those need special treatment. """ docstring = ast.get_docstring(node, clean=False) if not docstring or "*Overload 1:*" in docstring: @@ -124,11 +127,19 @@ def _annotation_from_docstring(self, node: ast.FunctionDef): lines_to_remove = set() for line_no, line in enumerate(docstring): - if match := re.match(r"\W*:rtype:\W*(.+)", line): + if ( + match := re.match( + r"\s*:rtype:\s*(?::py:class:`)?(\w+)`?\s+$", line + ) + ) and not match.group(1).startswith(":"): node.returns = ast.Constant(match.group(1)) lines_to_remove.add(line_no) - if match := re.match(r"\W*:type:\W*(\w+):\W*(.+)", line): + if ( + match := re.match( + r"\s*:type\s*(\w+):\W*(?::py:class:`)?(\w+)`?\s+$", line + ) + ) and not match.group(1).startswith(":"): for arg in node.args.args: if arg.arg == match.group(1): arg.annotation = ast.Constant(match.group(2)) From b1bd988285c016d72a8a86da0fad88791c8fb526 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Thu, 7 Mar 2024 21:30:14 +0100 Subject: [PATCH 073/188] Doc: Fix headings in ExampleJax (#2371) * Doc: Fix headings in ExampleJax To properly integrate into our sphinx docs. * remove duplicated notebook --- documentation/ExampleJax.ipynb | 1114 +----------------- python/examples/example_jax/ExampleJax.ipynb | 15 +- 2 files changed, 10 insertions(+), 1119 deletions(-) mode change 100644 => 120000 documentation/ExampleJax.ipynb diff --git a/documentation/ExampleJax.ipynb b/documentation/ExampleJax.ipynb deleted file mode 100644 index 53e03788da..0000000000 --- a/documentation/ExampleJax.ipynb +++ /dev/null @@ -1,1113 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "d4d2bc5c", - "metadata": {}, - "source": [ - "The purpose of this guide is to showcase how AMICI can be combined with differentiable programming in JAX. We will do so by reimplementing the parameter transformations available in AMICI in JAX and comparing it to the native implementation." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "b0a66e18", - "metadata": {}, - "outputs": [], - "source": [ - "import jax\n", - "import jax.numpy as jnp" - ] - }, - { - "cell_type": "markdown", - "id": "aa587260", - "metadata": {}, - "source": [ - "# Preparation" - ] - }, - { - "cell_type": "markdown", - "id": "fb2fe897", - "metadata": {}, - "source": [ - "To get started we will import a model using the [petab](https://petab.readthedocs.io). To this end, we will use the [benchmark collection](https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab), which features a variety of different models. For more details about petab import, see the respective notebook petab [notebook](https://amici.readthedocs.io/en/latest/petab.html)." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "04804185", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Cloning into 'tmp/benchmark-models'...\n", - "remote: Enumerating objects: 336, done.\u001B[K\n", - "remote: Counting objects: 100% (336/336), done.\u001B[K\n", - "remote: Compressing objects: 100% (285/285), done.\u001B[K\n", - "remote: Total 336 (delta 88), reused 216 (delta 39), pack-reused 0\u001B[K\n", - "Receiving objects: 100% (336/336), 2.11 MiB | 7.48 MiB/s, done.\n", - "Resolving deltas: 100% (88/88), done.\n" - ] - } - ], - "source": [ - "!git clone --depth 1 https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab.git tmp/benchmark-models || (cd tmp/benchmark-models && git pull)\n", - "from pathlib import Path\n", - "\n", - "folder_base = Path(\".\") / \"tmp\" / \"benchmark-models\" / \"Benchmark-Models\"" - ] - }, - { - "cell_type": "markdown", - "id": "8552f123", - "metadata": {}, - "source": [ - "From the benchmark collection, we now import the Boehm model." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "9166e3bf", - "metadata": {}, - "outputs": [], - "source": [ - "import petab\n", - "\n", - "model_name = \"Boehm_JProteomeRes2014\"\n", - "yaml_file = folder_base / model_name / (model_name + \".yaml\")\n", - "petab_problem = petab.Problem.from_yaml(yaml_file)" - ] - }, - { - "cell_type": "markdown", - "id": "d4038fc4", - "metadata": {}, - "source": [ - "The petab problem includes information about parameter scaling in it's the parameter table. For the boehm model, all estimated parameters (`petab.ESTIMATE` column equal to `1`) have a `petab.LOG10` as parameter scaling." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "b04ca561", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
parameterNameparameterScalelowerBoundupperBoundnominalValueestimate
parameterId
Epo_degradation_BaF3EPO_{degradation,BaF3}log100.000011000000.0269831
k_exp_heterok_{exp,hetero}log100.000011000000.0000101
k_exp_homok_{exp,homo}log100.000011000000.0061701
k_imp_heterok_{imp,hetero}log100.000011000000.0163681
k_imp_homok_{imp,homo}log100.0000110000097749.3794021
k_phosk_{phos}log100.0000110000015766.5070201
ratioratiolin-5.0000050.6930000
sd_pSTAT5A_rel\\sigma_{pSTAT5A,rel}log100.000011000003.8526121
sd_pSTAT5B_rel\\sigma_{pSTAT5B,rel}log100.000011000006.5914781
sd_rSTAT5A_rel\\sigma_{rSTAT5A,rel}log100.000011000003.1527131
specC17specC17lin-5.0000050.1070000
\n", - "
" - ], - "text/plain": [ - " parameterName parameterScale lowerBound \\\n", - "parameterId \n", - "Epo_degradation_BaF3 EPO_{degradation,BaF3} log10 0.00001 \n", - "k_exp_hetero k_{exp,hetero} log10 0.00001 \n", - "k_exp_homo k_{exp,homo} log10 0.00001 \n", - "k_imp_hetero k_{imp,hetero} log10 0.00001 \n", - "k_imp_homo k_{imp,homo} log10 0.00001 \n", - "k_phos k_{phos} log10 0.00001 \n", - "ratio ratio lin -5.00000 \n", - "sd_pSTAT5A_rel \\sigma_{pSTAT5A,rel} log10 0.00001 \n", - "sd_pSTAT5B_rel \\sigma_{pSTAT5B,rel} log10 0.00001 \n", - "sd_rSTAT5A_rel \\sigma_{rSTAT5A,rel} log10 0.00001 \n", - "specC17 specC17 lin -5.00000 \n", - "\n", - " upperBound nominalValue estimate \n", - "parameterId \n", - "Epo_degradation_BaF3 100000 0.026983 1 \n", - "k_exp_hetero 100000 0.000010 1 \n", - "k_exp_homo 100000 0.006170 1 \n", - "k_imp_hetero 100000 0.016368 1 \n", - "k_imp_homo 100000 97749.379402 1 \n", - "k_phos 100000 15766.507020 1 \n", - "ratio 5 0.693000 0 \n", - "sd_pSTAT5A_rel 100000 3.852612 1 \n", - "sd_pSTAT5B_rel 100000 6.591478 1 \n", - "sd_rSTAT5A_rel 100000 3.152713 1 \n", - "specC17 5 0.107000 0 " - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "petab_problem.parameter_df" - ] - }, - { - "cell_type": "markdown", - "id": "8914a18d", - "metadata": {}, - "source": [ - "We now import the petab problem using [`amici.petab_import`](https://amici.readthedocs.io/en/latest/generated/amici.petab_import.html#amici.petab_import.import_petab_problem)." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "6ada3fb8", - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2023-02-16 12:37:18.049 - amici.petab_import - INFO - Importing model ...\n", - "2023-02-16 12:37:18.050 - amici.petab_import - INFO - Validating PEtab problem ...\n", - "2023-02-16 12:37:18.343 - amici.petab_import - INFO - Model name is 'Boehm_JProteomeRes2014'.\n", - "Writing model code to '/Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014'.\n", - "2023-02-16 12:37:18.344 - amici.petab_import - INFO - Species: 8\n", - "2023-02-16 12:37:18.344 - amici.petab_import - INFO - Global parameters: 9\n", - "2023-02-16 12:37:18.344 - amici.petab_import - INFO - Reactions: 9\n", - "2023-02-16 12:37:18.353 - amici.petab_import - INFO - Observables: 3\n", - "2023-02-16 12:37:18.353 - amici.petab_import - INFO - Sigmas: 3\n", - "2023-02-16 12:37:18.357 - amici.petab_import - DEBUG - Adding output parameters to model: ['noiseParameter1_pSTAT5A_rel', 'noiseParameter1_pSTAT5B_rel', 'noiseParameter1_rSTAT5A_rel']\n", - "2023-02-16 12:37:18.357 - amici.petab_import - DEBUG - Adding initial assignments for []\n", - "2023-02-16 12:37:18.363 - amici.petab_import - DEBUG - Condition table: (1, 1)\n", - "2023-02-16 12:37:18.364 - amici.petab_import - DEBUG - Fixed parameters are ['ratio', 'specC17']\n", - "2023-02-16 12:37:18.364 - amici.petab_import - INFO - Overall fixed parameters: 2\n", - "2023-02-16 12:37:18.364 - amici.petab_import - INFO - Variable parameters: 10\n", - "2023-02-16 12:37:18.398 - amici.sbml_import - DEBUG - Finished gathering local SBML symbols ++ (1.08E-02s)\n", - "2023-02-16 12:37:18.403 - amici.sbml_import - DEBUG - Finished processing SBML parameters ++ (2.27E-03s)\n", - "2023-02-16 12:37:18.405 - amici.sbml_import - DEBUG - Finished processing SBML compartments ++ (1.18E-04s)\n", - "2023-02-16 12:37:18.411 - amici.sbml_import - DEBUG - Finished processing SBML species initials +++ (2.19E-03s)\n", - "2023-02-16 12:37:18.413 - amici.sbml_import - DEBUG - Finished processing SBML rate rules +++ (1.46E-05s)\n", - "2023-02-16 12:37:18.413 - amici.sbml_import - DEBUG - Finished processing SBML species ++ (5.96E-03s)\n", - "2023-02-16 12:37:18.417 - amici.sbml_import - DEBUG - Finished processing SBML reactions ++ (1.01E-03s)\n", - "2023-02-16 12:37:18.420 - amici.sbml_import - DEBUG - Finished processing SBML rules ++ (1.15E-03s)\n", - "2023-02-16 12:37:18.422 - amici.sbml_import - DEBUG - Finished processing SBML initial assignments++ (4.30E-05s)\n", - "2023-02-16 12:37:18.424 - amici.sbml_import - DEBUG - Finished processing SBML species references ++ (1.96E-04s)\n", - "2023-02-16 12:37:18.426 - amici.sbml_import - DEBUG - Finished processing SBML events ++ (3.40E-05s)\n", - "2023-02-16 12:37:18.426 - amici.sbml_import - DEBUG - Finished importing SBML + (4.08E-02s)\n", - "2023-02-16 12:37:18.457 - amici.sbml_import - DEBUG - Finished processing SBML observables + (2.87E-02s)\n", - "2023-02-16 12:37:18.460 - amici.sbml_import - DEBUG - Finished processing SBML event observables + (1.12E-06s)\n", - "2023-02-16 12:37:18.476 - amici.ode_export - DEBUG - Finished running smart_multiply ++ (1.06E-03s)\n", - "2023-02-16 12:37:18.499 - amici.ode_export - DEBUG - Finished importing SbmlImporter + (2.54E-02s)\n", - "2023-02-16 12:37:18.520 - amici.ode_export - DEBUG - Finished simplifying Jy ++++ (1.33E-02s)\n", - "2023-02-16 12:37:18.521 - amici.ode_export - DEBUG - Finished computing Jy +++ (1.53E-02s)\n", - "2023-02-16 12:37:18.549 - amici.ode_export - DEBUG - Finished simplifying y ++++ (2.28E-02s)\n", - "2023-02-16 12:37:18.549 - amici.ode_export - DEBUG - Finished computing y +++ (2.48E-02s)\n", - "2023-02-16 12:37:18.554 - amici.ode_export - DEBUG - Finished simplifying sigmay ++++ (7.19E-05s)\n", - "2023-02-16 12:37:18.554 - amici.ode_export - DEBUG - Finished computing sigmay +++ (2.17E-03s)\n", - "2023-02-16 12:37:18.570 - amici.ode_export - DEBUG - Finished writing Jy.cpp ++ (6.58E-02s)\n", - "2023-02-16 12:37:18.592 - amici.ode_export - DEBUG - Finished running smart_jacobian ++++ (1.63E-02s)\n", - "2023-02-16 12:37:18.601 - amici.ode_export - DEBUG - Finished simplifying dJydsigma ++++ (5.78E-03s)\n", - "2023-02-16 12:37:18.601 - amici.ode_export - DEBUG - Finished computing dJydsigma +++ (2.73E-02s)\n", - "2023-02-16 12:37:18.604 - amici.ode_export - DEBUG - Finished writing dJydsigma.cpp ++ (3.18E-02s)\n", - "2023-02-16 12:37:18.619 - amici.ode_export - DEBUG - Finished running smart_jacobian ++++ (9.30E-03s)\n", - "2023-02-16 12:37:18.630 - amici.ode_export - DEBUG - Finished simplifying dJydy ++++ (7.63E-03s)\n", - "2023-02-16 12:37:18.630 - amici.ode_export - DEBUG - Finished computing dJydy +++ (2.19E-02s)\n", - "2023-02-16 12:37:18.635 - amici.ode_export - DEBUG - Finished writing dJydy.cpp ++ (2.79E-02s)\n", - "2023-02-16 12:37:18.640 - amici.ode_export - DEBUG - Finished simplifying Jz ++++ (3.06E-05s)\n", - "2023-02-16 12:37:18.641 - amici.ode_export - DEBUG - Finished computing Jz +++ (1.71E-03s)\n", - "2023-02-16 12:37:18.643 - amici.ode_export - DEBUG - Finished computing z +++ (6.17E-05s)\n", - "2023-02-16 12:37:18.647 - amici.ode_export - DEBUG - Finished simplifying sigmaz ++++ (2.92E-05s)\n", - "2023-02-16 12:37:18.647 - amici.ode_export - DEBUG - Finished computing sigmaz +++ (2.01E-03s)\n", - "2023-02-16 12:37:18.647 - amici.ode_export - DEBUG - Finished writing Jz.cpp ++ (9.91E-03s)\n", - "2023-02-16 12:37:18.653 - amici.ode_export - DEBUG - Finished running smart_jacobian ++++ (2.68E-05s)\n", - "2023-02-16 12:37:18.655 - amici.ode_export - DEBUG - Finished simplifying dJzdsigma ++++ (3.75E-05s)\n", - "2023-02-16 12:37:18.656 - amici.ode_export - DEBUG - Finished computing dJzdsigma +++ (4.13E-03s)\n", - "2023-02-16 12:37:18.656 - amici.ode_export - DEBUG - Finished writing dJzdsigma.cpp ++ (6.25E-03s)\n", - "2023-02-16 12:37:18.661 - amici.ode_export - DEBUG - Finished running smart_jacobian ++++ (2.62E-05s)\n", - "2023-02-16 12:37:18.663 - amici.ode_export - DEBUG - Finished simplifying dJzdz ++++ (3.29E-05s)\n", - "2023-02-16 12:37:18.664 - amici.ode_export - DEBUG - Finished computing dJzdz +++ (3.72E-03s)\n", - "2023-02-16 12:37:18.664 - amici.ode_export - DEBUG - Finished writing dJzdz.cpp ++ (5.33E-03s)\n", - "2023-02-16 12:37:18.669 - amici.ode_export - DEBUG - Finished simplifying Jrz ++++ (3.16E-05s)\n", - "2023-02-16 12:37:18.670 - amici.ode_export - DEBUG - Finished computing Jrz +++ (1.69E-03s)\n", - "2023-02-16 12:37:18.672 - amici.ode_export - DEBUG - Finished computing rz +++ (5.45E-05s)\n", - "2023-02-16 12:37:18.672 - amici.ode_export - DEBUG - Finished writing Jrz.cpp ++ (5.46E-03s)\n", - "2023-02-16 12:37:18.677 - amici.ode_export - DEBUG - Finished running smart_jacobian ++++ (2.60E-05s)\n", - "2023-02-16 12:37:18.679 - amici.ode_export - DEBUG - Finished simplifying dJrzdsigma ++++ (3.63E-05s)\n", - "2023-02-16 12:37:18.679 - amici.ode_export - DEBUG - Finished computing dJrzdsigma +++ (3.57E-03s)\n", - "2023-02-16 12:37:18.679 - amici.ode_export - DEBUG - Finished writing dJrzdsigma.cpp ++ (5.15E-03s)\n", - "2023-02-16 12:37:18.684 - amici.ode_export - DEBUG - Finished running smart_jacobian ++++ (2.39E-05s)\n", - "2023-02-16 12:37:18.686 - amici.ode_export - DEBUG - Finished simplifying dJrzdz ++++ (3.45E-05s)\n", - "2023-02-16 12:37:18.687 - amici.ode_export - DEBUG - Finished computing dJrzdz +++ (3.68E-03s)\n", - "2023-02-16 12:37:18.687 - amici.ode_export - DEBUG - Finished writing dJrzdz.cpp ++ (5.31E-03s)\n", - "2023-02-16 12:37:18.692 - amici.ode_export - DEBUG - Finished simplifying root ++++ (3.28E-05s)\n", - "2023-02-16 12:37:18.693 - amici.ode_export - DEBUG - Finished computing root +++ (1.74E-03s)\n", - "2023-02-16 12:37:18.693 - amici.ode_export - DEBUG - Finished writing root.cpp ++ (3.46E-03s)\n", - "2023-02-16 12:37:18.707 - amici.ode_export - DEBUG - Finished simplifying w +++++ (7.04E-03s)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2023-02-16 12:37:18.707 - amici.ode_export - DEBUG - Finished computing w ++++ (8.93E-03s)\n", - "2023-02-16 12:37:18.719 - amici.ode_export - DEBUG - Finished running smart_jacobian ++++ (9.60E-03s)\n", - "2023-02-16 12:37:18.726 - amici.ode_export - DEBUG - Finished simplifying dwdp ++++ (4.59E-03s)\n", - "2023-02-16 12:37:18.726 - amici.ode_export - DEBUG - Finished computing dwdp +++ (2.93E-02s)\n", - "2023-02-16 12:37:18.730 - amici.ode_export - DEBUG - Finished writing dwdp.cpp ++ (3.43E-02s)\n", - "2023-02-16 12:37:18.743 - amici.ode_export - DEBUG - Finished running smart_jacobian ++++ (8.10E-03s)\n", - "2023-02-16 12:37:18.749 - amici.ode_export - DEBUG - Finished simplifying dwdx ++++ (3.24E-03s)\n", - "2023-02-16 12:37:18.749 - amici.ode_export - DEBUG - Finished computing dwdx +++ (1.54E-02s)\n", - "2023-02-16 12:37:18.753 - amici.ode_export - DEBUG - Finished writing dwdx.cpp ++ (2.02E-02s)\n", - "2023-02-16 12:37:18.762 - amici.ode_export - DEBUG - Finished running smart_jacobian ++++ (3.16E-03s)\n", - "2023-02-16 12:37:18.767 - amici.ode_export - DEBUG - Finished simplifying dwdw ++++ (2.23E-03s)\n", - "2023-02-16 12:37:18.767 - amici.ode_export - DEBUG - Finished computing dwdw +++ (9.56E-03s)\n", - "2023-02-16 12:37:18.769 - amici.ode_export - DEBUG - Finished writing dwdw.cpp ++ (1.30E-02s)\n", - "2023-02-16 12:37:18.780 - amici.ode_export - DEBUG - Finished simplifying xdot +++++ (3.54E-03s)\n", - "2023-02-16 12:37:18.780 - amici.ode_export - DEBUG - Finished computing xdot ++++ (5.53E-03s)\n", - "2023-02-16 12:37:18.790 - amici.ode_export - DEBUG - Finished running smart_jacobian ++++ (6.83E-03s)\n", - "2023-02-16 12:37:18.792 - amici.ode_export - DEBUG - Finished simplifying dxdotdw ++++ (2.24E-04s)\n", - "2023-02-16 12:37:18.793 - amici.ode_export - DEBUG - Finished computing dxdotdw +++ (1.98E-02s)\n", - "2023-02-16 12:37:18.797 - amici.ode_export - DEBUG - Finished writing dxdotdw.cpp ++ (2.55E-02s)\n", - "2023-02-16 12:37:18.804 - amici.ode_export - DEBUG - Finished running smart_jacobian ++++ (5.02E-04s)\n", - "2023-02-16 12:37:18.806 - amici.ode_export - DEBUG - Finished simplifying dxdotdx_explicit ++++ (3.57E-05s)\n", - "2023-02-16 12:37:18.806 - amici.ode_export - DEBUG - Finished computing dxdotdx_explicit +++ (4.47E-03s)\n", - "2023-02-16 12:37:18.806 - amici.ode_export - DEBUG - Finished writing dxdotdx_explicit.cpp ++ (6.56E-03s)\n", - "2023-02-16 12:37:18.812 - amici.ode_export - DEBUG - Finished running smart_jacobian ++++ (5.50E-04s)\n", - "2023-02-16 12:37:18.814 - amici.ode_export - DEBUG - Finished simplifying dxdotdp_explicit ++++ (3.08E-05s)\n", - "2023-02-16 12:37:18.815 - amici.ode_export - DEBUG - Finished computing dxdotdp_explicit +++ (4.37E-03s)\n", - "2023-02-16 12:37:18.815 - amici.ode_export - DEBUG - Finished writing dxdotdp_explicit.cpp ++ (6.87E-03s)\n", - "2023-02-16 12:37:18.854 - amici.ode_export - DEBUG - Finished running smart_jacobian +++++ (3.12E-02s)\n", - "2023-02-16 12:37:18.897 - amici.ode_export - DEBUG - Finished simplifying dydx +++++ (4.02E-02s)\n", - "2023-02-16 12:37:18.898 - amici.ode_export - DEBUG - Finished computing dydx ++++ (7.70E-02s)\n", - "2023-02-16 12:37:18.903 - amici.ode_export - DEBUG - Finished running smart_jacobian +++++ (4.23E-04s)\n", - "2023-02-16 12:37:18.905 - amici.ode_export - DEBUG - Finished simplifying dydw +++++ (2.99E-05s)\n", - "2023-02-16 12:37:18.906 - amici.ode_export - DEBUG - Finished computing dydw ++++ (4.61E-03s)\n", - "2023-02-16 12:37:18.942 - amici.ode_export - DEBUG - Finished simplifying dydx ++++ (3.36E-02s)\n", - "2023-02-16 12:37:18.942 - amici.ode_export - DEBUG - Finished computing dydx +++ (1.23E-01s)\n", - "2023-02-16 12:37:18.958 - amici.ode_export - DEBUG - Finished writing dydx.cpp ++ (1.40E-01s)\n", - "2023-02-16 12:37:18.966 - amici.ode_export - DEBUG - Finished running smart_jacobian +++++ (3.85E-04s)\n", - "2023-02-16 12:37:18.968 - amici.ode_export - DEBUG - Finished simplifying dydp +++++ (3.45E-05s)\n", - "2023-02-16 12:37:18.968 - amici.ode_export - DEBUG - Finished computing dydp ++++ (4.15E-03s)\n", - "2023-02-16 12:37:18.971 - amici.ode_export - DEBUG - Finished simplifying dydp ++++ (3.38E-05s)\n", - "2023-02-16 12:37:18.971 - amici.ode_export - DEBUG - Finished computing dydp +++ (8.39E-03s)\n", - "2023-02-16 12:37:18.971 - amici.ode_export - DEBUG - Finished writing dydp.cpp ++ (1.04E-02s)\n", - "2023-02-16 12:37:18.975 - amici.ode_export - DEBUG - Finished computing dzdx +++ (5.68E-05s)\n", - "2023-02-16 12:37:18.975 - amici.ode_export - DEBUG - Finished writing dzdx.cpp ++ (1.63E-03s)\n", - "2023-02-16 12:37:18.979 - amici.ode_export - DEBUG - Finished computing dzdp +++ (4.66E-05s)\n", - "2023-02-16 12:37:18.979 - amici.ode_export - DEBUG - Finished writing dzdp.cpp ++ (1.61E-03s)\n", - "2023-02-16 12:37:18.982 - amici.ode_export - DEBUG - Finished computing drzdx +++ (4.78E-05s)\n", - "2023-02-16 12:37:18.983 - amici.ode_export - DEBUG - Finished writing drzdx.cpp ++ (1.67E-03s)\n", - "2023-02-16 12:37:18.986 - amici.ode_export - DEBUG - Finished computing drzdp +++ (5.38E-05s)\n", - "2023-02-16 12:37:18.986 - amici.ode_export - DEBUG - Finished writing drzdp.cpp ++ (1.69E-03s)\n", - "2023-02-16 12:37:18.992 - amici.ode_export - DEBUG - Finished running smart_jacobian ++++ (1.11E-04s)\n", - "2023-02-16 12:37:18.994 - amici.ode_export - DEBUG - Finished simplifying dsigmaydy ++++ (3.44E-05s)\n", - "2023-02-16 12:37:18.994 - amici.ode_export - DEBUG - Finished computing dsigmaydy +++ (3.81E-03s)\n", - "2023-02-16 12:37:18.994 - amici.ode_export - DEBUG - Finished writing dsigmaydy.cpp ++ (5.42E-03s)\n", - "2023-02-16 12:37:19.000 - amici.ode_export - DEBUG - Finished running smart_jacobian ++++ (4.32E-04s)\n", - "2023-02-16 12:37:19.002 - amici.ode_export - DEBUG - Finished simplifying dsigmaydp ++++ (7.44E-05s)\n", - "2023-02-16 12:37:19.002 - amici.ode_export - DEBUG - Finished computing dsigmaydp +++ (4.23E-03s)\n", - "2023-02-16 12:37:19.003 - amici.ode_export - DEBUG - Finished writing dsigmaydp.cpp ++ (6.96E-03s)\n", - "2023-02-16 12:37:19.006 - amici.ode_export - DEBUG - Finished writing sigmay.cpp ++ (5.00E-04s)\n", - "2023-02-16 12:37:19.011 - amici.ode_export - DEBUG - Finished running smart_jacobian ++++ (2.56E-05s)\n", - "2023-02-16 12:37:19.013 - amici.ode_export - DEBUG - Finished simplifying dsigmazdp ++++ (3.78E-05s)\n", - "2023-02-16 12:37:19.013 - amici.ode_export - DEBUG - Finished computing dsigmazdp +++ (3.67E-03s)\n", - "2023-02-16 12:37:19.014 - amici.ode_export - DEBUG - Finished writing dsigmazdp.cpp ++ (5.26E-03s)\n", - "2023-02-16 12:37:19.016 - amici.ode_export - DEBUG - Finished writing sigmaz.cpp ++ (1.64E-05s)\n", - "2023-02-16 12:37:19.019 - amici.ode_export - DEBUG - Finished computing stau +++ (5.81E-05s)\n", - "2023-02-16 12:37:19.019 - amici.ode_export - DEBUG - Finished writing stau.cpp ++ (1.65E-03s)\n", - "2023-02-16 12:37:19.023 - amici.ode_export - DEBUG - Finished computing deltax +++ (5.10E-05s)\n", - "2023-02-16 12:37:19.024 - amici.ode_export - DEBUG - Finished writing deltax.cpp ++ (1.91E-03s)\n", - "2023-02-16 12:37:19.027 - amici.ode_export - DEBUG - Finished computing deltasx +++ (5.45E-05s)\n", - "2023-02-16 12:37:19.028 - amici.ode_export - DEBUG - Finished writing deltasx.cpp ++ (1.86E-03s)\n", - "2023-02-16 12:37:19.032 - amici.ode_export - DEBUG - Finished writing w.cpp ++ (2.53E-03s)\n", - "2023-02-16 12:37:19.038 - amici.ode_export - DEBUG - Finished simplifying x0 ++++ (8.98E-04s)\n", - "2023-02-16 12:37:19.038 - amici.ode_export - DEBUG - Finished computing x0 +++ (2.79E-03s)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2023-02-16 12:37:19.040 - amici.ode_export - DEBUG - Finished writing x0.cpp ++ (5.34E-03s)\n", - "2023-02-16 12:37:19.046 - amici.ode_export - DEBUG - Finished simplifying x0_fixedParameters ++++ (3.45E-04s)\n", - "2023-02-16 12:37:19.046 - amici.ode_export - DEBUG - Finished computing x0_fixedParameters +++ (2.33E-03s)\n", - "2023-02-16 12:37:19.047 - amici.ode_export - DEBUG - Finished writing x0_fixedParameters.cpp ++ (4.79E-03s)\n", - "2023-02-16 12:37:19.053 - amici.ode_export - DEBUG - Finished running smart_jacobian ++++ (9.02E-04s)\n", - "2023-02-16 12:37:19.055 - amici.ode_export - DEBUG - Finished simplifying sx0 ++++ (3.90E-05s)\n", - "2023-02-16 12:37:19.055 - amici.ode_export - DEBUG - Finished computing sx0 +++ (4.79E-03s)\n", - "2023-02-16 12:37:19.056 - amici.ode_export - DEBUG - Finished writing sx0.cpp ++ (6.54E-03s)\n", - "2023-02-16 12:37:19.061 - amici.ode_export - DEBUG - Finished running smart_jacobian ++++ (8.95E-05s)\n", - "2023-02-16 12:37:19.063 - amici.ode_export - DEBUG - Finished running smart_jacobian ++++ (1.41E-04s)\n", - "2023-02-16 12:37:19.066 - amici.ode_export - DEBUG - Finished simplifying sx0_fixedParameters ++++ (2.92E-05s)\n", - "2023-02-16 12:37:19.066 - amici.ode_export - DEBUG - Finished computing sx0_fixedParameters +++ (6.44E-03s)\n", - "2023-02-16 12:37:19.067 - amici.ode_export - DEBUG - Finished writing sx0_fixedParameters.cpp ++ (8.77E-03s)\n", - "2023-02-16 12:37:19.075 - amici.ode_export - DEBUG - Finished writing xdot.cpp ++ (5.99E-03s)\n", - "2023-02-16 12:37:19.080 - amici.ode_export - DEBUG - Finished writing y.cpp ++ (2.52E-03s)\n", - "2023-02-16 12:37:19.085 - amici.ode_export - DEBUG - Finished simplifying x_rdata ++++ (7.20E-05s)\n", - "2023-02-16 12:37:19.086 - amici.ode_export - DEBUG - Finished computing x_rdata +++ (2.21E-03s)\n", - "2023-02-16 12:37:19.087 - amici.ode_export - DEBUG - Finished writing x_rdata.cpp ++ (4.64E-03s)\n", - "2023-02-16 12:37:19.092 - amici.ode_export - DEBUG - Finished simplifying total_cl ++++ (3.50E-05s)\n", - "2023-02-16 12:37:19.093 - amici.ode_export - DEBUG - Finished computing total_cl +++ (1.79E-03s)\n", - "2023-02-16 12:37:19.093 - amici.ode_export - DEBUG - Finished writing total_cl.cpp ++ (3.50E-03s)\n", - "2023-02-16 12:37:19.098 - amici.ode_export - DEBUG - Finished running smart_jacobian ++++ (2.68E-05s)\n", - "2023-02-16 12:37:19.100 - amici.ode_export - DEBUG - Finished simplifying dtotal_cldp ++++ (3.98E-05s)\n", - "2023-02-16 12:37:19.100 - amici.ode_export - DEBUG - Finished computing dtotal_cldp +++ (3.62E-03s)\n", - "2023-02-16 12:37:19.100 - amici.ode_export - DEBUG - Finished writing dtotal_cldp.cpp ++ (5.25E-03s)\n", - "2023-02-16 12:37:19.105 - amici.ode_export - DEBUG - Finished simplifying dtotal_cldx_rdata ++++ (2.96E-05s)\n", - "2023-02-16 12:37:19.106 - amici.ode_export - DEBUG - Finished computing dtotal_cldx_rdata +++ (1.73E-03s)\n", - "2023-02-16 12:37:19.106 - amici.ode_export - DEBUG - Finished writing dtotal_cldx_rdata.cpp ++ (3.50E-03s)\n", - "2023-02-16 12:37:19.111 - amici.ode_export - DEBUG - Finished simplifying x_solver ++++ (7.02E-05s)\n", - "2023-02-16 12:37:19.112 - amici.ode_export - DEBUG - Finished computing x_solver +++ (1.82E-03s)\n", - "2023-02-16 12:37:19.113 - amici.ode_export - DEBUG - Finished writing x_solver.cpp ++ (4.36E-03s)\n", - "2023-02-16 12:37:19.118 - amici.ode_export - DEBUG - Finished simplifying dx_rdatadx_solver ++++ (2.82E-04s)\n", - "2023-02-16 12:37:19.118 - amici.ode_export - DEBUG - Finished computing dx_rdatadx_solver +++ (1.94E-03s)\n", - "2023-02-16 12:37:19.119 - amici.ode_export - DEBUG - Finished writing dx_rdatadx_solver.cpp ++ (4.05E-03s)\n", - "2023-02-16 12:37:19.125 - amici.ode_export - DEBUG - Finished simplifying dx_rdatadp ++++ (3.14E-04s)\n", - "2023-02-16 12:37:19.125 - amici.ode_export - DEBUG - Finished computing dx_rdatadp +++ (2.11E-03s)\n", - "2023-02-16 12:37:19.126 - amici.ode_export - DEBUG - Finished writing dx_rdatadp.cpp ++ (4.04E-03s)\n", - "2023-02-16 12:37:19.131 - amici.ode_export - DEBUG - Finished running smart_jacobian ++++ (2.58E-05s)\n", - "2023-02-16 12:37:19.133 - amici.ode_export - DEBUG - Finished simplifying dx_rdatadtcl ++++ (3.95E-05s)\n", - "2023-02-16 12:37:19.133 - amici.ode_export - DEBUG - Finished computing dx_rdatadtcl +++ (3.71E-03s)\n", - "2023-02-16 12:37:19.133 - amici.ode_export - DEBUG - Finished writing dx_rdatadtcl.cpp ++ (5.36E-03s)\n", - "2023-02-16 12:37:19.136 - amici.ode_export - DEBUG - Finished writing z.cpp ++ (1.84E-05s)\n", - "2023-02-16 12:37:19.138 - amici.ode_export - DEBUG - Finished writing rz.cpp ++ (1.69E-05s)\n", - "2023-02-16 12:37:19.147 - amici.ode_export - DEBUG - Finished generating cpp code + (6.45E-01s)\n", - "2023-02-16 12:37:31.424 - amici.ode_export - DEBUG - Finished compiling cpp code + (1.23E+01s)\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "running AmiciInstall\n", - "hdf5.h found in /opt/homebrew/Cellar/hdf5/1.12.2_2/include\n", - "libhdf5.a found in /opt/homebrew/Cellar/hdf5/1.12.2_2/lib\n", - "running build_ext\n", - "Changed extra_compile_args for unix to ['-std=c++14']\n", - "Building model extension in /Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014\n", - "building 'Boehm_JProteomeRes2014._Boehm_JProteomeRes2014' extension\n", - "Testing SWIG executable swig4.0... FAILED.\n", - "Testing SWIG executable swig3.0... FAILED.\n", - "Testing SWIG executable swig... SUCCEEDED.\n", - "swigging swig/Boehm_JProteomeRes2014.i to swig/Boehm_JProteomeRes2014_wrap.cpp\n", - "swig -python -c++ -modern -outdir Boehm_JProteomeRes2014 -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/swig -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/include -o swig/Boehm_JProteomeRes2014_wrap.cpp swig/Boehm_JProteomeRes2014.i\n", - "Deprecated command line option: -modern. Ignored, this option is now always on.\n", - "creating build\n", - "creating build/temp.macosx-13-arm64-cpython-310\n", - "creating build/temp.macosx-13-arm64-cpython-310/swig\n", - "clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk -I/Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014 -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/gsl -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/sundials/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/SuiteSparse/include -I/opt/homebrew/Cellar/hdf5/1.12.2_2/include -I/Users/fabian/Documents/projects/AMICI/build/venv/include -I/opt/homebrew/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/include/python3.10 -c Boehm_JProteomeRes2014.cpp -o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014.o -std=c++14\n", - "clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk -I/Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014 -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/gsl -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/sundials/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/SuiteSparse/include -I/opt/homebrew/Cellar/hdf5/1.12.2_2/include -I/Users/fabian/Documents/projects/AMICI/build/venv/include -I/opt/homebrew/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/include/python3.10 -c Boehm_JProteomeRes2014_Jy.cpp -o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_Jy.o -std=c++14\n", - "clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk -I/Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014 -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/gsl -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/sundials/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/SuiteSparse/include -I/opt/homebrew/Cellar/hdf5/1.12.2_2/include -I/Users/fabian/Documents/projects/AMICI/build/venv/include -I/opt/homebrew/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/include/python3.10 -c Boehm_JProteomeRes2014_dJydsigma.cpp -o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dJydsigma.o -std=c++14\n", - "clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk -I/Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014 -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/gsl -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/sundials/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/SuiteSparse/include -I/opt/homebrew/Cellar/hdf5/1.12.2_2/include -I/Users/fabian/Documents/projects/AMICI/build/venv/include -I/opt/homebrew/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/include/python3.10 -c Boehm_JProteomeRes2014_dJydy.cpp -o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dJydy.o -std=c++14\n", - "clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk -I/Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014 -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/gsl -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/sundials/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/SuiteSparse/include -I/opt/homebrew/Cellar/hdf5/1.12.2_2/include -I/Users/fabian/Documents/projects/AMICI/build/venv/include -I/opt/homebrew/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/include/python3.10 -c Boehm_JProteomeRes2014_dJydy_colptrs.cpp -o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dJydy_colptrs.o -std=c++14\n", - "clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk -I/Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014 -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/gsl -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/sundials/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/SuiteSparse/include -I/opt/homebrew/Cellar/hdf5/1.12.2_2/include -I/Users/fabian/Documents/projects/AMICI/build/venv/include -I/opt/homebrew/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/include/python3.10 -c Boehm_JProteomeRes2014_dJydy_rowvals.cpp -o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dJydy_rowvals.o -std=c++14\n", - "clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk -I/Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014 -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/gsl -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/sundials/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/SuiteSparse/include -I/opt/homebrew/Cellar/hdf5/1.12.2_2/include -I/Users/fabian/Documents/projects/AMICI/build/venv/include -I/opt/homebrew/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/include/python3.10 -c Boehm_JProteomeRes2014_dsigmaydp.cpp -o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dsigmaydp.o -std=c++14\n", - "clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk -I/Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014 -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/gsl -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/sundials/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/SuiteSparse/include -I/opt/homebrew/Cellar/hdf5/1.12.2_2/include -I/Users/fabian/Documents/projects/AMICI/build/venv/include -I/opt/homebrew/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/include/python3.10 -c Boehm_JProteomeRes2014_dwdp.cpp -o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dwdp.o -std=c++14\n", - "clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk -I/Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014 -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/gsl -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/sundials/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/SuiteSparse/include -I/opt/homebrew/Cellar/hdf5/1.12.2_2/include -I/Users/fabian/Documents/projects/AMICI/build/venv/include -I/opt/homebrew/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/include/python3.10 -c Boehm_JProteomeRes2014_dwdp_colptrs.cpp -o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dwdp_colptrs.o -std=c++14\n", - "clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk -I/Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014 -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/gsl -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/sundials/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/SuiteSparse/include -I/opt/homebrew/Cellar/hdf5/1.12.2_2/include -I/Users/fabian/Documents/projects/AMICI/build/venv/include -I/opt/homebrew/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/include/python3.10 -c Boehm_JProteomeRes2014_dwdp_rowvals.cpp -o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dwdp_rowvals.o -std=c++14\n", - "clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk -I/Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014 -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/gsl -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/sundials/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/SuiteSparse/include -I/opt/homebrew/Cellar/hdf5/1.12.2_2/include -I/Users/fabian/Documents/projects/AMICI/build/venv/include -I/opt/homebrew/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/include/python3.10 -c Boehm_JProteomeRes2014_dwdw.cpp -o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dwdw.o -std=c++14\n", - "clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk -I/Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014 -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/gsl -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/sundials/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/SuiteSparse/include -I/opt/homebrew/Cellar/hdf5/1.12.2_2/include -I/Users/fabian/Documents/projects/AMICI/build/venv/include -I/opt/homebrew/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/include/python3.10 -c Boehm_JProteomeRes2014_dwdw_colptrs.cpp -o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dwdw_colptrs.o -std=c++14\n", - "clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk -I/Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014 -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/gsl -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/sundials/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/SuiteSparse/include -I/opt/homebrew/Cellar/hdf5/1.12.2_2/include -I/Users/fabian/Documents/projects/AMICI/build/venv/include -I/opt/homebrew/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/include/python3.10 -c Boehm_JProteomeRes2014_dwdw_rowvals.cpp -o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dwdw_rowvals.o -std=c++14\n", - "clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk -I/Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014 -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/gsl -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/sundials/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/SuiteSparse/include -I/opt/homebrew/Cellar/hdf5/1.12.2_2/include -I/Users/fabian/Documents/projects/AMICI/build/venv/include -I/opt/homebrew/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/include/python3.10 -c Boehm_JProteomeRes2014_dwdx.cpp -o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dwdx.o -std=c++14\n", - "clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk -I/Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014 -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/gsl -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/sundials/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/SuiteSparse/include -I/opt/homebrew/Cellar/hdf5/1.12.2_2/include -I/Users/fabian/Documents/projects/AMICI/build/venv/include -I/opt/homebrew/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/include/python3.10 -c Boehm_JProteomeRes2014_dwdx_colptrs.cpp -o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dwdx_colptrs.o -std=c++14\n", - "clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk -I/Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014 -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/gsl -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/sundials/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/SuiteSparse/include -I/opt/homebrew/Cellar/hdf5/1.12.2_2/include -I/Users/fabian/Documents/projects/AMICI/build/venv/include -I/opt/homebrew/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/include/python3.10 -c Boehm_JProteomeRes2014_dwdx_rowvals.cpp -o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dwdx_rowvals.o -std=c++14\n", - "clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk -I/Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014 -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/gsl -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/sundials/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/SuiteSparse/include -I/opt/homebrew/Cellar/hdf5/1.12.2_2/include -I/Users/fabian/Documents/projects/AMICI/build/venv/include -I/opt/homebrew/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/include/python3.10 -c Boehm_JProteomeRes2014_dxdotdw.cpp -o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dxdotdw.o -std=c++14\n", - "clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk -I/Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014 -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/gsl -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/sundials/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/SuiteSparse/include -I/opt/homebrew/Cellar/hdf5/1.12.2_2/include -I/Users/fabian/Documents/projects/AMICI/build/venv/include -I/opt/homebrew/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/include/python3.10 -c Boehm_JProteomeRes2014_dxdotdw_colptrs.cpp -o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dxdotdw_colptrs.o -std=c++14\n", - "clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk -I/Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014 -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/gsl -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/sundials/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/SuiteSparse/include -I/opt/homebrew/Cellar/hdf5/1.12.2_2/include -I/Users/fabian/Documents/projects/AMICI/build/venv/include -I/opt/homebrew/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/include/python3.10 -c Boehm_JProteomeRes2014_dxdotdw_rowvals.cpp -o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dxdotdw_rowvals.o -std=c++14\n", - "clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk -I/Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014 -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/gsl -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/sundials/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/SuiteSparse/include -I/opt/homebrew/Cellar/hdf5/1.12.2_2/include -I/Users/fabian/Documents/projects/AMICI/build/venv/include -I/opt/homebrew/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/include/python3.10 -c Boehm_JProteomeRes2014_dydx.cpp -o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dydx.o -std=c++14\n", - "clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk -I/Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014 -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/gsl -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/sundials/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/SuiteSparse/include -I/opt/homebrew/Cellar/hdf5/1.12.2_2/include -I/Users/fabian/Documents/projects/AMICI/build/venv/include -I/opt/homebrew/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/include/python3.10 -c Boehm_JProteomeRes2014_sigmay.cpp -o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_sigmay.o -std=c++14\n", - "clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk -I/Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014 -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/gsl -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/sundials/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/SuiteSparse/include -I/opt/homebrew/Cellar/hdf5/1.12.2_2/include -I/Users/fabian/Documents/projects/AMICI/build/venv/include -I/opt/homebrew/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/include/python3.10 -c Boehm_JProteomeRes2014_sx0_fixedParameters.cpp -o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_sx0_fixedParameters.o -std=c++14\n", - "clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk -I/Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014 -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/gsl -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/sundials/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/SuiteSparse/include -I/opt/homebrew/Cellar/hdf5/1.12.2_2/include -I/Users/fabian/Documents/projects/AMICI/build/venv/include -I/opt/homebrew/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/include/python3.10 -c Boehm_JProteomeRes2014_w.cpp -o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_w.o -std=c++14\n", - "clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk -I/Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014 -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/gsl -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/sundials/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/SuiteSparse/include -I/opt/homebrew/Cellar/hdf5/1.12.2_2/include -I/Users/fabian/Documents/projects/AMICI/build/venv/include -I/opt/homebrew/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/include/python3.10 -c Boehm_JProteomeRes2014_x0.cpp -o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_x0.o -std=c++14\n", - "clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk -I/Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014 -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/gsl -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/sundials/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/SuiteSparse/include -I/opt/homebrew/Cellar/hdf5/1.12.2_2/include -I/Users/fabian/Documents/projects/AMICI/build/venv/include -I/opt/homebrew/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/include/python3.10 -c Boehm_JProteomeRes2014_x0_fixedParameters.cpp -o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_x0_fixedParameters.o -std=c++14\n", - "clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk -I/Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014 -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/gsl -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/sundials/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/SuiteSparse/include -I/opt/homebrew/Cellar/hdf5/1.12.2_2/include -I/Users/fabian/Documents/projects/AMICI/build/venv/include -I/opt/homebrew/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/include/python3.10 -c Boehm_JProteomeRes2014_x_rdata.cpp -o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_x_rdata.o -std=c++14\n", - "clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk -I/Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014 -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/gsl -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/sundials/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/SuiteSparse/include -I/opt/homebrew/Cellar/hdf5/1.12.2_2/include -I/Users/fabian/Documents/projects/AMICI/build/venv/include -I/opt/homebrew/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/include/python3.10 -c Boehm_JProteomeRes2014_x_solver.cpp -o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_x_solver.o -std=c++14\n", - "clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk -I/Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014 -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/gsl -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/sundials/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/SuiteSparse/include -I/opt/homebrew/Cellar/hdf5/1.12.2_2/include -I/Users/fabian/Documents/projects/AMICI/build/venv/include -I/opt/homebrew/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/include/python3.10 -c Boehm_JProteomeRes2014_xdot.cpp -o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_xdot.o -std=c++14\n", - "clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk -I/Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014 -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/gsl -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/sundials/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/SuiteSparse/include -I/opt/homebrew/Cellar/hdf5/1.12.2_2/include -I/Users/fabian/Documents/projects/AMICI/build/venv/include -I/opt/homebrew/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/include/python3.10 -c Boehm_JProteomeRes2014_y.cpp -o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_y.o -std=c++14\n", - "clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk -I/Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014 -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/gsl -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/sundials/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/SuiteSparse/include -I/opt/homebrew/Cellar/hdf5/1.12.2_2/include -I/Users/fabian/Documents/projects/AMICI/build/venv/include -I/opt/homebrew/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/include/python3.10 -c swig/Boehm_JProteomeRes2014_wrap.cpp -o build/temp.macosx-13-arm64-cpython-310/swig/Boehm_JProteomeRes2014_wrap.o -std=c++14\n", - "clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk -I/Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014 -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/gsl -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/sundials/include -I/Users/fabian/Documents/projects/AMICI/python/sdist/amici/ThirdParty/SuiteSparse/include -I/opt/homebrew/Cellar/hdf5/1.12.2_2/include -I/Users/fabian/Documents/projects/AMICI/build/venv/include -I/opt/homebrew/opt/python@3.10/Frameworks/Python.framework/Versions/3.10/include/python3.10 -c wrapfunctions.cpp -o build/temp.macosx-13-arm64-cpython-310/wrapfunctions.o -std=c++14\n", - "clang++ -bundle -undefined dynamic_lookup -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014.o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_Jy.o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dJydsigma.o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dJydy.o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dJydy_colptrs.o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dJydy_rowvals.o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dsigmaydp.o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dwdp.o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dwdp_colptrs.o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dwdp_rowvals.o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dwdw.o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dwdw_colptrs.o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dwdw_rowvals.o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dwdx.o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dwdx_colptrs.o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dwdx_rowvals.o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dxdotdw.o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dxdotdw_colptrs.o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dxdotdw_rowvals.o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_dydx.o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_sigmay.o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_sx0_fixedParameters.o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_w.o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_x0.o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_x0_fixedParameters.o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_x_rdata.o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_x_solver.o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_xdot.o build/temp.macosx-13-arm64-cpython-310/Boehm_JProteomeRes2014_y.o build/temp.macosx-13-arm64-cpython-310/swig/Boehm_JProteomeRes2014_wrap.o build/temp.macosx-13-arm64-cpython-310/wrapfunctions.o -L/opt/homebrew/Cellar/hdf5/1.12.2_2/lib -L/Users/fabian/Documents/projects/AMICI/python/sdist/amici/libs -lamici -lsundials -lsuitesparse -lcblas -lhdf5_hl_cpp -lhdf5_hl -lhdf5_cpp -lhdf5 -o /Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014/Boehm_JProteomeRes2014/_Boehm_JProteomeRes2014.cpython-310-darwin.so\n", - "ld: warning: -undefined dynamic_lookup may not work with chained fixups\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2023-02-16 12:37:31.673 - amici.petab_import - INFO - Finished Importing PEtab model (1.36E+01s)\n", - "2023-02-16 12:37:31.684 - amici.petab_import - INFO - Successfully loaded model Boehm_JProteomeRes2014 from /Users/fabian/Documents/projects/AMICI/documentation/amici_models/Boehm_JProteomeRes2014.\n" - ] - } - ], - "source": [ - "from amici.petab.petab_import import import_petab_problem\n", - "\n", - "amici_model = import_petab_problem(petab_problem, compile_=True)" - ] - }, - { - "cell_type": "markdown", - "id": "7827aaf7", - "metadata": {}, - "source": [ - "# JAX implementation" - ] - }, - { - "cell_type": "markdown", - "id": "e2ef051a", - "metadata": {}, - "source": [ - "For full jax support, we would have to implement a new [primitive](https://jax.readthedocs.io/en/latest/notebooks/How_JAX_primitives_work.html), which would require quite a bit of engineering, and in the end wouldn't add much benefit since AMICI can't run on GPUs. Instead, we will interface AMICI using the experimental jax module [`host_callback`](https://jax.readthedocs.io/en/latest/jax.experimental.host_callback.html). " - ] - }, - { - "cell_type": "markdown", - "id": "6bbf2f06", - "metadata": {}, - "source": [ - "To do so, we define a base function that only takes a single argument (the parameters) and runs simulation using petab via [`simulate_petab`](https://amici.readthedocs.io/en/latest/generated/amici.petab_objective.html#amici.petab_objective.simulate_petab). To enable gradient computation later on, we create a solver object and set the sensitivity order to first order and pass it to `simulate_petab`. Moreover, `simulate_petab` expects a dictionary of parameters, so we create a dictionary using the free parameter ids from the petab problem. As we want to implement parameter transformation in JAX, we disable parameter scaling in petab by passing `scaled_parameters=True`." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "72053647", - "metadata": {}, - "outputs": [], - "source": [ - "from amici.petab.simulations import simulate_petab\n", - "import amici\n", - "\n", - "amici_solver = amici_model.getSolver()\n", - "amici_solver.setSensitivityOrder(amici.SensitivityOrder.first)\n", - "\n", - "\n", - "def amici_hcb_base(parameters: jnp.array):\n", - " return simulate_petab(\n", - " petab_problem,\n", - " amici_model,\n", - " problem_parameters=dict(zip(petab_problem.x_free_ids, parameters)),\n", - " scaled_parameters=True,\n", - " solver=amici_solver,\n", - " )" - ] - }, - { - "cell_type": "markdown", - "id": "6f6201e8", - "metadata": {}, - "source": [ - "Now we can use this base function to create two functions separate functions that compute the log-likelihood (`llh`) and it's gradient (`sllh`) in two individual routines. Note that, as we are using the same base function here, the log-likelihood computation will also run with sensitivities which is not necessary and will add some overhead. This is only out of convenience and should be fixed in an application where efficiency is important." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "2dd50b53", - "metadata": {}, - "outputs": [], - "source": [ - "def amici_hcb_llh(parameters: jnp.array):\n", - " return amici_hcb_base(parameters)[\"llh\"]\n", - "\n", - "\n", - "def amici_hcb_sllh(parameters: jnp.array):\n", - " sllh = amici_hcb_base(parameters)[\"sllh\"]\n", - " return jnp.asarray(\n", - " tuple(sllh[par_id] for par_id in petab_problem.x_free_ids)\n", - " )" - ] - }, - { - "cell_type": "markdown", - "id": "98e819bd", - "metadata": {}, - "source": [ - "Now we can finally define the JAX function that runs amici simulation using the host callback. We add a `custom_jvp` decorator so that we can define a custom jacobian vector product function in the next step. More details about custom jacobian vector product functions can be found in the [JAX documentation](https://jax.readthedocs.io/en/latest/notebooks/Custom_derivative_rules_for_Python_code.html)" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "6e1f4f43", - "metadata": {}, - "outputs": [], - "source": [ - "import jax.experimental.host_callback as hcb\n", - "from jax import custom_jvp\n", - "\n", - "import numpy as np\n", - "\n", - "\n", - "@custom_jvp\n", - "def jax_objective(parameters: jnp.array):\n", - " return hcb.call(\n", - " amici_hcb_llh,\n", - " parameters,\n", - " result_shape=jax.ShapeDtypeStruct((), np.float64),\n", - " )" - ] - }, - { - "cell_type": "markdown", - "id": "c75535a5", - "metadata": {}, - "source": [ - "Now we define the function that implement the jacobian vector product. This effectively just returns the objective function value (computed using the previously defined `jax_objective`) as well as the inner product of the gradient (computed using a host callback to the previously defined `amici_hcb_sllh`) and the tangents vector. Note that this implementation performs two simulation runs, one for the function value and one for the gradient, which is inefficient and could be avoided by caching solutions." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "5a68c812", - "metadata": {}, - "outputs": [], - "source": [ - "@jax_objective.defjvp\n", - "def jax_objective_jvp(primals: jnp.array, tangents: jnp.array):\n", - " (parameters,) = primals\n", - " (x_dot,) = tangents\n", - " llh = jax_objective(parameters)\n", - " sllh = hcb.call(\n", - " amici_hcb_sllh,\n", - " parameters,\n", - " result_shape=jax.ShapeDtypeStruct(\n", - " (petab_problem.parameter_df.estimate.sum(),), np.float64\n", - " ),\n", - " )\n", - " return llh, sllh.dot(x_dot)" - ] - }, - { - "cell_type": "markdown", - "id": "379485ca", - "metadata": {}, - "source": [ - "As last step, we implement the parameter transformation in jax. This effectively just extracts parameter scales from the petab problem, implements rescaling in jax and then passes the scaled parameters to the previously objective function we previously defined. We add the `value_and_grad` decorator such that the generated jax function returns both function value and function gradient in a tuple. Moreover, we add the `jax.jit` decorator such that the function is [just in time compiled](https://jax.readthedocs.io/en/latest/jax-101/02-jitting.html) upon the first function call." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "3ab8fde9", - "metadata": {}, - "outputs": [], - "source": [ - "from jax import value_and_grad\n", - "\n", - "parameter_scales = petab_problem.parameter_df.loc[\n", - " petab_problem.x_free_ids, petab.PARAMETER_SCALE\n", - "].values\n", - "\n", - "\n", - "@jax.jit\n", - "@value_and_grad\n", - "def jax_objective_with_parameter_transform(parameters: jnp.array):\n", - " par_scaled = jnp.asarray(\n", - " tuple(\n", - " value\n", - " if scale == petab.LIN\n", - " else jnp.log(value)\n", - " if scale == petab.LOG\n", - " else jnp.log10(value)\n", - " for value, scale in zip(parameters, parameter_scales)\n", - " )\n", - " )\n", - " return jax_objective(par_scaled)" - ] - }, - { - "cell_type": "markdown", - "id": "bce56636", - "metadata": {}, - "source": [ - "# Testing" - ] - }, - { - "cell_type": "markdown", - "id": "293e29fb", - "metadata": {}, - "source": [ - "We can now run the function to compute the log-likelihood and the gradient. " - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "fb3085a8", - "metadata": {}, - "outputs": [], - "source": [ - "llh_jax, sllh_jax = jax_objective_with_parameter_transform(\n", - " petab_problem.x_nominal_free\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "6aa4a5f7", - "metadata": {}, - "source": [ - "As a sanity check, we compare the computed value to native parameter transformation in amici. " - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "48451b0e", - "metadata": {}, - "outputs": [], - "source": [ - "r = simulate_petab(petab_problem, amici_model, solver=amici_solver)\n", - "# TODO remove me as soon as sllh in simulate_petab is fixed\n", - "sllh = {\n", - " name: value / (np.log(10) * par_value)\n", - " for (name, value), par_value in zip(\n", - " r[\"sllh\"].items(), petab_problem.x_nominal_free\n", - " )\n", - "}" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "2628db12", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "amici -138.221997\n", - "jax -138.222000\n", - "dtype: float64" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import pandas as pd\n", - "\n", - "pd.Series(dict(amici=r[\"llh\"], jax=float(llh_jax)))" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "id": "0846523f", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
amicijax
Epo_degradation_BaF3-3.546026e-01-3.640394e-01
k_exp_hetero-2.401005e+03-2.401010e+03
k_exp_homo-4.073832e-01-4.106763e-01
k_imp_hetero-1.432855e-01-1.639030e-01
k_imp_homo2.006412e-102.006412e-10
k_phos-2.179950e-07-2.089803e-07
sd_pSTAT5A_rel-1.215545e-03-1.222887e-03
sd_pSTAT5B_rel-1.583889e-03-1.580870e-03
sd_rSTAT5A_rel-2.643776e-03-2.641361e-03
\n", - "
" - ], - "text/plain": [ - " amici jax\n", - "Epo_degradation_BaF3 -3.546026e-01 -3.640394e-01\n", - "k_exp_hetero -2.401005e+03 -2.401010e+03\n", - "k_exp_homo -4.073832e-01 -4.106763e-01\n", - "k_imp_hetero -1.432855e-01 -1.639030e-01\n", - "k_imp_homo 2.006412e-10 2.006412e-10\n", - "k_phos -2.179950e-07 -2.089803e-07\n", - "sd_pSTAT5A_rel -1.215545e-03 -1.222887e-03\n", - "sd_pSTAT5B_rel -1.583889e-03 -1.580870e-03\n", - "sd_rSTAT5A_rel -2.643776e-03 -2.641361e-03" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "pd.DataFrame(\n", - " index=sllh.keys(), data=dict(amici=sllh.values(), jax=np.asarray(sllh_jax))\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "4b00dcb2", - "metadata": {}, - "source": [ - "We see quite some differences in the gradient calculation. The primary reason is that running JAX in default configuration will use float32 precision for the parameters that are passed to AMICI, which uses float64, and the derivative of the parameter transformation \n", - "As AMICI simulations that run on the CPU are the most expensive operation, there is barely any tradeoff for using float32 vs float64 in JAX. Therefore, we configure JAX to use float64 instead and rerun simulations." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "5f81c693", - "metadata": {}, - "outputs": [], - "source": [ - "jax.config.update(\"jax_enable_x64\", True)\n", - "llh_jax, sllh_jax = jax_objective_with_parameter_transform(\n", - " petab_problem.x_nominal_free\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "ab39311d", - "metadata": {}, - "source": [ - "We can now evaluate the results again and see that differences between pure AMICI and AMICI/JAX implementations are now much smaller." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "id": "25e8b301", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "amici -138.221997\n", - "jax -138.221997\n", - "dtype: float64" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "pd.Series(dict(amici=r[\"llh\"], jax=float(llh_jax)))" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "id": "f31a3927", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
amicijax
Epo_degradation_BaF3-3.546026e-01-3.546504e-01
k_exp_hetero-2.401005e+03-2.401005e+03
k_exp_homo-4.073832e-01-4.074248e-01
k_imp_hetero-1.432855e-01-1.433139e-01
k_imp_homo2.006412e-102.006412e-10
k_phos-2.179950e-07-2.179076e-07
sd_pSTAT5A_rel-1.215545e-03-1.215596e-03
sd_pSTAT5B_rel-1.583889e-03-1.583805e-03
sd_rSTAT5A_rel-2.643776e-03-2.643703e-03
\n", - "
" - ], - "text/plain": [ - " amici jax\n", - "Epo_degradation_BaF3 -3.546026e-01 -3.546504e-01\n", - "k_exp_hetero -2.401005e+03 -2.401005e+03\n", - "k_exp_homo -4.073832e-01 -4.074248e-01\n", - "k_imp_hetero -1.432855e-01 -1.433139e-01\n", - "k_imp_homo 2.006412e-10 2.006412e-10\n", - "k_phos -2.179950e-07 -2.179076e-07\n", - "sd_pSTAT5A_rel -1.215545e-03 -1.215596e-03\n", - "sd_pSTAT5B_rel -1.583889e-03 -1.583805e-03\n", - "sd_rSTAT5A_rel -2.643776e-03 -2.643703e-03" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "pd.DataFrame(\n", - " index=sllh.keys(), data=dict(amici=sllh.values(), jax=np.asarray(sllh_jax))\n", - ")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.9" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/documentation/ExampleJax.ipynb b/documentation/ExampleJax.ipynb new file mode 120000 index 0000000000..af2929d76b --- /dev/null +++ b/documentation/ExampleJax.ipynb @@ -0,0 +1 @@ +../python/examples/example_jax/ExampleJax.ipynb \ No newline at end of file diff --git a/python/examples/example_jax/ExampleJax.ipynb b/python/examples/example_jax/ExampleJax.ipynb index 62391ce9be..931dfb7e28 100644 --- a/python/examples/example_jax/ExampleJax.ipynb +++ b/python/examples/example_jax/ExampleJax.ipynb @@ -5,7 +5,10 @@ "id": "d4d2bc5c", "metadata": {}, "source": [ - "# Overview\n", + "# AMICI & JAX\n", + "\n", + "## Overview\n", + "\n", "The purpose of this guide is to showcase how AMICI can be combined with differentiable programming in [JAX](https://jax.readthedocs.io/en/latest/index.html). We will do so by reimplementing the parameter transformations available in AMICI in JAX and comparing it to the native implementation." ] }, @@ -25,9 +28,9 @@ "id": "fb2fe897", "metadata": {}, "source": [ - "# Preparation\n", + "## Preparation\n", "\n", - "To get started we will import a model using the [petab](https://petab.readthedocs.io). To this end, we will use the [benchmark collection](https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab), which features a variety of different models. For more details about petab import, see the respective notebook petab [notebook](https://amici.readthedocs.io/en/latest/petab.html)." + "To get started, we will import a model using the [petab](https://petab.readthedocs.io). To this end, we will use the [benchmark collection](https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab), which features a variety of different models. For more details about petab import, see the respective notebook petab [notebook](https://amici.readthedocs.io/en/latest/petab.html)." ] }, { @@ -274,7 +277,7 @@ "id": "e2ef051a", "metadata": {}, "source": [ - "# JAX implementation\n", + "## JAX implementation\n", "\n", "For full jax support, we would have to implement a new [primitive](https://jax.readthedocs.io/en/latest/notebooks/How_JAX_primitives_work.html), which would require quite a bit of engineering, and in the end wouldn't add much benefit since AMICI can't run on GPUs. Instead, will interface AMICI using the experimental jax module [host_callback](https://jax.readthedocs.io/en/latest/jax.experimental.host_callback.html)." ] @@ -439,7 +442,7 @@ "id": "293e29fb", "metadata": {}, "source": [ - "# Testing\n", + "## Testing\n", "\n", "We can now run the function to compute the log-likelihood and the gradient." ] @@ -473,7 +476,7 @@ "id": "6aa4a5f7", "metadata": {}, "source": [ - "As a sanity check, we compare the computed value to native parameter transformation in amici. " + "As a sanity check, we compare the computed value to native parameter transformation in amici." ] }, { From 59d1d9f409ef393d18dc0d7b2e94247162a7fb58 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Thu, 7 Mar 2024 20:08:07 +0100 Subject: [PATCH 074/188] Bump version; update changelog --- CHANGELOG.md | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++ version.txt | 2 +- 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c1fd68c5c..ec825fa325 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,76 @@ ## v0.X Series +### v0.23.0 (2024-03-07) + +**Features** + +* SBML `InitialAssignment` are no longer absorbed into other model expressions, + but are available as parameters or expressions (`w`) in the amici model + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2304, + https://github.com/AMICI-dev/AMICI/pull/2305, + https://github.com/AMICI-dev/AMICI/pull/2345, + https://github.com/AMICI-dev/AMICI/pull/2359 +* Upgraded to SuiteSparse 7.6 + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2316 +* Model expressions `w` are now split into static and dynamic expressions, + and only evaluated as needed + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2303 +* Exposed additional solver settings: + * `Solver.setMaxConvFails()`: maximum number of non-linear solver + convergence failures + * `Solver.setMaxNonlinIters()`: maximum number of non-linear solver + iterations + * `Solver.setMaxStepSize()`: maximum step size + * `Solver.setConstraints()`: for setting (non)negativity/positivity + constraints on state variables + + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2335, + https://github.com/AMICI-dev/AMICI/pull/2360, + https://github.com/AMICI-dev/AMICI/pull/2340 +* Improved output for debugging simulation failures: + `ReturnData.{xdot,J}` now contain the respective + values from the timepoint of failure, not the last output timepoint. + NaN/Inf warnings now always include the timepoint at which the issue + occurred. Note that C++ stacktraces are now only logged for debug builds. + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2349, + https://github.com/AMICI-dev/AMICI/pull/2347, + https://github.com/AMICI-dev/AMICI/pull/2366 +* Updated dataframes import/export to include parameter values and scales + by @FFroehlich in https://github.com/AMICI-dev/AMICI/pull/2351 + +**Fixes** + +* CMake: Updated BLAS detection and some minor fixes + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2318 + and https://github.com/AMICI-dev/AMICI/pull/2357 +* Deterministic ordering of source files in generated `CMakeLists.txt` + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2322 +* Fixed size check in `Model::setStateIsNonNegative` + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2332 +* Fixed uncaught C++ exception in `runAmiciSimulation` that may crash Python + in case of invalid values for standard deviations + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2338 +* Fixed missing import in `amici/petab/petab_import.py` + by @plakrisenko in https://github.com/AMICI-dev/AMICI/pull/2342 +* Fixed `ReturnDataView` `AttributeError: messages` + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2341 +* Added a missing return code constant `LSETUP_FAIL` + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2353 +* Fixed in-place building of model wheels + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2352 +* Made is-zero-checks compatible with the upcoming sympy>1.12 + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2350 +* Fixed issues with paths containing blanks for sundials + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2361 +* Added `amici.petab.conditions` to the API documentation + by @PaulJonasJost in https://github.com/AMICI-dev/AMICI/pull/2364 +* Improved type annotations in swig-wrappers + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2344, + https://github.com/AMICI-dev/AMICI/pull/2365 + +**Full Changelog**: https://github.com/AMICI-dev/AMICI/compare/v0.22.0...v0.23.0 + ### v0.22.0 (2024-02-23) **Features** diff --git a/version.txt b/version.txt index 2157409059..ca222b7cf3 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.22.0 +0.23.0 From e7f2d3e6c584d8fe6395381ba0ba5f1e5e84241b Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Mon, 11 Mar 2024 13:19:54 +0100 Subject: [PATCH 075/188] Fix SuiteSparse build (#2375) * Only build static suitesparse libs, but position-independent * Make CMake prefer the vendored libraries over any system libraries Closes #2373. Closes #2374. --- python/sdist/setup.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/python/sdist/setup.py b/python/sdist/setup.py index 0da5ac9878..2f44a18342 100755 --- a/python/sdist/setup.py +++ b/python/sdist/setup.py @@ -41,6 +41,7 @@ def get_extensions(): # Used by all extensions global_cmake_configure_options = [ "-DCMAKE_VERBOSE_MAKEFILE=ON", + f"-DCMAKE_MODULE_PATH={prefix_path.as_posix()}", ] # SuiteSparse Config @@ -51,6 +52,7 @@ def get_extensions(): cmake_configure_options=[ *global_cmake_configure_options, "-DCMAKE_POSITION_INDEPENDENT_CODE=ON", + "-DBUILD_SHARED_LIBS=OFF", # Building SuiteSparse_config does not require a BLAS # we just set BLAS_LIBRARIES to skip the search, # the value is not used @@ -68,6 +70,8 @@ def get_extensions(): source_dir="amici/ThirdParty/SuiteSparse/AMD", cmake_configure_options=[ *global_cmake_configure_options, + "-DBUILD_SHARED_LIBS=OFF", + "-DCMAKE_POSITION_INDEPENDENT_CODE=ON", "-DSUITESPARSE_USE_FORTRAN=OFF", ], ) @@ -79,6 +83,8 @@ def get_extensions(): cmake_configure_options=[ *global_cmake_configure_options, "-DSUITESPARSE_USE_FORTRAN=OFF", + "-DBUILD_SHARED_LIBS=OFF", + "-DCMAKE_POSITION_INDEPENDENT_CODE=ON", ], ) # SuiteSparse COLAMD @@ -89,6 +95,8 @@ def get_extensions(): cmake_configure_options=[ *global_cmake_configure_options, "-DSUITESPARSE_USE_FORTRAN=OFF", + "-DBUILD_SHARED_LIBS=OFF", + "-DCMAKE_POSITION_INDEPENDENT_CODE=ON", ], ) # SuiteSparse KLU @@ -101,6 +109,8 @@ def get_extensions(): "-DKLU_USE_CHOLMOD=OFF", "-DSUITESPARSE_USE_CUDA=OFF", "-DSUITESPARSE_USE_FORTRAN=OFF", + "-DBUILD_SHARED_LIBS=OFF", + "-DCMAKE_POSITION_INDEPENDENT_CODE=ON", ], ) # SUNDIALS From 81ec1114b1582b601d6317d44fa34b0ef1e1601b Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Mon, 11 Mar 2024 13:22:16 +0100 Subject: [PATCH 076/188] Bump version, update release notes --- CHANGELOG.md | 5 +++++ version.txt | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec825fa325..7052c85b08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## v0.X Series +### v0.23.1 (2024-03-11) + +* Fixes installation issues related to building SuiteSparse on some systems + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2375 + ### v0.23.0 (2024-03-07) **Features** diff --git a/version.txt b/version.txt index ca222b7cf3..610e28725b 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.23.0 +0.23.1 From 032fb6c174723080ff33d7665e325bd0f6bc3c8e Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Wed, 13 Mar 2024 17:59:11 +0100 Subject: [PATCH 077/188] Add status code AMICI_CONSTR_FAIL (#2379) --- include/amici/defines.h | 1 + src/amici.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/include/amici/defines.h b/include/amici/defines.h index bd97d9a0b2..6ccba89923 100644 --- a/include/amici/defines.h +++ b/include/amici/defines.h @@ -71,6 +71,7 @@ constexpr int AMICI_CONV_FAILURE= -4; constexpr int AMICI_LSETUP_FAIL= -6; constexpr int AMICI_RHSFUNC_FAIL= -8; constexpr int AMICI_FIRST_RHSFUNC_ERR= -9; +constexpr int AMICI_CONSTR_FAIL= -15; constexpr int AMICI_ILL_INPUT= -22; constexpr int AMICI_ERROR= -99; constexpr int AMICI_NO_STEADY_STATE= -81; diff --git a/src/amici.cpp b/src/amici.cpp index 2f60a8e12a..c74c88e3a5 100644 --- a/src/amici.cpp +++ b/src/amici.cpp @@ -59,6 +59,7 @@ std::map simulation_status_to_str_map = { {AMICI_ERR_FAILURE, "AMICI_ERR_FAILURE"}, {AMICI_CONV_FAILURE, "AMICI_CONV_FAILURE"}, {AMICI_FIRST_RHSFUNC_ERR, "AMICI_FIRST_RHSFUNC_ERR"}, + {AMICI_CONSTR_FAIL, "AMICI_CONSTR_FAIL"}, {AMICI_RHSFUNC_FAIL, "AMICI_RHSFUNC_FAIL"}, {AMICI_ILL_INPUT, "AMICI_ILL_INPUT"}, {AMICI_ERROR, "AMICI_ERROR"}, From 41b4ce4c8c6340bfe599446732593054dd7358be Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Wed, 13 Mar 2024 19:22:38 +0100 Subject: [PATCH 078/188] Optionally include measurements in `plot_observable_trajectories` (#2381) If some `ExpData` is provided, `plot_observable_trajectories` will now also visualize the measurements. --- python/sdist/amici/plotting.py | 46 ++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/python/sdist/amici/plotting.py b/python/sdist/amici/plotting.py index 25607638d7..d27f2994ce 100644 --- a/python/sdist/amici/plotting.py +++ b/python/sdist/amici/plotting.py @@ -12,6 +12,7 @@ import seaborn as sns from matplotlib.axes import Axes +import amici from . import Model, ReturnDataView from .numpy import StrOrExpr, evaluate @@ -66,10 +67,11 @@ def plot_state_trajectories( for ix, label in zip(state_indices, labels): ax.plot(rdata["t"], rdata["x"][:, ix], marker=marker, label=label) - ax.set_xlabel("$t$") - ax.set_ylabel("$x(t)$") - ax.legend() - ax.set_title("State trajectories") + + ax.set_xlabel("$t$") + ax.set_ylabel("$x(t)$") + ax.legend() + ax.set_title("State trajectories") def plot_observable_trajectories( @@ -79,6 +81,7 @@ def plot_observable_trajectories( model: Model = None, prefer_names: bool = True, marker=None, + edata: Union[amici.ExpData, amici.ExpDataView] = None, ) -> None: """ Plot observable trajectories. @@ -97,8 +100,12 @@ def plot_observable_trajectories( :param marker: Point marker for plotting (see `matplotlib documentation `_). - + :param edata: + Experimental data to be plotted (no event observables yet). """ + if isinstance(edata, amici.amici.ExpData): + edata = amici.ExpDataView(edata) + if not ax: fig, ax = plt.subplots() if not observable_indices: @@ -125,11 +132,30 @@ def plot_observable_trajectories( labels = np.asarray(rdata.ptr.observable_ids)[list(observable_indices)] for iy, label in zip(observable_indices, labels): - ax.plot(rdata["t"], rdata["y"][:, iy], marker=marker, label=label) - ax.set_xlabel("$t$") - ax.set_ylabel("$y(t)$") - ax.legend() - ax.set_title("Observable trajectories") + (l,) = ax.plot( + rdata["t"], rdata["y"][:, iy], marker=marker, label=label + ) + + if edata is not None: + ax.plot( + edata.ts, + edata.observedData[:, iy], + "x", + label=f"exp. {label}", + color=l.get_color(), + ) + ax.errorbar( + edata.ts, + edata.observedData[:, iy], + yerr=rdata.sigmay[:, iy], + fmt="none", + color=l.get_color(), + ) + + ax.set_xlabel("$t$") + ax.set_ylabel("$y(t)$") + ax.set_title("Observable trajectories") + ax.legend() def plot_jacobian(rdata: ReturnDataView): From 790ab4427508de57ad430a6cc156e1869458bb31 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Mon, 18 Mar 2024 12:23:17 +0100 Subject: [PATCH 079/188] Fix initial state issues with PEtab (#2382) * Ensure initial state parameters are always fixed parameters * Ensure initial concentration for preequilibration and simulation are both set in both phases to avoid NaN issues * Fix initialAssignment handling during PEtab import (missed in #2359) --- python/sdist/amici/petab/parameter_mapping.py | 15 +++++++++------ python/sdist/amici/petab/sbml_import.py | 4 +++- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/python/sdist/amici/petab/parameter_mapping.py b/python/sdist/amici/petab/parameter_mapping.py index 9c527f0395..369ad13a96 100644 --- a/python/sdist/amici/petab/parameter_mapping.py +++ b/python/sdist/amici/petab/parameter_mapping.py @@ -486,12 +486,11 @@ def create_parameter_mapping_for_condition( condition_scale_map_preeq, preeq_value, ) - else: - # need to set dummy value for preeq parameter anyways, as it - # is expected below (set to 0, not nan, because will be - # multiplied with indicator variable in initial assignment) - condition_map_sim[init_par_id] = 0.0 - condition_scale_map_sim[init_par_id] = LIN + # need to set dummy value for preeq parameter anyways, as it + # is expected below (set to 0, not nan, because will be + # multiplied with indicator variable in initial assignment) + condition_map_sim[init_par_id] = 0.0 + condition_scale_map_sim[init_par_id] = LIN # for simulation condition_id = condition[SIMULATION_CONDITION_ID] @@ -505,6 +504,10 @@ def create_parameter_mapping_for_condition( condition_scale_map_sim, value, ) + # set dummy value as above + if condition_map_preeq: + condition_map_preeq[init_par_id] = 0.0 + condition_scale_map_preeq[init_par_id] = LIN ########################################################################## # separate fixed and variable AMICI parameters, because we may have diff --git a/python/sdist/amici/petab/sbml_import.py b/python/sdist/amici/petab/sbml_import.py index e349683505..2484d57a7a 100644 --- a/python/sdist/amici/petab/sbml_import.py +++ b/python/sdist/amici/petab/sbml_import.py @@ -300,6 +300,8 @@ def import_model_sbml( init_par = sbml_model.createParameter() init_par.setId(init_par_id) init_par.setName(init_par_id) + # must be a fixed parameter in any case to allow reinitialization + fixed_parameters.append(init_par_id) assignment = sbml_model.getInitialAssignment(assignee_id) if assignment is None: assignment = sbml_model.createInitialAssignment() @@ -535,7 +537,7 @@ def _get_fixed_parameters_sbml( ia.getMath(), parser_settings ) ) - if not sym_math.is_Number: + if not sym_math.evalf().is_Number: fixed_parameters.remove(fixed_parameter) continue From ebf5eeffb12d75ad8499d03102aab056958ee10d Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Thu, 28 Mar 2024 09:46:58 +0100 Subject: [PATCH 080/188] Fix cmake: `cannot create directory: /cmake/Amici` (#2389) Fixes cmake-install failures during model import in cases where BLAS was not found via FindBLAS. Needs a prettier solution at some point, but it does the job for now. --- cmake/AmiciFindBLAS.cmake | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/cmake/AmiciFindBLAS.cmake b/cmake/AmiciFindBLAS.cmake index 6b2f9cf5f9..51d9c9b257 100644 --- a/cmake/AmiciFindBLAS.cmake +++ b/cmake/AmiciFindBLAS.cmake @@ -83,13 +83,14 @@ if(NOT TARGET BLAS::BLAS) INTERFACE_INCLUDE_DIRECTORIES "${BLAS_INCLUDE_DIRS}" INTERFACE_LINK_LIBRARIES "${BLAS_LIBRARIES}") add_library(BLAS::BLAS ALIAS BLAS) - install(TARGETS BLAS EXPORT BLAS) - export(EXPORT BLAS NAMESPACE BLAS::) - install( - EXPORT BLAS - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Amici" - NAMESPACE BLAS::) - + if("${PROJECT_NAME}" STREQUAL "amici") + install(TARGETS BLAS EXPORT BLAS) + export(EXPORT BLAS NAMESPACE BLAS::) + install( + EXPORT BLAS + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Amici" + NAMESPACE BLAS::) + endif() # legacy python package environment variables: if(DEFINED ENV{BLAS_CFLAGS}) From b28b83b904504f7aa6c12da1254be3f335756644 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Thu, 28 Mar 2024 10:39:36 +0100 Subject: [PATCH 081/188] Fix Solver operator== and copyctor (#2388) A few recently added members were missing. Constraints were not copied when cloning a Solver. --- src/solver.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/solver.cpp b/src/solver.cpp index 6fc1132c4d..b9e497f309 100644 --- a/src/solver.cpp +++ b/src/solver.cpp @@ -17,6 +17,7 @@ Solver::Solver(Solver const& other) , maxsteps_(other.maxsteps_) , maxtime_(other.maxtime_) , simulation_timer_(other.simulation_timer_) + , constraints_(other.constraints_) , sensi_meth_(other.sensi_meth_) , sensi_meth_preeq_(other.sensi_meth_preeq_) , stldet_(other.stldet_) @@ -564,7 +565,9 @@ bool operator==(Solver const& a, Solver const& b) { == b.check_sensi_steadystate_conv_) && (a.rdata_mode_ == b.rdata_mode_) && (a.max_conv_fails_ == b.max_conv_fails_) - && (a.max_nonlin_iters_ == b.max_nonlin_iters_); + && (a.max_nonlin_iters_ == b.max_nonlin_iters_) + && (a.max_step_size_ == b.max_step_size_) + && (a.constraints_.getVector() == b.constraints_.getVector()); } void Solver::applyTolerances() const { From d9933622f2672360fe50c3a59d75bc14a0fcdaef Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Thu, 4 Apr 2024 16:41:16 +0200 Subject: [PATCH 082/188] Update valgrind suppressions (#2390) * Update valgrind suppressions --- python/tests/valgrind-python.supp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/python/tests/valgrind-python.supp b/python/tests/valgrind-python.supp index b4e0177033..01bc776aec 100644 --- a/python/tests/valgrind-python.supp +++ b/python/tests/valgrind-python.supp @@ -213,6 +213,19 @@ fun:__Pyx__PyObject_CallOneArg } +{ + other + Memcheck:Leak + match-leak-kinds: definite + fun:realloc + fun:resize_compact + fun:_PyUnicodeWriter_Finish + fun:PyUnicode_FromFormatV + fun:PyUnicode_FromFormat + fun:PyFortranObject_NewAsAttr + ... +} + { other Memcheck:Value8 From 6bbcde2cb12abe4286a0bc383fe3224b71fdc0db Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Wed, 17 Apr 2024 11:54:13 +0200 Subject: [PATCH 083/188] Require sphinx<7.3.0 (#2404) Closes #2403 --- documentation/rtd_requirements.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/documentation/rtd_requirements.txt b/documentation/rtd_requirements.txt index 8d2c2100f9..c5b1450dc2 100644 --- a/documentation/rtd_requirements.txt +++ b/documentation/rtd_requirements.txt @@ -1,5 +1,7 @@ # NOTE: relative paths are expected to be relative to the repository root -sphinx + +# sphinx<7.3.0: https://github.com/AMICI-dev/AMICI/issues/2403 +sphinx<7.3.0 mock>=5.0.2 setuptools>=67.7.2 pysb>=1.11.0 From 51c8cad7cb6130928091dd2a22e310433f351144 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Thu, 18 Apr 2024 08:37:41 +0200 Subject: [PATCH 084/188] Revert "Require sphinx<7.3.0 (#2404)" This reverts commit 6bbcde2cb12abe4286a0bc383fe3224b71fdc0db. Fixed in sphinx 7.3.6 (https://www.sphinx-doc.org/en/master/changes.html) --- documentation/rtd_requirements.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/documentation/rtd_requirements.txt b/documentation/rtd_requirements.txt index c5b1450dc2..8d2c2100f9 100644 --- a/documentation/rtd_requirements.txt +++ b/documentation/rtd_requirements.txt @@ -1,7 +1,5 @@ # NOTE: relative paths are expected to be relative to the repository root - -# sphinx<7.3.0: https://github.com/AMICI-dev/AMICI/issues/2403 -sphinx<7.3.0 +sphinx mock>=5.0.2 setuptools>=67.7.2 pysb>=1.11.0 From 18d4af316818208a1bd3ab316420b8e8c6ed4af7 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Fri, 19 Apr 2024 22:21:04 +0200 Subject: [PATCH 085/188] Model::checkFinite: exclude timepoints (#2395) Removes the check for non-finite timepoints. This rather confusing (#2370) than helpful. The current timepoint is already included in all other nan/inf messages, and the other timepoints don't matter in this context. Closes #2370. --- src/model.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/model.cpp b/src/model.cpp index cefdf1ac97..452e484f58 100644 --- a/src/model.cpp +++ b/src/model.cpp @@ -1671,7 +1671,6 @@ int Model::checkFinite( && model_quantity != ModelQuantity::ts) { checkFinite(state_.fixedParameters, ModelQuantity::k, t); checkFinite(state_.unscaledParameters, ModelQuantity::p, t); - checkFinite(simulation_parameters_.ts_, ModelQuantity::ts, t); if (!always_check_finite_ && model_quantity != ModelQuantity::w) { // don't check twice if always_check_finite_ is true checkFinite(derived_state_.w_, ModelQuantity::w, t); @@ -1789,7 +1788,6 @@ int Model::checkFinite( // check upstream checkFinite(state_.fixedParameters, ModelQuantity::k, t); checkFinite(state_.unscaledParameters, ModelQuantity::p, t); - checkFinite(simulation_parameters_.ts_, ModelQuantity::ts, t); checkFinite(derived_state_.w_, ModelQuantity::w, t); return AMICI_RECOVERABLE_ERROR; @@ -1880,7 +1878,6 @@ int Model::checkFinite(SUNMatrix m, ModelQuantity model_quantity, realtype t) // check upstream checkFinite(state_.fixedParameters, ModelQuantity::k, t); checkFinite(state_.unscaledParameters, ModelQuantity::p, t); - checkFinite(simulation_parameters_.ts_, ModelQuantity::ts, t); checkFinite(derived_state_.w_, ModelQuantity::w, t); return AMICI_RECOVERABLE_ERROR; From 0bba2361124aca5e8510fe331a2a1623cf8067a9 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Fri, 19 Apr 2024 22:22:16 +0200 Subject: [PATCH 086/188] =?UTF-8?q?More=20comprehensive/robust=20conversio?= =?UTF-8?q?n=20of=20docstring=20type/rtype=20to=20type=20=E2=80=A6=20(#240?= =?UTF-8?q?1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …annotations Should cover everything currently present in `amici.py`, except for overloaded methods. Exclude two methods from swig interface that aren't easily usable from Python. --- python/sdist/amici/swig.py | 75 ++++++++++++++++++++++++++++---------- swig/misc.i | 2 + 2 files changed, 57 insertions(+), 20 deletions(-) diff --git a/python/sdist/amici/swig.py b/python/sdist/amici/swig.py index fbc486c301..5ba8017005 100644 --- a/python/sdist/amici/swig.py +++ b/python/sdist/amici/swig.py @@ -1,11 +1,12 @@ """Functions related to SWIG or SWIG-generated code""" +from __future__ import annotations import ast import contextlib import re class TypeHintFixer(ast.NodeTransformer): - """Replaces SWIG-generated C++ typehints by corresponding Python types""" + """Replaces SWIG-generated C++ typehints by corresponding Python types.""" mapping = { "void": None, @@ -53,9 +54,13 @@ class TypeHintFixer(ast.NodeTransformer): "std::allocator< amici::ParameterScaling > > const &": ast.Constant( "ParameterScalingVector" ), + "H5::H5File": None, } def visit_FunctionDef(self, node): + # convert type/rtype from docstring to annotation, if possible. + # those may be c++ types, not valid in python, that need to be + # converted to python types below. self._annotation_from_docstring(node) # Has a return type annotation? @@ -67,14 +72,17 @@ def visit_FunctionDef(self, node): for arg in node.args.args: if not arg.annotation: continue - if isinstance(arg.annotation, ast.Name): + if not isinstance(arg.annotation, ast.Constant): # there is already proper annotation continue arg.annotation = self._new_annot(arg.annotation.value) return node - def _new_annot(self, old_annot: str): + def _new_annot(self, old_annot: str | ast.Name): + if isinstance(old_annot, ast.Name): + old_annot = old_annot.id + with contextlib.suppress(KeyError): return self.mapping[old_annot] @@ -117,6 +125,8 @@ def _annotation_from_docstring(self, node: ast.FunctionDef): Swig sometimes generates ``:type solver: :py:class:`Solver`` instead of ``:type solver: Solver``. Those need special treatment. + + Overloaded functions are skipped. """ docstring = ast.get_docstring(node, clean=False) if not docstring or "*Overload 1:*" in docstring: @@ -127,22 +137,18 @@ def _annotation_from_docstring(self, node: ast.FunctionDef): lines_to_remove = set() for line_no, line in enumerate(docstring): - if ( - match := re.match( - r"\s*:rtype:\s*(?::py:class:`)?(\w+)`?\s+$", line - ) - ) and not match.group(1).startswith(":"): - node.returns = ast.Constant(match.group(1)) + if type_str := self.extract_rtype(line): + # handle `:rtype:` + node.returns = ast.Constant(type_str) lines_to_remove.add(line_no) + continue - if ( - match := re.match( - r"\s*:type\s*(\w+):\W*(?::py:class:`)?(\w+)`?\s+$", line - ) - ) and not match.group(1).startswith(":"): + arg_name, type_str = self.extract_type(line) + if arg_name is not None: + # handle `:type ...:` for arg in node.args.args: - if arg.arg == match.group(1): - arg.annotation = ast.Constant(match.group(2)) + if arg.arg == arg_name: + arg.annotation = ast.Constant(type_str) lines_to_remove.add(line_no) if lines_to_remove: @@ -155,13 +161,42 @@ def _annotation_from_docstring(self, node: ast.FunctionDef): ) node.body[0].value = ast.Str(new_docstring) + @staticmethod + def extract_type(line: str) -> tuple[str, str] | tuple[None, None]: + """Extract argument name and type string from ``:type:`` docstring + line.""" + match = re.match(r"\s*:type\s+(\w+):\s+(.+?)(?:, optional)?\s*$", line) + if not match: + return None, None + + arg_name = match.group(1) + + # get rid of any :py:class`...` in the type string if necessary + if not match.group(2).startswith(":py:"): + return arg_name, match.group(2) + + match = re.match(r":py:\w+:`(.+)`", match.group(2)) + assert match + return arg_name, match.group(1) + + @staticmethod + def extract_rtype(line: str) -> str | None: + """Extract type string from ``:rtype:`` docstring line.""" + match = re.match(r"\s*:rtype:\s+(.+)\s*$", line) + if not match: + return None + + # get rid of any :py:class`...` in the type string if necessary + if not match.group(1).startswith(":py:"): + return match.group(1) + + match = re.match(r":py:\w+:`(.+)`", match.group(1)) + assert match + return match.group(1) + def fix_typehints(infilename, outfilename): """Change SWIG-generated C++ typehints to Python typehints""" - # Only available from Python3.9 - if not getattr(ast, "unparse", None): - return - # file -> AST with open(infilename) as f: source = f.read() diff --git a/swig/misc.i b/swig/misc.i index 8015e28bfe..af166b48ed 100644 --- a/swig/misc.i +++ b/swig/misc.i @@ -4,6 +4,8 @@ %ignore amici::regexErrorToString; %ignore amici::writeSlice; %ignore ContextManager; +%ignore amici::scaleParameters; +%ignore amici::unscaleParameters; // Add necessary symbols to generated header %{ From 206df037fc5ab2a5804982f87e5a17a080401cf5 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Fri, 19 Apr 2024 22:23:07 +0200 Subject: [PATCH 087/188] Fix ModuleNotFoundError in tox -e doc (#2400) `import exhale_multiproject_monkeypatch` fails on some systems due to unclear sys.path issues. Therefore, we need to adjust sys.path manually. --- documentation/conf.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/documentation/conf.py b/documentation/conf.py index 8b2379a299..25c6dab647 100644 --- a/documentation/conf.py +++ b/documentation/conf.py @@ -9,19 +9,33 @@ import subprocess import sys from enum import EnumType - -# need to import before setting typing.TYPE_CHECKING=True, fails otherwise -import amici import exhale.deploy -import exhale_multiproject_monkeypatch from unittest import mock -import pandas as pd import sphinx -import sympy as sp from exhale import configs as exhale_configs from sphinx.transforms.post_transforms import ReferencesResolver -exhale_multiproject_monkeypatch, pd, sp # to avoid removal of unused import +try: + import exhale_multiproject_monkeypatch # noqa: F401 +except ModuleNotFoundError: + # for unclear reasons, the import of exhale_multiproject_monkeypatch + # fails on some systems, because the the location of the editable install + # is not automatically added to sys.path ¯\_(ツ)_/¯ + from importlib.metadata import Distribution + import json + from urllib.parse import unquote_plus, urlparse + + dist = Distribution.from_name("sphinx-contrib-exhale-multiproject") + url = json.loads(dist.read_text("direct_url.json"))["url"] + package_dir = unquote_plus(urlparse(url).path) + sys.path.append(package_dir) + import exhale_multiproject_monkeypatch # noqa: F401 + +# need to import before setting typing.TYPE_CHECKING=True, fails otherwise +import amici +import pandas as pd # noqa: F401 +import sympy as sp # noqa: F401 + # BEGIN Monkeypatch exhale from exhale.deploy import _generate_doxygen as exhale_generate_doxygen From 09e0581ddc55d46e8ace6938cce33b50823c4fe0 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Fri, 19 Apr 2024 22:24:19 +0200 Subject: [PATCH 088/188] Fix incorrect exception types / messages for IDASolver (#2398) --- src/solver_idas.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/solver_idas.cpp b/src/solver_idas.cpp index 4f96c95f86..8093b336e5 100644 --- a/src/solver_idas.cpp +++ b/src/solver_idas.cpp @@ -17,7 +17,7 @@ namespace amici { /* - * The following static members are callback function to CVODES. + * The following static members are callback function to IDAS. * Their signatures must not be changes. */ @@ -437,7 +437,7 @@ void IDASolver::reInitPostProcess( auto status = IDASetStopTime(ida_mem, tout); if (status != IDA_SUCCESS) - throw IDAException(status, "CVodeSetStopTime"); + throw IDAException(status, "IDASetStopTime"); status = IDASolve( ami_mem, tout, t, yout->getNVector(), ypout->getNVector(), IDA_ONE_STEP @@ -853,7 +853,7 @@ void IDASolver::setNonLinearSolver() const { solver_memory_.get(), non_linear_solver_->get() ); if (status != IDA_SUCCESS) - throw CvodeException(status, "CVodeSetNonlinearSolver"); + throw IDAException(status, "IDASetNonlinearSolver"); } void IDASolver::setNonLinearSolverSens() const { @@ -883,7 +883,7 @@ void IDASolver::setNonLinearSolverSens() const { } if (status != IDA_SUCCESS) - throw CvodeException(status, "CVodeSolver::setNonLinearSolverSens"); + throw IDAException(status, "IDASolver::setNonLinearSolverSens"); } void IDASolver::setNonLinearSolverB(int which) const { @@ -891,7 +891,7 @@ void IDASolver::setNonLinearSolverB(int which) const { solver_memory_.get(), which, non_linear_solver_B_->get() ); if (status != IDA_SUCCESS) - throw CvodeException(status, "CVodeSetNonlinearSolverB"); + throw IDAException(status, "IDASetNonlinearSolverB"); } /** From 28b2611cc10c91f2d781944e9166b7d1e65341cf Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Fri, 19 Apr 2024 22:26:09 +0200 Subject: [PATCH 089/188] SwigPtrView: look up unhandled attributes in _swigptr (#2405) So far, only the explicitly listed attributes are accessible directly via SwigPtrView. Things like ReturnData.ny are only available through ReturnDataView.ptr.ny, which is inconvenient. Let's just look all non-private attributes that are not already explicitly handled by SwigPtrView on _swigptr and return them as is. --- python/sdist/amici/numpy.py | 17 ++++++++++------- python/sdist/amici/plotting.py | 2 +- python/tests/test_swig_interface.py | 3 +++ 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/python/sdist/amici/numpy.py b/python/sdist/amici/numpy.py index c1aef949c6..f40d0f4c6e 100644 --- a/python/sdist/amici/numpy.py +++ b/python/sdist/amici/numpy.py @@ -55,15 +55,18 @@ def __getitem__(self, item: str) -> Union[np.ndarray, float]: if item in self._cache: return self._cache[item] - if item == "id": - return getattr(self._swigptr, item) + if item in self._field_names: + value = _field_as_numpy( + self._field_dimensions, item, self._swigptr + ) + self._cache[item] = value - if item not in self._field_names: - self.__missing__(item) + return value + + if not item.startswith("_") and hasattr(self._swigptr, item): + return getattr(self._swigptr, item) - value = _field_as_numpy(self._field_dimensions, item, self._swigptr) - self._cache[item] = value - return value + self.__missing__(item) def __missing__(self, key: str) -> None: """ diff --git a/python/sdist/amici/plotting.py b/python/sdist/amici/plotting.py index d27f2994ce..19dbe05f89 100644 --- a/python/sdist/amici/plotting.py +++ b/python/sdist/amici/plotting.py @@ -109,7 +109,7 @@ def plot_observable_trajectories( if not ax: fig, ax = plt.subplots() if not observable_indices: - observable_indices = range(rdata["y"].shape[1]) + observable_indices = range(rdata.ny) if marker is None: # Show marker if only one time point is available, diff --git a/python/tests/test_swig_interface.py b/python/tests/test_swig_interface.py index f214519f26..b5063ca3cc 100644 --- a/python/tests/test_swig_interface.py +++ b/python/tests/test_swig_interface.py @@ -511,6 +511,9 @@ def test_rdataview(sbml_example_presimulation_module): rdata = amici.runAmiciSimulation(model, model.getSolver()) assert isinstance(rdata, amici.ReturnDataView) + # check that non-array attributes are looked up in the wrapped object + assert rdata.ptr.ny == rdata.ny + # fields are accessible via dot notation and [] operator, # __contains__ and __getattr__ are implemented correctly with pytest.raises(AttributeError): From e8493cf32773847078151954754b8a33eaa5047d Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Sat, 20 Apr 2024 00:49:20 +0200 Subject: [PATCH 090/188] cmake: set sundials path hint for python package (#2397) So far, CMakeLists.txt only provided a path hint for sundials for the standalone c++ library, but not for the python package. The latter should be added to ensure the right sundials version is found. There have been issues with system installations of sundials. Closes #2394 --- CMakeLists.txt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 744847930d..d8a1109d90 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -133,9 +133,16 @@ elseif(AMICI_TRY_ENABLE_HDF5) endif() set(VENDORED_SUNDIALS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ThirdParty/sundials) -set(VENDORED_SUNDIALS_BUILD_DIR ${VENDORED_SUNDIALS_DIR}/build) -set(VENDORED_SUNDIALS_INSTALL_DIR ${VENDORED_SUNDIALS_BUILD_DIR}) set(SUNDIALS_PRIVATE_INCLUDE_DIRS "${VENDORED_SUNDIALS_DIR}/src") +# Handle different sundials build/install dirs, depending on whether we are +# building the Python extension only or the full C++ interface +if(AMICI_PYTHON_BUILD_EXT_ONLY) + set(VENDORED_SUNDIALS_BUILD_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + set(VENDORED_SUNDIALS_INSTALL_DIR ${VENDORED_SUNDIALS_BUILD_DIR}) +else() + set(VENDORED_SUNDIALS_BUILD_DIR ${VENDORED_SUNDIALS_DIR}/build) + set(VENDORED_SUNDIALS_INSTALL_DIR ${VENDORED_SUNDIALS_BUILD_DIR}) +endif() find_package( SUNDIALS REQUIRED PATHS "${VENDORED_SUNDIALS_INSTALL_DIR}/${CMAKE_INSTALL_LIBDIR}/cmake/sundials/") From 01cc4e9cb00bb74a0f2f87e65331b1c2b783f885 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Sat, 20 Apr 2024 08:34:23 +0200 Subject: [PATCH 091/188] Allow subselection of state variables for steady-state simulations (#2387) * Allow subselection of state variables for steady-state simulations Closes #2368 * .. --- include/amici/model.h | 43 ++++++++++++++++++ include/amici/serialization.h | 1 + include/amici/steadystateproblem.h | 6 ++- include/amici/vector.h | 4 +- python/sdist/amici/swig_wrappers.py | 1 + python/tests/test_preequilibration.py | 63 +++++++++++++++++++++++++++ python/tests/test_swig_interface.py | 14 ++++-- src/hdf5.cpp | 6 +++ src/model.cpp | 21 ++++++++- src/steadystateproblem.cpp | 27 +++++++++--- swig/model.i | 1 + 11 files changed, 171 insertions(+), 16 deletions(-) diff --git a/include/amici/model.h b/include/amici/model.h index 29b98aa913..1c020400ac 100644 --- a/include/amici/model.h +++ b/include/amici/model.h @@ -1481,6 +1481,40 @@ class Model : public AbstractModel, public ModelDimensions { */ virtual std::vector get_trigger_timepoints() const; + /** + * @brief Get steady-state mask as std::vector. + * + * See `set_steadystate_mask` for details. + * + * @return Steady-state mask + */ + std::vector get_steadystate_mask() const { + return steadystate_mask_.getVector(); + }; + + /** + * @brief Get steady-state mask as AmiVector. + * + * See `set_steadystate_mask` for details. + * @return Steady-state mask + */ + AmiVector const& get_steadystate_mask_av() const { + return steadystate_mask_; + }; + + /** + * @brief Set steady-state mask. + * + * The mask is used to exclude certain state variables from the steady-state + * convergence check. Positive values indicate that the corresponding state + * variable should be included in the convergence check, while non-positive + * values indicate that the corresponding state variable should be excluded. + * An empty mask is interpreted as including all state variables. + * + * @param mask Mask of length `nx_solver`. + */ + void set_steadystate_mask(std::vector const& mask); + /** * Flag indicating whether for * `amici::Solver::sensi_` == `amici::SensitivityOrder::second` @@ -2087,6 +2121,15 @@ class Model : public AbstractModel, public ModelDimensions { /** Simulation parameters, initial state, etc. */ SimulationParameters simulation_parameters_; + + /** + * Mask for state variables that should be checked for steady state + * during pre-/post-equilibration. Positive values indicate that the + * corresponding state variable should be checked for steady state. + * Negative values indicate that the corresponding state variable should + * be ignored. + */ + AmiVector steadystate_mask_; }; bool operator==(Model const& a, Model const& b); diff --git a/include/amici/serialization.h b/include/amici/serialization.h index e4b1e3afe5..a03540104f 100644 --- a/include/amici/serialization.h +++ b/include/amici/serialization.h @@ -150,6 +150,7 @@ void serialize(Archive& ar, amici::Model& m, unsigned int const /*version*/) { ar & m.steadystate_computation_mode_; ar & m.steadystate_sensitivity_mode_; ar & m.state_independent_events_; + ar & m.steadystate_mask_; } /** diff --git a/include/amici/steadystateproblem.h b/include/amici/steadystateproblem.h index 55c9aaca77..72d248f8bf 100644 --- a/include/amici/steadystateproblem.h +++ b/include/amici/steadystateproblem.h @@ -246,14 +246,16 @@ class SteadystateProblem { * w_i = 1 / ( rtol * x_i + atol ) * @param x current state (sx[ip] for sensitivities) * @param xdot current rhs (sxdot[ip] for sensitivities) + * @param mask mask for state variables to include in WRMS norm. + * Positive value: include; non-positive value: exclude; empty: include all. * @param atol absolute tolerance * @param rtol relative tolerance * @param ewt error weight vector * @return root-mean-square norm */ realtype getWrmsNorm( - AmiVector const& x, AmiVector const& xdot, realtype atol, realtype rtol, - AmiVector& ewt + AmiVector const& x, AmiVector const& xdot, AmiVector const& mask, + realtype atol, realtype rtol, AmiVector& ewt ) const; /** diff --git a/include/amici/vector.h b/include/amici/vector.h index 486ea77682..27f1d81a81 100644 --- a/include/amici/vector.h +++ b/include/amici/vector.h @@ -430,8 +430,8 @@ inline span make_span(N_Vector nv) { } /** - * @brief Create span from N_Vector - * @param nv + * @brief Create span from AmiVector + * @param av * */ inline span make_span(amici::AmiVector const& av) { diff --git a/python/sdist/amici/swig_wrappers.py b/python/sdist/amici/swig_wrappers.py index e942cc76dd..72f850118c 100644 --- a/python/sdist/amici/swig_wrappers.py +++ b/python/sdist/amici/swig_wrappers.py @@ -255,6 +255,7 @@ def writeSolverSettingsToHDF5( "SteadyStateSensitivityMode", ("t0", "setT0"), "Timepoints", + "_steadystate_mask", ] diff --git a/python/tests/test_preequilibration.py b/python/tests/test_preequilibration.py index d003507199..d9a2335459 100644 --- a/python/tests/test_preequilibration.py +++ b/python/tests/test_preequilibration.py @@ -8,6 +8,10 @@ from amici.debugging import get_model_for_preeq from numpy.testing import assert_allclose, assert_equal from test_pysb import get_data +from amici.testing import ( + TemporaryDirectoryWinSafe as TemporaryDirectory, + skip_on_valgrind, +) @pytest.fixture @@ -658,3 +662,62 @@ def test_get_model_for_preeq(preeq_fixture): rdata1.sx, rdata2.sx, ) + + +@skip_on_valgrind +def test_partial_eq(): + """Check that partial equilibration is possible.""" + from amici.antimony_import import antimony2amici + + ant_str = """ + model test_partial_eq + explodes = 1 + explodes' = explodes + A = 1 + B = 0 + R: A -> B; k*A - k*B + k = 1 + end + """ + module_name = "test_partial_eq" + with TemporaryDirectory(prefix=module_name) as outdir: + antimony2amici( + ant_str, + model_name=module_name, + output_dir=outdir, + ) + model_module = amici.import_model_module( + module_name=module_name, module_path=outdir + ) + amici_model = model_module.getModel() + amici_model.setTimepoints([np.inf]) + amici_solver = amici_model.getSolver() + amici_solver.setRelativeToleranceSteadyState(1e-12) + + # equilibration of `explodes` will fail + rdata = amici.runAmiciSimulation(amici_model, amici_solver) + assert rdata.status == amici.AMICI_ERROR + assert rdata.messages[0].identifier == "EQUILIBRATION_FAILURE" + + # excluding `explodes` should enable equilibration + amici_model.set_steadystate_mask( + [ + 0 if state_id == "explodes" else 1 + for state_id in amici_model.getStateIdsSolver() + ] + ) + rdata = amici.runAmiciSimulation(amici_model, amici_solver) + assert rdata.status == amici.AMICI_SUCCESS + assert_allclose( + rdata.by_id("A"), + 0.5, + atol=amici_solver.getAbsoluteToleranceSteadyState(), + rtol=amici_solver.getRelativeToleranceSteadyState(), + ) + assert_allclose( + rdata.by_id("B"), + 0.5, + atol=amici_solver.getAbsoluteToleranceSteadyState(), + rtol=amici_solver.getRelativeToleranceSteadyState(), + ) + assert rdata.t_last < 100 diff --git a/python/tests/test_swig_interface.py b/python/tests/test_swig_interface.py index b5063ca3cc..2dfb46e0a7 100644 --- a/python/tests/test_swig_interface.py +++ b/python/tests/test_swig_interface.py @@ -39,7 +39,7 @@ def test_copy_constructors(pysb_example_presimulation_module): val = get_val(obj, attr) try: - modval = get_mod_val(val, attr) + modval = get_mod_val(val, attr, obj) except ValueError: # happens for everything that is not bool or scalar continue @@ -79,6 +79,10 @@ def test_copy_constructors(pysb_example_presimulation_module): tuple([1.0] + [0.0] * 35), tuple([0.1] * 36), ], + "_steadystate_mask": [ + (), + tuple([0] * 3), + ], "MinimumSigmaResiduals": [ 50.0, 60.0, @@ -361,19 +365,21 @@ def get_val(obj, attr): return getattr(obj, attr) -def get_mod_val(val, attr): +def get_mod_val(val, attr, obj): if attr == "getReturnDataReportingMode": return amici.RDataReporting.likelihood elif attr == "getParameterList": - return tuple(get_mod_val(val[0], "") for _ in val) + return tuple(get_mod_val(val[0], "", obj) for _ in val) elif attr == "getStateIsNonNegative": raise ValueError("Cannot modify value") + elif attr == "get_steadystate_mask": + return [0 for _ in range(obj.nx_solver)] elif isinstance(val, bool): return not val elif isinstance(val, numbers.Number): return val + 1 elif isinstance(val, tuple): - return tuple(get_mod_val(v, attr) for v in val) + return tuple(get_mod_val(v, attr, obj) for v in val) raise ValueError("Cannot modify value") diff --git a/src/hdf5.cpp b/src/hdf5.cpp index 0454b634f1..f9914452eb 100644 --- a/src/hdf5.cpp +++ b/src/hdf5.cpp @@ -1244,6 +1244,12 @@ void readModelDataFromHDF5( model.setInitialStates(x0); } + if (locationExists(file, datasetPath + "/steadystate_mask")) { + auto mask = getDoubleDataset1D(file, datasetPath + "/steadystate_mask"); + if (!mask.empty()) + model.set_steadystate_mask(mask); + } + if (locationExists(file, datasetPath + "/sx0")) { hsize_t length0 = 0; hsize_t length1 = 0; diff --git a/src/model.cpp b/src/model.cpp index 452e484f58..c27a5310d9 100644 --- a/src/model.cpp +++ b/src/model.cpp @@ -287,7 +287,9 @@ bool operator==(Model const& a, Model const& b) { && (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.state_ == b.state_; + && (a.state_ == b.state_) + && (a.steadystate_mask_.getVector() + == b.steadystate_mask_.getVector()); } bool operator==(ModelDimensions const& a, ModelDimensions const& b) { @@ -3135,6 +3137,23 @@ std::vector Model::get_trigger_timepoints() const { return trigger_timepoints; } +void Model::set_steadystate_mask(std::vector const& mask) { + if (mask.size() == 0) { + if (steadystate_mask_.getLength() != 0) { + steadystate_mask_ = AmiVector(); + } + return; + } + + if (gsl::narrow(mask.size()) != nx_solver) + throw AmiException( + "Steadystate mask has wrong size: %d, expected %d", + gsl::narrow(mask.size()), nx_solver + ); + + steadystate_mask_ = AmiVector(mask); +} + const_N_Vector Model::computeX_pos(const_N_Vector x) { if (any_state_non_negative_) { for (int ix = 0; ix < derived_state_.x_pos_tmp_.getLength(); ++ix) { diff --git a/src/steadystateproblem.cpp b/src/steadystateproblem.cpp index 98c36589f7..d78f9a8705 100644 --- a/src/steadystateproblem.cpp +++ b/src/steadystateproblem.cpp @@ -509,8 +509,8 @@ bool SteadystateProblem::getSensitivityFlag( } realtype SteadystateProblem::getWrmsNorm( - AmiVector const& x, AmiVector const& xdot, realtype atol, realtype rtol, - AmiVector& ewt + AmiVector const& x, AmiVector const& xdot, AmiVector const& mask, + realtype atol, realtype rtol, AmiVector& ewt ) const { /* Depending on what convergence we want to check (xdot, sxdot, xQBdot) we need to pass ewt[QB], as xdot and xQBdot have different sizes */ @@ -522,7 +522,14 @@ realtype SteadystateProblem::getWrmsNorm( N_VAddConst(ewt.getNVector(), atol, ewt.getNVector()); /* ewt = 1/ewt (ewt = 1/(rtol*x+atol)) */ N_VInv(ewt.getNVector(), ewt.getNVector()); - /* wrms = sqrt(sum((xdot/ewt)**2)/n) where n = size of state vector */ + + // wrms = sqrt(sum((xdot/ewt)**2)/n) where n = size of state vector + if (mask.getLength()) { + return N_VWrmsNormMask( + const_cast(xdot.getNVector()), ewt.getNVector(), + const_cast(mask.getNVector()) + ); + } return N_VWrmsNorm( const_cast(xdot.getNVector()), ewt.getNVector() ); @@ -543,7 +550,10 @@ SteadystateProblem::getWrms(Model& model, SensitivityMethod sensi_method) { "Newton type convergence check is not implemented for adjoint " "steady state computations. Stopping." ); - wrms = getWrmsNorm(xQB_, xQBdot_, atol_quad_, rtol_quad_, ewtQB_); + wrms = getWrmsNorm( + xQB_, xQBdot_, model.get_steadystate_mask_av(), atol_quad_, + rtol_quad_, ewtQB_ + ); } else { /* If we're doing a forward simulation (with or without sensitivities: Get RHS and compute weighted error norm */ @@ -552,7 +562,8 @@ SteadystateProblem::getWrms(Model& model, SensitivityMethod sensi_method) { else updateRightHandSide(model); wrms = getWrmsNorm( - state_.x, newton_step_conv_ ? delta_ : xdot_, atol_, rtol_, ewt_ + state_.x, newton_step_conv_ ? delta_ : xdot_, + model.get_steadystate_mask_av(), atol_, rtol_, ewt_ ); } return wrms; @@ -573,8 +584,10 @@ realtype SteadystateProblem::getWrmsFSA(Model& model) { ); if (newton_step_conv_) newton_solver_->solveLinearSystem(xdot_); - wrms - = getWrmsNorm(state_.sx[ip], xdot_, atol_sensi_, rtol_sensi_, ewt_); + wrms = getWrmsNorm( + state_.sx[ip], xdot_, model.get_steadystate_mask_av(), atol_sensi_, + rtol_sensi_, ewt_ + ); /* ideally this function would report the maximum of all wrms over all ip, but for practical purposes we can just report the wrms for the first ip where we know that the convergence threshold is not diff --git a/swig/model.i b/swig/model.i index fdb8ad9c85..93a21662c7 100644 --- a/swig/model.i +++ b/swig/model.i @@ -94,6 +94,7 @@ using namespace amici; %ignore fdx_rdatadtcl; %ignore fdx_rdatadx_solver; %ignore fdsigmaydy; +%ignore get_steadystate_mask_av; %newobject amici::Model::clone; From 97642613109d3fda86e7c51a0b4dd25e71a83576 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Mon, 22 Apr 2024 09:47:50 +0200 Subject: [PATCH 092/188] Update references (#2330) * KissVen2024 * .. --- documentation/amici_refs.bib | 31 +++++++++++++++++++++++++++++++ documentation/references.md | 17 ++++++++++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/documentation/amici_refs.bib b/documentation/amici_refs.bib index ef283eaf52..550a58ecc6 100644 --- a/documentation/amici_refs.bib +++ b/documentation/amici_refs.bib @@ -1275,6 +1275,37 @@ @Article{SluijsZho2024 publisher = {Springer Science and Business Media LLC}, } +@Article{KissVen2024, + author = {Kiss, Anna E and Venkatasubramani, Anuroop V and Pathirana, Dilan and Krause, Silke and Sparr, Aline Campos and Hasenauer, Jan and Imhof, Axel and Müller, Marisa and Becker, Peter B}, + journal = {Nucleic Acids Research}, + title = {{Processivity and specificity of histone acetylation by the male-specific lethal complex}}, + year = {2024}, + issn = {0305-1048}, + month = {02}, + pages = {gkae123}, + abstract = {{Acetylation of lysine 16 of histone H4 (H4K16ac) stands out among the histone modifications, because it decompacts the chromatin fiber. The metazoan acetyltransferase MOF (KAT8) regulates transcription through H4K16 acetylation. Antibody-based studies had yielded inconclusive results about the selectivity of MOF to acetylate the H4 N-terminus. We used targeted mass spectrometry to examine the activity of MOF in the male-specific lethal core (4-MSL) complex on nucleosome array substrates. This complex is part of the Dosage Compensation Complex (DCC) that activates X-chromosomal genes in male Drosophila. During short reaction times, MOF acetylated H4K16 efficiently and with excellent selectivity. Upon longer incubation, the enzyme progressively acetylated lysines 12, 8 and 5, leading to a mixture of oligo-acetylated H4. Mathematical modeling suggests that MOF recognizes and acetylates H4K16 with high selectivity, but remains substrate-bound and continues to acetylate more N-terminal H4 lysines in a processive manner. The 4-MSL complex lacks non-coding roX RNA, a critical component of the DCC. Remarkably, addition of RNA to the reaction non-specifically suppressed H4 oligo-acetylation in favor of specific H4K16 acetylation. Because RNA destabilizes the MSL-nucleosome interaction in vitro we speculate that RNA accelerates enzyme-substrate turn-over in vivo, thus limiting the processivity of MOF, thereby increasing specific H4K16 acetylation.}}, + creationdate = {2024-02-28T18:25:06}, + doi = {10.1093/nar/gkae123}, + eprint = {https://academic.oup.com/nar/advance-article-pdf/doi/10.1093/nar/gkae123/56756494/gkae123.pdf}, + modificationdate = {2024-02-28T18:25:06}, + url = {https://doi.org/10.1093/nar/gkae123}, +} + +@Article{DoresicGre2024, + author = {Domagoj Dore{\v s}i{\'c} and Stephan Grein and Jan Hasenauer}, + journal = {bioRxiv}, + title = {Efficient parameter estimation for ODE models of cellular processes using semi-quantitative data}, + year = {2024}, + abstract = {Quantitative dynamical models facilitate the understanding of biological processes and the prediction of their dynamics. The parameters of these models are commonly estimated from experimental data. Yet, experimental data generated from different techniques do not provide direct information about the state of the system but a non-linear (monotonic) transformation of it. For such semi-quantitative data, when this transformation is unknown, it is not apparent how the model simulations and the experimental data can be compared. Here, we propose a versatile spline-based approach for the integration of a broad spectrum of semi-quantitative data into parameter estimation. We derive analytical formulas for the gradients of the hierarchical objective function and show that this substantially increases the estimation efficiency. Subsequently, we demonstrate that the method allows for the reliable discovery of unknown measurement transformations. Furthermore, we show that this approach can significantly improve the parameter inference based on semi-quantitative data in comparison to available methods. Modelers can easily apply our method by using our implementation in the open-source Python Parameter EStimation TOolbox (pyPESTO).Competing Interest StatementThe authors have declared no competing interest.}, + creationdate = {2024-04-20T13:05:06}, + doi = {10.1101/2024.01.26.577371}, + elocation-id = {2024.01.26.577371}, + eprint = {https://www.biorxiv.org/content/early/2024/01/30/2024.01.26.577371.full.pdf}, + modificationdate = {2024-04-20T13:05:06}, + publisher = {Cold Spring Harbor Laboratory}, + url = {https://www.biorxiv.org/content/early/2024/01/30/2024.01.26.577371}, +} + @Comment{jabref-meta: databaseType:bibtex;} @Comment{jabref-meta: grouping: diff --git a/documentation/references.md b/documentation/references.md index 2164037aaf..0a2759f4eb 100644 --- a/documentation/references.md +++ b/documentation/references.md @@ -1,6 +1,6 @@ # References -List of publications using AMICI. Total number is 83. +List of publications using AMICI. Total number is 85. If you applied AMICI in your work and your publication is missing, please let us know via a new GitHub issue. @@ -14,6 +14,21 @@ If you applied AMICI in your work and your publication is missing, please let us

2024

+
+Dorešić, Domagoj, Stephan Grein, and Jan Hasenauer. 2024. +“Efficient Parameter Estimation for ODE Models of Cellular +Processes Using Semi-Quantitative Data.” bioRxiv. https://doi.org/10.1101/2024.01.26.577371. +
+
+Kiss, Anna E, Anuroop V Venkatasubramani, Dilan Pathirana, Silke Krause, +Aline Campos Sparr, Jan Hasenauer, Axel Imhof, Marisa Müller, and Peter +B Becker. 2024. Processivity and specificity +of histone acetylation by the male-specific lethal +complex.” Nucleic Acids Research, February, +gkae123. https://doi.org/10.1093/nar/gkae123. +
Lang, Paul F., David R. Penas, Julio R. Banga, Daniel Weindl, and Bela Novak. 2024. “Reusable Rule-Based Cell Cycle Model Explains From ca9d46799e5ceaa39c74e7647eb5fba6db46c6fb Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Sat, 20 Apr 2024 08:27:19 +0200 Subject: [PATCH 093/188] Bump version, update release notes --- CHANGELOG.md | 42 ++++++++++++++++++++++++++++++++++++++++++ version.txt | 2 +- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7052c85b08..e658b12593 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,48 @@ ## v0.X Series +### v0.24.0 (2024-04-22) + +This will be the last release supporting Python 3.9. +Future releases will require Python 3.10. + +**Fixes** + +* Fix cmake error `cannot create directory: /cmake/Amici` + during model import in cases where BLAS was not found via `FindBLAS` + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2389 +* Added status code `AMICI_CONSTR_FAIL` + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2379 +* Fixed certain initial state issues with PEtab + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2382 +* Fixed Solver `operator==` and copyctor + (constraints were not copied correctly) + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2388 +* Avoid confusing warnings about non-finite timepoints + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2395 +* Fixed incorrect exception types / messages for `IDASolver` + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2398 +* cmake: set SUNDIALS path hint for python package to help CMake find + the correct SUNDIALS installation + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2397 + +* **Features** + +* Optionally include measurements in `plot_observable_trajectories` + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2381 +* Improved type annotations in swig-wrappers + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2401 +* Additional attributes are accessible directly via `ReturnDataView` and + `ExpDataView`, e.g. `ReturnDataView.ny`, `ReturnDataView.nx` + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2405 +* Allow subselection of state variables for convergence check during + steady-state simulations via `Model.set_steadystate_mask([1, 0, ..., 1])` + (positive value: check; non-positive: don't check). + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2387 + +**Full Changelog**: https://github.com/AMICI-dev/AMICI/compare/v0.23.1...v0.24.0 + + ### v0.23.1 (2024-03-11) * Fixes installation issues related to building SuiteSparse on some systems diff --git a/version.txt b/version.txt index 610e28725b..2094a100ca 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.23.1 +0.24.0 From f392619c563eb3c80a7980a400b8cfd827a5bc93 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Mon, 22 Apr 2024 09:58:03 +0200 Subject: [PATCH 094/188] setup.cfg -> pyproject.toml (#2408) Let's reduce the number of package configuration files... --- .github/workflows/deploy_release.yml | 2 +- python/sdist/amici/custom_commands.py | 8 +- python/sdist/bin | 1 - python/sdist/pyproject.toml | 110 +++++++++++++++++++++++++- python/sdist/setup.cfg | 91 --------------------- python/sdist/setup.py | 10 --- 6 files changed, 116 insertions(+), 106 deletions(-) delete mode 120000 python/sdist/bin delete mode 100644 python/sdist/setup.cfg diff --git a/.github/workflows/deploy_release.yml b/.github/workflows/deploy_release.yml index de7a073cc2..25eaf97a0e 100644 --- a/.github/workflows/deploy_release.yml +++ b/.github/workflows/deploy_release.yml @@ -39,7 +39,7 @@ jobs: - name: Remove direct dependencies from setup.cfg # Remove any "git+https"-based dependencies that are not supported # by PyPI and are only required for testing. - run: sed -i '/git+https/d' python/sdist/setup.cfg + run: sed -i '/git+https/d' python/sdist/pyproject.toml - name: sdist run: scripts/buildSdist.sh diff --git a/python/sdist/amici/custom_commands.py b/python/sdist/amici/custom_commands.py index 46abfe3290..36fcd17605 100644 --- a/python/sdist/amici/custom_commands.py +++ b/python/sdist/amici/custom_commands.py @@ -17,13 +17,17 @@ class AmiciInstall(install): """Custom `install` command to handle extra arguments""" - print("running AmiciInstall") - # Passing --no-clibs allows to install the Python-only part of AMICI user_options = install.user_options + [ ("no-clibs", None, "Don't build AMICI C++ extension"), ] + def run(self): + """Setuptools entry-point""" + print(f"running {self.__class__.__name__}") + + super().run() + def initialize_options(self): super().initialize_options() self.no_clibs = False diff --git a/python/sdist/bin b/python/sdist/bin deleted file mode 120000 index 353d1e9f9e..0000000000 --- a/python/sdist/bin +++ /dev/null @@ -1 +0,0 @@ -../bin/ \ No newline at end of file diff --git a/python/sdist/pyproject.toml b/python/sdist/pyproject.toml index 91b8484af6..15ee06df73 100644 --- a/python/sdist/pyproject.toml +++ b/python/sdist/pyproject.toml @@ -1,6 +1,8 @@ +# https://packaging.python.org/en/latest/guides/writing-pyproject-toml/ +# https://setuptools.pypa.io/en/latest/userguide/index.html [build-system] requires = [ - "setuptools>=40.6.3", + "setuptools>=61", "wheel", # oldest-supported-numpy helps us to pin numpy here to the lowest supported # version to have ABI-compatibility with the numpy version in the runtime @@ -13,6 +15,112 @@ requires = [ ] build-backend = "setuptools.build_meta" +[project] +name = "amici" +dynamic = ["version"] +description = "Advanced multi-language Interface to CVODES and IDAS" +requires-python = ">=3.9" +dependencies = [ + "cmake-build-extension==0.5.1", + "sympy>=1.9", + "numpy>=1.19.3; python_version=='3.9'", + "numpy>=1.21.4; python_version>='3.10'", + "numpy>=1.23.2; python_version=='3.11'", + "numpy; python_version>='3.12'", + "python-libsbml", + "pandas>=2.0.2", + "pyarrow", + "wurlitzer", + "toposort", + "setuptools>=48", + "mpmath", +] +license = {text = "BSD 3-Clause License"} +authors = [ + {name = "Fabian Froehlich", email = "froehlichfab@gmail.com"}, + {name = "Daniel Weindl", email = "sci@danielweindl.de"}, + {name = "Jan Hasenauer"}, + {name = "AMICI contributors"}, +] +maintainers = [ + {name = "Fabian Froehlich", email = "froehlichfab@gmail.com"}, + {name = "Daniel Weindl", email = "sci@danielweindl.de"}, +] +readme = "README.md" +keywords =["differential equations", "simulation", "ode", "cvodes", + "systems biology", "sensitivity analysis", "sbml", "pysb", "petab"] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: BSD License", + "Operating System :: POSIX :: Linux", + "Operating System :: MacOS :: MacOS X", + "Programming Language :: Python", + "Programming Language :: C++", + "Topic :: Scientific/Engineering :: Bio-Informatics", +] + +[project.optional-dependencies] +# Don't include any URLs here - they are not supported by PyPI: +# HTTPError: 400 Bad Request from https://upload.pypi.org/legacy/ +# Invalid value for requires_dist. Error: Can't have direct dependency: ... +petab = ["petab>=0.2.9"] +pysb = ["pysb>=1.13.1"] +test = [ + "benchmark_models_petab @ git+https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab.git@master#subdirectory=src/python", + "h5py", + "pytest", + "pytest-cov", + "pytest-rerunfailures", + "coverage", + "shyaml", + "antimony>=2.13", + # see https://github.com/sys-bio/antimony/issues/92 + # unsupported x86_64 / x86_64h + "antimony!=2.14; platform_system=='Darwin' and platform_machine in 'x86_64h'", + "scipy", + "pooch" +] +vis =[ + "matplotlib", + "seaborn", +] +examples =[ + "jupyter", + "scipy", +] + +[project.scripts] +# amici_import_petab.py is kept for backwards compatibility +amici_import_petab = "amici.petab.cli.import_petab:_main" +"amici_import_petab.py" = "amici.petab.cli.import_petab:_main" + +[project.urls] +Homepage = "https://github.com/AMICI-dev/AMICI" +Documentation = "https://amici.readthedocs.io/en/latest/" +Repository = "https://github.com/AMICI-dev/AMICI.git" +"Bug Tracker" = "https://github.com/AMICI-dev/AMICI/issues" + +# TODO: consider using setuptools_scm +#[tool.setuptools_scm] +## https://setuptools-scm.readthedocs.io/en/latest/ +#root = "../.." + +[tool.setuptools.package-data] +amici = [ + "amici/include/amici/*", + "src/*template*", + "swig/*", + "libs/*", + "setup.py.template", +] + +[tool.setuptools.exclude-package-data] +"*" = ["README.txt"] + +[tool.setuptools.dynamic] +version = {attr = "amici.__version__"} + [tool.black] line-length = 79 diff --git a/python/sdist/setup.cfg b/python/sdist/setup.cfg deleted file mode 100644 index d34d42f98f..0000000000 --- a/python/sdist/setup.cfg +++ /dev/null @@ -1,91 +0,0 @@ -[metadata] -name = amici -description = Advanced multi-language Interface to CVODES and IDAS -version = file: amici/version.txt -license = BSD 3-Clause License -url = https://github.com/AMICI-dev/AMICI -keywords = differential equations, simulation, ode, cvodes, systems biology, sensitivity analysis, sbml, pysb, petab -author = Fabian Froehlich, Jan Hasenauer, Daniel Weindl and Paul Stapor -author_email = fabian_froehlich@hms.harvard.edu -project_urls = - Bug Reports = https://github.com/AMICI-dev/AMICI/issues - Source = https://github.com/AMICI-dev/AMICI - Documentation = https://amici.readthedocs.io/en/latest/ -classifiers = - Development Status :: 5 - Production/Stable - Intended Audience :: Science/Research - License :: OSI Approved :: BSD License - Operating System :: POSIX :: Linux - Operating System :: MacOS :: MacOS X - Programming Language :: Python - Programming Language :: C++ - Topic :: Scientific/Engineering :: Bio-Informatics - -[options] -packages = find_namespace: -package_dir = - amici = amici -python_requires = >=3.9 -install_requires = - cmake-build-extension==0.5.1 - sympy>=1.9 - numpy>=1.19.3; python_version=='3.9' - numpy>=1.21.4; python_version>='3.10' - numpy>=1.23.2; python_version=='3.11' - numpy; python_version>='3.12' - python-libsbml - pandas>=2.0.2 - pyarrow - wurlitzer - toposort - setuptools>=48 - mpmath -include_package_data = True -zip_safe = False - -[options.extras_require] -# Don't include any URLs here - they are not supported by PyPI: -# HTTPError: 400 Bad Request from https://upload.pypi.org/legacy/ -# Invalid value for requires_dist. Error: Can't have direct dependency: ... -petab = petab>=0.2.9 -pysb = pysb>=1.13.1 -test = - benchmark_models_petab @ git+https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab.git@master#subdirectory=src/python - h5py - pytest - pytest-cov - pytest-rerunfailures - coverage - shyaml - antimony>=2.13 - # see https://github.com/sys-bio/antimony/issues/92 - # unsupported x86_64 / x86_64h - antimony!=2.14; platform_system=='Darwin' and platform_machine in 'x86_64h' - scipy - pooch -vis = - matplotlib - seaborn -examples = - jupyter - scipy - -[options.package_data] -amici = - amici/include/amici/* - src/*template* - swig/* - libs/* - setup.py.template - -[options.exclude_package_data] -* = - README.txt - - -[options.entry_points] - -; amici_import_petab.py is kept for backwards compatibility -console_scripts = - amici_import_petab = amici.petab.cli.import_petab:_main - amici_import_petab.py = amici.petab.cli.import_petab:_main diff --git a/python/sdist/setup.py b/python/sdist/setup.py index 2f44a18342..cfc474ebe8 100755 --- a/python/sdist/setup.py +++ b/python/sdist/setup.py @@ -158,14 +158,6 @@ def get_extensions(): def main(): - # Readme as long package description to go on PyPi - # (https://pypi.org/project/amici/) - with open( - os.path.join(os.path.dirname(__file__), "README.md"), - encoding="utf-8", - ) as fh: - long_description = fh.read() - ext_modules = get_extensions() # handle parallel building @@ -185,8 +177,6 @@ def main(): "develop": AmiciDevelop, "build_py": AmiciBuildPy, }, - long_description=long_description, - long_description_content_type="text/markdown", ext_modules=ext_modules, ) From a3cc21e933e09da1e6ce8197c907226fdc1d75a1 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Mon, 22 Apr 2024 09:56:35 +0200 Subject: [PATCH 095/188] Move the `amici.swig_wrappers.ExpData` logic into the swig-generated `ExpData`. (#2396) This avoids shading the `ExpData` class by `amici.swig_wrappers.ExpData`. The old implementation prevented using e.g. `isinstance(x, amici.ExpData)`. This required also: * Moving some annotation types and `_get_ptr` from `amici.swig_wrappers` to the swig-generated `amici.py` * Some smaller changes to allow for using `from __future__ import annotations` in `amici.py` Closes #2380 --- documentation/conf.py | 8 +-- python/sdist/amici/__init__.py | 2 - python/sdist/amici/swig_wrappers.py | 78 +++-------------------------- swig/amici.i | 37 +++++++++++++- swig/edata.i | 18 +++++++ 5 files changed, 65 insertions(+), 78 deletions(-) diff --git a/documentation/conf.py b/documentation/conf.py index 25c6dab647..a0dcf658fb 100644 --- a/documentation/conf.py +++ b/documentation/conf.py @@ -558,15 +558,15 @@ def fix_typehints(sig: str) -> str: sig = sig.replace("sunindextype", "int") sig = sig.replace("H5::H5File", "object") - # remove const - sig = sig.replace(" const ", r" ") - sig = re.sub(r" const$", r"", sig) + # remove const / const& + sig = sig.replace(" const&? ", r" ") + sig = re.sub(r" const&?$", r"", sig) # remove pass by reference sig = re.sub(r" &(,|\))", r"\1", sig) sig = re.sub(r" &$", r"", sig) - # turn gsl_spans and pointers int Iterables + # turn gsl_spans and pointers into Iterables sig = re.sub(r"([\w.]+) \*", r"Iterable[\1]", sig) sig = re.sub(r"gsl::span< ([\w.]+) >", r"Iterable[\1]", sig) diff --git a/python/sdist/amici/__init__.py b/python/sdist/amici/__init__.py index cd7bcb0500..76369eda37 100644 --- a/python/sdist/amici/__init__.py +++ b/python/sdist/amici/__init__.py @@ -134,8 +134,6 @@ def get_model(self) -> amici.Model: """Create a model instance.""" ... - AmiciModel = Union[amici.Model, amici.ModelPtr] - class add_path: """Context manager for temporarily changing PYTHONPATH""" diff --git a/python/sdist/amici/swig_wrappers.py b/python/sdist/amici/swig_wrappers.py index 72f850118c..b6023b2c34 100644 --- a/python/sdist/amici/swig_wrappers.py +++ b/python/sdist/amici/swig_wrappers.py @@ -4,11 +4,16 @@ import warnings from contextlib import contextmanager, suppress from typing import Any, Optional, Union -from collections.abc import Sequence import amici import amici.amici as amici_swig - +from amici.amici import ( + _get_ptr, + AmiciExpData, + AmiciExpDataVector, + AmiciModel, + AmiciSolver, +) from . import numpy from .logging import get_logger @@ -18,25 +23,12 @@ __all__ = [ "runAmiciSimulation", "runAmiciSimulations", - "ExpData", "readSolverSettingsFromHDF5", "writeSolverSettingsToHDF5", "set_model_settings", "get_model_settings", - "AmiciModel", - "AmiciSolver", - "AmiciExpData", - "AmiciReturnData", - "AmiciExpDataVector", ] -AmiciModel = Union["amici.Model", "amici.ModelPtr"] -AmiciSolver = Union["amici.Solver", "amici.SolverPtr"] -AmiciExpData = Union["amici.ExpData", "amici.ExpDataPtr"] -AmiciReturnData = Union["amici.ReturnData", "amici.ReturnDataPtr"] -AmiciExpDataVector = Union["amici.ExpDataPtrVector", Sequence[AmiciExpData]] - - try: from wurlitzer import sys_pipes except ModuleNotFoundError: @@ -54,36 +46,6 @@ def _capture_cstdout(): yield -def _get_ptr( - obj: Union[AmiciModel, AmiciExpData, AmiciSolver, AmiciReturnData], -) -> Union[ - "amici_swig.Model", - "amici_swig.ExpData", - "amici_swig.Solver", - "amici_swig.ReturnData", -]: - """ - Convenience wrapper that returns the smart pointer pointee, if applicable - - :param obj: - Potential smart pointer - - :returns: - Non-smart pointer - """ - if isinstance( - obj, - ( - amici_swig.ModelPtr, - amici_swig.ExpDataPtr, - amici_swig.SolverPtr, - amici_swig.ReturnDataPtr, - ), - ): - return obj.get() - return obj - - def runAmiciSimulation( model: AmiciModel, solver: AmiciSolver, @@ -128,32 +90,6 @@ def runAmiciSimulation( return numpy.ReturnDataView(rdata) -def ExpData(*args) -> "amici_swig.ExpData": - """ - Convenience wrapper for :py:class:`amici.amici.ExpData` constructors - - :param args: arguments - - :returns: ExpData Instance - """ - if not args: - return amici_swig.ExpData() - - if isinstance(args[0], numpy.ReturnDataView): - return amici_swig.ExpData(_get_ptr(args[0]["ptr"]), *args[1:]) - - if isinstance(args[0], (amici_swig.ExpData, amici_swig.ExpDataPtr)): - # the *args[:1] should be empty, but by the time you read this, - # the constructor signature may have changed, and you are glad this - # wrapper did not break. - return amici_swig.ExpData(_get_ptr(args[0]), *args[1:]) - - if isinstance(args[0], (amici_swig.Model, amici_swig.ModelPtr)): - return amici_swig.ExpData(_get_ptr(args[0])) - - return amici_swig.ExpData(*args) - - def runAmiciSimulations( model: AmiciModel, solver: AmiciSolver, diff --git a/swig/amici.i b/swig/amici.i index 46a58f8365..48f99cd7a5 100644 --- a/swig/amici.i +++ b/swig/amici.i @@ -341,6 +341,8 @@ def __repr__(self): // Handle AMICI_DLL_DIRS environment variable %pythonbegin %{ +from __future__ import annotations + import sys import os @@ -353,7 +355,8 @@ if sys.platform == 'win32' and (dll_dirs := os.environ.get('AMICI_DLL_DIRS')): // import additional types for typehints // also import np for use in __repr__ functions %pythonbegin %{ -from typing import TYPE_CHECKING, Iterable, Sequence +from typing import TYPE_CHECKING, Iterable, Union +from collections.abc import Sequence import numpy as np if TYPE_CHECKING: import numpy @@ -361,6 +364,37 @@ if TYPE_CHECKING: %pythoncode %{ +AmiciModel = Union[Model, ModelPtr] +AmiciSolver = Union[Solver, SolverPtr] +AmiciExpData = Union[ExpData, ExpDataPtr] +AmiciReturnData = Union[ReturnData, ReturnDataPtr] +AmiciExpDataVector = Union[ExpDataPtrVector, Sequence[AmiciExpData]] + + +def _get_ptr( + obj: AmiciModel | AmiciExpData | AmiciSolver | AmiciReturnData, +) -> Model | ExpData | Solver | ReturnData: + """ + Convenience wrapper that returns the smart pointer pointee, if applicable + + :param obj: + Potential smart pointer + + :returns: + Non-smart pointer + """ + if isinstance( + obj, + ( + ModelPtr, + ExpDataPtr, + SolverPtr, + ReturnDataPtr, + ), + ): + return obj.get() + return obj + __all__ = [ x @@ -368,4 +402,5 @@ __all__ = [ if not x.startswith('_') and x not in {"np", "sys", "os", "numpy", "IntEnum", "enum", "pi", "TYPE_CHECKING", "Iterable", "Sequence"} ] + %} diff --git a/swig/edata.i b/swig/edata.i index f2f7d0da8a..0a8a01e3c9 100644 --- a/swig/edata.i +++ b/swig/edata.i @@ -8,6 +8,24 @@ using namespace amici; %ignore ConditionContext; +%feature("pythonprepend") amici::ExpData::ExpData %{ + """ + Convenience wrapper for :py:class:`amici.amici.ExpData` constructors + + :param args: arguments + + :returns: ExpData Instance + """ + if args: + from amici.numpy import ReturnDataView + + # Get the raw pointer if necessary + if isinstance(args[0], (ExpData, ExpDataPtr, Model, ModelPtr)): + args = (_get_ptr(args[0]), *args[1:]) + elif isinstance(args[0], ReturnDataView): + args = (_get_ptr(args[0]["ptr"]), *args[1:]) +%} + // ExpData.__repr__ %pythoncode %{ def _edata_repr(self: "ExpData"): From 1f18ed8975b76a553d5787960b622ff46cb2e801 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Mon, 22 Apr 2024 14:10:12 +0200 Subject: [PATCH 096/188] Drop python3.9 support (#2391) NEP 29: > On Apr 05, 2024 drop support for Python 3.9 --- .github/workflows/deploy_branch.yml | 2 +- .github/workflows/deploy_protected.yml | 2 +- .github/workflows/deploy_release.yml | 2 +- .../test_benchmark_collection_models.yml | 2 +- .github/workflows/test_doc.yml | 2 +- .github/workflows/test_install.yml | 6 +- .github/workflows/test_performance.yml | 2 +- .github/workflows/test_petab_test_suite.yml | 2 +- .github/workflows/test_pypi.yml | 2 +- .github/workflows/test_python_cplusplus.yml | 10 +- .github/workflows/test_python_ver_matrix.yml | 2 +- .../test_sbml_semantic_test_suite.yml | 2 +- .github/workflows/test_valgrind.yml | 4 +- .github/workflows/test_windows.yml | 2 +- documentation/gfx/amici_workflow.png | Bin 100907 -> 98637 bytes documentation/gfx/amici_workflow.svg | 16 +-- documentation/python_installation.rst | 2 +- python/sdist/amici/setup.template.py | 2 +- python/sdist/pyproject.toml | 5 +- python/sdist/setup.cfg | 91 ++++++++++++++++++ 20 files changed, 126 insertions(+), 32 deletions(-) create mode 100644 python/sdist/setup.cfg diff --git a/.github/workflows/deploy_branch.yml b/.github/workflows/deploy_branch.yml index ff1be3145d..24d77d8118 100644 --- a/.github/workflows/deploy_branch.yml +++ b/.github/workflows/deploy_branch.yml @@ -9,7 +9,7 @@ jobs: strategy: matrix: - python-version: [3.9] + python-version: ["3.11"] steps: - name: Set up Python ${{ matrix.python-version }} diff --git a/.github/workflows/deploy_protected.yml b/.github/workflows/deploy_protected.yml index 881a4eb77a..7f787e56c3 100644 --- a/.github/workflows/deploy_protected.yml +++ b/.github/workflows/deploy_protected.yml @@ -36,7 +36,7 @@ jobs: strategy: matrix: - python-version: [3.9] + python-version: ["3.11"] steps: - name: Set up Python ${{ matrix.python-version }} diff --git a/.github/workflows/deploy_release.yml b/.github/workflows/deploy_release.yml index 25eaf97a0e..ebb83ce2a9 100644 --- a/.github/workflows/deploy_release.yml +++ b/.github/workflows/deploy_release.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: - python-version: [3.9] + python-version: ["3.11"] environment: name: pypi diff --git a/.github/workflows/test_benchmark_collection_models.yml b/.github/workflows/test_benchmark_collection_models.yml index ab29938a12..023fe077e6 100644 --- a/.github/workflows/test_benchmark_collection_models.yml +++ b/.github/workflows/test_benchmark_collection_models.yml @@ -22,7 +22,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: [ "3.9" ] + python-version: [ "3.11" ] extract_subexpressions: ["true", "false"] env: AMICI_EXTRACT_CSE: ${{ matrix.extract_subexpressions }} diff --git a/.github/workflows/test_doc.yml b/.github/workflows/test_doc.yml index a4fe10de22..5fcf490eb7 100644 --- a/.github/workflows/test_doc.yml +++ b/.github/workflows/test_doc.yml @@ -20,7 +20,7 @@ jobs: strategy: matrix: - python-version: [3.9] + python-version: ["3.11"] steps: - name: Set up Python ${{ matrix.python-version }} diff --git a/.github/workflows/test_install.yml b/.github/workflows/test_install.yml index 0c624a33e5..9e1717d962 100644 --- a/.github/workflows/test_install.yml +++ b/.github/workflows/test_install.yml @@ -9,7 +9,7 @@ jobs: strategy: matrix: - python-version: [3.9] + python-version: ["3.11"] steps: - name: Set up Python ${{ matrix.python-version }} @@ -50,7 +50,7 @@ jobs: strategy: matrix: - python-version: [3.9] + python-version: ["3.11"] steps: - name: Set up Python ${{ matrix.python-version }} @@ -83,7 +83,7 @@ jobs: strategy: matrix: - python-version: [3.9] + python-version: ["3.11"] steps: - name: Set up Python ${{ matrix.python-version }} diff --git a/.github/workflows/test_performance.yml b/.github/workflows/test_performance.yml index c02245224c..6b66698d10 100644 --- a/.github/workflows/test_performance.yml +++ b/.github/workflows/test_performance.yml @@ -23,7 +23,7 @@ jobs: strategy: matrix: - python-version: [3.9] + python-version: ["3.11"] steps: - name: Set up Python ${{ matrix.python-version }} diff --git a/.github/workflows/test_petab_test_suite.yml b/.github/workflows/test_petab_test_suite.yml index b6465d554b..7e9a93c494 100644 --- a/.github/workflows/test_petab_test_suite.yml +++ b/.github/workflows/test_petab_test_suite.yml @@ -22,7 +22,7 @@ jobs: strategy: matrix: - python-version: [3.9] + python-version: ["3.11"] steps: - name: Set up Python ${{ matrix.python-version }} diff --git a/.github/workflows/test_pypi.yml b/.github/workflows/test_pypi.yml index fecda3edb0..1e91019c6f 100644 --- a/.github/workflows/test_pypi.yml +++ b/.github/workflows/test_pypi.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.10", "3.11", "3.12"] os: [ubuntu-22.04, macos-latest] runs-on: ${{ matrix.os }} diff --git a/.github/workflows/test_python_cplusplus.yml b/.github/workflows/test_python_cplusplus.yml index f729d47867..5714a37b21 100644 --- a/.github/workflows/test_python_cplusplus.yml +++ b/.github/workflows/test_python_cplusplus.yml @@ -15,7 +15,7 @@ jobs: strategy: matrix: - python-version: [ "3.9" ] + python-version: [ "3.11" ] steps: - name: Cache @@ -117,7 +117,7 @@ jobs: strategy: matrix: - python-version: [ "3.9" ] + python-version: [ "3.10" ] steps: - name: Set up Python ${{ matrix.python-version }} @@ -193,7 +193,7 @@ jobs: strategy: matrix: - python-version: [ "3.9" ] + python-version: [ "3.11" ] steps: - name: Set up Python ${{ matrix.python-version }} @@ -233,7 +233,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: 3.9 + python-version: "3.11" - uses: actions/checkout@v4 - run: git fetch --prune --unshallow @@ -282,7 +282,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: 3.9 + python-version: "3.11" - uses: actions/checkout@v4 - run: git fetch --prune --unshallow diff --git a/.github/workflows/test_python_ver_matrix.yml b/.github/workflows/test_python_ver_matrix.yml index 414daadccc..01d455b18a 100644 --- a/.github/workflows/test_python_ver_matrix.yml +++ b/.github/workflows/test_python_ver_matrix.yml @@ -25,7 +25,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ['3.9', '3.10', '3.11', '3.12'] + python-version: ['3.10', '3.11', '3.12'] experimental: [false] steps: diff --git a/.github/workflows/test_sbml_semantic_test_suite.yml b/.github/workflows/test_sbml_semantic_test_suite.yml index 3c0b3bd149..69c78d44b4 100644 --- a/.github/workflows/test_sbml_semantic_test_suite.yml +++ b/.github/workflows/test_sbml_semantic_test_suite.yml @@ -29,7 +29,7 @@ jobs: matrix: cases: ["1-250", "251-500", "501-750", "751-1000", "1000-1250", "1251-"] - python-version: [ "3.9" ] + python-version: [ "3.11" ] steps: - name: Set up Python ${{ matrix.python-version }} diff --git a/.github/workflows/test_valgrind.yml b/.github/workflows/test_valgrind.yml index 034945d461..b3f893647f 100644 --- a/.github/workflows/test_valgrind.yml +++ b/.github/workflows/test_valgrind.yml @@ -18,7 +18,7 @@ jobs: strategy: matrix: - python-version: [ "3.9" ] + python-version: [ "3.11" ] env: ENABLE_AMICI_DEBUGGING: "TRUE" @@ -57,7 +57,7 @@ jobs: strategy: matrix: - python-version: [ "3.9" ] + python-version: [ "3.11" ] env: ENABLE_AMICI_DEBUGGING: "TRUE" diff --git a/.github/workflows/test_windows.yml b/.github/workflows/test_windows.yml index fbd91ce73a..8b5b3b89f7 100644 --- a/.github/workflows/test_windows.yml +++ b/.github/workflows/test_windows.yml @@ -27,7 +27,7 @@ jobs: strategy: matrix: - python-version: [ "3.9" ] + python-version: [ "3.11" ] steps: - name: Set up Python ${{ matrix.python-version }} diff --git a/documentation/gfx/amici_workflow.png b/documentation/gfx/amici_workflow.png index 75f904ff7d328e47485e8ab5ece7bfbb673a9629..9eab38058049faf34e5719f2a65092837c37397a 100644 GIT binary patch literal 98637 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Ħ?{lC1Qx3}#67Z`n1e$i^R{)<#l)UA9dUo>uZZkIIJz=d>k*(HXQ z^#

2OmAU(Rv-f?7O!^?#GX2Ir_!cFA?Eu#cM>t>H48pL$Da#$#qUBphz7L#=H0( zSZ!-r@ew^+_7A`KDMP<;(*?gy6kOYeV-&467?^i2+B$~c)s5MXp2kL1j_*D)ik`n% zi0LS**-zwbkOeEV<9!}rBu7`R`)n5yln%S(4l#Ql)L{lvnF{!V5& z1shi}p(uHczna&Hk5GAYx2+3K9AlF9#)qs+MOPT3Eh6sWup!?ny$1Eaz~*NqJ>cI4 zDY`hvy}NYF(auw3(PKtELv^`Thu4AOWNSiXv)gI&#YV01zeSKDNfbc+L7avd<$Y`8 zQ0Hk}G-{k#xZ;C+V1&omOyMHthP*c~HPzojW>ldM}` zlh9ttA<_1#!sS~2|181)BpoDV{GU)7~{gTJNLQ3RcZ_a+?h3xi=+9w~5E85aU z{@bSbv6{?vzov9hi_1)5P-mB+2*nM|O3LD$)|#nx~dO4*5=M zB*G{|8uVeEWf~fhE@=A5)5o!QvzcRK7+^NiTehmaCGdGJN~Gv@^ZA;Xg~W&64o*_M zVy%DA7NQJ;E$G==M%0D{cdj7z499##lB(C|VzLsuVaj=p=!HE&F`Q41%rQ;As1Ey? zx#uKEL-E|=;$uV7g*%Ae{ohYfW}YE_%Fvj2+ylOn4K!Yh7;!OeIAA3#7_jRl`6~aK zuC`r3efs=))e>pK_;kR}%Ab0=$;?nNX6`m+1_G_UmKW{2B z#}TmKL6LuU6fzaSOEG6k(qz};E2WdHl*8~PUsjnnLUNmm{y`Ao; z#BS<9^(g&4Tl6+Gt*>_?RAdvfSbMq>DM@7ubI@H77xLLH&~}9&A}f!(ymWML8YEIxbzzVHJ<5(&+az(Sh@|oWHHwaAp>HGCF~8!}JEMlAV@Od}Z#2;ce4K1e zxUH2J0m$`tc-j8%5CM1}U!jmWGzasZw*ciK4|x$ah`YWRt;PNOQ0aM6|0?R(_UEJS zeiG)50T0_6+pw=Ue44<}Etv9|#q(xG{+yZyuIC1GJ4LA@A#frpK;!iYs0|q25D3VC zcQ-P|aqXWG{8_?$`*Qt(CJnK?@AXu@rd;gW2AmHV--kD6{=UBU=t|Db9TG*>FIugw z7UAEYZC>;@eDkkiYY#Fbzno!K)NXZO6BN&AAf--o*%#_uZAhIn0rO8vA$h& znKEV`U>%R4967Ma0a}v3`U2_qa459X5rz4NgE3c2^c}x|-yvWDej(`v>TBoMINmE4(SlT6n z21PJteG%wYG>~g{{MZeK9CpHw`+l%_II_=$)D>=ZHsI-QF<+PwWM6u6n(UW99e=Aa z7dv+K9y$@I5Lo5fYeM3L?uD>hzwlCfoQTfp9I0Se7Vh1W>_?J$NQ zlTj*v!#Z0V8$Ok-IBu-ci!*a4-+B_)F{kgKmQDZ0T$sL-q&wmhd0FjxP;)w7lY4Wi zRXRdz81YA#fwiUZcA*D^Q9gw1pS$yJmmSXt8bK?`$;m^bT~1{3mFPp3D@QJ$cTc*! zl~S8OKlk`sWkTDQ>7;V`GRqDX)x+u=7yjJ)xuUJ5p`oEST)L#;q@gja=2e6(SddH= zXmZ0II}=AdZ^zdc-51r?G5)G+JuF^^mhv*j0}1}(#j5V8s(Fv@TCVuHn)RF?a~R&^ z*kYE3Bqa5zZ`;npie1Z!KDRn0Na(T-PmvlevbRWnDHXFJD|O!d{Rx~_WI|16lnX^z z>jOZ%a47YoZ{E4{Vr0!xU01h&mX_8vFfgzThFQ&n1bLDdU>GY=`~50zzE z+K;ID_zpUU_OAKAnKr(@;`sIM(_k9Sn88clKi=NNQM5&}k_Gg*cX_PhKE9>;?`Lgd zDBEA98ukm9~k{E(Mt0lYP@V7r%}nVEt)Xftm9@514iwAD5>;w%C& z4oc(K+2)tV?84cJFC>TlT2rL+Rf=uDK~7A(2Svp>`83f*2DQA<&PNgxaMP=dIYIN=>vbxszSTp)B#lf2e#U6Zw83u*J3O zFdY@*lWkrUW#WRFg;u(+6PfAx!fY-#PvE}N1GT|T-*CQ`xPJEF$5`C*{kn=cmm}dH zqBLX5Crb-1;&&a2>kVUZui!Un-u&3WZ}2j~G(0b_HNRIQGV{z6za>sDW<~O0Egr*^NWV^UdEx>o#C_27YiU*u4-$qqSn_u+7Mys5zK&h1mLrJGaEO zvj=CjerVwD?supa$MSBU-AoGiXKtUR&w5SfK`lcz-G7iom~q67*EI5je~PVrYd(@1 zJ9O4vLK@Sk;{|C;q!_;5u!fRsw70EQ8S^S25a|=L@5mW3Q&L~^{x!RaHalpkdqYUo zUMe)Ye?-@HC^B`bPFQoCn~MW(Ml1`Towpn8ghxc8$q;9ogH{T(ZYS&Qf^j757( zpH*w3MS+8&2wS`&%OD)%pOJ( z3N=soYND0GDx)%89!%v+U72mDb+aG6CNZ_j5?o@CVELm|C{QH$CBWUUYV zqI%uhB;7n=sxqzDM@3-3sbe_CM61x;rt6A>-no9v{5E|SgC}tCu0B*IL-Q?WXV8-z z{%yV4#2(tGq~<_PNgd5$nvi|Sk%fjI@7O;)U_-&`3&Np%8hmO++R`=K3-|CX#^g>` zI{i<3nMkXoZ7RLtZir+MeU{Twx4ZvFV?@*QZF%2$RA6ei7+`NN^MbZ zVs|{?=38v8F5)#qv3sn`31rzj>j0cimsUo_2UgU5hAg@~<&4#%pQdCmU(pSAE$$?gn&(_K}*S-!LoHd#{SNwtU`$s&!eo^Vx&fb zcui)iumNe__q}{gmb49~E&B-VNanw3=lv+YX!ZOBkX>b|D2?Yh(5w;EZ8O8+CN6wg zXe{rn)gxPT_LiUcO$XIgfFF!dSs6YEwbP%_Mc&h8%Jf2WLJ6W94=)dVR3SFemV*y} z1urQ3JwcR7l~g?+5q;KTc}59rX_maa&`Z;_z6Ba;yGBVUFwbCT8af@Y>?Td) zbS|ZCKSK?_>U!=l#E<=-NxwiBio;KWoHTO2DCzY%J8jgCqDanvr_bx@3t*Lz-)sLS zK}`s5`zCF(@h{p5ZYTZkr{7$c$`nG(`81OH3KAv!zrW`pyoUSF$Ojg&gKeDg4G-=T zDgM8Y{Z9Db$A-Qk%(y1u@3>{&jkTdkm#X^j(0eUr^9JXEIP!ZIaSZp#L7)Gf7=%gH z|NDEX2L9qpN-*?pC?H4p!c!Bvl*j-3(G4cThqthk6@rUaH?z$xBB>{+WK@_J$1gm^ zLbZ&D_oJ`o=eZJ~{Ot4%Nm~@PV%+2+i67!{2vc;s%9>+ll?kHavJbfy$KheKgE@}2 z)KY(K;KQMAp0E&ly%#XG5naDHKBp<9X@NPk&q4F#A&kSn!~ip?Rkt2?$2Z*dpT&+bnWqTzH%|~`ZqFM=7gn_p%0&M0#FvTWG|>vhs%LK3 z?k2UYgvU9A?o4IK5~k1EM2w|Q_04lW3;ZUt40!1myIg$p@`OG1jp!)9-?Djp1+R&Z z6<&tZtFB~>Sw1VRp@s^$zAQ&A?ERi+2#K;l1R&wWP>@MXge9`~~bz&rYiM)5i5bt@CoOgtyD z3v1oZM~-u82K)%>+_3eOvqt`6f-iR;!YLDL;*v%!LrJIVLr6r`{_5`3`q@iabDAuB zK2$Dth^yW(uc~Z6L)O{xCXAH)!VOC>!jSZsMuv>O{Z$Z|zu}1_j=5S>X~8o%jo}e^ z6aIc0TqWN>ZqE$cBP)acg#}=lAK}Ps!Q=2zQv+KqYfhGhW_U_24vz&J)x^Fh9r;9& zL6}Ynj&h7h>f~^E!!*GqwSgHsbiTe7nKNz%31%pI4dQ0eP@?uyOzv?wxd+_IleX`v zsfEdbHGU!?{i?ov#6+uW&qHzFrYlUYX?D{pPPbgU&6M2FR^n*8KO}sOmRiUP!A{$H&jl#T54&8J^asjO5P)vE_LYNC*DnKjz$wlplFDfTus$u@7PqJ+tj5#v{&;#y`|4c7f{9(g{YgaQ3dz{MJ@b)UXE~4Ih)baDs+IT7IiNnW2+F@_qhA4ZAL%F}Lud z)y$6{*&PO!I0#TU!6`GSiddHoJHOXt$qk)2kq_uwXh#Swt=+3r4W7@m*9iP6>Y3?N zkfbNN*kTbE`Pch_;BF~1y3@a8G4<}=M4nnML=tZFS3K|fmXZ)ZFT6)F~tPCU{Ty}LO*c!!(b-i{vE zPo73eRt)Dg?KPosQPQ>sB6ae;r@eKqOa6mjmS|TdF4BMA_;nT$$Hh*TIqH(CRd;)J zKi0?lY47?YvFy%$v-mrQ+|ElrFVct_LT10tHoo3Oc`vH}Y52L8<-D9tDIHjL*I}IP zt7F63<~?R+JeY9-Lk(@dPK-oGeRk@Z7FTPv>%QMIJYdm z_-afoUEBLwL@|{c+B)kZF}(YiFTMMwa)4w5=_Mn)U=HR9ji9%@Ptr6mStZ~lGT!IW zd}R71(B>;1zcLM>kOuot2#7{5|8mn%!K_ zZ>`BQul{tjMZ!HoHQ2f`7CkyN*2vc?XRzKJm_)@q_2k{!q1w~B-9&;X({BxaDk(3T zYm$5+^}qZuro{79;JwBZZ(9Pj{-+5G@hP*EeE5e;AHwz={GkvCBK2(c>;H@86ov7c zme%bB7VC$TyThbiaGw57Js(fib>D0sDfJuSEt2|pQSu^2gk$~3OB^g)<2u5Pdy}7v zkJ^hU{vT0q0an$vy$^3Xq@-d?l{4^X;Z{@H&+XC&;~y)?5|fgp;YM^p350*UMf1iC*l{L)iD8D8>K9 zXJ*^z(z%xLM}qJQzkPW~wnhQ|=NG%&TkH06DkL`HBq-5`c;nj(1vR)&QnI}q(TIND zViKY@7tC52y@5WZ&7x_m$%c(lq`KSveR14q$u|5p3g-!)Qh0^A-rXC?i^Av=hD7v* zJr|@LE319s>lH$(ZKlax3Wc z%z>DqydCu%@4t66hPXSqi+X+Cy|L$X3{S2-%%=8|2F$4DBl(%Oe?^<&i$0Uljc=?gPFyc>A3Lbl++0nKjdavJtGO1-%%g9YHEvxbM%?KD`cXg zwd)n(zdj>jjI+KV9UM)~d{(L3T@nsE*cNTY%Z!Y67B*h==H_4ps^2MH6` z6{ld&j3<4VwM}EUs`d0@|B=fIs_pA3+B=1YDqk$IigUh^c^WY{bd&r?UFiurNtX@g zJ5_cwZkGRX0cLTDWgoeQJ32G*iDP~oBI>&LA}O(eNrT&5?XGbGE*2{JHZK~bh3CJ2 zO#!d``YhYJeHUR5vUoIOeYp~Q6MMoavb`1e{#_?-S*}H(fR+Kn{!^nx-z&iOA1f#- zrgHp~{9jQ|)>BzhytcO1jgcLS4yG^)%}SM3I?pCBD?Rvw*xT*}j$60A#b=_&-6E2= z$62#Q>%L2qLyVR@HDV!6;Z*;q^G2Vo>0F@2Y{qNGT_+7?^fyX!@8s;1FvE4&m4cb8 z6w&ehxD@2|Nmx39NS^6s7T&}~*K* zir%Ppw;-B@St(R4ABjvO@hJ_5{;(7(Zx=Kh|6YB29mhl95|Trm}>TZ z3!?CZa5_3Or z`h?pAq6Ues+HpeA8qHEPHxp{~?Jduex7x%iZukjgUobN%2A%p=(`mYiSs#Y)PA<~a`BV+?&Wrkmb`>e|Ft(ig|1~8 ztaW@`6A&_0|D1~h&?{fvY|u1l*lTA%_Zfyn!XnAXwTBSqssyRq7nEZ|%RD!3yn$Te z1<|2-AW+*#JIw|VqBh(mt`HI3*&@P%NTFVc4PEdAPP7Hk`%>kd5s(xFsa=la70lHT z0%Sa;TGqJLu!SfSFlS2@E8vn2!ipi=2zIg5*3ig==ka=7aS!uoH z(4Z>sBdaVls%Us8#lS#qxJRrH-Qv5MNwiIonNUx%&wBwtoD-kuFfDhT} z;{pL{65G{r)1|JUjM&-9m%^x{mh`Gq1)*0Ov(r`cKict>A}Uyy&D{9>mdv)T9!j;9SVRaeDA>Y@^q}T^?cJY(gd{a z$MZvbo|0^U{66hZRqvn%MbG=9b+1T;bh$UFN}g32;q2p)M>K+_d2gQv88^yy<8$p&R4ftGpF_Vk0Vo`3HaY`Gm|;G z263a^z1>EM8E`^L@wzi$mPAIg>BYaBXm3PMe5p)TlCswuidhfN$d0Hb+TU-SfMQyR$m&Terl$Td-G3FMS0h&PKvVYWPaUZEz^b(O)gZ}xxp_YWlyiiMm-nk zZlkvA587#V*mmLe!iVfIN{cAOokwjV5DAK9NAkh>+9ZY~FVFTbs|AG3Yz~9htA`_UNLh|(j-Ctmg^8>^) zQedO%HVA`=_nLZo9B9)=E0C9_s;Z~~-FGmJ@C6;!e>kmw;9BEQBt6-Q?40#gyw1zPtaI>wQGyoFC09^kXP^j~)yyAEtjuX&&dA{w8 zI58j+Ja-5nbTFy59(wP8zHT~oalY-Zm_vvF@`s?AV{1MbHSz|Pf(xh|s+SA%->W#- z1xbSJF9M1Mu`?TDH#fH*z(ejVYgjAlZ98B3mYVc7Ep6i~guQcda(+44&$#qiL4K~R zq$C&Kbf6s`0i7AI_0o__2-Wu6Wvl{m%PhQ_Jr6JM@5J=<&9gq!3zy%Y7{pV-8BPL- z@BUhZA)wc;=WpDo?6uhD`rpd76OfqU}}!6RrGaqiwa)spCf^9bpje53EaRQej;!qbW`zn1@+O5 zk|ESi3ieo9YQV7*;rfxzZ9xX!Nb?7p^gkZI9AxGDLZvgiPCm!zA>)C2sXsxVHK-|4 z2_9U)vepAiyDu=nDd281gQM@a-rnA8$KW8MfZhfN$QmNcMba4Wp|=4SIVcjwg}`jr zITp73{@$03i|cE6bhI$6t93dsEI0rK?7FbpJPg50x%l{W&;QxJ>wXWsbRQM22Wn~~ zj!nDW@nFty52FoqB01&FhZ9=)|r`^%#WBpDyO7 zK?gLoy1MFR*YkJd*Dy?vb->MQTM^X;Dh;}6AU`*NGU&QJAtLf!k~;eRJnW&)B0vyY z;2>@O?mA?R+Y3w0=A}Atzlw~!1-~C~po`%E`Dr+L^l;9j^`G%-2Dn!JX>)^9Rz>lB z4%ky}*_GAbqy7(!3^zL;E!6y(^g2PvD_Apl4IZs{l?0-6ZSSSp+FxqC8dKH4&D3bi zLyyNS&zcPq(YlK*B$4?G#qG^X&gMARmfCn&)Ic=w;`7HN+giiUg$Wz|7~vWp65AwvQ9EWL6>)gu8~HE zT%-LKw-hU zBpMyDZT-)^xXHqjoyGJRA}Tw((zSXau$1=tB1G6>jnap|dL;-)=C!mGASFCdR({nh zrU-w=$HmRz(HVjH%@n+-y}KGG1n>_3`0XOeXo#(4xh>rl}ts;6GM}`ln zFifCx?rawo?Rjt2bJW9qI7i0TD{yEA$e0e!{Z-HUj-je@5#PdG_v@VU6j*Lw-S zjEoEtlJQM-8Hp{TaS>nOhTN0O@ps|G=b1V-vp4T-xGa$(=%(hA=3s7<#0}TjNhR39 zuGareh1H&Vqx6GhTF`H&PC^?9$Lz;_lmt`rCPZr-YW^BFu3u=gpxft{hCt6vSPuk- zA0I0$fOyM@(ld&;PmS;O>(S-!2L}ggElI%Bw+5;|WU|7ang#~Ha^+-Syr_ri^G{2W zFo*`NYG`Os?rH;G2?Qz*95_kqT$l0)Fwk z>;b%J5d>c@B<2rqv@9n-Jh+A*f$u4avOr;FI^CvfGd6Z{HV)_Q!%!WTJdD{45(&Ow zEciKhnEaY8sDz1zQo?H+Z;oN35JQFF&y5&s=MtkT9KJ@vcJsU8xK*8HT$zb&0yOvp z7;it%4ym0b8(ryc5P6pAmBCuwkc!F7>(47znxi1Ty7&M<`bYz$fOjc>2H@*hJ_W8Sx;VSo`3vB$b$wvTCzg2dlJM zuiRt+?AN4x!L3Lbyi(rwE!rkOSzcP=hFtVDsHF@fz5ZHrpuoU}${aPjt&vs4ZC5(n zSvUa4{VgOu8GvA#wC6yCn^YZUQ_oL82l?MZ`NiUq{v*SWpSaDZrr&ucIOCyk3)=z~ zW?8jy6c~uAhHvL6{ST)w?lCau1oBUAs-sCje?RkUHkq-&V9eR7@2FUuqH^~jIx|jA zmnvK=>;RPf?mN7QKWeXb6uFer9-V96uHTN>Y#K@1xOG*jk@3Bhs$p|HwEYZDzKtNxfjt; zS}gWT(KT~2g=B6HS18o!!+Iq}^A!s`($GSvQW^SUBRsA7H)pE$>*hjfxe*o%L@Zp~ z-v8Lc@Vw{q=T$INH&jpv*;`0>u!NmQfUxs6U{!u^0J&16-#4;jSX}#H9&z8e+B$9@ zl%F*b5%?z{ewrdg#Y!EH&w)daWi+*5_BNcLc8J+USe}i~#o0-Sz0EYVitGq@PAm9W z9`S}Ag_QT{I0zAthAPNnY5f5--+#VxWT?Ja{g8AhJoEPb`!djWO^N{n<}WOxhhVob zsB3Jjw&^ax!^NeE$^;vYE#x85efaPT9KwdvucnjAfc=1lb6qug&1IyTCj-8N^oblnNGGnu=Ek2ynGnM@5k5P z^26(41A*!m)l?UobxG3T@61^>f{0-XSzKfzlk@oPthfr~Qo6;(MVDvKN}S>G*UQh% z6?)+!>9U}d&!(IVG0_IIyI@1~=K_?rwf(zARq|C+wEq3F$_N45BU;o> z7uf?k265K{?J>7M(lsD_C7j^~>AK4uFjuK>UGfz-$j!~ACYIchX=jap z!!|a4n2-4!$ohWvLu#)Uksgh``xim#q?Q@ugyVf?tDlJ*;X!jW%=G$I|D80+?C@}L z*e{;E--6ZORa|x|flYYInw^ar{(FyOe6#Ivvu)B*j#Va?Ah=JafFO7>z+p({J3;0H z$F@;=8G@!8^u1^c=x_c7oJnRj^{=bxQ<^!;EFT~DO6lmit}1xkgwjt0z3u~RHEgJc?1EZdJ1=wrJt>alVO(m4+i1&54h z7Lmuxx6$;>neJ8iXkRr;KHg8(@26SY6!IMN_iHC^EZ*5|x=@P!C}QpSwYdE^mW1HV zTLiFiD2y=hwtABY)jN}(P8?w!_e4nk_SWR~lNg;m*(ToJ?5>sI-Wh7T_!8l0nj}SJ zcFEsJ6wM%2xe)AVMz!CDLF@{7JI{9j-)PP+?qONc=Yz+rU)XLmSg`w=AKp`gr7M)Y z;}Qr>?{Mwms8R2-5qE?ASC8(rFB$KDS{r@)+a4Dd&#E)F%Xs5|Es7Gk0+-Z{eXq$* z(6zo%)-yi1c6xNl%EHogK)SXTFk#5VOK^N>gld_zg{x4}3KT`IiS@ zrQo4n>n#zTkBKPwQk6-?>(18U&{I8`cQMTpkQjwisu1(qkl$jC>;BFh?(iX=Do7dzk%Ht6+n*VPam8U z$>|>ZX)cw1v4Yy^yr3(m=J|}L*nBh>Iic6I;HOLMvSPJ$zPS{b=HS3J9E~a};>Oy^^Ml=qe*b(rO^@p0ucnEo9cMKJrB3T+_kcBFb{JiqMSy=$0h?RK2hI|pc(GALz-D2>;gGO7Vijt^hT zJJAoY$YrtO@%G|+&LmiME6z_l#+WI7z;TFub@}t!>!)}RS(r%CcHa&>6Cj6fcZ8pB zWwI}G{kmiWmV;q{&D}Wy;@H^EaRNoM`RpuhjCr$f*AJ-vMt8v?B%$J-|8o(i){TR@ zs6!78xQ3r5S1Y>Z>EV$&W@kv}pPUvIi7hWo`*juTLt-qWbW0h)rlR$aT!$1sAl9@ScB%&llC)9p7Z!hts;%{>f3rUCcucr3pD!)7{_zvR(uZCf>;nYeT z$o`Kev@WQ2>!@0U{JMG?wE}gcKkVjuJIBXDe^=&i)Zu#OklNY;fKcYGO|PO7(Cp3J zPMe;2{0-B;_vJ#gIBJ&$|AG@MCiLbRseP12=Fg-YHM;f2HaZ=2=clJUAkG~&YYii7)9C5>j@eVs)ygZ3bf&j~1g}Qsr zb)#MH(20wtCW#%QoB|I3|nQ|zP(=gr? zCj0H+=-QDi*hcG!nyk6Y+9Df8j{7!%iN?uVCKn?-7F&xnFwsHAnm51+h2J#AP=r>6 zDXN8;NQt5Irezj~%#2Pz%*@GUOvju1a^#ao!SjN`!W2c^l;Nv}Y0rHw7X5E|A8b-h z|8P%06Z`D2Q7X`e_BTdXP(VFIUg@P+rV$-2{?xePM(F+8*CcH7zaL8cU^R-VS+Z|h z6PRFb^zF-wSWPl+xLhERVOkLdx3ed$;KJa2PhOrHoGqs3Outie%xo7%U$|T8#NW3v zeyn?+bWZL^(EWVEaLH$5xiMcqlm54PstzwIQdIB54RN0FwMrHBJCpr6@-5ihvr&XP z)WgzL8{?tQ3#!(X%yk*nD37T!W2PMn3xbk#lGl5%iTy-7$`?!C31t*O(J^UR?B4Tl z#=7(`BSphq*256xA;3}_?p|x~+n}FBW5!%2VvPSc-$2N9Rd9DsQ1GMH?4vXkHC_H= zr$-i7lm|ph0%#=I+v=x;(k4p_0|zB_vf>U?aL)=D#Y5x40!Z6HSJ3Zw@>z_|orA*S z;)EK9DO*ST#?HBl(}Q&nS$jM*Gw`vx3obhE!^3gc!SSZ1r>iS@^9Bp2bvoF3)>#gu z8Ps?ktZ7^}q=y-2^|^3s^{z$f&U}BX@f6QDUUu*nGxb$w?v9O{PgG`q+(^`27xm0# zm?Rp>?jEtVR3M|Ce=;A~P`8P7plMNwY-WT!Z^8@8|GPS!;s+lz!d0xVzx_)nsco|b z<>sbF7~HF}{pZ1Rr}`$#pr>e5k5IhE_-B8~$kBr}9bIKfu!7F6;=40(lZQv6H+)8W z{az>5`=i2i1=B?_OoEqUy@0CloM{MHylLI3(hqubt)+c|4& z&oDC=aUj@hSpPfRx+3`PP>oGp@rl7bU0qB2$F&ah^Iy^|ON?sytN|7CHJY5Rbu?X$ zmAFt%A7 zE&YBzhn_o+=vC(|q{z5TehZ~u9@o?e<9AW{Se|=P*Zqri)WogtqjHkcgFhz2eKDkeZA zmP^)mvwKn0nx@*?RYR8zP>`O(zs9F9S;(L`P*k^KQ|TC9()y_YH zd=mJTKU?365lVC zX`U0*_`xJMcc(l|WgWH%JS9!$GaDvoqi184c4+Y&M6PKuQ)6HgW49cm54vgmQS;F7 zStW|+pSrt8@#HV<70hWlvHhBm;E>SlD5mL-C?PXuQki5C787*Tz;}GPcA;-lsOzo= zqL^0Kk@_3FJIM?{f&rnJlfrp>7yNb|R$B}>T0H}k zL{cfUHq;mxlc#g6Pdvc!>n)8IKQu83vL~-qS5^2PJWF^Yd#iK3{GTty0>={{O_S9ywfPYl+dnW6l?jkC8G&>`!16uP(9J%Q)g&3F2EvI{!9D>bXkgIVLOnBZ`1CE>LZRds9m7D?MLElH+DMu`o=mO1E z-oM%@1Y1TX2PJ!ZF7*`^8j$g8yNyf{olzuYWEKeayowk)v7scPWy5v zJwOeuV5S;|!14u3Az%k`gmfjTBth$Cjb%PC==+5PKRScu(?Ks!IUDHaoQ~L&9%*5q zpgSo%ncDVC5`^bE0%p)7NG2r~_^VpmBHs>%oop>&tM9E4*mKByAG20n<*urA&$o5^ zqTm_h*P$G5H{^7hkL+=46HXmH=w9DmrgwH7fAv4usPyr>r9I>9 zs*DHBnx?@)t-}}x3Y^EPSQ)KOFRW(D2ST5lOpjQQq@^q@EQo@OipiWmJsn*?RBHO4 zz*6TmxVGAT`0!y3;wq*pq*z(ywr5)Xr(phMl%|0|2yeQMu1u7hN_*Ja7I}lm1~VWx zE-;=*_PYg1p`RZ%KXsaGoA{ zb)M`WD(Pyi3FA@kfBXAl{(Ip1StJ|Uat5)_$p;Z}G!waYQ;Dosg);Bn;pZ9zqr(vG zP7d0gd71cY(OCWy7;-WYwG=>nQ?{yX!n7*?zQbdV%=J?*)XPRMx}aNE2E=&#Simn% zclH$AZi^cl3`gCJ(F4GT@cN`2jLv~_K-oG5l+Fa-mq9`LzP?f;LeXKMZ~Jv!nG_fe z#RUbg&&$DlMv7L%fdCABC7v6_x(a9#QOy6VF}4g8j3LA+`p903&8{;oUNV%$jh!nh z(4^>DY|JEyE$3hpy&TQxl4zL3@MG@ff7Ao1&p-@%2>YP~4F5b9o~*R)bX*aqSV^yF$bz`MdFD-(s|ULAfJu+X@^A`O?(b0#2E=`Z?0#cWrGsg&w$Bhil*# zyd0d>sV2#i$<0%sVfQ!h4(M1UMf=FM8Wp1jnhP_%*tYd*$pi%^BMuXd;jBy)+Y!+ zY7hVN{kxv6t?dFt<9VYTn?qryE)RI3MLm!qS{v+t*9jKPm!I}qtj+3wsXlNZ3WJG5 zABXpDcahJtJNnU`ZybR3Gr!z)t!cm0{OsiA)VTJ~>MeGhiW9RpFD~YJ`U}7N6pl3f z)!y1phSM^Qu*A6W_5$d?x7;iL);{}1S`8r9Y#4(Vr#%dMI1(QlX=$-s&Rm|)IDfi* z=gqHbn}l=VsBA#^%@ljWi?Ggi1Uz*aRulQ~?;AtlY~m}8^`_OjII>$; zg9Dt16e|dO!`nHNaMxVv`-PfAgUp3Sm91m)&~g+pu27+e4pFDiKJbv4XTg%^sHV(n zyAhz_xler-8gwbzT1etqQtr!!CWBXowzHtz{K6RbgV2yr<7lR&zCO+TDB*-pGuu^$ ztB*YraV|L0ip}x2RG^1DF-WX&MHcSI2_=Ayp>>Q|Ll3T+d~FJ)SthqtZI`(VWZpb zQcJn{!E0x>y-fOIH&(f*s1jUhs|_{D8K|i8XWS?43okE@TK9STIuOn@z_(l1B-~4? z;54cSyl(h%Crq0Bx7(PpBG`>m`oOhwGleHey>V*5P+U)MAJ`|Mx%v6)ur+O0cEm;S znmZj;=+-jAjor@J3Nvy%r_>!w^>qJy_qP~3A=JmOO}8FD;QcCSVo-nor^8iO?gmf_ z0v4$TK(gVm(5`89X5X%-S*#-(9CAiJCcTGt@WP`nrT1gwr3dl-L3#=(uY1pG}q9a)GUnLm6NE3(r?STHh+ zCI}L+mfz3V=CU)F@*o@Up%W~-{vn-Iz1clXwdPi z6u{GYqkKDcwBlQ9`L|a81PkHq?82`Q^Q&d>X}Dd?vGyvns2|W2|F)5bDl)vtJr-#_ zQTqy`RPV0tZX$^FwIV~t(F{9b$H;zz6Glj@D*}lTcfjGAb`Rmq!C1(<@z%|ouB|3c zP68OnBchmhJl5*Ey_p05qr^xp%($c>mS*bJ-_ zfwYcH3SNOWyc`Pte=4Y1#>->HmT#68-zE1|pXvS1KdgYOKMSJ+%Z%p(V!p^EZhT^5 zLjAIAJDnQ6^o=W4x2Vbal18ef>}*S%j!6{5D_)iR$U?k|I0_JU( z08=060~BrW#!?%~%(K-8{%bYfM-H1clMeS&gza=s_ZIu)q!@@(U`=!gk3AYb2ku40 zErOi6?I#8XDQ90;W7+!O3GI@lsAuzK@Y!Bavv;Z*@~c*O;;kW0K$pKrL9#R)}OVd9nCr1;Tg^p?EQWIY6QJC@wA` zVVLA;9b|~j!urjh7aec*u60MYs~A9mmXWu&fMhYXM;nNR-WI&vMh&qlNs6c2p}yy3^=8T3+7aE{lysjD=*n-L(LMPrS9g1gsORK}b*YkOHY$(= z{nE-bp6%H8sxpgNf8%>+qJe91zB>99+?UCxq-bMLa<)nt-LXgNB-OHCKQ!q0QX;y+ zQ+D^Rf(1(RVR)z?SKODcHx%8PV@T9$@G~+#p49QFGFbEP+MTQt3L)|IC`q8QM>R! zUq728_UH7R4a89)q7%(DacakE{`Yrv9`OY}0AdXtxI|t-VkO)ypFCLsDx1|Fg!r+5 zJ7rxnXi%<+xjZ?f_xP=Oj0_1@&Pn{gCVAl!o7R%AR8rS0$5?Xmv>;MJ+}px)MMyKJ}bE&v|jG~ z+}yT%zTdNwl>T%Hky4PV7_|7DOqVhAK_B>R`MtM8E7*iOK>E46$AKrNaacCzhxEgr zYbpU37i~3>TfeKG7GiM`zdaxq?X%X>xN0pGjW%ub*i)7_KF=7p)ats~un#jj+6ne8 zOO)Lhx?%>`Zy2ow4*NubBR%(4+eKX;x3)EpFr~g~r_$p73fWDH5jF zsMY%Vr=TuB7d*Nyl7tzatMUG5U3lSbz10$>%dPK{%P&YKIl}iYFK~1wJw|W1qGjGL zu*4#HvT~b?J(xWFK$JOn=NZ3fnvUV>zDAyGpM-8jrr_#(LM$>A&ugb595U4UizilA z|4U~2h2B@Zs&37+ zno0m~+ZWp75AZDe9Uwd$coz56;MXZX`8)+X!X*S#-1pm0oq0Ga5}M6f2hHDo$bdHX zf>@WyT;&9!Xt1*_DJb|d`jF%1%XX$zh(Fj$GV!vR0psu{VBQ|h!DD^;me1I5c-01q z%4sm;J0T?_iv}89QWTt0Y;<71hU{O0)C85VTqOM40?#!3>i?T)&>1VdDNz78t_AL1|8@($k zYDVZg5#Ndqk4;pt2LhY^pe~lH7~|3!(e1q&=2oa*u7zBN^%AbntxnVjemB^p?V-jX zo?5^amUEui*z(2UnNJC!md9D!g!J|6i|4e z{OblJkx1>mWW9i-RBmBcgFA_B`q%#*Y6a@#Eez;ppijn5SL9v-abX(FIfU0U^?}l~z|j zg!}0=3B;-!Igmkd0i?G|rb>#<)oj=vM*5=)65$61qqHbUUi!-Z zp$nP1Q?#^JR%*eXY5FLD0`me$DF1QwWI#TGKS020`KK?E;GGu1d`b1$>gMQ?h(1(Z zKqAPK0;0A(QWgyrYI9tIsr->K(ENHNse$tOBaE2Q#Air>>HrccoPI$ir0I@q)3<9Z zHNYbKePZGhbQ$=4K)jMn613Vd@a*;^o+8C>hgziv55^Eyc0Zg66BvOgnYsF;|7N5XJO?*Wqmd zI>xxKcMZ|wygKbS&xGK63Z1Cyl{EbYm`}}ON7B~Ivx@L<4r~)!sgM$#d*jIq;68Gv z*;&mG7`qbM&>sA515+q1y^ZB2IZLgswqn-%sXqsp6#CY-@^yV|IGM_4l=H-xW1*w9So9EC zF_D-#c)aY{ZtWc+B3g@F7JF!!_?G$yr=QS`Eg9Jv$M z>*FY`*iD|W)xD0}S%2qzeD0@y>96D)bGt{{wW14n%_EQmmG<`29XKG@=$ea)47UT# z-Q0u`itYTLKenTcFkE}_`0?X!?XMF_qXdtA?tFk6J3sGz^;4Ue`vTO@-fT}f(Jr3i zGAEl;H^G591$Dr@Cptb#060TUa9T3xLrHvQBn+&TqO2J51)EPq$vbHLBr-lqN4>55^k7+p%XrKmbMPXW=XUXgAuh!Q%I~P1 z+o#o%cHmbf`B!x;zJl317-jMt5qhP;j$OFcc*+IoihNha@+EzK+0tJtYQrN6d0wq{ zKfngRC(jtC@%C$)yV%@4VNLq8)anySVH7$0QRKpo z53*P?FQwVuC6a>2vzn}d!BpXhL+zxhs_IXqIz53o3e^vv$C)tC_Uonw{elaV&m>4* zk|B5zqol1L&LVLTdz}JpB&-^`BzMSiT78>WwEg26JmiLFN1-GvXDKE7k$h(`%pnjCY#;Saaa0+`m7Pn3x#ay786fu4dxK z^^x;p~p-Zm*D8 zfEyLI&W5Y>l_@xYBsI+>84?wuR+PK`R0G8=eP+M*^Lt1W9**Y&j|M#~U8IDMnlQ0VQ z$rhJIm80G_JX1(Wgc8@)mKM-N%wMDyz+v6;;LZt~4bW&N0OFWJ@^Z8m0xyyMZ_XLb z3@r9zAU~*~^T2l7uf%zXkV8QccWW@-p=WkP!n*QG*fq+=^<=$UKqsKVO1w@WApEt{`HW=Ya4& z4dpDFpy{)LAKj!RLX9p1Gu77IJgXTZ(^_&iiN(&H2c|gEvUb`I39H!0jS1qJeYTog znC@D<#J?J{$FfeTFKzb{Z$xK=bGHj4k`gV9*hrf`dqTeGsv8-(ZfZ2i052{MdIt^8 zPEgb<OW>Xpah#FkC zz>2p{bYD+JGS>_CPMO&Tv(&O1+rf&dTEMi>(!?OX&1ZPb% z*>tIwhgc<%o4CK~pIiCEbmE(bDU?_ynwoPs0SI997~s=MXs6Db!N0dNVD)L{>o0l>M5FX?_k5jQ{r&9_t4RJfiZpX01{G?KriZW)lUXWsCu9De!w zwa^dp)(=5YRhl<=1OR3_#Xu8;w;Tfa&{&9i{pJnD8IqEY^eRsvlUEQ}rs6z;f||g4 z?zs{QmFOYN2%o6K&wqn5^$MC9WG{XOtJkP{cmWCLfuSrpoJ$zM8JBV5!xcb;ezkXi z$Cd(zt4U1f90}4c{mQubo8J((HV7( zKRzUx7~TOcHNmiufgDyYs0j~%+UqFyxu|nn+0xSTrGJx^r6o4VwnA|Ta>24CHzw(n(k>1Rq?Dw{D>{*>V(B0J}>3HgS!(FbAl zPUEOl>;e@|B_`A`>Mv^c$Y7JDxLKE0xtwZ852HQvQw**fAqgq2N+M2LOux}6)%)9* zCqeDAXEIEK+}aGT%m(dQ`1V^RVp6((ANB9gij&}G=dRbU)Yvk3vGrtoPkItilM_Fv z!JiLk3*0W&pmfbywyT<39ip8h%aHwvP03;q$Q=5~=;%-6BCV#*reaACUcOo&cs$%D zOuwbI(8gddB1&)TEPU?qKQ6%fJze!d*u^LBbNvDe!;wP%M5%G(=n3rA9}o`18USnJ zW%VI1Uc9JLPZ!5Rn+0ivvfBOo!#&ZodR`?xY7oO%3K(DDlI^d*FkjBSr+5q^apWOF zgkb|{9YE54gb1A(Bs~@=QbIUad`7`_tfuN_Vw*||``Yv~4{e_b4Q7G`o`Rfi4;OC$N zlky}8ivIf^0OV-^zbI;dy5}f!4FL|C_MgQF1OXf$j_64s8Y#u}hyBJFE0_ za;k0$l0P!(Z8FW;4B3^W*Loj{Vso+M7oiXG6bC%*3os+$zM|$ft4L{4QneGl5xinU zJlFp+yfx6SK}vpGS3W;XJcQ;=xS(wUSEfK0*GS9ygTKPkDam>DISREL?8Uga9||UZ zE|qZE?~lhvq!GSawslqw*qlLYox;`|a?fqpaGDURqvK|xk#F$7S1=R^~yfJKB9bCh_jRS17vXu zy4UT|(o}!gf4Pv{Eh6lf1Sn`-BFAYR>X;{2T_Yg9!~$~rtjPd_gH%B`^o9Wr@ntO&ztbL${n31ev&Rqquwus5 zG(bHY$sgDm)_!$ujYMjMxj~Js-0D^G9tjcV&y!n{*#Z`Is`j4EPE4|R9^T=&8sP;t z#94o$JkUK`3r&tjEvdV2qvTS(CH&-eZc6{0dU|&`+g|6Z#C`N#KRK5b@yGM?nbT4f z)2)|yZ(bR!d^~*K^|^7by3lD{OcX_f*~Y%%K%^(ji%)e|p z^6%BaU~Ov$C{&I729;g6AV{Sbv=xFDz}yXi@g|tP|MvU0`5A#is~~uN98#KEd_Y!f zfNVQJ3rRXelaZAT+C|cGk#i3d8J|H}_*`zEnD{+R z0i>qkRTG^83y0e~gcVkX@8snTC|BSA=hk)TvHf}d@=^46|M7@#-b612GoRmMx%YmI z!XtJiwj^;}Jl6S_uxdk7?RoD7{o=Z5+0pmo@48``gXoZ5aQ#W`%EHShKLryU~qyOcZAkt~F!1MB?t@LI7Y|zL8JGZlF=B;qQxc{J~dM^@aJFA{suSq zKB8P7|5CMj|1Iw;=fjfaT!1P25sVSGfY0Pndj%8y8E9&@pmDW& zlbC3YflaXT5g3Z%tE=s1uch|Buo^%IyqWU$?Px3f?`A{?E&jOLsLR*TUFKsTwjmR1zJ`m=~>%H>BlyKQUO zKLp#n6V@KgP4onc+z(h5ztfPQ<#iY;Z-enc>t^B4_g|k{oaWg}6i=UjKg}tl+IBYE zt7|`AjaJ^_^Fr4#l?*)aZzdbQP9VmTOu}_bEYw&cGbblu>Bvo`LR*s)EXl2c^-O); z9PKd4P{nQ=uv$JgXQ@*x^#9DYXGJpuM;!FFRN}L0myjoaOCF}p-i{@>{?q0o z%9E#wbB_)6|JZx0xGK6g40JYK64G7L(hU+4(kb1DbT`rs(jeU+-QA6Vl1fU0h)4;D zgydO#-~T&tb?(m9`Te|bZ)RrCTC?U|Z#?hwh}~mU89hobnWIUXn@}x#8OkY?uBzf8 zk%NIaYe5|vW=4jBzYAC(yrf!@=`R&+H-fb5=r?UzEb%O`u zYQhO%fNmdL^NieFqf&}NboQ>#7GDab-%|Sh`Em~6AA5jy!~6ia=wIJ}Y+TAFdl_iZ zrk?_w6$_f$hRypRF|7Q+<}kVR!2SX-Y(i#;An_6Y}rPJ6dU<;REq8-$P0UV68P^;%<*3z@nlZ;!*Mdc1-KnTK6)2 zu@s1pZEYxCo15=Ks(?hr{cwr*7%+F^yG-ueYtI2?$qg`8eGY(ln5317d%6aIKniMj ze?cfqh7bst2U-3CU&!VZ%#6bk&<)S z1BYk(xkLBOW+ttQ-`!-brw=tWke!xe!oJqme)yz=>g}mqyM(K8XfAr%w$JhpBYuMM z6i3X8h3%#Ae)jWrn@ksqcP>IjcrpmMaT&$fzJMHkVX3!O%q{54z|D)a1X0jUMGND_wZ;y!9e|9vtKy_+vA#dh_D z96O$4RoS0eERH@adQchRz({uGvJS>_WKcLAP5dd5cVc=Y7Wq{UjBbQHfDIh=m2ZZ~}KRi060T8jK zqT&E3Xi|0XZa!c%Pg;9#?JYqf!w)>=;fEU40}u%$QRZf`+CXP{G*J81$WBZw=>4ZK z_Ucs(ke1(wyg~pEy-^GpwwwcxP8}d`9{yl$rs{)Hp<@6-qR$3p+wKQzvIfNBe!T%q zMV_PM;~0QF#LLy!_Xek&9#r6TR{*SN`2m_cHU$a~Kz2?LdO}Y>UuE06Ts&cc3nXgQ zVH;L+iJ2M^u>$>eX=%Xh~*H&M#dD4P(rW4GnPM zj%*U4wc38@!_q`WK)^jf-jt5L>hjNlpAWj{eR)S1F-Drg;iB<;@I%=SfytN=OOTYj z$zU8jMZl(iaX^$_xwF5wG)RjBQ37S<77KKsqQ#=Sb8V4xoyCyB6#hK&c7$BKj6tDa z-{@_36mH>9s=C*xPCr$1OoCZghNv4nBs4glgOX`s>9bj;@sE4#<2rdi_0VKyH6k<5 zq0ngV1`JQ#6>GI@P@22@nzU@2m-x7=*wJP4%y8BZUR`xXY|OnZOU=ZhRzLJl8&TUK z-v=K%**{j1r)5q8f<(a_IiIk*YeTrcJ^v6B0G%wvhVy&UcImeUGDXdCC7pb}{?I3p zR(+8c_Dyd7u=zPMj7g<;*X#k2n{j>&s-eR4KyjAHaOm=&aq1kj;K-5#){asD4%&7D z=Hv*Nh@8#fa2akOt+)r;4%;4zvXc+_vJ`-e>8z-bkF9F|+-NzK`CesGP{{Mw0m#nn z;2_U{j4S$s!4rNoX!5Y>`GHl?1Y|ngrD_T7KSbgTft{)nuvP_lgIfJ>j6o9+@Ko)s zw4dS_60CV`#eN4#nZ_|-?dq=Mgnuz2RrSn6s-S4~5)V+V)R}O6j4w4`cp!NC4tx^& zy9b!lgP;8uAchVB*$debXu_Stfvzr&jJqwTD3%}SXm-IEe=PzGLL*?0Ibc{$Mov@W zgs9d&`L<{AUmE$}qfgb`4#^-Wc>ZD^`g_qEGT{?f`TV1rrWUgEVY15cS!NgQuTx~D zo*!u2ZkJPaE>&UQ~y!*uv} z;j05p1MC#dzUg9df6*pYnTVg|n42cSqg7Dw<11oJ`ZBcqist^%kNFZx;Sy4&!ykLs z;sv(xalBAGeN4JM%DE|Q2r_RC$o z=yJs#BvLmc|0wH0C9)V7>h*{(0+X8$5in0~khzh*nj#4d4I-qy4H`-Co`V0{xPi+d zn?4X~jfV2Fk4GuJgt+6W2^!9*1_yPhHNHjiWA%nDYgw1kUEm1hgADu}sO-rN-26R# z_kdicZBWex#BY2L82D?TwY&$2U=Amzri_Dq4UAN8ma2c zx6aJwu~t8M$VeSOWQ;DS0`6XxMh^kCw54Yt;d8hS)=thQ$XAC}+5jMS?~jd#$^!}H zcJTEiowW#vLmPtC`{zZ=4jDkuECP7&D)ewA%Ll?bdjKow3ivx_>~RQg*&ZZ%qMJ*E zj0B0}lA@AsQ`o!jGhDZapY42^U#;&&2%+#oM(ABn^bg}Meg=h97c=geD-kVbc7PoQHyUHHa5mQ58~pn|Y31kEOsy8D%gO`!QcD>EY2GdA8++WPah}7}q){ZRFu9Jv8wNxLJQi zoD-wm8i04z{SCHb2Jn)#o`4)N>)W1tkUM$%02GR5P{}nA@H#fB1(g|)$C(tT>Vx*h zy9?N`z`}{`1t3N=N>C6A0hqUoTM+id0rbS&&gop$Jpexrh1+5sEVrs$1Ak=&81z#L z`#caUf4>FEYLD$7KWch6g3*~l;5y?ZHoyqV6JBaS+_%CXRJc8>Dk{D~#{el=+6Ax^ z#K6~D7Xnxgp))*?lmi)}8Y2MMmAGtq$ktN>bC-~l<2Md}&t}+h2|DTy(0YxaV~~_= zUuv=6M-HS@NPQAR!n=3;&_e9ik4WeR%v1_kv_d!j0nxXUq7;N4(d>ZxmMe~5t+Z-1 z{>w|kjbP;U{tMCzvI`HsL(x!YcHi*zXMZSK2+2;I?b5M-pCopdSK~iyBC_0{-9>so zMRQS%RT3Kiy^>tNv3-mBmorqL!Nkmp^9Dx7$oyP$$Bs&jQm2c^ZdK?j2DyiWIm1_p z4Ad4?jsoRy3BMqOIX#VLK85F+5*ZkYFDXcDDBmuf$5X@gJs$qPgO+BCAO4twE<`eS zipb#=MmJ3eZj3H`XcTua%QpfjzWKAl`qNOW+i-hx>4-1CM*CGq3=rbgp%R+}7%ZLn zW|a;3NbqsyAiM|>!q$8il_F}^MBKQoV<1P^95Ywp20d~FH6oFjNlZV=>LgJJN3~Yc zCQV%F`z(i!@;@MPYV#mI8+CP6a{=Bz8$coP2@pRf<+;WIX=4U}C?Nt&3d|7LU8r^R z^eTWLdC3fTd-s|w!Q*$F1BN{7hZA6s*Uk71K;`s6w;Opd9x)Ig{ze|Kqvim)qWNHi zeuF}ew*Lh0LNeu4PpkFJ@I&N+$@Akc*YLwqIC@~1aiIVS)*Ok)@`>2oi>*uZnDuMydjeVbbi7#EP_b8a-2?IHIlWi>bmn0 zDq9Xl2JV-m@>+QJ4ntQW5Q0)H z+Ec05Oj1brHo>R>903J#wj02DjWg6b*lb>1XhCDpjrC&SWkJklEyho>C^{;h*Nh4< zvmNg9?^aAIi~b=UHTTGUhSi;f^DJ2$=QziC^x_qtION?mMzBJ9r(!>+KPo8z0_J_m zffdgYLa?V{PWS_#O@AUm*3JIOC`#6Ka7Y*{9U^!J2@(<;-gQNWX#1Ar@90CeyZArPId ztxp(a@pC{|i5n{QS0(;cHvxmb2e-#(4di2TfoiFcHgHZrp^9}0^pD!4P3>$!8~|cD z2sGm#J+P{@n+_r^JOnOY4`>Eay~M%cw%w8}%;m##+O>AzXxsy6jwb+I&8Y!I%?iLg zoaHV$fI2ibR0C@Mzd@r9D_|gqsjaHg5WV{O-u|CV@}H|1&S$`FzNXpd25Gw!FjPB# zP~Uv_V2?mOo(Ci=JIX=a?+>K|uWJqLM%NEp7wCZP2W#;VGt}~6 z=9;(l)Hmx>t&-l0vl6~5=hc=~)?N4rsVa_s>8S3rLC9NvcvoY|kpNxv~t_P?&0`qMw zNpj5;cR17*?+&4|0=O8G+MK2Q@<7%uD;YLDkyhKxW+3B7ZQ$6C#)p76OflkWUOT^x z#Wvb1@=^pw;X8+yPBqy0tSDv4M841DU}@cf>DCgxYj&crqvMKrhDWAp{ybWqd5)6r z`%X-^OQ4c?M8KzFlNWTp)}Di_XF?iT6dFF2iC^epLHU@&c*o*`v(hT!lx|q#F`^uB z{$&+tOo(o)p@qkvV@Po_;nYY~HwwnIO5j7R(TeCsz74Vm@F4wYTFp)*^Bl*756zx* z@uYNl#<$bi!oqhL?fB`?V?wdY-*mw79Ake0?97-CMjOT2n=k87wGtT*+BhE=NJfjj z0Ghf7#ej-QkVfPz^TXx3prBE!>#@_x5N*R_8ZO+XND~RAKJn^Dc(bS^R_;d&g^ffs z(flrW9GXV8N=%M_cAILwhu5hQjc^}(5Th}op#FSpyxvxAr}M5Wp_+;aV-gDi?oi+M zIuV9AjGs(c1#27{AYVr^a4u*Vjfj#rX3WP%u;UZx$wljNrcRi}nd9$9G!)2pK~$Ij z84i{yO?T=SYGbzm4qFjQkjF|RV#@~ElpO5gQJYW;Yw8euK}QG_(`hPRAD z0LS7p^4+a*!61_10?zECpB*qs{(^lWx52=IeGUxr66-#WN9o}(Y8ca#C+|u<&0k0; z_eH{Tn6;)zdUwmSCfBz-tdvBn6*Sw(Jmr+X4D&NMczqiVlEzb{`sexT0?3gl6CB#e z)=|XhS_9`f11Xl0XQZATnu&IGe$XnCfJC&&w^G)wP&o7oi#at_nmxu@H%sxudK&n1 zry%NcCUY4+QO8gg{xts)qF$oD{z&@AifA4esNDk9fasr6--mKQr)14+kqN5v{I0{D)4t<5&+{PP!gn;6>u}wc zC^a5++NlJO_>a!#|Fe_@Q<$QX{6-P*AS$|cy9%W5HaLjH`{+*u4g+bMW)0#no=Pdc zQKc>AJ62UKaNseG|HSd(Zs08(JBm1l-Wr4TQm zacoR8(bK#d(o^t0O`^vkbjA0|3+rZMwd_^gzcWh0Nx zQJqM$ZN)-AXJcogD?-6scqoD;vjU}@*F9tBH`jkj19le#549HrtkOt!@~%H?F899K zEUESrF1^Zhq5MaEPj(9^IF)LMo66TGqN1+G#g zmSg8$VW$*h=k8%gF(s!<7*H%o^@r9aW<~MK&5#WQ8W!YBkia=>^KbL%MXfsYmNj2c zE25xMBb?)ry^t;@)HI?*!%P-OLmfns&>(-2KUR$L!K0~!3n93S6!*x(|((e{Wu}+*kt64Pqi~jQO5XRR& z7$XK2|AOe zn*U5^oud(g&!(PJCvL^#%&UAxRSo=#3JEo6D*VE<#4RjRsdkr%`8f`v1c~jRje`c> z+V#}ykB@eKxsn(6z*%C@BDt~M?at}*DHD74!#Yb;a}XZJmhdjLW}j50JrEB1AUK4p zEX=%$Nj3cFgLfbi7B;CFD`&wz=Py;0ert2q&=52*!g8!lCM*!!nL%4^{AVc*_dGkN zM2Y$h4EdwfCYJ;lJM2GG1=<8#7s!C#%a^5VY=M)& z9;%{1Y%MGmCOQPy!rv8XIH!xNCOsk8G5W0$^SFvZeivxRqcoQdwc)$U<-0`8lAERP z7^);bmiHg34kbGil3hCgU~PXGRa~6X`B9SA-!t^y)jwZwX-~P5Uj3P0RQTu7O=D|n za?j4My*1uio4_9m5M#JwS7yj{60~i^JSeDw*F-fZlX9~+8L!ap(>XM!aMpz@2!Mu< zeSeRcJGOpsCClbX7ESUM2{ z-lUrDcEoB8=n6}hfJ_o-+!mYGTwX409vzdG<*kV9CSMc>K6P-&wB=tVo`L80hj zFZ7S8|MQY7AA+16wjZ^Q_CQkwSQEg7_$~!!l9VJ_kV-6~&a?K;Fb? zlx3Q?Wa>XG!1g^Bd+Unp?)|&jYxoQQdDdSg(i!8th2mPREAuKbCDxMex8DWcSSj_I zL`FMHHkN|rj- zWPA66l|VzKrc-62Ip?7eBIPhJokQrs`aYZfa;2u1zbp&6qlZ#Tyivv(Z#?Cla&?oCnh5VX{K z?hm1O#!6{6Q&Rj0EnzD7id|`q_^FRdG6PXjUe=;HBDp~JUB)bG&jHJ(o-kHW+oUR1 z6+F5I)mK}NA{*q8m=!WArb6+5Z%L4LQF%8|;>Y;3yB9AIv~j{esQb-2kb$}>F5&eB zM$ln!%8i}5N3{qm+5FvRU0hs!W}vLuZ*nXH-Rk*>LHl2GgQ(etQI#5lSTO22*)-nV zn#2S}L|bzpl{h)o1j-@RFq&k;s|Qz-1h3YHYpI2{QCdh;@DW*eo6x)0ztU6;;=yoo z2qOqBV#9h^hrobv$AdgrPLd(To^1a;6|uOyl^kLM1{JC}O`sQ4550oCpf*SV5dXJ! z2q#;|7*dE;^V~1k<#MJD5bb{;inialdpfK$Vl3E_#pz*Sc~5LcF|5;R+?az4l=Qq? z|L~F0D5B1As2yVUQKIzipy#(0fkYosILwgWT6_v!}I1I)Q z{b(-n~KAPYQ%TF8$~V(P^>hLOT+j9?~n z&Z2+xpmoY4kIblKuJ^^Qi&TK5J;YjG_7m_N43tT|7=@p!;WWKa0I){%3%q=?L6dnIKnX8Dix0xvpZxT&4s89dti;Kcx8ET8;BoGPvuBU^`Mq)-%^(^Wo z+|JFDJy~iP(g*LAxCq)Cze6clc|b#Kjr-QUG1t`+JB)f9N(;(l^@t6V-&+?4TubF3ALuhYMza zY=Uc6HTG7lJAAjp2;>6u1zVk0ib4}0GteC{fTxOw**XI297Gzfvc^`9c%%;TWubwO zgV=S8yjFbp60%l&kD@BXWnrk|sB>)QWW4)2esYKibAZb2pB0>KDlyZqa@dmSPUJ=2 z=S3v0G=ltSe)G*GAM%gE?exGgwC;&Imh%_tH6H07#4kJFFRQ^cF*fDI02TyJ*n>dNkQDm9O;|X( zFGH1Y7NEw&TzPZ+H<(dC6{D6tvSJTH!%U^IB7xrVYzF;nYFvhKB%U@b9}27Fft@6I z>rpcNF^vIQJrvc2RlhBgpxXi!=$=eG2U3%l$bnyC>M@{hm@j@irkSEyj30&xcc6@^ z(xi^(vn}r7Z$xJ6@H&rJ-cw_fFsSR!o}uZ?&}OF8?8nP8jR8Q<2n}_$H5y!kgkCg4 zC{v$+q(K$Bhf>&pU=prbf1hb4++jmf^;xLYb=B*yJcv;Pi9|<1Go~ShWwOdKVXGwr zHRZBOF~Blm7mNeul7B-LNvFxMLPj(bB@6*SjiogiDh>jQ7MR01Wtn+2Y}c`0I zhi>3DT$#)R>xFjeWZ>G6#0iBF?c>x>NaA44u(6wk&P73x3q*uj`>WJw|IYeO~!b4&G z%->POQLCR)7lgh-E+dwi$DpTWlSOjc{v-eUpXme}W;n<2O^Nj+OcgEL0Two|M&3hJ zj2BanWr{CvK+>Tr^K&R%b7q4CYLBUZ1uer?ur?fH>c>^*2Yl+#m|DW->ZzZlY_QiY zDG`LCnN-0YQw5evl2ZjkJrQ%81qk!Wk-N~EL->v_f~F?neurqbtEf22mqMAvsDb(& zanxijp|;kRYH^h3bol?9s#4a;zt_s8r!&Xy^jWo4kTqnc7;?6R957@?4*iL=esorC zhK!wDef&d9Qxi1KX3wfG3+t$t|6+b>tF1|{N}9MkylmImF#TM`$I$l>B|khnav%Hu9Y)Q7F0BTH@}xaFZ*h1& zizL7S${nD88&Q-z=w_&5bLjWKr-kiez*kn0xevtUpfWovI&j6*1S2D(wLyzUL;7Me zyUddLC1I)m&14ZK@L8f@cBtlbWVncUvI^4x@yi=iZ`yBC_iVPFCX2HKjXE8B0L%ca zFAQ59#gt2lDWi~)87R$K!(qp8#=2sfmJ`DV@?eAvTZMs!tzcLG;h26_ zv{>h>7;4Z`A`wqE$fHEw_lYAM291yaO=h5QX^TM}$`opq$Vp^opq5-hA;}z7ILD6! znG(H-j_D_QmwhT^R#s6lPys2LWz(CbmoOD2|H$A9PgX}3H_I+GbDGqg6pY2;4UZI> zw;Bb%`s|O0(fU#T@rB;|Ve!?YGJ0b0qrDXs*{%){BPvqJ(Xw4jK8PPd-wNiIfSPzv z6FmV6LI(~I0<+lhV=g`u^uj~AOQlh-BUYba0xI-xztqz>I5Hh@g&3-<}VVoYMk3hmvM?a8!e4vP>5p z1~u>nQ|Y6E?2XHZ%gQaB8k)SkO14p){^#D^c%lcQJR_0eqwfmLA5J2@^DRcJUW+{d~gK3LeR|aQ=mU?H^^b9T-pfN z_izb0ltf?VcpzX6z#+@NF`O+Bid0Pf`mWorXo|t33(sWrlF*Sd(8H*q+5GYYk$>Kl zr^hUjAI4yvOgwdQGFzJM1QXXVtaPTGp9tfePiY2RDrLgr8c`fMZz_$1^F?q)MGlx4 z#0_ETk<0dm2Ch4BUu9*HGd$G_Rx$Qa#o)tYfQQbnZtvp4O*OzWlDtd-=H-8{VrasP zt?F+0f4wDO9R$x*#s9Y>0~-+g0wiGnJCcV45%0hMV^3QF)AHY0vhTr~{?D}h|0n%7 zWB(t{UdfB~KB5_@>5(C8nmWNkV;ow(;RhunCUdn}AzjF>pi*7FoyEcUyy?=XiH)`(#6@}OQN>z_~@vMavs%8R|tenG3 z1?Qe2OvU5z9bz{D0#)Mj#8!p&t*{L^Ro3FCb|1D5Ag=gkv6y-<=&jYp1(u-e-0Agu zMS0!F&B`*+Q#YXMy|N&+`~wmSts#(O>W=lYySz681)@)(oE9Nx7ZCPU?*x%6ye#_a z{FC*=nEG=F4=Pq4oyH}PPk4!M76bc9BgTMU)9hkY zj9%-fRP@G=VXIo>yXBn6cYjK*BX>_hl~;n*2nWlnf73JhOtzL>{iF|X3Y$+=+%}sn zS*BMj-Z|e>oP@i168p>DkD*B~R#BLWF%SI6j^rVPztceXp`8}4>O2x`UFmRzznb0a z;D^n<-m^c;=s3PokqZq8FEL>*R^&`Z+lPg88Gjhk)60>zP_INB80e?TVzxaL3 zthW)B`!}A6~^@E1Y zd|KZ*!{!S)?ccLxJo<@$9g($oXW?<0=9E*Ove&+pZ*x3yoK)s1-S^JSda65?)kP}buGUG5rZbwUGL zxe|iRX80a9_su1*9484+%Oc!ysA~SnjULI_&-7XznNJJB-tXP`4FoyJ9KGP}*vlP- zt)JobPB3Rh{3N;_c3(%n%336aew@l)M7go`Ps9riy7Y0@PoPkh_ZE%O-Re#cBu%FN zDJtqT`yQMij8UryrN#=766~}lyS*zaeX`1AeRRyKmUBetJQ;QC^W;&|AJ2^#nt0^U zm2sRSpClw%Z^da`=h&QA%B0d)V=`-$X@Ba9(CMo6-tAIbv&(r(i#jGZxpyZ#v8UT* zmW%)3ky3P*^K`#9$ucnh80kI==bY65#``$$)n_!hXn7&EY50>otc2OoD=Bqjz~rs_ z?CCD{lIl}AJ({~MYU*oGxtGyDdb|S^%s0cEU8^H4FMl(>>rYaSYL9ep$Cq#@E7E^r zKdqdpk9V{zE_eO$w%aY${p4yxMS4j_T9hxb$*^lmMgFR^NSBV4ZMP7mQ+gUndPR`t z*LD&?f}7PdDx~KLj&|DXNiBZy!^xEJCvr`_ovFUSKk{^Fz*^S{aIBVJx_5(uqJ7%zxo>&64V6+P7oU3Hj3{I_W9Os|dvV2-U~2zR&#$V zAo`@H-jsF1{5_jjnHSbV&8I3{>zt?7HJ9Wx_w!`=si|yoTy$S{`TS>{mt}G|c-9WZ z6f&#d9(X6+;kqpFRK`muIxyw>Mcdg0xGFY zSH)zT!Sq3ra^%I0sGM;4c--&157=_0sqPtFe$MShFKz!WiVfUL3Cm_0#X649s7>Xt z?CLxFQ?E>Sz6y{d9J!0)+53_!ojD`BRLmIdLw76pg@Nh&9l@Iw*=ek8`+|a`nyQ*S z--+@cq8o8C6@sA*TUf^Wu5*M;ljJzPwL|SaZS&KCVlyyGv_2mk4|ka#)*Ps!c!43` zF6lPx^(JB58w}Lic?=GHxz4{5=yx#MqS7sq>JKE}AJg!cH&HKR=A=Y1>E{j%|s$N8wDhKy$ljKAr{7^{wUsb3F z-uLT^H)KX1&)|@F6PfZ-G9=IS`y%NZ9AVM;*2?9gcyS+CQ!sQmCPfs-JeL`L86lZg zwPD&oUbJ@qraz6ZoU1oyeHlL=`>3wh6rA)ZHDMuc^rMh2eJ2TV+Aq2CX)*X!aLG$kA?R)C|aNfGxnnG{g$_P9vmrwi&*i=Blgy* zKyvi<0O`v{=bB`{^4N>=aZgQ8J|D+$>{H*}d~j=>&g>I+A+(Y|hUs_CV_mC#88>EV zGW~3OA%DYv*RcT)9b`1P(-%k~E1on+pMM0vK_A&=uZm%?hSwnTgp zb(-2HQ`PcPxF~0=Dsxmn=B~cT9Kq)z|6LHMf?T#Yypy(uH}Tu9@3gJi%l(kS zs!4LM;iy%6t=&(gMcww!&!p#TZ{IjYG&0Ik-no-wS2DW19tr|c|}z6i+(%dG!k^s?Np}h zdfp%+T5V>WKU;CfIeiCxg>%Ht3Cnst8Q$B4eJZleVvvo$aLZozwCCH9c-Kli)}d1X z3I0dr#I>5l`TpO6P8$SN8}i|APSoytw_>}d$Bw=a_g${moeS5oF;dd?pj?FBJ*zP^ zrXGOqio(x6P4bKnVGd7>%on$Z4G>?RauZ!hh_c_|q?2mb@2d zT{z*w*HZ3JYC}C&IdS=#G918#T`P=~=*A1b<$363QHj%jeInE``L=EQBhd=h7p}IX zQ`eg}B4pyzulgKmCwotJ{BSb$*Ka9wh#f!HhKZ#yY(Bc0`QGCt6uKf`cvbU^=&wG) zz5fM*a^%r)@MWVQxf9m0y@}=qfMvZY@hy%{DyFGTailtSl!?DI#6Gf;JiTy#V?x*k zhJ~`+`u>8@E{KJ%W{~yq^|0ur&m&PHr^8x&tjhC*LHssx|AGIg132Me~f-+F=#Y9K;JI?~soXu^Aj02UH z}oT$B8-2)%OcHtSE+N1txtrVF&Iw=i)dXYN4x!p_;VW zlrWLZk48izS@;W!J*l;bQLbWTIavgb8)w6$^`2oXAuPa;xt zx24{h;rKg|t;<^khU zBK&d@ftiCwJ9KW^?JLVdEJ%=cTZYoXg3?DmL_N7j>tg6DHvRJoAWj%k}>dGU`KsPTJ+jLI& zT8edIZ`n_WOy#o#?C3z70M&5=ZI)71ClSBlP?yne{R!n1zNtB-$rqHYC+J})dDQ+p zL&Eo(%dHJf%-R@BC6No`5(N!jT_ql|w`hyfG*u0%ZSNDZ$`h7LJM)%TIy@`MmL{S- zL!lQ6LNBH{n`UR5EsHrg{Q2`alUyx@f74M;J3)fR+(AT4KW%#YNN@1xglchJ2HJFP z&XIE8{V6OWJV8V4daqe3*+cogQs?GzZI=wxx=BF!S?Ag}k!Cb{E-pjF%wi#ZQNhAO zS~RWlH_=~%hSVwA<58a%U4vc}$Em(|j=KqVF7Kx^FNX1r{k~l$(l?~=4)3UR2hIG4 z1@Ice3e50(VTNf!_fjNeN=i^`_LeL8z1uzG=*1tDeYvrF8+mh+n;I2iEvo05;aJj( zTF(M|9XFzC+nu&yZDJVE_(D=ZORB{g*+t>@wbmOe$MYVgr;d2nlz7+0+p|9}51-qH zYY1ELr4Dzq67`OKID(&d@jJMVx;3}@^sae_wl?x%gFm|7Z>`MnrBlXAZ`)tYyNr-y6T-d@{oUJzW5eQxL$>+v(Up#1!s zJotsM$y+_2mc93GZEQS^D%Lt8lM%u`y%b*jH;m(R{Y9D0gqh9w`R?7?7v=2$gk3k+A8!l_C-Y1_~~V_Al+Fcrmjfd;PS_v zLCC^GCOzdSF6(J^uoyZi)GDj@j5+ob8uzuqw20Tp??99uhJpQWDKg8}PL-hzjrtiI z+V}UC&BsyR=DOaW$Q2c~HHJp)*ZPtNSKDYTC=5t3?OE_DxXRNsRY)j1RjmJFER40U z9oOV;#arKzEs#>YlhAEVA-}_lzC9PZaNlX3>(-dkID0lbR{Au=TV%GXN2!Md>D$>6 zMcaTo&t1(z8tK#Z)2mYhiU5f%-JhD*^Srh(v2TwFt!o(l{Netx@BZ|B?;Vj<9jsc* z!qRhaQSv_ftv*340^5*`s&;>MQjZmbk-nm$uK3u|?d&f^vA^EnMP$LfX08H_WM;nk zrADPGV?8Q%!W$x)t?(3%kLT$vJc^34PI{)0yh`678Bhy~k#ny*Y90A~Rjfnho{!TP ztgNE7cJ|G5HNNr3f;o&-_x{K~bzc$aiaV6vS$cAIH?E^@nhDKFMfkM}_*3K&8acp! zGceYhyk&1E4B)W*AOmN_*{Cg)7;n?_JBjCqiPk|xe9cpp1<@vQ;5FR?9^|p|ZF_WLno`%w^}Q)YScA(4iqfJZ zaJ`Jb1yL4kjOfc-i(*e+oEBTOAc-Ot>kYVK>e(_dFdz*(t}i*~?3vM$&}xOM1Xf^F zzWF&<_PcfPRnaREz27vkJ6r40fA4UOYyL>-#eG&v{9yH5(_d4tm6z*8=f+=~wY$kt z?Eq^a#UUlhW_3?Whi0z&)#$}xKK;?Mn6L+fG3pj6wKMzJh$-)tddk#r(iPsx-uBDs zs+V(BM)DoGK_~hm7Z-C+9hr>^Po6od32T#!Sz6Fq&9cvI`r|O~ouTvO-V%bh-DKt4 zj-s!PxC~P-=|0}|vfLHbGoLBhEkonBCe>)c(Uc)6?dyZD0!!3$kc1|ilJ>rJcoDIa z80`iowkLlhj0Z_xp4C-mJ@4c!SFh%v0p*b~*Tg6dYgAHs?8vd&Wz+J@ zXieCl^`sKT=qbk3e8{*;!I^!P8p0gk@K>>x+AnuXWl*kXY!r4)?We58fiXGu?!B(&TVy^Nn#)tEj`g z8=p~z2$glfjij2UYTon`f}hK)4TLzBU;Qj=_(1_-r=Ra(*Bhs2FSgL2y-D#3Rn-2o*>2*Z9Ue}>v2@rc&Si==pS1v&n;N@ zt_bI{DbsiZZGOsH;Qkq+rv(oshw3!37ejSJUHkm_UdhqK3-XFDnf^)VgJaDvX?f%# zh=TdoEaJCH)_J+FpX@EepX}&*!+Cdsook&|)wFrJ&tgL41ko)^!b^4sWEMo|2gus^Y=iZ|N^l3dDB`ue8Ui&|Xe zxsC+?%T5tU`%=qdJm>FPe5}3q#&wcIlv`zS{+M^McK|{3(G{-6{6TLw=7lK;r=*jI zWN-Wq!|vlJkg$_pR}|KvRA*5L(QMW8XH&prp=Gp1m=CQq#@T?)m?zk2N#@j?mhk+93?#gP3S@>@@i z?-m7TF3v}voeH(sNZTJxthZKLbvYVtCZCMcRPA2t-?LlqCZ7oDu?3wW9xpGQU07`y z{erV#{mxt(v%}LK0dMOZ@MU#se75e(H0QSi9KL*%N;0?>r%@u)?&HLUtNSa#YI0tO zrSHUT^tl1=HsKXnL+I?+a}iki=04ci6r56P#qXdW!oOaWj;d(vM}Ed8d0KHTH(yp;dMKm6 zIQi<>Pfa_G>#>pk)z56TjAwr>)M59%*1FrEcr(Ox`}f45pt(VNLgijYWmMf6`XU{T z-3vW7M8i2cO_BAImoE%HU0bmJ;vk8eXJ`Jlru{A}i(Z*n^8A=@^c&Yez1K(0!<~1w z=DKsM?L5C0d~qF!Ka+Zmjo&HZKABJ%+}$wg;?`FRXqz8jOKn1k{gPfeey~b3WI%Yi zGCmpAG(XInW;SuKLcgF#>iBhnoPFtio7~@>uyG8YRKGWU<9o_E%D0z17L*aY zKWxtS6|OLUDgH3z?yNw$=X4l4L3IktcocUmeWJE6eWJW;*oJIt>vD)bVW*gAL3y){ z)b?rIDEip?7|q#zMQB64>v#E&8e_{%MwU?%VS_9I0?$2nxi>ip`ZyQU!U4%U?sFn| zT--8x3R|&XwF5~fss`yxjS-WyMo81P?rH!2PIrZO%vx;5X`%a7e2dCWs`26It-iI! z7@lZI+M<(He%;~EMDhK8vHMeQe^X4YM5&g@1iO?9(x8<7tmOgazc++MNf;_;v8E^eZ* zU=vV~kwSm?Ns7uiwbDh|x2Jvkx!4Wp#8_V0nQC>mExnjH7}X=G+??v~@U!QT;!y2WnbB?V(IHfS?`$Y#IBkBiHY|RxbyVfu{(1lUHvWm@_`Afl z5#DxU=lwSSwr}TO5?Ff8_BXUbfn&Z$V|3Qt(LWw8eHnh)c^#f{%P{a_<7oPM8<&H2 z7psfyB0(Y#X>7*@3AYc!UPvs>fiJM~udlb(Z38P$Ui@|NTJ?>{=vMt$9UmZE8|C>% zrt5~1ukmDTQ(9z>(vkm93|HresKm}ou}W(eOZ`hr=G;6^3lG!%fG2YFVgp?5nP`5E z8QpsO$SWomgWs-OPm^@L)%vBtDozsOpY zrsfxRQ0t)*6}jN?r2?@?Z0&mqf4Q#PCXTh9jxXcPr5{qpwc5^hRk=HBQP%NWlZakh z7?-%qaAmxVg`UVIJ;^DmQDsb_l1pk*Dp%!bH0u-=1K-LeDU#*rSv(x@-#-KriiCII z(0oUqnOBsG;%xsM^eH|2cf$X9{1ds-PfS?|+ceg;a#?Pk zqW`;K5zWKJ-0-8}N1z!M2@-v{|D&zzj%q6F_9;N54}x?=Ay_CP319>P6%c|5c2EKa z&}gKXfKsF-A}B?WfJi7Z4iV%@H7N0;-JyyE9HXLOgb)x!DWM1nE#Gxy=B@SCTkHOF z);agAv-dvxclK}JeebPnThq%JNX^VHFHQ8O=w1uo_Rmni{;8h*U_)!)2DVi6$Ilvc zSI~q0s6%J0FMd<$QD<7II`%ER4)z2#4UeaQP$r9wg4?qvrVhp)!w2vr@J75yye~eK zXTzJ$=!s=HHp77?_QV|j>74VDeAO0`7O9#Za}4Ki!Ruf><(cuoZTlFzFeViyu?9`u z#SccXW#puKP)zq+?jkG#v9gi3A0Nh3;58eX(iW0dCO6DO)6AH);QOjmF_t_suO1ag z8zG-;SV&*)sKQT+pEn}-w?I2RvUs*V|j6CgoWs<*TW%emCgh!ybgNV zm16>Y26WkF-y{|#En#U!eC;4YNvr4p@Sf%-VcTr|XOjgplfRM#Fj=(W+DYzVQ`}a; zMYOeEb49bYxC?5Z=a8p>dB*Cs2PSE5DCX9!Xc?iKXCQl%Xt{u1aCBr^gNNsGhK}MR zvN~Bu30(O=4bff+VFuk>dFFL%`M&43=j^1yESX)yE5pDZJySagALxP^I`b-uq*(>o z{YU3&HzE*E2zNMJMD9wCN0z4=ZRs^*MkNOn^qtn&zAp5|pN{DI$l=zaFHakLA1ZDy zv5wZ%TjUpf)}geJ+5k4N$q}XKF}eg)2^*eZ#G1iN=Ryut$JqpHRBsEe7*wv!eLO2X zcD2T%5ST1eiL5KTy{v6(@w-j?SQ~rlt~tz8trh}dHRPmKZ~N;>c2fpTN=4+Qh$+cE zjVDM&BldIBSn2TtwCB5K1yWVHbosmH>KJd%QbZr_3c(|hi|01;26kiyumcyvrY6y6 z*VNpH#RfnkkarKYm8CB9upT#9m!;$-2tlZ8b1~eB)lYytU`>ma9ky;6gITvaFT0Zy z?8jg8%KZlUi(pOpd;vs2)Ud0Vv7KtcAF5YgIbhO5!atDacDT+g4@iu-wA1^RpH(fA z??vh$F zBsTtLZP)xkVhKL>o@Qp{RO#0f_eCXSGtYpVA7@oR?EQ3u$tJb#2$R?`Q@FvZ8)r7C zm2RoM6K=%u|98 zJ2lL4jvkEwe_+AXA>Yc%gU#_9H(Pae0XNJ}FIZ836!;>< z{xSy=yaFlIYfc1>2)>ivTiYQk%t|@8 ziTt6t!18%VW82z*FlO*$;zUt|MwZA6_ObZopf`~AM&xw-3YU*hU^x{H&2PjG0^;~` zl2ewwzb-mzlTh)Fc=#yDz-;_;JG)u1qF_mmQboIo>YBPJC!4gEvkunYj!S=gFgof4 z=MB(C3zlr>Z~M_fKSNX^DUf!O&cCQ1c0j3qG>(>tPOLms1s?W@EyNO5uO`i><-SL~ zJcbBcM7eQ#01LoHDcqln9nPF|m^38@;YzC&s-Dp0uMBGzwJ0`!0lv|EBzrqNk}+Fb z72v&653;W6+FtX;2t3eQ21HX{ji*(J=g5pzG?7iFr9pb`d{2coBJwX0DzR1yA7K%O z(I&=symn0V`t;S1P4$thau|0`1?$S~SA)$LgF0N-6`!VG$j?W1zDs6o0*hYeff&NV zrOB1!LKmsS^gda~2s?Zh-A1dUf%=Ss=ng!8(?o$COi*llyqXMbiN2Qf00<&1T%Wui zI#jS6Nh(QKR2j>bEZ)O(GU(nYYW&1*Zx`*QPQyYWiaCA5LEO1;!Fx5;cRc9pY z7E+YEk+2xo=Cu6b`{)GHyK7i@l7=w&EYD8#a-5*Qy7_A?@7wi3>aHaEt{?Bk|{Tes_85R87X1rw_-1=`P zmgbfzlkjgR5^Hq?6z_lA|L@JkFys+CY4Y;b3qJo06YxC_7AsYO|Mhu=1mD|_#O!#eoW7OFEy6G9{pDHiLO~Ka?`G!Pm zFa(=l(7So6#R{kYFg$QM@eo8(t9|@F5xUc(iwL@KCN|UZ3pl*w*W(u}oM4<-vRM`@QK(t1iWe zzv}Le_Z`|C3t!RvXvz7kj5)^n&k&p+#*2*B?6?7Z2TW+AslqX+EaWgW6dnqdJ(YC0 zUj>7}XnvJ1lP*)p=NO}D#qf$2>vwao!*C<`69@4B9Fp3w38<^$$_Al^pblfFQza*e zzg=rCP#}enOO0kqrB^8$R5S%jJAHc&4QYaGhJ;B3OQk`2;ip@=!sYP>c=7EM*H*%z zYm^V;OxV49ZFFE1kP>F)#8Zk4CY8oY5RC}7#+MhU-O8l%%%|(_NTY#dqEjWAaSpnD zC|d>%@z;xv#a`C$H=g;Fv3H>fF_0bWBdsAe4ntu z78l=`dh{w99o5AifP}%zvPbRM6ISTuckC(HTH^BWkH11Et`J`5_Jy*V@h_@^Jyku` zK7L|9Fk`+UgzXdTt_sL#_W~LTVaHs(Skl5$y1WI`ls%z@8>+hWGVZsgB6gWE9NC>< z6cl@f5YG-wrF&D1&Mz1`7S*3aGdn0Llq=>tOV+W2c%UvcArnd8moXoMqgR>O&cKE^aJ%^WNzE~RQ9BBoYC;9%l42uZx9 z_s}IYhf%BV{+7RH_&jB{JV(;g`S`TnM1r5#8%O+vo|PcwGG5El9WU*Wggihqk7tmL zs*)H@LvH3CY2STXZk~}beIo8{)`f-b$Z`YoDKGgeQRZuJ|B)~Ar z^{?z}?;czBoVs@Ey+)<=c5ROVsR1RsCahKEGjjL9k>)3E&;07tSw%@;re0Xq87YWD z@zQ~kXQ7Y$E?%AF&agJMCIFJG3x8H;6iS;J#(uwGVM3vw`Kr?YDs&y1YYzV;MLRQ( z0sG9vh3dWoh{8Ov+*DchriT!g2Ig@iXMfJiX+)|S)3hx_5T1 zJH!!&We%@NC-SqUhe=Hv-izonZ-mJgDI)g7^ULr$^zuMTMu5f=1 zrG`<%xN(+f*^r*%M+tupQh)ewN=z^)Pjmn5`IOKPK6JK`!O7#c5uLEsko!DU$&a#nV?c0^rvu})Y z&kccX?7eB4iMicyP9F|sTF%*DDbB~P-3tbezvbjg7 ee*&KVQ8D~wxm#AlBX&PHD+KH4=1_4Ef8)RB%#PFm literal 100907 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 z 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

AhFnk6+pHqQ#uTv~Dd{gM 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%0Dsi>=?q5%;s3c3yToKz>XLxU9^e;QgDo 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 o2`T8J{FJmxdi4)bE4e2CZxz}$)_@1PM1 + inkscape:pagecheckerboard="0" + inkscape:showpageshadow="2" + inkscape:deskcolor="#d1d1d1" /> @@ -1498,7 +1500,7 @@ id="tspan20651" x="-16.819941" y="40.732136" - style="stroke-width:0.264583px">>= 3.9 + style="stroke-width:0.264583px">>= 3.10 =3.9 +* Python>=3.10 * :term:`SWIG`>=3.0 * CBLAS compatible BLAS library (e.g., OpenBLAS, CBLAS, Atlas, Accelerate, Intel MKL) diff --git a/python/sdist/amici/setup.template.py b/python/sdist/amici/setup.template.py index 599c4df49b..11589564e0 100644 --- a/python/sdist/amici/setup.template.py +++ b/python/sdist/amici/setup.template.py @@ -72,7 +72,7 @@ def get_extension() -> CMakeExtension: packages=find_namespace_packages(), install_requires=["amici==TPL_AMICI_VERSION"], extras_require={"wurlitzer": ["wurlitzer"]}, - python_requires=">=3.9", + python_requires=">=3.10", package_data={}, zip_safe=False, classifiers=CLASSIFIERS, diff --git a/python/sdist/pyproject.toml b/python/sdist/pyproject.toml index 15ee06df73..6b427e2d16 100644 --- a/python/sdist/pyproject.toml +++ b/python/sdist/pyproject.toml @@ -19,14 +19,15 @@ build-backend = "setuptools.build_meta" name = "amici" dynamic = ["version"] description = "Advanced multi-language Interface to CVODES and IDAS" -requires-python = ">=3.9" +requires-python = ">=3.10" dependencies = [ "cmake-build-extension==0.5.1", "sympy>=1.9", "numpy>=1.19.3; python_version=='3.9'", "numpy>=1.21.4; python_version>='3.10'", "numpy>=1.23.2; python_version=='3.11'", - "numpy; python_version>='3.12'", + "numpy>=1.26.2; python_version=='3.12'", + "numpy; python_version>='3.13'", "python-libsbml", "pandas>=2.0.2", "pyarrow", diff --git a/python/sdist/setup.cfg b/python/sdist/setup.cfg new file mode 100644 index 0000000000..7b8f7b32e7 --- /dev/null +++ b/python/sdist/setup.cfg @@ -0,0 +1,91 @@ +[metadata] +name = amici +description = Advanced multi-language Interface to CVODES and IDAS +version = file: amici/version.txt +license = BSD 3-Clause License +url = https://github.com/AMICI-dev/AMICI +keywords = differential equations, simulation, ode, cvodes, systems biology, sensitivity analysis, sbml, pysb, petab +author = Fabian Froehlich, Jan Hasenauer, Daniel Weindl and Paul Stapor +author_email = fabian_froehlich@hms.harvard.edu +project_urls = + Bug Reports = https://github.com/AMICI-dev/AMICI/issues + Source = https://github.com/AMICI-dev/AMICI + Documentation = https://amici.readthedocs.io/en/latest/ +classifiers = + Development Status :: 5 - Production/Stable + Intended Audience :: Science/Research + License :: OSI Approved :: BSD License + Operating System :: POSIX :: Linux + Operating System :: MacOS :: MacOS X + Programming Language :: Python + Programming Language :: C++ + Topic :: Scientific/Engineering :: Bio-Informatics + +[options] +packages = find_namespace: +package_dir = + amici = amici +python_requires = >=3.10 +install_requires = + cmake-build-extension==0.5.1 + sympy>=1.9 + numpy>=1.21.4; python_version>='3.10' + numpy>=1.23.2; python_version=='3.11' + numpy>=1.26.2; python_version=='3.12' + numpy; python_version>='3.13' + python-libsbml + pandas>=2.0.2 + pyarrow + wurlitzer + toposort + setuptools>=48 + mpmath +include_package_data = True +zip_safe = False + +[options.extras_require] +# Don't include any URLs here - they are not supported by PyPI: +# HTTPError: 400 Bad Request from https://upload.pypi.org/legacy/ +# Invalid value for requires_dist. Error: Can't have direct dependency: ... +petab = petab>=0.2.9 +pysb = pysb>=1.13.1 +test = + benchmark_models_petab @ git+https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab.git@master#subdirectory=src/python + h5py + pytest + pytest-cov + pytest-rerunfailures + coverage + shyaml + antimony>=2.13 + # see https://github.com/sys-bio/antimony/issues/92 + # unsupported x86_64 / x86_64h + antimony!=2.14; platform_system=='Darwin' and platform_machine in 'x86_64h' + scipy + pooch +vis = + matplotlib + seaborn +examples = + jupyter + scipy + +[options.package_data] +amici = + amici/include/amici/* + src/*template* + swig/* + libs/* + setup.py.template + +[options.exclude_package_data] +* = + README.txt + + +[options.entry_points] + +; amici_import_petab.py is kept for backwards compatibility +console_scripts = + amici_import_petab = amici.petab.cli.import_petab:_main + amici_import_petab.py = amici.petab.cli.import_petab:_main From f59fed72eb4d0cedf6abf2a96fe95087ce61478a Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Tue, 23 Apr 2024 10:54:47 +0200 Subject: [PATCH 097/188] pyupgrade, pre-commit autoupdate (#2411) * Run pyupgrade for python>=3.10 * Update configuration for recent ruff manual changes: * .pre-commit-config.yam * pyproject.toml --- .pre-commit-config.yaml | 9 ++- python/sdist/amici/__init__.py | 7 ++- python/sdist/amici/_codegen/cxx_functions.py | 1 + python/sdist/amici/_codegen/model_class.py | 1 + python/sdist/amici/_codegen/template.py | 6 +- python/sdist/amici/antimony_import.py | 6 +- python/sdist/amici/bngl_import.py | 1 - python/sdist/amici/compile.py | 10 ++-- .../amici/conserved_quantities_demartino.py | 7 +-- .../sdist/amici/conserved_quantities_rref.py | 4 +- python/sdist/amici/cxxcodeprinter.py | 14 ++--- python/sdist/amici/de_export.py | 49 +++++++-------- python/sdist/amici/de_model.py | 4 +- python/sdist/amici/de_model_components.py | 28 ++++----- python/sdist/amici/debugging/__init__.py | 1 + python/sdist/amici/gradient_check.py | 19 +++--- python/sdist/amici/import_utils.py | 28 ++++----- python/sdist/amici/logging.py | 16 ++--- python/sdist/amici/numpy.py | 12 ++-- python/sdist/amici/pandas.py | 20 +++---- python/sdist/amici/petab/conditions.py | 13 ++-- python/sdist/amici/petab/import_helpers.py | 12 ++-- python/sdist/amici/petab/petab_import.py | 3 +- python/sdist/amici/petab/petab_problem.py | 19 +++--- python/sdist/amici/petab/pysb_import.py | 7 +-- python/sdist/amici/petab/sbml_import.py | 18 +++--- python/sdist/amici/petab/simulations.py | 15 ++--- python/sdist/amici/petab/simulator.py | 2 +- python/sdist/amici/petab/util.py | 7 ++- python/sdist/amici/petab_import.py | 1 + python/sdist/amici/petab_import_pysb.py | 1 + python/sdist/amici/plotting.py | 14 ++--- python/sdist/amici/pysb_import.py | 29 +++++---- python/sdist/amici/sbml_import.py | 60 +++++++++---------- python/sdist/amici/setup.template.py | 1 + python/sdist/amici/splines.py | 3 +- python/sdist/amici/swig.py | 1 + python/sdist/amici/swig_wrappers.py | 11 ++-- python/sdist/amici/sympy_utils.py | 10 ++-- python/sdist/amici/testing.py | 1 + python/sdist/pyproject.toml | 4 +- python/sdist/setup.py | 1 + python/tests/conftest.py | 1 + .../bngwiki_egfr_simple_deletemolecules.py | 1 - python/tests/splines_utils.py | 22 +++---- .../test_conserved_quantities_demartino.py | 1 + python/tests/test_edata.py | 1 + python/tests/test_events.py | 1 + python/tests/test_heavisides.py | 1 + python/tests/test_petab_simulate.py | 1 + python/tests/test_rdata.py | 1 + python/tests/test_sbml_import.py | 1 + python/tests/util.py | 1 + tests/benchmark-models/evaluate_benchmark.py | 1 + .../benchmark-models/test_petab_benchmark.py | 1 + tests/benchmark-models/test_petab_model.py | 1 + 56 files changed, 268 insertions(+), 243 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a2d00e00c1..449f94ca79 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.6.0 hooks: - id: check-added-large-files - id: check-merge-conflict @@ -12,7 +12,7 @@ repos: - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.1.11 + rev: v0.4.1 hooks: # Run the linter. - id: ruff @@ -28,10 +28,9 @@ repos: - python/sdist/pyproject.toml - repo: https://github.com/asottile/pyupgrade - rev: v3.15.0 + rev: v3.15.2 hooks: - id: pyupgrade - args: ["--py39-plus"] - additional_dependencies: [pyupgrade==3.15.0] + args: ["--py310-plus"] exclude: '^(ThirdParty|models)/' diff --git a/python/sdist/amici/__init__.py b/python/sdist/amici/__init__.py index 76369eda37..942b669fa2 100644 --- a/python/sdist/amici/__init__.py +++ b/python/sdist/amici/__init__.py @@ -13,7 +13,8 @@ import sys from pathlib import Path from types import ModuleType as ModelModule -from typing import Any, Callable, Union +from typing import Any +from collections.abc import Callable def _get_amici_path(): @@ -138,7 +139,7 @@ def get_model(self) -> amici.Model: class add_path: """Context manager for temporarily changing PYTHONPATH""" - def __init__(self, path: Union[str, Path]): + def __init__(self, path: str | Path): self.path: str = str(path) def __enter__(self): @@ -151,7 +152,7 @@ def __exit__(self, exc_type, exc_value, traceback): def import_model_module( - module_name: str, module_path: Union[Path, str] + module_name: str, module_path: Path | str ) -> ModelModule: """ Import Python module of an AMICI model diff --git a/python/sdist/amici/_codegen/cxx_functions.py b/python/sdist/amici/_codegen/cxx_functions.py index 7831ed97c2..5fd5ead94a 100644 --- a/python/sdist/amici/_codegen/cxx_functions.py +++ b/python/sdist/amici/_codegen/cxx_functions.py @@ -1,4 +1,5 @@ """Info about C++ functions in the generated model code.""" + from __future__ import annotations from dataclasses import dataclass diff --git a/python/sdist/amici/_codegen/model_class.py b/python/sdist/amici/_codegen/model_class.py index e6366c1dfd..d24884ca89 100644 --- a/python/sdist/amici/_codegen/model_class.py +++ b/python/sdist/amici/_codegen/model_class.py @@ -1,4 +1,5 @@ """Function for generating the ``amici::Model`` subclass for an amici model.""" + from __future__ import annotations from .cxx_functions import functions, multiobs_functions diff --git a/python/sdist/amici/_codegen/template.py b/python/sdist/amici/_codegen/template.py index 34f3391ed6..2a099ba907 100644 --- a/python/sdist/amici/_codegen/template.py +++ b/python/sdist/amici/_codegen/template.py @@ -1,7 +1,7 @@ """Functions to apply template substitution to files.""" + from pathlib import Path from string import Template -from typing import Union class TemplateAmici(Template): @@ -17,8 +17,8 @@ class TemplateAmici(Template): def apply_template( - source_file: Union[str, Path], - target_file: Union[str, Path], + source_file: str | Path, + target_file: str | Path, template_data: dict[str, str], ) -> None: """ diff --git a/python/sdist/amici/antimony_import.py b/python/sdist/amici/antimony_import.py index 545a2654bd..ad269f11fe 100644 --- a/python/sdist/amici/antimony_import.py +++ b/python/sdist/amici/antimony_import.py @@ -3,11 +3,11 @@ https://antimony.sourceforge.net/ https://tellurium.readthedocs.io/en/latest/antimony.html """ + from pathlib import Path -from typing import Union -def antimony2sbml(ant_model: Union[str, Path]) -> str: +def antimony2sbml(ant_model: str | Path) -> str: """Convert Antimony model to SBML. :param ant_model: Antimony model as string or path to file @@ -46,7 +46,7 @@ def antimony2sbml(ant_model: Union[str, Path]) -> str: return sbml_str -def antimony2amici(ant_model: Union[str, Path], *args, **kwargs): +def antimony2amici(ant_model: str | Path, *args, **kwargs): """Convert Antimony model to AMICI model. Converts the Antimony model provided as string of file to SBML and then imports it into AMICI. diff --git a/python/sdist/amici/bngl_import.py b/python/sdist/amici/bngl_import.py index 960413dbe6..8624ca15f1 100644 --- a/python/sdist/amici/bngl_import.py +++ b/python/sdist/amici/bngl_import.py @@ -5,7 +5,6 @@ in the :term:`BNGL` format. """ - from pysb.importers.bngl import model_from_bngl from .pysb_import import pysb2amici diff --git a/python/sdist/amici/compile.py b/python/sdist/amici/compile.py index 6c4a336afc..eb3668bfbb 100644 --- a/python/sdist/amici/compile.py +++ b/python/sdist/amici/compile.py @@ -2,18 +2,18 @@ Functionality for building the C++ extensions of an amici-created model package. """ + import subprocess import sys -from typing import Optional, Union from pathlib import Path import os def build_model_extension( - package_dir: Union[str, Path], - verbose: Optional[Union[bool, int]] = False, - compiler: Optional[str] = None, - extra_msg: Optional[str] = None, + package_dir: str | Path, + verbose: bool | int | None = False, + compiler: str | None = None, + extra_msg: str | None = None, ) -> None: """ Compile the model extension of an amici-created model package. diff --git a/python/sdist/amici/conserved_quantities_demartino.py b/python/sdist/amici/conserved_quantities_demartino.py index 4f2d326b54..43c84fdf5a 100644 --- a/python/sdist/amici/conserved_quantities_demartino.py +++ b/python/sdist/amici/conserved_quantities_demartino.py @@ -2,7 +2,6 @@ import math import random import sys -from typing import Optional, Union from collections.abc import MutableSequence, Sequence from .logging import get_logger @@ -21,8 +20,8 @@ def compute_moiety_conservation_laws( num_species: int, num_reactions: int, max_num_monte_carlo: int = 20, - rng_seed: Union[None, bool, int] = False, - species_names: Optional[Sequence[str]] = None, + rng_seed: None | bool | int = False, + species_names: Sequence[str] | None = None, ) -> tuple[list[list[int]], list[list[float]]]: """Compute moiety conservation laws. @@ -116,7 +115,7 @@ def _output( int_matched: list[int], species_indices: list[list[int]], species_coefficients: list[list[float]], - species_names: Optional[Sequence[str]] = None, + species_names: Sequence[str] | None = None, verbose: bool = False, log_level: int = logging.DEBUG, ): diff --git a/python/sdist/amici/conserved_quantities_rref.py b/python/sdist/amici/conserved_quantities_rref.py index b16053ab08..85dbd2b9b1 100644 --- a/python/sdist/amici/conserved_quantities_rref.py +++ b/python/sdist/amici/conserved_quantities_rref.py @@ -1,12 +1,12 @@ """Find conserved quantities deterministically""" -from typing import Literal, Optional, Union +from typing import Literal import numpy as np def rref( - mat: np.array, round_ndigits: Optional[Union[Literal[False], int]] = None + mat: np.array, round_ndigits: Literal[False] | int | None = None ) -> np.array: """ Bring matrix ``mat`` to reduced row echelon form diff --git a/python/sdist/amici/cxxcodeprinter.py b/python/sdist/amici/cxxcodeprinter.py index fb98b0aca0..d9de0ca8f8 100644 --- a/python/sdist/amici/cxxcodeprinter.py +++ b/python/sdist/amici/cxxcodeprinter.py @@ -1,8 +1,8 @@ """C++ code generation""" + import itertools import os import re -from typing import Optional from collections.abc import Sequence from collections.abc import Iterable @@ -49,7 +49,7 @@ def __init__(self): else: self._fpoptimizer = None - def doprint(self, expr: sp.Expr, assign_to: Optional[str] = None) -> str: + def doprint(self, expr: sp.Expr, assign_to: str | None = None) -> str: if self._fpoptimizer: if isinstance(expr, list): expr = list(map(self._fpoptimizer, expr)) @@ -124,7 +124,7 @@ def _get_sym_lines_symbols( equations: sp.Matrix, variable: str, indent_level: int, - indices: Optional[Sequence[int]] = None, + indices: Sequence[int] | None = None, ) -> list[str]: """ Generate C++ code for where array elements are directly replaced with @@ -230,8 +230,8 @@ def print_bool(expr) -> str: def get_switch_statement( condition: str, cases: dict[int, list[str]], - indentation_level: Optional[int] = 0, - indentation_step: Optional[str] = " " * 4, + indentation_level: int | None = 0, + indentation_step: str | None = " " * 4, ): """ Generate code for a C++ switch statement. @@ -296,8 +296,8 @@ def csc_matrix( matrix: sp.Matrix, rownames: list[sp.Symbol], colnames: list[sp.Symbol], - identifier: Optional[int] = 0, - pattern_only: Optional[bool] = False, + identifier: int | None = 0, + pattern_only: bool | None = False, ) -> tuple[list[int], list[int], sp.Matrix, list[str], sp.Matrix]: """ Generates the sparse symbolic identifiers, symbolic identifiers, diff --git a/python/sdist/amici/de_export.py b/python/sdist/amici/de_export.py index fea9325ab2..147ae3d381 100644 --- a/python/sdist/amici/de_export.py +++ b/python/sdist/amici/de_export.py @@ -9,6 +9,7 @@ :py:func:`amici.sbml_import.SbmlImporter.sbml2amici` and :py:func:`amici.petab_import.import_model`. """ + from __future__ import annotations import copy import logging @@ -1140,36 +1141,36 @@ def _write_model_header_cpp(self) -> None: ] = impl continue - tpl_data[ - f"{func_name.upper()}_DEF" - ] = get_function_extern_declaration( - func_name, self.model_name, self.model.is_ode() + tpl_data[f"{func_name.upper()}_DEF"] = ( + get_function_extern_declaration( + func_name, self.model_name, self.model.is_ode() + ) ) - tpl_data[ - f"{func_name.upper()}_IMPL" - ] = get_model_override_implementation( - func_name, self.model_name, self.model.is_ode() + tpl_data[f"{func_name.upper()}_IMPL"] = ( + get_model_override_implementation( + func_name, self.model_name, self.model.is_ode() + ) ) if func_name in sparse_functions: - tpl_data[ - f"{func_name.upper()}_COLPTRS_DEF" - ] = get_sunindex_extern_declaration( - func_name, self.model_name, "colptrs" + tpl_data[f"{func_name.upper()}_COLPTRS_DEF"] = ( + get_sunindex_extern_declaration( + func_name, self.model_name, "colptrs" + ) ) - tpl_data[ - f"{func_name.upper()}_COLPTRS_IMPL" - ] = get_sunindex_override_implementation( - func_name, self.model_name, "colptrs" + tpl_data[f"{func_name.upper()}_COLPTRS_IMPL"] = ( + get_sunindex_override_implementation( + func_name, self.model_name, "colptrs" + ) ) - tpl_data[ - f"{func_name.upper()}_ROWVALS_DEF" - ] = get_sunindex_extern_declaration( - func_name, self.model_name, "rowvals" + tpl_data[f"{func_name.upper()}_ROWVALS_DEF"] = ( + get_sunindex_extern_declaration( + func_name, self.model_name, "rowvals" + ) ) - tpl_data[ - f"{func_name.upper()}_ROWVALS_IMPL" - ] = get_sunindex_override_implementation( - func_name, self.model_name, "rowvals" + tpl_data[f"{func_name.upper()}_ROWVALS_IMPL"] = ( + get_sunindex_override_implementation( + func_name, self.model_name, "rowvals" + ) ) if self.model.num_states_solver() == self.model.num_states_rdata(): diff --git a/python/sdist/amici/de_model.py b/python/sdist/amici/de_model.py index ea2807df52..f0b9c6cbb0 100644 --- a/python/sdist/amici/de_model.py +++ b/python/sdist/amici/de_model.py @@ -1,4 +1,5 @@ """Symbolic differential equation model.""" + from __future__ import annotations import contextlib @@ -6,7 +7,8 @@ import itertools import re from itertools import chain -from typing import Callable, TYPE_CHECKING +from typing import TYPE_CHECKING +from collections.abc import Callable from collections.abc import Sequence import numpy as np diff --git a/python/sdist/amici/de_model_components.py b/python/sdist/amici/de_model_components.py index eeeabb35db..bc93f44b87 100644 --- a/python/sdist/amici/de_model_components.py +++ b/python/sdist/amici/de_model_components.py @@ -1,7 +1,8 @@ """Objects for AMICI's internal differential equation model representation""" + import abc import numbers -from typing import Optional, SupportsFloat, Union +from typing import SupportsFloat import sympy as sp @@ -45,7 +46,7 @@ def __init__( self, identifier: sp.Symbol, name: str, - value: Union[SupportsFloat, numbers.Number, sp.Expr], + value: SupportsFloat | numbers.Number | sp.Expr, ): """ Create a new ModelQuantity instance. @@ -165,7 +166,7 @@ def __init__( self._ncoeff: sp.Expr = coefficients[state_id] super().__init__(identifier, name, value) - def get_ncoeff(self, state_id) -> Union[sp.Expr, int, float]: + def get_ncoeff(self, state_id) -> sp.Expr | int | float: """ Computes the normalized coefficient a_i/a_j where i is the index of the provided state_id and j is the index of the state that is @@ -216,7 +217,7 @@ class State(ModelQuantity): Base class for differential and algebraic model states """ - _conservation_law: Optional[ConservationLaw] = None + _conservation_law: ConservationLaw | None = None def get_x_rdata(self): """ @@ -323,7 +324,7 @@ def __init__( """ super().__init__(identifier, name, init) self._dt = cast_to_sym(dt, "dt") - self._conservation_law: Union[ConservationLaw, None] = None + self._conservation_law: ConservationLaw | None = None def set_conservation_law(self, law: ConservationLaw) -> None: """ @@ -394,17 +395,16 @@ class Observable(ModelQuantity): function or residuals """ - _measurement_symbol: Union[sp.Symbol, None] = None + _measurement_symbol: sp.Symbol | None = None def __init__( self, identifier: sp.Symbol, name: str, value: sp.Expr, - measurement_symbol: Optional[sp.Symbol] = None, - transformation: Optional[ - ObservableTransformation - ] = ObservableTransformation.LIN, + measurement_symbol: sp.Symbol | None = None, + transformation: None + | (ObservableTransformation) = ObservableTransformation.LIN, ): """ Create a new Observable instance. @@ -459,8 +459,8 @@ def __init__( name: str, value: sp.Expr, event: sp.Symbol, - measurement_symbol: Optional[sp.Symbol] = None, - transformation: Optional[ObservableTransformation] = "lin", + measurement_symbol: sp.Symbol | None = None, + transformation: ObservableTransformation | None = "lin", ): """ Create a new EventObservable instance. @@ -668,8 +668,8 @@ def __init__( identifier: sp.Symbol, name: str, value: sp.Expr, - state_update: Union[sp.Expr, None], - initial_value: Optional[bool] = True, + state_update: sp.Expr | None, + initial_value: bool | None = True, ): """ Create a new Event instance. diff --git a/python/sdist/amici/debugging/__init__.py b/python/sdist/amici/debugging/__init__.py index 81663d17b9..55b24e7b18 100644 --- a/python/sdist/amici/debugging/__init__.py +++ b/python/sdist/amici/debugging/__init__.py @@ -1,4 +1,5 @@ """Functions for debugging AMICI simulation failures.""" + import amici import numpy as np diff --git a/python/sdist/amici/gradient_check.py b/python/sdist/amici/gradient_check.py index c5ddb03749..019f091e86 100644 --- a/python/sdist/amici/gradient_check.py +++ b/python/sdist/amici/gradient_check.py @@ -6,7 +6,6 @@ """ import copy -from typing import Optional from collections.abc import Sequence import numpy as np @@ -31,9 +30,9 @@ def check_finite_difference( edata: ExpData, ip: int, fields: list[str], - atol: Optional[float] = 1e-4, - rtol: Optional[float] = 1e-4, - epsilon: Optional[float] = 1e-3, + atol: float | None = 1e-4, + rtol: float | None = 1e-4, + epsilon: float | None = 1e-3, ) -> None: """ Checks the computed sensitivity based derivatives against a finite @@ -138,10 +137,10 @@ def check_finite_difference( def check_derivatives( model: Model, solver: Solver, - edata: Optional[ExpData] = None, - atol: Optional[float] = 1e-4, - rtol: Optional[float] = 1e-4, - epsilon: Optional[float] = 1e-3, + edata: ExpData | None = None, + atol: float | None = 1e-4, + rtol: float | None = 1e-4, + epsilon: float | None = 1e-3, check_least_squares: bool = True, skip_zero_pars: bool = False, ) -> None: @@ -249,8 +248,8 @@ def _check_close( atol: float, rtol: float, field: str, - ip: Optional[int] = None, - verbose: Optional[bool] = True, + ip: int | None = None, + verbose: bool | None = True, ) -> None: """ Compares computed values against expected values and provides rich diff --git a/python/sdist/amici/import_utils.py b/python/sdist/amici/import_utils.py index 029c2cc6de..1a0dc782db 100644 --- a/python/sdist/amici/import_utils.py +++ b/python/sdist/amici/import_utils.py @@ -1,16 +1,16 @@ """Miscellaneous functions related to model import, independent of any specific - model format""" +model format""" + import enum import itertools as itt import numbers import sys from typing import ( Any, - Callable, - Optional, SupportsFloat, Union, ) +from collections.abc import Callable from collections.abc import Iterable, Sequence import sympy as sp @@ -69,7 +69,7 @@ class ObservableTransformation(str, enum.Enum): def noise_distribution_to_observable_transformation( - noise_distribution: Union[str, Callable], + noise_distribution: str | Callable, ) -> ObservableTransformation: """ Parse noise distribution string and extract observable transformation @@ -90,7 +90,7 @@ def noise_distribution_to_observable_transformation( def noise_distribution_to_cost_function( - noise_distribution: Union[str, Callable], + noise_distribution: str | Callable, ) -> Callable[[str], str]: """ Parse noise distribution string to a cost function definition amici can @@ -258,7 +258,7 @@ def _get_str_symbol_identifiers(str_symbol: str) -> tuple: def smart_subs_dict( sym: sp.Expr, subs: SymbolDef, - field: Optional[str] = None, + field: str | None = None, reverse: bool = True, ) -> sp.Expr: """ @@ -315,7 +315,7 @@ def smart_subs(element: sp.Expr, old: sp.Symbol, new: sp.Expr) -> sp.Expr: def toposort_symbols( - symbols: SymbolDef, field: Optional[str] = None + symbols: SymbolDef, field: str | None = None ) -> SymbolDef: """ Topologically sort symbol definitions according to their interdependency @@ -420,8 +420,8 @@ def _parse_special_functions(sym: sp.Expr, toplevel: bool = True) -> sp.Expr: def _denest_piecewise( - args: Sequence[Union[sp.Expr, sp.logic.boolalg.Boolean, bool]], -) -> tuple[Union[sp.Expr, sp.logic.boolalg.Boolean, bool]]: + args: Sequence[sp.Expr | sp.logic.boolalg.Boolean | bool], +) -> tuple[sp.Expr | sp.logic.boolalg.Boolean | bool]: """ Denest piecewise functions that contain piecewise as condition @@ -567,7 +567,7 @@ def grouper( def _check_unsupported_functions( - sym: sp.Expr, expression_type: str, full_sym: Optional[sp.Expr] = None + sym: sp.Expr, expression_type: str, full_sym: sp.Expr | None = None ): """ Recursively checks the symbolic expression for unsupported symbolic @@ -619,7 +619,7 @@ def _check_unsupported_functions( def cast_to_sym( - value: Union[SupportsFloat, sp.Expr, BooleanAtom], input_name: str + value: SupportsFloat | sp.Expr | BooleanAtom, input_name: str ) -> sp.Expr: """ Typecasts the value to :py:class:`sympy.Float` if possible, and ensures the @@ -647,7 +647,7 @@ def cast_to_sym( return value -def generate_measurement_symbol(observable_id: Union[str, sp.Symbol]): +def generate_measurement_symbol(observable_id: str | sp.Symbol): """ Generates the appropriate measurement symbol for the provided observable @@ -662,7 +662,7 @@ def generate_measurement_symbol(observable_id: Union[str, sp.Symbol]): return symbol_with_assumptions(f"m{observable_id}") -def generate_regularization_symbol(observable_id: Union[str, sp.Symbol]): +def generate_regularization_symbol(observable_id: str | sp.Symbol): """ Generates the appropriate regularization symbol for the provided observable @@ -678,7 +678,7 @@ def generate_regularization_symbol(observable_id: Union[str, sp.Symbol]): def generate_flux_symbol( - reaction_index: int, name: Optional[str] = None + reaction_index: int, name: str | None = None ) -> sp.Symbol: """ Generate identifier symbol for a reaction flux. diff --git a/python/sdist/amici/logging.py b/python/sdist/amici/logging.py index df39c4a219..0a345ee0a7 100644 --- a/python/sdist/amici/logging.py +++ b/python/sdist/amici/logging.py @@ -27,14 +27,14 @@ "CRITICAL": logging.CRITICAL, } -from typing import Callable, Optional, Union +from collections.abc import Callable def _setup_logger( - level: Optional[int] = logging.WARNING, - console_output: Optional[bool] = True, - file_output: Optional[bool] = False, - capture_warnings: Optional[bool] = False, + level: int | None = logging.WARNING, + console_output: bool | None = True, + file_output: bool | None = False, + capture_warnings: bool | None = False, ) -> logging.Logger: """ Set up a new :class:`logging.Logger` for AMICI logging. @@ -118,7 +118,7 @@ def _setup_logger( return log -def set_log_level(logger: logging.Logger, log_level: Union[int, bool]) -> None: +def set_log_level(logger: logging.Logger, log_level: int | bool) -> None: if log_level is not None and log_level is not False: if isinstance(log_level, bool): log_level = logging.DEBUG @@ -134,8 +134,8 @@ def set_log_level(logger: logging.Logger, log_level: Union[int, bool]) -> None: def get_logger( - logger_name: Optional[str] = BASE_LOGGER_NAME, - log_level: Optional[int] = None, + logger_name: str | None = BASE_LOGGER_NAME, + log_level: int | None = None, **kwargs, ) -> logging.Logger: """ diff --git a/python/sdist/amici/numpy.py b/python/sdist/amici/numpy.py index f40d0f4c6e..9aa03fc2dd 100644 --- a/python/sdist/amici/numpy.py +++ b/python/sdist/amici/numpy.py @@ -37,7 +37,7 @@ class is memory efficient as copies of the underlying C++ objects is _field_names: list[str] = [] _field_dimensions: dict[str, list[int]] = dict() - def __getitem__(self, item: str) -> Union[np.ndarray, float]: + def __getitem__(self, item: str) -> np.ndarray | float: """ Access to field names, copies data from C++ object into numpy array, reshapes according to field dimensions and stores values in @@ -76,7 +76,7 @@ def __missing__(self, key: str) -> None: """ raise KeyError(f"Unknown field name {key}.") - def __getattr__(self, item) -> Union[np.ndarray, float]: + def __getattr__(self, item) -> np.ndarray | float: """ Attribute accessor for field names @@ -245,7 +245,7 @@ class ReturnDataView(SwigPtrView): "t_last", ] - def __init__(self, rdata: Union[ReturnDataPtr, ReturnData]): + def __init__(self, rdata: ReturnDataPtr | ReturnData): """ Constructor @@ -309,7 +309,7 @@ def __init__(self, rdata: Union[ReturnDataPtr, ReturnData]): def __getitem__( self, item: str - ) -> Union[np.ndarray, ReturnDataPtr, ReturnData, float]: + ) -> np.ndarray | ReturnDataPtr | ReturnData | float: """ Access fields by name.s @@ -391,7 +391,7 @@ class ExpDataView(SwigPtrView): "fixedParametersPresimulation", ] - def __init__(self, edata: Union[ExpDataPtr, ExpData]): + def __init__(self, edata: ExpDataPtr | ExpData): """ Constructor @@ -429,7 +429,7 @@ def __init__(self, edata: Union[ExpDataPtr, ExpData]): def _field_as_numpy( field_dimensions: dict[str, list[int]], field: str, data: SwigPtrView -) -> Union[np.ndarray, float, None]: +) -> np.ndarray | float | None: """ Convert data object field to numpy array with dimensions according to specified field dimensions diff --git a/python/sdist/amici/pandas.py b/python/sdist/amici/pandas.py index b776d2d5ef..b61cfa0c79 100644 --- a/python/sdist/amici/pandas.py +++ b/python/sdist/amici/pandas.py @@ -7,7 +7,7 @@ import copy import math -from typing import Optional, SupportsFloat, Union +from typing import SupportsFloat, Union import amici import numpy as np @@ -70,7 +70,7 @@ def _process_rdata_list(rdata_list: ReturnDatas) -> list[amici.ReturnDataView]: def getDataObservablesAsDataFrame( - model: AmiciModel, edata_list: ExpDatas, by_id: Optional[bool] = False + model: AmiciModel, edata_list: ExpDatas, by_id: bool | None = False ) -> pd.DataFrame: """ Write Observables from experimental data as DataFrame. @@ -123,7 +123,7 @@ def getSimulationObservablesAsDataFrame( model: amici.Model, edata_list: ExpDatas, rdata_list: ReturnDatas, - by_id: Optional[bool] = False, + by_id: bool | None = False, ) -> pd.DataFrame: """ Write Observables from simulation results as DataFrame. @@ -181,7 +181,7 @@ def getSimulationStatesAsDataFrame( model: amici.Model, edata_list: ExpDatas, rdata_list: ReturnDatas, - by_id: Optional[bool] = False, + by_id: bool | None = False, ) -> pd.DataFrame: """ Get model state according to lists of ReturnData and ExpData. @@ -237,7 +237,7 @@ def get_expressions_as_dataframe( model: amici.Model, edata_list: ExpDatas, rdata_list: ReturnDatas, - by_id: Optional[bool] = False, + by_id: bool | None = False, ) -> pd.DataFrame: """ Get values of model expressions from lists of ReturnData as DataFrame. @@ -293,7 +293,7 @@ def getResidualsAsDataFrame( model: amici.Model, edata_list: ExpDatas, rdata_list: ReturnDatas, - by_id: Optional[bool] = False, + by_id: bool | None = False, ) -> pd.DataFrame: """ Convert a list of ReturnData and ExpData to pandas DataFrame with @@ -626,8 +626,8 @@ def _get_names_or_ids( def _get_specialized_fixed_parameters( model: AmiciModel, - condition: Union[dict[str, SupportsFloat], pd.Series], - overwrite: Union[dict[str, SupportsFloat], pd.Series], + condition: dict[str, SupportsFloat] | pd.Series, + overwrite: dict[str, SupportsFloat] | pd.Series, by_id: bool, ) -> list[float]: """ @@ -661,7 +661,7 @@ def constructEdataFromDataFrame( df: pd.DataFrame, model: AmiciModel, condition: pd.Series, - by_id: Optional[bool] = False, + by_id: bool | None = False, ) -> amici.amici.ExpData: """ Constructs an ExpData instance according to the provided Model @@ -778,7 +778,7 @@ def constructEdataFromDataFrame( def getEdataFromDataFrame( - model: AmiciModel, df: pd.DataFrame, by_id: Optional[bool] = False + model: AmiciModel, df: pd.DataFrame, by_id: bool | None = False ) -> list[amici.amici.ExpData]: """ Constructs a ExpData instances according to the provided Model and diff --git a/python/sdist/amici/petab/conditions.py b/python/sdist/amici/petab/conditions.py index 34dd44cbcb..61b8679765 100644 --- a/python/sdist/amici/petab/conditions.py +++ b/python/sdist/amici/petab/conditions.py @@ -1,4 +1,5 @@ """PEtab conditions to AMICI ExpDatas.""" + import logging import numbers import warnings @@ -218,7 +219,7 @@ def create_parameterized_edatas( problem_parameters: dict[str, numbers.Number], scaled_parameters: bool = False, parameter_mapping: ParameterMapping = None, - simulation_conditions: Union[pd.DataFrame, dict] = None, + simulation_conditions: pd.DataFrame | dict = None, ) -> list[amici.ExpData]: """Create list of :class:amici.ExpData objects with parameters filled in. @@ -284,7 +285,7 @@ def create_parameterized_edatas( def create_edata_for_condition( - condition: Union[dict, pd.Series], + condition: dict | pd.Series, measurement_df: pd.DataFrame, amici_model: AmiciModel, petab_problem: petab.Problem, @@ -369,7 +370,7 @@ def create_edata_for_condition( def create_edatas( amici_model: AmiciModel, petab_problem: petab.Problem, - simulation_conditions: Union[pd.DataFrame, dict] = None, + simulation_conditions: pd.DataFrame | dict = None, ) -> list[amici.ExpData]: """Create list of :class:`amici.amici.ExpData` objects for PEtab problem. @@ -515,7 +516,7 @@ def _get_measurements_and_sigmas( if isinstance( measurement.get(NOISE_PARAMETERS, None), numbers.Number ): - sigma_y[ - time_ix_for_obs_ix[observable_ix], observable_ix - ] = measurement[NOISE_PARAMETERS] + sigma_y[time_ix_for_obs_ix[observable_ix], observable_ix] = ( + measurement[NOISE_PARAMETERS] + ) return y, sigma_y diff --git a/python/sdist/amici/petab/import_helpers.py b/python/sdist/amici/petab/import_helpers.py index 3caf951ace..29cbff07e6 100644 --- a/python/sdist/amici/petab/import_helpers.py +++ b/python/sdist/amici/petab/import_helpers.py @@ -2,12 +2,12 @@ Functions for PEtab import that are independent of the model format. """ + import importlib import logging import os import re from pathlib import Path -from typing import Union import amici import pandas as pd @@ -30,9 +30,7 @@ def get_observation_model( observable_df: pd.DataFrame, -) -> tuple[ - dict[str, dict[str, str]], dict[str, str], dict[str, Union[str, float]] -]: +) -> tuple[dict[str, dict[str, str]], dict[str, str], dict[str, str | float]]: """ Get observables, sigmas, and noise distributions from PEtab observation table in a format suitable for @@ -128,7 +126,7 @@ def petab_scale_to_amici_scale(scale_str: str) -> int: raise ValueError(f"Invalid parameter scale {scale_str}") -def _create_model_name(folder: Union[str, Path]) -> str: +def _create_model_name(folder: str | Path) -> str: """ Create a name for the model. Just re-use the last part of the folder. @@ -136,9 +134,7 @@ def _create_model_name(folder: Union[str, Path]) -> str: return os.path.split(os.path.normpath(folder))[-1] -def _can_import_model( - model_name: str, model_output_dir: Union[str, Path] -) -> bool: +def _can_import_model(model_name: str, model_output_dir: str | Path) -> bool: """ Check whether a module of that name can already be imported. """ diff --git a/python/sdist/amici/petab/petab_import.py b/python/sdist/amici/petab/petab_import.py index cb896b39e3..0e63496d75 100644 --- a/python/sdist/amici/petab/petab_import.py +++ b/python/sdist/amici/petab/petab_import.py @@ -9,7 +9,6 @@ import os import shutil from pathlib import Path -from typing import Union from warnings import warn import amici @@ -34,7 +33,7 @@ def import_petab_problem( petab_problem: petab.Problem, - model_output_dir: Union[str, Path, None] = None, + model_output_dir: str | Path | None = None, model_name: str = None, compile_: bool = None, non_estimated_parameters_as_constants=True, diff --git a/python/sdist/amici/petab/petab_problem.py b/python/sdist/amici/petab/petab_problem.py index 8ea177ad03..618b8b5247 100644 --- a/python/sdist/amici/petab/petab_problem.py +++ b/python/sdist/amici/petab/petab_problem.py @@ -1,6 +1,6 @@ """PEtab-problem based simulations.""" + import copy -from typing import Optional, Union import amici import pandas as pd @@ -36,10 +36,10 @@ class PetabProblem: def __init__( self, petab_problem: petab.Problem, - amici_model: Optional[amici.Model] = None, - problem_parameters: Optional[dict[str, float]] = None, + amici_model: amici.Model | None = None, + problem_parameters: dict[str, float] | None = None, scaled_parameters: bool = False, - simulation_conditions: Union[pd.DataFrame, list[dict]] = None, + simulation_conditions: pd.DataFrame | list[dict] = None, store_edatas: bool = True, ): self._petab_problem = copy.deepcopy(petab_problem) @@ -63,9 +63,9 @@ def __init__( if ( preeq_id := PREEQUILIBRATION_CONDITION_ID ) in self._simulation_conditions: - self._simulation_conditions[ - preeq_id - ] = self._simulation_conditions[preeq_id].fillna("") + self._simulation_conditions[preeq_id] = ( + self._simulation_conditions[preeq_id].fillna("") + ) if problem_parameters is None: # Use PEtab nominal values as default @@ -102,7 +102,10 @@ def set_parameters( :param scaled_parameters: Whether the provided parameters are on PEtab `parameterScale` or not. """ - if scaled_parameters != self._scaled_parameters and self._parameter_mapping is not None: + if ( + scaled_parameters != self._scaled_parameters + and self._parameter_mapping is not None + ): # redo parameter mapping if scale changed self._parameter_mapping = create_parameter_mapping( petab_problem=self._petab_problem, diff --git a/python/sdist/amici/petab/pysb_import.py b/python/sdist/amici/petab/pysb_import.py index 8c67bb0785..b770603935 100644 --- a/python/sdist/amici/petab/pysb_import.py +++ b/python/sdist/amici/petab/pysb_import.py @@ -8,7 +8,6 @@ import logging import re from pathlib import Path -from typing import Optional, Union import petab import pysb @@ -165,9 +164,9 @@ def _add_initialization_variables( @log_execution_time("Importing PEtab model", logger) def import_model_pysb( petab_problem: petab.Problem, - model_output_dir: Optional[Union[str, Path]] = None, - verbose: Optional[Union[bool, int]] = True, - model_name: Optional[str] = None, + model_output_dir: str | Path | None = None, + verbose: bool | int | None = True, + model_name: str | None = None, **kwargs, ) -> None: """ diff --git a/python/sdist/amici/petab/sbml_import.py b/python/sdist/amici/petab/sbml_import.py index 2484d57a7a..2c43c3adc4 100644 --- a/python/sdist/amici/petab/sbml_import.py +++ b/python/sdist/amici/petab/sbml_import.py @@ -4,7 +4,7 @@ import tempfile from itertools import chain from pathlib import Path -from typing import Optional, Union +from typing import Union from warnings import warn import amici @@ -31,17 +31,17 @@ @log_execution_time("Importing PEtab model", logger) def import_model_sbml( sbml_model: Union[str, Path, "libsbml.Model"] = None, - condition_table: Optional[Union[str, Path, pd.DataFrame]] = None, - observable_table: Optional[Union[str, Path, pd.DataFrame]] = None, - measurement_table: Optional[Union[str, Path, pd.DataFrame]] = None, + condition_table: str | Path | pd.DataFrame | None = None, + observable_table: str | Path | pd.DataFrame | None = None, + measurement_table: str | Path | pd.DataFrame | None = None, petab_problem: petab.Problem = None, - model_name: Optional[str] = None, - model_output_dir: Optional[Union[str, Path]] = None, - verbose: Optional[Union[bool, int]] = True, + model_name: str | None = None, + model_output_dir: str | Path | None = None, + verbose: bool | int | None = True, allow_reinit_fixpar_initcond: bool = True, validate: bool = True, non_estimated_parameters_as_constants=True, - output_parameter_defaults: Optional[dict[str, float]] = None, + output_parameter_defaults: dict[str, float] | None = None, discard_sbml_annotations: bool = False, **kwargs, ) -> amici.SbmlImporter: @@ -545,7 +545,7 @@ def _get_fixed_parameters_sbml( def _create_model_output_dir_name( - sbml_model: "libsbml.Model", model_name: Optional[str] = None + sbml_model: "libsbml.Model", model_name: str | None = None ) -> Path: """ Find a folder for storing the compiled amici model. diff --git a/python/sdist/amici/petab/simulations.py b/python/sdist/amici/petab/simulations.py index 0f9aae3bfd..77f14ccb35 100644 --- a/python/sdist/amici/petab/simulations.py +++ b/python/sdist/amici/petab/simulations.py @@ -3,9 +3,10 @@ Functionality related to running simulations or evaluating the objective function as defined by a PEtab problem. """ + import copy import logging -from typing import Any, Optional, Union +from typing import Any from collections.abc import Sequence import amici @@ -72,12 +73,12 @@ def simulate_petab( petab_problem: petab.Problem, amici_model: AmiciModel, - solver: Optional[amici.Solver] = None, - problem_parameters: Optional[dict[str, float]] = None, - simulation_conditions: Union[pd.DataFrame, dict] = None, + solver: amici.Solver | None = None, + problem_parameters: dict[str, float] | None = None, + simulation_conditions: pd.DataFrame | dict = None, edatas: list[AmiciExpData] = None, parameter_mapping: ParameterMapping = None, - scaled_parameters: Optional[bool] = False, + scaled_parameters: bool | None = False, log_level: int = logging.WARNING, num_threads: int = 1, failfast: bool = True, @@ -262,11 +263,11 @@ def simulate_petab( def aggregate_sllh( amici_model: AmiciModel, rdatas: Sequence[amici.ReturnDataView], - parameter_mapping: Optional[ParameterMapping], + parameter_mapping: ParameterMapping | None, edatas: list[AmiciExpData], petab_scale: bool = True, petab_problem: petab.Problem = None, -) -> Union[None, dict[str, float]]: +) -> None | dict[str, float]: """ Aggregate likelihood gradient for all conditions, according to PEtab parameter mapping. diff --git a/python/sdist/amici/petab/simulator.py b/python/sdist/amici/petab/simulator.py index a5f50112cc..9c655b1483 100644 --- a/python/sdist/amici/petab/simulator.py +++ b/python/sdist/amici/petab/simulator.py @@ -11,7 +11,7 @@ import inspect import sys -from typing import Callable +from collections.abc import Callable import pandas as pd import petab diff --git a/python/sdist/amici/petab/util.py b/python/sdist/amici/petab/util.py index 742f7bdfe3..e30b829a04 100644 --- a/python/sdist/amici/petab/util.py +++ b/python/sdist/amici/petab/util.py @@ -1,6 +1,7 @@ """Various helper functions for working with PEtab problems.""" + import re -from typing import TYPE_CHECKING, Union +from typing import TYPE_CHECKING import libsbml import pandas as pd @@ -15,9 +16,9 @@ def get_states_in_condition_table( petab_problem: petab.Problem, - condition: Union[dict, pd.Series] = None, + condition: dict | pd.Series = None, return_patterns: bool = False, -) -> dict[str, tuple[Union[float, str, None], Union[float, str, None]]]: +) -> dict[str, tuple[float | str | None, float | str | None]]: """Get states and their initial condition as specified in the condition table. Returns: Dictionary: ``stateId -> (initial condition simulation, initial condition preequilibration)`` diff --git a/python/sdist/amici/petab_import.py b/python/sdist/amici/petab_import.py index d5c67753ac..b81484c1cc 100644 --- a/python/sdist/amici/petab_import.py +++ b/python/sdist/amici/petab_import.py @@ -7,6 +7,7 @@ .. deprecated:: 0.21.0 Use :mod:`amici.petab` instead. """ + import warnings warnings.warn( diff --git a/python/sdist/amici/petab_import_pysb.py b/python/sdist/amici/petab_import_pysb.py index 595018f208..a1597d53b5 100644 --- a/python/sdist/amici/petab_import_pysb.py +++ b/python/sdist/amici/petab_import_pysb.py @@ -4,6 +4,7 @@ .. deprecated:: 0.21.0 Use :mod:`amici.petab.pysb_import` instead. """ + import warnings from .petab.pysb_import import * # noqa: F401, F403 diff --git a/python/sdist/amici/plotting.py b/python/sdist/amici/plotting.py index 19dbe05f89..651ba0e23e 100644 --- a/python/sdist/amici/plotting.py +++ b/python/sdist/amici/plotting.py @@ -3,7 +3,7 @@ -------- Plotting related functions """ -from typing import Optional, Union + from collections.abc import Iterable, Sequence import matplotlib.pyplot as plt @@ -19,8 +19,8 @@ def plot_state_trajectories( rdata: ReturnDataView, - state_indices: Optional[Sequence[int]] = None, - ax: Optional[Axes] = None, + state_indices: Sequence[int] | None = None, + ax: Axes | None = None, model: Model = None, prefer_names: bool = True, marker=None, @@ -76,12 +76,12 @@ def plot_state_trajectories( def plot_observable_trajectories( rdata: ReturnDataView, - observable_indices: Optional[Iterable[int]] = None, - ax: Optional[Axes] = None, + observable_indices: Iterable[int] | None = None, + ax: Axes | None = None, model: Model = None, prefer_names: bool = True, marker=None, - edata: Union[amici.ExpData, amici.ExpDataView] = None, + edata: amici.ExpData | amici.ExpDataView = None, ) -> None: """ Plot observable trajectories. @@ -175,7 +175,7 @@ def plot_jacobian(rdata: ReturnDataView): def plot_expressions( - exprs: Union[Sequence[StrOrExpr], StrOrExpr], rdata: ReturnDataView + exprs: Sequence[StrOrExpr] | StrOrExpr, rdata: ReturnDataView ) -> None: """Plot the given expressions evaluated on the given simulation outputs. diff --git a/python/sdist/amici/pysb_import.py b/python/sdist/amici/pysb_import.py index 94aad595d9..46476a753d 100644 --- a/python/sdist/amici/pysb_import.py +++ b/python/sdist/amici/pysb_import.py @@ -12,10 +12,9 @@ from pathlib import Path from typing import ( Any, - Callable, - Optional, Union, ) +from collections.abc import Callable from collections.abc import Iterable import numpy as np @@ -53,12 +52,12 @@ def pysb2amici( model: pysb.Model, - output_dir: Optional[Union[str, Path]] = None, + output_dir: str | Path | None = None, observables: list[str] = None, constant_parameters: list[str] = None, sigmas: dict[str, str] = None, - noise_distributions: Optional[dict[str, Union[str, Callable]]] = None, - verbose: Union[int, bool] = False, + noise_distributions: dict[str, str | Callable] | None = None, + verbose: int | bool = False, assume_pow_positivity: bool = False, compiler: str = None, compute_conservation_laws: bool = True, @@ -68,7 +67,7 @@ def pysb2amici( # See https://github.com/AMICI-dev/AMICI/pull/1672 cache_simplify: bool = False, generate_sensitivity_code: bool = True, - model_name: Optional[str] = None, + model_name: str | None = None, ): r""" Generate AMICI C++ files for the provided model. @@ -194,13 +193,13 @@ def ode_model_from_pysb_importer( constant_parameters: list[str] = None, observables: list[str] = None, sigmas: dict[str, str] = None, - noise_distributions: Optional[dict[str, Union[str, Callable]]] = None, + noise_distributions: dict[str, str | Callable] | None = None, compute_conservation_laws: bool = True, simplify: Callable = sp.powsimp, # Do not enable by default without testing. # See https://github.com/AMICI-dev/AMICI/pull/1672 cache_simplify: bool = False, - verbose: Union[int, bool] = False, + verbose: int | bool = False, ) -> DEModel: """ Creates an :class:`amici.DEModel` instance from a :class:`pysb.Model` @@ -441,7 +440,7 @@ def _process_pysb_expressions( ode_model: DEModel, observables: list[str], sigmas: dict[str, str], - noise_distributions: Optional[dict[str, Union[str, Callable]]] = None, + noise_distributions: dict[str, str | Callable] | None = None, ) -> None: r""" Converts pysb expressions/observables into Observables (with @@ -506,7 +505,7 @@ def _add_expression( ode_model: DEModel, observables: list[str], sigmas: dict[str, str], - noise_distributions: Optional[dict[str, Union[str, Callable]]] = None, + noise_distributions: dict[str, str | Callable] | None = None, ): """ Adds expressions to the ODE model given and adds observables/sigmas if @@ -621,7 +620,7 @@ def _process_pysb_observables( ode_model: DEModel, observables: list[str], sigmas: dict[str, str], - noise_distributions: Optional[dict[str, Union[str, Callable]]] = None, + noise_distributions: dict[str, str | Callable] | None = None, ) -> None: """ Converts :class:`pysb.core.Observable` into @@ -1349,7 +1348,7 @@ def has_fixed_parameter_ic( def extract_monomers( - complex_patterns: Union[pysb.ComplexPattern, list[pysb.ComplexPattern]], + complex_patterns: pysb.ComplexPattern | list[pysb.ComplexPattern], ) -> list[str]: """ Constructs a list of monomer names contained in complex patterns. @@ -1415,8 +1414,8 @@ def _get_unconserved_monomers( def _get_changed_stoichiometries( - reactants: Union[pysb.ComplexPattern, list[pysb.ComplexPattern]], - products: Union[pysb.ComplexPattern, list[pysb.ComplexPattern]], + reactants: pysb.ComplexPattern | list[pysb.ComplexPattern], + products: pysb.ComplexPattern | list[pysb.ComplexPattern], ) -> set[str]: """ Constructs the set of monomer names which have different @@ -1444,7 +1443,7 @@ def _get_changed_stoichiometries( return changed_stoichiometries -def pysb_model_from_path(pysb_model_file: Union[str, Path]) -> pysb.Model: +def pysb_model_from_path(pysb_model_file: str | Path) -> pysb.Model: """Load a pysb model module and return the :class:`pysb.Model` instance :param pysb_model_file: Full or relative path to the PySB model module diff --git a/python/sdist/amici/sbml_import.py b/python/sdist/amici/sbml_import.py index 8f7f67b02f..40c4881a09 100644 --- a/python/sdist/amici/sbml_import.py +++ b/python/sdist/amici/sbml_import.py @@ -4,6 +4,7 @@ This module provides all necessary functionality to import a model specified in the `Systems Biology Markup Language (SBML) `_. """ + import copy import itertools as itt import logging @@ -15,10 +16,9 @@ from pathlib import Path from typing import ( Any, - Callable, - Optional, Union, ) +from collections.abc import Callable from collections.abc import Iterable, Sequence import libsbml as sbml @@ -134,7 +134,7 @@ class SbmlImporter: def __init__( self, - sbml_source: Union[str, Path, sbml.Model], + sbml_source: str | Path | sbml.Model, show_sbml_warnings: bool = False, from_file: bool = True, discard_annotations: bool = False, @@ -177,7 +177,7 @@ def __init__( # Long and short names for model components self.symbols: dict[SymbolId, dict[sp.Symbol, dict[str, Any]]] = {} - self._local_symbols: dict[str, Union[sp.Expr, sp.Function]] = {} + self._local_symbols: dict[str, sp.Expr | sp.Function] = {} self.compartments: SymbolicFormula = {} self.compartment_assignment_rules: SymbolicFormula = {} self.species_assignment_rules: SymbolicFormula = {} @@ -269,21 +269,21 @@ def _reset_symbols(self) -> None: def sbml2amici( self, model_name: str, - output_dir: Union[str, Path] = None, + output_dir: str | Path = None, observables: dict[str, dict[str, str]] = None, event_observables: dict[str, dict[str, str]] = None, constant_parameters: Iterable[str] = None, - sigmas: dict[str, Union[str, float]] = None, - event_sigmas: dict[str, Union[str, float]] = None, - noise_distributions: dict[str, Union[str, Callable]] = None, - event_noise_distributions: dict[str, Union[str, Callable]] = None, - verbose: Union[int, bool] = logging.ERROR, + sigmas: dict[str, str | float] = None, + event_sigmas: dict[str, str | float] = None, + noise_distributions: dict[str, str | Callable] = None, + event_noise_distributions: dict[str, str | Callable] = None, + verbose: int | bool = logging.ERROR, assume_pow_positivity: bool = False, compiler: str = None, allow_reinit_fixpar_initcond: bool = True, compile: bool = True, compute_conservation_laws: bool = True, - simplify: Optional[Callable] = _default_simplify, + simplify: Callable | None = _default_simplify, cache_simplify: bool = False, log_as_log10: bool = True, generate_sensitivity_code: bool = True, @@ -451,13 +451,13 @@ def _build_ode_model( observables: dict[str, dict[str, str]] = None, event_observables: dict[str, dict[str, str]] = None, constant_parameters: Iterable[str] = None, - sigmas: dict[str, Union[str, float]] = None, - event_sigmas: dict[str, Union[str, float]] = None, - noise_distributions: dict[str, Union[str, Callable]] = None, - event_noise_distributions: dict[str, Union[str, Callable]] = None, - verbose: Union[int, bool] = logging.ERROR, + sigmas: dict[str, str | float] = None, + event_sigmas: dict[str, str | float] = None, + noise_distributions: dict[str, str | Callable] = None, + event_noise_distributions: dict[str, str | Callable] = None, + verbose: int | bool = logging.ERROR, compute_conservation_laws: bool = True, - simplify: Optional[Callable] = _default_simplify, + simplify: Callable | None = _default_simplify, cache_simplify: bool = False, log_as_log10: bool = True, hardcode_symbols: Sequence[str] = None, @@ -1043,7 +1043,7 @@ def add_d_dt( self, d_dt: sp.Expr, variable: sp.Symbol, - variable0: Union[float, sp.Expr], + variable0: float | sp.Expr, name: str, ) -> None: """ @@ -1701,8 +1701,8 @@ def get_empty_bolus_value() -> sp.Float: @log_execution_time("processing SBML observables", logger) def _process_observables( self, - observables: Union[dict[str, dict[str, str]], None], - sigmas: dict[str, Union[str, float]], + observables: dict[str, dict[str, str]] | None, + sigmas: dict[str, str | float], noise_distributions: dict[str, str], ) -> None: """ @@ -1768,7 +1768,7 @@ def _process_observables( def _process_event_observables( self, event_observables: dict[str, dict[str, str]], - event_sigmas: dict[str, Union[str, float]], + event_sigmas: dict[str, str | float], event_noise_distributions: dict[str, str], ) -> None: """ @@ -1884,7 +1884,7 @@ def _generate_default_observables(self): def _process_log_likelihood( self, - sigmas: dict[str, Union[str, float]], + sigmas: dict[str, str | float], noise_distributions: dict[str, str], events: bool = False, event_reg: bool = False, @@ -2036,8 +2036,8 @@ def _process_species_references(self): ) def _make_initial( - self, sym_math: Union[sp.Expr, None, float] - ) -> Union[sp.Expr, None, float]: + self, sym_math: sp.Expr | None | float + ) -> sp.Expr | None | float: """ Transforms an expression to its value at the initial time point by replacing species by their initial values. @@ -2488,7 +2488,7 @@ def _clean_reserved_symbols(self) -> None: def _sympy_from_sbml_math( self, var_or_math: [sbml.SBase, str] - ) -> Union[sp.Expr, float, None]: + ) -> sp.Expr | float | None: """ Sympify Math of SBML variables with all sanity checks and transformations @@ -2541,7 +2541,7 @@ def _sympy_from_sbml_math( def _get_element_initial_assignment( self, element_id: str - ) -> Union[sp.Expr, None]: + ) -> sp.Expr | None: """ Extract value of sbml variable according to its initial assignment @@ -2926,7 +2926,7 @@ def _get_list_of_species_references( ] -def replace_logx(math_str: Union[str, float, None]) -> Union[str, float, None]: +def replace_logx(math_str: str | float | None) -> str | float | None: """ Replace logX(.) by log(., X) since sympy cannot parse the former @@ -2961,7 +2961,7 @@ def _collect_event_assignment_parameter_targets( def _check_unsupported_functions_sbml( - sym: sp.Expr, expression_type: str, full_sym: Optional[sp.Expr] = None + sym: sp.Expr, expression_type: str, full_sym: sp.Expr | None = None ): try: _check_unsupported_functions(sym, expression_type, full_sym) @@ -2979,8 +2979,8 @@ def _parse_special_functions_sbml( def _validate_observables( - observables: Union[dict[str, dict[str, str]], None], - sigmas: dict[str, Union[str, float]], + observables: dict[str, dict[str, str]] | None, + sigmas: dict[str, str | float], noise_distributions: dict[str, str], events: bool = False, ) -> None: diff --git a/python/sdist/amici/setup.template.py b/python/sdist/amici/setup.template.py index 11589564e0..1f2d099110 100644 --- a/python/sdist/amici/setup.template.py +++ b/python/sdist/amici/setup.template.py @@ -1,4 +1,5 @@ """AMICI model package setup""" + import os import sys from pathlib import Path diff --git a/python/sdist/amici/splines.py b/python/sdist/amici/splines.py index ea0cd0e06d..cd500a4570 100644 --- a/python/sdist/amici/splines.py +++ b/python/sdist/amici/splines.py @@ -5,6 +5,7 @@ annotations from/to SBML files and for adding such splines to the AMICI C++ code. """ + from __future__ import annotations from typing import TYPE_CHECKING @@ -13,9 +14,9 @@ from numbers import Real from typing import ( Any, - Callable, Union, ) + from collections.abc import Callable from collections.abc import Sequence from . import sbml_import diff --git a/python/sdist/amici/swig.py b/python/sdist/amici/swig.py index 5ba8017005..e0c518957f 100644 --- a/python/sdist/amici/swig.py +++ b/python/sdist/amici/swig.py @@ -1,4 +1,5 @@ """Functions related to SWIG or SWIG-generated code""" + from __future__ import annotations import ast import contextlib diff --git a/python/sdist/amici/swig_wrappers.py b/python/sdist/amici/swig_wrappers.py index b6023b2c34..3c4df44809 100644 --- a/python/sdist/amici/swig_wrappers.py +++ b/python/sdist/amici/swig_wrappers.py @@ -1,9 +1,10 @@ """Convenience wrappers for the swig interface""" + import logging import sys import warnings from contextlib import contextmanager, suppress -from typing import Any, Optional, Union +from typing import Any import amici import amici.amici as amici_swig @@ -49,7 +50,7 @@ def _capture_cstdout(): def runAmiciSimulation( model: AmiciModel, solver: AmiciSolver, - edata: Optional[AmiciExpData] = None, + edata: AmiciExpData | None = None, ) -> "numpy.ReturnDataView": """ Convenience wrapper around :py:func:`amici.amici.runAmiciSimulation` @@ -139,7 +140,7 @@ def runAmiciSimulations( def readSolverSettingsFromHDF5( - file: str, solver: AmiciSolver, location: Optional[str] = "solverSettings" + file: str, solver: AmiciSolver, location: str | None = "solverSettings" ) -> None: """ Convenience wrapper for :py:func:`amici.readSolverSettingsFromHDF5` @@ -153,8 +154,8 @@ def readSolverSettingsFromHDF5( def writeSolverSettingsToHDF5( solver: AmiciSolver, - file: Union[str, object], - location: Optional[str] = "solverSettings", + file: str | object, + location: str | None = "solverSettings", ) -> None: """ Convenience wrapper for :py:func:`amici.amici.writeSolverSettingsToHDF5` diff --git a/python/sdist/amici/sympy_utils.py b/python/sdist/amici/sympy_utils.py index bc20c49dc4..2c8599ba7e 100644 --- a/python/sdist/amici/sympy_utils.py +++ b/python/sdist/amici/sympy_utils.py @@ -1,7 +1,9 @@ """Functionality for working with sympy objects.""" + import os from itertools import starmap -from typing import Union, Any, Callable +from typing import Any +from collections.abc import Callable import contextlib import sympy as sp import logging @@ -111,9 +113,9 @@ def smart_jacobian( @log_execution_time("running smart_multiply", logger) def smart_multiply( - x: Union[sp.MutableDenseMatrix, sp.MutableSparseMatrix], + x: sp.MutableDenseMatrix | sp.MutableSparseMatrix, y: sp.MutableDenseMatrix, -) -> Union[sp.MutableDenseMatrix, sp.MutableSparseMatrix]: +) -> sp.MutableDenseMatrix | sp.MutableSparseMatrix: """ Wrapper around symbolic multiplication with some additional checks that reduce computation time for large matrices @@ -136,7 +138,7 @@ def smart_multiply( def smart_is_zero_matrix( - x: Union[sp.MutableDenseMatrix, sp.MutableSparseMatrix], + x: sp.MutableDenseMatrix | sp.MutableSparseMatrix, ) -> bool: """A faster implementation of sympy's is_zero_matrix diff --git a/python/sdist/amici/testing.py b/python/sdist/amici/testing.py index 8d4a73fbe1..40b791d0d8 100644 --- a/python/sdist/amici/testing.py +++ b/python/sdist/amici/testing.py @@ -1,4 +1,5 @@ """Test support functions""" + import os import sys from tempfile import TemporaryDirectory diff --git a/python/sdist/pyproject.toml b/python/sdist/pyproject.toml index 6b427e2d16..58d39d4ea7 100644 --- a/python/sdist/pyproject.toml +++ b/python/sdist/pyproject.toml @@ -127,5 +127,7 @@ line-length = 79 [tool.ruff] line-length = 79 -ignore = ["E402", "F403", "F405", "E741"] extend-include = ["*.ipynb"] + +[tool.ruff.lint] +ignore = ["E402", "F403", "F405", "E741"] diff --git a/python/sdist/setup.py b/python/sdist/setup.py index cfc474ebe8..83bf33237a 100755 --- a/python/sdist/setup.py +++ b/python/sdist/setup.py @@ -9,6 +9,7 @@ - swig>=3.0 - Optional: hdf5 libraries and headers """ + import os import sys from pathlib import Path diff --git a/python/tests/conftest.py b/python/tests/conftest.py index 1da7cb31b3..d8d882fcfd 100644 --- a/python/tests/conftest.py +++ b/python/tests/conftest.py @@ -1,4 +1,5 @@ """pytest configuration file""" + import copy import importlib import os diff --git a/python/tests/pysb_test_models/bngwiki_egfr_simple_deletemolecules.py b/python/tests/pysb_test_models/bngwiki_egfr_simple_deletemolecules.py index 1a39d4a846..4c40f7e815 100644 --- a/python/tests/pysb_test_models/bngwiki_egfr_simple_deletemolecules.py +++ b/python/tests/pysb_test_models/bngwiki_egfr_simple_deletemolecules.py @@ -3,7 +3,6 @@ http://bionetgen.org/index.php/Egfr_simple """ - from pysb import * Model() diff --git a/python/tests/splines_utils.py b/python/tests/splines_utils.py index 29024d3b73..6565ac80a4 100644 --- a/python/tests/splines_utils.py +++ b/python/tests/splines_utils.py @@ -8,7 +8,7 @@ import os import uuid from tempfile import mkdtemp -from typing import Any, Optional, Union +from typing import Any from collections.abc import Sequence import amici @@ -45,7 +45,7 @@ def evaluate_spline( def integrate_spline( spline: AbstractSpline, - params: Union[dict, None], + params: dict | None, tt: Sequence[float], initial_value: float = 0, ): @@ -119,12 +119,12 @@ def species_to_index(name) -> int: def create_petab_problem( splines: list[AbstractSpline], params_true: dict, - initial_values: Optional[np.ndarray] = None, + initial_values: np.ndarray | None = None, use_reactions: bool = False, measure_upsample: int = 6, sigma: float = 1.0, t_extrapolate: float = 0.25, - folder: Optional[str] = None, + folder: str | None = None, model_name: str = "test_splines", ): """ @@ -283,9 +283,9 @@ def simulate_splines( params_true, initial_values=None, *, - folder: Optional[str] = None, + folder: str | None = None, keep_temporary: bool = False, - benchmark: Union[bool, int] = False, + benchmark: bool | int = False, rtol: float = 1e-12, atol: float = 1e-12, maxsteps: int = 500_000, @@ -505,8 +505,8 @@ def check_splines( discard_annotations: bool = False, use_adjoint: bool = False, skip_sensitivity: bool = False, - debug: Union[bool, str] = False, - parameter_lists: Optional[Sequence[Sequence[int]]] = None, + debug: bool | str = False, + parameter_lists: Sequence[Sequence[int]] | None = None, llh_rtol: float = 1e-8, sllh_atol: float = 1e-8, x_rtol: float = 1e-11, @@ -515,7 +515,7 @@ def check_splines( w_atol: float = 1e-11, sx_rtol: float = 1e-10, sx_atol: float = 1e-10, - groundtruth: Optional[Union[str, dict[str, Any]]] = None, + groundtruth: str | dict[str, Any] | None = None, **kwargs, ): """ @@ -771,8 +771,8 @@ def check_splines_full( check_piecewise: bool = True, check_forward: bool = True, check_adjoint: bool = True, - folder: Optional[str] = None, - groundtruth: Optional[Union[dict, str]] = "compute", + folder: str | None = None, + groundtruth: dict | str | None = "compute", return_groundtruth: bool = False, **kwargs, ): diff --git a/python/tests/test_conserved_quantities_demartino.py b/python/tests/test_conserved_quantities_demartino.py index ca40946db3..2ed778b85e 100644 --- a/python/tests/test_conserved_quantities_demartino.py +++ b/python/tests/test_conserved_quantities_demartino.py @@ -1,4 +1,5 @@ """Tests for conservation laws / conserved moieties""" + import os from time import perf_counter diff --git a/python/tests/test_edata.py b/python/tests/test_edata.py index 27c67de61e..fab49c160e 100644 --- a/python/tests/test_edata.py +++ b/python/tests/test_edata.py @@ -1,4 +1,5 @@ """Tests related to amici.ExpData via Python""" + import amici import numpy as np from amici.testing import skip_on_valgrind diff --git a/python/tests/test_events.py b/python/tests/test_events.py index 065cdeb126..05eddab59f 100644 --- a/python/tests/test_events.py +++ b/python/tests/test_events.py @@ -1,4 +1,5 @@ """Tests for SBML events, including piecewise expressions.""" + from copy import deepcopy import amici diff --git a/python/tests/test_heavisides.py b/python/tests/test_heavisides.py index c3bea26a0c..26de4f575e 100644 --- a/python/tests/test_heavisides.py +++ b/python/tests/test_heavisides.py @@ -1,4 +1,5 @@ """Tests for SBML events, including piecewise expressions.""" + import numpy as np import pytest from util import ( diff --git a/python/tests/test_petab_simulate.py b/python/tests/test_petab_simulate.py index a8240bff33..e1da7768e3 100644 --- a/python/tests/test_petab_simulate.py +++ b/python/tests/test_petab_simulate.py @@ -1,4 +1,5 @@ """Tests for petab_simulate.py.""" + import tempfile from pathlib import Path diff --git a/python/tests/test_rdata.py b/python/tests/test_rdata.py index 8e0f78655e..80ec3b80e6 100644 --- a/python/tests/test_rdata.py +++ b/python/tests/test_rdata.py @@ -1,4 +1,5 @@ """Test amici.ReturnData(View)-related functionality""" + import amici import numpy as np import pytest diff --git a/python/tests/test_sbml_import.py b/python/tests/test_sbml_import.py index aaab5688cc..f0400396f8 100644 --- a/python/tests/test_sbml_import.py +++ b/python/tests/test_sbml_import.py @@ -1,4 +1,5 @@ """Tests related to amici.sbml_import""" + import os import re from numbers import Number diff --git a/python/tests/util.py b/python/tests/util.py index dde10eb454..58e17137a6 100644 --- a/python/tests/util.py +++ b/python/tests/util.py @@ -1,4 +1,5 @@ """Tests for SBML events, including piecewise expressions.""" + import sys import tempfile from pathlib import Path diff --git a/tests/benchmark-models/evaluate_benchmark.py b/tests/benchmark-models/evaluate_benchmark.py index 0c6e2e4122..0fe80f6625 100644 --- a/tests/benchmark-models/evaluate_benchmark.py +++ b/tests/benchmark-models/evaluate_benchmark.py @@ -3,6 +3,7 @@ """ Aggregate computation times from different benchmarks and plot """ + import os import matplotlib.pyplot as plt diff --git a/tests/benchmark-models/test_petab_benchmark.py b/tests/benchmark-models/test_petab_benchmark.py index 753c88e500..976ff3dc05 100644 --- a/tests/benchmark-models/test_petab_benchmark.py +++ b/tests/benchmark-models/test_petab_benchmark.py @@ -1,4 +1,5 @@ """Tests for simulate_petab on PEtab benchmark problems.""" + import os from pathlib import Path diff --git a/tests/benchmark-models/test_petab_model.py b/tests/benchmark-models/test_petab_model.py index 8f52a341c4..da3d37b5fb 100755 --- a/tests/benchmark-models/test_petab_model.py +++ b/tests/benchmark-models/test_petab_model.py @@ -3,6 +3,7 @@ """ Simulate a PEtab problem and compare results to reference values """ + import argparse import contextlib import importlib From f6be02cecaa1655bdc5d6792c9a42a7969209e6b Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Tue, 23 Apr 2024 10:56:44 +0200 Subject: [PATCH 098/188] .git-blame-ignore-revs --- .git-blame-ignore-revs | 1 + 1 file changed, 1 insertion(+) create mode 100644 .git-blame-ignore-revs diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000000..87c86463fa --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1 @@ +f59fed72eb4d0cedf6abf2a96fe95087ce61478a From b2aeae004faac8c18cfadfd59c1e309302c81816 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Tue, 23 Apr 2024 16:23:03 +0200 Subject: [PATCH 099/188] Deploy releases to Docker Hub (#2413) Deploy releases (actual releases, not only release branches) to Docker Hub. Closes #2383 --- .github/workflows/deploy_protected.yml | 4 ++-- .github/workflows/deploy_release.yml | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy_protected.yml b/.github/workflows/deploy_protected.yml index 7f787e56c3..e98209c97d 100644 --- a/.github/workflows/deploy_protected.yml +++ b/.github/workflows/deploy_protected.yml @@ -29,7 +29,7 @@ jobs: fi dockerhub: - name: Deploy Dockerhub + name: Deploy Docker Hub needs: [check-secret] if: needs.check-secret.outputs.secrets-defined == 'true' runs-on: ubuntu-22.04 @@ -50,7 +50,7 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Publish to Registry - uses: elgohr/Publish-Docker-Github-Action@v4 + uses: elgohr/Publish-Docker-Github-Action@v5 with: name: dweindl/amici username: ${{ secrets.DOCKER_USERNAME }} diff --git a/.github/workflows/deploy_release.yml b/.github/workflows/deploy_release.yml index ebb83ce2a9..b18c31ff2a 100644 --- a/.github/workflows/deploy_release.yml +++ b/.github/workflows/deploy_release.yml @@ -74,3 +74,29 @@ jobs: -H "Accept: application/vnd.github.v3+json" \ https://api.github.com/repos/${DOWNSTREAM_REPOSITORY}/actions/workflows/${WORKFLOW_FILE}/dispatches \ -d "{\"ref\": \"dev\", \"inputs\": {\"simulatorVersion\": \"${PACKAGE_VERSION}\", \"simulatorVersionLatest\": \"true\"}}" + + dockerhub: + name: Release to Docker Hub + runs-on: ubuntu-22.04 + + steps: + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + - uses: actions/checkout@v4 + - run: git archive -o container/amici.tar.gz --format=tar.gz HEAD + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Publish to Registry + uses: elgohr/Publish-Docker-Github-Action@v5 + with: + name: dweindl/amici + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + workdir: container/ + dockerfile: Dockerfile + tag_semver: true + platforms: linux/amd64,linux/arm64 From a1d14513a976abaa4d6da6e24022e844d69d28c0 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Tue, 23 Apr 2024 18:19:07 +0200 Subject: [PATCH 100/188] zip(... , strict=True) (#2412) Closes #2410. --- .../amici/conserved_quantities_demartino.py | 8 +++--- python/sdist/amici/cxxcodeprinter.py | 10 +++++-- python/sdist/amici/de_export.py | 6 ++-- python/sdist/amici/de_model.py | 23 +++++++++++---- python/sdist/amici/pandas.py | 6 ++-- python/sdist/amici/petab/conditions.py | 4 ++- python/sdist/amici/petab/parameter_mapping.py | 2 +- python/sdist/amici/petab/pysb_import.py | 1 + python/sdist/amici/petab/simulations.py | 7 +++-- python/sdist/amici/plotting.py | 4 +-- python/sdist/amici/pysb_import.py | 6 +++- python/sdist/amici/sbml_import.py | 28 +++++++++++++++---- python/sdist/amici/splines.py | 8 ++++-- python/sdist/amici/sympy_utils.py | 6 +++- python/tests/petab/test_petab_problem.py | 2 +- python/tests/splines_utils.py | 12 ++++---- python/tests/test_petab_objective.py | 5 +--- python/tests/test_splines.py | 2 +- python/tests/test_splines_python.py | 7 +++-- python/tests/test_splines_short.py | 2 +- python/tests/util.py | 2 +- 21 files changed, 100 insertions(+), 51 deletions(-) diff --git a/python/sdist/amici/conserved_quantities_demartino.py b/python/sdist/amici/conserved_quantities_demartino.py index 43c84fdf5a..cf651241d1 100644 --- a/python/sdist/amici/conserved_quantities_demartino.py +++ b/python/sdist/amici/conserved_quantities_demartino.py @@ -139,7 +139,7 @@ def log(*args, **kwargs): # print all conserved quantities if verbose: for i, (coefficients, engaged_species_idxs) in enumerate( - zip(species_coefficients, species_indices) + zip(species_coefficients, species_indices, strict=True) ): if not engaged_species_idxs: continue @@ -148,7 +148,7 @@ def log(*args, **kwargs): "species:" ) for species_idx, coefficient in zip( - engaged_species_idxs, coefficients + engaged_species_idxs, coefficients, strict=True ): name = ( species_names[species_idx] @@ -957,12 +957,12 @@ def _reduce( k2 = order[j] column: list[float] = [0] * num_species for species_idx, coefficient in zip( - cls_species_idxs[k1], cls_coefficients[k1] + cls_species_idxs[k1], cls_coefficients[k1], strict=True ): column[species_idx] = coefficient ok1 = True for species_idx, coefficient in zip( - cls_species_idxs[k2], cls_coefficients[k2] + cls_species_idxs[k2], cls_coefficients[k2], strict=True ): column[species_idx] -= coefficient if column[species_idx] < -_MIN: diff --git a/python/sdist/amici/cxxcodeprinter.py b/python/sdist/amici/cxxcodeprinter.py index d9de0ca8f8..69060eb00a 100644 --- a/python/sdist/amici/cxxcodeprinter.py +++ b/python/sdist/amici/cxxcodeprinter.py @@ -179,7 +179,9 @@ def format_regular_line(symbol, math, index): # we need toposort to handle the dependencies of extracted # subexpressions expr_dict = dict( - itertools.chain(zip(symbols, reduced_exprs), replacements) + itertools.chain( + zip(symbols, reduced_exprs, strict=True), replacements + ) ) sorted_symbols = toposort( { @@ -192,7 +194,7 @@ def format_regular_line(symbol, math, index): } ) symbol_to_idx = { - sym: idx for idx, sym in zip(indices, symbols) + sym: idx for idx, sym in zip(indices, symbols, strict=True) } def format_line(symbol: sp.Symbol): @@ -217,7 +219,9 @@ def format_line(symbol: sp.Symbol): return [ format_regular_line(sym, math, index) - for index, sym, math in zip(indices, symbols, equations) + for index, sym, math in zip( + indices, symbols, equations, strict=True + ) if math not in [0, 0.0] ] diff --git a/python/sdist/amici/de_export.py b/python/sdist/amici/de_export.py index 147ae3d381..6b1392a3d1 100644 --- a/python/sdist/amici/de_export.py +++ b/python/sdist/amici/de_export.py @@ -717,7 +717,9 @@ def _get_function_body( for ipar in range(self.model.num_par()): expressions = [] for index, formula in zip( - self.model._x0_fixedParameters_idx, equations[:, ipar] + self.model._x0_fixedParameters_idx, + equations[:, ipar], + strict=True, ): if not formula.is_zero: expressions.extend( @@ -735,7 +737,7 @@ def _get_function_body( elif function == "x0_fixedParameters": for index, formula in zip( - self.model._x0_fixedParameters_idx, equations + self.model._x0_fixedParameters_idx, equations, strict=True ): lines.append( f" if(std::find(reinitialization_state_idxs.cbegin(), " diff --git a/python/sdist/amici/de_model.py b/python/sdist/amici/de_model.py index f0b9c6cbb0..0caac635a7 100644 --- a/python/sdist/amici/de_model.py +++ b/python/sdist/amici/de_model.py @@ -396,7 +396,9 @@ def states(self) -> list[State]: def _process_sbml_rate_of(self) -> None: """Substitute any SBML-rateOf constructs in the model equations""" rate_of_func = sp.core.function.UndefinedFunction("rateOf") - species_sym_to_xdot = dict(zip(self.sym("x"), self.sym("xdot"))) + species_sym_to_xdot = dict( + zip(self.sym("x"), self.sym("xdot"), strict=True) + ) species_sym_to_idx = {x: i for i, x in enumerate(self.sym("x"))} def get_rate(symbol: sp.Symbol): @@ -427,7 +429,7 @@ def get_rate(symbol: sp.Symbol): if made_substitutions: # substitute in topological order subs = toposort_symbols( - dict(zip(self.sym("xdot"), self.eq("xdot"))) + dict(zip(self.sym("xdot"), self.eq("xdot"), strict=True)) ) self._eqs["xdot"] = smart_subs_dict(self.eq("xdot"), subs) @@ -469,6 +471,7 @@ def get_rate(symbol: sp.Symbol): zip( self.sym("w")[self.num_cons_law() :, :], self.eq("w")[self.num_cons_law() :, :], + strict=True, ) ) ) @@ -477,6 +480,7 @@ def get_rate(symbol: sp.Symbol): zip( self.sym("w")[: self.num_cons_law(), :], self.eq("w")[: self.num_cons_law(), :], + strict=True, ) ) | w_sorted @@ -1027,7 +1031,9 @@ def static_indices(self, name: str) -> list[int]: # of non-zeros entries of the sparse matrix self._static_indices[name] = [ i - for i, (expr, row_idx) in enumerate(zip(sparseeq, rowvals)) + for i, (expr, row_idx) in enumerate( + zip(sparseeq, rowvals, strict=True) + ) # derivative of a static expression is static if row_idx in static_indices_w # constant expressions @@ -1396,6 +1402,8 @@ def _compute_equation(self, name: str) -> None: if not s.has_conservation_law() ), self.sym("dx"), + # dx contains extra elements for algebraic states + strict=False, ) ] + [eq.get_val() for eq in self._algebraic_equations] @@ -1556,7 +1564,9 @@ def _compute_equation(self, name: str) -> None: # backsubstitution of optimized right-hand side terms into RHS # calling subs() is costly. Due to looping over events though, the # following lines are only evaluated if a model has events - w_sorted = toposort_symbols(dict(zip(self.sym("w"), self.eq("w")))) + w_sorted = toposort_symbols( + dict(zip(self.sym("w"), self.eq("w"), strict=True)) + ) tmp_xdot = smart_subs_dict(self.eq("xdot"), w_sorted) self._eqs[name] = self.eq("drootdt") if self.num_states_solver(): @@ -1586,7 +1596,7 @@ def _compute_equation(self, name: str) -> None: for event_obs in self._event_observables ] for (iz, ie), event_obs in zip( - enumerate(z2event), self._event_observables + enumerate(z2event), self._event_observables, strict=True ): event_observables[ie - 1][iz] = event_obs.get_val() @@ -1727,7 +1737,7 @@ def _compute_equation(self, name: str) -> None: syms_x = self.sym("x") syms_yz = self.sym(name.removeprefix("sigma")) xs_in_sigma = {} - for sym_yz, eq_yz in zip(syms_yz, self._eqs[name]): + for sym_yz, eq_yz in zip(syms_yz, self._eqs[name], strict=True): yz_free_syms = eq_yz.free_symbols if tmp := {x for x in syms_x if x in yz_free_syms}: xs_in_sigma[sym_yz] = tmp @@ -2229,6 +2239,7 @@ def _collect_heaviside_roots( zip( [expr.get_id() for expr in self._expressions], [expr.get_val() for expr in self._expressions], + strict=True, ) ) ) diff --git a/python/sdist/amici/pandas.py b/python/sdist/amici/pandas.py index b61cfa0c79..e83e524e08 100644 --- a/python/sdist/amici/pandas.py +++ b/python/sdist/amici/pandas.py @@ -155,7 +155,7 @@ def getSimulationObservablesAsDataFrame( # aggregate records dicts = [] - for edata, rdata in zip(edata_list, rdata_list): + for edata, rdata in zip(edata_list, rdata_list, strict=True): for i_time, timepoint in enumerate(rdata["t"]): datadict = { "time": timepoint, @@ -212,7 +212,7 @@ def getSimulationStatesAsDataFrame( # aggregate records dicts = [] - for edata, rdata in zip(edata_list, rdata_list): + for edata, rdata in zip(edata_list, rdata_list, strict=True): for i_time, timepoint in enumerate(rdata["t"]): datadict = { "time": timepoint, @@ -268,7 +268,7 @@ def get_expressions_as_dataframe( # aggregate records dicts = [] - for edata, rdata in zip(edata_list, rdata_list): + for edata, rdata in zip(edata_list, rdata_list, strict=True): for i_time, timepoint in enumerate(rdata["t"]): datadict = { "time": timepoint, diff --git a/python/sdist/amici/petab/conditions.py b/python/sdist/amici/petab/conditions.py index 61b8679765..831420f68e 100644 --- a/python/sdist/amici/petab/conditions.py +++ b/python/sdist/amici/petab/conditions.py @@ -69,7 +69,9 @@ def fill_in_parameters( RuntimeWarning, ) - for edata, mapping_for_condition in zip(edatas, parameter_mapping): + for edata, mapping_for_condition in zip( + edatas, parameter_mapping, strict=True + ): fill_in_parameters_for_condition( edata, problem_parameters, diff --git a/python/sdist/amici/petab/parameter_mapping.py b/python/sdist/amici/petab/parameter_mapping.py index 369ad13a96..54930073da 100644 --- a/python/sdist/amici/petab/parameter_mapping.py +++ b/python/sdist/amici/petab/parameter_mapping.py @@ -385,7 +385,7 @@ def create_parameter_mapping( parameter_mapping = ParameterMapping() for (_, condition), prelim_mapping_for_condition in zip( - simulation_conditions.iterrows(), prelim_parameter_mapping + simulation_conditions.iterrows(), prelim_parameter_mapping, strict=True ): mapping_for_condition = create_parameter_mapping_for_condition( prelim_mapping_for_condition, condition, petab_problem, amici_model diff --git a/python/sdist/amici/petab/pysb_import.py b/python/sdist/amici/petab/pysb_import.py index b770603935..f5b1c84dbf 100644 --- a/python/sdist/amici/petab/pysb_import.py +++ b/python/sdist/amici/petab/pysb_import.py @@ -55,6 +55,7 @@ def _add_observation_model( petab_problem.observable_df.index, petab_problem.observable_df[OBSERVABLE_FORMULA], petab_problem.observable_df[NOISE_FORMULA], + strict=True, ): obs_symbol = sp.sympify(observable_formula, locals=local_syms) if observable_id in pysb_model.expressions.keys(): diff --git a/python/sdist/amici/petab/simulations.py b/python/sdist/amici/petab/simulations.py index 77f14ccb35..e80e40a94b 100644 --- a/python/sdist/amici/petab/simulations.py +++ b/python/sdist/amici/petab/simulations.py @@ -172,6 +172,7 @@ def simulate_petab( zip( petab_problem.x_ids, petab_problem.x_nominal_scaled, + strict=True, ) ) # depending on `fill_fixed_parameters` for parameter mapping, the @@ -311,7 +312,7 @@ def aggregate_sllh( ) for condition_parameter_mapping, edata, rdata in zip( - parameter_mapping, edatas, rdatas + parameter_mapping, edatas, rdatas, strict=True ): for sllh_parameter_index, condition_parameter_sllh in enumerate( rdata.sllh @@ -433,7 +434,9 @@ def rdatas_to_measurement_df( observable_ids = model.getObservableIds() rows = [] # iterate over conditions - for (_, condition), rdata in zip(simulation_conditions.iterrows(), rdatas): + for (_, condition), rdata in zip( + simulation_conditions.iterrows(), rdatas, strict=True + ): # current simulation matrix y = rdata.y # time array used in rdata diff --git a/python/sdist/amici/plotting.py b/python/sdist/amici/plotting.py index 651ba0e23e..3067d26bc5 100644 --- a/python/sdist/amici/plotting.py +++ b/python/sdist/amici/plotting.py @@ -65,7 +65,7 @@ def plot_state_trajectories( else: labels = np.asarray(rdata.ptr.state_ids)[list(state_indices)] - for ix, label in zip(state_indices, labels): + for ix, label in zip(state_indices, labels, strict=True): ax.plot(rdata["t"], rdata["x"][:, ix], marker=marker, label=label) ax.set_xlabel("$t$") @@ -131,7 +131,7 @@ def plot_observable_trajectories( else: labels = np.asarray(rdata.ptr.observable_ids)[list(observable_indices)] - for iy, label in zip(observable_indices, labels): + for iy, label in zip(observable_indices, labels, strict=True): (l,) = ax.plot( rdata["t"], rdata["y"][:, iy], marker=marker, label=label ) diff --git a/python/sdist/amici/pysb_import.py b/python/sdist/amici/pysb_import.py index 46476a753d..1a21fef1ca 100644 --- a/python/sdist/amici/pysb_import.py +++ b/python/sdist/amici/pysb_import.py @@ -563,7 +563,11 @@ def _add_expression( cost_fun_expr = sp.sympify( cost_fun_str, locals=dict( - zip(_get_str_symbol_identifiers(name), (y, my, sigma)) + zip( + _get_str_symbol_identifiers(name), + (y, my, sigma), + strict=True, + ) ), ) ode_model.add_component( diff --git a/python/sdist/amici/sbml_import.py b/python/sdist/amici/sbml_import.py index 40c4881a09..843b954a8f 100644 --- a/python/sdist/amici/sbml_import.py +++ b/python/sdist/amici/sbml_import.py @@ -556,9 +556,18 @@ def _build_ode_model( dxdt = smart_multiply( self.stoichiometric_matrix, MutableDenseMatrix(fluxes) ) + # dxdt has algebraic states at the end + assert dxdt.shape[0] - len(self.symbols[SymbolId.SPECIES]) == len( + self.symbols.get(SymbolId.ALGEBRAIC_STATE, []) + ), ( + self.symbols[SymbolId.SPECIES], + dxdt, + self.symbols[SymbolId.SPECIES], + ) + # correct time derivatives for compartment changes for ix, ((species_id, species), formula) in enumerate( - zip(self.symbols[SymbolId.SPECIES].items(), dxdt) + zip(self.symbols[SymbolId.SPECIES].items(), dxdt, strict=False) ): # rate rules and amount species don't need to be updated if "dt" in species: @@ -604,7 +613,7 @@ def _build_ode_model( # add fluxes as expressions, this needs to happen after base # expressions from symbols have been parsed - for flux_id, flux in zip(fluxes, self.flux_vector): + for flux_id, flux in zip(fluxes, self.flux_vector, strict=True): # replace splines inside fluxes flux = flux.subs(spline_subs) ode_model.add_component( @@ -981,7 +990,9 @@ def _process_species_initial(self): self.symbols[SymbolId.SPECIES], "init" ) for species, rateof_dummies in zip( - self.symbols[SymbolId.SPECIES].values(), all_rateof_dummies + self.symbols[SymbolId.SPECIES].values(), + all_rateof_dummies, + strict=True, ): species["init"] = _dummy_to_rateof( smart_subs_dict(species["init"], sorted_species, "init"), @@ -1945,6 +1956,7 @@ def _process_log_likelihood( for (obs_id, obs), (sigma_id, sigma) in zip( self.symbols[obs_symbol].items(), self.symbols[sigma_symbol].items(), + strict=True, ): symbol = symbol_with_assumptions(f"J{obs_id}") dist = noise_distributions.get(str(obs_id), "normal") @@ -1955,6 +1967,7 @@ def _process_log_likelihood( zip( _get_str_symbol_identifiers(obs_id), (obs_id, obs["measurement_symbol"], sigma_id), + strict=True, ) ), ) @@ -2173,9 +2186,9 @@ def _get_conservation_laws_demartino( len(cls_coefficients), len(ode_model._differential_states) ) for i_cl, (cl, coefficients) in enumerate( - zip(cls_state_idxs, cls_coefficients) + zip(cls_state_idxs, cls_coefficients, strict=True) ): - for i, c in zip(cl, coefficients): + for i, c in zip(cl, coefficients, strict=True): A[i_cl, i] = sp.Rational(c) rref, pivots = A.rref() @@ -2319,7 +2332,10 @@ def _add_conservation_for_non_constant_species( "coefficients": { state_id: coeff * compartment for state_id, coeff, compartment in zip( - state_ids, coefficients, compartment_sizes + state_ids, + coefficients, + compartment_sizes, + strict=True, ) }, } diff --git a/python/sdist/amici/splines.py b/python/sdist/amici/splines.py index cd500a4570..5d423a19e5 100644 --- a/python/sdist/amici/splines.py +++ b/python/sdist/amici/splines.py @@ -594,7 +594,9 @@ def check_if_valid(self, importer: sbml_import.SbmlImporter) -> None: importer.symbols[SymbolId.FIXED_PARAMETER][fp]["value"] for fp in fixed_parameters ] - subs = dict(zip(fixed_parameters, fixed_parameters_values)) + subs = dict( + zip(fixed_parameters, fixed_parameters_values, strict=True) + ) nodes_values = [sp.simplify(x.subs(subs)) for x in self.nodes] for x in nodes_values: assert x.is_Number @@ -1093,7 +1095,7 @@ def add_to_sbml_model( # It makes no sense to give a single nominal value: # grid values must all be different raise TypeError("x_nominal must be a Sequence!") - for _x, _val in zip(self.nodes, x_nominal): + for _x, _val in zip(self.nodes, x_nominal, strict=True): if _x.is_Symbol and not model.getParameter(_x.name): add_parameter( model, _x.name, value=_val, units=x_units @@ -1116,7 +1118,7 @@ def add_to_sbml_model( else: y_constant = len(self.values_at_nodes) * [y_constant] for _y, _val, _const in zip( - self.values_at_nodes, y_nominal, y_constant + self.values_at_nodes, y_nominal, y_constant, strict=True ): if _y.is_Symbol and not model.getParameter(_y.name): add_parameter( diff --git a/python/sdist/amici/sympy_utils.py b/python/sdist/amici/sympy_utils.py index 2c8599ba7e..9794fadef0 100644 --- a/python/sdist/amici/sympy_utils.py +++ b/python/sdist/amici/sympy_utils.py @@ -184,7 +184,11 @@ def _parallel_applyfunc(obj: sp.Matrix, func: Callable) -> sp.Matrix: elif isinstance(obj, sp.SparseMatrix): dok = obj.todok() mapped = p.map(func, dok.values()) - dok = {k: v for k, v in zip(dok.keys(), mapped) if v != 0} + dok = { + k: v + for k, v in zip(dok.keys(), mapped, strict=True) + if v != 0 + } return obj._new(obj.rows, obj.cols, dok) else: raise ValueError(f"Unsupported matrix type {type(obj)}") diff --git a/python/tests/petab/test_petab_problem.py b/python/tests/petab/test_petab_problem.py index 5a8a299bb9..736d895f8f 100644 --- a/python/tests/petab/test_petab_problem.py +++ b/python/tests/petab/test_petab_problem.py @@ -81,7 +81,7 @@ def test_amici_petab_problem_pregenerate_equals_on_demand(): app_store_false.set_parameters(parameter_update, scaled_parameters=True) for edata_store_true, edata_store_false in zip( - app_store_true.get_edatas(), app_store_false.get_edatas() + app_store_true.get_edatas(), app_store_false.get_edatas(), strict=True ): assert edata_store_true is not edata_store_false assert edata_store_true == edata_store_false diff --git a/python/tests/splines_utils.py b/python/tests/splines_utils.py index 6565ac80a4..99c0da880c 100644 --- a/python/tests/splines_utils.py +++ b/python/tests/splines_utils.py @@ -216,7 +216,7 @@ def create_petab_problem( zz_true = np.array( [ integrate_spline(spline, params_true, tt_obs, iv) - for (spline, iv) in zip(splines, initial_values) + for (spline, iv) in zip(splines, initial_values, strict=True) ], dtype=float, ) @@ -480,7 +480,7 @@ def compute_ground_truth( x_true_sym = sp.Matrix( [ integrate_spline(spline, None, times, iv) - for (spline, iv) in zip(splines, initial_values) + for (spline, iv) in zip(splines, initial_values, strict=True) ] ).transpose() groundtruth = { @@ -604,7 +604,7 @@ def param_by_name(id): x_true_sym = sp.Matrix( [ integrate_spline(spline, None, tt, iv) - for (spline, iv) in zip(splines, initial_values) + for (spline, iv) in zip(splines, initial_values, strict=True) ] ).transpose() x_true = np.asarray(x_true_sym.subs(params_true), dtype=float) @@ -896,7 +896,7 @@ def example_spline_1( yy = list(sp.symbols(f"y{idx}_0:{len(yy_true)}")) if fixed_values is None: - params = dict(zip(yy, yy_true)) + params = dict(zip(yy, yy_true, strict=True)) elif fixed_values == "all": params = {} for i in range(len(yy_true)): @@ -939,7 +939,7 @@ def example_spline_2(idx: int = 0): xx = UniformGrid(0, 25, number_of_nodes=len(yy_true)) yy = list(sp.symbols(f"y{idx}_0:{len(yy_true) - 1}")) yy.append(yy[0]) - params = dict(zip(yy, yy_true)) + params = dict(zip(yy, yy_true, strict=True)) spline = CubicHermiteSpline( f"y{idx}", nodes=xx, @@ -960,7 +960,7 @@ def example_spline_3(idx: int = 0): yy_true = [0.0, 2.0, 5.0, 6.0, 5.0, 4.0, 2.0, 3.0, 4.0, 6.0] xx = UniformGrid(0, 25, number_of_nodes=len(yy_true)) yy = list(sp.symbols(f"y{idx}_0:{len(yy_true)}")) - params = dict(zip(yy, yy_true)) + params = dict(zip(yy, yy_true, strict=True)) spline = CubicHermiteSpline( f"y{idx}", nodes=xx, diff --git a/python/tests/test_petab_objective.py b/python/tests/test_petab_objective.py index 5d29ad88ff..07770ac413 100755 --- a/python/tests/test_petab_objective.py +++ b/python/tests/test_petab_objective.py @@ -40,10 +40,7 @@ def test_simulate_petab_sensitivities(lotka_volterra): amici_solver.setMaxSteps(int(1e5)) problem_parameters = dict( - zip( - petab_problem.x_ids, - petab_problem.x_nominal, - ) + zip(petab_problem.x_ids, petab_problem.x_nominal, strict=True) ) results = {} diff --git a/python/tests/test_splines.py b/python/tests/test_splines.py index a7fe01e84a..66104b824b 100644 --- a/python/tests/test_splines.py +++ b/python/tests/test_splines.py @@ -62,7 +62,7 @@ def test_multiple_splines(**kwargs): tols = [] for t0, t1, t2, t3, t4, t5 in zip( - tols0, tols1, tols2, tols3, tols4, tols5 + tols0, tols1, tols2, tols3, tols4, tols5, strict=True ): keys = set().union( t0.keys(), t1.keys(), t2.keys(), t3.keys(), t4.keys(), t5.keys() diff --git a/python/tests/test_splines_python.py b/python/tests/test_splines_python.py index 4c4de5ccfc..539fb1dd4d 100644 --- a/python/tests/test_splines_python.py +++ b/python/tests/test_splines_python.py @@ -215,8 +215,11 @@ def test_SplineNonUniformPeriodicExtrapolation(): @skip_on_valgrind def check_gradient(spline, t, params, params_values, expected, rel_tol=1e-9): value = spline.evaluate(t) - subs = {pname: pvalue for (pname, pvalue) in zip(params, params_values)} - for p, exp in zip(params, expected): + subs = { + pname: pvalue + for (pname, pvalue) in zip(params, params_values, strict=True) + } + for p, exp in zip(params, expected, strict=True): assert math.isclose( float(value.diff(p).subs(subs)), exp, rel_tol=rel_tol ) diff --git a/python/tests/test_splines_short.py b/python/tests/test_splines_short.py index 59e54a3279..43b0ccd294 100644 --- a/python/tests/test_splines_short.py +++ b/python/tests/test_splines_short.py @@ -44,7 +44,7 @@ def test_two_splines(**kwargs): tols1 = (tols1, tols1, tols1) tols = [] - for t0, t1 in zip(tols0, tols1): + for t0, t1 in zip(tols0, tols1, strict=True): keys = set().union(t0.keys(), t1.keys()) t = { key: max( diff --git a/python/tests/util.py b/python/tests/util.py index 58e17137a6..b450501a81 100644 --- a/python/tests/util.py +++ b/python/tests/util.py @@ -109,7 +109,7 @@ def create_event_assignment(target, assignment): if isinstance(event_def["target"], list): for event_target, event_assignment in zip( - event_def["target"], event_def["assignment"] + event_def["target"], event_def["assignment"], strict=True ): create_event_assignment(event_target, event_assignment) From ba5479df035fba66ee903848874dfc77f5df5421 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Mon, 29 Apr 2024 09:56:19 +0200 Subject: [PATCH 101/188] Remove stray cmake message --- swig/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/swig/CMakeLists.txt b/swig/CMakeLists.txt index 73faccda7e..2b4fabadb0 100644 --- a/swig/CMakeLists.txt +++ b/swig/CMakeLists.txt @@ -122,7 +122,6 @@ if(WIN32) POST_BUILD COMMAND dumpbin "/DEPENDENTS" "$" COMMENT "Dumping extension dependencies.") - message("Dependencies: ${dump}") endif() install(FILES ${CMAKE_CURRENT_BINARY_DIR}/amici.py $ From 190f083d221716842e2f5f371b9f70ce114fac63 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Thu, 2 May 2024 08:52:42 +0200 Subject: [PATCH 102/188] GHA: Let dependabot update actions (#2416) --- .github/dependabot.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..d3e0189bdb --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-patch", "version-update:semver-minor"] From 80c8cfafb96e211bfffa0d56829d5084f97a7b58 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Thu, 2 May 2024 10:19:09 +0200 Subject: [PATCH 103/188] Increase test tolerances (#2419) Fix test failures related to switching from macos-12 to macos-14-arm64. Fixes #2415. --- python/tests/splines_utils.py | 13 ++++--------- python/tests/test_sbml_import.py | 2 +- tests/cpp/steadystate/tests1.cpp | 4 ++-- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/python/tests/splines_utils.py b/python/tests/splines_utils.py index 99c0da880c..19fd84eee6 100644 --- a/python/tests/splines_utils.py +++ b/python/tests/splines_utils.py @@ -6,6 +6,7 @@ import math import os +import platform import uuid from tempfile import mkdtemp from typing import Any @@ -716,13 +717,7 @@ def param_by_name(id): if sllh_atol is None: sllh_atol = np.finfo(float).eps sllh_err_abs = abs(sllh).max() - if ( - sllh_err_abs > sllh_atol and debug is not True - ) or debug == "print": - print(f"sllh_atol={sllh_atol}") - print(f"sllh_err_abs = {sllh_err_abs}") - if not debug: - assert sllh_err_abs <= sllh_atol + assert sllh_err_abs <= sllh_atol, f"{sllh_err_abs=} {sllh_atol=}" else: assert sllh is None @@ -917,11 +912,11 @@ def example_spline_1( extrapolate=extrapolate, ) - if os.name == "nt": + if os.name == "nt" or platform.system() == "Darwin": tols = ( dict(llh_rtol=1e-15, x_rtol=1e-8, x_atol=1e-7), dict(llh_rtol=1e-15, x_rtol=1e-8, x_atol=1e-7), - dict(llh_rtol=1e-15, sllh_atol=5e-8, x_rtol=1e-8, x_atol=1e-7), + dict(llh_rtol=1e-15, sllh_atol=5e-7, x_rtol=1e-8, x_atol=1e-7), ) else: tols = ( diff --git a/python/tests/test_sbml_import.py b/python/tests/test_sbml_import.py index f0400396f8..4936a3c901 100644 --- a/python/tests/test_sbml_import.py +++ b/python/tests/test_sbml_import.py @@ -759,7 +759,7 @@ def test_constraints(): # in practice assert np.any(rdata.x < 0) - amici_solver.setRelativeTolerance(1e-14) + amici_solver.setRelativeTolerance(1e-13) amici_solver.setConstraints( [Constraint.non_negative, Constraint.non_negative] ) diff --git a/tests/cpp/steadystate/tests1.cpp b/tests/cpp/steadystate/tests1.cpp index e939a84ff2..109cc9eecb 100644 --- a/tests/cpp/steadystate/tests1.cpp +++ b/tests/cpp/steadystate/tests1.cpp @@ -1,7 +1,6 @@ #include "testfunctions.h" #include "wrapfunctions.h" -#include #include @@ -175,7 +174,8 @@ TEST(ExampleSteadystate, SensitivityForwardErrorNewt) TEST(ExampleSteadystate, SensitivityForwardDense) { - amici::simulateVerifyWrite("/model_steadystate/sensiforwarddense/"); + amici::simulateVerifyWrite("/model_steadystate/sensiforwarddense/", + 1e-9, TEST_RTOL); } TEST(ExampleSteadystate, SensiFwdNewtonPreeq) From 304c23a2d2120535106dca1999d225f819851b1f Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Thu, 2 May 2024 10:19:52 +0200 Subject: [PATCH 104/188] Remove setup.cfg (#2420) Remove setup.cfg, originally removed in #2408, accidentally re-added in #2391 --- python/sdist/setup.cfg | 91 ------------------------------------------ 1 file changed, 91 deletions(-) delete mode 100644 python/sdist/setup.cfg diff --git a/python/sdist/setup.cfg b/python/sdist/setup.cfg deleted file mode 100644 index 7b8f7b32e7..0000000000 --- a/python/sdist/setup.cfg +++ /dev/null @@ -1,91 +0,0 @@ -[metadata] -name = amici -description = Advanced multi-language Interface to CVODES and IDAS -version = file: amici/version.txt -license = BSD 3-Clause License -url = https://github.com/AMICI-dev/AMICI -keywords = differential equations, simulation, ode, cvodes, systems biology, sensitivity analysis, sbml, pysb, petab -author = Fabian Froehlich, Jan Hasenauer, Daniel Weindl and Paul Stapor -author_email = fabian_froehlich@hms.harvard.edu -project_urls = - Bug Reports = https://github.com/AMICI-dev/AMICI/issues - Source = https://github.com/AMICI-dev/AMICI - Documentation = https://amici.readthedocs.io/en/latest/ -classifiers = - Development Status :: 5 - Production/Stable - Intended Audience :: Science/Research - License :: OSI Approved :: BSD License - Operating System :: POSIX :: Linux - Operating System :: MacOS :: MacOS X - Programming Language :: Python - Programming Language :: C++ - Topic :: Scientific/Engineering :: Bio-Informatics - -[options] -packages = find_namespace: -package_dir = - amici = amici -python_requires = >=3.10 -install_requires = - cmake-build-extension==0.5.1 - sympy>=1.9 - numpy>=1.21.4; python_version>='3.10' - numpy>=1.23.2; python_version=='3.11' - numpy>=1.26.2; python_version=='3.12' - numpy; python_version>='3.13' - python-libsbml - pandas>=2.0.2 - pyarrow - wurlitzer - toposort - setuptools>=48 - mpmath -include_package_data = True -zip_safe = False - -[options.extras_require] -# Don't include any URLs here - they are not supported by PyPI: -# HTTPError: 400 Bad Request from https://upload.pypi.org/legacy/ -# Invalid value for requires_dist. Error: Can't have direct dependency: ... -petab = petab>=0.2.9 -pysb = pysb>=1.13.1 -test = - benchmark_models_petab @ git+https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab.git@master#subdirectory=src/python - h5py - pytest - pytest-cov - pytest-rerunfailures - coverage - shyaml - antimony>=2.13 - # see https://github.com/sys-bio/antimony/issues/92 - # unsupported x86_64 / x86_64h - antimony!=2.14; platform_system=='Darwin' and platform_machine in 'x86_64h' - scipy - pooch -vis = - matplotlib - seaborn -examples = - jupyter - scipy - -[options.package_data] -amici = - amici/include/amici/* - src/*template* - swig/* - libs/* - setup.py.template - -[options.exclude_package_data] -* = - README.txt - - -[options.entry_points] - -; amici_import_petab.py is kept for backwards compatibility -console_scripts = - amici_import_petab = amici.petab.cli.import_petab:_main - amici_import_petab.py = amici.petab.cli.import_petab:_main From 6b05ddd19a19b5d18a5ab6656f82bfdc39ec9c77 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Thu, 2 May 2024 11:03:20 +0200 Subject: [PATCH 105/188] Faster generation of drootdt_total for models without state-dependent root functions (#2417) Don't compute things we don't need. For my test model, this reduces code generation time from 76s to 12s (-83%). --- python/sdist/amici/de_model.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/python/sdist/amici/de_model.py b/python/sdist/amici/de_model.py index 0caac635a7..0e48bf3af8 100644 --- a/python/sdist/amici/de_model.py +++ b/python/sdist/amici/de_model.py @@ -1561,16 +1561,18 @@ def _compute_equation(self, name: str) -> None: self._eqs[name] = smart_jacobian(self.eq("root"), time_symbol) elif name == "drootdt_total": - # backsubstitution of optimized right-hand side terms into RHS - # calling subs() is costly. Due to looping over events though, the - # following lines are only evaluated if a model has events - w_sorted = toposort_symbols( - dict(zip(self.sym("w"), self.eq("w"), strict=True)) - ) - tmp_xdot = smart_subs_dict(self.eq("xdot"), w_sorted) self._eqs[name] = self.eq("drootdt") - if self.num_states_solver(): - self._eqs[name] += smart_multiply(self.eq("drootdx"), tmp_xdot) + # backsubstitution of optimized right-hand side terms into RHS + # calling subs() is costly. We can skip it if we don't have any + # state-dependent roots. + if self.num_states_solver() and not smart_is_zero_matrix( + drootdx := self.eq("drootdx") + ): + w_sorted = toposort_symbols( + dict(zip(self.sym("w"), self.eq("w"), strict=True)) + ) + tmp_xdot = smart_subs_dict(self.eq("xdot"), w_sorted) + self._eqs[name] += smart_multiply(drootdx, tmp_xdot) elif name == "deltax": # fill boluses for Heaviside functions, as empty state updates From 8c3b59ac9f47f798d07cdeef79ddf6df6c36d5b4 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Fri, 3 May 2024 15:31:11 +0200 Subject: [PATCH 106/188] Add stacklevel to warnings (#2421) Make it easier to find out where warnings originate from. --- python/sdist/amici/logging.py | 3 ++- python/sdist/amici/parameter_mapping.py | 1 + python/sdist/amici/petab/conditions.py | 1 + python/sdist/amici/petab_import.py | 1 + python/sdist/amici/petab_import_pysb.py | 1 + python/sdist/amici/petab_objective.py | 1 + python/sdist/amici/petab_simulate.py | 1 + python/sdist/amici/petab_util.py | 1 + python/sdist/amici/sbml_import.py | 18 ++++++++++++------ python/sdist/amici/swig_wrappers.py | 6 ++++-- python/sdist/pyproject.toml | 1 + 11 files changed, 26 insertions(+), 9 deletions(-) diff --git a/python/sdist/amici/logging.py b/python/sdist/amici/logging.py index 0a345ee0a7..1f5ae1f175 100644 --- a/python/sdist/amici/logging.py +++ b/python/sdist/amici/logging.py @@ -175,7 +175,8 @@ def get_logger( elif kwargs: warnings.warn( "AMICI logger already exists, ignoring keyword " - "arguments to setup_logger" + "arguments to setup_logger", + stacklevel=2, ) logger = logging.getLogger(logger_name) diff --git a/python/sdist/amici/parameter_mapping.py b/python/sdist/amici/parameter_mapping.py index b39d54c87e..dc369b448b 100644 --- a/python/sdist/amici/parameter_mapping.py +++ b/python/sdist/amici/parameter_mapping.py @@ -27,6 +27,7 @@ warnings.warn( "Importing amici.parameter_mapping is deprecated. Use `amici.petab.parameter_mapping` instead.", DeprecationWarning, + stacklevel=2, ) __all__ = [ diff --git a/python/sdist/amici/petab/conditions.py b/python/sdist/amici/petab/conditions.py index 831420f68e..c0b702b69d 100644 --- a/python/sdist/amici/petab/conditions.py +++ b/python/sdist/amici/petab/conditions.py @@ -67,6 +67,7 @@ def fill_in_parameters( "The following problem parameters were not used: " + str(unused_parameters), RuntimeWarning, + stacklevel=2, ) for edata, mapping_for_condition in zip( diff --git a/python/sdist/amici/petab_import.py b/python/sdist/amici/petab_import.py index b81484c1cc..1bcadaa1d2 100644 --- a/python/sdist/amici/petab_import.py +++ b/python/sdist/amici/petab_import.py @@ -13,6 +13,7 @@ warnings.warn( "Importing amici.petab_import is deprecated. Use `amici.petab` instead.", DeprecationWarning, + stacklevel=2, ) from .petab.import_helpers import ( # noqa # pylint: disable=unused-import diff --git a/python/sdist/amici/petab_import_pysb.py b/python/sdist/amici/petab_import_pysb.py index a1597d53b5..4d73a4bab3 100644 --- a/python/sdist/amici/petab_import_pysb.py +++ b/python/sdist/amici/petab_import_pysb.py @@ -14,6 +14,7 @@ warnings.warn( "Importing amici.petab_import_pysb is deprecated. Use `amici.petab.pysb_import` instead.", DeprecationWarning, + stacklevel=2, ) __all__ = [ diff --git a/python/sdist/amici/petab_objective.py b/python/sdist/amici/petab_objective.py index 01724b7a7d..6d0dc44d84 100644 --- a/python/sdist/amici/petab_objective.py +++ b/python/sdist/amici/petab_objective.py @@ -12,6 +12,7 @@ warnings.warn( f"Importing {__name__} is deprecated. Use `amici.petab.simulations` instead.", DeprecationWarning, + stacklevel=2, ) from .petab.conditions import fill_in_parameters # noqa: F401 diff --git a/python/sdist/amici/petab_simulate.py b/python/sdist/amici/petab_simulate.py index 2dd25a8e4a..5f81d02a93 100644 --- a/python/sdist/amici/petab_simulate.py +++ b/python/sdist/amici/petab_simulate.py @@ -11,6 +11,7 @@ warnings.warn( f"Importing {__name__} is deprecated. Use `amici.petab.simulator` instead.", DeprecationWarning, + stacklevel=2, ) from .petab.simulator import PetabSimulator # noqa: F401 diff --git a/python/sdist/amici/petab_util.py b/python/sdist/amici/petab_util.py index ff202bf2e0..cf12a7411d 100644 --- a/python/sdist/amici/petab_util.py +++ b/python/sdist/amici/petab_util.py @@ -15,6 +15,7 @@ warnings.warn( f"Importing {__name__} is deprecated. Use `amici.petab.util` instead.", DeprecationWarning, + stacklevel=2, ) __all__ = [ diff --git a/python/sdist/amici/sbml_import.py b/python/sdist/amici/sbml_import.py index 843b954a8f..a26c6e7d4a 100644 --- a/python/sdist/amici/sbml_import.py +++ b/python/sdist/amici/sbml_import.py @@ -442,7 +442,8 @@ def sbml2amici( if not has_clibs: warnings.warn( "AMICI C++ extensions have not been built. " - "Generated model code, but unable to compile." + "Generated model code, but unable to compile.", + stacklevel=2, ) exporter.compile_model() @@ -1838,7 +1839,8 @@ def _process_event_observables( f'Event observable {eo["name"]} uses `t` in ' "it's formula which is not the time variable. " "For the time variable, please use `time` " - "instead!" + "instead!", + stacklevel=1, ) # check for nesting of observables (unsupported) @@ -2157,7 +2159,8 @@ def _get_conservation_laws_demartino( "Conservation laws for non-constant species in " "combination with parameterized stoichiometric " "coefficients are not currently supported " - "and will be turned off." + "and will be turned off.", + stacklevel=1, ) return [] @@ -2234,7 +2237,8 @@ def _get_conservation_laws_rref( "Conservation laws for non-constant species in " "combination with parameterized stoichiometric " "coefficients are not currently supported " - "and will be turned off." + "and will be turned off.", + stacklevel=1, ) return [] @@ -3048,7 +3052,8 @@ def _non_const_conservation_laws_supported(sbml_model: sbml.Model) -> bool: warnings.warn( "Conservation laws for non-constant species in " "models with RateRules are currently not supported " - "and will be turned off." + "and will be turned off.", + stacklevel=1, ) return False @@ -3060,7 +3065,8 @@ def _non_const_conservation_laws_supported(sbml_model: sbml.Model) -> bool: warnings.warn( "Conservation laws for non-constant species in " "models with Species-AssignmentRules are currently not " - "supported and will be turned off." + "supported and will be turned off.", + stacklevel=1, ) return False diff --git a/python/sdist/amici/swig_wrappers.py b/python/sdist/amici/swig_wrappers.py index 3c4df44809..5762f50bec 100644 --- a/python/sdist/amici/swig_wrappers.py +++ b/python/sdist/amici/swig_wrappers.py @@ -78,7 +78,8 @@ def runAmiciSimulation( warnings.warn( "Adjoint sensitivity analysis for models with discontinuous right hand sides (events/piecewise functions) has not been thoroughly tested." "Sensitivities might be wrong. Tracked at https://github.com/AMICI-dev/AMICI/issues/18. " - "Adjoint sensitivity analysis may work if the location of the discontinuity is not parameter-dependent, but we still recommend testing accuracy of gradients." + "Adjoint sensitivity analysis may work if the location of the discontinuity is not parameter-dependent, but we still recommend testing accuracy of gradients.", + stacklevel=1, ) with _capture_cstdout(): @@ -119,7 +120,8 @@ def runAmiciSimulations( warnings.warn( "Adjoint sensitivity analysis for models with discontinuous right hand sides (events/piecewise functions) has not been thoroughly tested. " "Sensitivities might be wrong. Tracked at https://github.com/AMICI-dev/AMICI/issues/18. " - "Adjoint sensitivity analysis may work if the location of the discontinuity is not parameter-dependent, but we still recommend testing accuracy of gradients." + "Adjoint sensitivity analysis may work if the location of the discontinuity is not parameter-dependent, but we still recommend testing accuracy of gradients.", + stacklevel=1, ) with _capture_cstdout(): diff --git a/python/sdist/pyproject.toml b/python/sdist/pyproject.toml index 58d39d4ea7..7c903273b5 100644 --- a/python/sdist/pyproject.toml +++ b/python/sdist/pyproject.toml @@ -130,4 +130,5 @@ line-length = 79 extend-include = ["*.ipynb"] [tool.ruff.lint] +extend-select = ["B028"] ignore = ["E402", "F403", "F405", "E741"] From e3490d53272a5757b2737d0a7f3613d819a5b02a Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Mon, 6 May 2024 15:08:15 +0200 Subject: [PATCH 107/188] np.Inf -> np.inf for numpy 2.0 compatibility (#2422) ... and show numpy version detected by CMake --- python/tests/test_conserved_quantities_demartino.py | 2 +- swig/CMakeLists.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/python/tests/test_conserved_quantities_demartino.py b/python/tests/test_conserved_quantities_demartino.py index 2ed778b85e..94d51a606d 100644 --- a/python/tests/test_conserved_quantities_demartino.py +++ b/python/tests/test_conserved_quantities_demartino.py @@ -823,7 +823,7 @@ def test_cl_detect_execution_time(data_demartino2014): # <5s on modern hardware, but leave some slack max_time_seconds = 40 if "GITHUB_ACTIONS" in os.environ else 10 - runtime = np.Inf + runtime = np.inf for _ in range(max_tries): runtime = compute_moiety_conservation_laws_demartino2014( diff --git a/swig/CMakeLists.txt b/swig/CMakeLists.txt index 2b4fabadb0..7b7baf9be9 100644 --- a/swig/CMakeLists.txt +++ b/swig/CMakeLists.txt @@ -20,6 +20,7 @@ find_package( Python3 COMPONENTS Interpreter Development NumPy REQUIRED) +message(STATUS "Found numpy ${Python3_NumPy_VERSION} include dir ${Python3_NumPy_INCLUDE_DIRS}") set(AMICI_INTERFACE_LIST ${CMAKE_CURRENT_SOURCE_DIR}/amici.i ${CMAKE_CURRENT_SOURCE_DIR}/edata.i From da19a550688e8232b23ace6d06722c66deb2703f Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Mon, 6 May 2024 16:46:12 +0200 Subject: [PATCH 108/188] Remove wurlitzer dependency (#2423) When using the Python interface, we don't rely on C(++) output anywhere anymore, and thus, we don't need wurlitzer anymore. Closes #2409 --- python/sdist/amici/setup.template.py | 1 - python/sdist/amici/swig_wrappers.py | 42 ++++++++-------------------- python/sdist/pyproject.toml | 1 - 3 files changed, 11 insertions(+), 33 deletions(-) diff --git a/python/sdist/amici/setup.template.py b/python/sdist/amici/setup.template.py index 1f2d099110..80fd3fde2a 100644 --- a/python/sdist/amici/setup.template.py +++ b/python/sdist/amici/setup.template.py @@ -72,7 +72,6 @@ def get_extension() -> CMakeExtension: ext_modules=[MODEL_EXT], packages=find_namespace_packages(), install_requires=["amici==TPL_AMICI_VERSION"], - extras_require={"wurlitzer": ["wurlitzer"]}, python_requires=">=3.10", package_data={}, zip_safe=False, diff --git a/python/sdist/amici/swig_wrappers.py b/python/sdist/amici/swig_wrappers.py index 5762f50bec..2e90891df3 100644 --- a/python/sdist/amici/swig_wrappers.py +++ b/python/sdist/amici/swig_wrappers.py @@ -1,9 +1,7 @@ """Convenience wrappers for the swig interface""" import logging -import sys import warnings -from contextlib import contextmanager, suppress from typing import Any import amici @@ -30,22 +28,6 @@ "get_model_settings", ] -try: - from wurlitzer import sys_pipes -except ModuleNotFoundError: - sys_pipes = suppress - - -@contextmanager -def _capture_cstdout(): - """Redirect C/C++ stdout to python stdout if python stdout is redirected, - e.g. in ipython notebook""" - if sys.stdout == sys.__stdout__: - yield - else: - with sys_pipes(): - yield - def runAmiciSimulation( model: AmiciModel, @@ -82,10 +64,9 @@ def runAmiciSimulation( stacklevel=1, ) - with _capture_cstdout(): - rdata = amici_swig.runAmiciSimulation( - _get_ptr(solver), _get_ptr(edata), _get_ptr(model) - ) + rdata = amici_swig.runAmiciSimulation( + _get_ptr(solver), _get_ptr(edata), _get_ptr(model) + ) _log_simulation(rdata) if solver.getReturnDataReportingMode() == amici.RDataReporting.full: _ids_and_names_to_rdata(rdata, model) @@ -124,15 +105,14 @@ def runAmiciSimulations( stacklevel=1, ) - with _capture_cstdout(): - edata_ptr_vector = amici_swig.ExpDataPtrVector(edata_list) - rdata_ptr_list = amici_swig.runAmiciSimulations( - _get_ptr(solver), - edata_ptr_vector, - _get_ptr(model), - failfast, - num_threads, - ) + edata_ptr_vector = amici_swig.ExpDataPtrVector(edata_list) + rdata_ptr_list = amici_swig.runAmiciSimulations( + _get_ptr(solver), + edata_ptr_vector, + _get_ptr(model), + failfast, + num_threads, + ) for rdata in rdata_ptr_list: _log_simulation(rdata) if solver.getReturnDataReportingMode() == amici.RDataReporting.full: diff --git a/python/sdist/pyproject.toml b/python/sdist/pyproject.toml index 7c903273b5..10a3b05374 100644 --- a/python/sdist/pyproject.toml +++ b/python/sdist/pyproject.toml @@ -31,7 +31,6 @@ dependencies = [ "python-libsbml", "pandas>=2.0.2", "pyarrow", - "wurlitzer", "toposort", "setuptools>=48", "mpmath", From 0464b832dfbf13c9860d92e70ebace08e81e9739 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Tue, 7 May 2024 12:27:44 +0200 Subject: [PATCH 109/188] Fix handling of event assignments to compartments (#2428) Previously, in some cases (see #2426), event assignments to compartments would result in incorrect concentrations because an incorrect post-event volume was used. This is fixed here. Closes #2426. --- python/sdist/amici/sbml_import.py | 12 +++-- python/tests/test_events.py | 83 +++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 5 deletions(-) diff --git a/python/sdist/amici/sbml_import.py b/python/sdist/amici/sbml_import.py index a26c6e7d4a..608ceeed8f 100644 --- a/python/sdist/amici/sbml_import.py +++ b/python/sdist/amici/sbml_import.py @@ -1613,7 +1613,9 @@ def get_empty_bolus_value() -> sp.Float: # parse the boluses / event assignments bolus = [get_empty_bolus_value() for _ in state_vector] event_assignments = event.getListOfEventAssignments() - compartment_event_assignments = set() + compartment_event_assignments: set[tuple[sp.Symbol, sp.Expr]] = ( + set() + ) for event_assignment in event_assignments: variable_sym = symbol_with_assumptions( event_assignment.getVariable() @@ -1644,7 +1646,7 @@ def get_empty_bolus_value() -> sp.Float: "expressions as event assignments." ) if variable_sym in concentration_species_by_compartment: - compartment_event_assignments.add(variable_sym) + compartment_event_assignments.add((variable_sym, formula)) for ( comp, @@ -1652,15 +1654,15 @@ def get_empty_bolus_value() -> sp.Float: ) in self.compartment_assignment_rules.items(): if variable_sym not in assignment.free_symbols: continue - compartment_event_assignments.add(comp) + compartment_event_assignments.add((comp, formula)) # Update the concentration of species with concentration units # in compartments that were affected by the event assignments. - for compartment_sym in compartment_event_assignments: + for compartment_sym, formula in compartment_event_assignments: for species_sym in concentration_species_by_compartment[ compartment_sym ]: - # If the species was not affected by an event assignment + # If the species was not affected by an event assignment, # then the old value should be updated. if ( bolus[state_vector.index(species_sym)] diff --git a/python/tests/test_events.py b/python/tests/test_events.py index 05eddab59f..d16877fd2e 100644 --- a/python/tests/test_events.py +++ b/python/tests/test_events.py @@ -15,6 +15,7 @@ create_amici_model, create_sbml_model, ) +from numpy.testing import assert_allclose @pytest.fixture( @@ -746,3 +747,85 @@ def test_handling_of_fixed_time_point_event_triggers(): assert (rdata.x[(rdata.ts >= 3)] == 3).all() check_derivatives(amici_model, amici_solver, edata=None) + + +def test_multiple_event_assignment_with_compartment(): + """see https://github.com/AMICI-dev/AMICI/issues/2426""" + ant_model = """ + model test_events_multiple_assignments + compartment event_target = 1 + event_target' = 0 + species species_in_event_target in event_target = 1 + unrelated = 2 + + # use different order of event assignments for the two events + at (time > 5): unrelated = 4, event_target = 10 + at (time > 10): event_target = 1, unrelated = 2 + end + """ + # watch out for too long path names on windows ... + module_name = "tst_mltple_ea_w_cmprtmnt" + with TemporaryDirectory(prefix=module_name, delete=False) as outdir: + antimony2amici( + ant_model, + model_name=module_name, + output_dir=outdir, + verbose=True, + ) + model_module = amici.import_model_module( + module_name=module_name, module_path=outdir + ) + amici_model = model_module.getModel() + assert amici_model.ne == 2 + assert amici_model.ne_solver == 0 + assert amici_model.nx_rdata == 3 + amici_model.setTimepoints(np.linspace(0, 15, 16)) + amici_solver = amici_model.getSolver() + rdata = amici.runAmiciSimulation(amici_model, amici_solver) + assert rdata.status == amici.AMICI_SUCCESS + idx_event_target = amici_model.getStateIds().index("event_target") + idx_unrelated = amici_model.getStateIds().index("unrelated") + idx_species_in_event_target = amici_model.getStateIds().index( + "species_in_event_target" + ) + + assert_allclose( + rdata.x[(rdata.ts < 5) & (rdata.ts > 10), idx_event_target], + 1, + rtol=0, + atol=1e-15, + ) + assert_allclose( + rdata.x[(5 < rdata.ts) & (rdata.ts < 10), idx_event_target], + 10, + rtol=0, + atol=1e-15, + ) + assert_allclose( + rdata.x[(rdata.ts < 5) & (rdata.ts > 10), idx_unrelated], + 2, + rtol=0, + atol=1e-15, + ) + assert_allclose( + rdata.x[(5 < rdata.ts) & (rdata.ts < 10), idx_unrelated], + 4, + rtol=0, + atol=1e-15, + ) + assert_allclose( + rdata.x[ + (rdata.ts < 5) & (rdata.ts > 10), idx_species_in_event_target + ], + 1, + rtol=0, + atol=1e-15, + ) + assert_allclose( + rdata.x[ + (5 < rdata.ts) & (rdata.ts < 10), idx_species_in_event_target + ], + 0.1, + rtol=0, + atol=1e-15, + ) From 41d19bb5d9dcbafae714260d8982da69223bba0c Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Tue, 7 May 2024 13:17:49 +0200 Subject: [PATCH 110/188] Support event-assignments to compartments that aren't rate rule targets (#2425) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Event-assignments to compartments weren't supported yet, now they are. Compartments that are targets of event assignments are now processed as "species", the same way as it is already done for parameters that are targets of event assignments. Closes #2424. --------- Co-authored-by: Fabian Fröhlich --- documentation/python_interface.rst | 2 +- python/sdist/amici/sbml_import.py | 37 +++++++++++++++++++++++++++--- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/documentation/python_interface.rst b/documentation/python_interface.rst index a919e925c4..2926abf963 100644 --- a/documentation/python_interface.rst +++ b/documentation/python_interface.rst @@ -26,7 +26,7 @@ AMICI can import :term:`SBML` models via the Status of SBML support in Python-AMICI ++++++++++++++++++++++++++++++++++++++ -Python-AMICI currently **passes 1247 out of the 1821 (~68%) test cases** from +Python-AMICI currently **passes 1252 out of the 1821 (~68%) test cases** from the semantic `SBML Test Suite `_ (`current status `_). diff --git a/python/sdist/amici/sbml_import.py b/python/sdist/amici/sbml_import.py index 608ceeed8f..5d71821f06 100644 --- a/python/sdist/amici/sbml_import.py +++ b/python/sdist/amici/sbml_import.py @@ -561,9 +561,9 @@ def _build_ode_model( assert dxdt.shape[0] - len(self.symbols[SymbolId.SPECIES]) == len( self.symbols.get(SymbolId.ALGEBRAIC_STATE, []) ), ( - self.symbols[SymbolId.SPECIES], + self.symbols.get(SymbolId.SPECIES), dxdt, - self.symbols[SymbolId.SPECIES], + self.symbols.get(SymbolId.ALGEBRAIC_STATE), ) # correct time derivatives for compartment changes @@ -954,6 +954,7 @@ def _process_species(self) -> None: } self._convert_event_assignment_parameter_targets_to_species() + self._convert_event_assignment_compartment_targets_to_species() self._process_species_initial() self._process_rate_rules() @@ -1564,6 +1565,36 @@ def _convert_event_assignment_parameter_targets_to_species(self): "dt": sp.Float(0), } + def _convert_event_assignment_compartment_targets_to_species(self): + """Find compartments that are event assignment targets and convert + those compartments to species.""" + for event in self.sbml.getListOfEvents(): + for event_assignment in event.getListOfEventAssignments(): + if event_assignment.getMath() is None: + # Ignore event assignments with no change in value. + continue + variable = symbol_with_assumptions( + event_assignment.getVariable() + ) + if variable not in self.compartments: + continue + if variable in self.symbols[SymbolId.SPECIES]: + # Compartments with rate rules are already present as + # species + continue + + self.symbols[SymbolId.SPECIES][variable] = { + "name": str(variable), + "init": self.compartments[variable], + # 'compartment': None, # can ignore for amounts + "constant": False, + "amount": True, + # 'conversion_factor': 1.0, # can be ignored + "index": len(self.symbols[SymbolId.SPECIES]), + "dt": sp.Float(0), + } + del self.compartments[variable] + @log_execution_time("processing SBML events", logger) def _process_events(self) -> None: """Process SBML events.""" @@ -1633,7 +1664,7 @@ def get_empty_bolus_value() -> sp.Float: "Could not process event assignment for " f"{str(variable_sym)}. AMICI currently only allows " "event assignments to species; parameters; or, " - "compartments with rate rules, at the moment." + "compartments." ) try: # Try working with the formula now to detect errors From 8986d387383ad7dbf742378ca9c73486498f30a8 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Wed, 8 May 2024 11:59:01 +0200 Subject: [PATCH 111/188] SBML import: handle `useValuesFromTriggerTime` attribute on events (#2429) Check whether we are able to handle a given event according to its `useValuesFromTriggerTime` setting. See also #2427. Does not affect SBML test suite stats. Closes #2427. --- python/sdist/amici/sbml_import.py | 83 +++++++++++++++++++++++++++++-- python/tests/util.py | 2 +- 2 files changed, 80 insertions(+), 5 deletions(-) diff --git a/python/sdist/amici/sbml_import.py b/python/sdist/amici/sbml_import.py index 5d71821f06..61ce9a0ee1 100644 --- a/python/sdist/amici/sbml_import.py +++ b/python/sdist/amici/sbml_import.py @@ -1625,6 +1625,10 @@ def get_empty_bolus_value() -> sp.Float: species_def["compartment"] ].append(species) + # Currently, all event assignment targets must exist in + # self.symbols[SymbolId.SPECIES] + state_vector = list(self.symbols[SymbolId.SPECIES].keys()) + for ievent, event in enumerate(events): # get the event id (which is optional unfortunately) event_id = event.getId() @@ -1637,10 +1641,6 @@ def get_empty_bolus_value() -> sp.Float: trigger_sym = self._sympy_from_sbml_math(trigger_sbml) trigger = _parse_event_trigger(trigger_sym) - # Currently, all event assignment targets must exist in - # self.symbols[SymbolId.SPECIES] - state_vector = list(self.symbols[SymbolId.SPECIES].keys()) - # parse the boluses / event assignments bolus = [get_empty_bolus_value() for _ in state_vector] event_assignments = event.getListOfEventAssignments() @@ -1736,13 +1736,88 @@ def get_empty_bolus_value() -> sp.Float: " algebraic rules." ) + # Store `useValuesFromTriggerTime` attribute for checking later + # Since we assume valid in SBML models here, this attribute is + # either given (mandatory in L3), or defaults to True (L2) + use_trig_val = ( + event.getUseValuesFromTriggerTime() + if event.isSetUseValuesFromTriggerTime() + else True + ) + self.symbols[SymbolId.EVENT][event_sym] = { "name": event_id, "value": trigger, "state_update": sp.MutableDenseMatrix(bolus), "initial_value": initial_value, + "use_values_from_trigger_time": use_trig_val, } + # Check `useValuesFromTriggerTime` attribute + # AMICI does not support events with + # `useValuesFromTriggerTime=true`, unless + # 1) there is only a single event + # 2) there are multiple events, but they are guaranteed to not + # trigger at the same time + # 3) event assignments from events triggering at the same time + # are independent + # in these cases, the attribute value doesn't matter, as long + # as we don't support delays. + # We can't check this in `check_event_support` without already + # processing all trigger expressions, so we do it here + + # are there any events with `useValuesFromTriggerTime=true`? + if len(self.symbols[SymbolId.EVENT]) <= 1 or not any( + event["use_values_from_trigger_time"] + for event in self.symbols[SymbolId.EVENT].values() + ): + return + + # check if events are guaranteed to not trigger at the same time + trigger_times = [ + sp.solve(event["value"], sbml_time_symbol) + for event_sym, event in self.symbols[SymbolId.EVENT].items() + ] + # for now, we only check for single/fixed/unique time points, but there + # are probably other cases we could cover + if all(len(ts) == 1 and ts[0].is_Number for ts in trigger_times): + trigger_times = [ts[0] for ts in trigger_times] + if len(trigger_times) == len(set(trigger_times)): + # all trigger times are unique + return + + # If all events assign to different species, we are fine. This is the + # case if the list of assigned-to variables across all events contains + # only unique values. + assigned_to_species = [ + variable + for event in self.symbols[SymbolId.EVENT].values() + for variable, update in zip(state_vector, event["state_update"]) + if not update.is_zero + ] + if len(assigned_to_species) == len(set(assigned_to_species)): + return + + # if all assignments are absolute (not referring to other non-constant + # model entities), we are fine. + if all( + update.is_zero or (update + variable).is_Number + for event in self.symbols[SymbolId.EVENT].values() + for variable, update in zip(state_vector, event["state_update"]) + if not update.is_zero + ): + return + + raise SBMLException( + "Events with `useValuesFromTriggerTime=true` are not " + "supported when there are multiple events.\n" + "If it is guaranteed that 1) events do not trigger at the same " + "time, or 2) different event assignments do not affect the same " + "entities, or 3) event assignments do not depend on the " + "pre-event state, then you can set " + "`useValuesFromTriggerTime=false` and retry." + ) + @log_execution_time("processing SBML observables", logger) def _process_observables( self, diff --git a/python/tests/util.py b/python/tests/util.py index b450501a81..0f368a4f6f 100644 --- a/python/tests/util.py +++ b/python/tests/util.py @@ -96,7 +96,7 @@ def create_sbml_model( event = model.createEvent() event.setId(event_id) event.setName(event_id) - event.setUseValuesFromTriggerTime(True) + event.setUseValuesFromTriggerTime(False) trigger = event.createTrigger() trigger.setMath(libsbml.parseL3Formula(event_def["trigger"])) trigger.setPersistent(True) From b9d3c7e45d79af7575ca7a7a24783b219c2bae54 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Wed, 8 May 2024 08:23:53 +0200 Subject: [PATCH 112/188] Update valgrind suppressions for Python 3.11 --- python/tests/valgrind-python.supp | 87 +++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/python/tests/valgrind-python.supp b/python/tests/valgrind-python.supp index 01bc776aec..dab3c06dc4 100644 --- a/python/tests/valgrind-python.supp +++ b/python/tests/valgrind-python.supp @@ -878,3 +878,90 @@ fun:loadAntimonyString ... } + +{ + Python + Memcheck:Cond + fun:maybe_small_long + ... +} + +{ + Python + Memcheck:Value8 + fun:medium_value + ... +} + +{ + Python + Memcheck:Value8 + fun:Py_INCREF + ... +} +{ + Python + Memcheck:Value8 + fun:Py_DECREF + ... +} +{ + Python + Memcheck:Value8 + fun:Py_SIZE + ... +} +{ + Python + Memcheck:Value8 + fun:Py_TYPE + ... +} +{ + Python + Memcheck:Value8 + fun:type_call + ... +} + +{ + Python + Memcheck:Value8 + fun:_PyEval_EvalFrameDefault + ... +} + + +{ + Python + Memcheck:Value8 + fun:PyType_HasFeature + ... +} + +{ + Python + Memcheck:Value8 + fun:unpack_indices + ... +} + +{ + Python + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:PyFloat_FromDouble + fun:fill_time + ... +} +{ + Python + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:gc_alloc + ... + fun:Py_CompileStringObject + ... +} From 52996d3b846cc0f2294e7d399ac1e00869d3b63a Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Tue, 7 May 2024 22:12:59 +0200 Subject: [PATCH 113/188] Bump version; update changelog --- CHANGELOG.md | 36 ++++++++++++++++++++++++++++++++++-- version.txt | 2 +- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e658b12593..1819436908 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,42 @@ ## v0.X Series +### v0.25.0 (2024-05-TBD) + +This release requires Python >= 3.10. + +**Fixes** +* Fixed a bug in event handling that could lead to incorrect simulation + results for models with events that assign to compartments *and* have + additional event assignments + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2428 +* SBML import: handle `useValuesFromTriggerTime` attribute on events. + This attribute was previously ignored. It is possible that now AMICI fails + to import models that it previously imported successfully. For cases where + `useValuesFromTriggerTime=True` made a difference, AMICI might have produced + incorrect results before. + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2429 +* Faster code generation for models with events if they don't have + state-dependent triggers + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2417 +* Most warnings now come with a more informative code location + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2421 +* `amici.ExpData` was changed so that `isinstance(edata, amici.ExpData)` works + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2396 + +**Features** +* Event-assignments to compartments are now supported. Previously, this only + worked for compartments that were rate rule targets. + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2425 +* Releases are now deployed to Docker Hub + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2413 + +**Full Changelog**: https://github.com/AMICI-dev/AMICI/compare/v0.24.0...v0.25.0 + ### v0.24.0 (2024-04-22) This will be the last release supporting Python 3.9. -Future releases will require Python 3.10. +Future releases will require Python>=3.10. **Fixes** @@ -27,7 +59,7 @@ Future releases will require Python 3.10. the correct SUNDIALS installation by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2397 -* **Features** +**Features** * Optionally include measurements in `plot_observable_trajectories` by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2381 diff --git a/version.txt b/version.txt index 2094a100ca..d21d277be5 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.24.0 +0.25.0 From 70be4a2e99fd89043741c4671a5b4765bcc4c6d2 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Wed, 8 May 2024 15:56:24 +0200 Subject: [PATCH 114/188] Fix release date --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1819436908..dc831f49c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## v0.X Series -### v0.25.0 (2024-05-TBD) +### v0.25.0 (2024-05-08) This release requires Python >= 3.10. From f30bada63420023ca3072fe31a30cfc76e1cfc26 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 May 2024 16:46:23 +0200 Subject: [PATCH 115/188] Bump actions/cache from 3 to 4 (#2434) Bumps [actions/cache](https://github.com/actions/cache) from 3 to 4. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/test_python_cplusplus.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_python_cplusplus.yml b/.github/workflows/test_python_cplusplus.yml index 5714a37b21..2128f4df1a 100644 --- a/.github/workflows/test_python_cplusplus.yml +++ b/.github/workflows/test_python_cplusplus.yml @@ -19,7 +19,7 @@ jobs: steps: - name: Cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ~/.cache/pooch @@ -273,7 +273,7 @@ jobs: steps: - name: Cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ~/Library/Caches/pooch From 7aa0950734c3b99b9f717834583c2ad8fff682ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 May 2024 16:48:55 +0200 Subject: [PATCH 116/188] Bump matlab-actions/setup-matlab from 1 to 2 (#2436) Bumps [matlab-actions/setup-matlab](https://github.com/matlab-actions/setup-matlab) from 1 to 2. - [Release notes](https://github.com/matlab-actions/setup-matlab/releases) - [Commits](https://github.com/matlab-actions/setup-matlab/compare/v1...v2) --- updated-dependencies: - dependency-name: matlab-actions/setup-matlab dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/test_matlab.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_matlab.yml b/.github/workflows/test_matlab.yml index 46185e9d45..90ada6d995 100644 --- a/.github/workflows/test_matlab.yml +++ b/.github/workflows/test_matlab.yml @@ -21,7 +21,7 @@ jobs: - run: echo "AMICI_DIR=$(pwd)" >> $GITHUB_ENV - name: Install MATLAB - uses: matlab-actions/setup-matlab@v1 + uses: matlab-actions/setup-matlab@v2 - name: Run script uses: matlab-actions/run-command@v1 with: From 253b1075d0f2cf5bea28bd511e0139f498de2ce9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 May 2024 19:29:49 +0200 Subject: [PATCH 117/188] Bump matlab-actions/run-command from 1 to 2 (#2435) Bumps [matlab-actions/run-command](https://github.com/matlab-actions/run-command) from 1 to 2. - [Release notes](https://github.com/matlab-actions/run-command/releases) - [Commits](https://github.com/matlab-actions/run-command/compare/v1...v2) --- updated-dependencies: - dependency-name: matlab-actions/run-command dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/test_matlab.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_matlab.yml b/.github/workflows/test_matlab.yml index 90ada6d995..473b927e7d 100644 --- a/.github/workflows/test_matlab.yml +++ b/.github/workflows/test_matlab.yml @@ -23,6 +23,6 @@ jobs: - name: Install MATLAB uses: matlab-actions/setup-matlab@v2 - name: Run script - uses: matlab-actions/run-command@v1 + uses: matlab-actions/run-command@v2 with: command: cd matlab; installAMICI; addpath tests; testModels From 56949761eda0d536e56575b6562691b0f1e7a1e7 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Mon, 13 May 2024 10:40:45 +0200 Subject: [PATCH 118/188] Change dependabot target branch to 'develop' --- .github/dependabot.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d3e0189bdb..edacb6bea9 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,3 +8,4 @@ updates: ignore: - dependency-name: "*" update-types: ["version-update:semver-patch", "version-update:semver-minor"] + target-branch: "develop" From 4eb4bbd613c48d78d05b9574026c4b21fc6aa268 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Mon, 13 May 2024 11:26:52 +0200 Subject: [PATCH 119/188] RTD: bump Jinja2 --- documentation/rtd_requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/rtd_requirements.txt b/documentation/rtd_requirements.txt index 8d2c2100f9..9b5cc9709b 100644 --- a/documentation/rtd_requirements.txt +++ b/documentation/rtd_requirements.txt @@ -18,7 +18,7 @@ exhale>=0.3.7 sphinxcontrib-matlabdomain>=0.20.0 sphinxcontrib-napoleon>=0.7 pygments>=2.15.1 -Jinja2==3.1.2 +Jinja2==3.1.4 git+https://github.com/readthedocs/readthedocs-sphinx-ext ipykernel -e git+https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab.git@master#subdirectory=src/python&egg=benchmark_models_petab From 0c000461941abd0462d4ddb893a33801fe49aae9 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Tue, 14 May 2024 10:02:43 +0200 Subject: [PATCH 120/188] Avoid symbol clashes in plot_expressions (#2440) Ignore sympy constants when sympifying expression strings to avoid clashes with sympy.*. --- python/sdist/amici/numpy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/sdist/amici/numpy.py b/python/sdist/amici/numpy.py index 9aa03fc2dd..4f659c0b45 100644 --- a/python/sdist/amici/numpy.py +++ b/python/sdist/amici/numpy.py @@ -13,7 +13,7 @@ import amici import numpy as np import sympy as sp - +from sympy.abc import _clash from . import ExpData, ExpDataPtr, Model, ReturnData, ReturnDataPtr StrOrExpr = Union[str, sp.Expr] @@ -497,7 +497,7 @@ def evaluate(expr: StrOrExpr, rdata: ReturnDataView) -> np.array: from sympy.utilities.lambdify import lambdify if isinstance(expr, str): - expr = sp.sympify(expr) + expr = sp.sympify(expr, locals=_clash) arg_names = list(sorted(expr.free_symbols, key=lambda x: x.name)) func = lambdify(arg_names, expr, "numpy") From a63b016f316e7ab95eca2a2b0a9d0119448daf61 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Tue, 14 May 2024 14:31:41 +0200 Subject: [PATCH 121/188] Pin cmake_build_extension==0.5.1 (#2446) Missed one location before. Fixes #2442. --- scripts/installAmiciSource.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/installAmiciSource.sh b/scripts/installAmiciSource.sh index c074c89d46..514926773f 100755 --- a/scripts/installAmiciSource.sh +++ b/scripts/installAmiciSource.sh @@ -33,7 +33,7 @@ fi export PYTHON_EXECUTABLE="${AMICI_PATH}/venv/bin/python" python -m pip install --upgrade pip wheel -python -m pip install --upgrade pip setuptools cmake_build_extension numpy +python -m pip install --upgrade pip setuptools cmake_build_extension==0.5.1 numpy python -m pip install git+https://github.com/FFroehlich/pysb@fix_pattern_matching # pin to PR for SPM with compartments AMICI_BUILD_TEMP="${AMICI_PATH}/python/sdist/build/temp" \ python -m pip install --verbose -e "${AMICI_PATH}/python/sdist[petab,test,vis]" --no-build-isolation From e03f82970a51d30cfe2b42eeed5d5125fe7f2d05 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Wed, 15 May 2024 18:26:45 +0200 Subject: [PATCH 122/188] pytest: ignore log(0) warnings (#2448) The warning/error in #2444 is caused by `petab.parameters.scale()`. Whether this warning should be suppressed there or not is to discussed in the PEtab context. For amici, I'd say we're fine with just accepting `np.log(0) == -inf` without a warning. Closes #2444. --- pytest.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pytest.ini b/pytest.ini index 3868c80b1e..4f682576a3 100644 --- a/pytest.ini +++ b/pytest.ini @@ -19,5 +19,7 @@ filterwarnings = ignore:The --rsyncdir command line argument and rsyncdirs config variable are deprecated.:DeprecationWarning ignore:.*:ImportWarning:tellurium ignore:.*PyDevIPCompleter6.*:DeprecationWarning + # ignore numpy log(0) warnings (np.log(0) = -inf) + ignore:divide by zero encountered in log:RuntimeWarning norecursedirs = .git amici_models build doc documentation matlab models ThirdParty amici sdist examples From 44ed074f1870259ee091d9e275118707bbb24aff Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Wed, 15 May 2024 18:33:32 +0200 Subject: [PATCH 123/188] PEtab: fix missing parameters in fill_in_parameters (#2449) Allows providing only a subset of parameter to simulate_petab. The missing parameters are taken from nominalValues in the parameter table. Fixes https://github.com/AMICI-dev/AMICI/issues/2444. Nevertheless, exclude `Lang_PLOSComputBiol2024` to save time. --- python/sdist/amici/petab/simulations.py | 45 ++++++++++--------- .../benchmark-models/test_petab_benchmark.py | 1 + 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/python/sdist/amici/petab/simulations.py b/python/sdist/amici/petab/simulations.py index e80e40a94b..2cbed98dce 100644 --- a/python/sdist/amici/petab/simulations.py +++ b/python/sdist/amici/petab/simulations.py @@ -166,30 +166,33 @@ def simulate_petab( amici_model=amici_model, ) + if problem_parameters is not None and not scaled_parameters: + problem_parameters = petab_problem.scale_parameters(problem_parameters) + scaled_parameters = True + if problem_parameters is None: - # scaled PEtab nominal values - problem_parameters = dict( - zip( - petab_problem.x_ids, - petab_problem.x_nominal_scaled, - strict=True, - ) + problem_parameters = {} + + # scaled PEtab nominal values + default_problem_parameters = dict( + zip( + petab_problem.x_ids, + petab_problem.get_x_nominal(scaled=scaled_parameters), + strict=True, ) - # depending on `fill_fixed_parameters` for parameter mapping, the - # parameter mapping may contain values instead of symbols for fixed - # parameters. In this case, we need to filter them here to avoid - # warnings in `fill_in_parameters`. - free_parameters = parameter_mapping.free_symbols - problem_parameters = { - par_id: par_value - for par_id, par_value in problem_parameters.items() - if par_id in free_parameters - } - - elif not scaled_parameters: - problem_parameters = petab_problem.scale_parameters(problem_parameters) + ) + # depending on `fill_fixed_parameters` for parameter mapping, the + # parameter mapping may contain values instead of symbols for fixed + # parameters. In this case, we need to filter them here to avoid + # warnings in `fill_in_parameters`. + free_parameters = parameter_mapping.free_symbols + default_problem_parameters = { + par_id: par_value + for par_id, par_value in default_problem_parameters.items() + if par_id in free_parameters + } - scaled_parameters = True + problem_parameters = default_problem_parameters | problem_parameters # Get edatas if edatas is None: diff --git a/tests/benchmark-models/test_petab_benchmark.py b/tests/benchmark-models/test_petab_benchmark.py index 976ff3dc05..47bf503984 100644 --- a/tests/benchmark-models/test_petab_benchmark.py +++ b/tests/benchmark-models/test_petab_benchmark.py @@ -36,6 +36,7 @@ "Isensee_JCB2018", "Beer_MolBioSystems2014", "Alkan_SciSignal2018", + "Lang_PLOSComputBiol2024", # excluded due to excessive numerical failures "Crauste_CellSystems2017", "Fujita_SciSignal2010", From 59256b4b9b9ad36b92f6006aad17d2ee744e8b87 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Thu, 16 May 2024 10:53:39 +0200 Subject: [PATCH 124/188] CMake: Fix MKL detection when not using environment modules (#2443) If MKLROOT is set (e.g., `source /opt/intel/oneapi/setvars.sh` will do that), first try FindBLAS instead of just relying on MKL_INCDIR and MKL_LIB. Fixes #2441. --- CMakeLists.txt | 2 +- cmake/AmiciFindBLAS.cmake | 51 ++++++++++++++++++++++++------------- cmake/cmakelang-tools.cmake | 2 ++ swig/CMakeLists.txt | 5 +++- 4 files changed, 41 insertions(+), 19 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d8a1109d90..81cc70c9d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -135,7 +135,7 @@ endif() set(VENDORED_SUNDIALS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ThirdParty/sundials) set(SUNDIALS_PRIVATE_INCLUDE_DIRS "${VENDORED_SUNDIALS_DIR}/src") # Handle different sundials build/install dirs, depending on whether we are -# building the Python extension only or the full C++ interface +# building the Python extension only or the full C++ interface if(AMICI_PYTHON_BUILD_EXT_ONLY) set(VENDORED_SUNDIALS_BUILD_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(VENDORED_SUNDIALS_INSTALL_DIR ${VENDORED_SUNDIALS_BUILD_DIR}) diff --git a/cmake/AmiciFindBLAS.cmake b/cmake/AmiciFindBLAS.cmake index 51d9c9b257..e0553b81c5 100644 --- a/cmake/AmiciFindBLAS.cmake +++ b/cmake/AmiciFindBLAS.cmake @@ -12,18 +12,33 @@ set_property(CACHE BLAS PROPERTY STRINGS "CBLAS" "MKL" "ACCELERATE") if(${BLAS} STREQUAL "MKL" OR DEFINED ENV{MKLROOT}) if(DEFINED ENV{MKLROOT}) - # This is set by Environment Modules - message(STATUS "Using MKL_INCDIR and MKL_LIB from environment module") set(BLAS "MKL" CACHE STRING "BLAS library to use" FORCE) - set(BLAS_INCLUDE_DIRS - "$ENV{MKL_INCDIR}" - CACHE STRING "" FORCE) - set(BLAS_LIBRARIES - "$ENV{MKL_LIB}" - CACHE STRING "" FORCE) + + # Was MKLROOT set by /opt/intel/oneapi/setvars.sh? then cmake will find it + message(STATUS "Trying to find BLAS based on MKLROOT=$ENV{MKLROOT}") + # give the user the option to override the BLA_VENDOR + if(NOT DEFINED BLA_VENDOR) + set(BLA_VENDOR Intel10_64lp) + endif() + message(STATUS "Trying FindBLAS with BLA_VENDOR=${BLA_VENDOR}") + find_package(BLAS) + if(BLAS_FOUND) + message(STATUS "Found BLAS via FindBLAS and MKLROOT") + else() + # This is set by Environment Modules and might not be compatible with + # FindBLAS + message(STATUS "Using MKL_INCDIR and MKL_LIB from environment module") + set(BLAS_INCLUDE_DIRS + "$ENV{MKL_INCDIR}" + CACHE STRING "" FORCE) + set(BLAS_LIBRARIES + "$ENV{MKL_LIB}" + CACHE STRING "" FORCE) + endif() else() + message(STATUS "BLAS is set to MKL, but MKLROOT is not set.") set(BLAS_INCLUDE_DIRS "" CACHE STRING "") @@ -42,6 +57,7 @@ elseif( if(APPLE AND (NOT DEFINED BLA_VENDOR OR BLA_VENDOR STREQUAL "All")) set(BLA_VENDOR Apple) + message(STATUS "Trying FindBLAS with BLA_VENDOR=${BLA_VENDOR}") find_package(BLAS) if(BLAS_FOUND) message(STATUS "Found Apple Accelerate BLAS") @@ -59,6 +75,7 @@ elseif( endif() if(NOT BLAS_FOUND) + message(STATUS "Trying FindBLAS with BLA_VENDOR=${BLA_VENDOR}") find_package(BLAS) if(BLAS_FOUND) message(STATUS "Found BLAS via FindBLAS") @@ -79,17 +96,17 @@ endif() # Create an imported target if(NOT TARGET BLAS::BLAS) add_library(BLAS INTERFACE) - set_target_properties(BLAS PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${BLAS_INCLUDE_DIRS}" - INTERFACE_LINK_LIBRARIES "${BLAS_LIBRARIES}") + set_target_properties( + BLAS PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${BLAS_INCLUDE_DIRS}" + INTERFACE_LINK_LIBRARIES "${BLAS_LIBRARIES}") add_library(BLAS::BLAS ALIAS BLAS) if("${PROJECT_NAME}" STREQUAL "amici") - install(TARGETS BLAS EXPORT BLAS) - export(EXPORT BLAS NAMESPACE BLAS::) - install( - EXPORT BLAS - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Amici" - NAMESPACE BLAS::) + install(TARGETS BLAS EXPORT BLAS) + export(EXPORT BLAS NAMESPACE BLAS::) + install( + EXPORT BLAS + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Amici" + NAMESPACE BLAS::) endif() # legacy python package environment variables: diff --git a/cmake/cmakelang-tools.cmake b/cmake/cmakelang-tools.cmake index ad489500bc..5796938d2c 100644 --- a/cmake/cmakelang-tools.cmake +++ b/cmake/cmakelang-tools.cmake @@ -9,7 +9,9 @@ set(ALL_CMAKE_FILES tests/cpp/unittests/CMakeLists.txt ${CMAKE_MODULE_PATH}/cmakelang-tools.cmake ${CMAKE_MODULE_PATH}/clang-tools.cmake + ${CMAKE_MODULE_PATH}/AmiciFindBLAS.cmake ${CMAKE_MODULE_PATH}/version.cmake) + list(JOIN ALL_CMAKE_FILES " " ALL_CMAKE_FILES) # --- cmake-format --- diff --git a/swig/CMakeLists.txt b/swig/CMakeLists.txt index 7b7baf9be9..5cc6e6b5a2 100644 --- a/swig/CMakeLists.txt +++ b/swig/CMakeLists.txt @@ -20,7 +20,10 @@ find_package( Python3 COMPONENTS Interpreter Development NumPy REQUIRED) -message(STATUS "Found numpy ${Python3_NumPy_VERSION} include dir ${Python3_NumPy_INCLUDE_DIRS}") +message( + STATUS + "Found numpy ${Python3_NumPy_VERSION} include dir ${Python3_NumPy_INCLUDE_DIRS}" +) set(AMICI_INTERFACE_LIST ${CMAKE_CURRENT_SOURCE_DIR}/amici.i ${CMAKE_CURRENT_SOURCE_DIR}/edata.i From 7f7be0f4636eb32267e855b770e8aa2474e1f306 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Thu, 16 May 2024 12:55:33 +0200 Subject: [PATCH 125/188] Specify cmake build types and require cmake-build-extension==0.6.0 (#2445) * Require https://github.com/diegoferigo/cmake-build-extension/releases/tag/v0.6.0 * Specify cmake build types properly Closes #2447 Closes #2442 --- CMakeLists.txt | 1 - python/sdist/amici/setup.template.py | 6 ++++++ python/sdist/pyproject.toml | 4 ++-- python/sdist/setup.py | 5 +++++ python/tests/test_misc.py | 5 +++-- scripts/buildAmici.sh | 3 ++- scripts/installAmiciSource.sh | 2 +- src/CMakeLists.template.cmake | 5 ++++- 8 files changed, 23 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 81cc70c9d0..4b0c95321b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -90,7 +90,6 @@ if("$ENV{ENABLE_AMICI_DEBUGGING}" OR "$ENV{ENABLE_GCOV_COVERAGE}") else() add_compile_options(-O0 -g) endif() - set(CMAKE_BUILD_TYPE "Debug") endif() # coverage options diff --git a/python/sdist/amici/setup.template.py b/python/sdist/amici/setup.template.py index 80fd3fde2a..14edbfd2d4 100644 --- a/python/sdist/amici/setup.template.py +++ b/python/sdist/amici/setup.template.py @@ -24,6 +24,11 @@ def get_extension() -> CMakeExtension: else: os.environ["CMAKE_BUILD_PARALLEL_LEVEL"] = "1" + debug_build = os.getenv("ENABLE_AMICI_DEBUGGING", "").lower() in [ + "1", + "true", + ] or os.getenv("ENABLE_GCOV_COVERAGE", "").lower() in ["1", "true"] + return CMakeExtension( name="model_ext", source_dir=os.getcwd(), @@ -37,6 +42,7 @@ def get_extension() -> CMakeExtension: "-DAMICI_PYTHON_BUILD_EXT_ONLY=ON", f"-DPython3_EXECUTABLE={Path(sys.executable).as_posix()}", ], + cmake_build_type="Debug" if debug_build else "Release", ) diff --git a/python/sdist/pyproject.toml b/python/sdist/pyproject.toml index 10a3b05374..905e564cf4 100644 --- a/python/sdist/pyproject.toml +++ b/python/sdist/pyproject.toml @@ -11,7 +11,7 @@ requires = [ # cf. discussion at https://github.com/numpy/numpy/issues/5888 # (https://github.com/scipy/oldest-supported-numpy/) "oldest-supported-numpy", - "cmake-build-extension==0.5.1", + "cmake-build-extension==0.6.0", ] build-backend = "setuptools.build_meta" @@ -21,7 +21,7 @@ dynamic = ["version"] description = "Advanced multi-language Interface to CVODES and IDAS" requires-python = ">=3.10" dependencies = [ - "cmake-build-extension==0.5.1", + "cmake-build-extension==0.6.0", "sympy>=1.9", "numpy>=1.19.3; python_version=='3.9'", "numpy>=1.21.4; python_version>='3.10'", diff --git a/python/sdist/setup.py b/python/sdist/setup.py index 83bf33237a..9dd268953a 100755 --- a/python/sdist/setup.py +++ b/python/sdist/setup.py @@ -141,6 +141,10 @@ def get_extensions(): ], ) # AMICI + debug_build = os.getenv("ENABLE_AMICI_DEBUGGING", "").lower() in [ + "1", + "true", + ] or os.getenv("ENABLE_GCOV_COVERAGE", "").lower() in ["1", "true"] amici_ext = CMakeExtension( name="amici", install_prefix="amici", @@ -153,6 +157,7 @@ def get_extensions(): "-DAMICI_PYTHON_BUILD_EXT_ONLY=ON", f"-DPython3_EXECUTABLE={Path(sys.executable).as_posix()}", ], + cmake_build_type="Debug" if debug_build else "Release", ) # Order matters! return [suitesparse_config, amd, btf, colamd, klu, sundials, amici_ext] diff --git a/python/tests/test_misc.py b/python/tests/test_misc.py index b68e96f1a0..80e1afaa03 100644 --- a/python/tests/test_misc.py +++ b/python/tests/test_misc.py @@ -65,8 +65,9 @@ def test_cmake_compilation(sbml_example_presimulation_module): amici_dir = (Path(__file__).parents[2] / "build").absolute() cmd = ( f"set -e; " - f"cmake -S {source_dir} -B '{build_dir}' -DAmici_DIR={amici_dir}; " - f"cmake --build '{build_dir}'" + f"cmake -S {source_dir} -B '{build_dir}' " + f"-DCMAKE_BUILD_TYPE=Debug -DAmici_DIR={amici_dir}; " + f"cmake --build '{build_dir}' --config Debug" ) try: diff --git a/scripts/buildAmici.sh b/scripts/buildAmici.sh index 80b724c8c3..cb2428579c 100755 --- a/scripts/buildAmici.sh +++ b/scripts/buildAmici.sh @@ -14,7 +14,8 @@ mkdir -p "${amici_build_dir}" cd "${amici_build_dir}" if [ "${GITHUB_ACTIONS:-}" = true ] || - [ "${ENABLE_AMICI_DEBUGGING:-}" = TRUE ]; then + [ "${ENABLE_AMICI_DEBUGGING:-}" = TRUE ] || + [ "${ENABLE_GCOV_COVERAGE:-}" = TRUE ]; then # Running on CI server build_type="Debug" # exceptions instead of terminate() diff --git a/scripts/installAmiciSource.sh b/scripts/installAmiciSource.sh index 514926773f..1c0a975708 100755 --- a/scripts/installAmiciSource.sh +++ b/scripts/installAmiciSource.sh @@ -33,7 +33,7 @@ fi export PYTHON_EXECUTABLE="${AMICI_PATH}/venv/bin/python" python -m pip install --upgrade pip wheel -python -m pip install --upgrade pip setuptools cmake_build_extension==0.5.1 numpy +python -m pip install --upgrade pip setuptools cmake_build_extension==0.6.0 numpy python -m pip install git+https://github.com/FFroehlich/pysb@fix_pattern_matching # pin to PR for SPM with compartments AMICI_BUILD_TEMP="${AMICI_PATH}/python/sdist/build/temp" \ python -m pip install --verbose -e "${AMICI_PATH}/python/sdist[petab,test,vis]" --no-build-isolation diff --git a/src/CMakeLists.template.cmake b/src/CMakeLists.template.cmake index ee50a551e8..dbad3e146c 100644 --- a/src/CMakeLists.template.cmake +++ b/src/CMakeLists.template.cmake @@ -38,6 +38,10 @@ endif() find_package(Amici TPL_AMICI_VERSION REQUIRED HINTS ${CMAKE_CURRENT_LIST_DIR}/../../build) message(STATUS "Found AMICI ${Amici_DIR}") +set_target_properties(Upstream::amici PROPERTIES + MAP_IMPORTED_CONFIG_RELWITHDEBINFO RelWithDebInfo;Release; + MAP_IMPORTED_CONFIG_RELEASE Release + MAP_IMPORTED_CONFIG_DEBUG Debug;RelWithDebInfo;) # Debug build? if("$ENV{ENABLE_AMICI_DEBUGGING}" OR "$ENV{ENABLE_GCOV_COVERAGE}") @@ -47,7 +51,6 @@ if("$ENV{ENABLE_AMICI_DEBUGGING}" OR "$ENV{ENABLE_GCOV_COVERAGE}") else() add_compile_options(-O0 -g) endif() - set(CMAKE_BUILD_TYPE "Debug") endif() # coverage options From d9b447731960dea8ce69858f295a348b1e19d375 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Thu, 16 May 2024 13:02:06 +0200 Subject: [PATCH 126/188] Bump version, update release notes --- CHANGELOG.md | 16 ++++++++++++++++ version.txt | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc831f49c7..f4eed1f392 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,22 @@ ## v0.X Series +### v0.25.1 (2024-05-16) + +**Fixes** +* Avoid clashes with sympy-entities in `plot_expressions` + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2440 +* PEtab: fix KeyErrors for missing parameters in `fill_in_parameters` + (default values are now used if only a subset of parameters is provided) + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2449 +* CMake: Fix Intel MKL detection when not using environment modules + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2443 +* CMake: Fix some issues with multi-config generators + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2445 + +**Full Changelog**: https://github.com/AMICI-dev/AMICI/compare/v0.25.0...v0.25.1 + + ### v0.25.0 (2024-05-08) This release requires Python >= 3.10. diff --git a/version.txt b/version.txt index d21d277be5..af2dabf3ff 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.25.0 +0.25.1 From 71beb5e55580e53f416fb7d84ee3147ee4c75162 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Mon, 27 May 2024 16:59:04 +0200 Subject: [PATCH 127/188] Update valgrind suppressions --- python/tests/valgrind-python.supp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/python/tests/valgrind-python.supp b/python/tests/valgrind-python.supp index dab3c06dc4..ada85d34fa 100644 --- a/python/tests/valgrind-python.supp +++ b/python/tests/valgrind-python.supp @@ -965,3 +965,13 @@ fun:Py_CompileStringObject ... } + +{ + Python + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:gc_alloc + fun:_PyObject_GC_NewVar + ... +} From 2b8c6a20c2a4c1ea594c1dbede0eba389a855a8e Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Wed, 29 May 2024 18:06:55 +0200 Subject: [PATCH 128/188] Update doxygen to 1.11.0 (#2453) --- documentation/conf.py | 10 ++++++---- scripts/downloadAndBuildDoxygen.sh | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/documentation/conf.py b/documentation/conf.py index a0dcf658fb..3f867e0a3a 100644 --- a/documentation/conf.py +++ b/documentation/conf.py @@ -104,7 +104,9 @@ def install_mtocpp(): def install_doxygen(): """Get a more recent doxygen""" - version = "1.9.7" + version = "1.11.0" + release = f"Release_{version.replace('.', '_')}" + filename = f"doxygen-{version}.linux.bin.tar.gz" doxygen_exe = os.path.join( amici_dir, "ThirdParty", f"doxygen-{version}", "bin", "doxygen" ) @@ -112,9 +114,9 @@ def install_doxygen(): some_dir_on_path = os.environ["PATH"].split(os.pathsep)[0] cmd = ( f"cd '{os.path.join(amici_dir, 'ThirdParty')}' " - f"&& wget 'https://www.doxygen.nl/files/" - f"doxygen-{version}.linux.bin.tar.gz' " - f"&& tar -xzf doxygen-{version}.linux.bin.tar.gz " + f"&& wget 'https://github.com/doxygen/doxygen/releases/download/" + f"{release}/{filename}' " + f"&& tar -xzf '{filename}' " f"&& ln -sf '{doxygen_exe}' '{some_dir_on_path}'" ) subprocess.run(cmd, shell=True, check=True) diff --git a/scripts/downloadAndBuildDoxygen.sh b/scripts/downloadAndBuildDoxygen.sh index 4efa9ab483..a27587d96c 100755 --- a/scripts/downloadAndBuildDoxygen.sh +++ b/scripts/downloadAndBuildDoxygen.sh @@ -9,7 +9,7 @@ DOXYGEN_DIR="${AMICI_PATH}"/ThirdParty/doxygen cd "${AMICI_PATH}"/ThirdParty if [[ ! -d ${DOXYGEN_DIR} ]]; then git clone --single-branch \ - --branch Release_1_10_0 \ + --branch Release_1_11_0 \ --depth 1 \ -c advice.detachedHead=false \ https://github.com/doxygen/doxygen.git "${DOXYGEN_DIR}" From 3fbb90be64a1aafd8d0ed61404a60f867a42a584 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Sun, 16 Jun 2024 14:44:04 +0200 Subject: [PATCH 129/188] PEtab: Fix estimated initial conditions specified via the conditions table (#2456) * For reinitialization of state variables after preequilibration, we need to add *fixed* parameters for initial conditions (otherwise parameter values can't be different for pre-equilibration and the following period). This case was handled fine. * If we want to estimate initial conditions and want to compute sensitivities w.r.t. those, we need *non-fixed* parameters for the initial conditions. This was not handled correctly, those were added as *fixed* parameters. * As a consequence, we *can't* handle reinitialization *and* estimation of initial values for the same state variable. Closes #2455. --- .github/workflows/test_petab_test_suite.yml | 2 +- python/sdist/amici/petab/cli/import_petab.py | 5 +- python/sdist/amici/petab/import_helpers.py | 3 +- python/sdist/amici/petab/sbml_import.py | 72 +++++++++++++++++--- tests/petab_test_suite/test_petab_suite.py | 3 +- 5 files changed, 67 insertions(+), 18 deletions(-) diff --git a/.github/workflows/test_petab_test_suite.yml b/.github/workflows/test_petab_test_suite.yml index 7e9a93c494..c63a733e07 100644 --- a/.github/workflows/test_petab_test_suite.yml +++ b/.github/workflows/test_petab_test_suite.yml @@ -63,7 +63,7 @@ jobs: # retrieve test models - name: Download and install PEtab test suite run: | - git clone --depth 1 --branch main \ + git clone --depth 1 --branch add_test_ic_est \ https://github.com/PEtab-dev/petab_test_suite \ && source ./venv/bin/activate \ && cd petab_test_suite && pip3 install -e . diff --git a/python/sdist/amici/petab/cli/import_petab.py b/python/sdist/amici/petab/cli/import_petab.py index db600b0590..9f29d26e71 100644 --- a/python/sdist/amici/petab/cli/import_petab.py +++ b/python/sdist/amici/petab/cli/import_petab.py @@ -145,10 +145,7 @@ def _main(): import_model_sbml( model_name=args.model_name, - sbml_model=pp.sbml_model, - condition_table=pp.condition_df, - observable_table=pp.observable_df, - measurement_table=pp.measurement_df, + petab_problem=pp, model_output_dir=args.model_output_dir, compile=args.compile, generate_sensitivity_code=args.generate_sensitivity_code, diff --git a/python/sdist/amici/petab/import_helpers.py b/python/sdist/amici/petab/import_helpers.py index 29cbff07e6..94d8e8e0ce 100644 --- a/python/sdist/amici/petab/import_helpers.py +++ b/python/sdist/amici/petab/import_helpers.py @@ -229,9 +229,10 @@ def get_fixed_parameters( # check global parameters if not petab_problem.model.has_entity_with_id(fixed_parameter): # TODO: could still exist as an output parameter? + # TODO: or in the parameters table logger.warning( f"Column '{fixed_parameter}' used in condition " - "table but not entity with the corresponding ID " + "table but no entity with the corresponding ID " "exists. Ignoring." ) fixed_parameters.remove(fixed_parameter) diff --git a/python/sdist/amici/petab/sbml_import.py b/python/sdist/amici/petab/sbml_import.py index 2c43c3adc4..e22a214e18 100644 --- a/python/sdist/amici/petab/sbml_import.py +++ b/python/sdist/amici/petab/sbml_import.py @@ -135,6 +135,7 @@ def import_model_sbml( else SbmlModel.from_file(sbml_model), condition_df=petab.get_condition_df(condition_table), observable_df=petab.get_observable_df(observable_table), + measurement_df=petab.get_measurement_df(measurement_table), ) if petab_problem.observable_df is None: @@ -264,12 +265,50 @@ def import_model_sbml( # feels dirty and should be changed (see also #924) # + # state variable IDs and initial values specified via the conditions' table initial_states = get_states_in_condition_table(petab_problem) + # is there any condition that involves preequilibration? + requires_preequilibration = ( + petab_problem.measurement_df is not None + and petab.PREEQUILIBRATION_CONDITION_ID in petab_problem.measurement_df + and petab_problem.measurement_df[petab.PREEQUILIBRATION_CONDITION_ID] + .notnull() + .any() + ) + estimated_parameters_ids = petab_problem.get_x_ids(free=True, fixed=False) + # any initial states overridden to be estimated via the conditions table? + has_estimated_initial_states = any( + par_id in petab_problem.condition_df[initial_states.keys()].values + for par_id in estimated_parameters_ids + ) + + if ( + has_estimated_initial_states + and requires_preequilibration + and kwargs.setdefault("generate_sensitivity_code", True) + ): + # To support reinitialization of initial conditions after + # preequilibration we need fixed parameters for the initial + # conditions. If we need sensitivities w.r.t. to initial conditions, + # we need to create non-fixed parameters for the initial conditions. + # We can't have both for the same state variable. + # (We could handle it via separate amici models if pre-equilibration + # and estimation of initial values for a given state variable are + # used in separate PEtab conditions.) + # We currently assume that we do need sensitivities w.r.t. initial + # conditions if sensitivities are needed at all. + # TODO: check this state by state, then we can support some additional + # cases + raise NotImplementedError( + "PEtab problems that have both, estimated initial conditions " + "specified in the condition table, and preequilibration with " + "initial conditions specified in the condition table are not " + "supported." + ) + fixed_parameters = [] - if initial_states: + if initial_states and requires_preequilibration: # add preequilibration indicator variable - # NOTE: would only be required if we actually have preequilibration - # adding it anyways. can be optimized-out later if sbml_model.getParameter(PREEQ_INDICATOR_ID) is not None: raise AssertionError( "Model already has a parameter with ID " @@ -286,11 +325,15 @@ def import_model_sbml( "Adding preequilibration indicator " f"constant {PREEQ_INDICATOR_ID}" ) - logger.debug(f"Adding initial assignments for {initial_states.keys()}") + logger.debug( + f"Adding initial assignments for {list(initial_states.keys())}" + ) for assignee_id in initial_states: init_par_id_preeq = f"initial_{assignee_id}_preeq" init_par_id_sim = f"initial_{assignee_id}_sim" - for init_par_id in [init_par_id_preeq, init_par_id_sim]: + for init_par_id in ( + [init_par_id_preeq] if requires_preequilibration else [] + ) + [init_par_id_sim]: if sbml_model.getElementBySId(init_par_id) is not None: raise ValueError( "Cannot create parameter for initial assignment " @@ -300,8 +343,12 @@ def import_model_sbml( init_par = sbml_model.createParameter() init_par.setId(init_par_id) init_par.setName(init_par_id) - # must be a fixed parameter in any case to allow reinitialization - fixed_parameters.append(init_par_id) + if requires_preequilibration: + # must be a fixed parameter to allow reinitialization + # TODO: also add other initial condition parameters that are + # not estimated + fixed_parameters.append(init_par_id) + assignment = sbml_model.getInitialAssignment(assignee_id) if assignment is None: assignment = sbml_model.createInitialAssignment() @@ -315,10 +362,13 @@ def import_model_sbml( "be overwritten to handle preequilibration and " "initial values specified by the PEtab problem." ) - formula = ( - f"{PREEQ_INDICATOR_ID} * {init_par_id_preeq} " - f"+ (1 - {PREEQ_INDICATOR_ID}) * {init_par_id_sim}" - ) + if requires_preequilibration: + formula = ( + f"{PREEQ_INDICATOR_ID} * {init_par_id_preeq} " + f"+ (1 - {PREEQ_INDICATOR_ID}) * {init_par_id_sim}" + ) + else: + formula = init_par_id_sim math_ast = libsbml.parseL3Formula(formula) assignment.setMath(math_ast) # diff --git a/tests/petab_test_suite/test_petab_suite.py b/tests/petab_test_suite/test_petab_suite.py index 6d08cfb693..fee6c6d763 100755 --- a/tests/petab_test_suite/test_petab_suite.py +++ b/tests/petab_test_suite/test_petab_suite.py @@ -178,8 +178,9 @@ def check_derivatives( problem_parameters=problem_parameters, ): # check_derivatives does currently not support parameters in ExpData - model.setParameters(edata.parameters) + # set parameter scales before setting parameter values! model.setParameterScale(edata.pscale) + model.setParameters(edata.parameters) edata.parameters = [] edata.pscale = amici.parameterScalingFromIntVector([]) amici_check_derivatives(model, solver, edata) From c28b8931253f6e0310efaba84df98e70e7de75bf Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Sun, 16 Jun 2024 14:52:38 +0200 Subject: [PATCH 130/188] Fix PEtab test suite branch --- .github/workflows/test_petab_test_suite.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_petab_test_suite.yml b/.github/workflows/test_petab_test_suite.yml index c63a733e07..7e9a93c494 100644 --- a/.github/workflows/test_petab_test_suite.yml +++ b/.github/workflows/test_petab_test_suite.yml @@ -63,7 +63,7 @@ jobs: # retrieve test models - name: Download and install PEtab test suite run: | - git clone --depth 1 --branch add_test_ic_est \ + git clone --depth 1 --branch main \ https://github.com/PEtab-dev/petab_test_suite \ && source ./venv/bin/activate \ && cd petab_test_suite && pip3 install -e . From 6277a5e3faabf8eff6eadaf787d67f99fccb3013 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Sun, 16 Jun 2024 15:02:19 +0200 Subject: [PATCH 131/188] Fix missing parameter table in performance test --- tests/performance/test.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/performance/test.py b/tests/performance/test.py index c497577ab1..f117371910 100755 --- a/tests/performance/test.py +++ b/tests/performance/test.py @@ -77,10 +77,7 @@ def run_import(model_name, model_dir: Path): import_model( model_name=model_name, model_output_dir=model_dir, - sbml_model=pp.sbml_model, - condition_table=pp.condition_df, - observable_table=pp.observable_df, - measurement_table=pp.measurement_df, + petab_problem=pp, compile=False, verbose=True, ) From 6393c0c14650ac5f5dbe38a96444af48f93a835a Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Sun, 16 Jun 2024 18:34:13 +0200 Subject: [PATCH 132/188] Numpy 2.0 compatibility (#2392) * Fix numpy2.0 np.infty -> np.inf Fixes `AttributeError: `np.infty` was removed in the NumPy 2.0 release. Use `np.inf` instead.` * Require numpy>=2.0 at build time --- python/sdist/pyproject.toml | 8 +------- python/tests/test_preequilibration.py | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/python/sdist/pyproject.toml b/python/sdist/pyproject.toml index 905e564cf4..f93d0c7e1b 100644 --- a/python/sdist/pyproject.toml +++ b/python/sdist/pyproject.toml @@ -4,13 +4,7 @@ requires = [ "setuptools>=61", "wheel", - # oldest-supported-numpy helps us to pin numpy here to the lowest supported - # version to have ABI-compatibility with the numpy version in the runtime - # environment. The undesirable alternative would be pinning the setup.py - # numpy requirement to the same version as here, which we want to avoid. - # cf. discussion at https://github.com/numpy/numpy/issues/5888 - # (https://github.com/scipy/oldest-supported-numpy/) - "oldest-supported-numpy", + "numpy>=2.0", "cmake-build-extension==0.6.0", ] build-backend = "setuptools.build_meta" diff --git a/python/tests/test_preequilibration.py b/python/tests/test_preequilibration.py index d9a2335459..bb657b4153 100644 --- a/python/tests/test_preequilibration.py +++ b/python/tests/test_preequilibration.py @@ -32,7 +32,7 @@ def preeq_fixture(pysb_example_presimulation_module): edata_preeq = amici.ExpData(edata) edata_preeq.t_presim = 0 - edata_preeq.setTimepoints([np.infty]) + edata_preeq.setTimepoints([np.inf]) edata_preeq.fixedParameters = edata.fixedParametersPreequilibration edata_preeq.fixedParametersPresimulation = () edata_preeq.fixedParametersPreequilibration = () From 3004be61c18420de34f100769a006ea76779c193 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Sun, 16 Jun 2024 18:37:39 +0200 Subject: [PATCH 133/188] Remove deprecated functionality for PEtab import from individual files instead of petab.Problem (#2459) Since almost two years, it's possible to pass a `petab.Problem`, which is safer and more convenient. Omitting the parameter table for model import (since there is no option to supply this) is likely to produce unwanted results (see #2458 and #2455 for more details), and therefore, this functionality is best removed. Closes #2458 --- python/sdist/amici/petab/sbml_import.py | 42 ------------------------- 1 file changed, 42 deletions(-) diff --git a/python/sdist/amici/petab/sbml_import.py b/python/sdist/amici/petab/sbml_import.py index e22a214e18..6acf4587f7 100644 --- a/python/sdist/amici/petab/sbml_import.py +++ b/python/sdist/amici/petab/sbml_import.py @@ -5,11 +5,9 @@ from itertools import chain from pathlib import Path from typing import Union -from warnings import warn import amici import libsbml -import pandas as pd import petab import sympy as sp from _collections import OrderedDict @@ -31,9 +29,6 @@ @log_execution_time("Importing PEtab model", logger) def import_model_sbml( sbml_model: Union[str, Path, "libsbml.Model"] = None, - condition_table: str | Path | pd.DataFrame | None = None, - observable_table: str | Path | pd.DataFrame | None = None, - measurement_table: str | Path | pd.DataFrame | None = None, petab_problem: petab.Problem = None, model_name: str | None = None, model_output_dir: str | Path | None = None, @@ -52,18 +47,6 @@ def import_model_sbml( PEtab SBML model or SBML file name. Deprecated, pass ``petab_problem`` instead. - :param condition_table: - PEtab condition table. If provided, parameters from there will be - turned into AMICI constant parameters (i.e. parameters w.r.t. which - no sensitivities will be computed). - Deprecated, pass ``petab_problem`` instead. - - :param observable_table: - PEtab observable table. Deprecated, pass ``petab_problem`` instead. - - :param measurement_table: - PEtab measurement table. Deprecated, pass ``petab_problem`` instead. - :param petab_problem: PEtab problem. @@ -113,31 +96,6 @@ def import_model_sbml( logger.info("Importing model ...") - if any([sbml_model, condition_table, observable_table, measurement_table]): - warn( - "The `sbml_model`, `condition_table`, `observable_table`, and " - "`measurement_table` arguments are deprecated and will be " - "removed in a future version. Use `petab_problem` instead.", - DeprecationWarning, - stacklevel=2, - ) - if petab_problem: - raise ValueError( - "Must not pass a `petab_problem` argument in " - "combination with any of `sbml_model`, " - "`condition_table`, `observable_table`, or " - "`measurement_table`." - ) - - petab_problem = petab.Problem( - model=SbmlModel(sbml_model) - if isinstance(sbml_model, libsbml.Model) - else SbmlModel.from_file(sbml_model), - condition_df=petab.get_condition_df(condition_table), - observable_df=petab.get_observable_df(observable_table), - measurement_df=petab.get_measurement_df(measurement_table), - ) - if petab_problem.observable_df is None: raise NotImplementedError( "PEtab import without observables table " From 350ff6c091125cbc06cfceee969525ffd05925b5 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Sun, 16 Jun 2024 18:38:07 +0200 Subject: [PATCH 134/188] Update reference list (#2414) * Update reference list * LakrisenkoPat2024 * Mutsuddy2024 --- documentation/amici_refs.bib | 46 ++++++++++++++++++++++++++++-------- documentation/references.md | 28 +++++++++++++++------- 2 files changed, 56 insertions(+), 18 deletions(-) diff --git a/documentation/amici_refs.bib b/documentation/amici_refs.bib index 550a58ecc6..b14d0e18b0 100644 --- a/documentation/amici_refs.bib +++ b/documentation/amici_refs.bib @@ -1196,16 +1196,6 @@ @Article{TunedalVio2023 url = {https://physoc.onlinelibrary.wiley.com/doi/abs/10.1113/JP284652}, } -@Unknown{HasenauerMer2023, - author = {Hasenauer, Jan and Merkt, Simon and Ali, Solomon and Gudina, Esayas and Adissu, Wondimagegn and Münchhoff, Maximilian and Graf, Alexander and Krebs, Stefan and Elsbernd, Kira and Kisch, Rebecca and Sirgu, Sisay and Fantahun, Bereket and Bekele, Delayehu and Rubio-Acero, Raquel and Gashaw, Mulatu and Girma, Eyob and Yilma, Daniel and Zeynudin, Ahmed and Paunovic, Ivana and Wieser, Andreas}, - creationdate = {2023-09-19T09:21:01}, - doi = {10.21203/rs.3.rs-3307821/v1}, - modificationdate = {2023-09-19T09:21:01}, - month = {09}, - title = {Long-term monitoring of SARS-CoV-2 seroprevalence and variants in Ethiopia provides prediction for immunity and cross-immunity}, - year = {2023}, -} - @Article{RaimundezFed2023, author = {Elba Raim{\'{u}}ndez and Michael Fedders and Jan Hasenauer}, journal = {{iScience}}, @@ -1306,6 +1296,42 @@ @Article{DoresicGre2024 url = {https://www.biorxiv.org/content/early/2024/01/30/2024.01.26.577371}, } +@Article{MerktAli2024, + author = {Merkt, Simon and Ali, Solomon and Gudina, Esayas Kebede and Adissu, Wondimagegn and Gize, Addisu and Muenchhoff, Maximilian and Graf, Alexander and Krebs, Stefan and Elsbernd, Kira and Kisch, Rebecca and Betizazu, Sisay Sirgu and Fantahun, Bereket and Bekele, Delayehu and Rubio-Acero, Raquel and Gashaw, Mulatu and Girma, Eyob and Yilma, Daniel and Zeynudin, Ahmed and Paunovic, Ivana and Hoelscher, Michael and Blum, Helmut and Hasenauer, Jan and Kroidl, Arne and Wieser, Andreas}, + journal = {Nature Communications}, + title = {Long-term monitoring of SARS-CoV-2 seroprevalence and variants in Ethiopia provides prediction for immunity and cross-immunity}, + year = {2024}, + issn = {2041-1723}, + month = apr, + number = {1}, + volume = {15}, + creationdate = {2024-04-29T08:30:06}, + doi = {10.1038/s41467-024-47556-2}, + modificationdate = {2024-04-29T08:30:06}, + publisher = {Springer Science and Business Media LLC}, +} + +@Misc{LakrisenkoPat2024, + author = {Polina Lakrisenko and Dilan Pathirana and Daniel Weindl and Jan Hasenauer}, + title = {Exploration of methods for computing sensitivities in ODE models at dynamic and steady states}, + year = {2024}, + archiveprefix = {arXiv}, + creationdate = {2024-05-30T09:48:00}, + eprint = {2405.16524}, + modificationdate = {2024-05-30T09:48:00}, + primaryclass = {q-bio.QM}, +} + +@PhdThesis{Mutsuddy2024, + author = {Mutsuddy, Arnab}, + school = {Clemson University}, + title = {Single cell pharmacodynamic modeling of cancer cell lines}, + year = {2024}, + creationdate = {2024-05-30T09:51:58}, + modificationdate = {2024-05-30T09:53:40}, + url = {https://tigerprints.clemson.edu/all_dissertations/3572}, +} + @Comment{jabref-meta: databaseType:bibtex;} @Comment{jabref-meta: grouping: diff --git a/documentation/references.md b/documentation/references.md index 0a2759f4eb..f17da06c55 100644 --- a/documentation/references.md +++ b/documentation/references.md @@ -1,6 +1,6 @@ # References -List of publications using AMICI. Total number is 85. +List of publications using AMICI. Total number is 87. If you applied AMICI in your work and your publication is missing, please let us know via a new GitHub issue. @@ -29,6 +29,12 @@ complex.” Nucleic Acids Research, February, gkae123. https://doi.org/10.1093/nar/gkae123.

+
+Lakrisenko, Polina, Dilan Pathirana, Daniel Weindl, and Jan Hasenauer. +2024. “Exploration of Methods for Computing Sensitivities in ODE +Models at Dynamic and Steady States.” https://arxiv.org/abs/2405.16524. +
Lang, Paul F., David R. Penas, Julio R. Banga, Daniel Weindl, and Bela Novak. 2024. “Reusable Rule-Based Cell Cycle Model Explains @@ -36,6 +42,19 @@ Compartment-Resolved Dynamics of 16 Observables in RPE-1 Cells.” PLOS Computational Biology 20 (1): 1–24. https://doi.org/10.1371/journal.pcbi.1011151.
+
+Merkt, Simon, Solomon Ali, Esayas Kebede Gudina, Wondimagegn Adissu, +Addisu Gize, Maximilian Muenchhoff, Alexander Graf, et al. 2024. +“Long-Term Monitoring of SARS-CoV-2 Seroprevalence and Variants in +Ethiopia Provides Prediction for Immunity and Cross-Immunity.” +Nature Communications 15 (1). https://doi.org/10.1038/s41467-024-47556-2. +
+
+Mutsuddy, Arnab. 2024. “Single Cell Pharmacodynamic Modeling of +Cancer Cell Lines.” PhD thesis, Clemson University. https://tigerprints.clemson.edu/all_dissertations/3572. +
Sluijs, Bob van, Tao Zhou, Britta Helwig, Mathieu G. Baltussen, Frank H. T. Nelissen, Hans A. Heus, and Wilhelm T. S. Huck. 2024. @@ -84,13 +103,6 @@ Rewiring Contribute to Drug Resistance.” Molecular Systems Biology 19 (2): e10988. https://doi.org/10.15252/msb.202210988.
-
-Hasenauer, Jan, Simon Merkt, Solomon Ali, Esayas Gudina, Wondimagegn -Adissu, Maximilian Münchhoff, Alexander Graf, et al. 2023. -“Long-Term Monitoring of SARS-CoV-2 Seroprevalence and Variants in -Ethiopia Provides Prediction for Immunity and Cross-Immunity.” https://doi.org/10.21203/rs.3.rs-3307821/v1. -
Huck, Wilhelm, Mathieu Baltussen, Thijs de Jong, Quentin Duez, and William Robinson. 2023. “Chemical Reservoir Computation in a From 6da397a442ef490c52a489d90215d9179d00408b Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Sun, 16 Jun 2024 14:50:39 +0200 Subject: [PATCH 135/188] Bump version number, update release notes --- CHANGELOG.md | 17 +++++++++++++++++ documentation/rtd_requirements.txt | 1 + version.txt | 2 +- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4eed1f392..b466d2d491 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,23 @@ ## v0.X Series +### v0.25.2 (2024-06-16) + +**Fixes** + +* Fixed a bug in PEtab import which led to incorrect gradients + *w.r.t. estimated initial values specified via the condition table* + **BREAKING CHANGE**: + `amici.petab.sbml_import.{import_model_sbml,import_model}` no longer supports + passing individual PEtab tables, but only the PEtab problem object. + This functionality was deprecated since v0.12.0 (2022-08-26). +* Fixes for numpy 2.0 compatibility + **NOTE**: As long as some amici dependencies don't support numpy 2.0 yet, + you may need to pin numpy to <2.0 in your requirements + (`pip install amici "numpy<2.0"`). + +**Full Changelog**: https://github.com/AMICI-dev/AMICI/compare/v0.25.1...v0.25.2 + ### v0.25.1 (2024-05-16) **Fixes** diff --git a/documentation/rtd_requirements.txt b/documentation/rtd_requirements.txt index 9b5cc9709b..06cf506666 100644 --- a/documentation/rtd_requirements.txt +++ b/documentation/rtd_requirements.txt @@ -23,3 +23,4 @@ git+https://github.com/readthedocs/readthedocs-sphinx-ext ipykernel -e git+https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab.git@master#subdirectory=src/python&egg=benchmark_models_petab -e python/sdist/ +numpy<2.0 diff --git a/version.txt b/version.txt index af2dabf3ff..166c9e29b7 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.25.1 +0.25.2 From 043d13a21ef241fa83688097e153233027789e0e Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Sun, 16 Jun 2024 20:22:42 +0200 Subject: [PATCH 136/188] Update valgrind suppressions --- python/tests/valgrind-python.supp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/python/tests/valgrind-python.supp b/python/tests/valgrind-python.supp index ada85d34fa..16c92e3d1f 100644 --- a/python/tests/valgrind-python.supp +++ b/python/tests/valgrind-python.supp @@ -975,3 +975,13 @@ fun:_PyObject_GC_NewVar ... } + +{ + Python + Memcheck:Leak + match-leak-kinds: definite + fun:realloc + fun:_PyObject_GC_Resize + fun:_PyTuple_Resize + ... +} From 9e310132153d5ee5a8771b72828a480263b6e786 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Wed, 26 Jun 2024 11:47:33 +0200 Subject: [PATCH 137/188] Require sympy>=1.12.1 for numpy>=2.0 compatibility (#2462) So far, sympy was restricted to 1.11 via pysb, but sympy 1.11 is incompatible with numpy>=2.0 --- python/sdist/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/sdist/pyproject.toml b/python/sdist/pyproject.toml index f93d0c7e1b..e24bf6811a 100644 --- a/python/sdist/pyproject.toml +++ b/python/sdist/pyproject.toml @@ -16,7 +16,7 @@ description = "Advanced multi-language Interface to CVODES and IDAS" requires-python = ">=3.10" dependencies = [ "cmake-build-extension==0.6.0", - "sympy>=1.9", + "sympy>=1.12.1", "numpy>=1.19.3; python_version=='3.9'", "numpy>=1.21.4; python_version>='3.10'", "numpy>=1.23.2; python_version=='3.11'", From 474b473b33eab4c92b03aca59577e8333aedc2fb Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Wed, 26 Jun 2024 11:48:19 +0200 Subject: [PATCH 138/188] Update AMICI papers (#2466) --- documentation/background.rst | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/documentation/background.rst b/documentation/background.rst index 2a2e748672..f47c077d46 100644 --- a/documentation/background.rst +++ b/documentation/background.rst @@ -32,14 +32,22 @@ publications: * Lakrisenko, Polina, Paul Stapor, Stephan Grein, Łukasz Paszkowski, Dilan Pathirana, Fabian Fröhlich, Glenn Terje Lines, Daniel Weindl, - and Jan Hasenauer. 2022. + and Jan Hasenauer. 2023. **Efficient Computation of Adjoint Sensitivities at Steady-State in ODE Models - of Biochemical Reaction Networks.** *bioRxiv* 2022.08.08.503176. - DOI: `10.1101/2022.08.08.503176 `_. + of Biochemical Reaction Networks.** *PLoS Comput Biol* 19(1): e1010783. + DOI: `10.1371/journal.pcbi.1010783 `_. + +* L. Contento, P. Stapor, D. Weindl, and J. Hasenauer. 2023. + **A more expressive spline representation for SBML models improves code generation performance in AMICI**, + In: Pang, J., Niehren, J. (eds) Computational Methods in Systems Biology. + CMSB 2023. *Lecture Notes in Computer Science*, vol 14137. Springer, Cham. + DOI: `10.1007/978-3-031-42697-1_3 `_. + Preprint available at `bioRxiv `_. + +* Lakrisenko, Polina, Dilan Pathirana, Daniel Weindl, and Jan Hasenauer. 2024. + **Exploration of methods for computing sensitivities in ODE models at dynamic and steady states.** *arXiv:2405.16524 [q-bio.QM]*. + DOI: `10.48550/arXiv.2405.16524 `_. -* L. Contento, P. Stapor, D. Weindl, and J. Hasenauer, "A more expressive spline - representation for SBML models improves code generation performance in AMICI," - bioRxiv, 2023, DOI: `10.1101/2023.06.29.547120 `_. .. note:: From f54abe6534b6552acab647c63b09a5226c86cda1 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Wed, 26 Jun 2024 14:45:57 +0200 Subject: [PATCH 139/188] Add deprecation policy (#2465) Add deprecation policy As suggested in https://github.com/AMICI-dev/AMICI/issues/2458. --- CHANGELOG.md | 2 ++ documentation/index.rst | 1 + documentation/versioning_policy.rst | 28 ++++++++++++++++++++++++++++ 3 files changed, 31 insertions(+) create mode 100644 documentation/versioning_policy.rst diff --git a/CHANGELOG.md b/CHANGELOG.md index b466d2d491..fcdc7a222a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Changelog +See also our [versioning policy](https://amici.readthedocs.io/en/latest/versioning_policy.html). + ## v0.X Series ### v0.25.2 (2024-06-16) diff --git a/documentation/index.rst b/documentation/index.rst index 73343e76c1..63fc3261ab 100644 --- a/documentation/index.rst +++ b/documentation/index.rst @@ -27,6 +27,7 @@ Welcome to AMICI's documentation! references background changelog + versioning_policy glossary contributing diff --git a/documentation/versioning_policy.rst b/documentation/versioning_policy.rst new file mode 100644 index 0000000000..bed419f02a --- /dev/null +++ b/documentation/versioning_policy.rst @@ -0,0 +1,28 @@ +.. _versioning_policy: + +Versioning policy +================= + +Versioning +---------- + +We use `Semantic Versioning `_ with the modifications +described under :ref:`deprecation_policy`. + +.. _deprecation_policy: + +Deprecation policy +------------------ + +AMICI aims to provide a stable API for users. However, not all features can be +maintained indefinitely. We will deprecate features in minor releases and +where possible, issue a warning when they are used. We will keep deprecated +features for at least six months after the release that includes the +respective deprecation warning and then remove them earliest in the next minor +or major release. If a deprecated feature is the source of a major bug, we may +remove it earlier. + +Python compatibility +-------------------- + +We follow `numpy's Python support policy `_. From 538fd59d2b230951720ab8f23994dc3259f17dfc Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Wed, 26 Jun 2024 15:07:19 +0200 Subject: [PATCH 140/188] Deprecate passing individual tables to `amici_import_petab` (#2464) Deprecated passing individual tables to `amici_import_petab` and replace deprecated `petab.Problem.from_files` in `amici.petab.cli.import_petab._main`. Fixes #2460 --- python/sdist/amici/petab/cli/import_petab.py | 69 +++++++++++++++---- .../test_benchmark_collection.sh | 19 ++++- 2 files changed, 72 insertions(+), 16 deletions(-) diff --git a/python/sdist/amici/petab/cli/import_petab.py b/python/sdist/amici/petab/cli/import_petab.py index 9f29d26e71..39fa9b9bfe 100644 --- a/python/sdist/amici/petab/cli/import_petab.py +++ b/python/sdist/amici/petab/cli/import_petab.py @@ -3,6 +3,7 @@ import petab from ..petab_import import import_model_sbml +from petab.models.sbml_model import SbmlModel def _parse_cli_args(): @@ -59,28 +60,33 @@ def _parse_cli_args(): ) # Call with set of files - parser.add_argument( + group = parser.add_argument_group( + "Providing individual PEtab tables *DEPRECATED*. " + "Pass a PEtab yaml file instead." + ) + + group.add_argument( "-s", "--sbml", dest="sbml_file_name", help="SBML model filename" ) - parser.add_argument( + group.add_argument( "-m", "--measurements", dest="measurement_file_name", help="Measurement table", ) - parser.add_argument( + group.add_argument( "-c", "--conditions", dest="condition_file_name", help="Conditions table", ) - parser.add_argument( + group.add_argument( "-p", "--parameters", dest="parameter_file_name", help="Parameter table", ) - parser.add_argument( + group.add_argument( "-b", "--observables", dest="observable_file_name", @@ -90,8 +96,15 @@ def _parse_cli_args(): parser.add_argument( "-y", "--yaml", + dest="yaml_file_name_deprecated", + help="PEtab YAML problem filename. *DEPRECATED* Pass the YAML file " + "as positional argument instead.", + ) + + parser.add_argument( dest="yaml_file_name", - help="PEtab YAML problem filename", + help="PEtab YAML problem filename.", + nargs="?", ) parser.add_argument( @@ -102,18 +115,42 @@ def _parse_cli_args(): ) args = parser.parse_args() - - if not args.yaml_file_name and not all( + if any( + [ + args.sbml_file_name, + args.condition_file_name, + args.observable_file_name, + args.measurement_file_name, + args.parameter_file_name, + ] + ): + print( + "WARNING: Passing individual tables to amico_import_petab is " + "deprecated, please pass a PEtab YAML file instead." + ) + if ( + not args.yaml_file_name and not args.yaml_file_name_deprecated + ) and not all( ( args.sbml_file_name, args.condition_file_name, args.observable_file_name, + args.measurement_file_name, + args.parameter_file_name, ) ): parser.error( "When not specifying a model name or YAML file, then " - "SBML, condition and observable file must be specified" + "SBML, condition, observable, measurement and parameter file must " + "be specified." + ) + + if args.yaml_file_name_deprecated: + print( + "WARNING: -y/--yaml is deprecated. Pass the YAML file as " + "positional argument instead." ) + args.yaml_file_name = args.yaml_file_name_deprecated return args @@ -128,12 +165,14 @@ def _main(): if args.yaml_file_name: pp = petab.Problem.from_yaml(args.yaml_file_name) else: - pp = petab.Problem.from_files( - sbml_file=args.sbml_file_name, - condition_file=args.condition_file_name, - measurement_file=args.measurement_file_name, - parameter_file=args.parameter_file_name, - observable_files=args.observable_file_name, + pp = petab.Problem( + model=SbmlModel.from_file(args.sbml_file_name), + condition_df=petab.get_condition_df(args.condition_file_name), + measurement_df=petab.get_measurement_df( + args.measurement_file_name + ), + parameter_df=petab.get_parameter_df(args.parameter_file_name), + observable_df=petab.get_observable_df(args.observable_file_name), ) # Check for valid PEtab before potentially modifying it diff --git a/tests/benchmark-models/test_benchmark_collection.sh b/tests/benchmark-models/test_benchmark_collection.sh index 6b9284f4e6..f8ccd0c1cd 100755 --- a/tests/benchmark-models/test_benchmark_collection.sh +++ b/tests/benchmark-models/test_benchmark_collection.sh @@ -89,7 +89,7 @@ for model in $models; do yaml="${model_dir}"/"${model}"/"${model}".yaml amici_model_dir=test_bmc/"${model}" mkdir -p "$amici_model_dir" - cmd_import="amici_import_petab -y ${yaml} -o ${amici_model_dir} -n ${model} --flatten" + cmd_import="amici_import_petab ${yaml} -o ${amici_model_dir} -n ${model} --flatten" cmd_run="$script_path/test_petab_model.py -y ${yaml} -d ${amici_model_dir} -m ${model} -c" printf '=%.0s' {1..40} @@ -111,3 +111,20 @@ for model in $models; do done cd "$script_path" && python evaluate_benchmark.py + +# Test deprecated import from individual PEtab files +model="Zheng_PNAS2012" +problem_dir="${model_dir}/${model}" +amici_model_dir=test_bmc/"${model}-deprecated" +cmd_import="amici_import_petab -s "${problem_dir}/model_${model}.xml" \ + -m "${problem_dir}/measurementData_${model}.tsv" \ + -c "${problem_dir}/experimentalCondition_${model}.tsv" \ + -p "${problem_dir}/parameters_${model}.tsv" \ + -b "${problem_dir}/observables_${model}.tsv" \ + -o ${amici_model_dir} -n ${model} --no-compile" + +if [[ -z "$dry_run" ]]; then + $cmd_import +else + echo "$cmd_import" +fi From 3cfb8e724f15be955e7028ac4db856173c4631bd Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Wed, 26 Jun 2024 15:18:48 +0200 Subject: [PATCH 141/188] GHA: sympy>=1.12.1 --- .github/workflows/test_python_ver_matrix.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test_python_ver_matrix.yml b/.github/workflows/test_python_ver_matrix.yml index 01d455b18a..da2669b866 100644 --- a/.github/workflows/test_python_ver_matrix.yml +++ b/.github/workflows/test_python_ver_matrix.yml @@ -57,6 +57,9 @@ jobs: - run: source venv/bin/activate && pip3 install git+https://github.com/sympy/sympy.git@master if: matrix.python-version == '3.12' + - run: source venv/bin/activate && pip3 install "sympy>=1.12.1" + if: matrix.python-version != '3.12' + - name: Python tests run: | source venv/bin/activate \ From e1ab9fb1f54a02bad56326fafbe556eddae8797d Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Wed, 26 Jun 2024 17:20:53 +0200 Subject: [PATCH 142/188] Doc: fix formatting (#2470) --- documentation/python_installation.rst | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/documentation/python_installation.rst b/documentation/python_installation.rst index eb7d499227..3c893bcd61 100644 --- a/documentation/python_installation.rst +++ b/documentation/python_installation.rst @@ -31,8 +31,8 @@ If this does not work for you, please follow the full instructions below. Installation on Linux +++++++++++++++++++++ -Ubuntu 22.04 ------------- +Ubuntu 22.04 / 24.04 +-------------------- Install the AMICI dependencies via ``apt`` (this requires superuser privileges): @@ -44,8 +44,8 @@ Install the AMICI dependencies via ``apt`` # optionally for HDF5 support: sudo apt install libhdf5-serial-dev - # optionally for boost support (thread-specific CPU times, extended math functions, serialization) - libboost-chrono-dev libboost-math-dev libboost-serialization-dev + # optionally for boost support (thread-specific CPU times, extended math functions, serialization) + libboost-chrono-dev libboost-math-dev libboost-serialization-dev Install AMICI: @@ -203,9 +203,8 @@ Newer installations could be located under so that it matches your directory structure. This will download OpenBLAS and compile it, creating - - C:\\BLAS\\OpenBLAS\\lib\\openblas.lib - C:\\BLAS\\OpenBLAS\\bin\\openblas.dll +``C:\\BLAS\\OpenBLAS\\lib\\openblas.lib`` and +``C:\\BLAS\\OpenBLAS\\bin\\openblas.dll``. You will also need to define two environment variables: @@ -231,8 +230,8 @@ Now you need to make sure that all required DLLs are within the scope of the ``PATH`` variable. In particular, the following directories need to be included in ``PATH``: - C:\\BLAS\\OpenBLAS\\bin - C:\\Program Files (x86)\\Windows Kits\\10\\Redist\\ucrt\\DLLs\\x64 +* ``C:\BLAS\OpenBLAS\bin`` +* ``C:\Program Files (x86)\Windows Kits\10\Redist\ucrt\DLLs\x64`` The first one is needed for ``openblas.dll`` and the second is needed for the Windows Universal C Runtime. @@ -240,7 +239,7 @@ Windows Universal C Runtime. If any DLLs are missing in the ``PATH`` variable, Python will return the following error upon ``import amici``: - ImportError: DLL load failed: The specified module could not be found. +``ImportError: DLL load failed: The specified module could not be found.`` Almost all of the DLLs are standard Windows DLLs and should be included in either Windows or Visual Studio. But, in case it is necessary to test this, From f8ce650154815899faa7e332b8a61f9fab2e7de0 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Thu, 27 Jun 2024 13:11:14 +0200 Subject: [PATCH 143/188] CMake: Set SUNDIALS_ROOT (#2468) Set SUNDIALS_ROOT to ensure we are finding the sundials CMake config from the vendored library and not any other installation. Fixes #2467. --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4b0c95321b..9b0374b3a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -142,9 +142,9 @@ else() set(VENDORED_SUNDIALS_BUILD_DIR ${VENDORED_SUNDIALS_DIR}/build) set(VENDORED_SUNDIALS_INSTALL_DIR ${VENDORED_SUNDIALS_BUILD_DIR}) endif() +set(SUNDIALS_ROOT "${VENDORED_SUNDIALS_INSTALL_DIR}/${CMAKE_INSTALL_LIBDIR}") find_package( - SUNDIALS REQUIRED PATHS - "${VENDORED_SUNDIALS_INSTALL_DIR}/${CMAKE_INSTALL_LIBDIR}/cmake/sundials/") + SUNDIALS REQUIRED CONFIG PATHS "${SUNDIALS_ROOT}/cmake/sundials/") message(STATUS "Found SUNDIALS: ${SUNDIALS_DIR}") set(GSL_LITE_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ThirdParty/gsl") From 65a113848cc17a0115d1f42f39abb0c9761d0e6b Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Mon, 1 Jul 2024 11:17:49 +0200 Subject: [PATCH 144/188] Update reference list (#2472) * Update reference list * BaltussenJon2024 * issue link * .. --- documentation/amici_refs.bib | 50 ++++++++++++++++++------ documentation/recreate_reference_list.py | 5 ++- documentation/references.md | 18 ++++++++- 3 files changed, 58 insertions(+), 15 deletions(-) diff --git a/documentation/amici_refs.bib b/documentation/amici_refs.bib index b14d0e18b0..7321d97194 100644 --- a/documentation/amici_refs.bib +++ b/documentation/amici_refs.bib @@ -1069,18 +1069,19 @@ @Article{LakrisenkoSta2023 } @Article{ContentoCas2023, - author = {Lorenzo Contento and Noemi Castelletti and Elba Raimúndez and Ronan {Le Gleut} and Yannik Schälte and Paul Stapor and Ludwig Christian Hinske and Michael Hoelscher and Andreas Wieser and Katja Radon and Christiane Fuchs and Jan Hasenauer}, - journal = {Epidemics}, - title = {Integrative modelling of reported case numbers and seroprevalence reveals time-dependent test efficiency and infectious contacts}, - year = {2023}, - issn = {1755-4365}, - pages = {100681}, - volume = {43}, - abstract = {Mathematical models have been widely used during the ongoing SARS-CoV-2 pandemic for data interpretation, forecasting, and policy making. However, most models are based on officially reported case numbers, which depend on test availability and test strategies. The time dependence of these factors renders interpretation difficult and might even result in estimation biases. Here, we present a computational modelling framework that allows for the integration of reported case numbers with seroprevalence estimates obtained from representative population cohorts. To account for the time dependence of infection and testing rates, we embed flexible splines in an epidemiological model. The parameters of these splines are estimated, along with the other parameters, from the available data using a Bayesian approach. The application of this approach to the official case numbers reported for Munich (Germany) and the seroprevalence reported by the prospective COVID-19 Cohort Munich (KoCo19) provides first estimates for the time dependence of the under-reporting factor. Furthermore, we estimate how the effectiveness of non-pharmaceutical interventions and of the testing strategy evolves over time. Overall, our results show that the integration of temporally highly resolved and representative data is beneficial for accurate epidemiological analyses.}, - creationdate = {2023-04-15T07:59:57}, - doi = {10.1016/j.epidem.2023.100681}, - keywords = {Compartmental model, Parameter estimation, Uncertainty quantification, COVID-19}, - url = {https://www.sciencedirect.com/science/article/pii/S1755436523000178}, + author = {Lorenzo Contento and Noemi Castelletti and Elba Raimúndez and Ronan {Le Gleut} and Yannik Schälte and Paul Stapor and Ludwig Christian Hinske and Michael Hoelscher and Andreas Wieser and Katja Radon and Christiane Fuchs and Jan Hasenauer}, + journal = {Epidemics}, + title = {Integrative modelling of reported case numbers and seroprevalence reveals time-dependent test efficiency and infectious contacts}, + year = {2023}, + issn = {1755-4365}, + pages = {100681}, + volume = {43}, + abstract = {Mathematical models have been widely used during the ongoing SARS-CoV-2 pandemic for data interpretation, forecasting, and policy making. However, most models are based on officially reported case numbers, which depend on test availability and test strategies. The time dependence of these factors renders interpretation difficult and might even result in estimation biases. Here, we present a computational modelling framework that allows for the integration of reported case numbers with seroprevalence estimates obtained from representative population cohorts. To account for the time dependence of infection and testing rates, we embed flexible splines in an epidemiological model. The parameters of these splines are estimated, along with the other parameters, from the available data using a Bayesian approach. The application of this approach to the official case numbers reported for Munich (Germany) and the seroprevalence reported by the prospective COVID-19 Cohort Munich (KoCo19) provides first estimates for the time dependence of the under-reporting factor. Furthermore, we estimate how the effectiveness of non-pharmaceutical interventions and of the testing strategy evolves over time. Overall, our results show that the integration of temporally highly resolved and representative data is beneficial for accurate epidemiological analyses.}, + creationdate = {2023-04-15T07:59:57}, + doi = {10.1016/j.epidem.2023.100681}, + keywords = {Compartmental model, Parameter estimation, Uncertainty quantification, COVID-19}, + modificationdate = {2024-06-28T08:27:57}, + url = {https://www.sciencedirect.com/science/article/pii/S1755436523000178}, } @Article{FroehlichGer2023, @@ -1332,6 +1333,31 @@ @PhdThesis{Mutsuddy2024 url = {https://tigerprints.clemson.edu/all_dissertations/3572}, } +@Misc{PhilippsKoe2024, + author = {Maren Philipps and Antonia Körner and Jakob Vanhoefer and Dilan Pathirana and Jan Hasenauer}, + title = {Non-Negative Universal Differential Equations With Applications in Systems Biology}, + year = {2024}, + archiveprefix = {arXiv}, + creationdate = {2024-06-28T08:27:59}, + eprint = {2406.14246}, + modificationdate = {2024-06-28T08:27:59}, + primaryclass = {q-bio.QM}, + url = {https://arxiv.org/abs/2406.14246}, +} + +@Article{BaltussenJon2024, + author = {Baltussen, Mathieu G. and de Jong, Thijs J. and Duez, Quentin and Robinson, William E. and Huck, Wilhelm T. S.}, + journal = {Nature}, + title = {Chemical reservoir computation in a self-organizing reaction network}, + year = {2024}, + issn = {1476-4687}, + month = jun, + creationdate = {2024-06-29T14:03:08}, + doi = {10.1038/s41586-024-07567-x}, + modificationdate = {2024-06-29T14:03:08}, + publisher = {Springer Science and Business Media LLC}, +} + @Comment{jabref-meta: databaseType:bibtex;} @Comment{jabref-meta: grouping: diff --git a/documentation/recreate_reference_list.py b/documentation/recreate_reference_list.py index 7750173ce1..a188f1daa0 100755 --- a/documentation/recreate_reference_list.py +++ b/documentation/recreate_reference_list.py @@ -75,7 +75,10 @@ def main(): ) f.write( "If you applied AMICI in your work and your publication is " - "missing, please let us know via a new GitHub issue.\n\n" + "missing, please let us know via a new\n" + "[GitHub issue](https://github.com/AMICI-dev/AMICI/issues/new" + "?labels=documentation&title=Add+publication" + "&body=AMICI+was+used+in+this+manuscript:+DOI).\n\n" ) f.write( """ diff --git a/documentation/references.md b/documentation/references.md index f17da06c55..cd103843fa 100644 --- a/documentation/references.md +++ b/documentation/references.md @@ -1,8 +1,9 @@ # References -List of publications using AMICI. Total number is 87. +List of publications using AMICI. Total number is 89. -If you applied AMICI in your work and your publication is missing, please let us know via a new GitHub issue. +If you applied AMICI in your work and your publication is missing, please let us know via a new +[GitHub issue](https://github.com/AMICI-dev/AMICI/issues/new?labels=documentation&title=Add+publication&body=AMICI+was+used+in+this+manuscript:+DOI).