From e5c2cb1aa9c603501a658c1265fa0ea348e249c9 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Mon, 16 Sep 2024 19:49:23 +0200 Subject: [PATCH] git subrepo clone (merge) --branch=v0.26.1 --force git@github.com:AMICI-dev/AMICI.git deps/AMICI subrepo: subdir: "deps/AMICI" merged: "b0d79fff" upstream: origin: "git@github.com:AMICI-dev/AMICI.git" branch: "v0.26.1" commit: "b0d79fff" git-subrepo: version: "0.4.6" origin: "https://github.com/ingydotnet/git-subrepo" commit: "110b9eb" --- deps/AMICI/.git-blame-ignore-revs | 1 + .../install-macos-dependencies/action.yml | 31 + .../actions/setup-amici-cpp/action.yml | 3 + .../actions/setup-sonar-tools/action.yml | 3 +- .../.github/actions/setup-swig/action.yml | 2 +- deps/AMICI/.github/dependabot.yml | 11 + .../AMICI/.github/workflows/deploy_branch.yml | 10 +- .../.github/workflows/deploy_protected.yml | 33 +- .../.github/workflows/deploy_release.yml | 46 +- .../test_benchmark_collection_models.yml | 18 +- deps/AMICI/.github/workflows/test_doc.yml | 16 +- deps/AMICI/.github/workflows/test_install.yml | 20 +- deps/AMICI/.github/workflows/test_matlab.yml | 15 +- .../.github/workflows/test_performance.yml | 10 +- .../workflows/test_petab_test_suite.yml | 32 +- deps/AMICI/.github/workflows/test_pypi.yml | 4 +- .../workflows/test_python_cplusplus.yml | 125 +- .../workflows/test_python_ver_matrix.yml | 25 +- .../test_sbml_semantic_test_suite.yml | 19 +- .../AMICI/.github/workflows/test_valgrind.yml | 12 +- deps/AMICI/.github/workflows/test_windows.yml | 9 +- deps/AMICI/.gitignore | 1 + deps/AMICI/.gitrepo | 6 +- deps/AMICI/.pre-commit-config.yaml | 38 +- deps/AMICI/.readthedocs.yml | 3 +- deps/AMICI/CHANGELOG.md | 363 +- deps/AMICI/CMakeLists.txt | 107 +- deps/AMICI/LICENSE.md | 2 +- deps/AMICI/ThirdParty/SuiteSparse/.gitignore | 4 + .../ThirdParty/SuiteSparse/AMD/CMakeLists.txt | 257 +- .../SuiteSparse/AMD/Config/AMD.pc.in | 17 + .../SuiteSparse/AMD/Config/AMDConfig.cmake.in | 152 + .../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 +- .../AMICI/ThirdParty/SuiteSparse/AMD/Makefile | 34 +- .../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 +- .../SuiteSparse/BTF/Config/BTF.pc.in | 16 + .../SuiteSparse/BTF/Config/BTFConfig.cmake.in | 152 + .../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 +- .../AMICI/ThirdParty/SuiteSparse/BTF/Makefile | 8 +- .../SuiteSparse/BTF/Source/btf_version.c | 19 + .../BTF/cmake_modules/FindBTF.cmake | 129 - .../ThirdParty/SuiteSparse/CMakeLists.txt | 540 +++ .../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 +- .../SuiteSparse/COLAMD/Doc/ChangeLog | 25 + .../SuiteSparse/COLAMD/Include/colamd.h | 33 +- .../ThirdParty/SuiteSparse/COLAMD/Makefile | 20 +- .../SuiteSparse/COLAMD/Source/colamd.c | 1 - .../COLAMD/Source/colamd_version.c | 19 + .../COLAMD/cmake_modules/FindCOLAMD.cmake | 129 - deps/AMICI/ThirdParty/SuiteSparse/ChangeLog | 176 +- .../ThirdParty/SuiteSparse/KLU/CMakeLists.txt | 555 ++- .../SuiteSparse/KLU/Config/KLU.pc.in | 17 + .../SuiteSparse/KLU/Config/KLUConfig.cmake.in | 193 + .../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 +- .../AMICI/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 - deps/AMICI/ThirdParty/SuiteSparse/LICENSE.txt | 170 +- deps/AMICI/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 + deps/AMICI/ThirdParty/gsl/gsl/gsl-lite.hpp | 167 +- deps/AMICI/ThirdParty/sundials/CMakeLists.txt | 6 +- .../sundials/cmake/tpl/FindKLU.cmake | 2 +- .../sundials/cmake/tpl/SundialsKLU.cmake | 17 +- deps/AMICI/cmake/AmiciConfig.cmake | 44 +- deps/AMICI/cmake/AmiciFindBLAS.cmake | 128 + deps/AMICI/cmake/cmakelang-tools.cmake | 2 + deps/AMICI/documentation/ExampleJax.ipynb | 1115 +----- deps/AMICI/documentation/GettingStarted.ipynb | 4 +- deps/AMICI/documentation/README.md | 8 +- deps/AMICI/documentation/amici_refs.bib | 195 +- deps/AMICI/documentation/background.rst | 20 +- deps/AMICI/documentation/conf.py | 90 +- .../documentation/gfx/amici_workflow.png | Bin 100907 -> 98637 bytes .../documentation/gfx/amici_workflow.svg | 16 +- deps/AMICI/documentation/index.rst | 1 + .../documentation/python_installation.rst | 61 +- deps/AMICI/documentation/python_interface.rst | 2 +- deps/AMICI/documentation/python_modules.rst | 10 + .../documentation/recreate_reference_list.py | 7 +- deps/AMICI/documentation/references.md | 288 +- deps/AMICI/documentation/rtd_requirements.txt | 19 +- .../AMICI/documentation/rtd_requirements2.txt | 1 - .../AMICI/documentation/versioning_policy.rst | 28 + deps/AMICI/include/amici/abstract_model.h | 128 +- deps/AMICI/include/amici/backwardproblem.h | 2 +- deps/AMICI/include/amici/defines.h | 12 +- deps/AMICI/include/amici/edata.h | 68 +- deps/AMICI/include/amici/exception.h | 22 +- deps/AMICI/include/amici/forwardproblem.h | 29 +- deps/AMICI/include/amici/logging.h | 2 +- deps/AMICI/include/amici/misc.h | 17 +- deps/AMICI/include/amici/model.h | 175 +- deps/AMICI/include/amici/model_dae.h | 30 +- deps/AMICI/include/amici/model_dimensions.h | 20 +- deps/AMICI/include/amici/model_ode.h | 24 +- deps/AMICI/include/amici/rdata.h | 11 +- deps/AMICI/include/amici/returndata_matlab.h | 2 +- deps/AMICI/include/amici/serialization.h | 327 +- .../include/amici/simulation_parameters.h | 23 +- deps/AMICI/include/amici/solver.h | 109 +- deps/AMICI/include/amici/solver_cvodes.h | 10 +- deps/AMICI/include/amici/solver_idas.h | 10 +- deps/AMICI/include/amici/spline.h | 1 - deps/AMICI/include/amici/splinefunctions.h | 14 +- deps/AMICI/include/amici/steadystateproblem.h | 7 +- .../include/amici/sundials_matrix_wrapper.h | 17 +- deps/AMICI/include/amici/vector.h | 35 +- deps/AMICI/matlab/@amifun/getArgs.m | 6 +- deps/AMICI/matlab/@amimodel/generateC.m | 4 +- .../models/model_calvetti/CMakeLists.txt | 37 +- deps/AMICI/models/model_calvetti/dwdx.cpp | 2 +- deps/AMICI/models/model_calvetti/main.cpp | 59 +- .../models/model_calvetti/model_calvetti.h | 21 +- deps/AMICI/models/model_calvetti/w.cpp | 2 +- deps/AMICI/models/model_dirac/CMakeLists.txt | 37 +- deps/AMICI/models/model_dirac/main.cpp | 59 +- deps/AMICI/models/model_dirac/model_dirac.h | 13 +- deps/AMICI/models/model_events/CMakeLists.txt | 37 +- deps/AMICI/models/model_events/main.cpp | 59 +- deps/AMICI/models/model_events/model_events.h | 13 +- .../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 | 25 +- deps/AMICI/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 | 25 +- .../models/model_jakstat_adjoint_o2/w.cpp | 2 +- .../models/model_nested_events/CMakeLists.txt | 37 +- .../AMICI/models/model_nested_events/main.cpp | 59 +- .../model_nested_events/model_nested_events.h | 13 +- deps/AMICI/models/model_neuron/CMakeLists.txt | 37 +- deps/AMICI/models/model_neuron/main.cpp | 59 +- deps/AMICI/models/model_neuron/model_neuron.h | 13 +- .../models/model_neuron_o2/CMakeLists.txt | 37 +- deps/AMICI/models/model_neuron_o2/dwdx.cpp | 2 +- deps/AMICI/models/model_neuron_o2/main.cpp | 59 +- .../models/model_neuron_o2/model_neuron_o2.h | 21 +- deps/AMICI/models/model_neuron_o2/w.cpp | 2 +- .../models/model_robertson/CMakeLists.txt | 37 +- deps/AMICI/models/model_robertson/dwdp.cpp | 2 +- deps/AMICI/models/model_robertson/dwdx.cpp | 2 +- deps/AMICI/models/model_robertson/main.cpp | 59 +- .../models/model_robertson/model_robertson.h | 25 +- deps/AMICI/models/model_robertson/w.cpp | 2 +- .../models/model_steadystate/CMakeLists.txt | 37 +- deps/AMICI/models/model_steadystate/dwdp.cpp | 2 +- deps/AMICI/models/model_steadystate/dwdx.cpp | 2 +- deps/AMICI/models/model_steadystate/main.cpp | 59 +- .../model_steadystate/model_steadystate.h | 25 +- deps/AMICI/models/model_steadystate/w.cpp | 2 +- deps/AMICI/pytest.ini | 6 +- .../ExampleEquilibrationLogic.ipynb | 11 +- .../python/examples/example_errors.ipynb | 37 +- .../examples/example_jax/ExampleJax.ipynb | 25 +- .../example_performance_optimization.ipynb | 24 +- .../python/examples/example_petab/petab.ipynb | 356 +- .../ExampleExperimentalConditions.ipynb | 16 +- .../example_splines/ExampleSplines.ipynb | 23 +- .../ExampleSplinesSwameye2003.ipynb | 24 +- .../ExampleSteadystate.ipynb | 2 +- deps/AMICI/python/sdist/MANIFEST.in | 2 +- .../python/sdist/amici/MANIFEST.template.in | 2 + deps/AMICI/python/sdist/amici/__init__.py | 19 +- .../python/sdist/amici/__init__.template.py | 4 +- deps/AMICI/python/sdist/amici/__main__.py | 1 - .../python/sdist/amici/_codegen/__init__.py | 0 .../sdist/amici/_codegen/cxx_functions.py | 409 ++ .../sdist/amici/_codegen/model_class.py | 192 + .../python/sdist/amici/_codegen/template.py | 42 + .../python/sdist/amici/antimony_import.py | 12 +- deps/AMICI/python/sdist/amici/bngl_import.py | 1 - deps/AMICI/python/sdist/amici/compile.py | 80 + .../amici/conserved_quantities_demartino.py | 54 +- .../sdist/amici/conserved_quantities_rref.py | 6 +- .../python/sdist/amici/custom_commands.py | 8 +- .../python/sdist/amici/cxxcodeprinter.py | 241 +- deps/AMICI/python/sdist/amici/de_export.py | 3380 ++--------------- deps/AMICI/python/sdist/amici/de_model.py | 2570 ++++++++++--- .../python/sdist/amici/de_model_components.py | 754 ++++ .../python/sdist/amici/debugging/__init__.py | 46 + .../python/sdist/amici/gradient_check.py | 24 +- deps/AMICI/python/sdist/amici/import_utils.py | 63 +- deps/AMICI/python/sdist/amici/logging.py | 41 +- deps/AMICI/python/sdist/amici/numpy.py | 76 +- deps/AMICI/python/sdist/amici/pandas.py | 111 +- .../python/sdist/amici/parameter_mapping.py | 493 +-- .../python/sdist/amici/petab/__init__.py | 35 + .../python/sdist/amici/petab/cli/__init__.py | 0 .../sdist/amici/petab/cli/import_petab.py | 197 + .../python/sdist/amici/petab/conditions.py | 525 +++ .../sdist/amici/petab/import_helpers.py | 269 ++ .../sdist/amici/petab/parameter_mapping.py | 700 ++++ .../python/sdist/amici/petab/petab_import.py | 168 + .../python/sdist/amici/petab/petab_problem.py | 278 ++ .../python/sdist/amici/petab/pysb_import.py | 274 ++ .../python/sdist/amici/petab/sbml_import.py | 575 +++ .../python/sdist/amici/petab/simulations.py | 490 +++ .../python/sdist/amici/petab/simulator.py | 109 + deps/AMICI/python/sdist/amici/petab/util.py | 107 + deps/AMICI/python/sdist/amici/petab_import.py | 1087 +----- .../python/sdist/amici/petab_import_pysb.py | 282 +- .../python/sdist/amici/petab_objective.py | 1222 +----- .../python/sdist/amici/petab_simulate.py | 118 +- deps/AMICI/python/sdist/amici/petab_util.py | 119 +- deps/AMICI/python/sdist/amici/plotting.py | 168 +- deps/AMICI/python/sdist/amici/pysb_import.py | 115 +- deps/AMICI/python/sdist/amici/sbml_import.py | 538 ++- deps/AMICI/python/sdist/amici/sbml_utils.py | 34 +- .../python/sdist/amici/setup.template.py | 12 +- deps/AMICI/python/sdist/amici/splines.py | 153 +- deps/AMICI/python/sdist/amici/swig.py | 112 +- .../AMICI/python/sdist/amici/swig_wrappers.py | 138 +- deps/AMICI/python/sdist/amici/sympy_utils.py | 201 + deps/AMICI/python/sdist/amici/testing.py | 13 + deps/AMICI/python/sdist/bin | 1 - deps/AMICI/python/sdist/pyproject.toml | 120 +- deps/AMICI/python/sdist/setup.cfg | 78 - deps/AMICI/python/sdist/setup.py | 53 +- deps/AMICI/python/tests/conftest.py | 2 +- .../python/tests/petab_/test_petab_problem.py | 87 + .../lotka_volterra/model/writer.py | 1 - .../bngwiki_egfr_simple_deletemolecules.py | 2 - deps/AMICI/python/tests/splines_utils.py | 60 +- .../python/tests/test_antimony_import.py | 2 + .../test_compare_conservation_laws_sbml.py | 3 + .../test_conserved_quantities_demartino.py | 30 +- .../tests/test_conserved_quantities_rref.py | 2 - .../AMICI/python/tests/test_cxxcodeprinter.py | 2 + deps/AMICI/python/tests/test_de_model.py | 37 + deps/AMICI/python/tests/test_edata.py | 5 +- deps/AMICI/python/tests/test_events.py | 125 + deps/AMICI/python/tests/test_hdf5.py | 2 + deps/AMICI/python/tests/test_heavisides.py | 1 + deps/AMICI/python/tests/test_misc.py | 33 +- deps/AMICI/python/tests/test_ode_export.py | 16 +- deps/AMICI/python/tests/test_pandas.py | 3 + .../python/tests/test_parameter_mapping.py | 4 +- deps/AMICI/python/tests/test_petab_import.py | 53 +- .../python/tests/test_petab_objective.py | 18 +- .../AMICI/python/tests/test_petab_simulate.py | 5 +- .../python/tests/test_preequilibration.py | 100 +- deps/AMICI/python/tests/test_pysb.py | 1 + deps/AMICI/python/tests/test_rdata.py | 4 + deps/AMICI/python/tests/test_sbml_import.py | 69 +- .../test_sbml_import_special_functions.py | 54 +- deps/AMICI/python/tests/test_splines.py | 2 +- .../AMICI/python/tests/test_splines_python.py | 7 +- deps/AMICI/python/tests/test_splines_short.py | 2 +- .../AMICI/python/tests/test_swig_interface.py | 140 +- deps/AMICI/python/tests/test_sympy_utils.py | 24 + deps/AMICI/python/tests/util.py | 5 +- deps/AMICI/python/tests/valgrind-python.supp | 360 +- deps/AMICI/scripts/buildAmici.sh | 28 +- deps/AMICI/scripts/buildSuiteSparse.sh | 13 +- deps/AMICI/scripts/buildSundials.sh | 2 +- deps/AMICI/scripts/downloadAndBuildDoxygen.sh | 2 +- deps/AMICI/scripts/downloadAndBuildSwig.sh | 10 +- deps/AMICI/scripts/installAmiciArchive.sh | 8 +- deps/AMICI/scripts/installAmiciSource.sh | 35 +- deps/AMICI/scripts/run-SBMLTestsuite.sh | 2 +- deps/AMICI/scripts/run-codecov.sh | 2 +- deps/AMICI/scripts/run-python-tests.sh | 14 +- deps/AMICI/scripts/run-sphinx-hasenv.sh | 17 - deps/AMICI/scripts/run-sphinx.sh | 16 - deps/AMICI/scripts/run-valgrind-py.sh | 34 +- deps/AMICI/scripts/runNotebook.sh | 2 +- deps/AMICI/src/CMakeLists.template.cmake | 34 +- deps/AMICI/src/abstract_model.cpp | 85 +- deps/AMICI/src/amici.cpp | 57 +- deps/AMICI/src/backwardproblem.cpp | 3 - deps/AMICI/src/edata.cpp | 35 +- deps/AMICI/src/exception.cpp | 23 +- deps/AMICI/src/forwardproblem.cpp | 237 +- deps/AMICI/src/hdf5.cpp | 64 +- deps/AMICI/src/interface_matlab.cpp | 38 +- deps/AMICI/src/model.cpp | 303 +- deps/AMICI/src/model_dae.cpp | 69 +- deps/AMICI/src/model_header.template.h | 4 +- deps/AMICI/src/model_ode.cpp | 73 +- deps/AMICI/src/newton_solver.cpp | 34 +- deps/AMICI/src/rdata.cpp | 19 +- deps/AMICI/src/solver.cpp | 125 +- deps/AMICI/src/solver_cvodes.cpp | 136 +- deps/AMICI/src/solver_idas.cpp | 115 +- deps/AMICI/src/spline.cpp | 4 +- deps/AMICI/src/splinefunctions.cpp | 46 +- deps/AMICI/src/steadystateproblem.cpp | 34 +- deps/AMICI/src/sundials_linsol_wrapper.cpp | 21 +- deps/AMICI/src/sundials_matrix_wrapper.cpp | 43 +- deps/AMICI/src/symbolic_functions.cpp | 1 - deps/AMICI/src/vector.cpp | 1 - deps/AMICI/swig/CMakeLists.txt | 12 +- deps/AMICI/swig/CMakeLists_model.cmake | 11 + deps/AMICI/swig/amici.i | 64 +- deps/AMICI/swig/edata.i | 18 + deps/AMICI/swig/misc.i | 2 + deps/AMICI/swig/model.i | 17 +- deps/AMICI/swig/solver.i | 9 + deps/AMICI/swig/solver_cvodes.i | 11 + deps/AMICI/swig/solver_idas.i | 11 + deps/AMICI/swig/std_unique_ptr.i | 5 +- .../benchmark-models/evaluate_benchmark.py | 1 + .../test_benchmark_collection.sh | 19 +- .../benchmark-models/test_petab_benchmark.py | 34 +- .../benchmark-models/test_petab_model.py | 19 +- deps/AMICI/tests/conftest.py | 9 +- deps/AMICI/tests/cpp/steadystate/tests1.cpp | 4 +- deps/AMICI/tests/cpp/testfunctions.cpp | 23 +- .../AMICI/tests/cpp/unittests/testExpData.cpp | 3 +- deps/AMICI/tests/cpp/unittests/testMisc.cpp | 25 +- .../tests/cpp/unittests/testSerialization.cpp | 4 +- .../generateTestConfig/example_steadystate.py | 2 +- deps/AMICI/tests/performance/test.py | 5 +- deps/AMICI/tests/petab_test_suite/conftest.py | 3 +- .../petab_test_suite/test_petab_suite.py | 16 +- deps/AMICI/tests/testSBMLSuite.py | 4 +- deps/AMICI/tox.ini | 23 + deps/AMICI/version.txt | 2 +- 363 files changed, 22486 insertions(+), 14245 deletions(-) create mode 100644 deps/AMICI/.git-blame-ignore-revs create mode 100644 deps/AMICI/.github/actions/install-macos-dependencies/action.yml create mode 100644 deps/AMICI/.github/dependabot.yml create mode 100644 deps/AMICI/ThirdParty/SuiteSparse/AMD/Config/AMD.pc.in create mode 100644 deps/AMICI/ThirdParty/SuiteSparse/AMD/Config/AMDConfig.cmake.in create mode 100644 deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_version.c delete mode 100644 deps/AMICI/ThirdParty/SuiteSparse/AMD/cmake_modules/FindAMD.cmake create mode 100644 deps/AMICI/ThirdParty/SuiteSparse/BTF/Config/BTF.pc.in create mode 100644 deps/AMICI/ThirdParty/SuiteSparse/BTF/Config/BTFConfig.cmake.in create mode 100644 deps/AMICI/ThirdParty/SuiteSparse/BTF/Source/btf_version.c delete mode 100644 deps/AMICI/ThirdParty/SuiteSparse/BTF/cmake_modules/FindBTF.cmake create mode 100644 deps/AMICI/ThirdParty/SuiteSparse/CMakeLists.txt create mode 100644 deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Config/COLAMD.pc.in create mode 100644 deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Config/COLAMDConfig.cmake.in create mode 100644 deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Source/colamd_version.c delete mode 100644 deps/AMICI/ThirdParty/SuiteSparse/COLAMD/cmake_modules/FindCOLAMD.cmake create mode 100644 deps/AMICI/ThirdParty/SuiteSparse/KLU/Config/KLU.pc.in create mode 100644 deps/AMICI/ThirdParty/SuiteSparse/KLU/Config/KLUConfig.cmake.in create mode 100644 deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_version.c delete mode 100644 deps/AMICI/ThirdParty/SuiteSparse/KLU/cmake_modules/FindKLU.cmake delete mode 100644 deps/AMICI/ThirdParty/SuiteSparse/KLU/cmake_modules/FindKLU_CHOLMOD.cmake create mode 100644 deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/Config/SuiteSparse_config.pc.in create mode 100644 deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/Config/SuiteSparse_configConfig.cmake.in delete mode 100644 deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/FindSuiteSparse_config.cmake create mode 100644 deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparse__thread.cmake delete mode 100644 deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparse_ssize_t.cmake create mode 100644 deps/AMICI/ThirdParty/SuiteSparse/build/.gitignore create mode 100644 deps/AMICI/ThirdParty/SuiteSparse/lib/.gitignore create mode 100644 deps/AMICI/cmake/AmiciFindBLAS.cmake mode change 100644 => 120000 deps/AMICI/documentation/ExampleJax.ipynb delete mode 100644 deps/AMICI/documentation/rtd_requirements2.txt create mode 100644 deps/AMICI/documentation/versioning_policy.rst create mode 100644 deps/AMICI/python/sdist/amici/_codegen/__init__.py create mode 100644 deps/AMICI/python/sdist/amici/_codegen/cxx_functions.py create mode 100644 deps/AMICI/python/sdist/amici/_codegen/model_class.py create mode 100644 deps/AMICI/python/sdist/amici/_codegen/template.py create mode 100644 deps/AMICI/python/sdist/amici/compile.py create mode 100644 deps/AMICI/python/sdist/amici/de_model_components.py create mode 100644 deps/AMICI/python/sdist/amici/debugging/__init__.py create mode 100644 deps/AMICI/python/sdist/amici/petab/__init__.py create mode 100644 deps/AMICI/python/sdist/amici/petab/cli/__init__.py create mode 100644 deps/AMICI/python/sdist/amici/petab/cli/import_petab.py create mode 100644 deps/AMICI/python/sdist/amici/petab/conditions.py create mode 100644 deps/AMICI/python/sdist/amici/petab/import_helpers.py create mode 100644 deps/AMICI/python/sdist/amici/petab/parameter_mapping.py create mode 100644 deps/AMICI/python/sdist/amici/petab/petab_import.py create mode 100644 deps/AMICI/python/sdist/amici/petab/petab_problem.py create mode 100644 deps/AMICI/python/sdist/amici/petab/pysb_import.py create mode 100644 deps/AMICI/python/sdist/amici/petab/sbml_import.py create mode 100644 deps/AMICI/python/sdist/amici/petab/simulations.py create mode 100644 deps/AMICI/python/sdist/amici/petab/simulator.py create mode 100644 deps/AMICI/python/sdist/amici/petab/util.py create mode 100644 deps/AMICI/python/sdist/amici/sympy_utils.py delete mode 120000 deps/AMICI/python/sdist/bin delete mode 100644 deps/AMICI/python/sdist/setup.cfg create mode 100644 deps/AMICI/python/tests/petab_/test_petab_problem.py create mode 100644 deps/AMICI/python/tests/test_de_model.py create mode 100644 deps/AMICI/python/tests/test_sympy_utils.py delete mode 100755 deps/AMICI/scripts/run-sphinx-hasenv.sh delete mode 100755 deps/AMICI/scripts/run-sphinx.sh mode change 100755 => 100644 deps/AMICI/tests/benchmark-models/test_petab_benchmark.py create mode 100644 deps/AMICI/tox.ini diff --git a/deps/AMICI/.git-blame-ignore-revs b/deps/AMICI/.git-blame-ignore-revs new file mode 100644 index 000000000..87c86463f --- /dev/null +++ b/deps/AMICI/.git-blame-ignore-revs @@ -0,0 +1 @@ +f59fed72eb4d0cedf6abf2a96fe95087ce61478a diff --git a/deps/AMICI/.github/actions/install-macos-dependencies/action.yml b/deps/AMICI/.github/actions/install-macos-dependencies/action.yml new file mode 100644 index 000000000..b19cac105 --- /dev/null +++ b/deps/AMICI/.github/actions/install-macos-dependencies/action.yml @@ -0,0 +1,31 @@ +name: Install AMICI dependencies for MacOS +description: Install AMICI dependencies for MacOS + +runs: + using: "composite" + steps: + # use all available cores + - run: echo "AMICI_PARALLEL_COMPILE=" >> $GITHUB_ENV + shell: bash + + # AMICI repository root + - run: echo "AMICI_DIR=$(pwd)" >> $GITHUB_ENV + shell: bash + + # BioNetGen path + - run: echo "BNGPATH=${AMICI_DIR}/ThirdParty/BioNetGen-2.7.0" >> $GITHUB_ENV + shell: bash + + # CMake hints + # Ensure CMake is using the python version that we will use for the python tests later on + - run: echo "PYTHON_EXECUTABLE=${Python3_ROOT_DIR}/bin/python3" >> $GITHUB_ENV + shell: bash + - run: echo "OpenMP_ROOT=$(brew --prefix)/opt/libomp" >> $GITHUB_ENV + shell: bash + - run: echo "BOOST_ROOT=$(brew --prefix)/opt/boost" >> $GITHUB_ENV + shell: bash + + # install amici dependencies + - name: homebrew + run: brew install hdf5 swig gcc libomp boost + shell: bash diff --git a/deps/AMICI/.github/actions/setup-amici-cpp/action.yml b/deps/AMICI/.github/actions/setup-amici-cpp/action.yml index 09ec9311b..b5ee4808b 100644 --- a/deps/AMICI/.github/actions/setup-amici-cpp/action.yml +++ b/deps/AMICI/.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/deps/AMICI/.github/actions/setup-sonar-tools/action.yml b/deps/AMICI/.github/actions/setup-sonar-tools/action.yml index d791c120b..154824c70 100644 --- a/deps/AMICI/.github/actions/setup-sonar-tools/action.yml +++ b/deps/AMICI/.github/actions/setup-sonar-tools/action.yml @@ -16,7 +16,8 @@ runs: - name: Install sonarcloud tools run: | - sudo apt-get install nodejs curl unzip \ + sudo apt-get update \ + && sudo apt-get install nodejs curl unzip \ && curl --create-dirs -sSLo $HOME/.sonar/sonar-scanner.zip \ https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-$SONAR_SCANNER_VERSION-linux.zip \ && unzip -o $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/ \ diff --git a/deps/AMICI/.github/actions/setup-swig/action.yml b/deps/AMICI/.github/actions/setup-swig/action.yml index 0eb8a0447..b4441cd05 100644 --- a/deps/AMICI/.github/actions/setup-swig/action.yml +++ b/deps/AMICI/.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/deps/AMICI/.github/dependabot.yml b/deps/AMICI/.github/dependabot.yml new file mode 100644 index 000000000..edacb6bea --- /dev/null +++ b/deps/AMICI/.github/dependabot.yml @@ -0,0 +1,11 @@ +version: 2 +updates: + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-patch", "version-update:semver-minor"] + target-branch: "develop" diff --git a/deps/AMICI/.github/workflows/deploy_branch.yml b/deps/AMICI/.github/workflows/deploy_branch.yml index 73294286a..24d77d811 100644 --- a/deps/AMICI/.github/workflows/deploy_branch.yml +++ b/deps/AMICI/.github/workflows/deploy_branch.yml @@ -1,5 +1,5 @@ name: Deploy Branch -on: [push, merge_group, workflow_dispatch] +on: [push, pull_request, merge_group, workflow_dispatch] jobs: sdist: @@ -9,15 +9,15 @@ jobs: strategy: matrix: - python-version: [3.9] + python-version: ["3.11"] 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/deps/AMICI/.github/workflows/deploy_protected.yml b/deps/AMICI/.github/workflows/deploy_protected.yml index cd9257818..e98209c97 100644 --- a/deps/AMICI/.github/workflows/deploy_protected.yml +++ b/deps/AMICI/.github/workflows/deploy_protected.yml @@ -13,29 +13,44 @@ on: workflow_dispatch: jobs: - dockerhub: - # https://github.com/marketplace/actions/publish-docker - name: Deploy Dockerhub + 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 Docker Hub + needs: [check-secret] + if: needs.check-secret.outputs.secrets-defined == 'true' runs-on: ubuntu-22.04 strategy: matrix: - python-version: [3.9] + python-version: ["3.11"] 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 + 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 + uses: elgohr/Publish-Docker-Github-Action@v5 with: name: dweindl/amici username: ${{ secrets.DOCKER_USERNAME }} diff --git a/deps/AMICI/.github/workflows/deploy_release.yml b/deps/AMICI/.github/workflows/deploy_release.yml index 8fe931763..b18c31ff2 100644 --- a/deps/AMICI/.github/workflows/deploy_release.yml +++ b/deps/AMICI/.github/workflows/deploy_release.yml @@ -12,15 +12,22 @@ jobs: strategy: matrix: - python-version: [3.9] + python-version: ["3.11"] + + 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 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 20 @@ -29,14 +36,17 @@ jobs: - run: echo "AMICI_DIR=$(pwd)" >> $GITHUB_ENV + - 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/pyproject.toml + - name: sdist run: scripts/buildSdist.sh - 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: @@ -64,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 diff --git a/deps/AMICI/.github/workflows/test_benchmark_collection_models.yml b/deps/AMICI/.github/workflows/test_benchmark_collection_models.yml index bf9509e5c..7c7aacb99 100644 --- a/deps/AMICI/.github/workflows/test_benchmark_collection_models.yml +++ b/deps/AMICI/.github/workflows/test_benchmark_collection_models.yml @@ -22,18 +22,18 @@ 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 }} 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 @@ -49,15 +49,19 @@ 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] + - run: | + python3 -m pip uninstall -y petab && python3 -m pip install git+https://github.com/petab-dev/libpetab-python.git@develop \ + && python3 -m pip install -U sympy + # retrieve test models - name: Download and test benchmark collection 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 @@ -66,9 +70,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/deps/AMICI/.github/workflows/test_doc.yml b/deps/AMICI/.github/workflows/test_doc.yml index 07e0afdcc..5fcf490eb 100644 --- a/deps/AMICI/.github/workflows/test_doc.yml +++ b/deps/AMICI/.github/workflows/test_doc.yml @@ -20,15 +20,15 @@ jobs: strategy: matrix: - python-version: [3.9] + python-version: ["3.11"] 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 @@ -43,15 +43,15 @@ jobs: strategy: matrix: - python-version: [ "3.10" ] + python-version: [ "3.11" ] 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,10 +67,10 @@ jobs: sudo apt-get update \ && sudo apt-get install -y \ pandoc \ - python3-venv + && pip install tox - name: Set up SWIG uses: ./.github/actions/setup-swig - name: Run sphinx - run: scripts/run-sphinx.sh + run: tox -e doc diff --git a/deps/AMICI/.github/workflows/test_install.yml b/deps/AMICI/.github/workflows/test_install.yml index be74cfa4c..9e1717d96 100644 --- a/deps/AMICI/.github/workflows/test_install.yml +++ b/deps/AMICI/.github/workflows/test_install.yml @@ -1,5 +1,5 @@ name: Installation -on: [push, merge_group, workflow_dispatch] +on: [push, pull_request, merge_group, workflow_dispatch] jobs: archive: @@ -9,15 +9,15 @@ jobs: strategy: matrix: - python-version: [3.9] + python-version: ["3.11"] 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 @@ -50,15 +50,15 @@ jobs: strategy: matrix: - python-version: [3.9] + python-version: ["3.11"] 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 @@ -83,15 +83,15 @@ jobs: strategy: matrix: - python-version: [3.9] + python-version: ["3.11"] 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/deps/AMICI/.github/workflows/test_matlab.yml b/deps/AMICI/.github/workflows/test_matlab.yml index d51cc3fbf..473b927e7 100644 --- a/deps/AMICI/.github/workflows/test_matlab.yml +++ b/deps/AMICI/.github/workflows/test_matlab.yml @@ -1,5 +1,12 @@ name: Matlab -on: [push, merge_group, workflow_dispatch] +on: + push: + merge_group: + workflow_dispatch: + pull_request: + branches: + - master + jobs: matlab: @@ -8,14 +15,14 @@ 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 - name: Install MATLAB - uses: matlab-actions/setup-matlab@v1 + 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 diff --git a/deps/AMICI/.github/workflows/test_performance.yml b/deps/AMICI/.github/workflows/test_performance.yml index de9dd686d..6b66698d1 100644 --- a/deps/AMICI/.github/workflows/test_performance.yml +++ b/deps/AMICI/.github/workflows/test_performance.yml @@ -23,15 +23,15 @@ jobs: strategy: matrix: - python-version: [3.9] + python-version: ["3.11"] 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 @@ -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) @@ -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/deps/AMICI/.github/workflows/test_petab_test_suite.yml b/deps/AMICI/.github/workflows/test_petab_test_suite.yml index d5c4dc4fe..64277fe26 100644 --- a/deps/AMICI/.github/workflows/test_petab_test_suite.yml +++ b/deps/AMICI/.github/workflows/test_petab_test_suite.yml @@ -22,15 +22,15 @@ jobs: strategy: matrix: - python-version: [3.9] + python-version: ["3.11"] 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 @@ -57,35 +57,45 @@ jobs: - name: Install petab run: | - source ./build/venv/bin/activate \ - && pip3 install wheel pytest shyaml pytest-cov pysb + source ./venv/bin/activate \ + && pip3 install wheel pytest shyaml pytest-cov pysb>=1.16 # retrieve test models - name: Download and install PEtab test suite 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: Install petab + run: | + source ./venv/bin/activate \ + && python3 -m pip uninstall -y petab \ + && python3 -m pip install git+https://github.com/petab-dev/libpetab-python.git@develop \ + && python3 -m pip install git+https://github.com/FFroehlich/pysb@fix_pattern_matching \ + && python3 -m pip install sympy>=1.12.1 + + - 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 + --cov=./ python/tests/test_*petab*.py python/tests/petab_/ # run test models - name: Run PEtab test suite run: | - source ./build/venv/bin/activate \ - && AMICI_PARALLEL_COMPILE=2 pytest -v \ + source ./venv/bin/activate \ + && AMICI_PARALLEL_COMPILE="" pytest -v \ --cov-report=xml:coverage.xml \ --cov-append \ --cov=amici \ tests/petab_test_suite/ - name: Codecov - uses: codecov/codecov-action@v3.1.0 + if: github.event_name == 'pull_request' || github.repository_owner == 'AMICI-dev' + uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} file: coverage.xml diff --git a/deps/AMICI/.github/workflows/test_pypi.yml b/deps/AMICI/.github/workflows/test_pypi.yml index 4f3533850..1e91019c6 100644 --- a/deps/AMICI/.github/workflows/test_pypi.yml +++ b/deps/AMICI/.github/workflows/test_pypi.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.9", "3.10", "3.11"] + python-version: ["3.10", "3.11", "3.12"] os: [ubuntu-22.04, macos-latest] runs-on: ${{ matrix.os }} @@ -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/deps/AMICI/.github/workflows/test_python_cplusplus.yml b/deps/AMICI/.github/workflows/test_python_cplusplus.yml index a531d2db2..2128f4df1 100644 --- a/deps/AMICI/.github/workflows/test_python_cplusplus.yml +++ b/deps/AMICI/.github/workflows/test_python_cplusplus.yml @@ -6,6 +6,7 @@ on: pull_request: branches: - master + - develop jobs: ubuntu-cpp-python-tests: @@ -14,15 +15,22 @@ jobs: strategy: matrix: - python-version: [ "3.9" ] + python-version: [ "3.11" ] steps: + - name: Cache + uses: actions/cache@v4 + with: + path: | + ~/.cache/pooch + key: ${{ runner.os }}-py${{ matrix.python-version }}-${{ github.job }} + - 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 @@ -37,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 \ @@ -56,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" \ @@ -65,7 +73,8 @@ jobs: ${AMICI_DIR}/python/tests/test_splines.py - name: Codecov Python - uses: codecov/codecov-action@v3.1.0 + if: github.event_name == 'pull_request' || github.repository_owner == 'AMICI-dev' + uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} file: build/coverage_py.xml @@ -84,7 +93,8 @@ jobs: && lcov -a coverage_cpp.info -a coverage_py.info -o coverage.info - name: Codecov CPP - uses: codecov/codecov-action@v3.1.0 + if: github.event_name == 'pull_request' || github.repository_owner == 'AMICI-dev' + uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} file: coverage.info @@ -92,6 +102,7 @@ jobs: fail_ci_if_error: true - name: Run sonar-scanner + if: ${{ env.SONAR_TOKEN != '' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} @@ -106,15 +117,15 @@ jobs: strategy: matrix: - python-version: [ "3.9" ] + python-version: [ "3.10" ] 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 @@ -127,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" \ @@ -137,7 +148,8 @@ jobs: ${AMICI_DIR}/python/tests/test_splines_short.py - name: Codecov Python - uses: codecov/codecov-action@v3.1.0 + if: github.event_name == 'pull_request' || github.repository_owner == 'AMICI-dev' + uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} file: build/coverage_py.xml @@ -156,7 +168,8 @@ jobs: && lcov -a coverage_cpp.info -a coverage_py.info -o coverage.info - name: Codecov CPP - uses: codecov/codecov-action@v3.1.0 + if: github.event_name == 'pull_request' || github.repository_owner == 'AMICI-dev' + uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} file: coverage.info @@ -164,6 +177,7 @@ jobs: fail_ci_if_error: true - name: Run sonar-scanner + if: ${{ env.SONAR_TOKEN != '' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} @@ -179,15 +193,15 @@ jobs: strategy: matrix: - python-version: [ "3.9" ] + python-version: [ "3.11" ] 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 @@ -200,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 @@ -211,29 +225,24 @@ jobs: # TODO: Include notebooks in coverage report - osx: - name: Tests OSX + macos_cpp_py: + name: Tests MacOS C++/Python runs-on: macos-latest steps: - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: - python-version: 3.9 + python-version: "3.11" - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: git fetch --prune --unshallow - - run: echo "AMICI_DIR=$(pwd)" >> $GITHUB_ENV - - run: echo "BNGPATH=${AMICI_DIR}/ThirdParty/BioNetGen-2.7.0" >> $GITHUB_ENV - # Ensure CMake is using the python version that we will use for the python tests later on - - run: echo "PYTHON_EXECUTABLE=${Python3_ROOT_DIR}/bin/python3" >> $GITHUB_ENV - - run: echo "OpenMP_ROOT=$(brew --prefix)/opt/libomp" >> $GITHUB_ENV - - run: echo "BOOST_ROOT=$(brew --prefix)/opt/boost" >> $GITHUB_ENV + - name: Install dependencies + uses: ./.github/actions/install-macos-dependencies - # install amici dependencies - name: homebrew - run: brew install hdf5 swig gcc cppcheck libomp boost + run: brew install cppcheck - name: Build AMICI run: scripts/buildAll.sh @@ -242,13 +251,59 @@ 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 - - name: Python tests - run: scripts/run-python-tests.sh - - name: C++ tests run: scripts/run-cpp-tests.sh + + - name: Python tests + run: | + scripts/run-python-tests.sh \ + test_pregenerated_models.py \ + test_splines_short.py \ + test_misc.py + + + macos_python: + name: Tests MacOS Python + runs-on: macos-latest + + steps: + - name: Cache + uses: actions/cache@v4 + with: + path: | + ~/Library/Caches/pooch + key: ${{ runner.os }}-py${{ matrix.python-version }}-${{ github.job }} + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - uses: actions/checkout@v4 + - run: git fetch --prune --unshallow + + - name: Install dependencies + uses: ./.github/actions/install-macos-dependencies + + - name: Install python package + run: | + pip show numpy > /dev/null || python3 -m pip install numpy + scripts/installAmiciSource.sh + + - name: Check OpenMP support + run: source venv/bin/activate && python -c "import amici; import sys; sys.exit(not amici.compiledWithOpenMP())" + + - name: Get BioNetGen + run: scripts/buildBNGL.sh + + - name: Python tests + run: | + scripts/run-python-tests.sh \ + --ignore=test_pregenerated_models.py \ + --ignore=test_splines_short.py \ + --ignore=test_misc.py diff --git a/deps/AMICI/.github/workflows/test_python_ver_matrix.yml b/deps/AMICI/.github/workflows/test_python_ver_matrix.yml index 866a3fc0f..da2669b86 100644 --- a/deps/AMICI/.github/workflows/test_python_ver_matrix.yml +++ b/deps/AMICI/.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'] + python-version: ['3.10', '3.11', '3.12'] experimental: [false] steps: @@ -33,26 +33,37 @@ 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 - name: Install apt dependencies uses: ./.github/actions/install-apt-dependencies - # install AMICI - name: Build BNGL run: scripts/buildBNGL.sh + - name: Install python package run: scripts/installAmiciSource.sh + # install pysb before sympy to allow for sympy>=1.12 (https://github.com/pysb/pysb/commit/e83937cb8c74afc9b2fa96595b68464946745f33) + - run: source venv/bin/activate && pip3 install git+https://github.com/pysb/pysb + + # until sympy>1.12 is released + - 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 build/venv/bin/activate \ - && pip3 install git+https://github.com/pysb/pysb \ - && python3 -m pytest --ignore-glob=*petab* \ + source venv/bin/activate \ + && python3 -m pytest \ + --durations=10 \ + --ignore-glob=*petab* \ --ignore-glob=*test_splines.py ${AMICI_DIR}/python/tests diff --git a/deps/AMICI/.github/workflows/test_sbml_semantic_test_suite.yml b/deps/AMICI/.github/workflows/test_sbml_semantic_test_suite.yml index 0fde56b8f..69c78d44b 100644 --- a/deps/AMICI/.github/workflows/test_sbml_semantic_test_suite.yml +++ b/deps/AMICI/.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 @@ -29,32 +29,33 @@ 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 }} - 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 - 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 + uses: actions/upload-artifact@v4 with: - name: amici-semantic-results + name: amici-semantic-results-${{ matrix.cases }} path: tests/amici-semantic-results - name: Codecov SBMLSuite - uses: codecov/codecov-action@v3.1.0 + if: github.event_name == 'pull_request' || github.repository_owner == 'AMICI-dev' + uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} file: coverage_SBMLSuite.xml diff --git a/deps/AMICI/.github/workflows/test_valgrind.yml b/deps/AMICI/.github/workflows/test_valgrind.yml index 32eea2e0c..b3f893647 100644 --- a/deps/AMICI/.github/workflows/test_valgrind.yml +++ b/deps/AMICI/.github/workflows/test_valgrind.yml @@ -18,18 +18,18 @@ jobs: strategy: matrix: - python-version: [ "3.9" ] + python-version: [ "3.11" ] env: ENABLE_AMICI_DEBUGGING: "TRUE" 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 @@ -57,18 +57,18 @@ jobs: strategy: matrix: - python-version: [ "3.9" ] + python-version: [ "3.11" ] env: ENABLE_AMICI_DEBUGGING: "TRUE" 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/deps/AMICI/.github/workflows/test_windows.yml b/deps/AMICI/.github/workflows/test_windows.yml index 53834c300..8b5b3b89f 100644 --- a/deps/AMICI/.github/workflows/test_windows.yml +++ b/deps/AMICI/.github/workflows/test_windows.yml @@ -7,6 +7,7 @@ on: - cron: '48 4 * * *' pull_request: branches: + - develop - master jobs: @@ -26,15 +27,15 @@ jobs: strategy: matrix: - python-version: [ "3.9" ] + python-version: [ "3.11" ] 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 @@ -60,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 diff --git a/deps/AMICI/.gitignore b/deps/AMICI/.gitignore index 60b9ff503..4dc288441 100644 --- a/deps/AMICI/.gitignore +++ b/deps/AMICI/.gitignore @@ -202,3 +202,4 @@ CS_Signalling_ERBB_RAS_AKT/ cache_fiddy/* debug/* tests/benchmark-models/cache_fiddy/* +venv/* diff --git a/deps/AMICI/.gitrepo b/deps/AMICI/.gitrepo index 16993f56c..489c6619c 100644 --- a/deps/AMICI/.gitrepo +++ b/deps/AMICI/.gitrepo @@ -5,8 +5,8 @@ ; [subrepo] remote = git@github.com:ICB-DCM/AMICI.git - branch = v0.20.0 - commit = ffcbf6652f0d9e28125647a471bd09bead78c537 - parent = 540e2f4f8a0f99cb949d948793dc297a58fbfe03 + branch = v0.26.1 + commit = b0d79fffd3ab6e39296353d906e27033bc13dd4c + parent = 157fd81f04bd866127bc75f46887c4ce7c43566d cmdver = 0.4.6 method = merge diff --git a/deps/AMICI/.pre-commit-config.yaml b/deps/AMICI/.pre-commit-config.yaml index 521ff54f8..449f94ca7 100644 --- a/deps/AMICI/.pre-commit-config.yaml +++ b/deps/AMICI/.pre-commit-config.yaml @@ -1,14 +1,8 @@ # See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks repos: -- repo: https://github.com/pycqa/isort - rev: 5.12.0 - hooks: - - id: isort - name: isort (python) - args: ["--profile", "black", "--filter-files", "--line-length", "79"] - 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 @@ -16,15 +10,27 @@ repos: args: [--allow-multiple-documents] - id: end-of-file-fixer - id: trailing-whitespace -- repo: https://github.com/psf/black - rev: 23.7.0 +- repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.4.1 hooks: - - id: black-jupyter - # It is recommended to specify the latest version of Python - # supported by your project here, or alternatively use - # pre-commit's default_language_version, see - # https://pre-commit.com/#top_level-default_language_version - language_version: python3.11 - args: ["--line-length", "79"] + # Run the linter. + - id: ruff + args: + - --fix + - --config + - python/sdist/pyproject.toml + + # Run the formatter. + - id: ruff-format + args: + - --config + - python/sdist/pyproject.toml + +- repo: https://github.com/asottile/pyupgrade + rev: v3.15.2 + hooks: + - id: pyupgrade + args: ["--py310-plus"] exclude: '^(ThirdParty|models)/' diff --git a/deps/AMICI/.readthedocs.yml b/deps/AMICI/.readthedocs.yml index 659015764..23cc6adde 100644 --- a/deps/AMICI/.readthedocs.yml +++ b/deps/AMICI/.readthedocs.yml @@ -17,11 +17,10 @@ formats: python: install: - requirements: documentation/rtd_requirements.txt - - requirements: documentation/rtd_requirements2.txt build: os: "ubuntu-22.04" apt_packages: - libatlas-base-dev - swig tools: - python: "3.9" + python: "3.11" diff --git a/deps/AMICI/CHANGELOG.md b/deps/AMICI/CHANGELOG.md index 4b511d92d..a9f1cde58 100644 --- a/deps/AMICI/CHANGELOG.md +++ b/deps/AMICI/CHANGELOG.md @@ -1,12 +1,373 @@ # Changelog +See also our [versioning policy](https://amici.readthedocs.io/en/latest/versioning_policy.html). + ## v0.X Series +### v0.26.1 (2024-07-11) + +**Fixes** + +* Fixed some C++ exception handling that previously could crash Python under + certain conditions + + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2484 + +* Disabled turning warnings into errors when building amici on GitHub Actions + in downstream projects + + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2481 + +* Fixed CMP0167 warning with CMake 3.30 + + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2480 + + +**Full Changelog**: https://github.com/AMICI-dev/AMICI/compare/v0.26.0...v0.26.1 + +### v0.26.0 (2024-07-02) + +AMICI v0.26.0 requires sympy>=1.12.1 and petab>=0.4.0. + +**Policy changes** + +* Updated AMICI's [versioning / deprecation policy](https://amici.readthedocs.io/en/develop/versioning_policy.html) + + We will start removing deprecated features that had a deprecation warning + for longer than six months in the next minor release. + +**Deprecations** + +* Passing individual tables to `amici_import_petab` is now deprecated. + Use a `petab.Problem` instance instead. + + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2464 + +**Fixes** + +* Fixed a bug where during installation of AMICI, an incorrect sundials CMake + would be used resulting in installation errors. + + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2468 + +**Full Changelog**: https://github.com/AMICI-dev/AMICI/compare/v0.25.2...v0.26.0 + + +### 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** +* 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. + +**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. + +**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 + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2375 + +### 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** + +* 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 + 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. + +### v0.21.0 (2024-01-16) + +**Deprecations** + +* Moved PEtab-related functionality from `amici.petab_*` to the + petab-subpackage `amici.petab.*`. The old public functions are still + available but will be removed in a future release. + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2205, + https://github.com/AMICI-dev/AMICI/pull/2211, + https://github.com/AMICI-dev/AMICI/pull/2252 + +**Features** + +* Handle events occurring at fixed timepoints without root-finding. + This avoids event-after-reinitialization errors in many cases a brings a + slight performance improvement. + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2227 +* Added `PetabProblem` class for handling PEtab-defined simulation conditions, + making it easier to perform customized operations based on PEtab-defined + simulation conditions. + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2255 +* code-gen: Simplified `switch` statements, leading to reduced file sizes and + faster compilation for certain models. + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2240 +* Made `Model` and `ModelPtr` deepcopyable + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2247 +* Made `Solver` and `SolverPtr` deepcopyable + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2245 +* Added a debugging helper `get_model_for_preeq` for debugging simulation + issues during pre-equilibration. + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2250 +* Added `SwigPtrView` fields to `dir()` outputs + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2244 +* Use proper labels for in plotting functions if IDs are available in + `ReturnData`. + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2249 +* Added `ExpData::clear_observations` to set all measurements/sigmas to NaN + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2258 + +**Fixes** + +* Fixed AMICI hiding all warnings. Previously, importing `amici` resulted + in all warnings being hidden in the rest of the program. + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2243 +* CMake: Fixed model debug builds + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2222 +* Fixed CMake potentially using incorrect Python library for building model + extension + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2220 +* CMake: fixed cxx flag check + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2225 +* Fixed potential out-of-bounds read in `Model::checkFinite` for + matlab-imported models + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2232 +* Fixed piecewise/Heaviside handling + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2234 +* Deterministic order of event assignments + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2242 +* Proper error message in case of unsupported state-dependent sigmas + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2239 +* Fixed swig shadow warning + other linting issues + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2261 +* Fixed `SwigPtrView.__getattr__` + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2259 +* `simulate_petab`: Avoid warning when simulating with default parameters + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2265 + +**Documentation** + +* Updated Python package installation instructions for Arch Linux + by @willov in https://github.com/AMICI-dev/AMICI/pull/2212 +* Updated `ExpData` documentation + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2254 +* Documented simulation starting time `t0` + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2263 +* Updated PEtab example + by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2255 + +... + +**Full Changelog**: https://github.com/AMICI-dev/AMICI/compare/v0.20.0...v0.21.0 + ### v0.20.0 (2023-11-23) **Fixes** -* Fixed CMake cmake_minimum_required deprecation warning +* Fixed CMake `cmake_minimum_required` deprecation warning by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2183 * Fixed misleading preequilibration failure messages by @dweindl in https://github.com/AMICI-dev/AMICI/pull/2181 diff --git a/deps/AMICI/CMakeLists.txt b/deps/AMICI/CMakeLists.txt index d6f08a509..2dc0ada46 100644 --- a/deps/AMICI/CMakeLists.txt +++ b/deps/AMICI/CMakeLists.txt @@ -8,6 +8,10 @@ cmake_policy(VERSION 3.15...3.27) if(POLICY CMP0144) cmake_policy(SET CMP0144 NEW) endif(POLICY CMP0144) +# cmake >= 3.30 +if(POLICY CMP0167) + cmake_policy(SET CMP0167 NEW) +endif(POLICY CMP0167) project(amici) @@ -32,10 +36,11 @@ message(STATUS "CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX}") list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) -if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - # require at least gcc 4.9, otherwise regex wont work properly - if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9) - message(FATAL_ERROR "GCC version must be at least 4.9!") +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" + STREQUAL "MINGW") + # require at least gcc 9.1 for proper C+17 support, e.g. std::reduce + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.1) + message(FATAL_ERROR "GCC version must be at least 9.1!") endif() endif() @@ -84,8 +89,12 @@ endif() # Debug build? if("$ENV{ENABLE_AMICI_DEBUGGING}" OR "$ENV{ENABLE_GCOV_COVERAGE}") - add_compile_options(-UNDEBUG -O0 -g) - set(CMAKE_BUILD_TYPE "Debug") + add_compile_options(-UNDEBUG) + if(MSVC) + add_compile_options(-DEBUG) + else() + add_compile_options(-O0 -g) + endif() endif() # coverage options @@ -114,6 +123,7 @@ endif() # find dependencies include(GNUInstallDirs) +include(AmiciFindBLAS) find_package(OpenMP) find_package(Boost COMPONENTS chrono) @@ -127,58 +137,22 @@ 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") -find_package(SUNDIALS REQUIRED PATHS - "${VENDORED_SUNDIALS_INSTALL_DIR}/${CMAKE_INSTALL_LIBDIR}/cmake/sundials/") +# 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() +set(SUNDIALS_ROOT "${VENDORED_SUNDIALS_INSTALL_DIR}/${CMAKE_INSTALL_LIBDIR}") +find_package(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") -# 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}) - # if nothing is specified via environment variables, let's try FindBLAS - find_package(BLAS) - 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 @@ -253,16 +227,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)") @@ -283,10 +247,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") @@ -322,11 +282,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> ) @@ -425,9 +385,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 @@ -435,6 +397,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/deps/AMICI/LICENSE.md b/deps/AMICI/LICENSE.md index 4dadbc28a..a086d4595 100644 --- a/deps/AMICI/LICENSE.md +++ b/deps/AMICI/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: diff --git a/deps/AMICI/ThirdParty/SuiteSparse/.gitignore b/deps/AMICI/ThirdParty/SuiteSparse/.gitignore index d0d4223fd..82b356ef4 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/.gitignore +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/AMD/CMakeLists.txt b/deps/AMICI/ThirdParty/SuiteSparse/AMD/CMakeLists.txt index 4fdf61499..325375911 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/CMakeLists.txt +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/AMD/Config/AMD.pc.in b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Config/AMD.pc.in new file mode 100644 index 000000000..e5b509861 --- /dev/null +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/AMD/Config/AMDConfig.cmake.in b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Config/AMDConfig.cmake.in new file mode 100644 index 000000000..d21e05017 --- /dev/null +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/AMD/Config/amd.h.in b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Config/amd.h.in index f2a691665..3192e1139 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Config/amd.h.in +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/AMD_UserGuide.tex b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/AMD_UserGuide.tex index 2e5e1edef..794a29cde 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/AMD_UserGuide.tex +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/ChangeLog b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/ChangeLog index 97dbc0d87..3828f5ab2 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/ChangeLog +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/amd_version.tex b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/amd_version.tex index e24c8cda6..31528c9f3 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Doc/amd_version.tex +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/AMD/Include/amd.h b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Include/amd.h index 94aa96f11..188ee5a67 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Include/amd.h +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/AMD/Include/amd_internal.h b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Include/amd_internal.h index 85d43930b..b5649cbc9 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Include/amd_internal.h +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/AMD/Makefile b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Makefile index ad08cc1d1..15da7ceab 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Makefile +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_order.c b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_order.c index 1dcc15a00..9df32164e 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_order.c +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_version.c b/deps/AMICI/ThirdParty/SuiteSparse/AMD/Source/amd_version.c new file mode 100644 index 000000000..7d045f351 --- /dev/null +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/AMD/cmake_modules/FindAMD.cmake b/deps/AMICI/ThirdParty/SuiteSparse/AMD/cmake_modules/FindAMD.cmake deleted file mode 100644 index 1b135e05a..000000000 --- a/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/BTF/CMakeLists.txt b/deps/AMICI/ThirdParty/SuiteSparse/BTF/CMakeLists.txt index ce4ecf45a..059bb80c2 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/CMakeLists.txt +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/BTF/Config/BTF.pc.in b/deps/AMICI/ThirdParty/SuiteSparse/BTF/Config/BTF.pc.in new file mode 100644 index 000000000..13de408ff --- /dev/null +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/BTF/Config/BTFConfig.cmake.in b/deps/AMICI/ThirdParty/SuiteSparse/BTF/Config/BTFConfig.cmake.in new file mode 100644 index 000000000..4fdca7a1e --- /dev/null +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/BTF/Config/btf.h.in b/deps/AMICI/ThirdParty/SuiteSparse/BTF/Config/btf.h.in index 01fda8fd8..a5985db8a 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/Config/btf.h.in +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/BTF/Doc/ChangeLog b/deps/AMICI/ThirdParty/SuiteSparse/BTF/Doc/ChangeLog index d984d8a69..d857cbc6e 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/Doc/ChangeLog +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/BTF/Include/btf.h b/deps/AMICI/ThirdParty/SuiteSparse/BTF/Include/btf.h index 58cb94d7e..c152e879a 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/Include/btf.h +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/BTF/Include/btf_internal.h b/deps/AMICI/ThirdParty/SuiteSparse/BTF/Include/btf_internal.h index 15b1b0445..96acda901 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/Include/btf_internal.h +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/BTF/Makefile b/deps/AMICI/ThirdParty/SuiteSparse/BTF/Makefile index f86ddabfd..5e9baae9f 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/BTF/Makefile +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/BTF/Source/btf_version.c b/deps/AMICI/ThirdParty/SuiteSparse/BTF/Source/btf_version.c new file mode 100644 index 000000000..151f24d79 --- /dev/null +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/BTF/cmake_modules/FindBTF.cmake b/deps/AMICI/ThirdParty/SuiteSparse/BTF/cmake_modules/FindBTF.cmake deleted file mode 100644 index b5e6153ed..000000000 --- a/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/CMakeLists.txt b/deps/AMICI/ThirdParty/SuiteSparse/CMakeLists.txt new file mode 100644 index 000000000..0a23a5a48 --- /dev/null +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/CMakeLists.txt b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/CMakeLists.txt index 0f9603c95..39ac72eda 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/CMakeLists.txt +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Config/COLAMD.pc.in b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Config/COLAMD.pc.in new file mode 100644 index 000000000..5444c608f --- /dev/null +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Config/COLAMDConfig.cmake.in b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Config/COLAMDConfig.cmake.in new file mode 100644 index 000000000..7bb4701f4 --- /dev/null +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Config/colamd.h.in b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Config/colamd.h.in index fb0450ac4..2d9a0c07f 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Config/colamd.h.in +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Doc/ChangeLog b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Doc/ChangeLog index f7a3dcf3c..9904dacaf 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Doc/ChangeLog +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Include/colamd.h b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Include/colamd.h index f258ef68e..93c2c08eb 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Include/colamd.h +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Makefile b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Makefile index 2c7de9ee4..787ee26f3 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Makefile +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Source/colamd.c b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Source/colamd.c index af5b27f7a..968d90fc6 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Source/colamd.c +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Source/colamd_version.c b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/Source/colamd_version.c new file mode 100644 index 000000000..9184817ef --- /dev/null +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/cmake_modules/FindCOLAMD.cmake b/deps/AMICI/ThirdParty/SuiteSparse/COLAMD/cmake_modules/FindCOLAMD.cmake deleted file mode 100644 index 00e8a9ac0..000000000 --- a/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/ChangeLog b/deps/AMICI/ThirdParty/SuiteSparse/ChangeLog index 06fe8950b..0f1eb8790 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/ChangeLog +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/KLU/CMakeLists.txt b/deps/AMICI/ThirdParty/SuiteSparse/KLU/CMakeLists.txt index f2e88576e..05025e37f 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/CMakeLists.txt +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/KLU/Config/KLU.pc.in b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Config/KLU.pc.in new file mode 100644 index 000000000..df7191b11 --- /dev/null +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/KLU/Config/KLUConfig.cmake.in b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Config/KLUConfig.cmake.in new file mode 100644 index 000000000..f28e22ba7 --- /dev/null +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/KLU/Config/klu.h.in b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Config/klu.h.in index 769a82aa6..1f02c4be5 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Config/klu.h.in +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/ChangeLog b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/ChangeLog index b4a4edabe..d4313dbc5 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/ChangeLog +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/KLU_UserGuide.tex b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/KLU_UserGuide.tex index 8ed946629..e710ff388 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/KLU_UserGuide.tex +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/klu_version.tex b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/klu_version.tex index f15e03053..5e71c34bc 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Doc/klu_version.tex +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/KLU/Include/klu.h b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Include/klu.h index 80204ecf7..22155ff8b 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Include/klu.h +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/KLU/Include/klu_internal.h b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Include/klu_internal.h index 60fa514a3..b30f93775 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Include/klu_internal.h +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/KLU/Include/klu_version.h b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Include/klu_version.h index f2d4915c9..0e0951d11 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Include/klu_version.h +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/KLU/Makefile b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Makefile index 896ea4a1b..97bb49f4e 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/KLU/Makefile +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_version.c b/deps/AMICI/ThirdParty/SuiteSparse/KLU/Source/klu_version.c new file mode 100644 index 000000000..e96215f58 --- /dev/null +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/KLU/cmake_modules/FindKLU.cmake b/deps/AMICI/ThirdParty/SuiteSparse/KLU/cmake_modules/FindKLU.cmake deleted file mode 100644 index cd3f4040e..000000000 --- a/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/KLU/cmake_modules/FindKLU_CHOLMOD.cmake b/deps/AMICI/ThirdParty/SuiteSparse/KLU/cmake_modules/FindKLU_CHOLMOD.cmake deleted file mode 100644 index d3b366322..000000000 --- a/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/LICENSE.txt b/deps/AMICI/ThirdParty/SuiteSparse/LICENSE.txt index 0e51e9e0c..8c6d47346 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/LICENSE.txt +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/README.md b/deps/AMICI/ThirdParty/SuiteSparse/README.md index 6771fa1d8..29c74df12 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/README.md +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/CMakeLists.txt b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/CMakeLists.txt index 3c2538236..8e5c119b5 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/CMakeLists.txt +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/Config/README.md.in b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/Config/README.md.in index e45ae9c08..bc0c9142f 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/Config/README.md.in +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/Config/SuiteSparse_config.h.in b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/Config/SuiteSparse_config.h.in index 09d05c922..0d203f55e 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/Config/SuiteSparse_config.h.in +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/Config/SuiteSparse_config.pc.in b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/Config/SuiteSparse_config.pc.in new file mode 100644 index 000000000..f082c2260 --- /dev/null +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/Config/SuiteSparse_configConfig.cmake.in b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/Config/SuiteSparse_configConfig.cmake.in new file mode 100644 index 000000000..1831e466a --- /dev/null +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/Makefile b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/Makefile index 9893afe78..a14ac2fdf 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/Makefile +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/README.txt b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/README.txt index 4ff01953f..9d306c9e2 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/README.txt +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/SuiteSparse_config.c b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/SuiteSparse_config.c index 079093716..ee220a77f 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/SuiteSparse_config.c +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/SuiteSparse_config.h b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/SuiteSparse_config.h index 2b917bded..d2300dcfc 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/SuiteSparse_config.h +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/FindSuiteSparse_config.cmake b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/FindSuiteSparse_config.cmake deleted file mode 100644 index 2b75390cc..000000000 --- a/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseBLAS.cmake b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseBLAS.cmake index 9e4b779ac..2225f5373 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseBLAS.cmake +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseBLAS64.cmake b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseBLAS64.cmake index 744aaef91..72efcd5ce 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseBLAS64.cmake +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseLAPACK.cmake b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseLAPACK.cmake index 27ff73cad..82366e0aa 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseLAPACK.cmake +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparsePolicy.cmake b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparsePolicy.cmake index 8c18fa758..b6b195407 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparsePolicy.cmake +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseReport.cmake b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseReport.cmake index 9271c4a92..0b47f34c1 100644 --- a/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparseReport.cmake +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparse__thread.cmake b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparse__thread.cmake new file mode 100644 index 000000000..1c52185b7 --- /dev/null +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparse_ssize_t.cmake b/deps/AMICI/ThirdParty/SuiteSparse/SuiteSparse_config/cmake_modules/SuiteSparse_ssize_t.cmake deleted file mode 100644 index 2b74bae83..000000000 --- a/deps/AMICI/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/deps/AMICI/ThirdParty/SuiteSparse/build/.gitignore b/deps/AMICI/ThirdParty/SuiteSparse/build/.gitignore new file mode 100644 index 000000000..52e15321b --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/build/.gitignore @@ -0,0 +1,4 @@ +# Ignore all files except this file. +* +*/ +!.gitignore diff --git a/deps/AMICI/ThirdParty/SuiteSparse/lib/.gitignore b/deps/AMICI/ThirdParty/SuiteSparse/lib/.gitignore new file mode 100644 index 000000000..5e7d2734c --- /dev/null +++ b/deps/AMICI/ThirdParty/SuiteSparse/lib/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore diff --git a/deps/AMICI/ThirdParty/gsl/gsl/gsl-lite.hpp b/deps/AMICI/ThirdParty/gsl/gsl/gsl-lite.hpp index 380fb2f34..f8e46ae41 100644 --- a/deps/AMICI/ThirdParty/gsl/gsl/gsl-lite.hpp +++ b/deps/AMICI/ThirdParty/gsl/gsl/gsl-lite.hpp @@ -3,7 +3,7 @@ // For more information see https://github.com/gsl-lite/gsl-lite // // Copyright (c) 2015-2019 Martin Moene -// Copyright (c) 2019-2021 Moritz Beutel +// Copyright (c) 2019-2023 Moritz Beutel // Copyright (c) 2015-2018 Microsoft Corporation. All rights reserved. // // This code is licensed under the MIT License (MIT). @@ -31,7 +31,7 @@ #include // for abort() #define gsl_lite_MAJOR 0 -#define gsl_lite_MINOR 40 +#define gsl_lite_MINOR 41 #define gsl_lite_PATCH 0 #define gsl_lite_VERSION gsl_STRINGIFY(gsl_lite_MAJOR) "." gsl_STRINGIFY(gsl_lite_MINOR) "." gsl_STRINGIFY(gsl_lite_PATCH) @@ -183,6 +183,15 @@ #endif #define gsl_FEATURE_OWNER_MACRO_() gsl_FEATURE_OWNER_MACRO +//#if defined( gsl_FEATURE_STRING_SPAN ) +//# if ! gsl_CHECK_CFG_TOGGLE_VALUE_( gsl_FEATURE_STRING_SPAN ) +//# pragma message ("invalid configuration value gsl_FEATURE_STRING_SPAN=" gsl_STRINGIFY(gsl_FEATURE_STRING_SPAN) ", must be 0 or 1") +//# endif +//#else +//# define gsl_FEATURE_STRING_SPAN (gsl_CONFIG_DEFAULTS_VERSION == 0) // default +//#endif +//#define gsl_FEATURE_STRING_SPAN_() gsl_FEATURE_STRING_SPAN + #if defined( gsl_FEATURE_EXPERIMENTAL_RETURN_GUARD ) # if ! gsl_CHECK_CFG_TOGGLE_VALUE_( gsl_FEATURE_EXPERIMENTAL_RETURN_GUARD ) # pragma message ("invalid configuration value gsl_FEATURE_EXPERIMENTAL_RETURN_GUARD=" gsl_STRINGIFY(gsl_FEATURE_EXPERIMENTAL_RETURN_GUARD) ", must be 0 or 1") @@ -217,7 +226,7 @@ #if ! defined( gsl_CONFIG_DEPRECATE_TO_LEVEL ) # if gsl_CONFIG_DEFAULTS_VERSION >= 1 -# define gsl_CONFIG_DEPRECATE_TO_LEVEL 6 +# define gsl_CONFIG_DEPRECATE_TO_LEVEL 7 # else # define gsl_CONFIG_DEPRECATE_TO_LEVEL 0 # endif @@ -301,6 +310,15 @@ #endif #define gsl_CONFIG_NARROW_THROWS_ON_TRUNCATION_() gsl_CONFIG_NARROW_THROWS_ON_TRUNCATION +#if defined( gsl_CONFIG_VALIDATES_UNENFORCED_CONTRACT_EXPRESSIONS ) +# if ! gsl_CHECK_CFG_TOGGLE_VALUE_( gsl_CONFIG_VALIDATES_UNENFORCED_CONTRACT_EXPRESSIONS ) +# pragma message ("invalid configuration value gsl_CONFIG_VALIDATES_UNENFORCED_CONTRACT_EXPRESSIONS=" gsl_STRINGIFY(gsl_CONFIG_VALIDATES_UNENFORCED_CONTRACT_EXPRESSIONS) ", must be 0 or 1") +# endif +#else +# define gsl_CONFIG_VALIDATES_UNENFORCED_CONTRACT_EXPRESSIONS 1 // default +#endif +#define gsl_CONFIG_VALIDATES_UNENFORCED_CONTRACT_EXPRESSIONS_() gsl_CONFIG_VALIDATES_UNENFORCED_CONTRACT_EXPRESSIONS + #if defined( gsl_CONFIG_CONTRACT_CHECKING_EXPECTS_OFF ) # if ! gsl_CHECK_CFG_NO_VALUE_( gsl_CONFIG_CONTRACT_CHECKING_EXPECTS_OFF ) # pragma message ("invalid configuration value gsl_CONFIG_CONTRACT_CHECKING_EXPECTS_OFF=" gsl_STRINGIFY(gsl_CONFIG_CONTRACT_CHECKING_EXPECTS_OFF) "; macro must be defined without value") @@ -474,7 +492,7 @@ # endif #endif -// C++ language version detection (C++20 is speculative): +// C++ language version detection (C++23 is speculative): // Note: VC14.0/1900 (VS2015) lacks too much from C++14. #ifndef gsl_CPLUSPLUS @@ -495,7 +513,8 @@ #define gsl_CPP11_OR_GREATER ( gsl_CPLUSPLUS >= 201103L ) #define gsl_CPP14_OR_GREATER ( gsl_CPLUSPLUS >= 201402L ) #define gsl_CPP17_OR_GREATER ( gsl_CPLUSPLUS >= 201703L ) -#define gsl_CPP20_OR_GREATER ( gsl_CPLUSPLUS >= 202000L ) +#define gsl_CPP20_OR_GREATER ( gsl_CPLUSPLUS >= 202002L ) +#define gsl_CPP23_OR_GREATER ( gsl_CPLUSPLUS > 202002L ) // tentative // C++ language version (represent 98 as 3): @@ -517,6 +536,7 @@ // MSVC++ 14.0 _MSC_VER == 1900 gsl_COMPILER_MSVC_VERSION == 140 (Visual Studio 2015) // MSVC++ 14.1 _MSC_VER >= 1910 gsl_COMPILER_MSVC_VERSION == 141 (Visual Studio 2017) // MSVC++ 14.2 _MSC_VER >= 1920 gsl_COMPILER_MSVC_VERSION == 142 (Visual Studio 2019) +// MSVC++ 14.3 _MSC_VER >= 1930 gsl_COMPILER_MSVC_VERSION == 143 (Visual Studio 2022) #if defined( _MSC_VER ) && ! defined( __clang__ ) # define gsl_COMPILER_MSVC_VER (_MSC_VER ) @@ -551,7 +571,10 @@ // AppleClang 11.0.3 __apple_build_version__ == 11030032 gsl_COMPILER_APPLECLANG_VERSION == 1103 (Xcode 11.4, 11.4.1, 11.5, 11.6) (LLVM 9.0.0) // AppleClang 12.0.0 __apple_build_version__ == 12000032 gsl_COMPILER_APPLECLANG_VERSION == 1200 (Xcode 12.0–12.4) (LLVM 10.0.0) // AppleClang 12.0.5 __apple_build_version__ == 12050022 gsl_COMPILER_APPLECLANG_VERSION == 1205 (Xcode 12.5) (LLVM 11.1.0) -// AppleClang 13.0.0 __apple_build_version__ == 13000029 gsl_COMPILER_APPLECLANG_VERSION == 1300 (Xcode 13.0) (LLVM 12.0.0) +// AppleClang 13.0.0 __apple_build_version__ == 13000029 gsl_COMPILER_APPLECLANG_VERSION == 1300 (Xcode 13.0–13.2.1) (LLVM 12.0.0) +// AppleClang 13.1.6 __apple_build_version__ == 13160021 gsl_COMPILER_APPLECLANG_VERSION == 1316 (Xcode 13.3–13.4.1) (LLVM 13.0.0) +// AppleClang 14.0.0 __apple_build_version__ == 14000029 gsl_COMPILER_APPLECLANG_VERSION == 1400 (Xcode 14.0–14.2) (LLVM 14.0.0) +// AppleClang 14.0.3 __apple_build_version__ == 14030022 gsl_COMPILER_APPLECLANG_VERSION == 1403 (Xcode 14.3) (LLVM 15.0.0) #if defined( __apple_build_version__ ) # define gsl_COMPILER_APPLECLANG_VERSION gsl_COMPILER_VERSION( __clang_major__, __clang_minor__, __clang_patchlevel__ ) @@ -611,7 +634,7 @@ // Presence of wide character support: -#ifdef __DJGPP__ +#if defined(__DJGPP__) || (defined(_LIBCPP_VERSION) && defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)) # define gsl_HAVE_WCHAR 0 #else # define gsl_HAVE_WCHAR 1 @@ -768,6 +791,12 @@ #define gsl_HAVE_CONSTEXPR_20_() gsl_HAVE_CONSTEXPR_20 +// Presence of C++23 language features: + +#define gsl_HAVE_CONSTEXPR_23 gsl_CPP23_OR_GREATER + +#define gsl_HAVE_CONSTEXPR_23_() gsl_HAVE_CONSTEXPR_23 + // Presence of C++ library features: #if gsl_BETWEEN( gsl_COMPILER_ARMCC_VERSION, 1, 600 ) @@ -777,12 +806,14 @@ # define gsl_STDLIB_CPP14_OR_GREATER 0 # define gsl_STDLIB_CPP17_OR_GREATER 0 # define gsl_STDLIB_CPP20_OR_GREATER 0 +# define gsl_STDLIB_CPP23_OR_GREATER 0 #else # define gsl_STDLIB_CPP98_OR_GREATER gsl_CPP98_OR_GREATER # define gsl_STDLIB_CPP11_OR_GREATER gsl_CPP11_OR_GREATER # define gsl_STDLIB_CPP14_OR_GREATER gsl_CPP14_OR_GREATER # define gsl_STDLIB_CPP17_OR_GREATER gsl_CPP17_OR_GREATER # define gsl_STDLIB_CPP20_OR_GREATER gsl_CPP20_OR_GREATER +# define gsl_STDLIB_CPP23_OR_GREATER gsl_CPP23_OR_GREATER #endif #define gsl_STDLIB_CPP11_100 (gsl_STDLIB_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VER >= 1600) @@ -890,6 +921,12 @@ # define gsl_constexpr20 /*constexpr*/ #endif +#if gsl_HAVE( CONSTEXPR_23 ) +# define gsl_constexpr23 constexpr +#else +# define gsl_constexpr23 /*constexpr*/ +#endif + #if gsl_HAVE( EXPLICIT ) # define gsl_explicit explicit #else @@ -1180,7 +1217,7 @@ namespace __cxxabiv1 { struct __cxa_eh_globals; extern "C" __cxa_eh_globals * __ #endif // gsl_FEATURE( EXPERIMENTAL_RETURN_GUARD ) -// MSVC warning suppression macros: +// Warning suppression macros: #if gsl_COMPILER_MSVC_VERSION >= 140 && ! gsl_COMPILER_NVCC_VERSION # define gsl_SUPPRESS_MSGSL_WARNING(expr) [[gsl::suppress(expr)]] @@ -1195,6 +1232,18 @@ namespace __cxxabiv1 { struct __cxa_eh_globals; extern "C" __cxa_eh_globals * __ # define gsl_RESTORE_MSVC_WARNINGS() #endif +// Warning suppressions: + +#if gsl_COMPILER_CLANG_VERSION || gsl_COMPILER_APPLECLANG_VERSION +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wweak-vtables" // because of `fail_fast` and `narrowing_error` +#endif // gsl_COMPILER_CLANG_VERSION || gsl_COMPILER_APPLECLANG_VERSION + +#if gsl_COMPILER_GNUC_VERSION +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wuseless-cast" // we use `static_cast<>()` in several places where it is possibly redundant depending on the configuration of the library +#endif // gsl_COMPILER_GNUC_VERSION + // Suppress the following MSVC GSL warnings: // - C26432: gsl::c.21 : if you define or delete any default operation in the type '...', define or delete them all // - C26410: gsl::r.32 : the parameter 'ptr' is a reference to const unique pointer, use const T* or const T& instead @@ -1214,6 +1263,12 @@ namespace __cxxabiv1 { struct __cxa_eh_globals; extern "C" __cxa_eh_globals * __ // - C26457: es.48 : (void) should not be used to ignore return values, use 'std::ignore =' instead gsl_DISABLE_MSVC_WARNINGS( 26432 26410 26415 26418 26472 26439 26440 26455 26473 26481 26482 26446 26490 26487 26457 ) +#if gsl_BETWEEN( gsl_COMPILER_MSVC_VERSION, 110, 140 ) // VS 2012 and 2013 +# pragma warning(disable: 4127) // conditional expression is constant +#endif // gsl_BETWEEN( gsl_COMPILER_MSVC_VERSION, 110, 140 ) +#if gsl_COMPILER_MSVC_VERSION == 140 // VS 2015 +# pragma warning(disable: 4577) // 'noexcept' used with no exception handling mode specified; termination on exception is not guaranteed. Specify /EHsc +#endif // gsl_COMPILER_MSVC_VERSION == 140 namespace gsl { @@ -1558,6 +1613,12 @@ template< class T > struct remove_cvref { typedef typename std11::remove_cv< typ } // namespace std20 +// C++23 emulation: + +namespace std23 { + +} // namespace std23 + namespace detail { /// for gsl_ENABLE_IF_() @@ -1723,7 +1784,7 @@ typedef gsl_CONFIG_INDEX_TYPE diff; // GSL.assert: assertions // -#if gsl_HAVE( TYPE_TRAITS ) +#if gsl_HAVE( TYPE_TRAITS ) && gsl_CONFIG( VALIDATES_UNENFORCED_CONTRACT_EXPRESSIONS ) # define gsl_ELIDE_( x ) static_assert( ::std::is_constructible::value, "argument of contract check must be convertible to bool" ) #else # define gsl_ELIDE_( x ) @@ -1742,7 +1803,7 @@ typedef gsl_CONFIG_INDEX_TYPE diff; #if gsl_DEVICE_CODE # if defined( gsl_CONFIG_DEVICE_UNENFORCED_CONTRACTS_ASSUME ) # if gsl_COMPILER_NVCC_VERSION >= 113 -# define gsl_ASSUME_( x ) ( ( x ) ? static_cast(0) : __builtin_unreachable() ) +# define gsl_ASSUME_( x ) ( __builtin_assume( !!( x ) ) ) # define gsl_ASSUME_UNREACHABLE_() __builtin_unreachable() # else // unknown device compiler # error gsl_CONFIG_DEVICE_UNENFORCED_CONTRACTS_ASSUME: gsl-lite does not know how to generate UB optimization hints in device code for this compiler; use gsl_CONFIG_DEVICE_UNENFORCED_CONTRACTS_ELIDE instead @@ -2340,7 +2401,7 @@ namespace detail { #endif template< class T, class U > -gsl_NODISCARD +gsl_NODISCARD gsl_constexpr14 #if !gsl_CONFIG( NARROW_THROWS_ON_TRUNCATION ) && !defined( gsl_CONFIG_CONTRACT_VIOLATION_THROWS ) gsl_api #endif @@ -2398,7 +2459,7 @@ narrow( U u ) } template< class T, class U > -gsl_NODISCARD gsl_api inline T +gsl_NODISCARD gsl_api gsl_constexpr14 inline T narrow_failfast( U u ) { T t = static_cast( u ); @@ -3261,8 +3322,8 @@ class not_null_ic : public not_null > gsl_api gsl_constexpr14 #if gsl_HAVE( MOVE_FORWARD ) - not_null_ic( U && u ) - : not_null( std::forward( u ) ) + not_null_ic( U u ) + : not_null( std::move( u ) ) #else // ! gsl_HAVE( MOVE_FORWARD ) not_null_ic( U const & u ) : not_null( u ) @@ -4393,6 +4454,7 @@ byte_span( T const & t ) gsl_noexcept #endif // gsl_FEATURE_TO_STD( BYTE_SPAN ) +//#if gsl_FEATURE( STRING_SPAN ) // // basic_string_span: // @@ -4465,19 +4527,31 @@ class basic_string_span #ifdef __CUDACC_RELAXED_CONSTEXPR__ gsl_api #endif // __CUDACC_RELAXED_CONSTEXPR__ +#if gsl_DEPRECATE_TO_LEVEL( 7 ) + gsl_DEPRECATED_MSG("basic_string_span<> is deprecated; use span<> instead") +#endif // gsl_DEPRECATE_TO_LEVEL( 7 ) gsl_constexpr basic_string_span( pointer ptr ) : span_( remove_z( ptr, (std::numeric_limits::max)() ) ) {} +#if gsl_DEPRECATE_TO_LEVEL( 7 ) + gsl_DEPRECATED_MSG("basic_string_span<> is deprecated; use span<> instead") +#endif // gsl_DEPRECATE_TO_LEVEL( 7 ) gsl_api gsl_constexpr basic_string_span( pointer ptr, index_type count ) : span_( ptr, count ) {} +#if gsl_DEPRECATE_TO_LEVEL( 7 ) + gsl_DEPRECATED_MSG("basic_string_span<> is deprecated; use span<> instead") +#endif // gsl_DEPRECATE_TO_LEVEL( 7 ) gsl_api gsl_constexpr basic_string_span( pointer firstElem, pointer lastElem ) : span_( firstElem, lastElem ) {} template< std::size_t N > +#if gsl_DEPRECATE_TO_LEVEL( 7 ) + gsl_DEPRECATED_MSG("basic_string_span<><> is deprecated; use span<> instead") +#endif // gsl_DEPRECATE_TO_LEVEL( 7 ) gsl_constexpr basic_string_span( element_type (&arr)[N] ) : span_( remove_z( gsl_ADDRESSOF( arr[0] ), N ) ) {} @@ -4485,11 +4559,17 @@ class basic_string_span #if gsl_HAVE( ARRAY ) template< std::size_t N > +#if gsl_DEPRECATE_TO_LEVEL( 7 ) + gsl_DEPRECATED_MSG("basic_string_span<><> is deprecated; use span<> instead") +#endif // gsl_DEPRECATE_TO_LEVEL( 7 ) gsl_constexpr basic_string_span( std::array< typename std11::remove_const::type, N> & arr ) : span_( remove_z( arr ) ) {} template< std::size_t N > +#if gsl_DEPRECATE_TO_LEVEL( 7 ) + gsl_DEPRECATED_MSG("basic_string_span<><> is deprecated; use span<> instead") +#endif // gsl_DEPRECATE_TO_LEVEL( 7 ) gsl_constexpr basic_string_span( std::array< typename std11::remove_const::type, N> const & arr ) : span_( remove_z( arr ) ) {} @@ -4508,6 +4588,9 @@ class basic_string_span && std::is_convertible< typename Container::pointer, decltype(std::declval().data()) >::value )) > +#if gsl_DEPRECATE_TO_LEVEL( 7 ) + gsl_DEPRECATED_MSG("basic_string_span<><> is deprecated; use span<> instead") +#endif // gsl_DEPRECATE_TO_LEVEL( 7 ) gsl_constexpr basic_string_span( Container & cont ) : span_( ( cont ) ) {} @@ -4522,6 +4605,9 @@ class basic_string_span && std::is_convertible< typename Container::pointer, decltype(std::declval().data()) >::value )) > +#if gsl_DEPRECATE_TO_LEVEL( 7 ) + gsl_DEPRECATED_MSG("basic_string_span<><> is deprecated; use span<> instead") +#endif // gsl_DEPRECATE_TO_LEVEL( 7 ) gsl_constexpr basic_string_span( Container const & cont ) : span_( ( cont ) ) {} @@ -4529,11 +4615,17 @@ class basic_string_span #elif gsl_HAVE( UNCONSTRAINED_SPAN_CONTAINER_CTOR ) template< class Container > +#if gsl_DEPRECATE_TO_LEVEL( 7 ) + gsl_DEPRECATED_MSG("basic_string_span<><> is deprecated; use span<> instead") +#endif // gsl_DEPRECATE_TO_LEVEL( 7 ) gsl_constexpr basic_string_span( Container & cont ) : span_( cont ) {} template< class Container > +#if gsl_DEPRECATE_TO_LEVEL( 7 ) + gsl_DEPRECATED_MSG("basic_string_span<><> is deprecated; use span<> instead") +#endif // gsl_DEPRECATE_TO_LEVEL( 7 ) gsl_constexpr basic_string_span( Container const & cont ) : span_( cont ) {} @@ -4541,6 +4633,9 @@ class basic_string_span #else template< class U > +#if gsl_DEPRECATE_TO_LEVEL( 7 ) + gsl_DEPRECATED_MSG("basic_string_span<><> is deprecated; use span<> instead") +#endif // gsl_DEPRECATE_TO_LEVEL( 7 ) gsl_api gsl_constexpr basic_string_span( span const & rhs ) : span_( rhs ) {} @@ -4550,6 +4645,9 @@ class basic_string_span #if gsl_FEATURE_TO_STD( WITH_CONTAINER ) template< class Container > +#if gsl_DEPRECATE_TO_LEVEL( 7 ) + gsl_DEPRECATED_MSG("basic_string_span<> is deprecated; use span<> instead") +#endif // gsl_DEPRECATE_TO_LEVEL( 7 ) gsl_constexpr basic_string_span( with_container_t, Container & cont ) : span_( with_container, cont ) {} @@ -4570,6 +4668,9 @@ class basic_string_span template< class U gsl_ENABLE_IF_(( std::is_convertible::pointer, pointer>::value )) > +#if gsl_DEPRECATE_TO_LEVEL( 7 ) + gsl_DEPRECATED_MSG("basic_string_span<><> is deprecated; use span<> instead") +#endif // gsl_DEPRECATE_TO_LEVEL( 7 ) gsl_api gsl_constexpr basic_string_span( basic_string_span const & rhs ) : span_( reinterpret_cast( rhs.data() ), rhs.length() ) // NOLINT {} @@ -4578,18 +4679,27 @@ class basic_string_span template< class U gsl_ENABLE_IF_(( std::is_convertible::pointer, pointer>::value )) > +#if gsl_DEPRECATE_TO_LEVEL( 7 ) + gsl_DEPRECATED_MSG("basic_string_span<><> is deprecated; use span<> instead") +#endif // gsl_DEPRECATE_TO_LEVEL( 7 ) gsl_api gsl_constexpr basic_string_span( basic_string_span && rhs ) : span_( reinterpret_cast( rhs.data() ), rhs.length() ) // NOLINT {} #endif // gsl_STDLIB_CPP11_120 template< class CharTraits, class Allocator > +#if gsl_DEPRECATE_TO_LEVEL( 7 ) + gsl_DEPRECATED_MSG("basic_string_span<><> is deprecated; use span<> instead") +#endif // gsl_DEPRECATE_TO_LEVEL( 7 ) gsl_constexpr basic_string_span( std::basic_string< typename std11::remove_const::type, CharTraits, Allocator > & str ) : span_( gsl_ADDRESSOF( str[0] ), str.length() ) {} template< class CharTraits, class Allocator > +#if gsl_DEPRECATE_TO_LEVEL( 7 ) + gsl_DEPRECATED_MSG("basic_string_span<><> is deprecated; use span<> instead") +#endif // gsl_DEPRECATE_TO_LEVEL( 7 ) gsl_constexpr basic_string_span( std::basic_string< typename std11::remove_const::type, CharTraits, Allocator > const & str ) : span_( gsl_ADDRESSOF( str[0] ), str.length() ) @@ -4933,6 +5043,7 @@ as_bytes( basic_string_span spn ) gsl_noexcept { return span< const byte >( reinterpret_cast( spn.data() ), spn.size_bytes() ); // NOLINT } +//#endif // gsl_FEATURE( STRING_SPAN ) // // String types: @@ -4946,6 +5057,8 @@ typedef wchar_t * wzstring; typedef const wchar_t * cwzstring; #endif +//#if gsl_FEATURE( STRING_SPAN ) + typedef basic_string_span< char > string_span; typedef basic_string_span< char const > cstring_span; @@ -5066,6 +5179,7 @@ std::basic_ostream< wchar_t, Traits > & operator<<( std::basic_ostream< wchar_t, } #endif // gsl_HAVE( WCHAR ) +//#endif // gsl_FEATURE( STRING_SPAN ) // // ensure_sentinel() @@ -5124,6 +5238,7 @@ ensure_z( Container & cont ) } # endif +//#if gsl_FEATURE( STRING_SPAN ) // // basic_zstring_span<> - A view of contiguous null-terminated characters, replace (*,len). // @@ -5141,6 +5256,9 @@ class basic_zstring_span typedef element_type * czstring_type; typedef basic_string_span string_span_type; +#if gsl_DEPRECATE_TO_LEVEL( 7 ) + gsl_DEPRECATED_MSG("basic_zstring_span<> is deprecated") +#endif // gsl_DEPRECATE_TO_LEVEL( 7 ) gsl_api gsl_constexpr14 basic_zstring_span( span_type s ) : span_( s ) { @@ -5164,13 +5282,19 @@ class basic_zstring_span return false; } +#if gsl_DEPRECATE_TO_LEVEL( 7 ) + gsl_DEPRECATED_MSG("basic_zstring_span<> is deprecated") +#endif // gsl_DEPRECATE_TO_LEVEL( 7 ) gsl_NODISCARD gsl_api gsl_constexpr string_span_type as_string_span() const gsl_noexcept { return string_span_type( span_.data(), span_.size() - 1 ); } - /*gsl_api*/ // currently disabled due to an apparent NVCC bug + #if gsl_DEPRECATE_TO_LEVEL( 7 ) + gsl_DEPRECATED_MSG("basic_zstring_span<> is deprecated") +#endif // gsl_DEPRECATE_TO_LEVEL( 7 ) + /*gsl_api*/ // currently disabled due to an apparent NVCC bug gsl_NODISCARD gsl_constexpr string_span_type ensure_z() const { @@ -5198,6 +5322,7 @@ typedef basic_zstring_span< char const > czstring_span; typedef basic_zstring_span< wchar_t > wzstring_span; typedef basic_zstring_span< wchar_t const > cwzstring_span; #endif +//#endif // gsl_FEATURE( STRING_SPAN ) } // namespace gsl @@ -5306,11 +5431,13 @@ namespace std11 = ::gsl::std11; namespace std14 = ::gsl::std14; namespace std17 = ::gsl::std17; namespace std20 = ::gsl::std20; +namespace std23 = ::gsl::std23; using namespace std11; //using namespace std14; // contains only make_unique<>(), which is superseded by `gsl::make_unique<>()` using namespace std17; using namespace std20; +using namespace std23; using namespace ::gsl::detail::no_adl; @@ -5380,6 +5507,7 @@ using ::gsl::as_writable_bytes; using ::gsl::as_writeable_bytes; # endif +//# if gsl_FEATURE( STRING_SPAN ) using ::gsl::basic_string_span; using ::gsl::string_span; using ::gsl::cstring_span; @@ -5387,6 +5515,7 @@ using ::gsl::cstring_span; using ::gsl::basic_zstring_span; using ::gsl::zstring_span; using ::gsl::czstring_span; +//# endif // gsl_FEATURE( STRING_SPAN ) using ::gsl::zstring; using ::gsl::czstring; @@ -5395,8 +5524,10 @@ using ::gsl::czstring; using ::gsl::wzstring; using ::gsl::cwzstring; +//# if gsl_FEATURE( STRING_SPAN ) using ::gsl::wzstring_span; using ::gsl::cwzstring_span; +//# endif // gsl_FEATURE( STRING_SPAN ) # endif // gsl_HAVE( WCHAR ) using ::gsl::ensure_z; @@ -5406,6 +5537,12 @@ using ::gsl::ensure_z; #endif // gsl_FEATURE( GSL_LITE_NAMESPACE ) gsl_RESTORE_MSVC_WARNINGS() +#if gsl_COMPILER_CLANG_VERSION || gsl_COMPILER_APPLECLANG_VERSION +# pragma clang diagnostic pop +#endif // gsl_COMPILER_CLANG_VERSION || gsl_COMPILER_APPLECLANG_VERSION +#if gsl_COMPILER_GNUC_VERSION +# pragma GCC diagnostic pop +#endif // gsl_COMPILER_GNUC_VERSION // #undef internal macros #undef gsl_STATIC_ASSERT_ diff --git a/deps/AMICI/ThirdParty/sundials/CMakeLists.txt b/deps/AMICI/ThirdParty/sundials/CMakeLists.txt index f535def9d..e23332cf3 100644 --- a/deps/AMICI/ThirdParty/sundials/CMakeLists.txt +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/sundials/cmake/tpl/FindKLU.cmake b/deps/AMICI/ThirdParty/sundials/cmake/tpl/FindKLU.cmake index 9e25883c4..98465aa18 100644 --- a/deps/AMICI/ThirdParty/sundials/cmake/tpl/FindKLU.cmake +++ b/deps/AMICI/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/deps/AMICI/ThirdParty/sundials/cmake/tpl/SundialsKLU.cmake b/deps/AMICI/ThirdParty/sundials/cmake/tpl/SundialsKLU.cmake index 8a9e111c6..fd7947cac 100644 --- a/deps/AMICI/ThirdParty/sundials/cmake/tpl/SundialsKLU.cmake +++ b/deps/AMICI/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 diff --git a/deps/AMICI/cmake/AmiciConfig.cmake b/deps/AMICI/cmake/AmiciConfig.cmake index 0ea3f77a4..d1e86c151 100644 --- a/deps/AMICI/cmake/AmiciConfig.cmake +++ b/deps/AMICI/cmake/AmiciConfig.cmake @@ -24,34 +24,34 @@ 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) - -if(NOT TARGET SUNDIALS::KLU) - add_library(SUNDIALS::KLU INTERFACE IMPORTED) - target_link_libraries( - SUNDIALS::KLU - INTERFACE "${KLU_STATIC}" - INTERFACE "${COLAMD_STATIC}" - INTERFACE "${BTF_STATIC}" - INTERFACE "${AMD_STATIC}" - INTERFACE "${SUITESPARSE_CONFIG_STATIC}") - set_target_properties(SUNDIALS::KLU PROPERTIES INTERFACE_INCLUDE_DIRECTORIES - "${KLU_INCLUDE_DIR}") -endif() - -find_package(SUNDIALS REQUIRED PATHS +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( + SUNDIALS::KLU + INTERFACE "${KLU_STATIC}" + INTERFACE "${COLAMD_STATIC}" + INTERFACE "${BTF_STATIC}" + INTERFACE "${AMD_STATIC}" + INTERFACE "${SUITESPARSE_CONFIG_STATIC}") +set_target_properties(SUNDIALS::KLU PROPERTIES INTERFACE_INCLUDE_DIRECTORIES + "${KLU_INCLUDE_DIR}") + +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/deps/AMICI/cmake/AmiciFindBLAS.cmake b/deps/AMICI/cmake/AmiciFindBLAS.cmake new file mode 100644 index 000000000..e0553b81c --- /dev/null +++ b/deps/AMICI/cmake/AmiciFindBLAS.cmake @@ -0,0 +1,128 @@ +# 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}) + set(BLAS + "MKL" + CACHE STRING "BLAS library to use" 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 "") + 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) + message(STATUS "Trying FindBLAS with BLA_VENDOR=${BLA_VENDOR}") + 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) + message(STATUS "Trying FindBLAS with BLA_VENDOR=${BLA_VENDOR}") + 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) + 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}) + 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() diff --git a/deps/AMICI/cmake/cmakelang-tools.cmake b/deps/AMICI/cmake/cmakelang-tools.cmake index ad489500b..5796938d2 100644 --- a/deps/AMICI/cmake/cmakelang-tools.cmake +++ b/deps/AMICI/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/deps/AMICI/documentation/ExampleJax.ipynb b/deps/AMICI/documentation/ExampleJax.ipynb deleted file mode 100644 index 1899305b6..000000000 --- a/deps/AMICI/documentation/ExampleJax.ipynb +++ /dev/null @@ -1,1114 +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", - "\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_import import import_petab_problem\n", - "\n", - "amici_model = import_petab_problem(petab_problem, force_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 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_objective 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` decorater 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/deps/AMICI/documentation/ExampleJax.ipynb b/deps/AMICI/documentation/ExampleJax.ipynb new file mode 120000 index 000000000..af2929d76 --- /dev/null +++ b/deps/AMICI/documentation/ExampleJax.ipynb @@ -0,0 +1 @@ +../python/examples/example_jax/ExampleJax.ipynb \ No newline at end of file diff --git a/deps/AMICI/documentation/GettingStarted.ipynb b/deps/AMICI/documentation/GettingStarted.ipynb index 91fb9cb12..1bacf00be 100644 --- a/deps/AMICI/documentation/GettingStarted.ipynb +++ b/deps/AMICI/documentation/GettingStarted.ipynb @@ -14,7 +14,7 @@ "metadata": {}, "source": [ "## Model Compilation\n", - "Before simulations can be run, the model must be imported and compiled. In this process, AMICI performs all symbolic manipulations that later enable scalable simulations and efficient sensitivity computation. The first step towards model compilation is the creation of an [SbmlImporter](https://amici.readthedocs.io/en/latest/generated/amici.sbml_import.SbmlImporter.html) instance, which requires an SBML Document that specifies the model using the [Systems Biology Markup Language (SBML)](http://sbml.org/Main_Page). \n", + "Before simulations can be run, the model must be imported and compiled. In this process, AMICI performs all symbolic manipulations that later enable scalable simulations and efficient sensitivity computation. The first step towards model compilation is the creation of an [SbmlImporter](https://amici.readthedocs.io/en/latest/generated/amici.sbml_import.SbmlImporter.html) instance, which requires an SBML Document that specifies the model using the [Systems Biology Markup Language (SBML)](https://sbml.org/). \n", "\n", "For the purpose of this tutorial, we will use `model_steadystate_scaled.xml`, which is contained in the same directory as this notebook." ] @@ -113,7 +113,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Model simulations can be executed using the [amici.runAmiciSimulations](https://amici.readthedocs.io/en/latest/generated/amici.html#amici.runAmiciSimulation) routine. By default the model does not not contain any timepoints for which the model is to be simulated. Here we define a simulation timecourse with two timepoints at `0` and `1` and then run the simulation." + "Model simulations can be executed using the [amici.runAmiciSimulations](https://amici.readthedocs.io/en/latest/generated/amici.html#amici.runAmiciSimulation) routine. By default, the model does not contain any timepoints for which the model is to be simulated. Here we define a simulation timecourse with two timepoints at `0` and `1` and then run the simulation." ] }, { diff --git a/deps/AMICI/documentation/README.md b/deps/AMICI/documentation/README.md index af9f33320..eece1de90 100644 --- a/deps/AMICI/documentation/README.md +++ b/deps/AMICI/documentation/README.md @@ -12,7 +12,7 @@ The legacy GitHub Pages URL https://amici-dev.github.io/AMICI/ is set up as a redirect to RTD. The main configuration file is `documentation/conf.py` and the documentation -is generated using `scripts/run-sphinx.sh`. The documentation is written to +is generated using `tox -e doc`. The documentation is written to `documentation/_build/`. The documentation comprises: @@ -50,12 +50,6 @@ Matlab documentation is processed by [mtoc++](https://www.morepas.org/software/mtocpp/docs/tools.html). This is configured in `matlab/mtoc/config`. -#### Python documentation - -Python documentation is processed by doxygen and doxypypy using the script and -filters in `scripts/`. - - ## Writing documentation ### Out-of-source documentation diff --git a/deps/AMICI/documentation/amici_refs.bib b/deps/AMICI/documentation/amici_refs.bib index 4c31869d8..7321d9719 100644 --- a/deps/AMICI/documentation/amici_refs.bib +++ b/deps/AMICI/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, @@ -1100,19 +1101,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 +1165,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}, @@ -1204,16 +1197,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}}, @@ -1251,6 +1234,130 @@ @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}, +} + +@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}, +} + +@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}, +} + +@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/deps/AMICI/documentation/background.rst b/deps/AMICI/documentation/background.rst index 2a2e74867..f47c077d4 100644 --- a/deps/AMICI/documentation/background.rst +++ b/deps/AMICI/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:: diff --git a/deps/AMICI/documentation/conf.py b/deps/AMICI/documentation/conf.py index ba88b25a8..3f867e0a3 100644 --- a/deps/AMICI/documentation/conf.py +++ b/deps/AMICI/documentation/conf.py @@ -1,27 +1,41 @@ -# -*- coding: utf-8 -*- # # Configuration file for the Sphinx documentation builder. # # This file does only contain a selection of the most common options. For a # full list see the documentation: # http://www.sphinx-doc.org/en/stable/config - import os import re import subprocess import sys - -# need to import before setting typing.TYPE_CHECKING=True, fails otherwise -import amici +from enum import EnumType import exhale.deploy -import exhale_multiproject_monkeypatch -import mock -import pandas as pd -import sympy as sp +from unittest import mock +import sphinx 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 @@ -90,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" ) @@ -98,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) @@ -254,6 +270,7 @@ def install_doxygen(): autodoc_default_options = { "special-members": "__init__", "inherited-members": True, + "undoc-members": True, } # sphinx-autodoc-typehints @@ -543,15 +560,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) @@ -567,7 +584,8 @@ def process_signature( return # only apply in the amici.amici module - if name.split(".")[1] != "amici": + split_name = name.split(".") + if len(split_name) < 2 or split_name[1] != "amici": return signature = fix_typehints(signature) @@ -605,7 +623,7 @@ def process_missing_ref(app, env, node, contnode): def skip_member(app, what, name, obj, skip, options): - ignored = [ + ignored_names = { "AbstractModel", "CVodeSolver", "IDASolver", @@ -614,7 +632,6 @@ def skip_member(app, what, name, obj, skip, options): "ConditionContext", "checkSigmaPositivity", "createGroup", - "createGroup", "equals", "printErrMsgIdAndTxt", "wrapErrHandlerFn", @@ -639,24 +656,45 @@ def skip_member(app, what, name, obj, skip, options): "stdVec2ndarray", "SwigPyIterator", "thisown", - ] + } - if name in ignored: + if name in ignored_names: return True if name.startswith("_") and name != "__init__": return True + obj_str = str(obj) + # ignore various functions for std::vector<> types - if re.match(r"^ python/sdist/amici/amici.py:docstring of amici.amici.FixedParameterContext.from_bytes:9: + # WARNING: Inline interpreted text or phrase reference start-string without end-string. + if ( + (qualname := getattr(obj, "__qualname__", "")) + and qualname == "int.to_bytes" + ) or ( + isinstance(getattr(obj, "__self__", None), EnumType) + and name == "from_bytes" + ): return True return None diff --git a/deps/AMICI/documentation/gfx/amici_workflow.png b/deps/AMICI/documentation/gfx/amici_workflow.png index 75f904ff7d328e47485e8ab5ece7bfbb673a9629..9eab38058049faf34e5719f2a65092837c37397a 100644 GIT binary patch literal 98637 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Ħ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 literal 100907 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 z zpG!smx1Z)aeTOZc2zZuakz4oLl$ls?5oKicj+zTj-N<&@*YRieYT(nuuua?Rl`=*r z?XIlb0I~7EPoI?UxzX+9r)`fN}-rMGE$7K9r zd5U9b=VM$!npj3RI(Db6w#QVz|E#-;(~^>!|9>={2RxVk`p0j3XGdmA_TDS2ZA8l6 zBNECC*+P+(6%w*HWp5(Nj_ggyPO{hkx_i$5yv})EPv_~;{kwnPagERQ{yYjd-#YMw zE>KoEHe~@I61G8SxBV_194*j4Q|Ck{ZfAb`VDoZ77CH7k+}|;G-Q66fRU57)9H=65 z)5yhQD<{fJeS4Sc_gji-aPkw=ee6gSUzyP^YQvHl?)PdID6m5%Bo;82%K}r9nwS#+ z13QFCJJ!kvjsLB@l0=S>If?M`i_zb9F=PU7T(GjcOS_a~@f3@U{==yWCW1uBw_bXo zo#&=r`776>ccn$3%gM5Iv^?|3&s1l802IuH>FMUNnvD4VXn8lb;z7#)e)`x) z9%V-caatBu#4QAq@ZNZoRKrKnWsW-(K9?{tnbQf>Fq`8TiZeP(D_i$UJ@Cc_z`;o} zyd`!-upkW5x1TWfoap5e2ZPmd$|UL&oAtg5@~~kg$?#(O1b8lHJYq=NjOj{HeU=VV7uByq9kM+);TCtEHr^)?=^8 z1X^+%-Ch&0#A&3YrL`?NVcwLszOGIsVhj)6odU!*_ZZ0({n^D*1w1-6jebu_Mi|S*;pG;fv3^LeD2t?z&{1=nbmrf&P;wfr2dKwhPM_5Dn*Pnm)>W6Vxwiy_aa>c$~>=~aZN z#`60dJAz_NMK!}W%B7MCpPdW3?S-5}qcDgUw%|PQyozUtjKWcUYO%`PedBD_{FkK=RXNc(+3p zFgX1Q#QKI(t{VkcJ@@VPl=uGkIy^-DhXu9MUC5%1d3O%o$K0Cs=Im7jJ>P@ zmbNbyd22iF2NvtyxYoEt|L+;Mqo_XLFVVmcy2=|B71at2JDm>lb8P>;!*WK3_N$Ma z?lYxTT-hhXm&Nk6M(k$C*$HoAue~0N+b6x$H^-b_!&d1Wu@>07G%ZdSriZAPIUB?B zsRM!J*q1MXcIcb^x4R3OZ`}J}H{)OTd5u^53sSf6>S7$0yuM-9ca@7H=S+@GR+Fqk z^lOB}z_nSFu3NUwmNn?Aq(nGYRBQ09|9-?s`qsg5+R#}cIg1&50vht>+Ccm{^GCJK z*a$*LE18VL~C1T+ZbuS4D-b1@mUQSHfb|%X$TbWZ^x>DN87ZtcNLVI0emi`iHQdO=f z3%iQhH4zDDLPJyid9$e$MTV1PG ztYhQjK~Aam&ILy7#XSu9$D6x>o)pQ}*}hEcRAg%`7#O`3{C7eVUP!y^RXs0em=o4a z6(6g$ydW4S)Z$cH$!bR?@Y$y__95GXqkFJYrcjx)9J+v@F{<}LT|`--?I%OwvAt)4 z@yD^hUpZ}YxS}m=Y`xpn`R^^aGVzfbWW;=wJRBZkWp^b_=fHig@a4zLHcQU03x8gX zF{$0T6TJ85kC5)uN&xLhgJ71w*v@Cr99x{{2-51%pba41!OgRM3IB8HAU0LjS<=OC zf@F9YeNMYi#x>hsk{cSb2#sL4Ik#+<;Xk5~I_gL*Ehbjs@vjoOa=o2K;p14*v|ZpU z`AzzOLzYtSD#^w6oZlKAVpCMaxYx)!KMBQsxiKqPPzV*hwRl1E#J_dX#X>~q+TZSH z{9Ji&JBf!OuT(T;ZKx1_tytc=Ild^o8Z&VwvE-dy2VeuPaB?#Gu@Zm>Pk0y|HIuH^ zu8uAtu)+Odn1ZkTDmz86#Hxv61k=^e7)V-rI`tqH;qJ`J%Hs;xcmHS6)IA^cOk+V655Jvr1FavC_|vH*>#Ktwpf3zIcV_udiPS|YKNPjwIm~{>`S$hA zJt@+A6sjRkHs0iEaUafXEZpNzk|>jD?=U~+^YH*{!Y3>%0)QnCf?aPrvp z;Z$ECga4#X*SOeN?kp9)Ac!yr>3B1gG#pC2*FZyYXLfcrc3=^Nztp&h0L;^WS~jS@Hednm+OH#RZwF`9hyj z2}T55UmjMLbx$hRTeN(f2*)et9v|h7H{orZU_Cf}U^Ei$gyXmHd}w365{HKW+3F)9 z2NN+T6CsD0kxZgK5t0)e%qsg?|JI>rk(%FUVVE`Mu%g(T<>_c+(HIeeK$AKDW2?P| zg~bwZy1jRvL1RWyCr=J$Q@hS!jq|0-dAZ%A1AlobU2^<}ZQ~Z!qC$abY_apmt ze`wz4RkRmkQCC-24{LG_Qg=oyY{B^U(0*57KmaWd23AO#x`V@(X7F{`WENId+jo;? zmhlmLh&(KmJ*aN5AijVK8C^I+)yGg^wC2UYc}L_t7|2Qxzn#Q<9eZ>gSN}!e(N~k^ zS4b-7c*B4Yo73?Jo5~HwnrAiuYA8Bo5;?qT{a8d*I*Rq+!PJG;5_YHxWEKp3qwjdM z__dKDCfO$4Ns8TS_5Hh2BsBQO&*cZP!-0&Yw32eKB%#@!_^$j+*?Il^#%yIf9yzrcT8LjKPvVSv-tfM~hjgy@0<;thqyFD9K zV-3V+v_rcn{CT_aXq#k(`7+}q2kzWaF0GF9Z*26bSEi{46QvIb0wCjhX;9wmA_%PH zSf4$3GdCo`_zcPUs(uk46LjuF8z6dc&MmwOxWfB(5m*ScOOjnoZ`*)c*jf(G1iA>Q zUDZkbTMD9-;qyw#uY5nx-0dN|FgMaanL|aMPE_3-}K5zZBDwakglZpS2T z<-gs@*q90-AgRGXj20kUtODq2)`z1K1~PqfqOob-AL1OMU7h#v&7W5((8yiDU(xNT zRI%}Q%2kqhV?ZoLul?g2m*r1m#$F=kfiHqi>Ui$=t{o1YXON_rYPRUtCSbE?z@Qr< zb*syd#RyyqP?YdFt;={i-5^F?1cZiX@j?$n`s3`N{cQ~Rkt7+n-!pds;6;p!YMPfA zS5c`x_MND&6e<#)`@h$L2k;2rKc^r94aTvMCXfMNLofP*_!2bpq3sNgilaYbtUi!=_5(2hYI8=2bQv8$0TFs?Sa8tTdfvc4A3l3j;AHYzDIt$q(_77wKQUP zY8-^AI$*!lE#7j-#x@p{Y5#jhfhM5OD{X8#tE43InUj++9v+@6oMe3;UfgA+a2UD| zMHRNYNjgtXq}v^0{ayefybfRiLmYFfaQEUOI*s1-q(~ERShm53M$?RdqYhnRafw7j z%^@Hf%FElMM6;|COYK{HrcnDq1`a*2R4W?zsaX}8Lj7bB?2PN|8El)i@wB-^^kbe{ z{-TiID&LFA`ImprcPB{-&j72BBvJg?)%)|0S1XtZG3vx+@ehNfWQRf|F3I8y9C@^? zEgk!6t7*N6R7-qtDAXO)$RAvhTxagYJnnz(<>zbNmck`GM3%yMH_g99D8-(WcD)I2 zqG^81w|yQCOM(8;3q2`vXJ7RQ0Ua8PffuMqE{KMKopHR|AAd;G%6WU@Bk~Fg7|e(X z0CX-4j_Wxh@@4^I zKQc_1?%V*59I^Wk8z3h_qJ1h6&TThvf$&7}{l%u8tzxTKJ<5r%>I!CYgnwd*bBHYk zO7Sfe>FScO`&K*h^9yG4)niw?);3C)U43L+t^{1mQN?=#vGpIog(jvw<{dKT zPEWY(a;XouS0qY$_g=X3P^~!Il!>bCd^O~0IIzrS;Jh~LPZ~2E4 zGUcOKi7yCUx7NKn+Zs%%xk%OkJgVxUMidVYeYg+mUttP8=jN{8H;YRkzL}E-Ha4A z+6odr@_2wd0ToA-@ApZj&9>+}7H<+8kE0C&fyc|1x(e37gqISO!5EQB@FTP`P^#ScN61SZ*IX z>F0}gd=crQpMCn)#Q#P~Hnt_nA3t99Ph3YI$ozKucBJddj)imwy~UQEQ>v~u`JH3o z$uF$K2j6X@Gh>))!+0b@J-q$tWxk9K9(7w)?XbQwD)V-g41WQT_+?P~ZJ_m`O+eaK zfW})Yiq+4Qb`C}=?;zCNVmikl+_VNiry`-9=1=c$v(7OvFpR*seLiRvU8o2HIWApN zqz9B}XjoY6V-dQ33YC&IN?KYSU0RJy4MQL`oX13BVq@=}J9lm^hVy>R85nL9p(9a_ zyKB1EAPtrk5s8734r_Z7vVSot-OIF`NG(C_nXtm(T%<46`HR)Kbkph!FM7`cxQRSn zWhfsU22uN;-{y>${JQjp`wpu`9JiHixM-`@`SJ9e7ZkfU%U2e9$rQ_OrjLSGgf&_b z8>*ufzSVH%q1AiK?i$iGZVsKkQiXh)G27u$hN?IB+9o=_i83p9D+NJ+mh8R;=6r9`M<5sfd;XHc z-;gqkz5CE7UN$>Cc1QQ|{^@nL_|c&0kNp>P_fOr<*NOGfUwV;W#PLSo(qwAdL)2qw z^hYYL_(af!ur`y#tcPs(-{*Uq((Y2Np1)Wu9&chsZlKElLVuKw<9ob(7kl%h0#ZLN z?VI8>d4H^on8>onWS?y@q^b22iC)ns-mzM7j#gVOZA&&84{N+vyQIEQXy6(TK0fj5 z?8u`~0iVw{clTyA?HtU8oB|sc_nBP!Y zz(G>7ZZTwzL&+l%Fta2qIm5beEhigW1Xp;>i*J78^Ls1IZl33P{sg4uA;f681pIez zg1?7&Jxq19^jEIPlOdEI?lO+=#wF=S+@kv;KF9?{YlD@(@d&k%8 zfIru-6jti}>dJk`j)2`?Ha0*ikbT*DT^!;8iEzKE;98T)4P=d}f&O+}z{fLjx^sa91UAuh5Dx#Xm-_Y=;^|O zJmFNkE^mUu~a zZ2t#?Rwc}MDsq8rP3EMDAvz{y()-)f74gcHb7G}my`x`@lLeU)>coB7)`D;vATN5w zWo06@lgp%zlrLVUZ7f1MRkHl(X19s|j#vWM{#xJFK0)eC#*IarQ5x}NkzGTt$dsu2 zLu^=c5$0e(*38q1*)oB*9b0WL?~3?hA?JO~CHI~B$#rDSBzYnX&SiYpsqV)*DM|KU zu&Rq7O@jS;Q^Xtmd@wNm5M4aVCsTEt*g5f!F%WeE?CU)32pZ5>< zWppCb`0E-cq*uVj<^q{^gcnJ>05STDlB5CVmp9W{zAd4 zwsnX`r(7AYNuP!$6r4+>K9obgvHB97ysU#vyj+P$%ZQ2o9B7@ z@?pjOtqYfjju-B$7uobt*r?G}8yG>f^K&VYs9)!7X@9dxTW! zh0JMb>-01gm5wf+F)4{*^PdDt*^ABx>y|g4M~^|3+Z;GymU0}F=dWRCt6xw-&Ldq~ za;&}sk!sCOqn!H;8yK2};c!-%7?iRV90~%`qq2jr9p-!$eM1J%14WZ)4|Hs?SjgY? zadCr-WEZOqn6|)uEDjSveMA_Mrtjb1garrZ{=s{Do3+4zFXB~j@SB%GK|+A!UIdZx zq$e=+>H&Y;6-P}6v%~0sL=4O>IVVWFJ`qIZL3&!FJGKlvlHE6i`Vv?H2OZ4=UH^X>pIftj)jXZ?Iv12!t1gSKYv8{t5?4q=UNGLPxOa3!0B=hEuWye<~Iv3 z$@~EUqzw3>c{i{$xB-oj!sa93dU0L4bg4#!tz?S>=5Xx9fdG_>8g*<#8g-&Gtk2Ni z3>EqnUIsZx5YB*9C_Lj3{ynlm1&lb{UC0)(GPo(27Fq2p*AihUq5B;Vz_XvN0fH>rH6qg~Rdy%N`Nj)=rtt#d z^B`$p`&Vy&xT3#c+VVI2BK43gx9Mo|cxwNkVV)m?1V;|6y{_f`dhfA~>b0by;;Uk4+i<#J%v4vUN=+bU(#!L=Bw2{;I~sRARxVBCj+2is z%h14JO#>@K43zwYXy)J+^~a*;ux_2DoR|(2aEkJ$-2D`FGy| z*dYZ7hdbcPr3x_AAlTofCUdR93sg5mxyFfEuwh#94;*xN++>*OWIe0$V6aH4)9K&l zfQqIjOe#Aj%ea5)2UB!tyfPX;U(VF%w3-1BTK~_V7UT%v*{!U);_vC5NGN4SNddm+ z5)d4It*_f9iP^z+x)^tm}8GNX!JIefzh9+F+O`B3Rc6G zS%L|$)x*6_<4l2d;Jo8N#f}o}jZ`nt({rByi1;U_pYJ<4AMZEm>6hQZT_yo=a}h4C zJjc}F@bI_+kuX?vS&-=0xtDgLF9n8+u7e{?yb}=7_)h?$HVMP|s!%}SDX-Ip*O6Dp=LYM% z6AOeRPU4wI&0`UbI!rMI%Bm6OVmyqzSvcdeUgS(icus7k~yL@Zxy&M|@ma6W| zG`xC0>EXC&giWOC8#1#3cyI1a+OGG~l_Q5qz|^pO3nKI!;AF3hz_xQtP-#OS<1RX- zl8}kp1E^;MTvDcjiqI=i>8pcde9{!C3O5FOvjNvU2|#W8GiZ$G{iy`~TucFJRlpLKiu9^A>QEoZ;Q_!Tsf^Q|IPzPX5cE5oj1{>Q#(3`ZQjy~K-Pfmrl_YZ)x2PM}uW!8@7+D{Vdnc+-c#U5|rl0ks~m9eii^3I<5SH?ZsAf=T~bQ< zdr|r9;a^Q_!{z{zf#=tL%%~&C{(j&_iL*1ieGL9g`{>DR^<5+RH>}4JwQ?#U_>YcX zTPu8|G#mG6_QWJ3Lb40I%`j#}kRD z6u>}c0QA?9&Or#>d;*w;r$XsZ5XjwMf>XQP5V%V%8tnQX0bxD`L@ZAjY@r(ikbk(a zWf#8N4~BiHST`6IXWJ66Ju268T<*^V4Cdcu2}rp6Q2f7swhDL_XbGWp4T$K@d7u-X z`960)=4ln^ya8jPYQROG!lVC$D1j73Pb>k7Ok}10XyyfJ9T*4nokLKR3lq>q+vQI8 zuYs0=4BHc!2^c8T4Uksh0x;HP4>+eOm>-LR4)}xf9K9VKbM5Wz%BTDDq)V_s>)QUz zfy+@Hge7+y&rWNF5x8OTl9IvY_3n_p;RbHs0W4|$cF_uYs(`uc1$Cg+ z*ks)V-}q|qeD{S%X!&^P1Wq)60d>Q;KcFTj3Du%fFD0drUyy7FDYL;}-?9Q<**dm% z0wsTRqL(FaV=)X`S%Co@25NzG1s@Z9>VI8@clB)O2#DwgSz7tIFgY)RmwHLZ%>4;W zOZJpnb`t{aoD#(ppQ0R`S{V4B`t-09Q!QDB6DOyNBMct86+Wlz>Av~dSPu$$&6)-( zBW7nu$1mvbO~GL!4NM*$XB zV|`E$HMwy3xk)na8XwV*yy8L&_&dsJRIdY6FZ^_vP?X)D!2-()s_(E+Uhs~iVBv@?C6oX9hJCEX` zqBIt+!qhJ%`cZ&O&0YyuzUIP1LFl+Gz?}HH^Y)^u;Ej8aAbgzyzF46rBjBdfQ2MND zvP|^}_gu+*WD%(6p~*FeeQgmR`RL>f^m?T?`#PG5kW^K6Ql zRJ;H640M<;PPAiKd_D{7eR^&cKfU${X~zgP_Ni<3dP0D$Afpab6Ud{%1S;B~!KW}F zCkvoIuHW0+Hh;_T5-+W&_~hGUG6wsK$bS81QABh8n%PI)!<=32tYA%BA=U{Z$hhCW zUKW6-X!Wcy3k=5h`O4AXRQ&_ju(pD0oOQg&*27z0*>(O6Kr}Q)`n?|U0lz}S3*xm! z_{L=sU+8SWr;mjJT`1CMN`P>qJ&ayk%IEZ0(baX2jpT_4bpRP^jL_QB(t^&;E(s7{ zgV&xtg)TT5mz4)fc1LI>_d9TA=v;Rn1K4wOf1<%l|2XCBcnK7a?9)Kq_d$FC)V&A} zX)3#Lr3CaO2(!DQ?y)EtB$Q4x&)nJhuU%cut#zPE<^eUc03HnC;*$dBJsjAKXj;I{ zz{seIK0d%}Qv0Ojb(rB+@dldG9{iAeX!7y#Ev`@24!fhF0fJE7_yEwxw-Ade(_FYP zdQjRc+nL5eSP!-YvE>wSZ{CxF@4O#It!r(^%5rapoB{Zsxfl@fCve-v;;IX_d;a

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) @@ -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: @@ -79,6 +79,13 @@ Install the AMICI dependencies via ``pacman`` sudo pacman -S python swig openblas gcc hdf5 boost-libs +Export the bash variables ``BLAS_CFLAGS`` and ``BLAS_LIBS`` to point to where BLAS was installed, e.g.: + +.. code-block:: bash + + export BLAS_CFLAGS="-I/usr/include/openblas/" + export BLAS_LIBS="-lopenblas" + Install AMICI: .. code-block:: bash @@ -99,7 +106,14 @@ Alternatively: sudo pacman -Su python swig openblas gcc hdf5 boost-libs -3. Install AMICI: +3. Export the bash variables ``BLAS_CFLAGS`` and ``BLAS_LIBS`` to point to where BLAS was installed, e.g.: + +.. code-block:: bash + + export BLAS_CFLAGS="-I/usr/include/openblas/" + export BLAS_LIBS="-lopenblas" + +4. Install AMICI: .. code-block:: bash @@ -189,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: @@ -217,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. @@ -226,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, @@ -347,16 +360,19 @@ environment variables: | | Default: ``ON`` | | +----------------------------+----------------------------------+---------------------------------+ -Installation under Anaconda ---------------------------- +Installation under conda +------------------------ -To use an Anaconda installation of Python -`https://www.anaconda.com/distribution/ `_, -Python>=3.7), proceed as follows: +There is no amici conda recipe available yet. However, you can install AMICI +using pip in a conda environment. -Since Anaconda provides own versions of some packages which might not -work with AMICI (in particular the ``gcc`` compiler), create a minimal -virtual environment via: +.. note:: + + It is possible, but we currently don't recommend using conda for installing + AMICI, as it commonly leads to conflicts with system installations of + libraries and compilers. + +Create a minimal conda environment via: .. code-block:: bash @@ -402,7 +418,7 @@ Now, you are ready to use AMICI in the virtual environment. .. note:: - **Anaconda on Mac** + **conda on Mac** If the above installation does not work for you, try installing AMICI via: @@ -424,6 +440,9 @@ Now, you are ready to use AMICI in the virtual environment. (For further discussion see https://github.com/AMICI-dev/AMICI/issues/357) +Known issues: + +* ``CMAKE_AR-NOTFOUND: not found``: Try ``conda install binutils``. Optional Boost support ---------------------- diff --git a/deps/AMICI/documentation/python_interface.rst b/deps/AMICI/documentation/python_interface.rst index a919e925c..2926abf96 100644 --- a/deps/AMICI/documentation/python_interface.rst +++ b/deps/AMICI/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/deps/AMICI/documentation/python_modules.rst b/deps/AMICI/documentation/python_modules.rst index 5481865a7..2607447f0 100644 --- a/deps/AMICI/documentation/python_modules.rst +++ b/deps/AMICI/documentation/python_modules.rst @@ -11,6 +11,15 @@ AMICI Python API amici.sbml_import amici.pysb_import amici.bngl_import + amici.petab + amici.petab.conditions + amici.petab.import_helpers + amici.petab.parameter_mapping + amici.petab.petab_import + amici.petab.pysb_import + amici.petab.sbml_import + amici.petab.simulations + amici.petab.simulator amici.petab_import amici.petab_import_pysb amici.petab_objective @@ -18,6 +27,7 @@ AMICI Python API amici.import_utils amici.de_export amici.de_model + amici.de_model_components amici.plotting amici.pandas amici.logging diff --git a/deps/AMICI/documentation/recreate_reference_list.py b/deps/AMICI/documentation/recreate_reference_list.py index 034c884c4..a188f1daa 100755 --- a/deps/AMICI/documentation/recreate_reference_list.py +++ b/deps/AMICI/documentation/recreate_reference_list.py @@ -20,7 +20,7 @@ def get_keys_by_year(bibfile): """Get bibtex entry keys as dict by year""" - with open(bibfile, "r") as f: + with open(bibfile) as f: db = biblib.bib.Parser().parse(f, log_fp=sys.stderr).get_entries() recoverer = biblib.messages.InputErrorRecoverer() by_year = {} @@ -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/deps/AMICI/documentation/references.md b/deps/AMICI/documentation/references.md index 00c3f40cc..cd103843f 100644 --- a/deps/AMICI/documentation/references.md +++ b/deps/AMICI/documentation/references.md @@ -1,8 +1,9 @@ # References -List of publications using AMICI. Total number is 82. +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). +

2024

+
+
+Baltussen, Mathieu G., Thijs J. de Jong, Quentin Duez, William E. +Robinson, and Wilhelm T. S. Huck. 2024. “Chemical Reservoir +Computation in a Self-Organizing Reaction Network.” +Nature, June. https://doi.org/10.1038/s41586-024-07567-x. +
+
+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. +
+
+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 +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. +
+
+Philipps, Maren, Antonia Körner, Jakob Vanhoefer, Dilan Pathirana, and +Jan Hasenauer. 2024. “Non-Negative Universal Differential +Equations with Applications in Systems Biology.” https://arxiv.org/abs/2406.14246. +
+
+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 +95,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 +110,21 @@ 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 +132,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 +164,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 +173,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 +181,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 +203,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 +225,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 +233,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 +243,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 +260,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 +275,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 +290,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 +332,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 +342,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 +359,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 +367,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 +388,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 +412,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 +430,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 +446,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 +468,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 +540,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 +548,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 +599,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 +667,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 +675,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 +686,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. diff --git a/deps/AMICI/documentation/rtd_requirements.txt b/deps/AMICI/documentation/rtd_requirements.txt index 64bc03e51..06cf50666 100644 --- a/deps/AMICI/documentation/rtd_requirements.txt +++ b/deps/AMICI/documentation/rtd_requirements.txt @@ -1,7 +1,7 @@ # NOTE: relative paths are expected to be relative to the repository root -sphinx<7 +sphinx mock>=5.0.2 -setuptools==67.7.2 +setuptools>=67.7.2 pysb>=1.11.0 matplotlib==3.7.1 nbsphinx==0.9.1 @@ -9,17 +9,18 @@ nbformat==5.8.0 recommonmark>=0.7.1 sphinx_rtd_theme>=1.2.0 petab[vis]>=0.2.0 -sphinx-autodoc-typehints==1.23.0 +sphinx-autodoc-typehints git+https://github.com/readthedocs/sphinx-hoverxref@main -ipython==8.13.2 -breathe==4.35.0 -#exhale>=0.3.5 +ipython>=8.13.2 +breathe>=4.35.0 +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.19.0 +sphinxcontrib-matlabdomain>=0.20.0 sphinxcontrib-napoleon>=0.7 -pygments==2.15.1 -Jinja2==3.1.2 +pygments>=2.15.1 +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 -e python/sdist/ +numpy<2.0 diff --git a/deps/AMICI/documentation/rtd_requirements2.txt b/deps/AMICI/documentation/rtd_requirements2.txt deleted file mode 100644 index 5a39f8e68..000000000 --- a/deps/AMICI/documentation/rtd_requirements2.txt +++ /dev/null @@ -1 +0,0 @@ -exhale>=0.3.6 diff --git a/deps/AMICI/documentation/versioning_policy.rst b/deps/AMICI/documentation/versioning_policy.rst new file mode 100644 index 000000000..bed419f02 --- /dev/null +++ b/deps/AMICI/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 `_. diff --git a/deps/AMICI/include/amici/abstract_model.h b/deps/AMICI/include/amici/abstract_model.h index bb824577b..f340214e7 100644 --- a/deps/AMICI/include/amici/abstract_model.h +++ b/deps/AMICI/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 ); @@ -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, 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); + 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, 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 + 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, const realtype 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, 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 + 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/deps/AMICI/include/amici/backwardproblem.h b/deps/AMICI/include/amici/backwardproblem.h index 1c26186c1..a5d32e3c4 100644 --- a/deps/AMICI/include/amici/backwardproblem.h +++ b/deps/AMICI/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/deps/AMICI/include/amici/defines.h b/deps/AMICI/include/amici/defines.h index 068b8a9d5..6ccba8992 100644 --- a/deps/AMICI/include/amici/defines.h +++ b/deps/AMICI/include/amici/defines.h @@ -6,7 +6,6 @@ #endif #include -#include /* Math constants in case _USE_MATH_DEFINES is not supported */ #if defined(_USE_MATH_DEFINES) @@ -69,8 +68,10 @@ 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_CONSTR_FAIL= -15; constexpr int AMICI_ILL_INPUT= -22; constexpr int AMICI_ERROR= -99; constexpr int AMICI_NO_STEADY_STATE= -81; @@ -255,6 +256,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/deps/AMICI/include/amici/edata.h b/deps/AMICI/include/amici/edata.h index f8639ca2e..5d613c626 100644 --- a/deps/AMICI/include/amici/edata.h +++ b/deps/AMICI/include/amici/edata.h @@ -14,24 +14,24 @@ class ReturnData; /** * @brief ExpData carries all information about experimental or - * condition-specific data + * condition-specific data. */ class ExpData : public SimulationParameters { public: /** - * @brief default constructor + * @brief Default constructor. */ ExpData() = default; /** - * @brief Copy constructor, needs to be declared to be generated in - * swig + * @brief Copy constructor. */ + // needs to be declared to be wrapped by SWIG ExpData(ExpData const&) = default; /** - * @brief constructor that only initializes dimensions + * @brief Constructor that only initializes dimensions. * * @param nytrue Number of observables * @param nztrue Number of event outputs @@ -154,6 +154,16 @@ class ExpData : public SimulationParameters { /** * @brief Set output timepoints. * + * If the number of timepoint increases, this will grow the + * observation/sigma matrices and fill new entries with NaN. + * If the number of timepoints decreases, this will shrink the + * observation/sigma matrices. + * + * Note that the mapping from timepoints to measurements will not be + * preserved. E.g., say there are measurements at t = 2, and this + * function is called with [1, 2], then the old measurements will belong to + * t = 1. + * * @param ts timepoints */ void setTimepoints(std::vector const& ts); @@ -225,7 +235,7 @@ class ExpData : public SimulationParameters { void setObservedDataStdDev(std::vector const& observedDataStdDev); /** - * @brief Set indentical standard deviation for all measurements. + * @brief Set identical standard deviation for all measurements. * * @param stdDev standard deviation (dimension: scalar) */ @@ -278,8 +288,7 @@ class ExpData : public SimulationParameters { realtype const* getObservedDataStdDevPtr(int it) const; /** - * @brief set function that copies observed event data from input to - * ExpData::observedEvents + * @brief Set observed event data. * * @param observedEvents observed data (dimension: nmaxevent x nztrue, * row-major) @@ -287,8 +296,7 @@ class ExpData : public SimulationParameters { void setObservedEvents(std::vector const& observedEvents); /** - * @brief set function that copies observed event data for specific event - * observable + * @brief Set observed event data for specific event observable. * * @param observedEvents observed data (dimension: nmaxevent) * @param iz observed event data index @@ -296,8 +304,7 @@ class ExpData : public SimulationParameters { void setObservedEvents(std::vector const& observedEvents, int iz); /** - * @brief get function that checks whether event data at specified indices - * has been set + * @brief Check whether event data at specified indices has been set. * * @param ie event index * @param iz event observable index @@ -306,25 +313,23 @@ class ExpData : public SimulationParameters { bool isSetObservedEvents(int ie, int iz) const; /** - * @brief get function that copies data from ExpData::mz to output + * @brief Get observed event data. * * @return observed event data */ std::vector const& getObservedEvents() const; /** - * @brief get function that returns a pointer to observed data at ieth - * occurrence + * @brief Get pointer to observed data at ie-th occurrence. * * @param ie event occurrence * - * @return pointer to observed event data at ieth occurrence + * @return pointer to observed event data at ie-th occurrence */ realtype const* getObservedEventsPtr(int ie) const; /** - * @brief set function that copies data from input to - * ExpData::observedEventsStdDev + * @brief Set standard deviation of observed event data. * * @param observedEventsStdDev standard deviation of observed event data */ @@ -332,16 +337,14 @@ class ExpData : public SimulationParameters { setObservedEventsStdDev(std::vector const& observedEventsStdDev); /** - * @brief set function that sets all ExpData::observedDataStdDev to the - * input value + * @brief Set standard deviation of observed event data. * * @param stdDev standard deviation (dimension: scalar) */ void setObservedEventsStdDev(realtype stdDev); /** - * @brief set function that copies standard deviation of observed data for - * specific observable + * @brief Set standard deviation of observed data for a specific observable. * * @param observedEventsStdDev standard deviation of observed data * (dimension: nmaxevent) @@ -352,8 +355,7 @@ class ExpData : public SimulationParameters { ); /** - * @brief set function that sets all standard deviation of a specific - * observable to the input value + * @brief Set all standard deviations of a specific event-observable. * * @param stdDev standard deviation (dimension: scalar) * @param iz observed data index @@ -361,8 +363,8 @@ class ExpData : public SimulationParameters { void setObservedEventsStdDev(realtype stdDev, int iz); /** - * @brief get function that checks whether standard deviation of even data - * at specified indices has been set + * @brief Check whether standard deviation of event data + * at specified indices has been set. * * @param ie event index * @param iz event observable index @@ -371,16 +373,15 @@ class ExpData : public SimulationParameters { bool isSetObservedEventsStdDev(int ie, int iz) const; /** - * @brief get function that copies data from ExpData::observedEventsStdDev - * to output + * @brief Get standard deviation of observed event data. * * @return standard deviation of observed event data */ std::vector const& getObservedEventsStdDev() const; /** - * @brief get function that returns a pointer to standard deviation of - * observed event data at ie-th occurrence + * @brief Get pointer to standard deviation of + * observed event data at ie-th occurrence. * * @param ie event occurrence * @@ -389,6 +390,13 @@ class ExpData : public SimulationParameters { */ realtype const* getObservedEventsStdDevPtr(int ie) const; + /** + * @brief Set all observations and their standard deviations to NaN. + * + * Useful, e.g., after calling ExpData::setTimepoints. + */ + void clear_observations(); + /** * @brief Arbitrary (not necessarily unique) identifier. */ diff --git a/deps/AMICI/include/amici/exception.h b/deps/AMICI/include/amici/exception.h index 7ee91d511..431be48cb 100644 --- a/deps/AMICI/include/amici/exception.h +++ b/deps/AMICI/include/amici/exception.h @@ -62,29 +62,35 @@ class AmiException : public std::exception { }; /** - * @brief cvode exception handler class + * @brief CVODE exception handler class */ class CvodeException : public AmiException { public: /** * @brief Constructor - * @param error_code error code returned by cvode function - * @param function cvode function name + * @param error_code error code returned by CVODE function + * @param function CVODE function name + * @param extra Extra text to append to error message */ - CvodeException(int error_code, char const* function); + CvodeException( + int error_code, char const* function, char const* extra = nullptr + ); }; /** - * @brief ida exception handler class + * @brief IDA exception handler class */ class IDAException : public AmiException { public: /** * @brief Constructor - * @param error_code error code returned by ida function - * @param function ida function name + * @param error_code error code returned by IDA function + * @param function IDA function name + * @param extra Extra text to append to error message */ - IDAException(int error_code, char const* function); + IDAException( + int error_code, char const* function, char const* extra = nullptr + ); }; /** diff --git a/deps/AMICI/include/amici/forwardproblem.h b/deps/AMICI/include/amici/forwardproblem.h index dfe3bd8f2..67658ebee 100644 --- a/deps/AMICI/include/amici/forwardproblem.h +++ b/deps/AMICI/include/amici/forwardproblem.h @@ -7,7 +7,6 @@ #include "amici/vector.h" #include -#include #include #include @@ -197,7 +196,9 @@ class ForwardProblem { SimulationState const& getSimulationStateTimepoint(int it) const { if (model->getTimepoint(it) == initial_state_.t) return getInitialSimulationState(); - return timepoint_states_.find(model->getTimepoint(it))->second; + auto map_iter = timepoint_states_.find(model->getTimepoint(it)); + assert(map_iter != timepoint_states_.end()); + return map_iter->second; }; /** @@ -250,6 +251,21 @@ class ForwardProblem { void handleEvent(realtype* tlastroot, bool seflag, bool initial_event); + /** + * @brief Store pre-event model state + * + * @param seflag Secondary event flag + * @param initial_event initial event flag + */ + void store_pre_event_state(bool seflag, bool initial_event); + + /** + * @brief Check for, and if applicable, handle any secondary events + * + * @param tlastroot pointer to the timepoint of the last event + */ + void handle_secondary_event(realtype* tlastroot); + /** * @brief Extract output information for events */ @@ -258,9 +274,9 @@ class ForwardProblem { /** * @brief Execute everything necessary for the handling of data points * - * @param it index of data point + * @param t measurement timepoint */ - void handleDataPoint(int it); + void handleDataPoint(realtype t); /** * @brief Applies the event bolus to the current state @@ -300,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, @@ -353,7 +369,8 @@ class ForwardProblem { * @brief Array of flags indicating which root has been found. * * Array of length nr (ne) with the indices of the user functions gi found - * to have a root. For i = 0, . . . ,nr 1 if gi has a root, and = 0 if not. + * to have a root. For i = 0, . . . ,nr 1 or -1 if gi has a root, and = 0 + * if not. See CVodeGetRootInfo for details. */ std::vector roots_found_; diff --git a/deps/AMICI/include/amici/logging.h b/deps/AMICI/include/amici/logging.h index 0118bedd2..6af039d4c 100644 --- a/deps/AMICI/include/amici/logging.h +++ b/deps/AMICI/include/amici/logging.h @@ -83,7 +83,7 @@ struct LogItem { , message(message){}; /** Severity level */ - LogSeverity severity; + LogSeverity severity = LogSeverity::error; /** Short identifier for the logged event */ std::string identifier; diff --git a/deps/AMICI/include/amici/misc.h b/deps/AMICI/include/amici/misc.h index c18606e3e..6874c1918 100644 --- a/deps/AMICI/include/amici/misc.h +++ b/deps/AMICI/include/amici/misc.h @@ -9,7 +9,6 @@ #include #include #include -#include #include #include @@ -86,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()); }; @@ -98,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(), @@ -291,7 +290,11 @@ class CpuTimer { return d_milliseconds(clock::now() - start_).count(); } - static const bool uses_thread_clock = true; + /** + * @brief Whether the timer uses a thread clock (i.e. provides proper, + * thread-specific CPU time). + */ + static bool const uses_thread_clock = true; private: /** Start time */ @@ -330,7 +333,11 @@ class CpuTimer { / CLOCKS_PER_SEC; } - static const bool uses_thread_clock = false; + /** + * @brief Whether the timer uses a thread clock (i.e. provides proper, + * thread-specific CPU time). + */ + static bool const uses_thread_clock = false; private: /** Start time */ diff --git a/deps/AMICI/include/amici/model.h b/deps/AMICI/include/amici/model.h index 72f733e6c..1c020400a 100644 --- a/deps/AMICI/include/amici/model.h +++ b/deps/AMICI/include/amici/model.h @@ -12,7 +12,6 @@ #include "amici/vector.h" #include -#include #include namespace amici { @@ -93,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. @@ -117,6 +116,8 @@ class Model : public AbstractModel, public ModelDimensions { * @param ndxdotdp_explicit Number of nonzero elements in `dxdotdp_explicit` * @param ndxdotdx_explicit Number of nonzero elements in `dxdotdx_explicit` * @param w_recursion_depth Recursion depth of fw + * @param state_independent_events Map of events with state-independent + * triggers functions, mapping trigger timepoints to event indices. */ Model( ModelDimensions const& model_dimensions, @@ -124,7 +125,8 @@ class Model : public AbstractModel, public ModelDimensions { amici::SecondOrderMode o2mode, std::vector idlist, std::vector z2event, bool pythonGenerated = false, int ndxdotdp_explicit = 0, int ndxdotdx_explicit = 0, - int w_recursion_depth = 0 + int w_recursion_depth = 0, + std::map> state_independent_events = {} ); /** Destructor. */ @@ -237,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 @@ -702,6 +716,12 @@ class Model : public AbstractModel, public ModelDimensions { /** * @brief Set simulation start time. + * + * Output timepoints are absolute timepoints, independent of + * \f$ t_{0} \f$. + * For output timepoints \f$ t < t_{0} \f$, the initial state will be + * returned. + * @param t0 Simulation start time */ void setT0(double t0); @@ -910,7 +930,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. @@ -919,7 +939,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 @@ -939,7 +959,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 ); @@ -1037,7 +1057,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 ); /** @@ -1052,7 +1072,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 ); @@ -1074,7 +1094,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 ); @@ -1091,7 +1111,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 ); /** @@ -1105,7 +1125,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 ); /** @@ -1123,7 +1143,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 ); /** @@ -1136,7 +1156,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 ); @@ -1150,7 +1170,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 ); @@ -1172,7 +1192,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 ); @@ -1193,7 +1213,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 ); @@ -1211,7 +1231,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 ); /** @@ -1226,7 +1246,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 ); @@ -1239,7 +1259,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 ); @@ -1255,7 +1275,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 ); @@ -1270,7 +1290,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 ); @@ -1285,7 +1305,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 ); @@ -1314,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. @@ -1327,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; /** @@ -1435,7 +1458,7 @@ class Model : public AbstractModel, public ModelDimensions { std::vector const& getReinitializationStateIdxs() const; /** Flag indicating Matlab- or Python-based model generation */ - bool pythonGenerated; + bool pythonGenerated = false; /** * @brief getter for dxdotdp (matlab generated) @@ -1449,6 +1472,49 @@ class Model : public AbstractModel, public ModelDimensions { */ SUNMatrixWrapper const& get_dxdotdp_full() const; + /** + * @brief Get trigger times for events that don't require root-finding. + * + * @return List of unique trigger points for events that don't require + * root-finding (i.e. that trigger at predetermined timepoints), + * in ascending order. + */ + 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` @@ -1462,6 +1528,12 @@ class Model : public AbstractModel, public ModelDimensions { /** Logger */ Logger* logger = nullptr; + /** + * @brief Map of trigger timepoints to event indices for events that don't + * require root-finding. + */ + std::map> state_independent_events_ = {}; + protected: /** * @brief Write part of a slice to a buffer according to indices specified @@ -1670,7 +1742,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 ); /** @@ -1704,7 +1776,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 ); @@ -1718,7 +1790,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 ); @@ -1771,7 +1843,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 ); @@ -1785,7 +1857,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 ); @@ -1805,29 +1877,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. @@ -1993,17 +2081,23 @@ 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 * checked for finiteness */ +#ifdef NDEBUG bool always_check_finite_{false}; +#else + bool always_check_finite_{true}; +#endif /** indicates whether sigma residuals are to be added for every datapoint */ bool sigma_res_{false}; @@ -2027,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/deps/AMICI/include/amici/model_dae.h b/deps/AMICI/include/amici/model_dae.h index dd16e7466..03ffbf8fb 100644 --- a/deps/AMICI/include/amici/model_dae.h +++ b/deps/AMICI/include/amici/model_dae.h @@ -10,7 +10,6 @@ #include #include -#include #include namespace amici { @@ -41,20 +40,23 @@ class Model_DAE : public Model { * @param ndxdotdp_explicit number of nonzero elements dxdotdp_explicit * @param ndxdotdx_explicit number of nonzero elements dxdotdx_explicit * @param w_recursion_depth Recursion depth of fw + * @param state_independent_events Map of events with state-independent + * triggers functions, mapping trigger timepoints to event indices. */ 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 + int const w_recursion_depth = 0, + std::map> state_independent_events = {} ) : Model( - model_dimensions, simulation_parameters, o2mode, idlist, z2event, - pythonGenerated, ndxdotdp_explicit, ndxdotdx_explicit, - w_recursion_depth - ) { + model_dimensions, simulation_parameters, o2mode, idlist, z2event, + pythonGenerated, ndxdotdp_explicit, ndxdotdx_explicit, + w_recursion_depth, state_independent_events + ) { derived_state_.M_ = SUNMatrixWrapper(nx_solver, nx_solver); auto M_nnz = static_cast( std::reduce(idlist.begin(), idlist.end()) @@ -83,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; @@ -120,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; @@ -251,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; @@ -296,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; @@ -306,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()); }; @@ -524,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/deps/AMICI/include/amici/model_dimensions.h b/deps/AMICI/include/amici/model_dimensions.h index f0679dbe3..b5aa1ba21 100644 --- a/deps/AMICI/include/amici/model_dimensions.h +++ b/deps/AMICI/include/amici/model_dimensions.h @@ -31,6 +31,7 @@ struct ModelDimensions { * @param nz Number of event observables * @param nztrue Number of event observables of the non-augmented model * @param ne Number of events + * @param ne_solver Number of events that require root-finding * @param nspl Number of splines * @param nJ Number of objective functions * @param nw Number of repeating elements @@ -58,11 +59,12 @@ struct ModelDimensions { int const nx_rdata, int const nxtrue_rdata, int const nx_solver, int const nxtrue_solver, int const nx_solver_reinit, int const np, int const nk, int const ny, int const nytrue, int const nz, - int const nztrue, int const ne, int const nspl, int const nJ, - int const nw, int const ndwdx, int const ndwdp, int const ndwdw, - int const ndxdotdw, std::vector ndJydy, int const ndxrdatadxsolver, - int const ndxrdatadtcl, int const ndtotal_cldx_rdata, int const nnz, - int const ubw, int const lbw + int const nztrue, int const ne, int const ne_solver, int const nspl, + int const nJ, int const nw, int const ndwdx, int const ndwdp, + int const ndwdw, int const ndxdotdw, std::vector ndJydy, + int const ndxrdatadxsolver, int const ndxrdatadtcl, + int const ndtotal_cldx_rdata, int const nnz, int const ubw, + int const lbw ) : nx_rdata(nx_rdata) , nxtrue_rdata(nxtrue_rdata) @@ -76,6 +78,7 @@ struct ModelDimensions { , nz(nz) , nztrue(nztrue) , ne(ne) + , ne_solver(ne_solver) , nspl(nspl) , nw(nw) , ndwdx(ndwdx) @@ -104,6 +107,8 @@ struct ModelDimensions { Expects(nztrue >= 0); Expects(nztrue <= nz); Expects(ne >= 0); + Expects(ne_solver >= 0); + Expects(ne >= ne_solver); Expects(nspl >= 0); Expects(nw >= 0); Expects(ndwdx >= 0); @@ -164,7 +169,10 @@ struct ModelDimensions { /** Number of events */ int ne{0}; - /** numer of spline functions in the model */ + /** Number of events that require root-finding */ + int ne_solver{0}; + + /** Number of spline functions in the model */ int nspl{0}; /** Number of common expressions */ diff --git a/deps/AMICI/include/amici/model_ode.h b/deps/AMICI/include/amici/model_ode.h index 91e0c9cd4..24d410a33 100644 --- a/deps/AMICI/include/amici/model_ode.h +++ b/deps/AMICI/include/amici/model_ode.h @@ -10,7 +10,6 @@ #include #include -#include #include namespace amici { @@ -40,20 +39,23 @@ class Model_ODE : public Model { * @param ndxdotdp_explicit number of nonzero elements dxdotdp_explicit * @param ndxdotdx_explicit number of nonzero elements dxdotdx_explicit * @param w_recursion_depth Recursion depth of fw + * @param state_independent_events Map of events with state-independent + * triggers functions, mapping trigger timepoints to event indices. */ 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 + int const w_recursion_depth = 0, + std::map> state_independent_events = {} ) : Model( - model_dimensions, simulation_parameters, o2mode, idlist, z2event, - pythonGenerated, ndxdotdp_explicit, ndxdotdx_explicit, - w_recursion_depth - ) {} + model_dimensions, simulation_parameters, o2mode, idlist, z2event, + pythonGenerated, ndxdotdp_explicit, ndxdotdx_explicit, + w_recursion_depth, state_independent_events + ) {} void fJ(realtype t, realtype cj, AmiVector const& x, AmiVector const& dx, @@ -73,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; @@ -107,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; @@ -227,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; @@ -266,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/deps/AMICI/include/amici/rdata.h b/deps/AMICI/include/amici/rdata.h index 3b9ea01b9..b5b4c269b 100644 --- a/deps/AMICI/include/amici/rdata.h +++ b/deps/AMICI/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; @@ -576,7 +579,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++) @@ -688,7 +691,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/deps/AMICI/include/amici/returndata_matlab.h b/deps/AMICI/include/amici/returndata_matlab.h index 6a51db526..26b7d55d9 100644 --- a/deps/AMICI/include/amici/returndata_matlab.h +++ b/deps/AMICI/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/deps/AMICI/include/amici/serialization.h b/deps/AMICI/include/amici/serialization.h index 7d0428f71..a03540104 100644 --- a/deps/AMICI/include/amici/serialization.h +++ b/deps/AMICI/include/amici/serialization.h @@ -5,11 +5,9 @@ #include "amici/rdata.h" #include "amici/solver.h" #include "amici/solver_cvodes.h" +#include "amici/vector.h" -#include #include -#include -#include #include #include @@ -18,6 +16,7 @@ #include #include #include +#include #include /** @file serialization.h Helper functions and forward declarations for @@ -35,12 +34,12 @@ template void archiveVector(Archive& ar, T** p, int size) { if (Archive::is_loading::value) { if (*p != nullptr) - delete[] * p; - ar& size; + delete[] *p; + ar & size; *p = size ? new T[size] : nullptr; } else { size = *p == nullptr ? 0 : size; - ar& size; + ar & size; } ar& make_array(*p, size); } @@ -53,40 +52,44 @@ 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_; + ar & s.max_conv_fails_; + ar & s.max_nonlin_iters_; + ar & s.constraints_; + ar & s.max_step_size_; } /** @@ -101,11 +104,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; } } @@ -129,23 +132,25 @@ 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.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.steadystate_mask_; } /** @@ -157,18 +162,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; } /** @@ -182,63 +187,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; } /** @@ -251,29 +256,49 @@ 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.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; +} + +/** + * @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 diff --git a/deps/AMICI/include/amici/simulation_parameters.h b/deps/AMICI/include/amici/simulation_parameters.h index ca0e127c5..110b8fc03 100644 --- a/deps/AMICI/include/amici/simulation_parameters.h +++ b/deps/AMICI/include/amici/simulation_parameters.h @@ -35,6 +35,19 @@ class SimulationParameters { this->parameters.size(), ParameterScaling::none )) {} +#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 > >). + */ /** * @brief Constructor * @param fixedParameters Model constants @@ -69,6 +82,7 @@ class SimulationParameters { this->parameters.size(), ParameterScaling::none )) , ts_(std::move(timepoints)) {} +#endif /** * @brief Set reinitialization of all states based on model constants for @@ -169,7 +183,14 @@ class SimulationParameters { */ std::vector plist; - /** starting time */ + /** + * @brief Starting time of the simulation. + * + * Output timepoints are absolute timepoints, independent of + * \f$ t_{start} \f$. + * For output timepoints \f$ t < t_{start} \f$, the initial state will be + * returned. + */ realtype tstart_{0.0}; /** diff --git a/deps/AMICI/include/amici/solver.h b/deps/AMICI/include/amici/solver.h index 120a963ba..661a9fac2 100644 --- a/deps/AMICI/include/amici/solver.h +++ b/deps/AMICI/include/amici/solver.h @@ -48,7 +48,8 @@ class Solver { public: /** Type of what is passed to Sundials solvers as user_data */ using user_data_type = std::pair; - + /** Type of the function to free a raw sundials solver pointer */ + using free_solver_ptr = std::function; /** * @brief Default constructor */ @@ -133,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; @@ -935,6 +936,64 @@ 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 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. + */ + 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 @@ -1089,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; @@ -1607,11 +1666,16 @@ 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_; + mutable std::unique_ptr solver_memory_; /** pointer to solver memory block */ - mutable std::vector>> + mutable std::vector> solver_memory_B_; /** Sundials user_data */ @@ -1708,9 +1772,26 @@ class Solver { * @param preequilibration flag indicating preequilibration or simulation */ void checkSensitivityMethod( - const SensitivityMethod sensi_meth, bool preequilibration + 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; + + /** + * @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}; @@ -1751,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 @@ -1783,7 +1867,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}; @@ -1842,6 +1927,16 @@ 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}; + + /** Maximum allowed step size */ + realtype max_step_size_{0.0}; + /** CPU time, forward solve */ mutable realtype cpu_time_{0.0}; diff --git a/deps/AMICI/include/amici/solver_cvodes.h b/deps/AMICI/include/amici/solver_cvodes.h index d6d1dcea2..8d04b22a3 100644 --- a/deps/AMICI/include/amici/solver_cvodes.h +++ b/deps/AMICI/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) @@ -249,6 +249,14 @@ 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; + + void apply_constraints() const override; + + void apply_max_step_size() const override; }; } // namespace amici diff --git a/deps/AMICI/include/amici/solver_idas.h b/deps/AMICI/include/amici/solver_idas.h index 0dba1a950..c8efc69c0 100644 --- a/deps/AMICI/include/amici/solver_idas.h +++ b/deps/AMICI/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) @@ -229,6 +229,14 @@ 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; + + void apply_constraints() const override; + + void apply_max_step_size() const override; }; } // namespace amici diff --git a/deps/AMICI/include/amici/spline.h b/deps/AMICI/include/amici/spline.h index 07a436e38..d6f6b24b0 100644 --- a/deps/AMICI/include/amici/spline.h +++ b/deps/AMICI/include/amici/spline.h @@ -1,6 +1,5 @@ #ifndef amici_spline_h #define amici_spline_h -#include namespace amici { diff --git a/deps/AMICI/include/amici/splinefunctions.h b/deps/AMICI/include/amici/splinefunctions.h index db4410de9..ed9afb9ff 100644 --- a/deps/AMICI/include/amici/splinefunctions.h +++ b/deps/AMICI/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/deps/AMICI/include/amici/steadystateproblem.h b/deps/AMICI/include/amici/steadystateproblem.h index dc19c014c..72d248f8b 100644 --- a/deps/AMICI/include/amici/steadystateproblem.h +++ b/deps/AMICI/include/amici/steadystateproblem.h @@ -8,7 +8,6 @@ #include -#include #include namespace amici { @@ -247,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/deps/AMICI/include/amici/sundials_matrix_wrapper.h b/deps/AMICI/include/amici/sundials_matrix_wrapper.h index 8d63eca5e..0bb9b9215 100644 --- a/deps/AMICI/include/amici/sundials_matrix_wrapper.h +++ b/deps/AMICI/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 @@ -266,7 +271,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 +314,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 +362,7 @@ class SUNMatrixWrapper { */ void multiply( gsl::span c, gsl::span b, - const realtype alpha = 1.0 + realtype const alpha = 1.0 ) const; /** @@ -440,8 +445,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 +460,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/deps/AMICI/include/amici/vector.h b/deps/AMICI/include/amici/vector.h index b1b496c26..27f1d81a8 100644 --- a/deps/AMICI/include/amici/vector.h +++ b/deps/AMICI/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 AmiVector + * @param av + * + */ +inline span make_span(amici::AmiVector const& av) { + return make_span(av.getVector()); +} + } // namespace gsl #endif /* AMICI_VECTOR_H */ diff --git a/deps/AMICI/matlab/@amifun/getArgs.m b/deps/AMICI/matlab/@amifun/getArgs.m index afb50802e..a34c658ee 100644 --- a/deps/AMICI/matlab/@amifun/getArgs.m +++ b/deps/AMICI/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/deps/AMICI/matlab/@amimodel/generateC.m b/deps/AMICI/matlab/@amimodel/generateC.m index 59eb5e37b..2f09831b2 100644 --- a/deps/AMICI/matlab/@amimodel/generateC.m +++ b/deps/AMICI/matlab/@amimodel/generateC.m @@ -163,6 +163,7 @@ function generateC(this) fprintf(fid,[' ' num2str(this.nz) ',\n']); fprintf(fid,[' ' num2str(this.nztrue) ',\n']); fprintf(fid,[' ' num2str(this.nevent) ',\n']); +fprintf(fid,[' ' num2str(this.nevent) ',\n']); fprintf(fid,[' 0,\n']); fprintf(fid,[' ' num2str(this.ng) ',\n']); fprintf(fid,[' ' num2str(this.nw) ',\n']); @@ -208,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); @@ -252,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/deps/AMICI/models/model_calvetti/CMakeLists.txt b/deps/AMICI/models/model_calvetti/CMakeLists.txt index 2d1347b55..30262f7de 100644 --- a/deps/AMICI/models/model_calvetti/CMakeLists.txt +++ b/deps/AMICI/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/deps/AMICI/models/model_calvetti/dwdx.cpp b/deps/AMICI/models/model_calvetti/dwdx.cpp index 9066d0e95..f185abd31 100644 --- a/deps/AMICI/models/model_calvetti/dwdx.cpp +++ b/deps/AMICI/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/deps/AMICI/models/model_calvetti/main.cpp b/deps/AMICI/models/model_calvetti/main.cpp index 00a5e6b44..ecdff85a4 100644 --- a/deps/AMICI/models/model_calvetti/main.cpp +++ b/deps/AMICI/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/deps/AMICI/models/model_calvetti/model_calvetti.h b/deps/AMICI/models/model_calvetti/model_calvetti.h index 828be8272..c189ae8d7 100644 --- a/deps/AMICI/models/model_calvetti/model_calvetti.h +++ b/deps/AMICI/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); @@ -45,6 +45,7 @@ class Model_model_calvetti : public amici::Model_DAE { 0, 0, 4, + 4, 0, 1, 38, @@ -71,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); @@ -135,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 { @@ -184,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 { @@ -207,6 +208,6 @@ class Model_model_calvetti : public amici::Model_DAE { } // namespace model_model_calvetti -} // namespace amici +} // namespace amici #endif /* _amici_model_calvetti_h */ diff --git a/deps/AMICI/models/model_calvetti/w.cpp b/deps/AMICI/models/model_calvetti/w.cpp index fb1aaef8e..38773562d 100644 --- a/deps/AMICI/models/model_calvetti/w.cpp +++ b/deps/AMICI/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/deps/AMICI/models/model_dirac/CMakeLists.txt b/deps/AMICI/models/model_dirac/CMakeLists.txt index 64f02dca9..d96169b04 100644 --- a/deps/AMICI/models/model_dirac/CMakeLists.txt +++ b/deps/AMICI/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/deps/AMICI/models/model_dirac/main.cpp b/deps/AMICI/models/model_dirac/main.cpp index 00a5e6b44..ecdff85a4 100644 --- a/deps/AMICI/models/model_dirac/main.cpp +++ b/deps/AMICI/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/deps/AMICI/models/model_dirac/model_dirac.h b/deps/AMICI/models/model_dirac/model_dirac.h index 7a762479b..042532076 100644 --- a/deps/AMICI/models/model_dirac/model_dirac.h +++ b/deps/AMICI/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" @@ -45,6 +45,7 @@ class Model_model_dirac : public amici::Model_ODE { 0, 0, 2, + 2, 0, 1, 0, @@ -71,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); @@ -133,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 { @@ -183,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 { @@ -204,6 +205,6 @@ class Model_model_dirac : public amici::Model_ODE { } // namespace model_model_dirac -} // namespace amici +} // namespace amici #endif /* _amici_model_dirac_h */ diff --git a/deps/AMICI/models/model_events/CMakeLists.txt b/deps/AMICI/models/model_events/CMakeLists.txt index 277573426..53e3a335b 100644 --- a/deps/AMICI/models/model_events/CMakeLists.txt +++ b/deps/AMICI/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/deps/AMICI/models/model_events/main.cpp b/deps/AMICI/models/model_events/main.cpp index 00a5e6b44..ecdff85a4 100644 --- a/deps/AMICI/models/model_events/main.cpp +++ b/deps/AMICI/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/deps/AMICI/models/model_events/model_events.h b/deps/AMICI/models/model_events/model_events.h index df4bb68ae..bc2b1b615 100644 --- a/deps/AMICI/models/model_events/model_events.h +++ b/deps/AMICI/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" @@ -59,6 +59,7 @@ class Model_model_events : public amici::Model_ODE { 2, 2, 6, + 6, 0, 1, 0, @@ -85,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); @@ -153,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 { @@ -209,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 { @@ -232,6 +233,6 @@ class Model_model_events : public amici::Model_ODE { } // namespace model_model_events -} // namespace amici +} // namespace amici #endif /* _amici_model_events_h */ diff --git a/deps/AMICI/models/model_jakstat_adjoint/CMakeLists.txt b/deps/AMICI/models/model_jakstat_adjoint/CMakeLists.txt index 1670a68ee..75cc52769 100644 --- a/deps/AMICI/models/model_jakstat_adjoint/CMakeLists.txt +++ b/deps/AMICI/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/deps/AMICI/models/model_jakstat_adjoint/dwdp.cpp b/deps/AMICI/models/model_jakstat_adjoint/dwdp.cpp index 3213a319d..092466e87 100644 --- a/deps/AMICI/models/model_jakstat_adjoint/dwdp.cpp +++ b/deps/AMICI/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/deps/AMICI/models/model_jakstat_adjoint/dwdx.cpp b/deps/AMICI/models/model_jakstat_adjoint/dwdx.cpp index 70a26b8a2..354b72f24 100644 --- a/deps/AMICI/models/model_jakstat_adjoint/dwdx.cpp +++ b/deps/AMICI/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/deps/AMICI/models/model_jakstat_adjoint/main.cpp b/deps/AMICI/models/model_jakstat_adjoint/main.cpp index 00a5e6b44..ecdff85a4 100644 --- a/deps/AMICI/models/model_jakstat_adjoint/main.cpp +++ b/deps/AMICI/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/deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint.h b/deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint.h index fdac2a9f9..aad482c26 100644 --- a/deps/AMICI/models/model_jakstat_adjoint/model_jakstat_adjoint.h +++ b/deps/AMICI/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); @@ -49,6 +49,7 @@ class Model_model_jakstat_adjoint : public amici::Model_ODE { 0, 0, 0, + 0, 1, 2, 1, @@ -74,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); @@ -135,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 { @@ -187,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 { @@ -210,6 +211,6 @@ class Model_model_jakstat_adjoint : public amici::Model_ODE { } // namespace model_model_jakstat_adjoint -} // namespace amici +} // namespace amici #endif /* _amici_model_jakstat_adjoint_h */ diff --git a/deps/AMICI/models/model_jakstat_adjoint/w.cpp b/deps/AMICI/models/model_jakstat_adjoint/w.cpp index 06238238c..430b96de3 100644 --- a/deps/AMICI/models/model_jakstat_adjoint/w.cpp +++ b/deps/AMICI/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/deps/AMICI/models/model_jakstat_adjoint_o2/CMakeLists.txt b/deps/AMICI/models/model_jakstat_adjoint_o2/CMakeLists.txt index b4b9cc03c..4b2b35e22 100644 --- a/deps/AMICI/models/model_jakstat_adjoint_o2/CMakeLists.txt +++ b/deps/AMICI/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/deps/AMICI/models/model_jakstat_adjoint_o2/dwdp.cpp b/deps/AMICI/models/model_jakstat_adjoint_o2/dwdp.cpp index b3e591fcb..a936666e3 100644 --- a/deps/AMICI/models/model_jakstat_adjoint_o2/dwdp.cpp +++ b/deps/AMICI/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/deps/AMICI/models/model_jakstat_adjoint_o2/dwdx.cpp b/deps/AMICI/models/model_jakstat_adjoint_o2/dwdx.cpp index 3226a7535..81ad6343f 100644 --- a/deps/AMICI/models/model_jakstat_adjoint_o2/dwdx.cpp +++ b/deps/AMICI/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/deps/AMICI/models/model_jakstat_adjoint_o2/main.cpp b/deps/AMICI/models/model_jakstat_adjoint_o2/main.cpp index 00a5e6b44..ecdff85a4 100644 --- a/deps/AMICI/models/model_jakstat_adjoint_o2/main.cpp +++ b/deps/AMICI/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/deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2.h b/deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2.h index 22ca27606..e44c31b8d 100644 --- a/deps/AMICI/models/model_jakstat_adjoint_o2/model_jakstat_adjoint_o2.h +++ b/deps/AMICI/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); @@ -49,6 +49,7 @@ class Model_model_jakstat_adjoint_o2 : public amici::Model_ODE { 0, 0, 0, + 0, 18, 10, 2, @@ -74,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); @@ -135,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 { @@ -187,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 { @@ -210,6 +211,6 @@ class Model_model_jakstat_adjoint_o2 : public amici::Model_ODE { } // namespace model_model_jakstat_adjoint_o2 -} // namespace amici +} // namespace amici #endif /* _amici_model_jakstat_adjoint_o2_h */ diff --git a/deps/AMICI/models/model_jakstat_adjoint_o2/w.cpp b/deps/AMICI/models/model_jakstat_adjoint_o2/w.cpp index 766860cfa..827202e13 100644 --- a/deps/AMICI/models/model_jakstat_adjoint_o2/w.cpp +++ b/deps/AMICI/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/deps/AMICI/models/model_nested_events/CMakeLists.txt b/deps/AMICI/models/model_nested_events/CMakeLists.txt index 1a67d0a2c..c609531e1 100644 --- a/deps/AMICI/models/model_nested_events/CMakeLists.txt +++ b/deps/AMICI/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/deps/AMICI/models/model_nested_events/main.cpp b/deps/AMICI/models/model_nested_events/main.cpp index 00a5e6b44..ecdff85a4 100644 --- a/deps/AMICI/models/model_nested_events/main.cpp +++ b/deps/AMICI/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/deps/AMICI/models/model_nested_events/model_nested_events.h b/deps/AMICI/models/model_nested_events/model_nested_events.h index 9ff8f519f..5cad2049a 100644 --- a/deps/AMICI/models/model_nested_events/model_nested_events.h +++ b/deps/AMICI/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" @@ -48,6 +48,7 @@ class Model_model_nested_events : public amici::Model_ODE { 0, 0, 4, + 4, 0, 1, 0, @@ -74,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); @@ -137,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 { @@ -188,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 { @@ -210,6 +211,6 @@ class Model_model_nested_events : public amici::Model_ODE { } // namespace model_model_nested_events -} // namespace amici +} // namespace amici #endif /* _amici_model_nested_events_h */ diff --git a/deps/AMICI/models/model_neuron/CMakeLists.txt b/deps/AMICI/models/model_neuron/CMakeLists.txt index 323427169..4b580c036 100644 --- a/deps/AMICI/models/model_neuron/CMakeLists.txt +++ b/deps/AMICI/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/deps/AMICI/models/model_neuron/main.cpp b/deps/AMICI/models/model_neuron/main.cpp index 00a5e6b44..ecdff85a4 100644 --- a/deps/AMICI/models/model_neuron/main.cpp +++ b/deps/AMICI/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/deps/AMICI/models/model_neuron/model_neuron.h b/deps/AMICI/models/model_neuron/model_neuron.h index e8f6f5c21..387a3cadb 100644 --- a/deps/AMICI/models/model_neuron/model_neuron.h +++ b/deps/AMICI/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" @@ -62,6 +62,7 @@ class Model_model_neuron : public amici::Model_ODE { 1, 1, 1, + 1, 0, 1, 0, @@ -88,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); @@ -159,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 { @@ -215,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 { @@ -238,6 +239,6 @@ class Model_model_neuron : public amici::Model_ODE { } // namespace model_model_neuron -} // namespace amici +} // namespace amici #endif /* _amici_model_neuron_h */ diff --git a/deps/AMICI/models/model_neuron_o2/CMakeLists.txt b/deps/AMICI/models/model_neuron_o2/CMakeLists.txt index 42f5da90a..161fd4e9c 100644 --- a/deps/AMICI/models/model_neuron_o2/CMakeLists.txt +++ b/deps/AMICI/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/deps/AMICI/models/model_neuron_o2/dwdx.cpp b/deps/AMICI/models/model_neuron_o2/dwdx.cpp index a746d7549..3a2036c02 100644 --- a/deps/AMICI/models/model_neuron_o2/dwdx.cpp +++ b/deps/AMICI/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/deps/AMICI/models/model_neuron_o2/main.cpp b/deps/AMICI/models/model_neuron_o2/main.cpp index 00a5e6b44..ecdff85a4 100644 --- a/deps/AMICI/models/model_neuron_o2/main.cpp +++ b/deps/AMICI/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/deps/AMICI/models/model_neuron_o2/model_neuron_o2.h b/deps/AMICI/models/model_neuron_o2/model_neuron_o2.h index 23df2b9b3..4ae613ee8 100644 --- a/deps/AMICI/models/model_neuron_o2/model_neuron_o2.h +++ b/deps/AMICI/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); @@ -64,6 +64,7 @@ class Model_model_neuron_o2 : public amici::Model_ODE { 5, 1, 1, + 1, 0, 5, 2, @@ -90,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); @@ -161,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 { @@ -218,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 { @@ -242,6 +243,6 @@ class Model_model_neuron_o2 : public amici::Model_ODE { } // namespace model_model_neuron_o2 -} // namespace amici +} // namespace amici #endif /* _amici_model_neuron_o2_h */ diff --git a/deps/AMICI/models/model_neuron_o2/w.cpp b/deps/AMICI/models/model_neuron_o2/w.cpp index cbd2f0a25..fca88b9e3 100644 --- a/deps/AMICI/models/model_neuron_o2/w.cpp +++ b/deps/AMICI/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/deps/AMICI/models/model_robertson/CMakeLists.txt b/deps/AMICI/models/model_robertson/CMakeLists.txt index 9b27e3daa..1a4c57353 100644 --- a/deps/AMICI/models/model_robertson/CMakeLists.txt +++ b/deps/AMICI/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/deps/AMICI/models/model_robertson/dwdp.cpp b/deps/AMICI/models/model_robertson/dwdp.cpp index 831c448ca..5911b9907 100644 --- a/deps/AMICI/models/model_robertson/dwdp.cpp +++ b/deps/AMICI/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/deps/AMICI/models/model_robertson/dwdx.cpp b/deps/AMICI/models/model_robertson/dwdx.cpp index 5c300a54e..1e75c2924 100644 --- a/deps/AMICI/models/model_robertson/dwdx.cpp +++ b/deps/AMICI/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/deps/AMICI/models/model_robertson/main.cpp b/deps/AMICI/models/model_robertson/main.cpp index 00a5e6b44..ecdff85a4 100644 --- a/deps/AMICI/models/model_robertson/main.cpp +++ b/deps/AMICI/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/deps/AMICI/models/model_robertson/model_robertson.h b/deps/AMICI/models/model_robertson/model_robertson.h index 7f4377d78..4f25f4d9b 100644 --- a/deps/AMICI/models/model_robertson/model_robertson.h +++ b/deps/AMICI/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); @@ -47,6 +47,7 @@ class Model_model_robertson : public amici::Model_DAE { 0, 0, 0, + 0, 1, 1, 2, @@ -72,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); @@ -136,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 { @@ -186,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 { @@ -209,6 +210,6 @@ class Model_model_robertson : public amici::Model_DAE { } // namespace model_model_robertson -} // namespace amici +} // namespace amici #endif /* _amici_model_robertson_h */ diff --git a/deps/AMICI/models/model_robertson/w.cpp b/deps/AMICI/models/model_robertson/w.cpp index 6905b49c0..ae4145ba6 100644 --- a/deps/AMICI/models/model_robertson/w.cpp +++ b/deps/AMICI/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/deps/AMICI/models/model_steadystate/CMakeLists.txt b/deps/AMICI/models/model_steadystate/CMakeLists.txt index 9b699da5b..3d0dacaf8 100644 --- a/deps/AMICI/models/model_steadystate/CMakeLists.txt +++ b/deps/AMICI/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/deps/AMICI/models/model_steadystate/dwdp.cpp b/deps/AMICI/models/model_steadystate/dwdp.cpp index 154db2a72..d31310d34 100644 --- a/deps/AMICI/models/model_steadystate/dwdp.cpp +++ b/deps/AMICI/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/deps/AMICI/models/model_steadystate/dwdx.cpp b/deps/AMICI/models/model_steadystate/dwdx.cpp index d447f2140..dcd5f5e49 100644 --- a/deps/AMICI/models/model_steadystate/dwdx.cpp +++ b/deps/AMICI/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/deps/AMICI/models/model_steadystate/main.cpp b/deps/AMICI/models/model_steadystate/main.cpp index 00a5e6b44..ecdff85a4 100644 --- a/deps/AMICI/models/model_steadystate/main.cpp +++ b/deps/AMICI/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/deps/AMICI/models/model_steadystate/model_steadystate.h b/deps/AMICI/models/model_steadystate/model_steadystate.h index b61649f9c..ca02261ef 100644 --- a/deps/AMICI/models/model_steadystate/model_steadystate.h +++ b/deps/AMICI/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); @@ -46,6 +46,7 @@ class Model_model_steadystate : public amici::Model_ODE { 0, 0, 0, + 0, 1, 2, 2, @@ -71,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); @@ -131,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 { @@ -181,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 { @@ -204,6 +205,6 @@ class Model_model_steadystate : public amici::Model_ODE { } // namespace model_model_steadystate -} // namespace amici +} // namespace amici #endif /* _amici_model_steadystate_h */ diff --git a/deps/AMICI/models/model_steadystate/w.cpp b/deps/AMICI/models/model_steadystate/w.cpp index 948d4529c..5a0acafc8 100644 --- a/deps/AMICI/models/model_steadystate/w.cpp +++ b/deps/AMICI/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/deps/AMICI/pytest.ini b/deps/AMICI/pytest.ini index 3868c80b1..adbf31392 100644 --- a/deps/AMICI/pytest.ini +++ b/deps/AMICI/pytest.ini @@ -3,7 +3,10 @@ addopts = -vv --strict-markers filterwarnings = + # warnings are errors error + # petab + ignore:Using petab.v1.Problem with PEtab2.0 is deprecated:DeprecationWarning # amici ignore:Conservation laws for non-constant species in models with RateRules are currently not supported and will be turned off.:UserWarning ignore:Conservation laws for non-constant species in models with Species-AssignmentRules are currently not supported and will be turned off.:UserWarning @@ -13,11 +16,12 @@ filterwarnings = ignore:.*inspect.getargspec\(\) is deprecated.*:DeprecationWarning # pysb warnings ignore:the imp module is deprecated in favour of importlib.*:DeprecationWarning:pysb\.core - ignore:Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated since Python 3.3,and in 3.9 it will stop working.*:DeprecationWarning:pysb\.core ignore:Model.initial_conditions will be removed in a future version. Instead, you can get a list of Initial objects with Model.initials.:DeprecationWarning:pysb\.core # https://github.com/pytest-dev/pytest-xdist/issues/825#issuecomment-1292283870 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 diff --git a/deps/AMICI/python/examples/example_constant_species/ExampleEquilibrationLogic.ipynb b/deps/AMICI/python/examples/example_constant_species/ExampleEquilibrationLogic.ipynb index 5f66ea4db..eb343dca0 100644 --- a/deps/AMICI/python/examples/example_constant_species/ExampleEquilibrationLogic.ipynb +++ b/deps/AMICI/python/examples/example_constant_species/ExampleEquilibrationLogic.ipynb @@ -62,7 +62,7 @@ "from IPython.display import Image\n", "\n", "fig = Image(\n", - " filename=(\"../../../documentation/gfx/steadystate_solver_workflow.png\")\n", + " filename=\"../../../documentation/gfx/steadystate_solver_workflow.png\"\n", ")\n", "fig" ] @@ -97,12 +97,9 @@ ], "source": [ "import libsbml\n", - "import importlib\n", "import amici\n", "import os\n", - "import sys\n", "import numpy as np\n", - "import matplotlib.pyplot as plt\n", "\n", "# SBML model we want to import\n", "sbml_file = \"model_constant_species.xml\"\n", @@ -397,7 +394,7 @@ " * `-5`: Error: The model was simulated past the timepoint `t=1e100` without finding a steady state. Therefore, it is likely that the model has not steady state for the given parameter vector.\n", "\n", "Here, only the second entry of `posteq_status` contains a positive integer: The first run of Newton's method failed due to a Jacobian, which oculd not be factorized, but the second run (simulation) contains the entry 1 (success). The third entry is 0, thus Newton's method was not launched for a second time.\n", - "More information can be found in`posteq_numsteps`: Also here, only the second entry contains a positive integer, which is smaller than the maximum number of steps taken (<1000). Hence steady state was reached via simulation, which corresponds to the simulated time written to `posteq_time`.\n", + "More information can be found in`posteq_numsteps`: Also here, only the second entry contains a positive integer, which is smaller than the maximum number of steps taken (<1000). Hence, steady state was reached via simulation, which corresponds to the simulated time written to `posteq_time`.\n", "\n", "We want to demonstrate a complete failure if inferring the steady state by reducing the number of integration steps to a lower value:" ] @@ -951,7 +948,7 @@ } ], "source": [ - "# Singluar Jacobian, use simulation\n", + "# Singular Jacobian, use simulation\n", "model.setSteadyStateSensitivityMode(\n", " amici.SteadyStateSensitivityMode.integrateIfNewtonFails\n", ")\n", @@ -1207,7 +1204,7 @@ } ], "source": [ - "# Non-singular Jacobian, use simulaiton\n", + "# Non-singular Jacobian, use simulation\n", "model_reduced.setSteadyStateSensitivityMode(\n", " amici.SteadyStateSensitivityMode.integrateIfNewtonFails\n", ")\n", diff --git a/deps/AMICI/python/examples/example_errors.ipynb b/deps/AMICI/python/examples/example_errors.ipynb index 5e07803d9..3bf81aff6 100644 --- a/deps/AMICI/python/examples/example_errors.ipynb +++ b/deps/AMICI/python/examples/example_errors.ipynb @@ -19,15 +19,16 @@ "source": [ "%matplotlib inline\n", "import os\n", + "from contextlib import suppress\n", + "from pathlib import Path\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", "import amici\n", - "from amici.petab_import import import_petab_problem\n", - "from amici.petab_objective import simulate_petab, RDATAS, EDATAS\n", + "from amici.petab.petab_import import import_petab_problem\n", + "from amici.petab.simulations import simulate_petab, RDATAS, EDATAS\n", "from amici.plotting import plot_state_trajectories, plot_jacobian\n", - "import petab\n", - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "from pathlib import Path\n", - "from contextlib import suppress\n", "\n", "try:\n", " import benchmark_models_petab\n", @@ -76,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", @@ -153,7 +154,7 @@ " [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]],\n", ")\n", "assert all(rdata.status == amici.AMICI_SUCCESS for rdata in res[RDATAS])\n", - "print(\"Simulations finished succesfully.\")\n", + "print(\"Simulations finished successfully.\")\n", "print()\n", "\n", "\n", @@ -174,7 +175,7 @@ " [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]],\n", ")\n", "assert all(rdata.status == amici.AMICI_SUCCESS for rdata in res[RDATAS])\n", - "print(\"Simulations finished succesfully.\")" + "print(\"Simulations finished successfully.\")" ] }, { @@ -339,7 +340,7 @@ "**What happened?**\n", "\n", "AMICI failed to integrate the forward problem. The problem occurred for only one simulation condition, `condition_step_00_3`. The issue occurred at $t = 429.232$, where the error test failed.\n", - "This means, the solver is unable to take a step of non-zero size without violating the choosen error tolerances." + "This means, the solver is unable to take a step of non-zero size without violating the chosen error tolerances." ] }, { @@ -400,7 +401,7 @@ " [amici.simulation_status_to_str(rdata.status) for rdata in res[RDATAS]],\n", ")\n", "assert all(rdata.status == amici.AMICI_SUCCESS for rdata in res[RDATAS])\n", - "print(\"Simulations finished succesfully.\")" + "print(\"Simulations finished successfully.\")" ] }, { @@ -422,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", @@ -457,7 +458,7 @@ "source": [ "**What happened?**\n", "\n", - "The simulation failed because the initial step-size after an event or heaviside function was too small. The error occured during simulation of condition `model1_data1` after successful preequilibration (`model1_data2`)." + "The simulation failed because the initial step-size after an event or heaviside function was too small. The error occurred during simulation of condition `model1_data1` after successful preequilibration (`model1_data2`)." ] }, { @@ -646,7 +647,7 @@ "id": "62d82971", "metadata": {}, "source": [ - "Considering that `n_par` occurrs as exponent, it's magnitude looks pretty high.\n", + "Considering that `n_par` occurs as exponent, it's magnitude looks pretty high.\n", "This term is very likely causing the problem - let's check:" ] }, @@ -698,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", @@ -818,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", @@ -909,7 +910,7 @@ "source": [ "**What happened?**\n", "\n", - "All given experimental conditions require pre-equilibration, i.e., finding a steady state. AMICI first tries to find a steady state using the Newton solver, if that fails, it tries simulating until steady state, if that also failes, it tries the Newton solver from the end of the simulation. In this case, all three failed. Neither Newton's method nor simulation yielded a steady state satisfying the required tolerances.\n", + "All given experimental conditions require pre-equilibration, i.e., finding a steady state. AMICI first tries to find a steady state using the Newton solver, if that fails, it tries simulating until steady state, if that also fails, it tries the Newton solver from the end of the simulation. In this case, all three failed. Neither Newton's method nor simulation yielded a steady state satisfying the required tolerances.\n", "\n", "This can also be seen in `ReturnDataView.preeq_status` (the three statuses corresponds to Newton \\#1, Simulation, Newton \\#2):" ] diff --git a/deps/AMICI/python/examples/example_jax/ExampleJax.ipynb b/deps/AMICI/python/examples/example_jax/ExampleJax.ipynb index efda5b458..931dfb7e2 100644 --- a/deps/AMICI/python/examples/example_jax/ExampleJax.ipynb +++ b/deps/AMICI/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)." ] }, { @@ -262,10 +265,10 @@ "metadata": {}, "outputs": [], "source": [ - "from amici.petab_import import import_petab_problem\n", + "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", ")" ] }, @@ -274,9 +277,9 @@ "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)." + "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)." ] }, { @@ -294,7 +297,7 @@ "metadata": {}, "outputs": [], "source": [ - "from amici.petab_objective import simulate_petab\n", + "from amici.petab.simulations import simulate_petab\n", "import amici\n", "\n", "amici_solver = amici_model.getSolver()\n", @@ -341,7 +344,7 @@ "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` decorater 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)" + "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)" ] }, { @@ -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." ] }, { diff --git a/deps/AMICI/python/examples/example_large_models/example_performance_optimization.ipynb b/deps/AMICI/python/examples/example_large_models/example_performance_optimization.ipynb index 31a9fc172..82ca8d9db 100644 --- a/deps/AMICI/python/examples/example_large_models/example_performance_optimization.ipynb +++ b/deps/AMICI/python/examples/example_large_models/example_performance_optimization.ipynb @@ -9,9 +9,9 @@ "\n", "**Objective:** Give some hints to speed up import and simulation of larger models\n", "\n", - "This notebook gives some hints that may help to speed up import and simulation of (mostly) larger models. While some of these settings may also yield slight performance improvements for smaller models, other settings may make things slower. The impact may be highly model-dependent (number of states, number of parameters, rate expressions) or system-dependent and it's worthile doing some benchmarking.\n", + "This notebook gives some hints that may help to speed up import and simulation of (mostly) larger models. While some of these settings may also yield slight performance improvements for smaller models, other settings may make things slower. The impact may be highly model-dependent (number of states, number of parameters, rate expressions) or system-dependent, and it's worthwhile doing some benchmarking.\n", "\n", - "To simulate models in AMICI, a model specified in a high-level format needs to be imported first, as shown in the following figure. This rougly involves the following steps:\n", + "To simulate models in AMICI, a model specified in a high-level format needs to be imported first, as shown in the following figure. This roughly involves the following steps:\n", "\n", "1. Generating the ODEs\n", "2. Computing derivatives\n", @@ -21,7 +21,7 @@ "\n", "![AMICI workflow](https://raw.githubusercontent.com/AMICI-dev/AMICI/master/documentation/gfx/amici_workflow.png)\n", "\n", - "There are various options to speed up individual steps of this process. Generally, faster import comes with slower simulation and vice versa. During parameter estimation, a model is often imported only once, and then millions of simulations are run. Therefore, faster simulation will easily compensate for slower import (one-off cost). In other cases, many models may to have to be imported, but only few simulations will be executed. In this case, faster import may bee more relevant.\n", + "There are various options to speed up individual steps of this process. Generally, faster import comes with slower simulation and vice versa. During parameter estimation, a model is often imported only once, and then millions of simulations are run. Therefore, faster simulation will easily compensate for slower import (one-off cost). In other cases, many models may to have to be imported, but only few simulations will be executed. In this case, faster import may be more relevant.\n", "\n", "In the following, we will present various settings that (may) influence import and simulation time. We will follow the order of steps outlined above.\n", "\n", @@ -35,7 +35,7 @@ "metadata": {}, "outputs": [], "source": [ - "from IPython.core.pylabtools import figsize, getfigs\n", + "from IPython.core.pylabtools import figsize\n", "import matplotlib.pyplot as plt\n", "import pandas as pd\n", "\n", @@ -78,7 +78,7 @@ "See also the following section for the case that no sensitivities are required at all.\n", "\n", "\n", - "#### Not generating sensivitiy code\n", + "#### Not generating sensitivity code\n", "\n", "If only forward simulations of a model are required, a modest import speedup can be obtained from not generating sensitivity code. This can be enabled via the `generate_sensitivity_code` argument of [amici.sbml_import.SbmlImporter.sbml2amici](https://amici.readthedocs.io/en/latest/generated/amici.sbml_import.SbmlImporter.html#amici.sbml_import.SbmlImporter.sbml2amici) or [amici.pysb_import.pysb2amici](https://amici.readthedocs.io/en/latest/generated/amici.pysb_import.html?highlight=pysb2amici#amici.pysb_import.pysb2amici).\n", "\n", @@ -160,7 +160,7 @@ "source": [ "#### Parallelization\n", "\n", - "For large models or complex model expressions, symbolic computation of the derivatives can be quite time consuming. This can be parallelized by setting the environment variable `AMICI_IMPORT_NPROCS` to the number of parallel processes that should be used. The impact strongly depends on the model. Note that setting this value too high may have a negative performance impact (benchmark!).\n", + "For large models or complex model expressions, symbolic computation of the derivatives can be quite time-consuming. This can be parallelized by setting the environment variable `AMICI_IMPORT_NPROCS` to the number of parallel processes that should be used. The impact strongly depends on the model. Note that setting this value too high may have a negative performance impact (benchmark!).\n", "\n", "Impact for a large and a tiny model:" ] @@ -241,7 +241,7 @@ "\n", "Simplification of model expressions can be disabled by passing `simplify=None` to [amici.sbml_import.SbmlImporter.sbml2amici](https://amici.readthedocs.io/en/latest/generated/amici.sbml_import.SbmlImporter.html#amici.sbml_import.SbmlImporter.sbml2amici) or [amici.pysb_import.pysb2amici](https://amici.readthedocs.io/en/latest/generated/amici.pysb_import.html?highlight=pysb2amici#amici.pysb_import.pysb2amici).\n", "\n", - "Depending on the given model, different simplification schemes may be cheaper or more beneficial than the default. SymPy's simplifcation functions are [well documentated](https://docs.sympy.org/latest/modules/simplify/simplify.html)." + "Depending on the given model, different simplification schemes may be cheaper or more beneficial than the default. SymPy's simplification functions are [well documented](https://docs.sympy.org/latest/modules/simplify/simplify.html)." ] }, { @@ -384,11 +384,11 @@ "source": [ "#### Compiler flags\n", "\n", - "For most compilers, different machine code optimizations can be enabled/disabled by the `-O0`, `-O1`, `-O2`, `-O3` flags, where a higher number enables more optimizations. For fastet simulation, `-O3` should be used. However, these optimizations come at the cost of increased compile times. If models grow very large, some optimizations (especially with `g++`, see above) become prohibitively slow. In this case, a lower optimization level may be necessary to be able to compile models at all.\n", + "For most compilers, different machine code optimizations can be enabled/disabled by the `-O0`, `-O1`, `-O2`, `-O3` flags, where a higher number enables more optimizations. For faster simulation, `-O3` should be used. However, these optimizations come at the cost of increased compile times. If models grow very large, some optimizations (especially with `g++`, see above) become prohibitively slow. In this case, a lower optimization level may be necessary to be able to compile models at all.\n", "\n", - "Another potential performance gain can be obtained from using CPU-specific instructions using `-march=native`. The disadvantage is, that the compiled model extension will only run on CPUs supporting the same instruction set. This may be become problematic when attempting to use an AMICI model on a machine other than on which it was compiled (e.g. on hetergenous compute clusters).\n", + "Another potential performance gain can be obtained from using CPU-specific instructions using `-march=native`. The disadvantage is, that the compiled model extension will only run on CPUs supporting the same instruction set. This may be become problematic when attempting to use an AMICI model on a machine other than on which it was compiled (e.g. on heterogeneous compute clusters).\n", "\n", - "These compiler flags should be set for both, AMICI installation installation and model compilation. \n", + "These compiler flags should be set for both, AMICI installation and model compilation. \n", "\n", "For AMICI installation, e.g.,\n", "```bash\n", @@ -475,7 +475,7 @@ "source": [ "#### Using some optimized BLAS\n", "\n", - "You might have access to some custom [BLAS](https://en.wikipedia.org/wiki/Basic_Linear_Algebra_Subprograms) optimized for your hardware which might speed up your simulations somewhat. We are not aware of any systematic evaluation and cannot make any recomendation. You pass the respective compiler and linker flags via the environment variables `BLAS_CFLAGS` and `BLAS_LIBS`, respectively." + "You might have access to some custom [BLAS](https://en.wikipedia.org/wiki/Basic_Linear_Algebra_Subprograms) optimized for your hardware which might speed up your simulations somewhat. We are not aware of any systematic evaluation and cannot make any recommendation. You pass the respective compiler and linker flags via the environment variables `BLAS_CFLAGS` and `BLAS_LIBS`, respectively." ] }, { @@ -487,7 +487,7 @@ "\n", "A major determinant of simulation time for a given model is the required accuracy and the selected solvers. This has been evaluated, for example, in https://doi.org/10.1038/s41598-021-82196-2 and is not covered further here. \n", "\n", - "### Adjoint *vs.* forward sensivities\n", + "### Adjoint *vs.* forward sensitivities\n", "\n", "If only the objective function gradient is required, adjoint sensitivity analysis are often preferable over forward sensitivity analysis. As a rule of thumb, adjoint sensitivity analysis seems to outperform forward sensitivity analysis for models with more than 20 parameters:\n", "\n", diff --git a/deps/AMICI/python/examples/example_petab/petab.ipynb b/deps/AMICI/python/examples/example_petab/petab.ipynb index 689d793f5..afc4b2a38 100644 --- a/deps/AMICI/python/examples/example_petab/petab.ipynb +++ b/deps/AMICI/python/examples/example_petab/petab.ipynb @@ -6,304 +6,90 @@ "source": [ "# Using PEtab\n", "\n", - "This notebook illustrates how to use [PEtab](https://github.com/petab-dev/petab) with AMICI." + "This notebook illustrates how to run model simulations based on [PEtab](https://github.com/petab-dev/petab) problems with AMICI.\n", + "\n", + "PEtab is a format for specifying parameter estimation problems in systems biology. It is based on [SBML](http://sbml.org/) and [TSV](https://en.wikipedia.org/wiki/Tab-separated_values) files. (AMICI also supports PySB-based PEtab problems, that will be covered by PEtab v2). The Python package [pyPESTO](https://pypesto.readthedocs.io/) provides a convenient interface for parameter estimation with PEtab problems and uses AMICI as a backend. However, AMICI can also be used directly to simulate PEtab problems. This is illustrated in this notebook." ] }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "from amici.petab_import import import_petab_problem\n", - "from amici.petab_objective import simulate_petab\n", "import petab\n", "\n", - "import os" + "from amici import runAmiciSimulation\n", + "from amici.petab.petab_import import import_petab_problem\n", + "from amici.petab.petab_problem import PetabProblem\n", + "from amici.petab.simulations import simulate_petab\n", + "from amici.plotting import plot_state_trajectories" ] }, { "cell_type": "markdown", - "metadata": {}, - "source": [ - "We use an example model from the [benchmark collection](https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab):" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Cloning into 'tmp/benchmark-models'...\n", - "remote: Enumerating objects: 142, done.\u001b[K\n", - "remote: Counting objects: 100% (142/142), done.\u001b[K\n", - "remote: Compressing objects: 100% (122/122), done.\u001b[K\n", - "remote: Total 142 (delta 41), reused 104 (delta 18), pack-reused 0\u001b[K\n", - "Receiving objects: 100% (142/142), 648.29 KiB | 1.23 MiB/s, done.\n", - "Resolving deltas: 100% (41/41), 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)" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "total 68\r\n", - "drwxr-xr-x 2 yannik yannik 4096 Mär 17 15:27 Alkan_SciSignal2018\r\n", - "drwxr-xr-x 2 yannik yannik 4096 Mär 17 15:27 Beer_MolBioSystems2014\r\n", - "drwxr-xr-x 2 yannik yannik 4096 Mär 17 15:27 Boehm_JProteomeRes2014\r\n", - "drwxr-xr-x 2 yannik yannik 4096 Mär 17 15:27 Borghans_BiophysChem1997\r\n", - "drwxr-xr-x 2 yannik yannik 4096 Mär 17 15:27 Brannmark_JBC2010\r\n", - "drwxr-xr-x 2 yannik yannik 4096 Mär 17 15:27 Bruno_JExpBio2016\r\n", - "-rwxr-xr-x 1 yannik yannik 654 Mär 17 15:27 checkBenchmarkModels.py\r\n", - "drwxr-xr-x 2 yannik yannik 4096 Mär 17 15:27 Chen_MSB2009\r\n", - "drwxr-xr-x 2 yannik yannik 4096 Mär 17 15:27 Crauste_CellSystems2017\r\n", - "drwxr-xr-x 2 yannik yannik 4096 Mär 17 15:27 Elowitz_Nature2000\r\n", - "drwxr-xr-x 2 yannik yannik 4096 Mär 17 15:27 Fiedler_BMC2016\r\n", - "drwxr-xr-x 2 yannik yannik 4096 Mär 17 15:27 Fujita_SciSignal2010\r\n", - "drwxr-xr-x 2 yannik yannik 4096 Mär 17 15:27 Perelson_Science1996\r\n", - "drwxr-xr-x 2 yannik yannik 4096 Mär 17 15:27 Rahman_MBS2016\r\n", - "drwxr-xr-x 2 yannik yannik 4096 Mär 17 15:27 Sneyd_PNAS2002\r\n", - "drwxr-xr-x 2 yannik yannik 4096 Mär 17 15:27 Weber_BMC2015\r\n", - "drwxr-xr-x 2 yannik yannik 4096 Mär 17 15:27 Zheng_PNAS2012\r\n" - ] - } + "## Importing a PEtab problem" ], - "source": [ - "folder_base = \"tmp/benchmark-models/Benchmark-Models/\"\n", - "!ls -l $folder_base" - ] + "metadata": { + "collapsed": false + } }, { "cell_type": "markdown", "metadata": {}, "source": [ - "We import a model to PEtab from a provided yaml file:" + "We use the [Boehm_JProteomeRes2014](https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab/tree/master/Benchmark-Models/Boehm_JProteomeRes2014) example model from the [benchmark collection](https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab):" ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ "model_name = \"Boehm_JProteomeRes2014\"\n", - "yaml_file = os.path.join(folder_base, model_name, model_name + \".yaml\")\n", - "petab_problem = petab.Problem.from_yaml(yaml_file)" + "# local path or URL to the yaml file for the PEtab problem\n", + "petab_yaml = f\"https://raw.githubusercontent.com/Benchmarking-Initiative/Benchmark-Models-PEtab/master/Benchmark-Models/{model_name}/{model_name}.yaml\"\n", + "# load the problem using the PEtab library\n", + "petab_problem = petab.Problem.from_yaml(petab_yaml)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Next, we import the model to amici, compile it and obtain a function handle:" + "\n", + "Next, we import the model to amici using `import_petab_problem`. `import_petab_problem` has many options to choose between faster importer or more flexible or faster model simulations. We import the model with default settings, and we obtain an AMICI model instance:" ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2020-03-17 15:27:27.586 - amici.petab_import - INFO - Importing model ...\n", - "2020-03-17 15:27:27.593 - amici.petab_import - INFO - Model name is 'Boehm_JProteomeRes2014'. Writing model code to '/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014'.\n", - "2020-03-17 15:27:27.598 - amici.petab_import - INFO - Species: 8\n", - "2020-03-17 15:27:27.599 - amici.petab_import - INFO - Global parameters: 9\n", - "2020-03-17 15:27:27.599 - amici.petab_import - INFO - Reactions: 9\n", - "2020-03-17 15:27:27.715 - amici.petab_import - INFO - Observables: 3\n", - "2020-03-17 15:27:27.715 - amici.petab_import - INFO - Sigmas: 3\n", - "2020-03-17 15:27:27.722 - amici.petab_import - DEBUG - Adding output parameters to model: OrderedDict([('noiseParameter1_pSTAT5A_rel', None), ('noiseParameter1_pSTAT5B_rel', None), ('noiseParameter1_rSTAT5A_rel', None)])\n", - "2020-03-17 15:27:27.725 - amici.petab_import - DEBUG - Condition table: (1, 1)\n", - "2020-03-17 15:27:27.726 - amici.petab_import - DEBUG - Fixed parameters are []\n", - "2020-03-17 15:27:27.728 - amici.petab_import - INFO - Overall fixed parameters: 0\n", - "2020-03-17 15:27:27.729 - amici.petab_import - INFO - Variable parameters: 12\n", - "2020-03-17 15:27:27.735 - amici.sbml_import - INFO - Finished processing SBML parameters (1.25E-03s)\n", - "2020-03-17 15:27:27.749 - amici.sbml_import - INFO - Finished processing SBML species (1.26E-02s)\n", - "2020-03-17 15:27:27.829 - amici.sbml_import - INFO - Finished processing SBML reactions (7.41E-02s)\n", - "2020-03-17 15:27:27.833 - amici.sbml_import - INFO - Finished processing SBML compartments (4.23E-04s)\n", - "2020-03-17 15:27:27.898 - amici.sbml_import - INFO - Finished processing SBML rules (6.47E-02s)\n", - "2020-03-17 15:27:28.012 - amici.sbml_import - INFO - Finished processing SBML observables (6.77E-02s)\n", - "2020-03-17 15:27:28.139 - amici.ode_export - INFO - Finished writing J.cpp (1.14E-01s)\n", - "2020-03-17 15:27:28.160 - amici.ode_export - INFO - Finished writing JB.cpp (2.04E-02s)\n", - "2020-03-17 15:27:28.167 - amici.ode_export - INFO - Finished writing JDiag.cpp (6.41E-03s)\n", - "2020-03-17 15:27:28.187 - amici.ode_export - INFO - Finished writing JSparse.cpp (1.91E-02s)\n", - "2020-03-17 15:27:28.217 - amici.ode_export - INFO - Finished writing JSparseB.cpp (2.73E-02s)\n", - "2020-03-17 15:27:28.236 - amici.ode_export - INFO - Finished writing Jy.cpp (1.65E-02s)\n", - "2020-03-17 15:27:28.344 - amici.ode_export - INFO - Finished writing dJydsigmay.cpp (1.07E-01s)\n", - "2020-03-17 15:27:28.389 - amici.ode_export - INFO - Finished writing dJydy.cpp (3.99E-02s)\n", - "2020-03-17 15:27:28.466 - amici.ode_export - INFO - Finished writing dwdp.cpp (7.61E-02s)\n", - "2020-03-17 15:27:28.473 - amici.ode_export - INFO - Finished writing dwdx.cpp (5.87E-03s)\n", - "2020-03-17 15:27:28.497 - amici.ode_export - INFO - Finished writing dxdotdw.cpp (2.32E-02s)\n", - "2020-03-17 15:27:28.533 - amici.ode_export - INFO - Finished writing dxdotdp_explicit.cpp (3.38E-02s)\n", - "2020-03-17 15:27:28.756 - amici.ode_export - INFO - Finished writing dydx.cpp (1.98E-01s)\n", - "2020-03-17 15:27:28.910 - amici.ode_export - INFO - Finished writing dydp.cpp (1.53E-01s)\n", - "2020-03-17 15:27:28.926 - amici.ode_export - INFO - Finished writing dsigmaydp.cpp (1.40E-02s)\n", - "2020-03-17 15:27:28.931 - amici.ode_export - INFO - Finished writing sigmay.cpp (2.46E-03s)\n", - "2020-03-17 15:27:28.950 - amici.ode_export - INFO - Finished writing w.cpp (1.55E-02s)\n", - "2020-03-17 15:27:28.967 - amici.ode_export - INFO - Finished writing x0.cpp (1.57E-02s)\n", - "2020-03-17 15:27:28.975 - amici.ode_export - INFO - Finished writing x0_fixedParameters.cpp (4.78E-03s)\n", - "2020-03-17 15:27:29.027 - amici.ode_export - INFO - Finished writing sx0.cpp (5.01E-02s)\n", - "2020-03-17 15:27:29.069 - amici.ode_export - INFO - Finished writing sx0_fixedParameters.cpp (3.14E-02s)\n", - "2020-03-17 15:27:29.104 - amici.ode_export - INFO - Finished writing xdot.cpp (3.43E-02s)\n", - "2020-03-17 15:27:29.129 - amici.ode_export - INFO - Finished writing y.cpp (2.16E-02s)\n", - "2020-03-17 15:27:29.136 - amici.ode_export - INFO - Finished writing x_rdata.cpp (4.95E-03s)\n", - "2020-03-17 15:27:29.138 - amici.ode_export - INFO - Finished writing total_cl.cpp (6.59E-04s)\n", - "2020-03-17 15:27:29.147 - amici.ode_export - INFO - Finished writing x_solver.cpp (7.72E-03s)\n", - "2020-03-17 15:27:29.166 - amici.ode_export - INFO - Finished generating cpp code (1.14E+00s)\n", - "2020-03-17 15:27:46.200 - amici.ode_export - INFO - Finished compiling cpp code (1.70E+01s)\n", - "2020-03-17 15:27:46.204 - amici.petab_import - INFO - Finished Importing PEtab model (1.86E+01s)\n", - "2020-03-17 15:27:46.209 - amici.petab_import - INFO - Successfully loaded model Boehm_JProteomeRes2014 from /home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014.\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "running build_ext\n", - "building 'Boehm_JProteomeRes2014._Boehm_JProteomeRes2014' extension\n", - "swigging swig/Boehm_JProteomeRes2014.i to swig/Boehm_JProteomeRes2014_wrap.cpp\n", - "swig -python -c++ -modern -outdir Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/swig -I/home/yannik/amici/python/sdist/amici/include -o swig/Boehm_JProteomeRes2014_wrap.cpp swig/Boehm_JProteomeRes2014.i\n", - "creating build\n", - "creating build/temp.linux-x86_64-3.7\n", - "creating build/temp.linux-x86_64-3.7/swig\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c swig/Boehm_JProteomeRes2014_wrap.cpp -o build/temp.linux-x86_64-3.7/swig/Boehm_JProteomeRes2014_wrap.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_dxdotdw.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dxdotdw.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_total_cl.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_total_cl.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_x_rdata.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_x_rdata.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_dxdotdp_implicit_colptrs.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dxdotdp_implicit_colptrs.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_dsigmaydp.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dsigmaydp.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_y.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_y.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_dydp.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dydp.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_w.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_w.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_JSparseB_rowvals.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_JSparseB_rowvals.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_dxdotdw_rowvals.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dxdotdw_rowvals.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_dwdx_rowvals.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dwdx_rowvals.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_x0.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_x0.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_dwdx.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dwdx.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_dJydy_colptrs.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dJydy_colptrs.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_JSparseB.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_JSparseB.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_JSparseB_colptrs.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_JSparseB_colptrs.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_dxdotdp_explicit_colptrs.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dxdotdp_explicit_colptrs.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_sx0_fixedParameters.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_sx0_fixedParameters.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_JSparse_rowvals.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_JSparse_rowvals.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_dxdotdp_explicit.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dxdotdp_explicit.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_dJydy.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dJydy.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_dwdp_colptrs.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dwdp_colptrs.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_x0_fixedParameters.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_x0_fixedParameters.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_dxdotdw_colptrs.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dxdotdw_colptrs.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_dJydsigmay.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dJydsigmay.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_dxdotdp_implicit_rowvals.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dxdotdp_implicit_rowvals.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_dwdp.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dwdp.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_sx0.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_sx0.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_JB.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_JB.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_dwdx_colptrs.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dwdx_colptrs.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c wrapfunctions.cpp -o build/temp.linux-x86_64-3.7/wrapfunctions.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_x_solver.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_x_solver.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_JSparse.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_JSparse.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_xdot.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_xdot.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_dJydy_rowvals.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dJydy_rowvals.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_dwdp_rowvals.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dwdp_rowvals.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_JSparse_colptrs.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_JSparse_colptrs.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_J.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_J.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_dydx.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dydx.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_JDiag.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_JDiag.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_Jy.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_Jy.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_sigmay.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_sigmay.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "gcc -pthread -B /home/yannik/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014 -I/home/yannik/amici/python/sdist/amici/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/gsl -I/home/yannik/amici/python/sdist/amici/ThirdParty/sundials/include -I/home/yannik/amici/python/sdist/amici/ThirdParty/SuiteSparse/include -I/usr/include/hdf5/serial -I/home/yannik/anaconda3/include/python3.7m -c Boehm_JProteomeRes2014_dxdotdp_explicit_rowvals.cpp -o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dxdotdp_explicit_rowvals.o -std=c++14\n", - "cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++\n", - "g++ -pthread -shared -B /home/yannik/anaconda3/compiler_compat -L/home/yannik/anaconda3/lib -Wl,-rpath=/home/yannik/anaconda3/lib -Wl,--no-as-needed -Wl,--sysroot=/ build/temp.linux-x86_64-3.7/swig/Boehm_JProteomeRes2014_wrap.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dxdotdw.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_total_cl.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_x_rdata.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dxdotdp_implicit_colptrs.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dsigmaydp.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_y.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dydp.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_w.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_JSparseB_rowvals.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dxdotdw_rowvals.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dwdx_rowvals.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_x0.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dwdx.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dJydy_colptrs.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_JSparseB.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_JSparseB_colptrs.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dxdotdp_explicit_colptrs.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_sx0_fixedParameters.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_JSparse_rowvals.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dxdotdp_explicit.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dJydy.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dwdp_colptrs.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_x0_fixedParameters.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dxdotdw_colptrs.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dJydsigmay.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dxdotdp_implicit_rowvals.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dwdp.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_sx0.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_JB.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dwdx_colptrs.o build/temp.linux-x86_64-3.7/wrapfunctions.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_x_solver.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_JSparse.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_xdot.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dJydy_rowvals.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dwdp_rowvals.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_JSparse_colptrs.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_J.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dydx.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_JDiag.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_Jy.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_sigmay.o build/temp.linux-x86_64-3.7/Boehm_JProteomeRes2014_dxdotdp_explicit_rowvals.o -L/usr/lib/x86_64-linux-gnu/hdf5/serial -L/home/yannik/amici/python/sdist/amici/libs -lamici -lsundials -lsuitesparse -lcblas -lhdf5_hl_cpp -lhdf5_hl -lhdf5_cpp -lhdf5 -o /home/yannik/amici/python/examples/amici_models/Boehm_JProteomeRes2014/Boehm_JProteomeRes2014/_Boehm_JProteomeRes2014.cpython-37m-x86_64-linux-gnu.so\n", - "\n" - ] - } - ], + "outputs": [], "source": [ - "amici_model = import_petab_problem(petab_problem)" + "amici_model = import_petab_problem(petab_problem, verbose=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "That's it. Now, we can use the model to perform simulations. For more involved purposes, consider using the objective function provided by [pyPESTO](https://github.com/icb-dcm/pypesto). For simple simulations, a function `simulate_petab` is available:" + "That's it. Now, we can use the model to perform simulations.\n", + "\n", + "## Simulating a PEtab problem\n", + "\n", + "For simple simulations, a function `simulate_petab` is available. This function will simulate the model for all conditions specified in the PEtab problem and compute the objective value (and if requested, the gradient). `simulate_petab` is mostly useful for running individual simulations. If large numbers of model simulations are required, there are more efficient means. In particular, for parameter estimation, consider using the optimized objective function provided by [pyPESTO](https://github.com/icb-dcm/pypesto).\n", + "\n", + "We use the `simulate_petab` function to simulate the model at the nominal parameters (i.e., the parameters specified in the PEtab problem in the `nominalValue` column of the parameter table):" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'llh': -138.22199570334107,\n", - " 'sllh': None,\n", - " 'rdatas': []}" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "simulate_petab(petab_problem, amici_model)" ] @@ -312,31 +98,21 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "This performs a simulation at the nominal parameters. Parameters can also be directly specified, both scaled and unscaled:" + " Parameters can also be directly specified, both scaled and unscaled:" ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'llh': -138.22199570334107,\n", - " 'sllh': None,\n", - " 'rdatas': []}" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "parameters = {\n", " x_id: x_val\n", " for x_id, x_val in zip(petab_problem.x_ids, petab_problem.x_nominal_scaled)\n", + " # Fixed parameters cannot be changed in `simulate_petab`, unless we explicitly pass\n", + " # a `parameter_mapping` that was generated with `fill_fixed_parameters=False`\n", + " if x_id not in amici_model.getFixedParameterIds()\n", "}\n", "simulate_petab(\n", " petab_problem,\n", @@ -350,8 +126,60 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "For further information, see the [documentation](https://amici.readthedocs.io/en/latest/)." + "## Working with PEtab-defined simulation conditions\n", + "\n", + "`simulate_petab` is convenient for quickly simulating PEtab-based problems, but for certain applications it may be too inflexible.\n", + "For example, it is not easily possible to obtain model outputs for time points other than the measurement timepoints specified in the PEtab problem. In such a case, the `PetabProblem` class can be used to easily generate AMICI `ExpData` objects representing PEtab-defined simulation conditions:" ] + }, + { + "cell_type": "code", + "outputs": [], + "source": [ + "app = PetabProblem(petab_problem)\n", + "\n", + "# ExpData for all conditions:\n", + "app.get_edatas()\n", + "\n", + "# ExpData for a single condition:\n", + "edata = app.get_edata(\"model1_data1\")" + ], + "metadata": { + "collapsed": false + }, + "execution_count": null + }, + { + "cell_type": "code", + "outputs": [], + "source": [ + "rdata = runAmiciSimulation(amici_model, solver=amici_model.getSolver(), edata=edata)\n", + "rdata" + ], + "metadata": { + "collapsed": false + }, + "execution_count": null + }, + { + "cell_type": "code", + "outputs": [], + "source": [ + "plot_state_trajectories(rdata)" + ], + "metadata": { + "collapsed": false + }, + "execution_count": null + }, + { + "cell_type": "markdown", + "source": [ + "For further information, check out the [AMICI documentation](https://amici.readthedocs.io/en/latest/)." + ], + "metadata": { + "collapsed": false + } } ], "metadata": { diff --git a/deps/AMICI/python/examples/example_presimulation/ExampleExperimentalConditions.ipynb b/deps/AMICI/python/examples/example_presimulation/ExampleExperimentalConditions.ipynb index 63fbc7a4f..83f23273a 100644 --- a/deps/AMICI/python/examples/example_presimulation/ExampleExperimentalConditions.ipynb +++ b/deps/AMICI/python/examples/example_presimulation/ExampleExperimentalConditions.ipynb @@ -21,16 +21,12 @@ "# Directory to which the generated model code is written\n", "model_output_dir = model_name\n", "\n", + "from pprint import pprint\n", + "\n", "import libsbml\n", - "import amici\n", - "import amici.plotting\n", - "import os\n", - "import sys\n", - "import importlib\n", "import numpy as np\n", - "import pandas as pd\n", - "import matplotlib.pyplot as plt\n", - "from pprint import pprint" + "\n", + "import amici.plotting" ] }, { @@ -143,7 +139,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "For this example we want specify the initial drug and kinase concentrations as experimental conditions. Accordingly we specify them as `fixedParameters`. The meaning of `fixedParameters` is defined in the [Glossary](https://amici.readthedocs.io/en/latest/glossary.html#term-fixed-parameters), which we display here for convenience." + "For this example we want to specify the initial drug and kinase concentrations as experimental conditions. Accordingly, we specify them as `fixedParameters`. The meaning of `fixedParameters` is defined in the [Glossary](https://amici.readthedocs.io/en/latest/glossary.html#term-fixed-parameters), which we display here for convenience." ] }, { @@ -369,7 +365,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The resulting trajectory is definitely not what one may expect. The problem is that the `DRUG_0` and `KIN_0` set initial conditions for species in the model. By default these initial conditions are only applied at the very beginning of the simulation, i.e., before the preequilibration. Accordingly, the `fixedParameters` that we specified do not have any effect. To fix this, we need to set the `reinitializeFixedParameterInitialStates` attribue to `True`, to spefify that AMICI reinitializes all states that have `fixedParameter`-dependent initial states." + "The resulting trajectory is definitely not what one may expect. The problem is that the `DRUG_0` and `KIN_0` set initial conditions for species in the model. By default, these initial conditions are only applied at the very beginning of the simulation, i.e., before the preequilibration. Accordingly, the `fixedParameters` that we specified do not have any effect. To fix this, we need to set the `reinitializeFixedParameterInitialStates` attribute to `True`, to specify that AMICI reinitializes all states that have `fixedParameter`-dependent initial states." ] }, { diff --git a/deps/AMICI/python/examples/example_splines/ExampleSplines.ipynb b/deps/AMICI/python/examples/example_splines/ExampleSplines.ipynb index d376ba91e..0c237c6e6 100644 --- a/deps/AMICI/python/examples/example_splines/ExampleSplines.ipynb +++ b/deps/AMICI/python/examples/example_splines/ExampleSplines.ipynb @@ -24,21 +24,20 @@ }, "outputs": [], "source": [ - "import sys\n", "import os\n", - "import libsbml\n", - "import amici\n", - "\n", - "import numpy as np\n", - "import sympy as sp\n", - "\n", - "from shutil import rmtree\n", + "import sys\n", "from importlib import import_module\n", - "from uuid import uuid1\n", + "from shutil import rmtree\n", "from tempfile import TemporaryDirectory\n", + "from uuid import uuid1\n", + "\n", "import matplotlib as mpl\n", + "import numpy as np\n", + "import sympy as sp\n", "from matplotlib import pyplot as plt\n", "\n", + "import amici\n", + "\n", "# Choose build directory\n", "BUILD_PATH = None # temporary folder\n", "# BUILD_PATH = 'build' # specified folder for debugging\n", @@ -458,8 +457,7 @@ "\t\t\t2\n", "\t\t\n", "\t\n", - "\n", - "\n" + "\n" ] } ], @@ -1137,7 +1135,6 @@ "outputs": [], "source": [ "import pandas as pd\n", - "import seaborn as sns\n", "import tempfile\n", "import time" ] @@ -1179,7 +1176,7 @@ "metadata": {}, "outputs": [], "source": [ - "# If running as a Github action, just do the minimal amount of work required to check whether the code is working\n", + "# If running as a GitHub action, just do the minimal amount of work required to check whether the code is working\n", "if os.getenv(\"GITHUB_ACTIONS\") is not None:\n", " nruns = 1\n", " num_nodes = [4]\n", diff --git a/deps/AMICI/python/examples/example_splines_swameye/ExampleSplinesSwameye2003.ipynb b/deps/AMICI/python/examples/example_splines_swameye/ExampleSplinesSwameye2003.ipynb index 8e3ee6db1..e8b75e49a 100644 --- a/deps/AMICI/python/examples/example_splines_swameye/ExampleSplinesSwameye2003.ipynb +++ b/deps/AMICI/python/examples/example_splines_swameye/ExampleSplinesSwameye2003.ipynb @@ -47,23 +47,19 @@ }, "outputs": [], "source": [ - "import os\n", - "import math\n", - "import logging\n", - "import contextlib\n", - "import multiprocessing\n", "import copy\n", + "import logging\n", + "import os\n", "\n", + "import libsbml\n", "import numpy as np\n", - "import sympy as sp\n", "import pandas as pd\n", + "import petab\n", + "import pypesto.petab\n", + "import sympy as sp\n", "from matplotlib import pyplot as plt\n", "\n", - "import libsbml\n", - "import amici\n", - "import petab\n", - "import pypesto\n", - "import pypesto.petab" + "import amici" ] }, { @@ -103,13 +99,13 @@ }, "outputs": [], "source": [ - "# If running as a Github action, just do the minimal amount of work required to check whether the code is working\n", + "# If running as a GitHub action, just do the minimal amount of work required to check whether the code is working\n", "if os.getenv(\"GITHUB_ACTIONS\") is not None:\n", - " n_starts = 15\n", + " n_starts = 25\n", " pypesto_optimizer = pypesto.optimize.FidesOptimizer(\n", " verbose=logging.WARNING, options=dict(maxiter=10)\n", " )\n", - " pypesto_engine = pypesto.engine.SingleCoreEngine()" + " pypesto_engine = pypesto.engine.MultiProcessEngine()" ] }, { diff --git a/deps/AMICI/python/examples/example_steadystate/ExampleSteadystate.ipynb b/deps/AMICI/python/examples/example_steadystate/ExampleSteadystate.ipynb index b57ed522a..d9f6ae635 100644 --- a/deps/AMICI/python/examples/example_steadystate/ExampleSteadystate.ipynb +++ b/deps/AMICI/python/examples/example_steadystate/ExampleSteadystate.ipynb @@ -129,7 +129,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "In this example, we want to specify fixed parameters, observables and a $\\sigma$ parameter. Unfortunately, the latter two are not part of the [SBML standard](http://sbml.org/). However, they can be provided to `amici.SbmlImporter.sbml2amici` as demonstrated in the following." + "In this example, we want to specify fixed parameters, observables and a $\\sigma$ parameter. Unfortunately, the latter two are not part of the [SBML standard](https://sbml.org/). However, they can be provided to `amici.SbmlImporter.sbml2amici` as demonstrated in the following." ] }, { diff --git a/deps/AMICI/python/sdist/MANIFEST.in b/deps/AMICI/python/sdist/MANIFEST.in index 762987665..5be0b1ebc 100644 --- a/deps/AMICI/python/sdist/MANIFEST.in +++ b/deps/AMICI/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 diff --git a/deps/AMICI/python/sdist/amici/MANIFEST.template.in b/deps/AMICI/python/sdist/amici/MANIFEST.template.in index eb3b1b450..fd7812985 100644 --- a/deps/AMICI/python/sdist/amici/MANIFEST.template.in +++ b/deps/AMICI/python/sdist/amici/MANIFEST.template.in @@ -1 +1,3 @@ include *.cpp *.h +include CMakeLists.txt +recursive-include swig/ * diff --git a/deps/AMICI/python/sdist/amici/__init__.py b/deps/AMICI/python/sdist/amici/__init__.py index bcb7387fb..942b669fa 100644 --- a/deps/AMICI/python/sdist/amici/__init__.py +++ b/deps/AMICI/python/sdist/amici/__init__.py @@ -6,7 +6,6 @@ models and turning them into C++ Python extensions. """ - import contextlib import importlib import os @@ -14,7 +13,8 @@ import sys from pathlib import Path from types import ModuleType as ModelModule -from typing import Any, Callable, Optional, Union +from typing import Any +from collections.abc import Callable def _get_amici_path(): @@ -108,18 +108,21 @@ def _imported_from_setup() -> bool: # from .swig_wrappers hdf5_enabled = "readSolverSettingsFromHDF5" in dir() # These modules require the swig interface and other dependencies - from .numpy import ExpDataView, ReturnDataView + from .numpy import ExpDataView, ReturnDataView # noqa: F401 from .pandas import * from .swig_wrappers import * # These modules don't require the swig interface from typing import Protocol, runtime_checkable - from .de_export import DEExporter, DEModel - from .sbml_import import SbmlImporter, assignmentRules2observables + from .de_export import DEExporter # noqa: F401 + from .sbml_import import ( # noqa: F401 + SbmlImporter, + assignmentRules2observables, + ) @runtime_checkable - class ModelModule(Protocol): + class ModelModule(Protocol): # noqa: F811 """Type of AMICI-generated model modules. To enable static type checking.""" @@ -136,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): @@ -149,7 +152,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: Path | str ) -> ModelModule: """ Import Python module of an AMICI model diff --git a/deps/AMICI/python/sdist/amici/__init__.template.py b/deps/AMICI/python/sdist/amici/__init__.template.py index c37ac0f96..f5e49b03d 100644 --- a/deps/AMICI/python/sdist/amici/__init__.template.py +++ b/deps/AMICI/python/sdist/amici/__init__.template.py @@ -15,7 +15,7 @@ "version currently installed." ) -from .TPL_MODELNAME import * -from .TPL_MODELNAME import getModel as get_model +from .TPL_MODELNAME import * # noqa: F403, F401 +from .TPL_MODELNAME import getModel as get_model # noqa: F401 __version__ = "TPL_PACKAGE_VERSION" diff --git a/deps/AMICI/python/sdist/amici/__main__.py b/deps/AMICI/python/sdist/amici/__main__.py index 165f5d951..bf179cf87 100644 --- a/deps/AMICI/python/sdist/amici/__main__.py +++ b/deps/AMICI/python/sdist/amici/__main__.py @@ -1,6 +1,5 @@ """Package-level entrypoint""" -import os import sys from . import __version__, compiledWithOpenMP, has_clibs, hdf5_enabled diff --git a/deps/AMICI/python/sdist/amici/_codegen/__init__.py b/deps/AMICI/python/sdist/amici/_codegen/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/deps/AMICI/python/sdist/amici/_codegen/cxx_functions.py b/deps/AMICI/python/sdist/amici/_codegen/cxx_functions.py new file mode 100644 index 000000000..5fd5ead94 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/_codegen/cxx_functions.py @@ -0,0 +1,409 @@ +"""Info about C++ functions in the generated model code.""" + +from __future__ import annotations + +from dataclasses import dataclass +import re + + +@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() +] + + +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/deps/AMICI/python/sdist/amici/_codegen/model_class.py b/deps/AMICI/python/sdist/amici/_codegen/model_class.py new file mode 100644 index 000000000..d24884ca8 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/_codegen/model_class.py @@ -0,0 +1,192 @@ +"""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/deps/AMICI/python/sdist/amici/_codegen/template.py b/deps/AMICI/python/sdist/amici/_codegen/template.py new file mode 100644 index 000000000..2a099ba90 --- /dev/null +++ b/deps/AMICI/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 + + +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: str | Path, + target_file: 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/deps/AMICI/python/sdist/amici/antimony_import.py b/deps/AMICI/python/sdist/amici/antimony_import.py index cc2307978..ad269f11f 100644 --- a/deps/AMICI/python/sdist/amici/antimony_import.py +++ b/deps/AMICI/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 @@ -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.") @@ -44,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/deps/AMICI/python/sdist/amici/bngl_import.py b/deps/AMICI/python/sdist/amici/bngl_import.py index 960413dbe..8624ca15f 100644 --- a/deps/AMICI/python/sdist/amici/bngl_import.py +++ b/deps/AMICI/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/deps/AMICI/python/sdist/amici/compile.py b/deps/AMICI/python/sdist/amici/compile.py new file mode 100644 index 000000000..eb3668bfb --- /dev/null +++ b/deps/AMICI/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 pathlib import Path +import os + + +def build_model_extension( + 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. + + :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/deps/AMICI/python/sdist/amici/conserved_quantities_demartino.py b/deps/AMICI/python/sdist/amici/conserved_quantities_demartino.py index 5b04fa147..cf651241d 100644 --- a/deps/AMICI/python/sdist/amici/conserved_quantities_demartino.py +++ b/deps/AMICI/python/sdist/amici/conserved_quantities_demartino.py @@ -2,7 +2,7 @@ import math import random import sys -from typing import List, MutableSequence, Optional, Sequence, Tuple, Union +from collections.abc import MutableSequence, Sequence from .logging import get_logger @@ -20,9 +20,9 @@ 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, -) -> Tuple[List[List[int]], List[List[float]]]: + rng_seed: None | bool | int = False, + species_names: Sequence[str] | None = None, +) -> tuple[list[list[int]], list[list[float]]]: """Compute moiety conservation laws. According to the algorithm proposed by De Martino et al. (2014) @@ -112,10 +112,10 @@ def compute_moiety_conservation_laws( def _output( int_kernel_dim: int, kernel_dim: int, - int_matched: List[int], - species_indices: List[List[int]], - species_coefficients: List[List[float]], - species_names: Optional[Sequence[str]] = None, + int_matched: list[int], + species_indices: list[list[int]], + species_coefficients: list[list[float]], + species_names: Sequence[str] | None = None, verbose: bool = False, log_level: int = logging.DEBUG, ): @@ -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] @@ -203,7 +203,7 @@ def _qsort( def _kernel( stoichiometric_list: Sequence[float], num_species: int, num_reactions: int -) -> Tuple[int, List[int], int, List[int], List[List[int]], List[List[float]]]: +) -> tuple[int, list[int], int, list[int], list[list[int]], list[list[float]]]: """ Kernel (left nullspace of :math:`S`) calculation by Gaussian elimination @@ -227,8 +227,8 @@ def _kernel( kernel dimension, MCLs, integer kernel dimension, integer MCLs and indices to species and reactions in the preceding order as a tuple """ - matrix: List[List[int]] = [[] for _ in range(num_species)] - matrix2: List[List[float]] = [[] for _ in range(num_species)] + matrix: list[list[int]] = [[] for _ in range(num_species)] + matrix2: list[list[float]] = [[] for _ in range(num_species)] i_reaction = 0 i_species = 0 for val in stoichiometric_list: @@ -243,7 +243,7 @@ def _kernel( matrix[i].append(num_reactions + i) matrix2[i].append(1) - order: List[int] = list(range(num_species)) + order: list[int] = list(range(num_species)) pivots = [ matrix[i][0] if len(matrix[i]) else _MAX for i in range(num_species) ] @@ -283,7 +283,7 @@ def _kernel( if pivots[order[j + 1]] == pivots[order[j]] != _MAX: k1 = order[j + 1] k2 = order[j] - column: List[float] = [0] * (num_species + num_reactions) + column: list[float] = [0] * (num_species + num_reactions) g = matrix2[k2][0] / matrix2[k1][0] for i in range(1, len(matrix[k1])): column[matrix[k1][i]] = matrix2[k1][i] * g @@ -369,7 +369,7 @@ def _fill( stoichiometric_list: Sequence[float], matched: Sequence[int], num_species: int, -) -> Tuple[List[List[int]], List[List[int]], List[int]]: +) -> tuple[list[list[int]], list[list[int]], list[int]]: """Construct interaction matrix Construct the interaction matrix out of the given stoichiometric matrix @@ -454,8 +454,8 @@ def _is_linearly_dependent( boolean indicating linear dependence (true) or not (false) """ K = int_kernel_dim + 1 - matrix: List[List[int]] = [[] for _ in range(K)] - matrix2: List[List[float]] = [[] for _ in range(K)] + matrix: list[list[int]] = [[] for _ in range(K)] + matrix2: list[list[float]] = [[] for _ in range(K)] # Populate matrices with species ids and coefficients for CLs for i in range(K - 1): for j in range(len(cls_species_idxs[i])): @@ -508,7 +508,7 @@ def _is_linearly_dependent( if pivots[order[j + 1]] == pivots[order[j]] != _MAX: k1 = order[j + 1] k2 = order[j] - column: List[float] = [0] * num_species + column: list[float] = [0] * num_species g = matrix2[k2][0] / matrix2[k1][0] for i in range(1, len(matrix[k1])): column[matrix[k1][i]] = matrix2[k1][i] * g @@ -540,7 +540,7 @@ def _monte_carlo( initial_temperature: float = 1, cool_rate: float = 1e-3, max_iter: int = 10, -) -> Tuple[bool, int, Sequence[int]]: +) -> tuple[bool, int, Sequence[int]]: """MonteCarlo simulated annealing for finding integer MCLs Finding integer solutions for the MCLs by Monte Carlo, see step (b) in @@ -712,8 +712,8 @@ def _relax( (``False``) """ K = len(int_matched) - matrix: List[List[int]] = [[] for _ in range(K)] - matrix2: List[List[float]] = [[] for _ in range(K)] + matrix: list[list[int]] = [[] for _ in range(K)] + matrix2: list[list[float]] = [[] for _ in range(K)] i_reaction = 0 i_species = 0 for val in stoichiometric_list: @@ -767,7 +767,7 @@ def _relax( if pivots[order[j + 1]] == pivots[order[j]] != _MAX: k1 = order[j + 1] k2 = order[j] - column: List[float] = [0] * num_reactions + column: list[float] = [0] * num_reactions g = matrix2[k2][0] / matrix2[k1][0] for i in range(1, len(matrix[k1])): column[matrix[k1][i]] = matrix2[k1][i] * g @@ -807,7 +807,7 @@ def _relax( # subtract rows # matrix2[k] = matrix2[k] - matrix2[j] * matrix2[k][i] - row_k: List[float] = [0] * num_reactions + row_k: list[float] = [0] * num_reactions for a in range(len(matrix[k])): row_k[matrix[k][a]] = matrix2[k][a] for a in range(len(matrix[j])): @@ -955,14 +955,14 @@ def _reduce( k1 = order[i] for j in range(i + 1, K): k2 = order[j] - column: List[float] = [0] * num_species + 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/deps/AMICI/python/sdist/amici/conserved_quantities_rref.py b/deps/AMICI/python/sdist/amici/conserved_quantities_rref.py index 46028e94b..85dbd2b9b 100644 --- a/deps/AMICI/python/sdist/amici/conserved_quantities_rref.py +++ b/deps/AMICI/python/sdist/amici/conserved_quantities_rref.py @@ -1,12 +1,12 @@ """Find conserved quantities deterministically""" -from typing import List, 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 @@ -67,7 +67,7 @@ def _round(mat): return mat -def pivots(mat: np.array) -> List[int]: +def pivots(mat: np.array) -> list[int]: """Get indices of pivot columns in ``mat``, assumed to be in reduced row echelon form""" pivot_cols = [] diff --git a/deps/AMICI/python/sdist/amici/custom_commands.py b/deps/AMICI/python/sdist/amici/custom_commands.py index 46abfe329..36fcd1760 100644 --- a/deps/AMICI/python/sdist/amici/custom_commands.py +++ b/deps/AMICI/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/deps/AMICI/python/sdist/amici/cxxcodeprinter.py b/deps/AMICI/python/sdist/amici/cxxcodeprinter.py index e6e377b33..69060eb00 100644 --- a/deps/AMICI/python/sdist/amici/cxxcodeprinter.py +++ b/deps/AMICI/python/sdist/amici/cxxcodeprinter.py @@ -1,8 +1,10 @@ """C++ code generation""" + import itertools import os import re -from typing import Dict, Iterable, List, Optional, Tuple +from collections.abc import Sequence +from collections.abc import Iterable import sympy as sp from sympy.codegen.rewriting import Optimization, optimize @@ -47,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)) @@ -73,7 +75,7 @@ def _print_min_max(self, expr, cpp_fun: str, sympy_fun): ) if len(expr.args) == 1: return self._print(arg0) - return "%s%s(%s, %s)" % ( + return "{}{}({}, {})".format( self._ns, cpp_fun, self._print(arg0), @@ -92,7 +94,7 @@ def _print_Max(self, expr): def _get_sym_lines_array( self, equations: sp.Matrix, variable: str, indent_level: int - ) -> List[str]: + ) -> list[str]: """ Generate C++ code for assigning symbolic terms in symbols to C++ array `variable`. @@ -122,7 +124,8 @@ def _get_sym_lines_symbols( equations: sp.Matrix, variable: str, indent_level: int, - ) -> List[str]: + indices: Sequence[int] | None = None, + ) -> list[str]: """ Generate C++ code for where array elements are directly replaced with their corresponding macro symbol @@ -139,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): @@ -166,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( { @@ -178,7 +193,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, strict=True) + } def format_line(symbol: sp.Symbol): math = expr_dict[symbol] @@ -202,94 +219,12 @@ 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, strict=True + ) 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""" @@ -298,12 +233,14 @@ 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, + cases: dict[int, list[str]], + indentation_level: int | None = 0, + indentation_step: str | None = " " * 4, ): """ - Generate code for switch statement + Generate code for a C++ switch statement. + + Generate code for a C++ switch statement with a ``break`` after each case. :param condition: Condition for switch @@ -321,26 +258,120 @@ def get_switch_statement( :return: Code for switch expression as list of strings """ - lines = [] - if not cases: - return lines + return [] indent0 = indentation_level * indentation_step indent1 = (indentation_level + 1) * indentation_step indent2 = (indentation_level + 2) * indentation_step + + # try to find redundant statements and collapse those cases + # map statements to case expressions + cases_map: dict[tuple[str, ...], list[str]] = {} for expression, statements in cases.items(): if statements: - lines.extend( + statement_code = tuple( [ - f"{indent1}case {expression}:", *(f"{indent2}{statement}" for statement in statements), f"{indent2}break;", ] ) + case_code = f"{indent1}case {expression}:" + + cases_map[statement_code] = cases_map.get(statement_code, []) + [ + case_code + ] + + if not cases_map: + return [] + + return [ + f"{indent0}switch({condition}) {{", + *( + code + for codes in cases_map.items() + for code in itertools.chain.from_iterable(reversed(codes)) + ), + indent0 + "}", + ] + + +def csc_matrix( + matrix: sp.Matrix, + rownames: list[sp.Symbol], + colnames: list[sp.Symbol], + 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, + 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) - if lines: - lines.insert(0, f"{indent0}switch({condition}) {{") - lines.append(indent0 + "}") + :param colnames: + ids of the variable with respect to which the derivative is computed + (assuming matrix is the jacobian) - return lines + :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].is_zero: + 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/deps/AMICI/python/sdist/amici/de_export.py b/deps/AMICI/python/sdist/amici/de_export.py index b1fa02c42..6b1392a3d 100644 --- a/deps/AMICI/python/sdist/amici/de_export.py +++ b/deps/AMICI/python/sdist/amici/de_export.py @@ -1,2754 +1,106 @@ """ C++ Export ---------- -This module provides all necessary functionality specify an 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`. """ -import contextlib -import copy -import itertools -import logging -import os -import re -import shutil -import subprocess -import sys -from dataclasses import dataclass -from itertools import chain, starmap -from pathlib import Path -from string import Template -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Dict, - List, - Literal, - Optional, - Sequence, - Set, - Tuple, - Union, -) - -import numpy as np -import sympy as sp -from sympy.matrices.dense import MutableDenseMatrix -from sympy.matrices.immutable import ImmutableDenseMatrix - -from . import ( - __commit__, - __version__, - amiciModulePath, - amiciSrcPath, - amiciSwigPath, - splines, -) -from .constants import SymbolId -from .cxxcodeprinter import AmiciCxxCodePrinter, get_switch_statement -from .de_model import * -from .import_utils import ( - ObservableTransformation, - SBMLException, - amici_time_symbol, - generate_flux_symbol, - smart_subs_dict, - strip_pysb, - symbol_with_assumptions, - toposort_symbols, -) -from .logging import get_logger, log_execution_time, set_log_level - -if TYPE_CHECKING: - from . import sbml_import - - -# Template for model simulation main.cpp file -CXX_MAIN_TEMPLATE_FILE = os.path.join(amiciSrcPath, "main.template.cpp") -# Template for model/swig/CMakeLists.txt -SWIG_CMAKE_TEMPLATE_FILE = os.path.join( - amiciSwigPath, "CMakeLists_model.cmake" -) -# Template for model/CMakeLists.txt -MODEL_CMAKE_TEMPLATE_FILE = os.path.join( - amiciSrcPath, "CMakeLists.template.cmake" -) - -IDENTIFIER_PATTERN = re.compile(r"^[a-zA-Z_]\w*$") -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", - 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", - 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", - 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", - 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 -non_unique_id_symbols = ["x_rdata", "y"] - -# custom c++ function replacements -CUSTOM_FUNCTIONS = [ - { - "sympy": "polygamma", - "c++": "boost::math::polygamma", - "include": "#include ", - "build_hint": "Using polygamma requires libboost-math header files.", - }, - {"sympy": "Heaviside", "c++": "amici::heaviside"}, - {"sympy": "DiracDelta", "c++": "amici::dirac"}, -] - -# python log manager -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), - ) - - -# 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, -} - - -@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. - 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 _code_printer: - Code printer to generate C++ code - - :ivar _z2event: - list of event indices for each event observable - """ - - def __init__( - self, - verbose: Optional[Union[bool, int]] = False, - simplify: Optional[Callable] = _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 = [] - 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, - Union[ - sp.Matrix, - sp.SparseMatrix, - List[Union[sp.Matrix, sp.SparseMatrix]], - ], - ] = dict() - self._sparseeqs: Dict[str, Union[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._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, Union[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] = {}, - _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: Union[None, Sequence[int]] - self._w_recursion_depth: int = 0 - 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 - - 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 - - @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() - ) - - self._process_sbml_rate_of( - symbols - ) # substitute SBML-rateOf constructs - - def _process_sbml_rate_of(self, symbols) -> 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""" - nonlocal symbols - - 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 - 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 - } - ) - # 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 - } - ) - - for component in chain( - self.observables(), - self.expressions(), - 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: Optional[bool] = 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 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: - """ - Number of Events. - - :return: - number of event symbols (length of the root vector in AMICI) - """ - return len(self.sym("h")) - - 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 - ) -> Union[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 - ) -> Union[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() + self.algebraic_equations() - ) - ) - - 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) - - 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, - ) = self._code_printer.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, - ) = self._code_printer.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": - 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 == "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: Optional[bool] = False, - sign: Optional[int] = 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], - ) -> Union[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 +from __future__ import annotations +import copy +import logging +import os +import re +import shutil +from pathlib import Path +from typing import ( + TYPE_CHECKING, + Literal, +) +import sympy as sp - :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)) - - # 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] +from . import ( + __commit__, + __version__, + amiciModulePath, + amiciSrcPath, + 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.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, + get_switch_statement, +) +from .de_model import DEModel +from .de_model_components import * +from .import_utils import ( + strip_pysb, +) +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_is_zero_matrix, +) - return root_funs +if TYPE_CHECKING: + pass - 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 +# Template for model simulation main.cpp file +CXX_MAIN_TEMPLATE_FILE = os.path.join(amiciSrcPath, "main.template.cpp") +# Template for model/swig/CMakeLists.txt +SWIG_CMAKE_TEMPLATE_FILE = os.path.join( + amiciSwigPath, "CMakeLists_model.cmake" +) +# Template for model/CMakeLists.txt +MODEL_CMAKE_TEMPLATE_FILE = os.path.join( + amiciSrcPath, "CMakeLists.template.cmake" +) - :returns: - dxdt with Heaviside functions replaced by amici helper variables - """ +IDENTIFIER_PATTERN = re.compile(r"^[a-zA-Z_]\w*$") - # expanding the rhs will in general help to collect the same - # heaviside function - dt_expanded = dxdt.expand() - # 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(dt_expanded.args) - for tmp_old in 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)) +#: list of equations that have ids which may not be unique +non_unique_id_symbols = ["x_rdata", "y"] - if heavisides: - # only apply subs if necessary - for heaviside_sympy, heaviside_amici in heavisides: - dxdt = dxdt.subs(heaviside_sympy, heaviside_amici) +#: custom c++ function replacements +CUSTOM_FUNCTIONS = [ + { + "sympy": "polygamma", + "c++": "boost::math::polygamma", + "include": "#include ", + "build_hint": "Using polygamma requires libboost-math header files.", + }, + {"sympy": "Heaviside", "c++": "amici::heaviside"}, + {"sympy": "DiracDelta", "c++": "amici::dirac"}, +] - return dxdt +#: python log manager +logger = get_logger(__name__, logging.ERROR) class DEExporter: @@ -2791,6 +143,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 @@ -2806,13 +161,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. @@ -2857,17 +212,21 @@ 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") + self.model._splines, self._get_index("p") ) ) # To only generate a subset of functions, apply subselection here - self.functions: Dict[str, _FunctionInfo] = copy.deepcopy(functions) + self.functions: dict[str, _FunctionInfo] = copy.deepcopy(functions) self.allow_reinit_fixpar_initcond: bool = allow_reinit_fixpar_initcond self._build_hints = set() @@ -2891,7 +250,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: """ @@ -2942,74 +306,12 @@ 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") ) - 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 @@ -3045,7 +347,7 @@ def _generate_m_code(self) -> None: with open(compile_script, "w") as fileout: fileout.write("\n".join(lines)) - def _get_index(self, name: str) -> Dict[sp.Symbol, int]: + def _get_index(self, name: str) -> dict[sp.Symbol, int]: """ Compute indices for a symbolic array. :param name: @@ -3252,7 +554,7 @@ def _write_function_file(self, function: str) -> None: def _generate_function_index( self, function: str, indextype: Literal["colptrs", "rowvals"] - ) -> List[str]: + ) -> list[str]: """ Generate equations and C++ code for the function ``function``. @@ -3354,7 +656,7 @@ def _generate_function_index( def _get_function_body( self, function: str, equations: sp.Matrix - ) -> List[str]: + ) -> list[str]: """ Generate C++ code for body of function ``function``. @@ -3415,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( @@ -3425,7 +729,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 @@ -3433,19 +737,19 @@ 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(), " 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()) @@ -3458,7 +762,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()) @@ -3473,7 +777,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()) @@ -3483,7 +787,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()) @@ -3491,7 +795,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]) @@ -3511,26 +815,74 @@ 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( - 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.model._code_printer._get_sym_lines_array( + lines += self._code_printer._get_sym_lines_array( equations, function, 4 ) 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}}}, " @@ -3642,8 +994,9 @@ def _write_model_header_cpp(self) -> None: "NZ": self.model.num_eventobs(), "NZTRUE": self.model.num_eventobs(), "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( @@ -3671,10 +1024,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( @@ -3736,12 +1089,13 @@ def _write_model_header_cpp(self) -> None: ) ), "Z2EVENT": ", ".join(map(str, self.model._z2event)), + "STATE_INDEPENDENT_EVENTS": get_state_independent_event_intializer( + self.model.events() + ), "ID": ", ".join( - ( - str(float(isinstance(s, DifferentialState))) - for s in self.model.states() - if not s.has_conservation_law() - ) + str(float(isinstance(s, DifferentialState))) + for s in self.model.states() + if not s.has_conservation_law() ), } @@ -3789,36 +1143,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(): @@ -3867,19 +1221,21 @@ 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)) ) 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 = { @@ -3936,7 +1292,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 @@ -3971,204 +1327,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 - - :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});{ind4}\n".format( - ind4=" " * 4, - ind8=" " * 8, - maybe_return="" if func_info.return_type == "void" else "return ", - fun=fun, - name=name, - eval_signature=remove_argument_types(func_info.arguments(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 += "{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 ", - "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, @@ -4185,89 +1343,15 @@ 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 _write_gitignore(dest_dir: Path) -> None: + """Write .gitignore file. + Generate a `.gitignore` file to ignore a model directory. -def _custom_pow_eval_derivative(self, s): + :param dest_dir: + Path to the directory to write the `.gitignore` file to. """ - 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`. + dest_dir.mkdir(exist_ok=True, parents=True) - :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 + with open(dest_dir / ".gitignore", "w") as f: + f.write("**") diff --git a/deps/AMICI/python/sdist/amici/de_model.py b/deps/AMICI/python/sdist/amici/de_model.py index 77d9013ad..0e48bf3af 100644 --- a/deps/AMICI/python/sdist/amici/de_model.py +++ b/deps/AMICI/python/sdist/amici/de_model.py @@ -1,715 +1,2291 @@ -"""Objects for AMICI's internal differential equation model representation""" -import abc -import numbers -from typing import Dict, Optional, Set, SupportsFloat, Union +"""Symbolic differential equation model.""" +from __future__ import annotations + +import contextlib +import copy +import itertools +import re +from itertools import chain +from typing import TYPE_CHECKING +from collections.abc import Callable +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 ( - RESERVED_SYMBOLS, + _default_simplify, + SBMLException, + toposort_symbols, + smart_subs_dict, ObservableTransformation, - cast_to_sym, - generate_measurement_symbol, - generate_regularization_symbol, + 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)?$") + -__all__ = [ - "ConservationLaw", - "Constant", - "Event", - "Expression", - "LogLikelihoodY", - "LogLikelihoodZ", - "LogLikelihoodRZ", - "ModelQuantity", - "Observable", - "Parameter", - "SigmaY", - "SigmaZ", - "DifferentialState", - "EventObservable", - "AlgebraicState", - "AlgebraicEquation", - "State", -] - - -class ModelQuantity: +class DEModel: """ - Base class for model components + 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, - identifier: sp.Symbol, - name: str, - value: Union[SupportsFloat, numbers.Number, sp.Expr], + verbose: bool | int | None = False, + simplify: Callable | None = _default_simplify, + cache_simplify: bool = False, ): """ - Create a new ModelQuantity instance. + 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"), strict=True) + ) + 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"), strict=True)) + ) + 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() :, :], + strict=True, + ) + ) + ) + w_sorted = ( + dict( + zip( + self.sym("w")[: self.num_cons_law(), :], + self.eq("w")[: self.num_cons_law(), :], + strict=True, + ) + ) + | 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) - :param identifier: - unique identifier of the quantity + 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 name: - individual name of the quantity (does not need to be unique) + :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." + ) - :param value: - either formula, numeric value or initial value - """ + state_id = self._differential_states[ix].get_id() - if not isinstance(identifier, sp.Symbol): - raise TypeError( - f"identifier must be sympy.Symbol, was " f"{type(identifier)}" + # \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] + ) - if str(identifier) in RESERVED_SYMBOLS or ( - hasattr(identifier, "name") and identifier.name in RESERVED_SYMBOLS - ): - raise ValueError( - f'Cannot add model quantity with name "{name}", ' - f"please rename." - ) - self._identifier: sp.Symbol = identifier + # x_j = T/a_j - \sum_{i≠j}(a_i * x_i)/a_j + state_expr = total_abundance - target_expression - if not isinstance(name, str): - raise TypeError(f"name must be str, was {type(name)}") + # T/a_j = \sum_{i≠j}(a_i * x_i)/a_j + x_j + abundance_expr = target_expression + state_id - self._name: str = name + self.add_component( + Expression(state_id, str(state_id), state_expr), insert_first=True + ) - self._value: sp.Expr = cast_to_sym(value, "value") + cl = ConservationLaw( + total_abundance, + f"total_{state_id}", + abundance_expr, + coefficients, + state_id, + ) - def __repr__(self) -> str: - """ - Representation of the ModelQuantity object + self.add_component(cl) + self._differential_states[ix].set_conservation_law(cl) - :return: - string representation of the ModelQuantity + 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()``. """ - return str(self._identifier) + self._splines.append(spline) + self.add_component( + Expression( + identifier=spline.sbml_id, + name=str(spline.sbml_id), + value=spline_expr, + ) + ) - def get_id(self) -> sp.Symbol: + def get_observable_transformations(self) -> list[ObservableTransformation]: """ - ModelQuantity identifier + List of observable transformations :return: - identifier of the ModelQuantity + list of transformations """ - return self._identifier + return [obs.trafo for obs in self._observables] - def get_name(self) -> str: + def num_states_rdata(self) -> int: """ - ModelQuantity name + Number of states. :return: - name of the ModelQuantity + number of state variable symbols """ - return self._name + return len(self.sym("x_rdata")) - def get_val(self) -> sp.Expr: + def num_states_solver(self) -> int: """ - ModelQuantity value + Number of states after applying conservation laws. :return: - value of the ModelQuantity + number of state variable symbols """ - return self._value + return len(self.sym("x")) - def set_val(self, val: sp.Expr): + def num_cons_law(self) -> int: """ - Set ModelQuantity value + Number of conservation laws. :return: - value of the ModelQuantity + number of conservation laws """ - self._value = cast_to_sym(val, "value") - + return self.num_states_rdata() - self.num_states_solver() -class ConservationLaw(ModelQuantity): - """ - A conservation law defines the absolute the total amount of a - (weighted) sum of states - - """ + def num_state_reinits(self) -> int: + """ + Number of solver states which would be reinitialized after + preequilibration - def __init__( - self, - identifier: sp.Symbol, - name: str, - value: sp.Expr, - coefficients: Dict[sp.Symbol, sp.Expr], - state_id: sp.Symbol, - ): + :return: + number of state variable symbols with reinitialization """ - Create a new ConservationLaw instance. + reinit_states = self.eq("x0_fixedParameters") + solver_states = self.eq("x_solver") + return sum(ix in solver_states for ix in reinit_states) - :param identifier: - unique identifier of the ConservationLaw + def num_obs(self) -> int: + """ + Number of Observables. - :param name: - individual name of the ConservationLaw (does not need to be - unique) + :return: + number of observable symbols + """ + return len(self.sym("y")) - :param value: formula (sum of states) + def num_eventobs(self) -> int: + """ + Number of Event Observables. - :param coefficients: - coefficients of the states in the sum + :return: + number of event observable symbols + """ + return len(self.sym("z")) - :param state_id: - identifier of the state that this conservation law replaces + def num_const(self) -> int: """ - self._state_expr: sp.Symbol = identifier - (value - state_id) - self._coefficients: Dict[sp.Symbol, sp.Expr] = coefficients - self._ncoeff: sp.Expr = coefficients[state_id] - super(ConservationLaw, self).__init__(identifier, name, value) + Number of Constants. - def get_ncoeff(self, state_id) -> Union[sp.Expr, int, float]: + :return: + number of constant symbols """ - 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 - replaced by this conservation law. This can be used to compute both - dtotal_cl/dx_rdata (=ncoeff) and dx_rdata/dx_solver (=-ncoeff). + return len(self.sym("k")) - :param state_id: - identifier of the state + def num_par(self) -> int: + """ + Number of Parameters. - :return: normalized coefficent of the state + :return: + number of parameter symbols """ - return self._coefficients.get(state_id, 0.0) / self._ncoeff + return len(self.sym("p")) - def get_x_rdata(self): + def num_expr(self) -> int: """ - Returns the expression that allows computation of x_rdata for the state - that this conservation law replaces. + Number of Expressions. - :return: x_rdata expression + :return: + number of expression symbols """ - return self._state_expr + return len(self.sym("w")) + def num_events(self) -> int: + """ + Total number of Events (those for which root-functions are added and those without). -class AlgebraicEquation(ModelQuantity): - """ - An AlgebraicEquation defines an algebraic equation. - """ + :return: + number of events + """ + return len(self.sym("h")) - def __init__(self, identifier: str, value: sp.Expr): + def num_events_solver(self) -> int: """ - Create a new AlgebraicEquation instance. + Number of Events. - :param value: - formula of the algebraic equation, solution is given by - ``formula == 0`` + :return: + number of event symbols (length of the root vector in AMICI) """ - super(AlgebraicEquation, self).__init__( - sp.Symbol(identifier), identifier, value + return sum( + not event.triggers_at_fixed_timepoint() for event in self.events() ) - def get_free_symbols(self): - return self._value.free_symbols - - def __repr__(self): - return str(self._value) + 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 -class State(ModelQuantity): - """ - Base class for differential and algebraic model states - """ + :return: + matrix of symbolic identifiers + """ + if name not in self._syms: + self._generate_symbol(name) - _conservation_law: Optional[ConservationLaw] = None + return self._syms[name] - def get_x_rdata(self): + def sparsesym(self, name: str, force_generate: bool = True) -> list[str]: """ - Returns the expression that allows computation of x_rdata for this - state, accounting for conservation laws. + Returns (and constructs if necessary) the sparsified identifiers for + a sparsified symbolic variable. - :return: x_rdata expression - """ - if self._conservation_law is None: - return self.get_id() - else: - return self._conservation_law.get_x_rdata() + :param name: + name of the symbolic variable - def get_dx_rdata_dx_solver(self, state_id): - """ - Returns the expression that allows computation of - ``dx_rdata_dx_solver`` for this state, accounting for conservation - laws. + :param force_generate: + whether the symbols should be generated if not available - :return: dx_rdata_dx_solver expression + :return: + linearized Matrix containing the symbolic identifiers """ - if self._conservation_law is None: - return sp.Integer(self._identifier == state_id) - else: - return -self._conservation_law.get_ncoeff(state_id) + 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, []) - @abc.abstractmethod - def has_conservation_law(self): + def eq(self, name: str) -> sp.Matrix: """ - Checks whether this state has a conservation law assigned. + Returns (and constructs if necessary) the formulas for a symbolic + entity. - :return: True if assigned, False otherwise - """ - ... + :param name: + name of the symbolic variable + :return: + matrix of symbolic formulas + """ -class AlgebraicState(State): - """ - An AlgebraicState defines an entity that is algebraically determined - """ + if name not in self._eqs: + dec = log_execution_time(f"computing {name}", logger) + dec(self._compute_equation)(name) + return self._eqs[name] - def __init__(self, identifier: sp.Symbol, name: str, init: sp.Expr): + def sparseeq(self, name) -> sp.Matrix: """ - Create a new AlgebraicState instance. - - :param identifier: - unique identifier of the AlgebraicState + Returns (and constructs if necessary) the sparsified formulas for a + sparsified symbolic variable. :param name: - individual name of the AlgebraicState (does not need to be unique) - - :param init: - initial value of the AlgebraicState - """ - super(AlgebraicState, self).__init__(identifier, name, init) + name of the symbolic variable - def has_conservation_law(self): + :return: + linearized matrix containing the symbolic formulas """ - Checks whether this state has a conservation law assigned. + 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] - :return: True if assigned, False otherwise + def colptrs(self, name: str) -> list[sp.Number] | list[list[sp.Number]]: """ - return False + Returns (and constructs if necessary) the column pointers for + a sparsified symbolic variable. - def get_free_symbols(self): - return self._value.free_symbols + :param name: + name of the symbolic variable - def get_x_rdata(self): - return self._identifier + :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. -class DifferentialState(State): - """ - A State variable defines an entity that evolves with time according to - the provided time derivative, abbreviated by ``x``. + :param name: + name of the symbolic variable - :ivar _conservation_law: - algebraic formula that allows computation of this - state according to a conservation law + :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] - :ivar _dt: - algebraic formula that defines the temporal derivative of this state + 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 - def __init__( - self, identifier: sp.Symbol, name: str, init: sp.Expr, dt: sp.Expr - ): + :return: + list containing the numeric values """ - Create a new State instance. Extends :meth:`ModelQuantity.__init__` - by ``dt`` + if name not in self._vals: + self._generate_value(name) + return self._vals[name] - :param identifier: - unique identifier of the state + def name(self, name: str) -> list[str]: + """ + Returns (and constructs if necessary) the names of a symbolic + variable :param name: - individual name of the state (does not need to be unique) - - :param init: - initial value + name of the symbolic variable - :param dt: - time derivative + :return: + list of names """ - super(DifferentialState, self).__init__(identifier, name, init) - self._dt = cast_to_sym(dt, "dt") - self._conservation_law: Union[ConservationLaw, None] = None + if name not in self._names: + self._generate_name(name) + return self._names[name] - def set_conservation_law(self, law: ConservationLaw) -> None: + def free_symbols(self) -> set[sp.Basic]: + """ + Returns list of free symbols that appear in RHS and initial + conditions. """ - Sets the conservation law of a state. + return set( + chain.from_iterable( + state.get_free_symbols() for state in self.states() + ) + ) - If a conservation law is set, the respective state will be replaced by - an algebraic formula according to the respective conservation law. + 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], + ) + ) - :param law: - linear sum of states that if added to this state remain - constant over time - """ - if not isinstance(law, ConservationLaw): - raise TypeError( - f"conservation law must have type ConservationLaw" - f", was {type(law)}" + 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() ) - self._conservation_law = law + 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, strict=True) + ) + # 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 set_dt(self, dt: sp.Expr) -> None: + def _generate_symbol(self, name: str) -> None: """ - Sets the time derivative + Generates the symbolic identifiers for a symbolic variable - :param dt: - time derivative + :param name: + name of the symbolic variable """ - self._dt = cast_to_sym(dt, "dt") + 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 get_dt(self) -> sp.Expr: - """ - Gets the time derivative + 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), + ) + ) - :return: - time derivative + def get_appearance_counts(self, idxs: list[int]) -> list[int]: """ - return self._dt + Counts how often a state appears in the time derivative of + another state and expressions for a subset of states - def get_free_symbols(self) -> Set[sp.Basic]: - """ - Gets the set of free symbols in time derivative and initial conditions + :param idxs: + list of state indices for which counts are to be computed :return: - free symbols + list of counts for the states ordered according to the provided + indices """ - return self._dt.free_symbols.union(self._value.free_symbols) + free_symbols_dt = list( + itertools.chain.from_iterable( + [str(symbol) for symbol in state.get_dt().free_symbols] + for state in self.states() + ) + ) - def has_conservation_law(self): - """ - Checks whether this state has a conservation law assigned. + free_symbols_expr = list( + itertools.chain.from_iterable( + [str(symbol) for symbol in expr.get_val().free_symbols] + for expr in self._expressions + ) + ) - :return: True if assigned, False otherwise + 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: """ - return self._conservation_law is not 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, + ) -class Observable(ModelQuantity): - """ - An Observable links model simulations to experimental measurements, - abbreviated by ``y``. + 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 - :ivar _measurement_symbol: - sympy symbol used in the objective function to represent - measurements to this observable + def _compute_equation(self, name: str) -> None: + """ + Computes the symbolic formula for a symbolic variable - :ivar trafo: - observable transformation, only applies when evaluating objective - function or residuals - """ + :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]) - _measurement_symbol: Union[sp.Symbol, None] = None + if name in self._equation_prototype: + self._equation_from_components( + name, self._equation_prototype[name]() + ) - def __init__( - self, - identifier: sp.Symbol, - name: str, - value: sp.Expr, - measurement_symbol: Optional[sp.Symbol] = None, - transformation: Optional[ - ObservableTransformation - ] = ObservableTransformation.LIN, - ): - """ - Create a new Observable instance. + 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"), + # dx contains extra elements for algebraic states + strict=False, + ) + ] + + [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()] + ) - :param identifier: - unique identifier of the Observable + elif name == "x_solver": + self._eqs[name] = sp.Matrix( + [ + state.get_id() + for state in self.states() + if not state.has_conservation_law() + ] + ) - :param name: - individual name of the Observable (does not need to be unique) + 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() + ] + ) - :param value: - formula + 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") + ) - :param transformation: - observable transformation, only applies when evaluating objective - function or residuals - """ - super(Observable, self).__init__(identifier, name, value) - self._measurement_symbol = measurement_symbol - self._regularization_symbol = None - self.trafo = transformation + dx0_fixed_parametersdx = smart_jacobian( + self.eq("x0_fixedParameters"), self.sym("x") + ) - def get_measurement_symbol(self) -> sp.Symbol: - if self._measurement_symbol is None: - self._measurement_symbol = generate_measurement_symbol( - self.get_id() + 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] ) - return self._measurement_symbol + 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 + ] + ) - def get_regularization_symbol(self) -> sp.Symbol: - if self._regularization_symbol is None: - self._regularization_symbol = generate_regularization_symbol( - self.get_id() + elif name == "dtcldx": + # this is always zero + self._eqs[name] = sp.zeros( + self.num_cons_law(), self.num_states_solver() ) - return self._regularization_symbol + 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) -class EventObservable(Observable): - """ - An Event Observable links model simulations to event related experimental - measurements, abbreviated by ``z``. + elif name == "dxdotdp_explicit": + # force symbols + self._derivative("xdot", "p", name=name) - :ivar _event: - symbolic event identifier - """ + elif name == "spl": + self._eqs[name] = self.sym(name) - def __init__( - self, - identifier: sp.Symbol, - name: str, - value: sp.Expr, - event: sp.Symbol, - measurement_symbol: Optional[sp.Symbol] = None, - transformation: Optional[ObservableTransformation] = "lin", - ): - """ - Create a new EventObservable instance. + elif name == "sspl": + # force symbols + self._eqs[name] = self.sym(name) - :param identifier: - See :py:meth:`Observable.__init__`. + elif name == "spline_values": + # force symbols + self._eqs[name] = sp.Matrix( + [y for spline in self._splines for y in spline.values_at_nodes] + ) - :param name: - See :py:meth:`Observable.__init__`. + 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 + ) + ] + ) - :param value: - See :py:meth:`Observable.__init__`. + elif name == "drootdt": + self._eqs[name] = smart_jacobian(self.eq("root"), time_symbol) + + elif name == "drootdt_total": + self._eqs[name] = self.eq("drootdt") + # 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 + # 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, strict=True + ): + 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) + ) - :param transformation: - See :py:meth:`Observable.__init__`. + elif match_deriv: + self._derivative(match_deriv[1], match_deriv[2], name) - :param event: - Symbolic identifier of the corresponding event. - """ - super(EventObservable, self).__init__( - identifier, name, value, measurement_symbol, transformation - ) - self._event: sp.Symbol = event + 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], 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 + 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 - def get_event(self) -> sp.Symbol: + :return: + list of names """ - Get the symbolic identifier of the corresponding event. + return list(self._syms.keys()) - :return: symbolic identifier + def _derivative(self, eq: str, var: str, name: str = None) -> None: """ - return self._event + Creates a new symbolic variable according to a derivative + :param eq: + name of the symbolic variable that defines the formula -class Sigma(ModelQuantity): - """ - A Standard Deviation Sigma rescales the distance between simulations - and measurements when computing residuals or objective functions, - abbreviated by ``sigma{y,z}``. - """ + :param var: + name of the symbolic variable that defines the identifiers + with respect to which the derivatives are to be computed - def __init__(self, identifier: sp.Symbol, name: str, value: sp.Expr): + :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: """ - Create a new Standard Deviation instance. + Creates a new symbolic variable according to a total derivative + using the chain rule - :param identifier: - unique identifier of the Standard Deviation + :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: - individual name of the Standard Deviation (does not need to - be unique) + name of function for which the signature should be checked - :param value: - formula - """ - if self.__class__.__name__ == "Sigma": - raise RuntimeError( - "This class is meant to be sub-classed, not used directly." - ) - super(Sigma, self).__init__(identifier, name, value) + :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) -class SigmaY(Sigma): - """ - Standard deviation for observables - """ + 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}`` -class SigmaZ(Sigma): - """ - Standard deviation for event observables - """ + :param x: + name of the symbolic variable that defines the first factor + :param y: + name of the symbolic variable that defines the second factor -class Expression(ModelQuantity): - """ - An Expression is a recurring elements in symbolic formulas. Specifying - this may yield more compact expression which may lead to substantially - shorter model compilation times, but may also reduce model simulation time. - Abbreviated by ``w``. - """ + :param transpose_x: + indicates whether the first factor should be + transposed before multiplication - def __init__(self, identifier: sp.Symbol, name: str, value: sp.Expr): + :param sign: + defines the sign of the product, should be +1 or -1 """ - Create a new Expression instance. + if sign not in [-1, 1]: + raise TypeError(f"sign must be +1 or -1, was {sign}") - :param identifier: - unique identifier of the Expression + variables = { + varname: self.sym(varname) + if var_in_function_signature(name, varname, self.is_ode()) + else self.eq(varname) + for varname in [x, y] + } - :param name: - individual name of the Expression (does not need to be unique) - - :param value: - formula - """ - super(Expression, self).__init__(identifier, name, value) + xx = variables[x].transpose() if transpose_x else variables[x] + yy = variables[y] + self._eqs[name] = sign * smart_multiply(xx, yy) -class Parameter(ModelQuantity): - """ - A Parameter is a free variable in the model with respect to which - sensitivities may be computed, abbreviated by ``p``. - """ - - def __init__( - self, identifier: sp.Symbol, name: str, value: numbers.Number - ): + def _equation_from_components( + self, name: str, components: list[ModelQuantity] + ) -> None: """ - Create a new Expression instance. - - :param identifier: - unique identifier of the Parameter + Generates the formulas of a symbolic variable from the attributes :param name: - individual name of the Parameter (does not need to be - unique) + name of resulting symbolic variable - :param value: - numeric value + :param component: + name of the attribute """ - super(Parameter, self).__init__(identifier, name, value) - + self._eqs[name] = sp.Matrix([comp.get_val() for comp in components]) -class Constant(ModelQuantity): - """ - A Constant is a fixed variable in the model with respect to which - sensitivities cannot be computed, abbreviated by ``k``. - """ + def get_conservation_laws(self) -> list[tuple[sp.Symbol, sp.Expr]]: + """Returns a list of states with conservation law set - def __init__( - self, identifier: sp.Symbol, name: str, value: numbers.Number - ): + :return: + list of state identifiers """ - Create a new Expression instance. + return [ + (state.get_id(), state.get_x_rdata()) + for state in self.states() + if state.has_conservation_law() + ] - :param identifier: - unique identifier of the Constant + def _generate_value(self, name: str) -> None: + """ + Generates the numeric values of a symbolic variable from value + prototypes :param name: - individual name of the Constant (does not need to be unique) - - :param value: - numeric value + name of resulting symbolic variable """ - super(Constant, self).__init__(identifier, name, value) - + if name in self._value_prototype: + components = self._value_prototype[name]() + else: + raise ValueError(f"No values for {name}") -class LogLikelihood(ModelQuantity): - """ - A LogLikelihood defines the distance between measurements and - experiments for a particular observable. The final LogLikelihood value - in the simulation will be the sum of all specified LogLikelihood - instances evaluated at all timepoints, abbreviated by ``Jy``. - """ + self._vals[name] = [comp.get_val() for comp in components] - def __init__(self, identifier: sp.Symbol, name: str, value: sp.Expr): + def _generate_name(self, name: str) -> None: """ - Create a new Expression instance. - - :param identifier: - unique identifier of the LogLikelihood + Generates the names of a symbolic variable from variable prototypes or + equation prototypes :param name: - individual name of the LogLikelihood (does not need to be - unique) - - :param value: - formula + name of resulting symbolic variable """ - if self.__class__.__name__ == "LogLikelihood": - raise RuntimeError( - "This class is meant to be sub-classed, not used directly." - ) - super(LogLikelihood, self).__init__(identifier, name, value) + 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] -class LogLikelihoodY(LogLikelihood): - """ - Loglikelihood for observables - """ + 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 -class LogLikelihoodZ(LogLikelihood): - """ - Loglikelihood for event observables - """ + :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 -class LogLikelihoodRZ(LogLikelihood): - """ - Loglikelihood for event observables regularization - """ + :param ix: + state index + :return: + boolean indicating if conservation_law is not None + """ + return self.states()[ix].has_conservation_law() -class Event(ModelQuantity): - """ - An Event defines either a SBML event or a root of the argument of a - Heaviside function. The Heaviside functions will be tracked via the - vector ``h`` during simulation and are needed to inform the solver - about a discontinuity in either the right-hand side or the states - themselves, causing a reinitialization of the solver. - """ + def get_solver_indices(self) -> dict[int, int]: + """ + Returns a mapping that maps rdata species indices to solver indices - def __init__( - self, - identifier: sp.Symbol, - name: str, - value: sp.Expr, - state_update: Union[sp.Expr, None], - initial_value: Optional[bool] = True, - ): + :return: + dictionary mapping rdata species indices to solver indices """ - Create a new Event instance. + 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 - :param identifier: - unique identifier of the Event + def state_is_constant(self, ix: int) -> bool: + """ + Checks whether the temporal derivative of the state is zero - :param name: - individual name of the Event (does not need to be unique) + :param ix: + state index - :param value: - formula for the root / trigger function + :return: + boolean indicating if constant over time + """ + state = self.states()[ix] + if isinstance(state, AlgebraicState): + return False - :param state_update: - formula for the bolus function (None for Heaviside functions, - zero vector for events without bolus) + return state.get_dt().is_zero - :param initial_value: - initial boolean value of the trigger function at t0. If set to - `False`, events may trigger at ``t==t0``, otherwise not. + def conservation_law_has_multispecies(self, tcl: ConservationLaw) -> bool: """ - super(Event, self).__init__(identifier, name, value) - # add the Event specific components - self._state_update = state_update - self._initial_value = initial_value + Checks whether a conservation law has multiple species or it just + defines one constant species - def get_initial_value(self) -> bool: - """ - Return the initial value for the root function. + :param tcl: + conservation law :return: - initial value formula + boolean indicating if conservation_law is not None """ - return self._initial_value + state_set = set(self.sym("x_rdata")) + n_species = len(state_set.intersection(tcl.get_val().free_symbols)) + return n_species > 1 - def __eq__(self, other): - """ - Check equality of events at the level of trigger/root functions, as we - need to collect unique root functions for ``roots.cpp`` + 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. """ - return self.get_val() == other.get_val() and ( - self.get_initial_value() == other.get_initial_value() + # `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()).is_zero: + 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], + strict=True, + ) + ) + ) + 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/deps/AMICI/python/sdist/amici/de_model_components.py b/deps/AMICI/python/sdist/amici/de_model_components.py new file mode 100644 index 000000000..bc93f44b8 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/de_model_components.py @@ -0,0 +1,754 @@ +"""Objects for AMICI's internal differential equation model representation""" + +import abc +import numbers +from typing import SupportsFloat + +import sympy as sp + +from .import_utils import ( + RESERVED_SYMBOLS, + ObservableTransformation, + amici_time_symbol, + cast_to_sym, + generate_measurement_symbol, + generate_regularization_symbol, +) +from .constants import SymbolId + +__all__ = [ + "ConservationLaw", + "Constant", + "Event", + "Expression", + "LogLikelihoodY", + "LogLikelihoodZ", + "LogLikelihoodRZ", + "ModelQuantity", + "Observable", + "Parameter", + "SigmaY", + "SigmaZ", + "DifferentialState", + "EventObservable", + "AlgebraicState", + "AlgebraicEquation", + "State", +] + + +class ModelQuantity: + """ + Base class for model components + """ + + def __init__( + self, + identifier: sp.Symbol, + name: str, + value: SupportsFloat | numbers.Number | sp.Expr, + ): + """ + Create a new ModelQuantity instance. + + :param identifier: + unique identifier of the quantity + + :param name: + individual name of the quantity (does not need to be unique) + + :param value: + either formula, numeric value or initial value + """ + + if not isinstance(identifier, sp.Symbol): + raise TypeError( + f"identifier must be sympy.Symbol, was " f"{type(identifier)}" + ) + + if str(identifier) in RESERVED_SYMBOLS or ( + hasattr(identifier, "name") and identifier.name in RESERVED_SYMBOLS + ): + raise ValueError( + f'Cannot add model quantity with name "{name}", ' + f"please rename." + ) + self._identifier: sp.Symbol = identifier + + if not isinstance(name, str): + raise TypeError(f"name must be str, was {type(name)}") + + self._name: str = name + + self._value: sp.Expr = cast_to_sym(value, "value") + + def __repr__(self) -> str: + """ + Representation of the ModelQuantity object + + :return: + string representation of the ModelQuantity + """ + return str(self._identifier) + + def get_id(self) -> sp.Symbol: + """ + ModelQuantity identifier + + :return: + identifier of the ModelQuantity + """ + return self._identifier + + def get_name(self) -> str: + """ + ModelQuantity name + + :return: + name of the ModelQuantity + """ + return self._name + + def get_val(self) -> sp.Expr: + """ + ModelQuantity value + + :return: + value of the ModelQuantity + """ + return self._value + + def set_val(self, val: sp.Expr): + """ + Set ModelQuantity value + + :return: + value of the ModelQuantity + """ + self._value = cast_to_sym(val, "value") + + +class ConservationLaw(ModelQuantity): + """ + A conservation law defines the absolute the total amount of a + (weighted) sum of states + + """ + + def __init__( + self, + identifier: sp.Symbol, + name: str, + value: sp.Expr, + coefficients: dict[sp.Symbol, sp.Expr], + state_id: sp.Symbol, + ): + """ + Create a new ConservationLaw instance. + + :param identifier: + unique identifier of the ConservationLaw + + :param name: + individual name of the ConservationLaw (does not need to be + unique) + + :param value: formula (sum of states) + + :param coefficients: + coefficients of the states in the sum + + :param state_id: + identifier of the state that this conservation law replaces + """ + self._state_expr: sp.Symbol = identifier - (value - state_id) + self._coefficients: dict[sp.Symbol, sp.Expr] = coefficients + self._ncoeff: sp.Expr = coefficients[state_id] + super().__init__(identifier, name, value) + + 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 + replaced by this conservation law. This can be used to compute both + dtotal_cl/dx_rdata (=ncoeff) and dx_rdata/dx_solver (=-ncoeff). + + :param state_id: + identifier of the state + + :return: normalized coefficent of the state + """ + return self._coefficients.get(state_id, 0.0) / self._ncoeff + + def get_x_rdata(self): + """ + Returns the expression that allows computation of x_rdata for the state + that this conservation law replaces. + + :return: x_rdata expression + """ + return self._state_expr + + +class AlgebraicEquation(ModelQuantity): + """ + An AlgebraicEquation defines an algebraic equation. + """ + + def __init__(self, identifier: str, value: sp.Expr): + """ + Create a new AlgebraicEquation instance. + + :param value: + Formula of the algebraic equation, the solution is given by + ``formula == 0`` + """ + super().__init__(sp.Symbol(identifier), identifier, value) + + def get_free_symbols(self): + return self._value.free_symbols + + def __repr__(self): + return str(self._value) + + +class State(ModelQuantity): + """ + Base class for differential and algebraic model states + """ + + _conservation_law: ConservationLaw | None = None + + def get_x_rdata(self): + """ + Returns the expression that allows computation of x_rdata for this + state, accounting for conservation laws. + + :return: x_rdata expression + """ + if self._conservation_law is None: + return self.get_id() + else: + return self._conservation_law.get_x_rdata() + + def get_dx_rdata_dx_solver(self, state_id): + """ + Returns the expression that allows computation of + ``dx_rdata_dx_solver`` for this state, accounting for conservation + laws. + + :return: dx_rdata_dx_solver expression + """ + if self._conservation_law is None: + return sp.Integer(self._identifier == state_id) + else: + return -self._conservation_law.get_ncoeff(state_id) + + @abc.abstractmethod + def has_conservation_law(self): + """ + Checks whether this state has a conservation law assigned. + + :return: True if assigned, False otherwise + """ + ... + + +class AlgebraicState(State): + """ + An AlgebraicState defines an entity that is algebraically determined + """ + + def __init__(self, identifier: sp.Symbol, name: str, init: sp.Expr): + """ + Create a new AlgebraicState instance. + + :param identifier: + unique identifier of the AlgebraicState + + :param name: + individual name of the AlgebraicState (does not need to be unique) + + :param init: + initial value of the AlgebraicState + """ + super().__init__(identifier, name, init) + + def has_conservation_law(self): + """ + Checks whether this state has a conservation law assigned. + + :return: True if assigned, False otherwise + """ + return False + + def get_free_symbols(self): + return self._value.free_symbols + + def get_x_rdata(self): + return self._identifier + + +class DifferentialState(State): + """ + A State variable defines an entity that evolves with time according to + the provided time derivative, abbreviated by ``x``. + + :ivar _conservation_law: + algebraic formula that allows computation of this + state according to a conservation law + + :ivar _dt: + algebraic formula that defines the temporal derivative of this state + + """ + + def __init__( + self, identifier: sp.Symbol, name: str, init: sp.Expr, dt: sp.Expr + ): + """ + Create a new State instance. Extends :meth:`ModelQuantity.__init__` + by ``dt`` + + :param identifier: + unique identifier of the state + + :param name: + individual name of the state (does not need to be unique) + + :param init: + initial value + + :param dt: + time derivative + """ + super().__init__(identifier, name, init) + self._dt = cast_to_sym(dt, "dt") + self._conservation_law: ConservationLaw | None = None + + def set_conservation_law(self, law: ConservationLaw) -> None: + """ + Sets the conservation law of a state. + + If a conservation law is set, the respective state will be replaced by + an algebraic formula according to the respective conservation law. + + :param law: + linear sum of states that if added to this state remain + constant over time + """ + if not isinstance(law, ConservationLaw): + raise TypeError( + f"conservation law must have type ConservationLaw" + f", was {type(law)}" + ) + + self._conservation_law = law + + def set_dt(self, dt: sp.Expr) -> None: + """ + Sets the time derivative + + :param dt: + time derivative + """ + self._dt = cast_to_sym(dt, "dt") + + def get_dt(self) -> sp.Expr: + """ + Gets the time derivative + + :return: + time derivative + """ + return self._dt + + def get_free_symbols(self) -> set[sp.Basic]: + """ + Gets the set of free symbols in time derivative and initial conditions + + :return: + free symbols + """ + return self._dt.free_symbols.union(self._value.free_symbols) + + def has_conservation_law(self): + """ + Checks whether this state has a conservation law assigned. + + :return: True if assigned, False otherwise + """ + return self._conservation_law is not None + + +class Observable(ModelQuantity): + """ + An Observable links model simulations to experimental measurements, + abbreviated by ``y``. + + :ivar _measurement_symbol: + sympy symbol used in the objective function to represent + measurements to this observable + + :ivar trafo: + observable transformation, only applies when evaluating objective + function or residuals + """ + + _measurement_symbol: sp.Symbol | None = None + + def __init__( + self, + identifier: sp.Symbol, + name: str, + value: sp.Expr, + measurement_symbol: sp.Symbol | None = None, + transformation: None + | (ObservableTransformation) = ObservableTransformation.LIN, + ): + """ + Create a new Observable instance. + + :param identifier: + unique identifier of the Observable + + :param name: + individual name of the Observable (does not need to be unique) + + :param value: + formula + + :param transformation: + observable transformation, only applies when evaluating objective + function or residuals + """ + super().__init__(identifier, name, value) + self._measurement_symbol = measurement_symbol + self._regularization_symbol = None + self.trafo = transformation + + def get_measurement_symbol(self) -> sp.Symbol: + if self._measurement_symbol is None: + self._measurement_symbol = generate_measurement_symbol( + self.get_id() + ) + + return self._measurement_symbol + + def get_regularization_symbol(self) -> sp.Symbol: + if self._regularization_symbol is None: + self._regularization_symbol = generate_regularization_symbol( + self.get_id() + ) + + return self._regularization_symbol + + +class EventObservable(Observable): + """ + An Event Observable links model simulations to event related experimental + measurements, abbreviated by ``z``. + + :ivar _event: + symbolic event identifier + """ + + def __init__( + self, + identifier: sp.Symbol, + name: str, + value: sp.Expr, + event: sp.Symbol, + measurement_symbol: sp.Symbol | None = None, + transformation: ObservableTransformation | None = "lin", + ): + """ + Create a new EventObservable instance. + + :param identifier: + See :py:meth:`Observable.__init__`. + + :param name: + See :py:meth:`Observable.__init__`. + + :param value: + See :py:meth:`Observable.__init__`. + + :param transformation: + See :py:meth:`Observable.__init__`. + + :param event: + Symbolic identifier of the corresponding event. + """ + super().__init__( + identifier, name, value, measurement_symbol, transformation + ) + self._event: sp.Symbol = event + + def get_event(self) -> sp.Symbol: + """ + Get the symbolic identifier of the corresponding event. + + :return: symbolic identifier + """ + return self._event + + +class Sigma(ModelQuantity): + """ + A Standard Deviation Sigma rescales the distance between simulations + and measurements when computing residuals or objective functions, + abbreviated by ``sigma{y,z}``. + """ + + def __init__(self, identifier: sp.Symbol, name: str, value: sp.Expr): + """ + Create a new Standard Deviation instance. + + :param identifier: + unique identifier of the Standard Deviation + + :param name: + individual name of the Standard Deviation (does not need to + be unique) + + :param value: + formula + """ + if self.__class__.__name__ == "Sigma": + raise RuntimeError( + "This class is meant to be sub-classed, not used directly." + ) + super().__init__(identifier, name, value) + + +class SigmaY(Sigma): + """ + Standard deviation for observables + """ + + +class SigmaZ(Sigma): + """ + Standard deviation for event observables + """ + + +class Expression(ModelQuantity): + """ + An Expression is a recurring elements in symbolic formulas. Specifying + this may yield more compact expression which may lead to substantially + shorter model compilation times, but may also reduce model simulation time. + Abbreviated by ``w``. + """ + + def __init__(self, identifier: sp.Symbol, name: str, value: sp.Expr): + """ + Create a new Expression instance. + + :param identifier: + unique identifier of the Expression + + :param name: + individual name of the Expression (does not need to be unique) + + :param value: + formula + """ + super().__init__(identifier, name, value) + + +class Parameter(ModelQuantity): + """ + A Parameter is a free variable in the model with respect to which + sensitivities may be computed, abbreviated by ``p``. + """ + + def __init__( + self, identifier: sp.Symbol, name: str, value: numbers.Number + ): + """ + Create a new Expression instance. + + :param identifier: + unique identifier of the Parameter + + :param name: + individual name of the Parameter (does not need to be + unique) + + :param value: + numeric value + """ + super().__init__(identifier, name, value) + + +class Constant(ModelQuantity): + """ + A Constant is a fixed variable in the model with respect to which + sensitivities cannot be computed, abbreviated by ``k``. + """ + + def __init__( + self, identifier: sp.Symbol, name: str, value: numbers.Number + ): + """ + Create a new Expression instance. + + :param identifier: + unique identifier of the Constant + + :param name: + individual name of the Constant (does not need to be unique) + + :param value: + numeric value + """ + super().__init__(identifier, name, value) + + +class LogLikelihood(ModelQuantity): + """ + A LogLikelihood defines the distance between measurements and + experiments for a particular observable. The final LogLikelihood value + in the simulation will be the sum of all specified LogLikelihood + instances evaluated at all timepoints, abbreviated by ``Jy``. + """ + + def __init__(self, identifier: sp.Symbol, name: str, value: sp.Expr): + """ + Create a new Expression instance. + + :param identifier: + unique identifier of the LogLikelihood + + :param name: + individual name of the LogLikelihood (does not need to be + unique) + + :param value: + formula + """ + if self.__class__.__name__ == "LogLikelihood": + raise RuntimeError( + "This class is meant to be sub-classed, not used directly." + ) + super().__init__(identifier, name, value) + + +class LogLikelihoodY(LogLikelihood): + """ + Loglikelihood for observables + """ + + +class LogLikelihoodZ(LogLikelihood): + """ + Loglikelihood for event observables + """ + + +class LogLikelihoodRZ(LogLikelihood): + """ + Loglikelihood for event observables regularization + """ + + +class Event(ModelQuantity): + """ + An Event defines either a SBML event or a root of the argument of a + Heaviside function. The Heaviside functions will be tracked via the + vector ``h`` during simulation and are needed to inform the solver + about a discontinuity in either the right-hand side or the states + themselves, causing a reinitialization of the solver. + """ + + def __init__( + self, + identifier: sp.Symbol, + name: str, + value: sp.Expr, + state_update: sp.Expr | None, + initial_value: bool | None = True, + ): + """ + Create a new Event instance. + + :param identifier: + unique identifier of the Event + + :param name: + individual name of the Event (does not need to be unique) + + :param value: + formula for the root / trigger function + + :param state_update: + formula for the bolus function (None for Heaviside functions, + zero vector for events without bolus) + + :param initial_value: + initial boolean value of the trigger function at t0. If set to + `False`, events may trigger at ``t==t0``, otherwise not. + """ + super().__init__(identifier, name, value) + # add the Event specific components + self._state_update = state_update + self._initial_value = initial_value + + # expression(s) for the timepoint(s) at which the event triggers + self._t_root = sp.solve(self.get_val(), amici_time_symbol) + + def get_initial_value(self) -> bool: + """ + Return the initial value for the root function. + + :return: + initial value formula + """ + return self._initial_value + + def __eq__(self, other): + """ + Check equality of events at the level of trigger/root functions, as we + need to collect unique root functions for ``roots.cpp`` + """ + return self.get_val() == other.get_val() and ( + self.get_initial_value() == other.get_initial_value() + ) + + def triggers_at_fixed_timepoint(self) -> bool: + """Check whether the event triggers at a (single) fixed time-point.""" + if len(self._t_root) != 1: + return False + return self._t_root[0].is_Number + + def get_trigger_time(self) -> sp.Float: + """Get the time at which the event triggers. + + Only for events that trigger at a single fixed time-point. + """ + if not self.triggers_at_fixed_timepoint(): + raise NotImplementedError( + "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/deps/AMICI/python/sdist/amici/debugging/__init__.py b/deps/AMICI/python/sdist/amici/debugging/__init__.py new file mode 100644 index 000000000..55b24e7b1 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/debugging/__init__.py @@ -0,0 +1,46 @@ +"""Functions for debugging AMICI simulation failures.""" + +import amici +import numpy as np + + +def get_model_for_preeq(model: amici.Model, edata: amici.ExpData): + """Get a model set-up to simulate the preequilibration condition as + specified in `edata`. + + Useful for analyzing simulation errors during preequilibration. + Simulating the returned model will reproduce the behavior of + simulation-based preequilibration. + + Note that for models with events, the simulation results may differ. + During preequilibration, event-handling is disabled. However, when + simulating the returned model, event-handling will be enabled. + For events triggered at fixed timepoints, this can be avoided by setting + :meth:`t0 ` to a timepoints after the last trigger + timepoint. + + :param model: + The model for which *edata* was generated. + :param edata: + The experimental data object with a preequilibration condition + specified. + :return: + A copy of *model* with the same parameters, initial states, ... + as *amici_model* for the preequilibration condition. + Output timepoints are set to ``[inf]`` and will have to be adjusted. + """ + model = model.clone() + model.setTimepoints([np.inf]) + model.setFixedParameters(edata.fixedParametersPreequilibration) + if edata.pscale: + model.setParameterScale(edata.pscale) + if edata.parameters: + model.setParameters(edata.parameters) + if edata.plist: + model.setParameterList(edata.plist) + model.setInitialStates(edata.x0) + # has to be set *after* parameter list/scale! + model.setInitialStateSensitivities(edata.sx0) + model.setT0(edata.tstart_) + + return model diff --git a/deps/AMICI/python/sdist/amici/gradient_check.py b/deps/AMICI/python/sdist/amici/gradient_check.py index 27e2d671d..019f091e8 100644 --- a/deps/AMICI/python/sdist/amici/gradient_check.py +++ b/deps/AMICI/python/sdist/amici/gradient_check.py @@ -6,7 +6,7 @@ """ import copy -from typing import Callable, List, Optional, Sequence +from collections.abc import Sequence import numpy as np @@ -29,10 +29,10 @@ def check_finite_difference( solver: Solver, edata: ExpData, ip: int, - fields: List[str], - atol: Optional[float] = 1e-4, - rtol: Optional[float] = 1e-4, - epsilon: Optional[float] = 1e-3, + fields: list[str], + 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 @@ -137,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: @@ -248,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 @@ -331,7 +331,7 @@ def _check_results( """ result = rdata[field] - if type(result) is float: + if type(result) is float: # noqa E721 result = np.array(result) _check_close( diff --git a/deps/AMICI/python/sdist/amici/import_utils.py b/deps/AMICI/python/sdist/amici/import_utils.py index 77a2add60..1a0dc782d 100644 --- a/deps/AMICI/python/sdist/amici/import_utils.py +++ b/deps/AMICI/python/sdist/amici/import_utils.py @@ -1,20 +1,17 @@ """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, - Dict, - Iterable, - Optional, - Sequence, SupportsFloat, - Tuple, Union, ) +from collections.abc import Callable +from collections.abc import Iterable, Sequence import sympy as sp from sympy.functions.elementary.piecewise import ExprCondPair @@ -33,7 +30,7 @@ class SBMLException(Exception): pass -SymbolDef = Dict[sp.Symbol, Union[Dict[str, sp.Expr], sp.Expr]] +SymbolDef = dict[sp.Symbol, Union[dict[str, sp.Expr], sp.Expr]] # Monkey-patch toposort CircularDependencyError to handle non-sortable objects, @@ -44,13 +41,13 @@ def __init__(self, data): # error messages. That's convenient for doctests. s = "Circular dependencies exist among these items: {{{}}}".format( ", ".join( - "{!r}:{!r}".format(key, value) + f"{key!r}:{value!r}" for key, value in sorted( {str(k): v for k, v in data.items()}.items() ) ) ) - super(CircularDependencyError, self).__init__(s) + super().__init__(s) self.data = data @@ -72,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 @@ -93,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 @@ -261,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: """ @@ -318,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 @@ -423,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 @@ -548,7 +545,7 @@ def _parse_heaviside_trigger(trigger: sp.Expr) -> sp.Expr: def grouper( iterable: Iterable, n: int, fillvalue: Any = None -) -> Iterable[Tuple[Any]]: +) -> Iterable[tuple[Any]]: """ Collect data into fixed-length chunks or blocks @@ -570,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 @@ -622,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 @@ -650,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 @@ -665,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 @@ -681,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. @@ -734,5 +731,27 @@ def strip_pysb(symbol: sp.Basic) -> sp.Basic: return symbol +def unique_preserve_order(seq: Sequence) -> list: + """Return a list of unique elements in Sequence, keeping only the first + occurrence of each element + + Parameters: + seq: Sequence to prune + + Returns: + List of unique elements in ``seq`` + """ + seen = set() + seen_add = seen.add + return [x for x in seq if not (x in seen or seen_add(x))] + + 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/deps/AMICI/python/sdist/amici/logging.py b/deps/AMICI/python/sdist/amici/logging.py index 2648fc5b2..1f5ae1f17 100644 --- a/deps/AMICI/python/sdist/amici/logging.py +++ b/deps/AMICI/python/sdist/amici/logging.py @@ -27,31 +27,31 @@ "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] = True, + 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 logging.Logger for AMICI logging + Set up a new :class:`logging.Logger` for AMICI logging. :param level: - Logging level, typically using a constant like logging.INFO or - logging.DEBUG + Logging level, typically using a constant like :obj:`logging.INFO` or + :obj:`logging.DEBUG` :param console_output: - Set up a default console log handler if True (default) + Set up a default console log handler if ``True`` (default) :param file_output: Supply a filename to copy all log output to that file, or - set to False to disable (default) + set to ``False`` to disable (default) :param capture_warnings: - Capture warnings from Python's warnings module if True (default) + Capture warnings from Python's warnings module if ``True`` :return: A :class:`logging.Logger` object for AMICI logging. Note that other @@ -81,7 +81,12 @@ def _setup_logger( log.setLevel(level) + py_warn_logger = logging.getLogger("py.warnings") + # Remove default logging handler + for handler in log.handlers: + if handler in py_warn_logger.handlers: + py_warn_logger.removeHandler(handler) log.handlers = [] log_fmt = logging.Formatter( @@ -105,12 +110,15 @@ def _setup_logger( log.debug("Python version: %s", platform.python_version()) log.debug("Hostname: %s", socket.getfqdn()) - logging.captureWarnings(capture_warnings) + if capture_warnings: + logging.captureWarnings(capture_warnings) + for handler in log.handlers: + py_warn_logger.addHandler(handler) 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 @@ -126,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: """ @@ -167,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/deps/AMICI/python/sdist/amici/numpy.py b/deps/AMICI/python/sdist/amici/numpy.py index b84e52cc2..4f659c0b4 100644 --- a/deps/AMICI/python/sdist/amici/numpy.py +++ b/deps/AMICI/python/sdist/amici/numpy.py @@ -6,12 +6,14 @@ import collections import copy -from typing import Dict, Iterator, List, Literal, Union - +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 - +from sympy.abc import _clash from . import ExpData, ExpDataPtr, Model, ReturnData, ReturnDataPtr StrOrExpr = Union[str, sp.Expr] @@ -32,10 +34,10 @@ class is memory efficient as copies of the underlying C++ objects is """ _swigptr = None - _field_names: List[str] = [] - _field_dimensions: Dict[str, List[int]] = dict() + _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 @@ -53,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 + + return value - if item not in self._field_names: - self.__missing__(item) + 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: """ @@ -71,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 @@ -79,7 +84,10 @@ def __getattr__(self, item) -> Union[np.ndarray, float]: :returns: value """ - return self.__getitem__(item) + try: + return self.__getitem__(item) + except KeyError as e: + raise AttributeError(item) from e def __init__(self, swigptr): """ @@ -89,7 +97,7 @@ def __init__(self, swigptr): """ self._swigptr = swigptr self._cache = {} - super(SwigPtrView, self).__init__() + super().__init__() def __len__(self) -> int: """ @@ -164,6 +172,13 @@ def __eq__(self, other): return False return self._swigptr == other._swigptr + def __dir__(self): + return sorted( + set( + itertools.chain(dir(super()), self.__dict__, self._field_names) + ) + ) + class ReturnDataView(SwigPtrView): """ @@ -226,9 +241,11 @@ class ReturnDataView(SwigPtrView): "numnonlinsolvconvfailsB", "cpu_timeB", "cpu_time_total", + "messages", + "t_last", ] - def __init__(self, rdata: Union[ReturnDataPtr, ReturnData]): + def __init__(self, rdata: ReturnDataPtr | ReturnData): """ Constructor @@ -237,7 +254,7 @@ def __init__(self, rdata: Union[ReturnDataPtr, ReturnData]): if not isinstance(rdata, (ReturnDataPtr, ReturnData)): raise TypeError( f"Unsupported pointer {type(rdata)}, must be" - f"amici.ExpDataPtr!" + f"amici.ReturnDataPtr or amici.ReturnData!" ) self._field_dimensions = { "ts": [rdata.nt], @@ -288,11 +305,11 @@ def __init__(self, rdata: Union[ReturnDataPtr, ReturnData]): "numerrtestfailsB": [rdata.nt], "numnonlinsolvconvfailsB": [rdata.nt], } - super(ReturnDataView, self).__init__(rdata) + super().__init__(rdata) def __getitem__( self, item: str - ) -> Union[np.ndarray, ReturnDataPtr, ReturnData, float]: + ) -> np.ndarray | ReturnDataPtr | ReturnData | float: """ Access fields by name.s @@ -347,7 +364,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] @@ -373,7 +391,7 @@ class ExpDataView(SwigPtrView): "fixedParametersPresimulation", ] - def __init__(self, edata: Union[ExpDataPtr, ExpData]): + def __init__(self, edata: ExpDataPtr | ExpData): """ Constructor @@ -406,12 +424,12 @@ def __init__(self, edata: Union[ExpDataPtr, ExpData]): edata.observedDataStdDev = edata.getObservedDataStdDev() edata.observedEvents = edata.getObservedEvents() edata.observedEventsStdDev = edata.getObservedEventsStdDev() - super(ExpDataView, self).__init__(edata) + super().__init__(edata) def _field_as_numpy( - field_dimensions: Dict[str, List[int]], field: str, data: SwigPtrView -) -> Union[np.ndarray, float, None]: + field_dimensions: dict[str, list[int]], field: str, data: SwigPtrView +) -> np.ndarray | float | None: """ Convert data object field to numpy array with dimensions according to specified field dimensions @@ -427,7 +445,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( @@ -475,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") diff --git a/deps/AMICI/python/sdist/amici/pandas.py b/deps/AMICI/python/sdist/amici/pandas.py index 8a2eb5049..e83e524e0 100644 --- a/deps/AMICI/python/sdist/amici/pandas.py +++ b/deps/AMICI/python/sdist/amici/pandas.py @@ -7,7 +7,7 @@ import copy import math -from typing import Dict, List, Optional, SupportsFloat, Union +from typing import SupportsFloat, Union import amici import numpy as np @@ -25,17 +25,17 @@ ] ExpDatas = Union[ - List[amici.amici.ExpData], - List[amici.ExpDataPtr], + list[amici.amici.ExpData], + list[amici.ExpDataPtr], amici.amici.ExpData, amici.ExpDataPtr, ] -ReturnDatas = Union[List[amici.ReturnDataView], amici.ReturnDataView] +ReturnDatas = Union[list[amici.ReturnDataView], amici.ReturnDataView] AmiciModel = Union[amici.ModelPtr, amici.Model] -def _process_edata_list(edata_list: ExpDatas) -> List[amici.amici.ExpData]: +def _process_edata_list(edata_list: ExpDatas) -> list[amici.amici.ExpData]: """ Maps single instances of :class:`amici.amici.ExpData` to lists of :class:`amici.amici.ExpData` @@ -52,7 +52,7 @@ def _process_edata_list(edata_list: ExpDatas) -> List[amici.amici.ExpData]: return edata_list -def _process_rdata_list(rdata_list: ReturnDatas) -> List[amici.ReturnDataView]: +def _process_rdata_list(rdata_list: ReturnDatas) -> list[amici.ReturnDataView]: """ Maps single instances of :class:`amici.ReturnData` to lists of :class:`amici.ReturnData` @@ -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. @@ -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, @@ -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. @@ -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, @@ -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. @@ -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, @@ -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 @@ -359,11 +359,11 @@ def getResidualsAsDataFrame( def _fill_conditions_dict( - datadict: Dict[str, float], + datadict: dict[str, float], model: AmiciModel, edata: amici.amici.ExpData, by_id: bool, -) -> Dict[str, float]: +) -> dict[str, float]: """ Helper function that fills in condition parameters from model and edata. @@ -410,10 +410,24 @@ 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 -def _get_extended_observable_cols(model: AmiciModel, by_id: bool) -> List[str]: +def _get_extended_observable_cols(model: AmiciModel, by_id: bool) -> list[str]: """ Construction helper for extended observable dataframe headers. @@ -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" @@ -446,7 +465,7 @@ def _get_extended_observable_cols(model: AmiciModel, by_id: bool) -> List[str]: ) -def _get_observable_cols(model: AmiciModel, by_id: bool) -> List[str]: +def _get_observable_cols(model: AmiciModel, by_id: bool) -> list[str]: """ Construction helper for observable dataframe headers. @@ -471,11 +490,16 @@ 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) ) -def _get_state_cols(model: AmiciModel, by_id: bool) -> List[str]: +def _get_state_cols(model: AmiciModel, by_id: bool) -> list[str]: """ Construction helper for state dataframe headers. @@ -500,11 +524,16 @@ 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) ) -def _get_expression_cols(model: AmiciModel, by_id: bool) -> List[str]: +def _get_expression_cols(model: AmiciModel, by_id: bool) -> list[str]: """Construction helper for expression dataframe headers. :param model: @@ -528,13 +557,18 @@ 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) ) def _get_names_or_ids( model: AmiciModel, variable: str, by_id: bool -) -> List[str]: +) -> list[str]: """ Obtains a unique list of identifiers for the specified variable. First tries model.getVariableNames and then uses model.getVariableIds. @@ -592,10 +626,10 @@ 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]: +) -> list[float]: """ Copies values in condition and overwrites them according to key value pairs specified in overwrite. @@ -627,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 @@ -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] @@ -729,8 +778,8 @@ def constructEdataFromDataFrame( def getEdataFromDataFrame( - model: AmiciModel, df: pd.DataFrame, by_id: Optional[bool] = False -) -> List[amici.amici.ExpData]: + model: AmiciModel, df: pd.DataFrame, by_id: bool | None = False +) -> list[amici.amici.ExpData]: """ Constructs a ExpData instances according to the provided Model and DataFrame. @@ -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/deps/AMICI/python/sdist/amici/parameter_mapping.py b/deps/AMICI/python/sdist/amici/parameter_mapping.py index f1cf75a15..dc369b448 100644 --- a/deps/AMICI/python/sdist/amici/parameter_mapping.py +++ b/deps/AMICI/python/sdist/amici/parameter_mapping.py @@ -1,457 +1,46 @@ -""" -Parameter mapping ------------------ - -When performing parameter inference, often parameters need to be mapped from -simulation to estimation parameters, and parameters can differ between -conditions. This can be handled using the `ParameterMapping`. - -Note -~~~~ +"""Parameter mapping between AMICI and PEtab. -While the parameter mapping can be used directly with AMICI, it was developed -for usage together with PEtab, for which the whole workflow of generating -the mapping is automatized. +.. deprecated:: 0.21.0 + Use :mod:`amici.petab.parameter_mapping` instead. """ -from __future__ import annotations -import numbers +# some extra imports for backward-compatibility import warnings -from collections.abc import Sequence -from itertools import chain -from typing import Any, Dict, List, Set, Union - -import amici -import numpy as np -from petab.C import * # noqa: F403 - -SingleParameterMapping = Dict[str, Union[numbers.Number, str]] -SingleScaleMapping = Dict[str, str] -AmiciModel = Union[amici.Model, amici.ModelPtr] - - -class ParameterMappingForCondition: - """Parameter mapping for condition. - - Contains mappings for free parameters, fixed parameters, and fixed - preequilibration parameters, both for parameters and scales. - - In the scale mappings, for each simulation parameter the scale - on which the value is passed (and potentially gradients are to be - returned) is given. In the parameter mappings, for each simulation - parameter a corresponding optimization parameter (or a numeric value) - is given. - - If a mapping is not passed, the parameter mappings are assumed to be empty, - and if a scale mapping is not passed, all scales are set to linear. - - :param map_sim_var: - Mapping for free simulation parameters. - :param scale_map_sim_var: - Scales for free simulation parameters. - :param map_preeq_fix: - Mapping for fixed preequilibration parameters. - :param scale_map_preeq_fix: - Scales for fixed preequilibration parameters. - :param map_sim_fix: - Mapping for fixed simulation parameters. - :param scale_map_sim_fix: - Scales for fixed simulation parameters. - """ - - def __init__( - self, - map_sim_var: SingleParameterMapping = None, - scale_map_sim_var: SingleScaleMapping = None, - map_preeq_fix: SingleParameterMapping = None, - scale_map_preeq_fix: SingleScaleMapping = None, - map_sim_fix: SingleParameterMapping = None, - scale_map_sim_fix: SingleScaleMapping = None, - ): - if map_sim_var is None: - map_sim_var = {} - self.map_sim_var = map_sim_var - - if scale_map_sim_var is None: - scale_map_sim_var = {key: LIN for key in map_sim_var} - self.scale_map_sim_var = scale_map_sim_var - - if map_preeq_fix is None: - map_preeq_fix = {} - self.map_preeq_fix = map_preeq_fix - - if scale_map_preeq_fix is None: - scale_map_preeq_fix = {key: LIN for key in map_preeq_fix} - self.scale_map_preeq_fix = scale_map_preeq_fix - - if map_sim_fix is None: - map_sim_fix = {} - self.map_sim_fix = map_sim_fix - - if scale_map_sim_fix is None: - scale_map_sim_fix = {key: LIN for key in map_sim_fix} - self.scale_map_sim_fix = scale_map_sim_fix - - def __repr__(self): - return ( - f"{self.__class__.__name__}(" - f"map_sim_var={repr(self.map_sim_var)}," - f"scale_map_sim_var={repr(self.scale_map_sim_var)}," - f"map_preeq_fix={repr(self.map_preeq_fix)}," - f"scale_map_preeq_fix={repr(self.scale_map_preeq_fix)}," - f"map_sim_fix={repr(self.map_sim_fix)}," - f"scale_map_sim_fix={repr(self.scale_map_sim_fix)})" - ) - - @property - def free_symbols(self) -> Set[str]: - """Get IDs of all (symbolic) parameters present in this mapping""" - return { - p - for p in chain( - self.map_sim_var.values(), - self.map_preeq_fix.values(), - self.map_sim_fix.values(), - ) - if isinstance(p, str) - } - - -class ParameterMapping(Sequence): - r"""Parameter mapping for multiple conditions. - - This can be used like a list of :class:`ParameterMappingForCondition`\ s. - - :param parameter_mappings: - List of parameter mappings for specific conditions. - """ - - def __init__( - self, parameter_mappings: List[ParameterMappingForCondition] = None - ): - super().__init__() - if parameter_mappings is None: - parameter_mappings = [] - self.parameter_mappings = parameter_mappings - - def __iter__(self): - yield from self.parameter_mappings - - def __getitem__( - self, item - ) -> Union[ParameterMapping, ParameterMappingForCondition]: - result = self.parameter_mappings[item] - if isinstance(result, ParameterMappingForCondition): - return result - return ParameterMapping(result) - - def __len__(self): - return len(self.parameter_mappings) - - def append( - self, parameter_mapping_for_condition: ParameterMappingForCondition - ): - """Append a condition specific parameter mapping.""" - self.parameter_mappings.append(parameter_mapping_for_condition) - - def __repr__(self): - return f"{self.__class__.__name__}({repr(self.parameter_mappings)})" - - @property - def free_symbols(self) -> Set[str]: - """Get IDs of all (symbolic) parameters present in this mapping""" - return set.union(*(mapping.free_symbols for mapping in self)) - - -def fill_in_parameters( - edatas: List[amici.ExpData], - problem_parameters: Dict[str, numbers.Number], - scaled_parameters: bool, - parameter_mapping: ParameterMapping, - amici_model: AmiciModel, -) -> None: - """Fill fixed and dynamic parameters into the edatas (in-place). - - :param edatas: - List of experimental datas :class:`amici.amici.ExpData` with - everything except parameters filled. - :param problem_parameters: - Problem parameters as parameterId=>value dict. Only - parameters included here will be set. Remaining parameters will - be used as currently set in `amici_model`. - :param scaled_parameters: - If True, problem_parameters are assumed to be on the scale provided - in the parameter mapping. If False, they are assumed - to be in linear scale. - :param parameter_mapping: - Parameter mapping for all conditions. - :param amici_model: - AMICI model. - """ - if unused_parameters := ( - set(problem_parameters.keys()) - parameter_mapping.free_symbols - ): - warnings.warn( - "The following problem parameters were not used: " - + str(unused_parameters), - RuntimeWarning, - ) - - for edata, mapping_for_condition in zip(edatas, parameter_mapping): - fill_in_parameters_for_condition( - edata, - problem_parameters, - scaled_parameters, - mapping_for_condition, - amici_model, - ) - - -def fill_in_parameters_for_condition( - edata: amici.ExpData, - problem_parameters: Dict[str, numbers.Number], - scaled_parameters: bool, - parameter_mapping: ParameterMappingForCondition, - amici_model: AmiciModel, -) -> None: - """Fill fixed and dynamic parameters into the edata for condition - (in-place). - - :param edata: - Experimental data object to fill parameters into. - :param problem_parameters: - Problem parameters as parameterId=>value dict. Only - parameters included here will be set. Remaining parameters will - be used as already set in `amici_model` and `edata`. - :param scaled_parameters: - If True, problem_parameters are assumed to be on the scale provided - in the parameter mapping. If False, they - are assumed to be in linear scale. - :param parameter_mapping: - Parameter mapping for current condition. - :param amici_model: - AMICI model - """ - map_sim_var = parameter_mapping.map_sim_var - scale_map_sim_var = parameter_mapping.scale_map_sim_var - map_preeq_fix = parameter_mapping.map_preeq_fix - scale_map_preeq_fix = parameter_mapping.scale_map_preeq_fix - map_sim_fix = parameter_mapping.map_sim_fix - scale_map_sim_fix = parameter_mapping.scale_map_sim_fix - - # Parameter mapping may contain parameter_ids as values, these *must* - # be replaced - - def _get_par(model_par, value, mapping): - """Replace parameter IDs in mapping dicts by values from - problem_parameters where necessary""" - if isinstance(value, str): - try: - # estimated parameter - return problem_parameters[value] - except KeyError: - # condition table overrides must have been handled already, - # e.g. by the PEtab parameter mapping, but parameters from - # InitialAssignments may still be present. - if mapping[value] == model_par: - # prevent infinite recursion - raise - return _get_par(value, mapping[value], mapping) - if model_par in problem_parameters: - # user-provided - return problem_parameters[model_par] - # prevent nan-propagation in derivative - if np.isnan(value): - return 0.0 - # constant value - return value - - map_preeq_fix = { - key: _get_par(key, val, map_preeq_fix) - for key, val in map_preeq_fix.items() - } - map_sim_fix = { - key: _get_par(key, val, map_sim_fix) - for key, val in map_sim_fix.items() - } - map_sim_var = { - key: _get_par(key, val, dict(map_sim_fix, **map_sim_var)) - for key, val in map_sim_var.items() - } - - # If necessary, (un)scale parameters - if scaled_parameters: - unscale_parameters_dict(map_preeq_fix, scale_map_preeq_fix) - unscale_parameters_dict(map_sim_fix, scale_map_sim_fix) - if not scaled_parameters: - # We scale all parameters to the scale they are estimated on, and pass - # that information to amici via edata.{parameters,pscale}. - # The scaling is necessary to obtain correct derivatives. - scale_parameters_dict(map_sim_var, scale_map_sim_var) - # We can skip preequilibration parameters, because they are identical - # with simulation parameters, and only the latter are used from here - # on. - - ########################################################################## - # variable parameters and parameter scale - - # parameter list from mapping dict - parameters = [ - map_sim_var[par_id] for par_id in amici_model.getParameterIds() - ] - - # scales list from mapping dict - scales = [ - petab_to_amici_scale(scale_map_sim_var[par_id]) - for par_id in amici_model.getParameterIds() - ] - - # plist - plist = [ - ip - for ip, par_id in enumerate(amici_model.getParameterIds()) - if isinstance(parameter_mapping.map_sim_var[par_id], str) - ] - - if parameters: - edata.parameters = np.asarray(parameters, dtype=float) - - if scales: - edata.pscale = amici.parameterScalingFromIntVector(scales) - - if plist: - edata.plist = plist - - ########################################################################## - # fixed parameters preequilibration - if map_preeq_fix: - fixed_pars_preeq = [ - map_preeq_fix[par_id] - for par_id in amici_model.getFixedParameterIds() - ] - edata.fixedParametersPreequilibration = fixed_pars_preeq - - ########################################################################## - # fixed parameters simulation - if map_sim_fix: - fixed_pars_sim = [ - map_sim_fix[par_id] - for par_id in amici_model.getFixedParameterIds() - ] - edata.fixedParameters = fixed_pars_sim - - -def petab_to_amici_scale(petab_scale: str) -> int: - """Convert petab scale id to amici scale id.""" - if petab_scale == LIN: - return amici.ParameterScaling_none - if petab_scale == LOG10: - return amici.ParameterScaling_log10 - if petab_scale == LOG: - return amici.ParameterScaling_ln - raise ValueError(f"PEtab scale not recognized: {petab_scale}") - - -def amici_to_petab_scale(amici_scale: int) -> str: - """Convert amici scale id to petab scale id.""" - if amici_scale == amici.ParameterScaling_none: - return LIN - if amici_scale == amici.ParameterScaling_log10: - return LOG10 - if amici_scale == amici.ParameterScaling_ln: - return LOG - raise ValueError(f"AMICI scale not recognized: {amici_scale}") - - -def scale_parameter(value: numbers.Number, petab_scale: str) -> numbers.Number: - """Bring parameter from linear scale to target scale. - - :param value: - Value to scale - :param petab_scale: - Target scale of ``value`` - - :return: - ``value`` on target scale - """ - if petab_scale == LIN: - return value - if petab_scale == LOG10: - return np.log10(value) - if petab_scale == LOG: - return np.log(value) - raise ValueError( - f"Unknown parameter scale {petab_scale}. " - f"Must be from {(LIN, LOG, LOG10)}" - ) - - -def unscale_parameter( - value: numbers.Number, petab_scale: str -) -> numbers.Number: - """Bring parameter from scale to linear scale. - - :param value: - Value to scale - :param petab_scale: - Target scale of ``value`` - - :return: - ``value`` on linear scale - """ - if petab_scale == LIN: - return value - if petab_scale == LOG10: - return np.power(10, value) - if petab_scale == LOG: - return np.exp(value) - raise ValueError( - f"Unknown parameter scale {petab_scale}. " - f"Must be from {(LIN, LOG, LOG10)}" - ) - - -def scale_parameters_dict( - value_dict: Dict[Any, numbers.Number], petab_scale_dict: Dict[Any, str] -) -> None: - """ - Bring parameters from linear scale to target scale. - - Bring values in ``value_dict`` from linear scale to the scale - provided in ``petab_scale_dict`` (in-place). - Both arguments are expected to have the same length and matching keys. - - :param value_dict: - Values to scale - - :param petab_scale_dict: - Target scales of ``values`` - """ - if value_dict.keys() != petab_scale_dict.keys(): - raise AssertionError("Keys don't match.") - - for key, value in value_dict.items(): - value_dict[key] = scale_parameter(value, petab_scale_dict[key]) - - -def unscale_parameters_dict( - value_dict: Dict[Any, numbers.Number], petab_scale_dict: Dict[Any, str] -) -> None: - """ - Bring parameters from target scale to linear scale. - - Bring values in ``value_dict`` from linear scale to the scale - provided in ``petab_scale_dict`` (in-place). - Both arguments are expected to have the same length and matching keys. - - :param value_dict: - Values to scale - - :param petab_scale_dict: - Target scales of ``values`` - """ - if value_dict.keys() != petab_scale_dict.keys(): - raise AssertionError("Keys don't match.") - for key, value in value_dict.items(): - value_dict[key] = unscale_parameter(value, petab_scale_dict[key]) +from .petab.conditions import ( # noqa # pylint: disable=unused-import + fill_in_parameters, + fill_in_parameters_for_condition, +) +from .petab.parameter_mapping import ( # noqa # pylint: disable=unused-import + ParameterMapping, + ParameterMappingForCondition, + SingleParameterMapping, + SingleScaleMapping, + amici_to_petab_scale, + petab_to_amici_scale, + scale_parameter, + scale_parameters_dict, + unscale_parameter, + unscale_parameters_dict, +) + +warnings.warn( + "Importing amici.parameter_mapping is deprecated. Use `amici.petab.parameter_mapping` instead.", + DeprecationWarning, + stacklevel=2, +) + +__all__ = [ + "fill_in_parameters", + "fill_in_parameters_for_condition", + "ParameterMapping", + "ParameterMappingForCondition", + "SingleParameterMapping", + "SingleScaleMapping", + "amici_to_petab_scale", + "petab_to_amici_scale", + "scale_parameter", + "scale_parameters_dict", + "unscale_parameter", + "unscale_parameters_dict", +] diff --git a/deps/AMICI/python/sdist/amici/petab/__init__.py b/deps/AMICI/python/sdist/amici/petab/__init__.py new file mode 100644 index 000000000..6d2201ce3 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/petab/__init__.py @@ -0,0 +1,35 @@ +"""PEtab import related code.""" + +# ID of model parameter that is to be added to SBML model to indicate +# preequilibration +PREEQ_INDICATOR_ID = "preequilibration_indicator" + +from .petab_import import import_petab_problem +from .simulations import ( + EDATAS, + FIM, + LLH, + RDATAS, + RES, + S2LLH, + SLLH, + SRES, + rdatas_to_measurement_df, + rdatas_to_simulation_df, + simulate_petab, +) + +__all__ = [ + "import_petab_problem", + "simulate_petab", + "rdatas_to_simulation_df", + "rdatas_to_measurement_df", + "LLH", + "SLLH", + "FIM", + "S2LLH", + "RES", + "SRES", + "RDATAS", + "EDATAS", +] diff --git a/deps/AMICI/python/sdist/amici/petab/cli/__init__.py b/deps/AMICI/python/sdist/amici/petab/cli/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/deps/AMICI/python/sdist/amici/petab/cli/import_petab.py b/deps/AMICI/python/sdist/amici/petab/cli/import_petab.py new file mode 100644 index 000000000..b124b4d98 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/petab/cli/import_petab.py @@ -0,0 +1,197 @@ +import argparse + +import petab + +from ..petab_import import import_model_sbml +from petab.v1.models.sbml_model import SbmlModel + + +def _parse_cli_args(): + """ + Parse command line arguments + + :return: + Parsed CLI arguments from :mod:`argparse`. + """ + parser = argparse.ArgumentParser( + description="Import PEtab-format model into AMICI." + ) + + # General options: + parser.add_argument( + "-v", + "--verbose", + dest="verbose", + action="store_true", + help="More verbose output", + ) + parser.add_argument( + "-o", + "--output-dir", + dest="model_output_dir", + help="Name of the model directory to create", + ) + parser.add_argument( + "--no-compile", + action="store_false", + dest="compile", + help="Only generate model code, do not compile", + ) + parser.add_argument( + "--no-validate", + action="store_false", + dest="validate", + help="Skip validation of PEtab files", + ) + parser.add_argument( + "--flatten", + dest="flatten", + default=False, + action="store_true", + help="Flatten measurement specific overrides of " + "observable and noise parameters", + ) + parser.add_argument( + "--no-sensitivities", + dest="generate_sensitivity_code", + default=True, + action="store_false", + help="Skip generation of sensitivity code", + ) + + # Call with set of files + 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" + ) + group.add_argument( + "-m", + "--measurements", + dest="measurement_file_name", + help="Measurement table", + ) + group.add_argument( + "-c", + "--conditions", + dest="condition_file_name", + help="Conditions table", + ) + group.add_argument( + "-p", + "--parameters", + dest="parameter_file_name", + help="Parameter table", + ) + group.add_argument( + "-b", + "--observables", + dest="observable_file_name", + help="Observable table", + ) + + 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.", + nargs="?", + ) + + parser.add_argument( + "-n", + "--model-name", + dest="model_name", + help="Name of the python module generated for the " "model", + ) + + args = parser.parse_args() + 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, 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 + + +def _main(): + """ + Command line interface to import a model in the PEtab + (https://github.com/PEtab-dev/PEtab/) format into AMICI. + """ + args = _parse_cli_args() + + if args.yaml_file_name: + pp = petab.Problem.from_yaml(args.yaml_file_name) + else: + 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 + if args.validate: + petab.lint_problem(pp) + + if args.flatten: + petab.flatten_timepoint_specific_output_overrides(pp) + + import_model_sbml( + model_name=args.model_name, + petab_problem=pp, + model_output_dir=args.model_output_dir, + compile=args.compile, + generate_sensitivity_code=args.generate_sensitivity_code, + verbose=args.verbose, + validate=False, + ) + + +if __name__ == "__main__": + _main() diff --git a/deps/AMICI/python/sdist/amici/petab/conditions.py b/deps/AMICI/python/sdist/amici/petab/conditions.py new file mode 100644 index 000000000..08c2f9030 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/petab/conditions.py @@ -0,0 +1,525 @@ +"""PEtab conditions to AMICI ExpDatas.""" + +import logging +import numbers +import warnings +from typing import Union +from collections.abc import Sequence + +import amici +import numpy as np +import pandas as pd +import petab.v1 as petab +from amici import AmiciModel +from petab.v1.C import ( + MEASUREMENT, + NOISE_PARAMETERS, + OBSERVABLE_ID, + PREEQUILIBRATION_CONDITION_ID, + SIMULATION_CONDITION_ID, + TIME, +) + +from .parameter_mapping import ( + ParameterMapping, + ParameterMappingForCondition, + petab_to_amici_scale, + scale_parameters_dict, + unscale_parameters_dict, +) +from .util import get_states_in_condition_table + +logger = logging.getLogger(__name__) + +SingleParameterMapping = dict[str, Union[numbers.Number, str]] +SingleScaleMapping = dict[str, str] + + +def fill_in_parameters( + edatas: list[amici.ExpData], + problem_parameters: dict[str, numbers.Number], + scaled_parameters: bool, + parameter_mapping: ParameterMapping, + amici_model: AmiciModel, +) -> None: + """Fill fixed and dynamic parameters into the edatas (in-place). + + :param edatas: + List of experimental datas :class:`amici.amici.ExpData` with + everything except parameters filled. + :param problem_parameters: + Problem parameters as parameterId=>value dict. Only + parameters included here will be set. Remaining parameters will + be used as currently set in `amici_model`. + :param scaled_parameters: + If True, problem_parameters are assumed to be on the scale provided + in the parameter mapping. If False, they are assumed + to be in linear scale. + :param parameter_mapping: + Parameter mapping for all conditions. + :param amici_model: + AMICI model. + """ + if unused_parameters := ( + set(problem_parameters.keys()) - parameter_mapping.free_symbols + ): + warnings.warn( + "The following problem parameters were not used: " + + str(unused_parameters), + RuntimeWarning, + stacklevel=2, + ) + + for edata, mapping_for_condition in zip( + edatas, parameter_mapping, strict=True + ): + fill_in_parameters_for_condition( + edata, + problem_parameters, + scaled_parameters, + mapping_for_condition, + amici_model, + ) + + +def fill_in_parameters_for_condition( + edata: amici.ExpData, + problem_parameters: dict[str, numbers.Number], + scaled_parameters: bool, + parameter_mapping: ParameterMappingForCondition, + amici_model: AmiciModel, +) -> None: + """Fill fixed and dynamic parameters into the edata for condition + (in-place). + + :param edata: + Experimental data object to fill parameters into. + :param problem_parameters: + Problem parameters as parameterId=>value dict. Only + parameters included here will be set. Remaining parameters will + be used as already set in `amici_model` and `edata`. + :param scaled_parameters: + If True, problem_parameters are assumed to be on the scale provided + in the parameter mapping. If False, they + are assumed to be in linear scale. + :param parameter_mapping: + Parameter mapping for current condition. + :param amici_model: + AMICI model + """ + map_sim_var = parameter_mapping.map_sim_var + scale_map_sim_var = parameter_mapping.scale_map_sim_var + map_preeq_fix = parameter_mapping.map_preeq_fix + scale_map_preeq_fix = parameter_mapping.scale_map_preeq_fix + map_sim_fix = parameter_mapping.map_sim_fix + scale_map_sim_fix = parameter_mapping.scale_map_sim_fix + + # Parameter mapping may contain parameter_ids as values, these *must* + # be replaced + + def _get_par(model_par, value, mapping): + """Replace parameter IDs in mapping dicts by values from + problem_parameters where necessary""" + if isinstance(value, str): + try: + # estimated parameter + return problem_parameters[value] + except KeyError: + # condition table overrides must have been handled already, + # e.g. by the PEtab parameter mapping, but parameters from + # InitialAssignments may still be present. + if mapping[value] == model_par: + # prevent infinite recursion + raise + return _get_par(value, mapping[value], mapping) + if model_par in problem_parameters: + # user-provided + return problem_parameters[model_par] + # prevent nan-propagation in derivative + if np.isnan(value): + return 0.0 + # constant value + return value + + map_preeq_fix = { + key: _get_par(key, val, map_preeq_fix) + for key, val in map_preeq_fix.items() + } + map_sim_fix = { + key: _get_par(key, val, map_sim_fix) + for key, val in map_sim_fix.items() + } + map_sim_var = { + key: _get_par(key, val, dict(map_sim_fix, **map_sim_var)) + for key, val in map_sim_var.items() + } + + # If necessary, (un)scale parameters + if scaled_parameters: + unscale_parameters_dict(map_preeq_fix, scale_map_preeq_fix) + unscale_parameters_dict(map_sim_fix, scale_map_sim_fix) + if not scaled_parameters: + # We scale all parameters to the scale they are estimated on, and pass + # that information to amici via edata.{parameters,pscale}. + # The scaling is necessary to obtain correct derivatives. + scale_parameters_dict(map_sim_var, scale_map_sim_var) + # We can skip preequilibration parameters, because they are identical + # with simulation parameters, and only the latter are used from here + # on. + + ########################################################################## + # variable parameters and parameter scale + + # parameter list from mapping dict + parameters = [ + map_sim_var[par_id] for par_id in amici_model.getParameterIds() + ] + + # scales list from mapping dict + scales = [ + petab_to_amici_scale(scale_map_sim_var[par_id]) + for par_id in amici_model.getParameterIds() + ] + + # plist + plist = [ + ip + for ip, par_id in enumerate(amici_model.getParameterIds()) + if isinstance(parameter_mapping.map_sim_var[par_id], str) + ] + + if parameters: + edata.parameters = np.asarray(parameters, dtype=float) + + if scales: + edata.pscale = amici.parameterScalingFromIntVector(scales) + + if plist: + edata.plist = plist + + ########################################################################## + # fixed parameters preequilibration + if map_preeq_fix: + fixed_pars_preeq = [ + map_preeq_fix[par_id] + for par_id in amici_model.getFixedParameterIds() + ] + edata.fixedParametersPreequilibration = fixed_pars_preeq + + ########################################################################## + # fixed parameters simulation + if map_sim_fix: + fixed_pars_sim = [ + map_sim_fix[par_id] + for par_id in amici_model.getFixedParameterIds() + ] + edata.fixedParameters = fixed_pars_sim + + +def create_parameterized_edatas( + amici_model: AmiciModel, + petab_problem: petab.Problem, + problem_parameters: dict[str, numbers.Number], + scaled_parameters: bool = False, + parameter_mapping: ParameterMapping = None, + simulation_conditions: pd.DataFrame | dict = None, +) -> list[amici.ExpData]: + """Create list of :class:amici.ExpData objects with parameters filled in. + + :param amici_model: + AMICI Model assumed to be compatible with ``petab_problem``. + :param petab_problem: + PEtab problem to work on. + :param problem_parameters: + Run simulation with these parameters. If ``None``, PEtab + ``nominalValues`` will be used. To be provided as dict, mapping PEtab + problem parameters to SBML IDs. + :param scaled_parameters: + If ``True``, ``problem_parameters`` are assumed to be on the scale + provided in the PEtab parameter table and will be unscaled. + If ``False``, they are assumed to be in linear scale. + :param parameter_mapping: + Optional precomputed PEtab parameter mapping for efficiency, as + generated by :func:`create_parameter_mapping`. + :param simulation_conditions: + Result of :func:`petab.get_simulation_conditions`. Can be provided to + save time if this has been obtained before. + + :return: + List with one :class:`amici.amici.ExpData` per simulation condition, + with filled in timepoints, data and parameters. + """ + # number of amici simulations will be number of unique + # (preequilibrationConditionId, simulationConditionId) pairs. + # Can be optimized by checking for identical condition vectors. + if simulation_conditions is None: + simulation_conditions = ( + petab_problem.get_simulation_conditions_from_measurement_df() + ) + + # Get parameter mapping + if parameter_mapping is None: + from .parameter_mapping import create_parameter_mapping + + parameter_mapping = create_parameter_mapping( + petab_problem=petab_problem, + simulation_conditions=simulation_conditions, + scaled_parameters=scaled_parameters, + amici_model=amici_model, + ) + + # Generate ExpData with all condition-specific information + edatas = create_edatas( + amici_model=amici_model, + petab_problem=petab_problem, + simulation_conditions=simulation_conditions, + ) + + # Fill parameters in ExpDatas (in-place) + fill_in_parameters( + edatas=edatas, + problem_parameters=problem_parameters, + scaled_parameters=scaled_parameters, + parameter_mapping=parameter_mapping, + amici_model=amici_model, + ) + + return edatas + + +def create_edata_for_condition( + condition: dict | pd.Series, + measurement_df: pd.DataFrame, + amici_model: AmiciModel, + petab_problem: petab.Problem, + observable_ids: list[str], +) -> amici.ExpData: + """Get :class:`amici.amici.ExpData` for the given PEtab condition. + + Sets timepoints, observed data and sigmas. + + :param condition: + :class:`pandas.DataFrame` row with ``preequilibrationConditionId`` and + ``simulationConditionId``. + :param measurement_df: + :class:`pandas.DataFrame` with measurements for the given condition. + :param amici_model: + AMICI model + :param petab_problem: + Underlying PEtab problem + :param observable_ids: + List of observable IDs + + :return: + ExpData instance. + """ + if amici_model.nytrue != len(observable_ids): + raise AssertionError( + "Number of AMICI model observables does not " + "match number of PEtab observables." + ) + + # create an ExpData object + edata = amici.ExpData(amici_model) + edata.id = condition[SIMULATION_CONDITION_ID] + if condition.get(PREEQUILIBRATION_CONDITION_ID): + edata.id += "+" + condition.get(PREEQUILIBRATION_CONDITION_ID) + ########################################################################## + # enable initial parameters reinitialization + + states_in_condition_table = get_states_in_condition_table( + petab_problem, condition=condition + ) + if ( + condition.get(PREEQUILIBRATION_CONDITION_ID) + and states_in_condition_table + ): + state_ids = amici_model.getStateIds() + state_idx_reinitalization = [ + state_ids.index(s) + for s, (v, v_preeq) in states_in_condition_table.items() + if not np.isnan(v) + ] + edata.reinitialization_state_idxs_sim = state_idx_reinitalization + logger.debug( + "Enabling state reinitialization for condition " + f"{condition.get(PREEQUILIBRATION_CONDITION_ID, '')} - " + f"{condition.get(SIMULATION_CONDITION_ID)} " + f"{states_in_condition_table}" + ) + + ########################################################################## + # timepoints + + # find replicate numbers of time points + timepoints_w_reps = _get_timepoints_with_replicates( + df_for_condition=measurement_df + ) + edata.setTimepoints(timepoints_w_reps) + + ########################################################################## + # measurements and sigmas + y, sigma_y = _get_measurements_and_sigmas( + df_for_condition=measurement_df, + timepoints_w_reps=timepoints_w_reps, + observable_ids=observable_ids, + ) + edata.setObservedData(y.flatten()) + edata.setObservedDataStdDev(sigma_y.flatten()) + + return edata + + +def create_edatas( + amici_model: AmiciModel, + petab_problem: petab.Problem, + simulation_conditions: pd.DataFrame | dict = None, +) -> list[amici.ExpData]: + """Create list of :class:`amici.amici.ExpData` objects for PEtab problem. + + :param amici_model: + AMICI model. + :param petab_problem: + Underlying PEtab problem. + :param simulation_conditions: + Result of :func:`petab.get_simulation_conditions`. Can be provided to + save time if this has be obtained before. + + :return: + List with one :class:`amici.amici.ExpData` per simulation condition, + with filled in timepoints and data. + """ + if simulation_conditions is None: + simulation_conditions = ( + petab_problem.get_simulation_conditions_from_measurement_df() + ) + + observable_ids = amici_model.getObservableIds() + + measurement_groupvar = [SIMULATION_CONDITION_ID] + if PREEQUILIBRATION_CONDITION_ID in simulation_conditions: + measurement_groupvar.append(petab.PREEQUILIBRATION_CONDITION_ID) + measurement_dfs = dict( + list( + petab_problem.measurement_df.fillna( + {PREEQUILIBRATION_CONDITION_ID: ""} + ).groupby(measurement_groupvar) + ) + ) + + edatas = [] + for _, condition in simulation_conditions.iterrows(): + # Create amici.ExpData for each simulation + if PREEQUILIBRATION_CONDITION_ID in condition: + measurement_index = ( + condition.get(SIMULATION_CONDITION_ID), + condition.get(PREEQUILIBRATION_CONDITION_ID) or "", + ) + else: + measurement_index = (condition.get(SIMULATION_CONDITION_ID),) + + edata = create_edata_for_condition( + condition=condition, + amici_model=amici_model, + measurement_df=measurement_dfs[measurement_index], + petab_problem=petab_problem, + observable_ids=observable_ids, + ) + edatas.append(edata) + + return edatas + + +def _get_timepoints_with_replicates( + df_for_condition: pd.DataFrame, +) -> list[numbers.Number]: + """ + Get list of timepoints including replicate measurements + + :param df_for_condition: + PEtab measurement table subset for a single condition. + + :return: + Sorted list of timepoints, including multiple timepoints accounting + for replicate measurements. + """ + # create sorted list of all timepoints for which measurements exist + timepoints = sorted(df_for_condition[TIME].unique().astype(float)) + + # find replicate numbers of time points + timepoints_w_reps = [] + for time in timepoints: + # subselect for time + df_for_time = df_for_condition[ + df_for_condition.time.astype(float) == time + ] + # rep number is maximum over rep numbers for observables + n_reps = max(df_for_time.groupby([OBSERVABLE_ID, TIME]).size()) + # append time point n_rep times + timepoints_w_reps.extend([time] * n_reps) + + return timepoints_w_reps + + +def _get_measurements_and_sigmas( + df_for_condition: pd.DataFrame, + timepoints_w_reps: Sequence[numbers.Number], + observable_ids: Sequence[str], +) -> tuple[np.array, np.array]: + """ + Get measurements and sigmas + + Generate arrays with measurements and sigmas in AMICI format from a + PEtab measurement table subset for a single condition. + + :param df_for_condition: + Subset of PEtab measurement table for one condition + + :param timepoints_w_reps: + Timepoints for which there exist measurements, including replicates + + :param observable_ids: + List of observable IDs for mapping IDs to indices. + + :return: + arrays for measurement and sigmas + """ + # prepare measurement matrix + y = np.full( + shape=(len(timepoints_w_reps), len(observable_ids)), fill_value=np.nan + ) + # prepare sigma matrix + sigma_y = y.copy() + + timepoints = sorted(df_for_condition[TIME].unique().astype(float)) + + for time in timepoints: + # subselect for time + df_for_time = df_for_condition[df_for_condition[TIME] == time] + time_ix_0 = timepoints_w_reps.index(time) + + # remember used time indices for each observable + time_ix_for_obs_ix = {} + + # iterate over measurements + for _, measurement in df_for_time.iterrows(): + # extract observable index + observable_ix = observable_ids.index(measurement[OBSERVABLE_ID]) + + # update time index for observable + if observable_ix in time_ix_for_obs_ix: + time_ix_for_obs_ix[observable_ix] += 1 + else: + time_ix_for_obs_ix[observable_ix] = time_ix_0 + + # fill observable and possibly noise parameter + y[time_ix_for_obs_ix[observable_ix], observable_ix] = measurement[ + MEASUREMENT + ] + if isinstance( + measurement.get(NOISE_PARAMETERS, None), numbers.Number + ): + sigma_y[time_ix_for_obs_ix[observable_ix], observable_ix] = ( + measurement[NOISE_PARAMETERS] + ) + return y, sigma_y diff --git a/deps/AMICI/python/sdist/amici/petab/import_helpers.py b/deps/AMICI/python/sdist/amici/petab/import_helpers.py new file mode 100644 index 000000000..b52d74004 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/petab/import_helpers.py @@ -0,0 +1,269 @@ +"""General helper functions for PEtab import. + +Functions for PEtab import that are independent of the model format. +""" + +import importlib +import logging +import os +import re +from pathlib import Path + +import amici +import pandas as pd +import petab.v1 as petab +import sympy as sp +from petab.v1.C import ( + CONDITION_NAME, + ESTIMATE, + NOISE_DISTRIBUTION, + NOISE_FORMULA, + OBSERVABLE_FORMULA, + OBSERVABLE_NAME, + OBSERVABLE_TRANSFORMATION, +) +from petab.v1.parameters import get_valid_parameters_for_parameter_table +from sympy.abc import _clash + +logger = logging.getLogger(__name__) + + +def get_observation_model( + observable_df: pd.DataFrame, +) -> 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 + :meth:`amici.sbml_import.SbmlImporter.sbml2amici`. + + :param observable_df: + PEtab observables table + + :return: + Tuple of dicts with observables, noise distributions, and sigmas. + """ + if observable_df is None: + return {}, {}, {} + + observables = {} + sigmas = {} + + nan_pat = r"^[nN]a[nN]$" + for _, observable in observable_df.iterrows(): + oid = str(observable.name) + # need to sanitize due to https://github.com/PEtab-dev/PEtab/issues/447 + name = re.sub(nan_pat, "", str(observable.get(OBSERVABLE_NAME, ""))) + formula_obs = re.sub(nan_pat, "", str(observable[OBSERVABLE_FORMULA])) + formula_noise = re.sub(nan_pat, "", str(observable[NOISE_FORMULA])) + observables[oid] = {"name": name, "formula": formula_obs} + sigmas[oid] = formula_noise + + # PEtab does currently not allow observables in noiseFormula and AMICI + # cannot handle states in sigma expressions. Therefore, where possible, + # replace species occurring in error model definition by observableIds. + replacements = { + sp.sympify(observable["formula"], locals=_clash): sp.Symbol( + observable_id + ) + for observable_id, observable in observables.items() + } + for observable_id, formula in sigmas.items(): + repl = sp.sympify(formula, locals=_clash).subs(replacements) + sigmas[observable_id] = str(repl) + + noise_distrs = petab_noise_distributions_to_amici(observable_df) + + return observables, noise_distrs, sigmas + + +def petab_noise_distributions_to_amici( + observable_df: pd.DataFrame, +) -> dict[str, str]: + """ + Map from the petab to the amici format of noise distribution + identifiers. + + :param observable_df: + PEtab observable table + + :return: + dictionary of observable_id => AMICI noise-distributions + """ + amici_distrs = {} + for _, observable in observable_df.iterrows(): + amici_val = "" + + if ( + OBSERVABLE_TRANSFORMATION in observable + and isinstance(observable[OBSERVABLE_TRANSFORMATION], str) + and observable[OBSERVABLE_TRANSFORMATION] + ): + amici_val += observable[OBSERVABLE_TRANSFORMATION] + "-" + + if ( + NOISE_DISTRIBUTION in observable + and isinstance(observable[NOISE_DISTRIBUTION], str) + and observable[NOISE_DISTRIBUTION] + ): + amici_val += observable[NOISE_DISTRIBUTION] + else: + amici_val += "normal" + amici_distrs[observable.name] = amici_val + + return amici_distrs + + +def petab_scale_to_amici_scale(scale_str: str) -> int: + """Convert PEtab parameter scaling string to AMICI scaling integer""" + + if scale_str == petab.LIN: + return amici.ParameterScaling_none + if scale_str == petab.LOG: + return amici.ParameterScaling_ln + if scale_str == petab.LOG10: + return amici.ParameterScaling_log10 + + raise ValueError(f"Invalid parameter scale {scale_str}") + + +def _create_model_name(folder: str | Path) -> str: + """ + Create a name for the model. + Just re-use the last part of the folder. + """ + return os.path.split(os.path.normpath(folder))[-1] + + +def _can_import_model(model_name: str, model_output_dir: str | Path) -> bool: + """ + Check whether a module of that name can already be imported. + """ + # try to import (in particular checks version) + try: + with amici.add_path(model_output_dir): + model_module = importlib.import_module(model_name) + except ModuleNotFoundError: + return False + + # no need to (re-)compile + return hasattr(model_module, "getModel") + + +def get_fixed_parameters( + petab_problem: petab.Problem, + non_estimated_parameters_as_constants=True, +) -> list[str]: + """ + Determine, set and return fixed model parameters. + + Non-estimated parameters and parameters specified in the condition table + are turned into constants (unless they are overridden). + Only global SBML parameters are considered. Local parameters are ignored. + + :param petab_problem: + The PEtab problem instance + + :param non_estimated_parameters_as_constants: + Whether parameters marked as non-estimated in PEtab should be + considered constant in AMICI. Setting this to ``True`` will reduce + model size and simulation times. If sensitivities with respect to those + parameters are required, this should be set to ``False``. + + :return: + list of IDs of parameters which are to be considered constant. + """ + # if we have a parameter table, all parameters that are allowed to be + # listed in the parameter table, but are not marked as estimated, can be + # turned into AMICI constants + # due to legacy API, we might not always have a parameter table, though + fixed_parameters = set() + if petab_problem.parameter_df is not None: + all_parameters = get_valid_parameters_for_parameter_table( + model=petab_problem.model, + condition_df=petab_problem.condition_df, + observable_df=petab_problem.observable_df + if petab_problem.observable_df is not None + else pd.DataFrame(columns=petab.OBSERVABLE_DF_REQUIRED_COLS), + measurement_df=petab_problem.measurement_df + if petab_problem.measurement_df is not None + else pd.DataFrame(columns=petab.MEASUREMENT_DF_REQUIRED_COLS), + ) + if non_estimated_parameters_as_constants: + estimated_parameters = petab_problem.parameter_df.index.values[ + petab_problem.parameter_df[ESTIMATE] == 1 + ] + else: + # don't treat parameter table parameters as constants + estimated_parameters = petab_problem.parameter_df.index.values + fixed_parameters = set(all_parameters) - set(estimated_parameters) + + # Column names are model parameter IDs, compartment IDs or species IDs. + # Thereof, all parameters except for any overridden ones should be made + # constant. + # (Could potentially still be made constant, but leaving them might + # increase model reusability) + + # handle parameters in condition table + condition_df = petab_problem.condition_df + if condition_df is not None: + logger.debug(f"Condition table: {condition_df.shape}") + + # remove overridden parameters (`object`-type columns) + fixed_parameters.update( + p + for p in condition_df.columns + # get rid of conditionName column + if p != CONDITION_NAME + # there is no parametric override + # TODO: could check if the final overriding parameter is estimated + # or not, but for now, we skip the parameter if there is any kind + # of overriding + if condition_df[p].dtype != "O" + # p is a parameter + and not petab_problem.model.is_state_variable(p) + ) + + # Ensure mentioned parameters exist in the model. Remove additional ones + # from list + for fixed_parameter in fixed_parameters.copy(): + # 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 no entity with the corresponding ID " + "exists. Ignoring." + ) + fixed_parameters.remove(fixed_parameter) + + return list(sorted(fixed_parameters)) + + +def check_model( + amici_model: amici.Model, + petab_problem: petab.Problem, +) -> None: + """Check that the model is consistent with the PEtab problem.""" + if petab_problem.parameter_df is None: + return + + amici_ids_free = set(amici_model.getParameterIds()) + amici_ids = amici_ids_free | set(amici_model.getFixedParameterIds()) + + petab_ids_free = set( + petab_problem.parameter_df.loc[ + petab_problem.parameter_df[ESTIMATE] == 1 + ].index + ) + + amici_ids_free_required = petab_ids_free.intersection(amici_ids) + + if not amici_ids_free_required.issubset(amici_ids_free): + raise ValueError( + "The available AMICI model does not support estimating the " + "following parameters. Please recompile the model and ensure " + "that these parameters are not treated as constants. Deleting " + "the current model might also resolve this. Parameters: " + f"{amici_ids_free_required.difference(amici_ids_free)}" + ) diff --git a/deps/AMICI/python/sdist/amici/petab/parameter_mapping.py b/deps/AMICI/python/sdist/amici/petab/parameter_mapping.py new file mode 100644 index 000000000..dc88c1064 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/petab/parameter_mapping.py @@ -0,0 +1,700 @@ +from __future__ import annotations + +""" +Parameter mapping +----------------- + +When performing parameter inference, often parameters need to be mapped from +simulation to estimation parameters, and parameters can differ between +conditions. This can be handled using the `ParameterMapping`. + +Note +~~~~ + +While the parameter mapping can be used directly with AMICI, it was developed +for usage together with PEtab, for which the whole workflow of generating +the mapping is automatized. +""" + +import logging +import numbers +import re +from collections.abc import Sequence +from itertools import chain +from typing import Any, Union +from collections.abc import Collection, Iterator + +import amici +import numpy as np +import pandas as pd +import petab.v1 as petab +import sympy as sp +from amici.sbml_import import get_species_initial +from petab.v1.C import * # noqa: F403 +from petab.v1.C import ( + LIN, + PARAMETER_SCALE, + PREEQUILIBRATION_CONDITION_ID, + SIMULATION_CONDITION_ID, +) +from petab.v1.models import MODEL_TYPE_PYSB, MODEL_TYPE_SBML +from sympy.abc import _clash + +from .. import AmiciModel +from . import PREEQ_INDICATOR_ID +from .util import get_states_in_condition_table + +try: + import pysb +except ImportError: + pysb = None + + +logger = logging.getLogger(__name__) + +SingleParameterMapping = dict[str, Union[numbers.Number, str]] +SingleScaleMapping = dict[str, str] + + +class ParameterMappingForCondition: + """Parameter mapping for condition. + + Contains mappings for free parameters, fixed parameters, and fixed + preequilibration parameters, both for parameters and scales. + + In the scale mappings, for each simulation parameter the scale + on which the value is passed (and potentially gradients are to be + returned) is given. In the parameter mappings, for each simulation + parameter a corresponding optimization parameter (or a numeric value) + is given. + + If a mapping is not passed, the parameter mappings are assumed to be empty, + and if a scale mapping is not passed, all scales are set to linear. + + :param map_sim_var: + Mapping for free simulation parameters. + :param scale_map_sim_var: + Scales for free simulation parameters. + :param map_preeq_fix: + Mapping for fixed preequilibration parameters. + :param scale_map_preeq_fix: + Scales for fixed preequilibration parameters. + :param map_sim_fix: + Mapping for fixed simulation parameters. + :param scale_map_sim_fix: + Scales for fixed simulation parameters. + """ + + def __init__( + self, + map_sim_var: SingleParameterMapping = None, + scale_map_sim_var: SingleScaleMapping = None, + map_preeq_fix: SingleParameterMapping = None, + scale_map_preeq_fix: SingleScaleMapping = None, + map_sim_fix: SingleParameterMapping = None, + scale_map_sim_fix: SingleScaleMapping = None, + ): + if map_sim_var is None: + map_sim_var = {} + self.map_sim_var = map_sim_var + + if scale_map_sim_var is None: + scale_map_sim_var = {key: LIN for key in map_sim_var} + self.scale_map_sim_var = scale_map_sim_var + + if map_preeq_fix is None: + map_preeq_fix = {} + self.map_preeq_fix = map_preeq_fix + + if scale_map_preeq_fix is None: + scale_map_preeq_fix = {key: LIN for key in map_preeq_fix} + self.scale_map_preeq_fix = scale_map_preeq_fix + + if map_sim_fix is None: + map_sim_fix = {} + self.map_sim_fix = map_sim_fix + + if scale_map_sim_fix is None: + scale_map_sim_fix = {key: LIN for key in map_sim_fix} + self.scale_map_sim_fix = scale_map_sim_fix + + def __repr__(self): + return ( + f"{self.__class__.__name__}(" + f"map_sim_var={repr(self.map_sim_var)}," + f"scale_map_sim_var={repr(self.scale_map_sim_var)}," + f"map_preeq_fix={repr(self.map_preeq_fix)}," + f"scale_map_preeq_fix={repr(self.scale_map_preeq_fix)}," + f"map_sim_fix={repr(self.map_sim_fix)}," + f"scale_map_sim_fix={repr(self.scale_map_sim_fix)})" + ) + + @property + def free_symbols(self) -> set[str]: + """Get IDs of all (symbolic) parameters present in this mapping""" + return { + p + for p in chain( + self.map_sim_var.values(), + self.map_preeq_fix.values(), + self.map_sim_fix.values(), + ) + if isinstance(p, str) + } + + +class ParameterMapping(Sequence): + r"""Parameter mapping for multiple conditions. + + This can be used like a list of :class:`ParameterMappingForCondition`\ s. + + :param parameter_mappings: + List of parameter mappings for specific conditions. + """ + + def __init__( + self, parameter_mappings: list[ParameterMappingForCondition] = None + ): + super().__init__() + if parameter_mappings is None: + parameter_mappings = [] + self.parameter_mappings = parameter_mappings + + def __iter__(self): + yield from self.parameter_mappings + + def __getitem__( + self, item + ) -> ParameterMapping | ParameterMappingForCondition: + result = self.parameter_mappings[item] + if isinstance(result, ParameterMappingForCondition): + return result + return ParameterMapping(result) + + def __len__(self): + return len(self.parameter_mappings) + + def append( + self, parameter_mapping_for_condition: ParameterMappingForCondition + ): + """Append a condition specific parameter mapping.""" + self.parameter_mappings.append(parameter_mapping_for_condition) + + def __repr__(self): + return f"{self.__class__.__name__}({repr(self.parameter_mappings)})" + + @property + def free_symbols(self) -> set[str]: + """Get IDs of all (symbolic) parameters present in this mapping""" + return set.union(*(mapping.free_symbols for mapping in self)) + + +def petab_to_amici_scale(petab_scale: str) -> int: + """Convert petab scale id to amici scale id.""" + if petab_scale == LIN: + return amici.ParameterScaling_none + if petab_scale == LOG10: + return amici.ParameterScaling_log10 + if petab_scale == LOG: + return amici.ParameterScaling_ln + raise ValueError(f"PEtab scale not recognized: {petab_scale}") + + +def amici_to_petab_scale(amici_scale: int) -> str: + """Convert amici scale id to petab scale id.""" + if amici_scale == amici.ParameterScaling_none: + return LIN + if amici_scale == amici.ParameterScaling_log10: + return LOG10 + if amici_scale == amici.ParameterScaling_ln: + return LOG + raise ValueError(f"AMICI scale not recognized: {amici_scale}") + + +def scale_parameter(value: numbers.Number, petab_scale: str) -> numbers.Number: + """Bring parameter from linear scale to target scale. + + :param value: + Value to scale + :param petab_scale: + Target scale of ``value`` + + :return: + ``value`` on target scale + """ + if petab_scale == LIN: + return value + if petab_scale == LOG10: + return np.log10(value) + if petab_scale == LOG: + return np.log(value) + raise ValueError( + f"Unknown parameter scale {petab_scale}. " + f"Must be from {(LIN, LOG, LOG10)}" + ) + + +def unscale_parameter( + value: numbers.Number, petab_scale: str +) -> numbers.Number: + """Bring parameter from scale to linear scale. + + :param value: + Value to scale + :param petab_scale: + Target scale of ``value`` + + :return: + ``value`` on linear scale + """ + if petab_scale == LIN: + return value + if petab_scale == LOG10: + return np.power(10, value) + if petab_scale == LOG: + return np.exp(value) + raise ValueError( + f"Unknown parameter scale {petab_scale}. " + f"Must be from {(LIN, LOG, LOG10)}" + ) + + +def scale_parameters_dict( + value_dict: dict[Any, numbers.Number], petab_scale_dict: dict[Any, str] +) -> None: + """ + Bring parameters from linear scale to target scale. + + Bring values in ``value_dict`` from linear scale to the scale + provided in ``petab_scale_dict`` (in-place). + Both arguments are expected to have the same length and matching keys. + + :param value_dict: + Values to scale + + :param petab_scale_dict: + Target scales of ``values`` + """ + if value_dict.keys() != petab_scale_dict.keys(): + raise AssertionError("Keys don't match.") + + for key, value in value_dict.items(): + value_dict[key] = scale_parameter(value, petab_scale_dict[key]) + + +def unscale_parameters_dict( + value_dict: dict[Any, numbers.Number], petab_scale_dict: dict[Any, str] +) -> None: + """ + Bring parameters from target scale to linear scale. + + Bring values in ``value_dict`` from linear scale to the scale + provided in ``petab_scale_dict`` (in-place). + Both arguments are expected to have the same length and matching keys. + + :param value_dict: + Values to scale + + :param petab_scale_dict: + Target scales of ``values`` + """ + if value_dict.keys() != petab_scale_dict.keys(): + raise AssertionError("Keys don't match.") + + for key, value in value_dict.items(): + value_dict[key] = unscale_parameter(value, petab_scale_dict[key]) + + +def create_parameter_mapping( + petab_problem: petab.Problem, + simulation_conditions: pd.DataFrame | list[dict], + scaled_parameters: bool, + amici_model: AmiciModel, + **parameter_mapping_kwargs, +) -> ParameterMapping: + """Generate AMICI specific parameter mapping. + + :param petab_problem: + PEtab problem + :param simulation_conditions: + Result of :func:`petab.get_simulation_conditions`. Can be provided to + save time if this has been obtained before. + :param scaled_parameters: + If ``True``, problem_parameters are assumed to be on the scale provided + in the PEtab parameter table and will be unscaled. If ``False``, they + are assumed to be in linear scale. + :param amici_model: + AMICI model. + :param parameter_mapping_kwargs: + Optional keyword arguments passed to + :func:`petab.get_optimization_to_simulation_parameter_mapping`. + To allow changing fixed PEtab problem parameters (``estimate=0``), + use ``fill_fixed_parameters=False``. + :return: + List of the parameter mappings. + """ + if simulation_conditions is None: + simulation_conditions = ( + petab_problem.get_simulation_conditions_from_measurement_df() + ) + if isinstance(simulation_conditions, list): + simulation_conditions = pd.DataFrame(data=simulation_conditions) + + # Because AMICI globalizes all local parameters during model import, + # we need to do that here as well to prevent parameter mapping errors + # (PEtab does currently not care about SBML LocalParameters) + if petab_problem.model.type_id == MODEL_TYPE_SBML: + import libsbml + + if petab_problem.sbml_document: + converter_config = ( + libsbml.SBMLLocalParameterConverter().getDefaultProperties() + ) + petab_problem.sbml_document.convert(converter_config) + else: + logger.debug( + "No petab_problem.sbml_document is set. Cannot " + "convert SBML LocalParameters. If the model contains " + "LocalParameters, parameter mapping will fail." + ) + + default_parameter_mapping_kwargs = { + "warn_unmapped": False, + "scaled_parameters": scaled_parameters, + "allow_timepoint_specific_numeric_noise_parameters": not petab.lint.observable_table_has_nontrivial_noise_formula( + petab_problem.observable_df + ), + } + if parameter_mapping_kwargs is None: + parameter_mapping_kwargs = {} + + prelim_parameter_mapping = ( + petab.get_optimization_to_simulation_parameter_mapping( + condition_df=petab_problem.condition_df, + measurement_df=petab_problem.measurement_df, + parameter_df=petab_problem.parameter_df, + observable_df=petab_problem.observable_df, + mapping_df=petab_problem.mapping_df, + model=petab_problem.model, + simulation_conditions=simulation_conditions, + **dict( + default_parameter_mapping_kwargs, **parameter_mapping_kwargs + ), + ) + ) + + parameter_mapping = ParameterMapping() + for (_, condition), prelim_mapping_for_condition in zip( + 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 + ) + parameter_mapping.append(mapping_for_condition) + + return parameter_mapping + + +def create_parameter_mapping_for_condition( + parameter_mapping_for_condition: petab.ParMappingDictQuadruple, + condition: pd.Series | dict, + petab_problem: petab.Problem, + amici_model: AmiciModel, +) -> ParameterMappingForCondition: + """Generate AMICI specific parameter mapping for condition. + + :param parameter_mapping_for_condition: + Preliminary parameter mapping for condition. + :param condition: + :class:`pandas.DataFrame` row with ``preequilibrationConditionId`` and + ``simulationConditionId``. + :param petab_problem: + Underlying PEtab problem. + :param amici_model: + AMICI model. + + :return: + The parameter and parameter scale mappings, for fixed + preequilibration, fixed simulation, and variable simulation + parameters, and then the respective scalings. + """ + ( + condition_map_preeq, + condition_map_sim, + condition_scale_map_preeq, + condition_scale_map_sim, + ) = parameter_mapping_for_condition + logger.debug(f"PEtab mapping: {parameter_mapping_for_condition}") + + if len(condition_map_preeq) != len(condition_scale_map_preeq) or len( + condition_map_sim + ) != len(condition_scale_map_sim): + raise AssertionError( + "Number of parameters and number of parameter " + "scales do not match." + ) + if len(condition_map_preeq) and len(condition_map_preeq) != len( + condition_map_sim + ): + logger.debug(f"Preequilibration parameter map: {condition_map_preeq}") + logger.debug(f"Simulation parameter map: {condition_map_sim}") + raise AssertionError( + "Number of parameters for preequilbration " + "and simulation do not match." + ) + + ########################################################################## + # initial states + # Initial states have been set during model import based on the SBML model. + # If initial states were overwritten in the PEtab condition table, they are + # applied here. + # During model generation, parameters for initial concentrations and + # respective initial assignments have been created for the + # relevant species, here we add these parameters to the parameter mapping. + # In absence of preequilibration this could also be handled via + # ExpData.x0, but in the case of preequilibration this would not allow for + # resetting initial states. + + if states_in_condition_table := get_states_in_condition_table( + petab_problem, condition + ): + # set indicator fixed parameter for preeq + # (we expect here, that this parameter was added during import and + # that it was not added by the user with a different meaning...) + if condition_map_preeq: + condition_map_preeq[PREEQ_INDICATOR_ID] = 1.0 + condition_scale_map_preeq[PREEQ_INDICATOR_ID] = LIN + + condition_map_sim[PREEQ_INDICATOR_ID] = 0.0 + condition_scale_map_sim[PREEQ_INDICATOR_ID] = LIN + + for element_id, ( + value, + preeq_value, + ) in states_in_condition_table.items(): + # for preequilibration + init_par_id = f"initial_{element_id}_preeq" + if ( + condition_id := condition.get(PREEQUILIBRATION_CONDITION_ID) + ) is not None: + _set_initial_state( + petab_problem, + condition_id, + element_id, + init_par_id, + condition_map_preeq, + condition_scale_map_preeq, + preeq_value, + ) + # 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] + init_par_id = f"initial_{element_id}_sim" + _set_initial_state( + petab_problem, + condition_id, + element_id, + init_par_id, + condition_map_sim, + 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 + # different fixed parameters for preeq and sim condition, but we cannot + # have different variable parameters. without splitting, + # merge_preeq_and_sim_pars_condition below may fail. + # TODO: This can be done already in parameter mapping creation. + variable_par_ids = amici_model.getParameterIds() + fixed_par_ids = amici_model.getFixedParameterIds() + + condition_map_preeq_var, condition_map_preeq_fix = _subset_dict( + condition_map_preeq, variable_par_ids, fixed_par_ids + ) + + ( + condition_scale_map_preeq_var, + condition_scale_map_preeq_fix, + ) = _subset_dict( + condition_scale_map_preeq, variable_par_ids, fixed_par_ids + ) + + condition_map_sim_var, condition_map_sim_fix = _subset_dict( + condition_map_sim, variable_par_ids, fixed_par_ids + ) + + condition_scale_map_sim_var, condition_scale_map_sim_fix = _subset_dict( + condition_scale_map_sim, variable_par_ids, fixed_par_ids + ) + + logger.debug( + "Fixed parameters preequilibration: " f"{condition_map_preeq_fix}" + ) + logger.debug("Fixed parameters simulation: " f"{condition_map_sim_fix}") + logger.debug( + "Variable parameters preequilibration: " f"{condition_map_preeq_var}" + ) + logger.debug("Variable parameters simulation: " f"{condition_map_sim_var}") + + petab.merge_preeq_and_sim_pars_condition( + condition_map_preeq_var, + condition_map_sim_var, + condition_scale_map_preeq_var, + condition_scale_map_sim_var, + condition, + ) + logger.debug(f"Merged: {condition_map_sim_var}") + + parameter_mapping_for_condition = ParameterMappingForCondition( + map_preeq_fix=condition_map_preeq_fix, + map_sim_fix=condition_map_sim_fix, + map_sim_var=condition_map_sim_var, + scale_map_preeq_fix=condition_scale_map_preeq_fix, + scale_map_sim_fix=condition_scale_map_sim_fix, + scale_map_sim_var=condition_scale_map_sim_var, + ) + + return parameter_mapping_for_condition + + +def _set_initial_state( + petab_problem, + condition_id, + element_id, + init_par_id, + par_map, + scale_map, + value, +): + value = petab.to_float_if_float(value) + if pd.isna(value): + if petab_problem.model.type_id == MODEL_TYPE_SBML: + value = _get_initial_state_sbml(petab_problem, element_id) + elif petab_problem.model.type_id == MODEL_TYPE_PYSB: + value = _get_initial_state_pysb(petab_problem, element_id) + + try: + value = float(value) + except (ValueError, TypeError): + if sp.nsimplify(value).is_Atom and ( + pysb is None or not isinstance(value, pysb.Component) + ): + # Get rid of multiplication with one + value = sp.nsimplify(value) + else: + raise NotImplementedError( + "Cannot handle non-trivial initial state " + f"expression for {element_id}: {value}" + ) + # this should be a parameter ID + value = str(value) + logger.debug( + f"The species {element_id} has no initial value " + f"defined for the condition {condition_id} in " + "the PEtab conditions table. The initial value is " + f"now set to {value}, which is the initial value " + "defined in the SBML model." + ) + par_map[init_par_id] = value + if isinstance(value, float): + # numeric initial state + scale_map[init_par_id] = petab.LIN + else: + # parametric initial state + scale_map[init_par_id] = petab_problem.parameter_df[ + PARAMETER_SCALE + ].get(value, petab.LIN) + + +def _subset_dict( + full: dict[Any, Any], *args: Collection[Any] +) -> Iterator[dict[Any, Any]]: + """Get subset of dictionary based on provided keys + + :param full: + Dictionary to subset + :param args: + Collections of keys to be contained in the different subsets + + :return: + subsetted dictionary + """ + for keys in args: + yield {key: val for (key, val) in full.items() if key in keys} + + +def _get_initial_state_sbml( + petab_problem: petab.Problem, element_id: str +) -> float | sp.Basic: + import libsbml + + element = petab_problem.sbml_model.getElementBySId(element_id) + type_code = element.getTypeCode() + initial_assignment = petab_problem.sbml_model.getInitialAssignmentBySymbol( + element_id + ) + if initial_assignment: + initial_assignment = sp.sympify( + libsbml.formulaToL3String(initial_assignment.getMath()), + locals=_clash, + ) + if type_code == libsbml.SBML_SPECIES: + value = ( + get_species_initial(element) + if initial_assignment is None + else initial_assignment + ) + elif type_code == libsbml.SBML_PARAMETER: + value = ( + element.getValue() + if initial_assignment is None + else initial_assignment + ) + elif type_code == libsbml.SBML_COMPARTMENT: + value = ( + element.getSize() + if initial_assignment is None + else initial_assignment + ) + else: + raise NotImplementedError( + f"Don't know what how to handle {element_id} in " + "condition table." + ) + return value + + +def _get_initial_state_pysb( + petab_problem: petab.Problem, element_id: str +) -> float | sp.Symbol: + species_idx = int(re.match(r"__s(\d+)$", element_id)[1]) + species_pattern = petab_problem.model.model.species[species_idx] + from pysb.pattern import match_complex_pattern + + value = next( + ( + initial.value + for initial in petab_problem.model.model.initials + if match_complex_pattern( + initial.pattern, species_pattern, exact=True + ) + ), + 0.0, + ) + if isinstance(value, pysb.Parameter): + if value.name in petab_problem.parameter_df.index: + value = value.name + else: + value = value.value + + return value diff --git a/deps/AMICI/python/sdist/amici/petab/petab_import.py b/deps/AMICI/python/sdist/amici/petab/petab_import.py new file mode 100644 index 000000000..52b08cfd4 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/petab/petab_import.py @@ -0,0 +1,168 @@ +""" +PEtab Import +------------ +Import a model in the :mod:`petab` (https://github.com/PEtab-dev/PEtab) format +into AMICI. +""" + +import logging +import os +import shutil +from pathlib import Path +from warnings import warn + +import amici +import petab.v1 as petab +from petab.v1.models import MODEL_TYPE_PYSB, MODEL_TYPE_SBML + +from ..logging import get_logger +from .import_helpers import _can_import_model, _create_model_name, check_model +from .sbml_import import import_model_sbml + +try: + from .pysb_import import import_model_pysb +except ModuleNotFoundError: + # pysb not available + import_model_pysb = None + + +__all__ = ["import_petab_problem"] + +logger = get_logger(__name__, logging.WARNING) + + +def import_petab_problem( + petab_problem: petab.Problem, + model_output_dir: str | Path | None = None, + model_name: str = None, + compile_: bool = None, + non_estimated_parameters_as_constants=True, + **kwargs, +) -> "amici.Model": + """ + Create an AMICI model for a PEtab problem. + + :param petab_problem: + A petab problem containing all relevant information on the model. + + :param model_output_dir: + Directory to write the model code to. It will be created if it doesn't + exist. Defaults to current directory. + + :param model_name: + Name of the generated model module. Defaults to the ID of the model + or the model file name without the extension. + + :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 + considered constant in AMICI. Setting this to ``True`` will reduce + model size and simulation times. If sensitivities with respect to those + parameters are required, this should be set to ``False``. + + :param kwargs: + Additional keyword arguments to be passed to + :meth:`amici.sbml_import.SbmlImporter.sbml2amici` or + :func:`amici.pysb_import.pysb2amici`, depending on the model type. + + :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 + ) + + if petab_problem.mapping_df is not None: + # It's partially supported. Remove at your own risk... + raise NotImplementedError( + "PEtab v2.0.0 mapping tables are not yet supported." + ) + + model_name = model_name or petab_problem.model.model_id + + if petab_problem.model.type_id == MODEL_TYPE_PYSB and model_name is None: + model_name = petab_problem.pysb_model.name + elif model_name is None and model_output_dir: + model_name = _create_model_name(model_output_dir) + + # generate folder and model name if necessary + if model_output_dir is None: + if petab_problem.model.type_id == MODEL_TYPE_PYSB: + raise ValueError("Parameter `model_output_dir` is required.") + + from .sbml_import import _create_model_output_dir_name + + model_output_dir = _create_model_output_dir_name( + petab_problem.sbml_model, model_name + ) + else: + model_output_dir = os.path.abspath(model_output_dir) + + # create folder + if not os.path.exists(model_output_dir): + os.makedirs(model_output_dir) + + # check if compilation necessary + 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 compile_: + raise ValueError( + f"Cannot compile to {model_output_dir}: not empty. " + "Please assign a different target or set `compile_` to `True`." + ) + + # remove folder if exists + if os.path.exists(model_output_dir): + shutil.rmtree(model_output_dir) + + logger.info(f"Compiling model {model_name} to {model_output_dir}.") + # compile the model + if petab_problem.model.type_id == MODEL_TYPE_PYSB: + import_model_pysb( + petab_problem, + model_name=model_name, + model_output_dir=model_output_dir, + **kwargs, + ) + else: + import_model_sbml( + petab_problem=petab_problem, + model_name=model_name, + model_output_dir=model_output_dir, + non_estimated_parameters_as_constants=non_estimated_parameters_as_constants, + **kwargs, + ) + + # import model + model_module = amici.import_model_module(model_name, model_output_dir) + model = model_module.getModel() + check_model(amici_model=model, petab_problem=petab_problem) + + logger.info( + f"Successfully loaded model {model_name} " f"from {model_output_dir}." + ) + + return model + + +# for backwards compatibility +import_model = import_model_sbml diff --git a/deps/AMICI/python/sdist/amici/petab/petab_problem.py b/deps/AMICI/python/sdist/amici/petab/petab_problem.py new file mode 100644 index 000000000..b93eb06a8 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/petab/petab_problem.py @@ -0,0 +1,278 @@ +"""PEtab-problem based simulations.""" + +import copy + +import amici +import pandas as pd +import petab.v1 as petab +from petab.v1.C import PREEQUILIBRATION_CONDITION_ID, SIMULATION_CONDITION_ID + +from .conditions import create_edatas, fill_in_parameters +from .parameter_mapping import create_parameter_mapping + + +class PetabProblem: + """Manage experimental conditions based on a PEtab problem definition. + + Create :class:`ExpData` objects from a PEtab problem definition, and handle + parameter scales and parameter mapping. + + :param petab_problem: PEtab problem definition. + :param amici_model: AMICI model + :param problem_parameters: Problem parameters to use for simulation + (default: PEtab nominal values and model values). + :param scaled_parameters: Whether the provided parameters are on PEtab + `parameterScale` or not. + :param simulation_conditions: Simulation conditions to use for simulation. + It can be used to subset the conditions in the PEtab problem. + All subsequent operations will only be performed on that subset. + By default, all conditions are used. + :param store_edatas: Whether to create and store all `ExpData` objects for + all conditions upfront. If set to ``False``, `ExpData` objects will be + created and disposed of on the fly during simulation. The latter saves + memory if the given PEtab problem comprises many simulation conditions. + """ + + def __init__( + self, + petab_problem: petab.Problem, + amici_model: amici.Model | None = None, + problem_parameters: dict[str, float] | None = None, + scaled_parameters: bool = False, + simulation_conditions: pd.DataFrame | list[dict] = None, + store_edatas: bool = True, + ): + self._petab_problem = copy.deepcopy(petab_problem) + + if amici_model is not None: + self._amici_model = amici_model + else: + from .petab_import import import_petab_problem + + self._amici_model = import_petab_problem(petab_problem) + + self._scaled_parameters = scaled_parameters + + self._simulation_conditions = simulation_conditions or ( + petab_problem.get_simulation_conditions_from_measurement_df() + ) + if not isinstance(self._simulation_conditions, pd.DataFrame): + self._simulation_conditions = pd.DataFrame( + self._simulation_conditions + ) + if ( + preeq_id := PREEQUILIBRATION_CONDITION_ID + ) in self._simulation_conditions: + self._simulation_conditions[preeq_id] = ( + self._simulation_conditions[preeq_id].fillna("") + ) + + if problem_parameters is None: + # Use PEtab nominal values as default + self._problem_parameters = self._default_parameters() + if scaled_parameters: + raise NotImplementedError( + "scaled_parameters=True in combination with default " + "parameters is not implemented yet." + ) + else: + self._problem_parameters = problem_parameters + + if store_edatas: + self._parameter_mapping = create_parameter_mapping( + petab_problem=self._petab_problem, + simulation_conditions=self._simulation_conditions, + scaled_parameters=self._scaled_parameters, + amici_model=self._amici_model, + ) + self._create_edatas() + else: + self._parameter_mapping = None + self._edatas = None + + def set_parameters( + self, + problem_parameters: dict[str, float], + scaled_parameters: bool = False, + ): + """Set problem parameters. + + :param problem_parameters: Problem parameters to use for simulation. + This may be a subset of all 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 + ): + # redo parameter mapping if scale changed + self._parameter_mapping = create_parameter_mapping( + petab_problem=self._petab_problem, + simulation_conditions=self._simulation_conditions, + scaled_parameters=scaled_parameters, + amici_model=self._amici_model, + ) + + if set(self._problem_parameters) - set(problem_parameters): + # not all parameters are provided - update + # bring previously set parameters to the same scale if necessary + if scaled_parameters and not self._scaled_parameters: + self._problem_parameters = ( + self._petab_problem.scale_parameters( + self._problem_parameters, + ) + ) + elif not scaled_parameters and self._scaled_parameters: + self._problem_parameters = ( + self._petab_problem.unscale_parameters( + self._problem_parameters, + ) + ) + self._problem_parameters |= problem_parameters + else: + self._problem_parameters = problem_parameters + + self._scaled_parameters = scaled_parameters + + if self._edatas: + fill_in_parameters( + edatas=self._edatas, + problem_parameters=self._problem_parameters, + scaled_parameters=self._scaled_parameters, + parameter_mapping=self._parameter_mapping, + amici_model=self._amici_model, + ) + + def get_edata( + self, condition_id: str, preequilibration_condition_id: str = None + ) -> amici.ExpData: + """Get ExpData object for a given condition. + + NOTE: If ``store_edatas=True`` was passed to the constructor and the + returned object is modified, the changes will be reflected in the + internal `ExpData` objects. Also, if parameter values of + `PetabProblem` are changed, all `ExpData` objects will be updated. + Create a deep copy if you want to avoid this. + + :param condition_id: PEtab condition ID + :param preequilibration_condition_id: PEtab preequilibration condition ID + :return: ExpData object + """ + # exists or has to be created? + if self._edatas: + edata_id = condition_id + if preequilibration_condition_id: + edata_id += "+" + preequilibration_condition_id + + for edata in self._edatas: + if edata.id == edata_id: + return edata + + return self._create_edata(condition_id, preequilibration_condition_id) + + def get_edatas(self): + """Get all ExpData objects. + + NOTE: If ``store_edatas=True`` was passed to the constructor and the + returned objects are modified, the changes will be reflected in the + internal `ExpData` objects. Also, if parameter values of + `PetabProblem` are changed, all `ExpData` objects will be updated. + Create a deep copy if you want to avoid this. + + :return: List of ExpData objects + """ + if self._edatas: + # shallow copy + return self._edatas.copy() + + # not storing edatas - create and return + self._parameter_mapping = create_parameter_mapping( + petab_problem=self._petab_problem, + simulation_conditions=self._simulation_conditions, + scaled_parameters=self._scaled_parameters, + amici_model=self._amici_model, + ) + self._create_edatas() + result = self._edatas + self._edatas = [] + return result + + def _create_edata( + self, condition_id: str, preequilibration_condition_id: str + ) -> amici.ExpData: + """Create ExpData object for a given condition. + + :param condition_id: PEtab condition ID + :param preequilibration_condition_id: PEtab preequilibration condition ID + :return: ExpData object + """ + simulation_condition = pd.DataFrame( + [ + { + SIMULATION_CONDITION_ID: condition_id, + PREEQUILIBRATION_CONDITION_ID: preequilibration_condition_id + or None, + } + ] + ) + edatas = create_edatas( + amici_model=self._amici_model, + petab_problem=self._petab_problem, + simulation_conditions=simulation_condition, + ) + parameter_mapping = create_parameter_mapping( + petab_problem=self._petab_problem, + simulation_conditions=simulation_condition, + scaled_parameters=self._scaled_parameters, + amici_model=self._amici_model, + ) + + # Fill parameters in ExpDatas (in-place) + fill_in_parameters( + edatas=edatas, + problem_parameters={ + p: self._problem_parameters[p] + for p in parameter_mapping.free_symbols + if p in self._problem_parameters + }, + scaled_parameters=self._scaled_parameters, + parameter_mapping=parameter_mapping, + amici_model=self._amici_model, + ) + + if len(edatas) != 1: + raise AssertionError("Expected exactly one ExpData object.") + return edatas[0] + + def _create_edatas( + self, + ): + """Create ExpData objects from PEtab problem definition.""" + self._edatas = create_edatas( + amici_model=self._amici_model, + petab_problem=self._petab_problem, + simulation_conditions=self._simulation_conditions, + ) + + fill_in_parameters( + edatas=self._edatas, + problem_parameters=self._problem_parameters, + scaled_parameters=self._scaled_parameters, + parameter_mapping=self._parameter_mapping, + amici_model=self._amici_model, + ) + + def _default_parameters(self) -> dict[str, float]: + """Get unscaled default parameters.""" + return { + t.Index: getattr(t, petab.NOMINAL_VALUE) + for t in self._petab_problem.parameter_df[ + self._petab_problem.parameter_df[petab.ESTIMATE] == 1 + ].itertuples() + } + + @property + def model(self) -> amici.Model: + """AMICI model.""" + return self._amici_model diff --git a/deps/AMICI/python/sdist/amici/petab/pysb_import.py b/deps/AMICI/python/sdist/amici/petab/pysb_import.py new file mode 100644 index 000000000..aac3a8f33 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/petab/pysb_import.py @@ -0,0 +1,274 @@ +""" +PySB-PEtab Import +----------------- +Import a model in the PySB-adapted :mod:`petab` +(https://github.com/PEtab-dev/PEtab) format into AMICI. +""" + +import logging +import re +from pathlib import Path + +import petab.v1 as petab +import pysb +import pysb.bng +import sympy as sp +from petab.v1.C import CONDITION_NAME, NOISE_FORMULA, OBSERVABLE_FORMULA +from petab.v1.models.pysb_model import PySBModel + +from ..logging import get_logger, log_execution_time, set_log_level +from . import PREEQ_INDICATOR_ID +from .import_helpers import ( + get_fixed_parameters, + petab_noise_distributions_to_amici, +) +from .util import get_states_in_condition_table + +logger = get_logger(__name__, logging.WARNING) + + +def _add_observation_model( + pysb_model: pysb.Model, petab_problem: petab.Problem +): + """Extend PySB model by observation model as defined in the PEtab + observables table""" + + # add any required output parameters + local_syms = { + sp.Symbol.__str__(comp): comp + for comp in pysb_model.components + if isinstance(comp, sp.Symbol) + } + for formula in [ + *petab_problem.observable_df[OBSERVABLE_FORMULA], + *petab_problem.observable_df[NOISE_FORMULA], + ]: + sym = sp.sympify(formula, locals=local_syms) + for s in sym.free_symbols: + if not isinstance(s, pysb.Component): + p = pysb.Parameter(str(s), 1.0) + pysb_model.add_component(p) + local_syms[sp.Symbol.__str__(p)] = p + + # add observables and sigmas to pysb model + for observable_id, observable_formula, noise_formula in zip( + 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(): + obs_expr = pysb_model.expressions[observable_id] + else: + obs_expr = pysb.Expression(observable_id, obs_symbol) + pysb_model.add_component(obs_expr) + local_syms[observable_id] = obs_expr + + sigma_id = f"{observable_id}_sigma" + sigma_symbol = sp.sympify(noise_formula, locals=local_syms) + sigma_expr = pysb.Expression(sigma_id, sigma_symbol) + pysb_model.add_component(sigma_expr) + local_syms[sigma_id] = sigma_expr + + +def _add_initialization_variables( + pysb_model: pysb.Model, petab_problem: petab.Problem +): + """Add initialization variables to the PySB model to support initial + conditions specified in the PEtab condition table. + + To parameterize initial states, we currently need initial assignments. + If they occur in the condition table, we create a new parameter + initial_${speciesID}. Feels dirty and should be changed (see also #924). + """ + + initial_states = get_states_in_condition_table(petab_problem) + fixed_parameters = [] + if initial_states: + # add preequilibration indicator variable + # NOTE: would only be required if we actually have preequilibration + # adding it anyways. can be optimized-out later + if PREEQ_INDICATOR_ID in [c.name for c in pysb_model.components]: + raise AssertionError( + "Model already has a component with ID " + f"{PREEQ_INDICATOR_ID}. Cannot handle " + "species and compartments in condition table " + "then." + ) + preeq_indicator = pysb.Parameter(PREEQ_INDICATOR_ID) + pysb_model.add_component(preeq_indicator) + # Can only reset parameters after preequilibration if they are fixed. + fixed_parameters.append(PREEQ_INDICATOR_ID) + logger.debug( + "Adding preequilibration indicator constant " + f"{PREEQ_INDICATOR_ID}" + ) + logger.debug(f"Adding initial assignments for {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]: + if init_par_id in [c.name for c in pysb_model.components]: + raise ValueError( + "Cannot create parameter for initial assignment " + f"for {assignee_id} because an entity named " + f"{init_par_id} exists already in the model." + ) + p = pysb.Parameter(init_par_id) + pysb_model.add_component(p) + + species_idx = int(re.match(r"__s(\d+)$", assignee_id)[1]) + # use original model here since that's what was used to generate + # the ids in initial_states + species_pattern = petab_problem.model.model.species[species_idx] + + # species pattern comes from the _original_ model, but we only want + # to modify pysb_model, so we have to reconstitute the pattern using + # pysb_model + for c in pysb_model.components: + globals()[c.name] = c + species_pattern = pysb.as_complex_pattern(eval(str(species_pattern))) + + from pysb.pattern import match_complex_pattern + + formula = pysb.Expression( + f"initial_{assignee_id}_formula", + preeq_indicator * pysb_model.parameters[init_par_id_preeq] + + (1 - preeq_indicator) * pysb_model.parameters[init_par_id_sim], + ) + pysb_model.add_component(formula) + + for initial in pysb_model.initials: + if match_complex_pattern( + initial.pattern, species_pattern, exact=True + ): + logger.debug( + "The PySB model has an initial defined for species " + f"{assignee_id}, but this species also has an initial " + "value defined in the PEtab condition table. The SBML " + "initial assignment will be overwritten to handle " + "preequilibration and initial values specified by the " + "PEtab problem." + ) + initial.value = formula + break + else: + # No initial in the pysb model, so add one + init = pysb.Initial(species_pattern, formula) + pysb_model.add_component(init) + + return fixed_parameters + + +@log_execution_time("Importing PEtab model", logger) +def import_model_pysb( + petab_problem: petab.Problem, + model_output_dir: str | Path | None = None, + verbose: bool | int | None = True, + model_name: str | None = None, + **kwargs, +) -> None: + """ + Create AMICI model from PySB-PEtab problem + + :param petab_problem: + PySB PEtab problem + + :param model_output_dir: + Directory to write the model code to. Will be created if doesn't + exist. Defaults to current directory. + + :param verbose: + Print/log extra information. + + :param model_name: + Name of the generated model module + + :param kwargs: + Additional keyword arguments to be passed to + :func:`amici.pysb_import.pysb2amici`. + """ + set_log_level(logger, verbose) + + logger.info("Importing model ...") + + if not isinstance(petab_problem.model, PySBModel): + raise ValueError("Not a PySB model") + + # need to create a copy here as we don't want to modify the original + pysb.SelfExporter.cleanup() + og_export = pysb.SelfExporter.do_export + pysb.SelfExporter.do_export = False + pysb_model = pysb.Model( + base=petab_problem.model.model, + name=petab_problem.model.model_id, + ) + + _add_observation_model(pysb_model, petab_problem) + # generate species for the _original_ model + pysb.bng.generate_equations(petab_problem.model.model) + fixed_parameters = _add_initialization_variables(pysb_model, petab_problem) + pysb.SelfExporter.do_export = og_export + + # check condition table for supported features, important to use pysb_model + # here, as we want to also cover output parameters + model_parameters = [p.name for p in pysb_model.parameters] + condition_species_parameters = get_states_in_condition_table( + petab_problem, return_patterns=True + ) + for x in petab_problem.condition_df.columns: + if x == CONDITION_NAME: + continue + + x = petab.mapping.resolve_mapping(petab_problem.mapping_df, x) + + # parameters + if x in model_parameters: + continue + + # species/pattern + if x in condition_species_parameters: + continue + + raise NotImplementedError( + "For PySB PEtab import, only model parameters and species, but " + "not compartments are allowed in the condition table. Offending " + f"column: {x}" + ) + + constant_parameters = ( + get_fixed_parameters(petab_problem) + fixed_parameters + ) + + if petab_problem.observable_df is None: + observables = None + sigmas = None + noise_distrs = None + else: + observables = [ + expr.name + for expr in pysb_model.expressions + if expr.name in petab_problem.observable_df.index + ] + + sigmas = {obs_id: f"{obs_id}_sigma" for obs_id in observables} + + noise_distrs = petab_noise_distributions_to_amici( + petab_problem.observable_df + ) + + from amici.pysb_import import pysb2amici + + pysb2amici( + model=pysb_model, + output_dir=model_output_dir, + model_name=model_name, + verbose=True, + observables=observables, + sigmas=sigmas, + constant_parameters=constant_parameters, + noise_distributions=noise_distrs, + **kwargs, + ) diff --git a/deps/AMICI/python/sdist/amici/petab/sbml_import.py b/deps/AMICI/python/sdist/amici/petab/sbml_import.py new file mode 100644 index 000000000..92009bf7c --- /dev/null +++ b/deps/AMICI/python/sdist/amici/petab/sbml_import.py @@ -0,0 +1,575 @@ +import logging +import math +import os +import tempfile +from itertools import chain +from pathlib import Path +from typing import Union + +import amici +import libsbml +import petab.v1 as petab +import sympy as sp +from _collections import OrderedDict +from amici.logging import log_execution_time, set_log_level +from petab.v1.models import MODEL_TYPE_SBML +from sympy.abc import _clash + +from . import PREEQ_INDICATOR_ID +from .import_helpers import ( + check_model, + get_fixed_parameters, + get_observation_model, +) +from .util import get_states_in_condition_table + +logger = logging.getLogger(__name__) + + +@log_execution_time("Importing PEtab model", logger) +def import_model_sbml( + sbml_model: Union[str, Path, "libsbml.Model"] = None, + petab_problem: petab.Problem = None, + 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: dict[str, float] | None = None, + discard_sbml_annotations: bool = False, + **kwargs, +) -> amici.SbmlImporter: + """ + Create AMICI model from PEtab problem + + :param sbml_model: + PEtab SBML model or SBML file name. + Deprecated, pass ``petab_problem`` instead. + + :param petab_problem: + PEtab problem. + + :param model_name: + Name of the generated model. If model file name was provided, + this defaults to the file name without extension, otherwise + the SBML model ID will be used. + + :param model_output_dir: + Directory to write the model code to. Will be created if doesn't + exist. Defaults to current directory. + + :param verbose: + Print/log extra information. + + :param allow_reinit_fixpar_initcond: + See :class:`amici.de_export.ODEExporter`. Must be enabled if initial + states are to be reset after preequilibration. + + :param validate: + Whether to validate the PEtab problem + + :param non_estimated_parameters_as_constants: + Whether parameters marked as non-estimated in PEtab should be + considered constant in AMICI. Setting this to ``True`` will reduce + model size and simulation times. If sensitivities with respect to those + parameters are required, this should be set to ``False``. + + :param output_parameter_defaults: + Optional default parameter values for output parameters introduced in + the PEtab observables table, in particular for placeholder parameters. + dictionary mapping parameter IDs to default values. + + :param discard_sbml_annotations: + Discard information contained in AMICI SBML annotations (debug). + + :param kwargs: + Additional keyword arguments to be passed to + :meth:`amici.sbml_import.SbmlImporter.sbml2amici`. + + :return: + The created :class:`amici.sbml_import.SbmlImporter` instance. + """ + from petab.v1.models.sbml_model import SbmlModel + + set_log_level(logger, verbose) + + logger.info("Importing model ...") + + if petab_problem.observable_df is None: + raise NotImplementedError( + "PEtab import without observables table " + "is currently not supported." + ) + + assert isinstance(petab_problem.model, SbmlModel) + + if validate: + logger.info("Validating PEtab problem ...") + petab.lint_problem(petab_problem) + + # Model name from SBML ID or filename + if model_name is None: + if not (model_name := petab_problem.model.sbml_model.getId()): + if not isinstance(sbml_model, (str, Path)): + raise ValueError( + "No `model_name` was provided and no model " + "ID was specified in the SBML model." + ) + model_name = os.path.splitext(os.path.split(sbml_model)[-1])[0] + + if model_output_dir is None: + model_output_dir = os.path.join( + os.getcwd(), f"{model_name}-amici{amici.__version__}" + ) + + logger.info( + f"Model name is '{model_name}'.\n" + f"Writing model code to '{model_output_dir}'." + ) + + # Create a copy, because it will be modified by SbmlImporter + sbml_doc = petab_problem.model.sbml_model.getSBMLDocument().clone() + sbml_model = sbml_doc.getModel() + + show_model_info(sbml_model) + + sbml_importer = amici.SbmlImporter( + sbml_model, + discard_annotations=discard_sbml_annotations, + ) + sbml_model = sbml_importer.sbml + + allow_n_noise_pars = ( + not petab.lint.observable_table_has_nontrivial_noise_formula( + petab_problem.observable_df + ) + ) + if ( + petab_problem.measurement_df is not None + and petab.lint.measurement_table_has_timepoint_specific_mappings( + petab_problem.measurement_df, + allow_scalar_numeric_noise_parameters=allow_n_noise_pars, + ) + ): + raise ValueError( + "AMICI does not support importing models with timepoint specific " + "mappings for noise or observable parameters. Please flatten " + "the problem and try again." + ) + + if petab_problem.observable_df is not None: + observables, noise_distrs, sigmas = get_observation_model( + petab_problem.observable_df + ) + else: + observables = noise_distrs = sigmas = None + + logger.info(f"Observables: {len(observables)}") + logger.info(f"Sigmas: {len(sigmas)}") + + if len(sigmas) != len(observables): + raise AssertionError( + f"Number of provided observables ({len(observables)}) and sigmas " + f"({len(sigmas)}) do not match." + ) + + # TODO: adding extra output parameters is currently not supported, + # so we add any output parameters to the SBML model. + # this should be changed to something more elegant + # + formulas = chain( + (val["formula"] for val in observables.values()), sigmas.values() + ) + output_parameters = OrderedDict() + for formula in formulas: + # we want reproducible parameter ordering upon repeated import + free_syms = sorted( + sp.sympify(formula, locals=_clash).free_symbols, + key=lambda symbol: symbol.name, + ) + for free_sym in free_syms: + sym = str(free_sym) + if ( + sbml_model.getElementBySId(sym) is None + and sym != "time" + and sym not in observables + ): + output_parameters[sym] = None + logger.debug( + "Adding output parameters to model: " + f"{list(output_parameters.keys())}" + ) + output_parameter_defaults = output_parameter_defaults or {} + if extra_pars := ( + set(output_parameter_defaults) - set(output_parameters.keys()) + ): + raise ValueError( + f"Default output parameter values were given for {extra_pars}, " + "but they those are not output parameters." + ) + + for par in output_parameters.keys(): + _add_global_parameter( + sbml_model=sbml_model, + parameter_id=par, + value=output_parameter_defaults.get(par, 0.0), + ) + # + + # TODO: to parameterize initial states or compartment sizes, we currently + # need initial assignments. if they occur in the condition table, we + # create a new parameter initial_${speciesOrCompartmentID}. + # 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 and requires_preequilibration: + # add preequilibration indicator variable + if sbml_model.getParameter(PREEQ_INDICATOR_ID) is not None: + raise AssertionError( + "Model already has a parameter with ID " + f"{PREEQ_INDICATOR_ID}. Cannot handle " + "species and compartments in condition table " + "then." + ) + indicator = sbml_model.createParameter() + indicator.setId(PREEQ_INDICATOR_ID) + indicator.setName(PREEQ_INDICATOR_ID) + # Can only reset parameters after preequilibration if they are fixed. + fixed_parameters.append(PREEQ_INDICATOR_ID) + logger.debug( + "Adding preequilibration indicator " + f"constant {PREEQ_INDICATOR_ID}" + ) + 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] 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 " + f"for {assignee_id} because an entity named " + f"{init_par_id} exists already in the model." + ) + init_par = sbml_model.createParameter() + init_par.setId(init_par_id) + init_par.setName(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() + assignment.setSymbol(assignee_id) + else: + logger.debug( + "The SBML model has an initial assignment defined " + f"for model entity {assignee_id}, but this entity " + "also has an initial value defined in the PEtab " + "condition table. The SBML initial assignment will " + "be overwritten to handle preequilibration and " + "initial values specified by the PEtab problem." + ) + 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) + # + + fixed_parameters.extend( + _get_fixed_parameters_sbml( + petab_problem=petab_problem, + non_estimated_parameters_as_constants=non_estimated_parameters_as_constants, + ) + ) + + logger.debug(f"Fixed parameters are {fixed_parameters}") + logger.info(f"Overall fixed parameters: {len(fixed_parameters)}") + logger.info( + "Variable parameters: " + + str(len(sbml_model.getListOfParameters()) - len(fixed_parameters)) + ) + + # Create Python module from SBML model + sbml_importer.sbml2amici( + model_name=model_name, + output_dir=model_output_dir, + observables=observables, + constant_parameters=fixed_parameters, + sigmas=sigmas, + allow_reinit_fixpar_initcond=allow_reinit_fixpar_initcond, + noise_distributions=noise_distrs, + verbose=verbose, + **kwargs, + ) + + if kwargs.get( + "compile", + amici._get_default_argument(sbml_importer.sbml2amici, "compile"), + ): + # check that the model extension was compiled successfully + model_module = amici.import_model_module(model_name, model_output_dir) + model = model_module.getModel() + check_model(amici_model=model, petab_problem=petab_problem) + + return sbml_importer + + +def show_model_info(sbml_model: "libsbml.Model"): + """Log some model quantities""" + + logger.info(f"Species: {len(sbml_model.getListOfSpecies())}") + logger.info( + "Global parameters: " + str(len(sbml_model.getListOfParameters())) + ) + logger.info(f"Reactions: {len(sbml_model.getListOfReactions())}") + + +# TODO - remove?! +def species_to_parameters( + species_ids: list[str], sbml_model: "libsbml.Model" +) -> list[str]: + """ + Turn a SBML species into parameters and replace species references + inside the model instance. + + :param species_ids: + list of SBML species ID to convert to parameters with the same ID as + the replaced species. + + :param sbml_model: + SBML model to modify + + :return: + list of IDs of species which have been converted to parameters + """ + transformables = [] + + for species_id in species_ids: + species = sbml_model.getSpecies(species_id) + + if species.getHasOnlySubstanceUnits(): + logger.warning( + f"Ignoring {species.getId()} which has only substance units." + " Conversion not yet implemented." + ) + continue + + if math.isnan(species.getInitialConcentration()): + logger.warning( + f"Ignoring {species.getId()} which has no initial " + "concentration. Amount conversion not yet implemented." + ) + continue + + transformables.append(species_id) + + # Must not remove species while iterating over getListOfSpecies() + for species_id in transformables: + species = sbml_model.removeSpecies(species_id) + par = sbml_model.createParameter() + par.setId(species.getId()) + par.setName(species.getName()) + par.setConstant(True) + par.setValue(species.getInitialConcentration()) + par.setUnits(species.getUnits()) + + # Remove from reactants and products + for reaction in sbml_model.getListOfReactions(): + for species_id in transformables: + # loop, since removeX only removes one instance + while reaction.removeReactant(species_id): + # remove from reactants + pass + while reaction.removeProduct(species_id): + # remove from products + pass + while reaction.removeModifier(species_id): + # remove from modifiers + pass + + return transformables + + +def _add_global_parameter( + sbml_model: libsbml.Model, + parameter_id: str, + parameter_name: str = None, + constant: bool = False, + units: str = "dimensionless", + value: float = 0.0, +) -> libsbml.Parameter: + """Add new global parameter to SBML model + + Arguments: + sbml_model: SBML model + parameter_id: ID of the new parameter + parameter_name: Name of the new parameter + constant: Is parameter constant? + units: SBML unit ID + value: parameter value + + Returns: + The created parameter + """ + if parameter_name is None: + parameter_name = parameter_id + + p = sbml_model.createParameter() + p.setId(parameter_id) + p.setName(parameter_name) + p.setConstant(constant) + p.setValue(value) + p.setUnits(units) + return p + + +def _get_fixed_parameters_sbml( + petab_problem: petab.Problem, + non_estimated_parameters_as_constants=True, +) -> list[str]: + """ + Determine, set and return fixed model parameters. + + Non-estimated parameters and parameters specified in the condition table + are turned into constants (unless they are overridden). + Only global SBML parameters are considered. Local parameters are ignored. + + :param petab_problem: + The PEtab problem instance + + :param non_estimated_parameters_as_constants: + Whether parameters marked as non-estimated in PEtab should be + considered constant in AMICI. Setting this to ``True`` will reduce + model size and simulation times. If sensitivities with respect to those + parameters are required, this should be set to ``False``. + + :return: + list of IDs of parameters which are to be considered constant. + """ + if not petab_problem.model.type_id == MODEL_TYPE_SBML: + raise ValueError("Not an SBML model.") + # initial concentrations for species or initial compartment sizes in + # condition table will need to be turned into fixed parameters + + # if there is no initial assignment for that species, we'd need + # to create one. to avoid any naming collision right away, we don't + # allow that for now + + # we can't handle them yet + compartments = [ + col + for col in petab_problem.condition_df + if petab_problem.model.sbml_model.getCompartment(col) is not None + ] + if compartments: + raise NotImplementedError( + "Can't handle initial compartment sizes " + "at the moment. Consider creating an " + f"initial assignment for {compartments}" + ) + + fixed_parameters = get_fixed_parameters( + petab_problem, non_estimated_parameters_as_constants + ) + + # 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.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.evalf().is_Number: + fixed_parameters.remove(fixed_parameter) + continue + + return list(sorted(fixed_parameters)) + + +def _create_model_output_dir_name( + sbml_model: "libsbml.Model", model_name: str | None = None +) -> Path: + """ + Find a folder for storing the compiled amici model. + If possible, use the sbml model id, otherwise create a random folder. + The folder will be located in the `amici_models` subfolder of the current + folder. + """ + BASE_DIR = Path("amici_models").absolute() + BASE_DIR.mkdir(exist_ok=True) + # try model_name + if model_name: + return BASE_DIR / model_name + + # try sbml model id + if sbml_model_id := sbml_model.getId(): + return BASE_DIR / sbml_model_id + + # create random folder name + return Path(tempfile.mkdtemp(dir=BASE_DIR)) diff --git a/deps/AMICI/python/sdist/amici/petab/simulations.py b/deps/AMICI/python/sdist/amici/petab/simulations.py new file mode 100644 index 000000000..0f09c83b7 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/petab/simulations.py @@ -0,0 +1,490 @@ +"""Functionality related to simulation of PEtab problems. + +Functionality related to running simulations or evaluating the objective +function as defined by a PEtab problem. +""" + +import copy +import logging +from typing import Any +from collections.abc import Sequence + +import amici +import numpy as np +import pandas as pd +import petab.v1 as petab +from petab.v1.C import * # noqa: F403 + +from .. import AmiciExpData, AmiciModel +from ..logging import get_logger, log_execution_time + +# some extra imports for backward-compatibility +# DEPRECATED: remove in 1.0 +from .conditions import ( # noqa # pylint: disable=unused-import + create_edata_for_condition, + create_edatas, + create_parameterized_edatas, + fill_in_parameters, +) +from .parameter_mapping import ( # noqa # pylint: disable=unused-import + ParameterMapping, + create_parameter_mapping, + create_parameter_mapping_for_condition, +) +from .util import ( # noqa # pylint: disable=unused-import + get_states_in_condition_table, +) + +# END DEPRECATED + +try: + import pysb +except ImportError: + pysb = None + +logger = get_logger(__name__) + + +# string constant definitions +LLH = "llh" +SLLH = "sllh" +FIM = "fim" +S2LLH = "s2llh" +RES = "res" +SRES = "sres" +RDATAS = "rdatas" +EDATAS = "edatas" + + +__all__ = [ + "simulate_petab", + "LLH", + "SLLH", + "FIM", + "S2LLH", + "RES", + "SRES", + "RDATAS", + "EDATAS", +] + + +@log_execution_time("Simulating PEtab model", logger) +def simulate_petab( + petab_problem: petab.Problem, + amici_model: AmiciModel, + 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: bool | None = False, + log_level: int = logging.WARNING, + num_threads: int = 1, + failfast: bool = True, + scaled_gradients: bool = False, +) -> dict[str, Any]: + """Simulate PEtab model. + + .. note:: + Regardless of `scaled_parameters`, unscaled sensitivities are returned, + unless `scaled_gradients=True`. + + :param petab_problem: + PEtab problem to work on. + :param amici_model: + AMICI Model assumed to be compatible with ``petab_problem``. + :param solver: + An AMICI solver. Will use default options if None. + :param problem_parameters: + Run simulation with these parameters. If ``None``, PEtab + ``nominalValues`` will be used. To be provided as dict, mapping PEtab + problem parameters to SBML IDs. + :param simulation_conditions: + Result of :py:func:`petab.get_simulation_conditions`. Can be provided + to save time if this has be obtained before. + Not required if ``edatas`` and ``parameter_mapping`` are provided. + :param edatas: + Experimental data. Parameters are inserted in-place for simulation. + :param parameter_mapping: + Optional precomputed PEtab parameter mapping for efficiency, as + generated by :py:func:`create_parameter_mapping` with + ``scaled_parameters=True``. + :param scaled_parameters: + If ``True``, ``problem_parameters`` are assumed to be on the scale + provided in the PEtab parameter table and will be unscaled. + If ``False``, they are assumed to be in linear scale. + If `parameter_mapping` is provided, this must match the value of + `scaled_parameters` used to generate the mapping. + :param log_level: + Log level, see :mod:`amici.logging` module. + :param num_threads: + Number of threads to use for simulating multiple conditions + (only used if compiled with OpenMP). + :param failfast: + Returns as soon as an integration failure is encountered, skipping + any remaining simulations. + :param scaled_gradients: + Whether to compute gradients on parameter scale (``True``) or not + (``False``). + + :return: + Dictionary of + + * cost function value (``LLH``), + * list of :class:`amici.amici.ReturnData` (``RDATAS``), + * list of :class:`amici.amici.ExpData` (``EDATAS``), + + corresponding to the different simulation conditions. + For ordering of simulation conditions, see + :meth:`petab.Problem.get_simulation_conditions_from_measurement_df`. + """ + logger.setLevel(log_level) + + if solver is None: + solver = amici_model.getSolver() + + # number of amici simulations will be number of unique + # (preequilibrationConditionId, simulationConditionId) pairs. + # Can be optimized by checking for identical condition vectors. + if ( + simulation_conditions is None + and parameter_mapping is None + and edatas is None + ): + simulation_conditions = ( + petab_problem.get_simulation_conditions_from_measurement_df() + ) + + # Get parameter mapping + if parameter_mapping is None: + parameter_mapping = create_parameter_mapping( + petab_problem=petab_problem, + simulation_conditions=simulation_conditions, + # we will always use scaled parameters internally + scaled_parameters=True, + 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: + 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 + default_problem_parameters = { + par_id: par_value + for par_id, par_value in default_problem_parameters.items() + if par_id in free_parameters + } + + problem_parameters = default_problem_parameters | problem_parameters + + # Get edatas + if edatas is None: + # Generate ExpData with all condition-specific information + edatas = create_edatas( + amici_model=amici_model, + petab_problem=petab_problem, + simulation_conditions=simulation_conditions, + ) + + # Fill parameters in ExpDatas (in-place) + fill_in_parameters( + edatas=edatas, + problem_parameters=problem_parameters, + scaled_parameters=scaled_parameters, + parameter_mapping=parameter_mapping, + amici_model=amici_model, + ) + + # Simulate + rdatas = amici.runAmiciSimulations( + amici_model, + solver, + edata_list=edatas, + num_threads=num_threads, + failfast=failfast, + ) + + # Compute total llh + llh = sum(rdata["llh"] for rdata in rdatas) + # Compute total sllh + sllh = None + if solver.getSensitivityOrder() != amici.SensitivityOrder.none: + sllh = aggregate_sllh( + amici_model=amici_model, + rdatas=rdatas, + parameter_mapping=parameter_mapping, + petab_scale=scaled_parameters, + petab_problem=petab_problem, + edatas=edatas, + ) + if not scaled_gradients and sllh is not None: + sllh = { + parameter_id: rescale_sensitivity( + sensitivity=sensitivity, + parameter_value=problem_parameters[parameter_id], + old_scale=petab_problem.parameter_df.loc[ + parameter_id, PARAMETER_SCALE + ], + new_scale=LIN, + ) + for parameter_id, sensitivity in sllh.items() + } + + # Log results + sim_cond = petab_problem.get_simulation_conditions_from_measurement_df() + for i, rdata in enumerate(rdatas): + sim_cond_id = "N/A" if sim_cond.empty else sim_cond.iloc[i, :].values + logger.debug( + f"Condition: {sim_cond_id}, status: {rdata['status']}, " + f"llh: {rdata['llh']}" + ) + + return { + LLH: llh, + SLLH: sllh, + RDATAS: rdatas, + EDATAS: edatas, + } + + +def aggregate_sllh( + amici_model: AmiciModel, + rdatas: Sequence[amici.ReturnDataView], + parameter_mapping: ParameterMapping | None, + edatas: list[AmiciExpData], + petab_scale: bool = True, + petab_problem: petab.Problem = None, +) -> None | dict[str, float]: + """ + Aggregate likelihood gradient for all conditions, according to PEtab + parameter mapping. + + :param amici_model: + AMICI model from which ``rdatas`` were obtained. + :param rdatas: + Simulation results. + :param parameter_mapping: + PEtab parameter mapping to condition-specific simulation parameters. + :param edatas: + Experimental data used for simulation. + :param petab_scale: + Whether to check that sensitivities were computed with parameters on + the scales provided in the PEtab parameters table. + :param petab_problem: + The PEtab problem that defines the parameter scales. + + :return: + Aggregated likelihood sensitivities. + """ + accumulated_sllh = {} + model_parameter_ids = amici_model.getParameterIds() + + if petab_scale and petab_problem is None: + raise ValueError( + "Please provide the PEtab problem, when using " + "`petab_scale=True`." + ) + + # Check for issues in all condition simulation results. + for rdata in rdatas: + # Condition failed during simulation. + if rdata.status != amici.AMICI_SUCCESS: + return None + # Condition simulation result does not provide SLLH. + if rdata.sllh is None: + raise ValueError( + "The sensitivities of the likelihood for a condition were " + "not computed." + ) + + for condition_parameter_mapping, edata, rdata in zip( + parameter_mapping, edatas, rdatas, strict=True + ): + for sllh_parameter_index, condition_parameter_sllh in enumerate( + rdata.sllh + ): + # Get PEtab parameter ID + # Use ExpData if it provides a parameter list, else default to + # Model. + if edata.plist: + model_parameter_index = edata.plist[sllh_parameter_index] + else: + model_parameter_index = amici_model.plist(sllh_parameter_index) + model_parameter_id = model_parameter_ids[model_parameter_index] + petab_parameter_id = condition_parameter_mapping.map_sim_var[ + model_parameter_id + ] + + # Initialize + if petab_parameter_id not in accumulated_sllh: + accumulated_sllh[petab_parameter_id] = 0 + + # Check that the scale is consistent + if petab_scale: + # `ParameterMappingForCondition` objects provide the scale in + # terms of `petab.C` constants already, not AMICI equivalents. + model_parameter_scale = ( + condition_parameter_mapping.scale_map_sim_var[ + model_parameter_id + ] + ) + petab_parameter_scale = petab_problem.parameter_df.loc[ + petab_parameter_id, PARAMETER_SCALE + ] + if model_parameter_scale != petab_parameter_scale: + raise ValueError( + f"The scale of the parameter `{petab_parameter_id}` " + "differs between the AMICI model " + f"({model_parameter_scale}) and the PEtab problem " + f"({petab_parameter_scale})." + ) + + # Accumulate + accumulated_sllh[petab_parameter_id] += condition_parameter_sllh + + return accumulated_sllh + + +def rescale_sensitivity( + sensitivity: float, + parameter_value: float, + old_scale: str, + new_scale: str, +) -> float: + """Rescale a sensitivity between parameter scales. + + :param sensitivity: + The sensitivity corresponding to the parameter value. + :param parameter_value: + The parameter vector element, on ``old_scale``. + :param old_scale: + The scale of the parameter value. + :param new_scale: + The parameter scale on which to rescale the sensitivity. + + :return: + The rescaled sensitivity. + """ + LOG_E_10 = np.log(10) + + if old_scale == new_scale: + return sensitivity + + unscaled_parameter_value = petab.parameters.unscale( + parameter=parameter_value, + scale_str=old_scale, + ) + + scale = { + (LIN, LOG): lambda s: s * unscaled_parameter_value, + (LOG, LIN): lambda s: s / unscaled_parameter_value, + (LIN, LOG10): lambda s: s * (unscaled_parameter_value * LOG_E_10), + (LOG10, LIN): lambda s: s / (unscaled_parameter_value * LOG_E_10), + } + + scale[(LOG, LOG10)] = lambda s: scale[(LIN, LOG10)](scale[(LOG, LIN)](s)) + scale[(LOG10, LOG)] = lambda s: scale[(LIN, LOG)](scale[(LOG10, LIN)](s)) + + if (old_scale, new_scale) not in scale: + raise NotImplementedError( + f"Old scale: {old_scale}. New scale: {new_scale}." + ) + + return scale[(old_scale, new_scale)](sensitivity) + + +def rdatas_to_measurement_df( + rdatas: Sequence[amici.ReturnData], + model: AmiciModel, + measurement_df: pd.DataFrame, +) -> pd.DataFrame: + """ + Create a measurement dataframe in the PEtab format from the passed + ``rdatas`` and own information. + + :param rdatas: + A sequence of rdatas with the ordering of + :func:`petab.get_simulation_conditions`. + + :param model: + AMICI model used to generate ``rdatas``. + + :param measurement_df: + PEtab measurement table used to generate ``rdatas``. + + :return: + A dataframe built from the rdatas in the format of ``measurement_df``. + """ + simulation_conditions = petab.get_simulation_conditions(measurement_df) + + observable_ids = model.getObservableIds() + rows = [] + # iterate over conditions + for (_, condition), rdata in zip( + simulation_conditions.iterrows(), rdatas, strict=True + ): + # current simulation matrix + y = rdata.y + # time array used in rdata + t = list(rdata.ts) + + # extract rows for condition + cur_measurement_df = petab.get_rows_for_condition( + measurement_df, condition + ) + + # iterate over entries for the given condition + # note: this way we only generate a dataframe entry for every + # row that existed in the original dataframe. if we want to + # e.g. have also timepoints non-existent in the original file, + # we need to instead iterate over the rdata['y'] entries + for _, row in cur_measurement_df.iterrows(): + # copy row + row_sim = copy.deepcopy(row) + + # extract simulated measurement value + timepoint_idx = t.index(row[TIME]) + observable_idx = observable_ids.index(row[OBSERVABLE_ID]) + measurement_sim = y[timepoint_idx, observable_idx] + + # change measurement entry + row_sim[MEASUREMENT] = measurement_sim + + rows.append(row_sim) + + return pd.DataFrame(rows) + + +def rdatas_to_simulation_df( + rdatas: Sequence[amici.ReturnData], + model: AmiciModel, + measurement_df: pd.DataFrame, +) -> pd.DataFrame: + """Create a PEtab simulation dataframe from + :class:`amici.amici.ReturnData` s. + + See :func:`rdatas_to_measurement_df` for details, only that model outputs + will appear in column ``simulation`` instead of ``measurement``.""" + + df = rdatas_to_measurement_df( + rdatas=rdatas, model=model, measurement_df=measurement_df + ) + + return df.rename(columns={MEASUREMENT: SIMULATION}) diff --git a/deps/AMICI/python/sdist/amici/petab/simulator.py b/deps/AMICI/python/sdist/amici/petab/simulator.py new file mode 100644 index 000000000..407bf9f7f --- /dev/null +++ b/deps/AMICI/python/sdist/amici/petab/simulator.py @@ -0,0 +1,109 @@ +""" +PEtab Simulator +--------------- +Functionality related to the use of AMICI for simulation with :class:`petab.Simulator`. + +Use cases: + +- generate data for use with PEtab's plotting methods +- generate synthetic data +""" + +import inspect +import sys +from collections.abc import Callable + +import pandas as pd +import petab.v1 as petab +from amici import AmiciModel, SensitivityMethod_none + +from .petab_import import import_petab_problem +from .simulations import RDATAS, rdatas_to_measurement_df, simulate_petab + +AMICI_MODEL = "amici_model" +AMICI_SOLVER = "solver" +MODEL_NAME = "model_name" +MODEL_OUTPUT_DIR = "model_output_dir" + +PETAB_PROBLEM = "petab_problem" + + +class PetabSimulator(petab.simulate.Simulator): + """Implementation of the PEtab `Simulator` class that uses AMICI.""" + + def __init__(self, *args, amici_model: AmiciModel = None, **kwargs): + super().__init__(*args, **kwargs) + self.amici_model = amici_model + + def simulate_without_noise(self, **kwargs) -> pd.DataFrame: + """ + See :py:func:`petab.simulate.Simulator.simulate()` docstring. + + Additional keyword arguments can be supplied to specify arguments for + the AMICI PEtab import, simulate, and export methods. See the + docstrings for the respective methods for argument options: + - :py:func:`amici.petab_import.import_petab_problem`, and + - :py:func:`amici.petab_objective.simulate_petab`. + + Note that some arguments are expected to have already been specified + in the Simulator constructor (including the PEtab problem). + """ + if AMICI_MODEL in {*kwargs, *dir(self)} and ( + any( + k in kwargs + for k in inspect.signature(import_petab_problem).parameters + ) + ): + print( + "Arguments related to the PEtab import are unused if " + f"`{AMICI_MODEL}` is specified, or the " + "`PetabSimulator.simulate()` method was previously called." + ) + + kwargs[PETAB_PROBLEM] = self.petab_problem + + # The AMICI model instance for the PEtab problem is saved in the state, + # such that it need not be supplied with each request for simulated + # data. Any user-supplied AMICI model will overwrite the model saved + # in the state. + if AMICI_MODEL not in kwargs: + if self.amici_model is None: + if MODEL_NAME not in kwargs: + kwargs[MODEL_NAME] = AMICI_MODEL + # If the model name is the name of a module that is already + # cached, it can cause issues during import. + while kwargs[MODEL_NAME] in sys.modules: + kwargs[MODEL_NAME] += str(self.rng.integers(10)) + if MODEL_OUTPUT_DIR not in kwargs: + kwargs[MODEL_OUTPUT_DIR] = self.working_dir + self.amici_model = _subset_call(import_petab_problem, kwargs) + kwargs[AMICI_MODEL] = self.amici_model + self.amici_model = kwargs[AMICI_MODEL] + + if AMICI_SOLVER not in kwargs: + kwargs[AMICI_SOLVER] = self.amici_model.getSolver() + kwargs[AMICI_SOLVER].setSensitivityMethod(SensitivityMethod_none) + + result = _subset_call(simulate_petab, kwargs) + return rdatas_to_measurement_df( + result[RDATAS], self.amici_model, self.petab_problem.measurement_df + ) + + +def _subset_call(method: Callable, kwargs: dict): + """ + Helper function to call a method with the intersection of arguments in the + method signature and the supplied arguments. + + :param method: + The method to be called. + :param kwargs: + The argument superset as a dictionary, similar to ``**kwargs`` in + method signatures. + :return: + The output of ``method``, called with the applicable arguments in + ``kwargs``. + """ + method_args = inspect.signature(method).parameters + subset_kwargs = {k: v for k, v in kwargs.items() if k in method_args} + return method(**subset_kwargs) diff --git a/deps/AMICI/python/sdist/amici/petab/util.py b/deps/AMICI/python/sdist/amici/petab/util.py new file mode 100644 index 000000000..5392dcdb8 --- /dev/null +++ b/deps/AMICI/python/sdist/amici/petab/util.py @@ -0,0 +1,107 @@ +"""Various helper functions for working with PEtab problems.""" + +import re +from typing import TYPE_CHECKING + +import libsbml +import pandas as pd +import petab.v1 as petab +from petab.v1.C import PREEQUILIBRATION_CONDITION_ID, SIMULATION_CONDITION_ID +from petab.v1.mapping import resolve_mapping +from petab.v1.models import MODEL_TYPE_PYSB, MODEL_TYPE_SBML + +if TYPE_CHECKING: + pysb = None + + +def get_states_in_condition_table( + petab_problem: petab.Problem, + condition: dict | pd.Series = None, + return_patterns: bool = False, +) -> 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)`` + """ + if petab_problem.model.type_id not in (MODEL_TYPE_SBML, MODEL_TYPE_PYSB): + raise NotImplementedError() + + species_check_funs = { + MODEL_TYPE_SBML: lambda x: _element_is_sbml_state( + petab_problem.sbml_model, x + ), + MODEL_TYPE_PYSB: lambda x: _element_is_pysb_pattern( + petab_problem.model.model, x + ), + } + states = { + resolve_mapping(petab_problem.mapping_df, col): (None, None) + if condition is None + else ( + petab_problem.condition_df.loc[ + condition[SIMULATION_CONDITION_ID], col + ], + petab_problem.condition_df.loc[ + condition[PREEQUILIBRATION_CONDITION_ID], col + ] + if PREEQUILIBRATION_CONDITION_ID in condition + else None, + ) + for col in petab_problem.condition_df.columns + if species_check_funs[petab_problem.model.type_id]( + resolve_mapping(petab_problem.mapping_df, col) + ) + } + + if petab_problem.model.type_id == MODEL_TYPE_PYSB: + if return_patterns: + return states + import pysb.pattern + + if not petab_problem.model.model.species: + import pysb.bng + + pysb.bng.generate_equations(petab_problem.model.model) + + try: + spm = pysb.pattern.SpeciesPatternMatcher( + model=petab_problem.model.model + ) + except NotImplementedError: + raise NotImplementedError( + "Requires https://github.com/pysb/pysb/pull/570. " + "To use this functionality, update pysb via " + "`pip install git+https://github.com/FFroehlich/pysb@fix_pattern_matching`" + ) + + # expose model components as variables so we can evaluate patterns + for c in petab_problem.model.model.components: + globals()[c.name] = c + + states = { + f"__s{ix}": value + for pattern, value in states.items() + for ix in spm.match(eval(pattern), index=True, exact=True) + } + return states + + +def _element_is_pysb_pattern(model: "pysb.Model", element: str) -> bool: + """Check if element is a pysb pattern""" + if match := re.match(r"[a-zA-Z_][\w_]*\(", element): + return match[0][:-1] in [m.name for m in model.monomers] + return False + + +def _element_is_sbml_state(sbml_model: libsbml.Model, sbml_id: str) -> bool: + """Does the element with ID `sbml_id` correspond to a state variable?""" + if sbml_model.getCompartment(sbml_id) is not None: + return True + if sbml_model.getSpecies(sbml_id) is not None: + return True + if ( + rule := sbml_model.getRuleByVariable(sbml_id) + ) is not None and rule.getTypeCode() == libsbml.SBML_RATE_RULE: + return True + + return False diff --git a/deps/AMICI/python/sdist/amici/petab_import.py b/deps/AMICI/python/sdist/amici/petab_import.py index 23fe4394f..1bcadaa1d 100644 --- a/deps/AMICI/python/sdist/amici/petab_import.py +++ b/deps/AMICI/python/sdist/amici/petab_import.py @@ -3,1052 +3,45 @@ ------------ Import a model in the :mod:`petab` (https://github.com/PEtab-dev/PEtab) format into AMICI. -""" -import argparse -import importlib -import logging -import math -import os -import re -import shutil -import tempfile -from itertools import chain -from pathlib import Path -from typing import Dict, List, Optional, Tuple, Union -from warnings import warn - -import amici -import libsbml -import pandas as pd -import petab -import sympy as sp -from _collections import OrderedDict -from amici.logging import get_logger, log_execution_time, set_log_level -from petab.C import * -from petab.models import MODEL_TYPE_PYSB, MODEL_TYPE_SBML -from petab.parameters import get_valid_parameters_for_parameter_table -from sympy.abc import _clash - -from .petab_util import PREEQ_INDICATOR_ID, get_states_in_condition_table - -try: - from amici.petab_import_pysb import import_model_pysb -except ModuleNotFoundError: - # pysb not available - import_model_pysb = None - -logger = get_logger(__name__, logging.WARNING) - - -def _add_global_parameter( - sbml_model: libsbml.Model, - parameter_id: str, - parameter_name: str = None, - constant: bool = False, - units: str = "dimensionless", - value: float = 0.0, -) -> libsbml.Parameter: - """Add new global parameter to SBML model - - Arguments: - sbml_model: SBML model - parameter_id: ID of the new parameter - parameter_name: Name of the new parameter - constant: Is parameter constant? - units: SBML unit ID - value: parameter value - - Returns: - The created parameter - """ - if parameter_name is None: - parameter_name = parameter_id - - p = sbml_model.createParameter() - p.setId(parameter_id) - p.setName(parameter_name) - p.setConstant(constant) - p.setValue(value) - p.setUnits(units) - return p - - -def get_fixed_parameters( - petab_problem: petab.Problem, - non_estimated_parameters_as_constants=True, -) -> List[str]: - """ - Determine, set and return fixed model parameters. - - Non-estimated parameters and parameters specified in the condition table - are turned into constants (unless they are overridden). - Only global SBML parameters are considered. Local parameters are ignored. - - :param petab_problem: - The PEtab problem instance - - :param non_estimated_parameters_as_constants: - Whether parameters marked as non-estimated in PEtab should be - considered constant in AMICI. Setting this to ``True`` will reduce - model size and simulation times. If sensitivities with respect to those - parameters are required, this should be set to ``False``. - - :return: - List of IDs of parameters which are to be considered constant. - """ - if petab_problem.model.type_id == MODEL_TYPE_SBML: - # initial concentrations for species or initial compartment sizes in - # condition table will need to be turned into fixed parameters - - # if there is no initial assignment for that species, we'd need - # to create one. to avoid any naming collision right away, we don't - # allow that for now - - # we can't handle them yet - compartments = [ - col - for col in petab_problem.condition_df - if petab_problem.model.sbml_model.getCompartment(col) is not None - ] - if compartments: - raise NotImplementedError( - "Can't handle initial compartment sizes " - "at the moment. Consider creating an " - f"initial assignment for {compartments}" - ) - - # if we have a parameter table, all parameters that are allowed to be - # listed in the parameter table, but are not marked as estimated, can be - # turned into AMICI constants - # due to legacy API, we might not always have a parameter table, though - fixed_parameters = set() - if petab_problem.parameter_df is not None: - all_parameters = get_valid_parameters_for_parameter_table( - model=petab_problem.model, - condition_df=petab_problem.condition_df, - observable_df=petab_problem.observable_df - if petab_problem.observable_df is not None - else pd.DataFrame(columns=petab.OBSERVABLE_DF_REQUIRED_COLS), - measurement_df=petab_problem.measurement_df - if petab_problem.measurement_df is not None - else pd.DataFrame(columns=petab.MEASUREMENT_DF_REQUIRED_COLS), - ) - if non_estimated_parameters_as_constants: - estimated_parameters = petab_problem.parameter_df.index.values[ - petab_problem.parameter_df[ESTIMATE] == 1 - ] - else: - # don't treat parameter table parameters as constants - estimated_parameters = petab_problem.parameter_df.index.values - fixed_parameters = set(all_parameters) - set(estimated_parameters) - - # Column names are model parameter IDs, compartment IDs or species IDs. - # Thereof, all parameters except for any overridden ones should be made - # constant. - # (Could potentially still be made constant, but leaving them might - # increase model reusability) - - # handle parameters in condition table - condition_df = petab_problem.condition_df - if condition_df is not None: - logger.debug(f"Condition table: {condition_df.shape}") - - # remove overridden parameters (`object`-type columns) - fixed_parameters.update( - p - for p in condition_df.columns - # get rid of conditionName column - if p != CONDITION_NAME - # there is no parametric override - # TODO: could check if the final overriding parameter is estimated - # or not, but for now, we skip the parameter if there is any kind - # of overriding - if condition_df[p].dtype != "O" - # p is a parameter - and not petab_problem.model.is_state_variable(p) - ) - - # Ensure mentioned parameters exist in the model. Remove additional ones - # from list - for fixed_parameter in fixed_parameters.copy(): - # check global parameters - if not petab_problem.model.has_entity_with_id(fixed_parameter): - # TODO: could still exist as an output parameter? - logger.warning( - f"Column '{fixed_parameter}' used in condition " - "table but not entity with the corresponding ID " - "exists. Ignoring." - ) - fixed_parameters.remove(fixed_parameter) - - if petab_problem.model.type_id == MODEL_TYPE_SBML: - # exclude targets of rules or initial assignments - sbml_model = petab_problem.model.sbml_model - for fixed_parameter in fixed_parameters.copy(): - # check global parameters - if sbml_model.getInitialAssignmentBySymbol( - fixed_parameter - ) or sbml_model.getRuleByVariable(fixed_parameter): - fixed_parameters.remove(fixed_parameter) - - return list(sorted(fixed_parameters)) - - -def species_to_parameters( - species_ids: List[str], sbml_model: "libsbml.Model" -) -> List[str]: - """ - Turn a SBML species into parameters and replace species references - inside the model instance. - - :param species_ids: - List of SBML species ID to convert to parameters with the same ID as - the replaced species. - - :param sbml_model: - SBML model to modify - - :return: - List of IDs of species which have been converted to parameters - """ - transformables = [] - - for species_id in species_ids: - species = sbml_model.getSpecies(species_id) - - if species.getHasOnlySubstanceUnits(): - logger.warning( - f"Ignoring {species.getId()} which has only substance units." - " Conversion not yet implemented." - ) - continue - - if math.isnan(species.getInitialConcentration()): - logger.warning( - f"Ignoring {species.getId()} which has no initial " - "concentration. Amount conversion not yet implemented." - ) - continue - - transformables.append(species_id) - - # Must not remove species while iterating over getListOfSpecies() - for species_id in transformables: - species = sbml_model.removeSpecies(species_id) - par = sbml_model.createParameter() - par.setId(species.getId()) - par.setName(species.getName()) - par.setConstant(True) - par.setValue(species.getInitialConcentration()) - par.setUnits(species.getUnits()) - - # Remove from reactants and products - for reaction in sbml_model.getListOfReactions(): - for species_id in transformables: - # loop, since removeX only removes one instance - while reaction.removeReactant(species_id): - # remove from reactants - pass - while reaction.removeProduct(species_id): - # remove from products - pass - while reaction.removeModifier(species_id): - # remove from modifiers - pass - - return transformables - - -def import_petab_problem( - petab_problem: petab.Problem, - model_output_dir: Union[str, Path, None] = None, - model_name: str = None, - force_compile: bool = False, - non_estimated_parameters_as_constants=True, - **kwargs, -) -> "amici.Model": - """ - Import model from petab problem. - - :param petab_problem: - A petab problem containing all relevant information on the model. - - :param model_output_dir: - Directory to write the model code to. Will be created if doesn't - exist. Defaults to current directory. - - :param model_name: - Name of the generated model. If model file name was provided, - this defaults to the file name without extension, otherwise - the model ID will be used. - - :param force_compile: - Whether to compile the model even if the target folder is not empty, - or the model exists already. - - :param non_estimated_parameters_as_constants: - Whether parameters marked as non-estimated in PEtab should be - considered constant in AMICI. Setting this to ``True`` will reduce - model size and simulation times. If sensitivities with respect to those - parameters are required, this should be set to ``False``. - - :param kwargs: - Additional keyword arguments to be passed to - :meth:`amici.sbml_import.SbmlImporter.sbml2amici`. - - :return: - The imported model. - """ - if petab_problem.model.type_id not in (MODEL_TYPE_SBML, MODEL_TYPE_PYSB): - raise NotImplementedError( - "Unsupported model type " + petab_problem.model.type_id - ) - - if petab_problem.mapping_df is not None: - # It's partially supported. Remove at your own risk... - raise NotImplementedError( - "PEtab v2.0.0 mapping tables are not yet supported." - ) - - model_name = model_name or petab_problem.model.model_id - - if petab_problem.model.type_id == MODEL_TYPE_PYSB and model_name is None: - model_name = petab_problem.pysb_model.name - elif model_name is None and model_output_dir: - model_name = _create_model_name(model_output_dir) - - # generate folder and model name if necessary - if model_output_dir is None: - if petab_problem.model.type_id == MODEL_TYPE_PYSB: - raise ValueError("Parameter `model_output_dir` is required.") - - model_output_dir = _create_model_output_dir_name( - petab_problem.sbml_model, model_name - ) - else: - model_output_dir = os.path.abspath(model_output_dir) - - # create folder - if not os.path.exists(model_output_dir): - os.makedirs(model_output_dir) - - # check if compilation necessary - if force_compile or not _can_import_model(model_name, model_output_dir): - # check if folder exists - if os.listdir(model_output_dir) and not force_compile: - raise ValueError( - f"Cannot compile to {model_output_dir}: not empty. " - "Please assign a different target or set `force_compile`." - ) - - # remove folder if exists - if os.path.exists(model_output_dir): - shutil.rmtree(model_output_dir) - - logger.info(f"Compiling model {model_name} to {model_output_dir}.") - # compile the model - if petab_problem.model.type_id == MODEL_TYPE_PYSB: - import_model_pysb( - petab_problem, - model_name=model_name, - model_output_dir=model_output_dir, - **kwargs, - ) - else: - import_model_sbml( - petab_problem=petab_problem, - model_name=model_name, - model_output_dir=model_output_dir, - non_estimated_parameters_as_constants=non_estimated_parameters_as_constants, - **kwargs, - ) - - # import model - model_module = amici.import_model_module(model_name, model_output_dir) - model = model_module.getModel() - check_model(amici_model=model, petab_problem=petab_problem) - - logger.info( - f"Successfully loaded model {model_name} " f"from {model_output_dir}." - ) - - return model - - -def check_model( - amici_model: amici.Model, - petab_problem: petab.Problem, -) -> None: - """Check that the model is consistent with the PEtab problem.""" - if petab_problem.parameter_df is None: - return - - amici_ids_free = set(amici_model.getParameterIds()) - amici_ids = amici_ids_free | set(amici_model.getFixedParameterIds()) - - petab_ids_free = set( - petab_problem.parameter_df.loc[ - petab_problem.parameter_df[ESTIMATE] == 1 - ].index - ) - - amici_ids_free_required = petab_ids_free.intersection(amici_ids) - - if not amici_ids_free_required.issubset(amici_ids_free): - raise ValueError( - "The available AMICI model does not support estimating the " - "following parameters. Please recompile the model and ensure " - "that these parameters are not treated as constants. Deleting " - "the current model might also resolve this. Parameters: " - f"{amici_ids_free_required.difference(amici_ids_free)}" - ) - - -def _create_model_output_dir_name( - sbml_model: "libsbml.Model", model_name: Optional[str] = None -) -> Path: - """ - Find a folder for storing the compiled amici model. - If possible, use the sbml model id, otherwise create a random folder. - The folder will be located in the `amici_models` subfolder of the current - folder. - """ - BASE_DIR = Path("amici_models").absolute() - BASE_DIR.mkdir(exist_ok=True) - # try model_name - if model_name: - return BASE_DIR / model_name - - # try sbml model id - if sbml_model_id := sbml_model.getId(): - return BASE_DIR / sbml_model_id - - # create random folder name - return Path(tempfile.mkdtemp(dir=BASE_DIR)) - - -def _create_model_name(folder: Union[str, Path]) -> str: - """ - Create a name for the model. - Just re-use the last part of the folder. - """ - return os.path.split(os.path.normpath(folder))[-1] - - -def _can_import_model( - model_name: str, model_output_dir: Union[str, Path] -) -> bool: - """ - Check whether a module of that name can already be imported. - """ - # try to import (in particular checks version) - try: - with amici.add_path(model_output_dir): - model_module = importlib.import_module(model_name) - except ModuleNotFoundError: - return False - - # no need to (re-)compile - return hasattr(model_module, "getModel") - - -@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, - petab_problem: petab.Problem = None, - model_name: Optional[str] = None, - model_output_dir: Optional[Union[str, Path]] = None, - verbose: Optional[Union[bool, int]] = True, - allow_reinit_fixpar_initcond: bool = True, - validate: bool = True, - non_estimated_parameters_as_constants=True, - output_parameter_defaults: Optional[Dict[str, float]] = None, - discard_sbml_annotations: bool = False, - **kwargs, -) -> amici.SbmlImporter: - """ - Create AMICI model from PEtab problem - - :param sbml_model: - 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. - - :param model_name: - Name of the generated model. If model file name was provided, - this defaults to the file name without extension, otherwise - the SBML model ID will be used. - - :param model_output_dir: - Directory to write the model code to. Will be created if doesn't - exist. Defaults to current directory. - - :param verbose: - Print/log extra information. - - :param allow_reinit_fixpar_initcond: - See :class:`amici.de_export.ODEExporter`. Must be enabled if initial - states are to be reset after preequilibration. - - :param validate: - Whether to validate the PEtab problem - - :param non_estimated_parameters_as_constants: - Whether parameters marked as non-estimated in PEtab should be - considered constant in AMICI. Setting this to ``True`` will reduce - model size and simulation times. If sensitivities with respect to those - parameters are required, this should be set to ``False``. - - :param output_parameter_defaults: - Optional default parameter values for output parameters introduced in - the PEtab observables table, in particular for placeholder parameters. - Dictionary mapping parameter IDs to default values. - - :param discard_sbml_annotations: - Discard information contained in AMICI SBML annotations (debug). - - :param kwargs: - Additional keyword arguments to be passed to - :meth:`amici.sbml_import.SbmlImporter.sbml2amici`. - - :return: - The created :class:`amici.sbml_import.SbmlImporter` instance. - """ - from petab.models.sbml_model import SbmlModel - - set_log_level(logger, verbose) - - 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), - ) - - if petab_problem.observable_df is None: - raise NotImplementedError( - "PEtab import without observables table " - "is currently not supported." - ) - - assert isinstance(petab_problem.model, SbmlModel) - - if validate: - logger.info("Validating PEtab problem ...") - petab.lint_problem(petab_problem) - - # Model name from SBML ID or filename - if model_name is None: - if not (model_name := petab_problem.model.sbml_model.getId()): - if not isinstance(sbml_model, (str, Path)): - raise ValueError( - "No `model_name` was provided and no model " - "ID was specified in the SBML model." - ) - model_name = os.path.splitext(os.path.split(sbml_model)[-1])[0] - - if model_output_dir is None: - model_output_dir = os.path.join( - os.getcwd(), f"{model_name}-amici{amici.__version__}" - ) - - logger.info( - f"Model name is '{model_name}'.\n" - f"Writing model code to '{model_output_dir}'." - ) - - # Create a copy, because it will be modified by SbmlImporter - sbml_doc = petab_problem.model.sbml_model.getSBMLDocument().clone() - sbml_model = sbml_doc.getModel() - - show_model_info(sbml_model) - - sbml_importer = amici.SbmlImporter( - sbml_model, - discard_annotations=discard_sbml_annotations, - ) - sbml_model = sbml_importer.sbml - - allow_n_noise_pars = ( - not petab.lint.observable_table_has_nontrivial_noise_formula( - petab_problem.observable_df - ) - ) - if ( - petab_problem.measurement_df is not None - and petab.lint.measurement_table_has_timepoint_specific_mappings( - petab_problem.measurement_df, - allow_scalar_numeric_noise_parameters=allow_n_noise_pars, - ) - ): - raise ValueError( - "AMICI does not support importing models with timepoint specific " - "mappings for noise or observable parameters. Please flatten " - "the problem and try again." - ) - - if petab_problem.observable_df is not None: - observables, noise_distrs, sigmas = get_observation_model( - petab_problem.observable_df - ) - else: - observables = noise_distrs = sigmas = None - - logger.info(f"Observables: {len(observables)}") - logger.info(f"Sigmas: {len(sigmas)}") - - if len(sigmas) != len(observables): - raise AssertionError( - f"Number of provided observables ({len(observables)}) and sigmas " - f"({len(sigmas)}) do not match." - ) - - # TODO: adding extra output parameters is currently not supported, - # so we add any output parameters to the SBML model. - # this should be changed to something more elegant - # - formulas = chain( - (val["formula"] for val in observables.values()), sigmas.values() - ) - output_parameters = OrderedDict() - for formula in formulas: - # we want reproducible parameter ordering upon repeated import - free_syms = sorted( - sp.sympify(formula, locals=_clash).free_symbols, - key=lambda symbol: symbol.name, - ) - for free_sym in free_syms: - sym = str(free_sym) - if ( - sbml_model.getElementBySId(sym) is None - and sym != "time" - and sym not in observables - ): - output_parameters[sym] = None - logger.debug( - "Adding output parameters to model: " - f"{list(output_parameters.keys())}" - ) - output_parameter_defaults = output_parameter_defaults or {} - if extra_pars := ( - set(output_parameter_defaults) - set(output_parameters.keys()) - ): - raise ValueError( - f"Default output parameter values were given for {extra_pars}, " - "but they those are not output parameters." - ) - - for par in output_parameters.keys(): - _add_global_parameter( - sbml_model=sbml_model, - parameter_id=par, - value=output_parameter_defaults.get(par, 0.0), - ) - # - - # TODO: to parameterize initial states or compartment sizes, we currently - # need initial assignments. if they occur in the condition table, we - # create a new parameter initial_${speciesOrCompartmentID}. - # feels dirty and should be changed (see also #924) - # - - initial_states = get_states_in_condition_table(petab_problem) - fixed_parameters = [] - if initial_states: - # 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 " - f"{PREEQ_INDICATOR_ID}. Cannot handle " - "species and compartments in condition table " - "then." - ) - indicator = sbml_model.createParameter() - indicator.setId(PREEQ_INDICATOR_ID) - indicator.setName(PREEQ_INDICATOR_ID) - # Can only reset parameters after preequilibration if they are fixed. - fixed_parameters.append(PREEQ_INDICATOR_ID) - logger.debug( - "Adding preequilibration indicator " - f"constant {PREEQ_INDICATOR_ID}" - ) - logger.debug(f"Adding initial assignments for {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]: - if sbml_model.getElementBySId(init_par_id) is not None: - raise ValueError( - "Cannot create parameter for initial assignment " - f"for {assignee_id} because an entity named " - f"{init_par_id} exists already in the model." - ) - init_par = sbml_model.createParameter() - init_par.setId(init_par_id) - init_par.setName(init_par_id) - assignment = sbml_model.getInitialAssignment(assignee_id) - if assignment is None: - assignment = sbml_model.createInitialAssignment() - assignment.setSymbol(assignee_id) - else: - logger.debug( - "The SBML model has an initial assignment defined " - f"for model entity {assignee_id}, but this entity " - "also has an initial value defined in the PEtab " - "condition table. The SBML initial assignment will " - "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}" - ) - math_ast = libsbml.parseL3Formula(formula) - assignment.setMath(math_ast) - # - - fixed_parameters.extend( - get_fixed_parameters( - petab_problem=petab_problem, - non_estimated_parameters_as_constants=non_estimated_parameters_as_constants, - ) - ) - - logger.debug(f"Fixed parameters are {fixed_parameters}") - logger.info(f"Overall fixed parameters: {len(fixed_parameters)}") - logger.info( - "Variable parameters: " - + str(len(sbml_model.getListOfParameters()) - len(fixed_parameters)) - ) - - # Create Python module from SBML model - sbml_importer.sbml2amici( - model_name=model_name, - output_dir=model_output_dir, - observables=observables, - constant_parameters=fixed_parameters, - sigmas=sigmas, - allow_reinit_fixpar_initcond=allow_reinit_fixpar_initcond, - noise_distributions=noise_distrs, - verbose=verbose, - **kwargs, - ) - - if kwargs.get( - "compile", - amici._get_default_argument(sbml_importer.sbml2amici, "compile"), - ): - # check that the model extension was compiled successfully - model_module = amici.import_model_module(model_name, model_output_dir) - model = model_module.getModel() - check_model(amici_model=model, petab_problem=petab_problem) - - return sbml_importer - - -# for backwards compatibility -import_model = import_model_sbml - - -def get_observation_model( - observable_df: pd.DataFrame, -) -> Tuple[ - Dict[str, Dict[str, str]], Dict[str, str], Dict[str, Union[str, float]] -]: - """ - Get observables, sigmas, and noise distributions from PEtab observation - table in a format suitable for - :meth:`amici.sbml_import.SbmlImporter.sbml2amici`. - - :param observable_df: - PEtab observables table - - :return: - Tuple of dicts with observables, noise distributions, and sigmas. - """ - if observable_df is None: - return {}, {}, {} - - observables = {} - sigmas = {} - - nan_pat = r"^[nN]a[nN]$" - for _, observable in observable_df.iterrows(): - oid = str(observable.name) - # need to sanitize due to https://github.com/PEtab-dev/PEtab/issues/447 - name = re.sub(nan_pat, "", str(observable.get(OBSERVABLE_NAME, ""))) - formula_obs = re.sub(nan_pat, "", str(observable[OBSERVABLE_FORMULA])) - formula_noise = re.sub(nan_pat, "", str(observable[NOISE_FORMULA])) - observables[oid] = {"name": name, "formula": formula_obs} - sigmas[oid] = formula_noise - - # PEtab does currently not allow observables in noiseFormula and AMICI - # cannot handle states in sigma expressions. Therefore, where possible, - # replace species occurring in error model definition by observableIds. - replacements = { - sp.sympify(observable["formula"], locals=_clash): sp.Symbol( - observable_id - ) - for observable_id, observable in observables.items() - } - for observable_id, formula in sigmas.items(): - repl = sp.sympify(formula, locals=_clash).subs(replacements) - sigmas[observable_id] = str(repl) - - noise_distrs = petab_noise_distributions_to_amici(observable_df) - - return observables, noise_distrs, sigmas - - -def petab_noise_distributions_to_amici( - observable_df: pd.DataFrame, -) -> Dict[str, str]: - """ - Map from the petab to the amici format of noise distribution - identifiers. - - :param observable_df: - PEtab observable table - - :return: - Dictionary of observable_id => AMICI noise-distributions - """ - amici_distrs = {} - for _, observable in observable_df.iterrows(): - amici_val = "" - - if ( - OBSERVABLE_TRANSFORMATION in observable - and isinstance(observable[OBSERVABLE_TRANSFORMATION], str) - and observable[OBSERVABLE_TRANSFORMATION] - ): - amici_val += observable[OBSERVABLE_TRANSFORMATION] + "-" - - if ( - NOISE_DISTRIBUTION in observable - and isinstance(observable[NOISE_DISTRIBUTION], str) - and observable[NOISE_DISTRIBUTION] - ): - amici_val += observable[NOISE_DISTRIBUTION] - else: - amici_val += "normal" - amici_distrs[observable.name] = amici_val - - return amici_distrs - - -def petab_scale_to_amici_scale(scale_str: str) -> int: - """Convert PEtab parameter scaling string to AMICI scaling integer""" - - if scale_str == petab.LIN: - return amici.ParameterScaling_none - if scale_str == petab.LOG: - return amici.ParameterScaling_ln - if scale_str == petab.LOG10: - return amici.ParameterScaling_log10 - - raise ValueError(f"Invalid parameter scale {scale_str}") - - -def show_model_info(sbml_model: "libsbml.Model"): - """Log some model quantities""" - - logger.info(f"Species: {len(sbml_model.getListOfSpecies())}") - logger.info( - "Global parameters: " + str(len(sbml_model.getListOfParameters())) - ) - logger.info(f"Reactions: {len(sbml_model.getListOfReactions())}") - - -def _parse_cli_args(): - """ - Parse command line arguments - - :return: - Parsed CLI arguments from :mod:`argparse`. - """ - parser = argparse.ArgumentParser( - description="Import PEtab-format model into AMICI." - ) - - # General options: - parser.add_argument( - "-v", - "--verbose", - dest="verbose", - action="store_true", - help="More verbose output", - ) - parser.add_argument( - "-o", - "--output-dir", - dest="model_output_dir", - help="Name of the model directory to create", - ) - parser.add_argument( - "--no-compile", - action="store_false", - dest="compile", - help="Only generate model code, do not compile", - ) - parser.add_argument( - "--no-validate", - action="store_false", - dest="validate", - help="Skip validation of PEtab files", - ) - parser.add_argument( - "--flatten", - dest="flatten", - default=False, - action="store_true", - help="Flatten measurement specific overrides of " - "observable and noise parameters", - ) - parser.add_argument( - "--no-sensitivities", - dest="generate_sensitivity_code", - default=True, - action="store_false", - help="Skip generation of sensitivity code", - ) - - # Call with set of files - parser.add_argument( - "-s", "--sbml", dest="sbml_file_name", help="SBML model filename" - ) - parser.add_argument( - "-m", - "--measurements", - dest="measurement_file_name", - help="Measurement table", - ) - parser.add_argument( - "-c", - "--conditions", - dest="condition_file_name", - help="Conditions table", - ) - parser.add_argument( - "-p", - "--parameters", - dest="parameter_file_name", - help="Parameter table", - ) - parser.add_argument( - "-b", - "--observables", - dest="observable_file_name", - help="Observable table", - ) - - parser.add_argument( - "-y", - "--yaml", - dest="yaml_file_name", - help="PEtab YAML problem filename", - ) - - parser.add_argument( - "-n", - "--model-name", - dest="model_name", - help="Name of the python module generated for the " "model", - ) - - args = parser.parse_args() - - if not args.yaml_file_name and not all( - ( - args.sbml_file_name, - args.condition_file_name, - args.observable_file_name, - ) - ): - parser.error( - "When not specifying a model name or YAML file, then " - "SBML, condition and observable file must be specified" - ) - - return args - - -def _main(): - """ - Command line interface to import a model in the PEtab - (https://github.com/PEtab-dev/PEtab/) format into AMICI. - """ - args = _parse_cli_args() - - 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, - ) - - # Check for valid PEtab before potentially modifying it - if args.validate: - petab.lint_problem(pp) - - if args.flatten: - petab.flatten_timepoint_specific_output_overrides(pp) - - import_model( - 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, - model_output_dir=args.model_output_dir, - compile=args.compile, - generate_sensitivity_code=args.generate_sensitivity_code, - verbose=args.verbose, - validate=False, - ) +.. deprecated:: 0.21.0 + Use :mod:`amici.petab` instead. +""" -if __name__ == "__main__": - _main() +import warnings + +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 + get_observation_model, + petab_noise_distributions_to_amici, + petab_scale_to_amici_scale, +) + +# DEPRECATED - DON'T ADD ANYTHING NEW HERE +from .petab.petab_import import ( # noqa # pylint: disable=unused-import + check_model, + import_model, + import_model_sbml, + import_petab_problem, +) +from .petab.sbml_import import ( # noqa + _get_fixed_parameters_sbml as get_fixed_parameters, +) +from .petab.sbml_import import species_to_parameters # noqa + +__all__ = [ + "get_observation_model", + "petab_noise_distributions_to_amici", + "petab_scale_to_amici_scale", + "check_model", + "import_model", + "import_model_sbml", + "import_petab_problem", + "get_fixed_parameters", + "species_to_parameters", +] diff --git a/deps/AMICI/python/sdist/amici/petab_import_pysb.py b/deps/AMICI/python/sdist/amici/petab_import_pysb.py index 8036d1358..4d73a4bab 100644 --- a/deps/AMICI/python/sdist/amici/petab_import_pysb.py +++ b/deps/AMICI/python/sdist/amici/petab_import_pysb.py @@ -1,274 +1,22 @@ """ -PySB-PEtab Import ------------------ -Import a model in the PySB-adapted :mod:`petab` -(https://github.com/PEtab-dev/PEtab) format into AMICI. -""" - -import logging -import re -from pathlib import Path -from typing import Optional, Union - -import petab -import pysb -import pysb.bng -import sympy as sp -from petab.C import CONDITION_NAME, NOISE_FORMULA, OBSERVABLE_FORMULA -from petab.models.pysb_model import PySBModel - -from .logging import get_logger, log_execution_time, set_log_level -from .petab_util import PREEQ_INDICATOR_ID, get_states_in_condition_table - -logger = get_logger(__name__, logging.WARNING) - - -def _add_observation_model( - pysb_model: pysb.Model, petab_problem: petab.Problem -): - """Extend PySB model by observation model as defined in the PEtab - observables table""" - - # add any required output parameters - local_syms = { - sp.Symbol.__str__(comp): comp - for comp in pysb_model.components - if isinstance(comp, sp.Symbol) - } - for formula in [ - *petab_problem.observable_df[OBSERVABLE_FORMULA], - *petab_problem.observable_df[NOISE_FORMULA], - ]: - sym = sp.sympify(formula, locals=local_syms) - for s in sym.free_symbols: - if not isinstance(s, pysb.Component): - p = pysb.Parameter(str(s), 1.0) - pysb_model.add_component(p) - local_syms[sp.Symbol.__str__(p)] = p - - # add observables and sigmas to pysb model - for observable_id, observable_formula, noise_formula in zip( - petab_problem.observable_df.index, - petab_problem.observable_df[OBSERVABLE_FORMULA], - petab_problem.observable_df[NOISE_FORMULA], - ): - obs_symbol = sp.sympify(observable_formula, locals=local_syms) - if observable_id in pysb_model.expressions.keys(): - obs_expr = pysb_model.expressions[observable_id] - else: - obs_expr = pysb.Expression(observable_id, obs_symbol) - pysb_model.add_component(obs_expr) - local_syms[observable_id] = obs_expr - - sigma_id = f"{observable_id}_sigma" - sigma_symbol = sp.sympify(noise_formula, locals=local_syms) - sigma_expr = pysb.Expression(sigma_id, sigma_symbol) - pysb_model.add_component(sigma_expr) - local_syms[sigma_id] = sigma_expr - - -def _add_initialization_variables( - pysb_model: pysb.Model, petab_problem: petab.Problem -): - """Add initialization variables to the PySB model to support initial - conditions specified in the PEtab condition table. - - To parameterize initial states, we currently need initial assignments. - If they occur in the condition table, we create a new parameter - initial_${speciesID}. Feels dirty and should be changed (see also #924). - """ - - initial_states = get_states_in_condition_table(petab_problem) - fixed_parameters = [] - if initial_states: - # add preequilibration indicator variable - # NOTE: would only be required if we actually have preequilibration - # adding it anyways. can be optimized-out later - if PREEQ_INDICATOR_ID in [c.name for c in pysb_model.components]: - raise AssertionError( - "Model already has a component with ID " - f"{PREEQ_INDICATOR_ID}. Cannot handle " - "species and compartments in condition table " - "then." - ) - preeq_indicator = pysb.Parameter(PREEQ_INDICATOR_ID) - pysb_model.add_component(preeq_indicator) - # Can only reset parameters after preequilibration if they are fixed. - fixed_parameters.append(PREEQ_INDICATOR_ID) - logger.debug( - "Adding preequilibration indicator constant " - f"{PREEQ_INDICATOR_ID}" - ) - logger.debug(f"Adding initial assignments for {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]: - if init_par_id in [c.name for c in pysb_model.components]: - raise ValueError( - "Cannot create parameter for initial assignment " - f"for {assignee_id} because an entity named " - f"{init_par_id} exists already in the model." - ) - p = pysb.Parameter(init_par_id) - pysb_model.add_component(p) - - species_idx = int(re.match(r"__s(\d+)$", assignee_id)[1]) - # use original model here since that's what was used to generate - # the ids in initial_states - species_pattern = petab_problem.model.model.species[species_idx] - - # species pattern comes from the _original_ model, but we only want - # to modify pysb_model, so we have to reconstitute the pattern using - # pysb_model - for c in pysb_model.components: - globals()[c.name] = c - species_pattern = pysb.as_complex_pattern(eval(str(species_pattern))) - - from pysb.pattern import match_complex_pattern - - formula = pysb.Expression( - f"initial_{assignee_id}_formula", - preeq_indicator * pysb_model.parameters[init_par_id_preeq] - + (1 - preeq_indicator) * pysb_model.parameters[init_par_id_sim], - ) - pysb_model.add_component(formula) - - for initial in pysb_model.initials: - if match_complex_pattern( - initial.pattern, species_pattern, exact=True - ): - logger.debug( - "The PySB model has an initial defined for species " - f"{assignee_id}, but this species also has an initial " - "value defined in the PEtab condition table. The SBML " - "initial assignment will be overwritten to handle " - "preequilibration and initial values specified by the " - "PEtab problem." - ) - initial.value = formula - break - else: - # No initial in the pysb model, so add one - init = pysb.Initial(species_pattern, formula) - pysb_model.add_component(init) +PEtab import for PySB models - return fixed_parameters - - -@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, - **kwargs, -) -> None: - """ - Create AMICI model from PySB-PEtab problem - - :param petab_problem: - PySB PEtab problem - - :param model_output_dir: - Directory to write the model code to. Will be created if doesn't - exist. Defaults to current directory. - - :param verbose: - Print/log extra information. - - :param model_name: - Name of the generated model module - - :param kwargs: - Additional keyword arguments to be passed to - :meth:`amici.pysb_import.pysb2amici`. - """ - set_log_level(logger, verbose) - - logger.info("Importing model ...") - - if not isinstance(petab_problem.model, PySBModel): - raise ValueError("Not a PySB model") - - # need to create a copy here as we don't want to modify the original - pysb.SelfExporter.cleanup() - og_export = pysb.SelfExporter.do_export - pysb.SelfExporter.do_export = False - pysb_model = pysb.Model( - base=petab_problem.model.model, - name=petab_problem.model.model_id, - ) - - _add_observation_model(pysb_model, petab_problem) - # generate species for the _original_ model - pysb.bng.generate_equations(petab_problem.model.model) - fixed_parameters = _add_initialization_variables(pysb_model, petab_problem) - pysb.SelfExporter.do_export = og_export - - # check condition table for supported features, important to use pysb_model - # here, as we want to also cover output parameters - model_parameters = [p.name for p in pysb_model.parameters] - condition_species_parameters = get_states_in_condition_table( - petab_problem, return_patterns=True - ) - for x in petab_problem.condition_df.columns: - if x == CONDITION_NAME: - continue - - x = petab.mapping.resolve_mapping(petab_problem.mapping_df, x) - - # parameters - if x in model_parameters: - continue - - # species/pattern - if x in condition_species_parameters: - continue - - raise NotImplementedError( - "For PySB PEtab import, only model parameters and species, but " - "not compartments are allowed in the condition table. Offending " - f"column: {x}" - ) - - from .petab_import import ( - get_fixed_parameters, - petab_noise_distributions_to_amici, - ) - - constant_parameters = ( - get_fixed_parameters(petab_problem) + fixed_parameters - ) +.. deprecated:: 0.21.0 + Use :mod:`amici.petab.pysb_import` instead. +""" - if petab_problem.observable_df is None: - observables = None - sigmas = None - noise_distrs = None - else: - observables = [ - expr.name - for expr in pysb_model.expressions - if expr.name in petab_problem.observable_df.index - ] +import warnings - sigmas = {obs_id: f"{obs_id}_sigma" for obs_id in observables} +from .petab.pysb_import import * # noqa: F401, F403 - noise_distrs = petab_noise_distributions_to_amici( - petab_problem.observable_df - ) +# DEPRECATED - DON'T ADD ANYTHING NEW HERE - from amici.pysb_import import pysb2amici +warnings.warn( + "Importing amici.petab_import_pysb is deprecated. Use `amici.petab.pysb_import` instead.", + DeprecationWarning, + stacklevel=2, +) - pysb2amici( - model=pysb_model, - output_dir=model_output_dir, - model_name=model_name, - verbose=True, - observables=observables, - sigmas=sigmas, - constant_parameters=constant_parameters, - noise_distributions=noise_distrs, - **kwargs, - ) +__all__ = [ + "import_model_pysb", +] diff --git a/deps/AMICI/python/sdist/amici/petab_objective.py b/deps/AMICI/python/sdist/amici/petab_objective.py index e3111d3b6..6d0dc44d8 100644 --- a/deps/AMICI/python/sdist/amici/petab_objective.py +++ b/deps/AMICI/python/sdist/amici/petab_objective.py @@ -1,1184 +1,54 @@ """ -PEtab Objective ---------------- -Functionality related to running simulations or evaluating the objective -function as defined by a PEtab problem +Evaluate a PEtab objective function. + +.. deprecated:: 0.21.0 + Use :mod:`amici.petab.simulations` instead. """ -import copy -import logging -import numbers -import re -from typing import ( - Any, - Collection, - Dict, - Iterator, - List, - Optional, - Sequence, - Tuple, - Union, -) +# THIS FILE IS TO BE REMOVED - DON'T ADD ANYTHING HERE! -import amici -import libsbml -import numpy as np -import pandas as pd -import petab -import sympy as sp -from amici.sbml_import import get_species_initial -from petab.C import * # noqa: F403 -from petab.models import MODEL_TYPE_PYSB, MODEL_TYPE_SBML -from sympy.abc import _clash +import warnings -from . import AmiciExpData, AmiciModel -from .logging import get_logger, log_execution_time -from .parameter_mapping import ( - ParameterMapping, - ParameterMappingForCondition, - fill_in_parameters, +warnings.warn( + f"Importing {__name__} is deprecated. Use `amici.petab.simulations` instead.", + DeprecationWarning, + stacklevel=2, ) -from .petab_import import PREEQ_INDICATOR_ID -from .petab_util import get_states_in_condition_table - -try: - import pysb -except ImportError: - pysb = None - -logger = get_logger(__name__) - - -# string constant definitions -LLH = "llh" -SLLH = "sllh" -FIM = "fim" -S2LLH = "s2llh" -RES = "res" -SRES = "sres" -RDATAS = "rdatas" -EDATAS = "edatas" - - -@log_execution_time("Simulating PEtab model", logger) -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, - edatas: List[AmiciExpData] = None, - parameter_mapping: ParameterMapping = None, - scaled_parameters: Optional[bool] = False, - log_level: int = logging.WARNING, - num_threads: int = 1, - failfast: bool = True, - scaled_gradients: bool = False, -) -> Dict[str, Any]: - """Simulate PEtab model. - - .. note:: - Regardless of `scaled_parameters`, unscaled sensitivities are returned, - unless `scaled_gradients=True`. - - :param petab_problem: - PEtab problem to work on. - :param amici_model: - AMICI Model assumed to be compatible with ``petab_problem``. - :param solver: - An AMICI solver. Will use default options if None. - :param problem_parameters: - Run simulation with these parameters. If ``None``, PEtab - ``nominalValues`` will be used. To be provided as dict, mapping PEtab - problem parameters to SBML IDs. - :param simulation_conditions: - Result of :py:func:`petab.get_simulation_conditions`. Can be provided - to save time if this has be obtained before. - Not required if ``edatas`` and ``parameter_mapping`` are provided. - :param edatas: - Experimental data. Parameters are inserted in-place for simulation. - :param parameter_mapping: - Optional precomputed PEtab parameter mapping for efficiency, as - generated by :py:func:`create_parameter_mapping`. - :param scaled_parameters: - If ``True``, ``problem_parameters`` are assumed to be on the scale - provided in the PEtab parameter table and will be unscaled. - If ``False``, they are assumed to be in linear scale. - :param log_level: - Log level, see :mod:`amici.logging` module. - :param num_threads: - Number of threads to use for simulating multiple conditions - (only used if compiled with OpenMP). - :param failfast: - Returns as soon as an integration failure is encountered, skipping - any remaining simulations. - :param scaled_gradients: - Whether to compute gradients on parameter scale (``True``) or not - (``False``). - - :return: - Dictionary of - - * cost function value (``LLH``), - * list of :class:`amici.amici.ReturnData` (``RDATAS``), - * list of :class:`amici.amici.ExpData` (``EDATAS``), - - corresponding to the different simulation conditions. - For ordering of simulation conditions, see - :meth:`petab.Problem.get_simulation_conditions_from_measurement_df`. - """ - logger.setLevel(log_level) - - if solver is None: - solver = amici_model.getSolver() - - # Switch to scaled parameters. - problem_parameters = _default_scaled_parameters( - petab_problem=petab_problem, - problem_parameters=problem_parameters, - scaled_parameters=scaled_parameters, - ) - scaled_parameters = True - - # number of amici simulations will be number of unique - # (preequilibrationConditionId, simulationConditionId) pairs. - # Can be optimized by checking for identical condition vectors. - if ( - simulation_conditions is None - and parameter_mapping is None - and edatas is None - ): - simulation_conditions = ( - petab_problem.get_simulation_conditions_from_measurement_df() - ) - - # Get parameter mapping - if parameter_mapping is None: - parameter_mapping = create_parameter_mapping( - petab_problem=petab_problem, - simulation_conditions=simulation_conditions, - scaled_parameters=scaled_parameters, - amici_model=amici_model, - ) - - # Get edatas - if edatas is None: - # Generate ExpData with all condition-specific information - edatas = create_edatas( - amici_model=amici_model, - petab_problem=petab_problem, - simulation_conditions=simulation_conditions, - ) - - # Fill parameters in ExpDatas (in-place) - fill_in_parameters( - edatas=edatas, - problem_parameters=problem_parameters, - scaled_parameters=scaled_parameters, - parameter_mapping=parameter_mapping, - amici_model=amici_model, - ) - - # Simulate - rdatas = amici.runAmiciSimulations( - amici_model, - solver, - edata_list=edatas, - num_threads=num_threads, - failfast=failfast, - ) - - # Compute total llh - llh = sum(rdata["llh"] for rdata in rdatas) - # Compute total sllh - sllh = None - if solver.getSensitivityOrder() != amici.SensitivityOrder.none: - sllh = aggregate_sllh( - amici_model=amici_model, - rdatas=rdatas, - parameter_mapping=parameter_mapping, - petab_scale=scaled_parameters, - petab_problem=petab_problem, - edatas=edatas, - ) - if not scaled_gradients and sllh is not None: - sllh = { - parameter_id: rescale_sensitivity( - sensitivity=sensitivity, - parameter_value=problem_parameters[parameter_id], - old_scale=petab_problem.parameter_df.loc[ - parameter_id, PARAMETER_SCALE - ], - new_scale=LIN, - ) - for parameter_id, sensitivity in sllh.items() - } - - # Log results - sim_cond = petab_problem.get_simulation_conditions_from_measurement_df() - for i, rdata in enumerate(rdatas): - sim_cond_id = "N/A" if sim_cond.empty else sim_cond.iloc[i, :].values - logger.debug( - f"Condition: {sim_cond_id}, status: {rdata['status']}, " - f"llh: {rdata['llh']}" - ) - - return { - LLH: llh, - SLLH: sllh, - RDATAS: rdatas, - EDATAS: edatas, - } - - -def aggregate_sllh( - amici_model: AmiciModel, - rdatas: Sequence[amici.ReturnDataView], - parameter_mapping: Optional[ParameterMapping], - edatas: List[AmiciExpData], - petab_scale: bool = True, - petab_problem: petab.Problem = None, -) -> Union[None, Dict[str, float]]: - """ - Aggregate likelihood gradient for all conditions, according to PEtab - parameter mapping. - - :param amici_model: - AMICI model from which ``rdatas`` were obtained. - :param rdatas: - Simulation results. - :param parameter_mapping: - PEtab parameter mapping to condition-specific simulation parameters. - :param edatas: - Experimental data used for simulation. - :param petab_scale: - Whether to check that sensitivities were computed with parameters on - the scales provided in the PEtab parameters table. - :param petab_problem: - The PEtab problem that defines the parameter scales. - - :return: - Aggregated likelihood sensitivities. - """ - accumulated_sllh = {} - model_parameter_ids = amici_model.getParameterIds() - - if petab_scale and petab_problem is None: - raise ValueError( - "Please provide the PEtab problem, when using " - "`petab_scale=True`." - ) - - # Check for issues in all condition simulation results. - for rdata in rdatas: - # Condition failed during simulation. - if rdata.status != amici.AMICI_SUCCESS: - return None - # Condition simulation result does not provide SLLH. - if rdata.sllh is None: - raise ValueError( - "The sensitivities of the likelihood for a condition were " - "not computed." - ) - - for condition_parameter_mapping, edata, rdata in zip( - parameter_mapping, edatas, rdatas - ): - for sllh_parameter_index, condition_parameter_sllh in enumerate( - rdata.sllh - ): - # Get PEtab parameter ID - # Use ExpData if it provides a parameter list, else default to - # Model. - if edata.plist: - model_parameter_index = edata.plist[sllh_parameter_index] - else: - model_parameter_index = amici_model.plist(sllh_parameter_index) - model_parameter_id = model_parameter_ids[model_parameter_index] - petab_parameter_id = condition_parameter_mapping.map_sim_var[ - model_parameter_id - ] - - # Initialize - if petab_parameter_id not in accumulated_sllh: - accumulated_sllh[petab_parameter_id] = 0 - - # Check that the scale is consistent - if petab_scale: - # `ParameterMappingForCondition` objects provide the scale in - # terms of `petab.C` constants already, not AMICI equivalents. - model_parameter_scale = ( - condition_parameter_mapping.scale_map_sim_var[ - model_parameter_id - ] - ) - petab_parameter_scale = petab_problem.parameter_df.loc[ - petab_parameter_id, PARAMETER_SCALE - ] - if model_parameter_scale != petab_parameter_scale: - raise ValueError( - f"The scale of the parameter `{petab_parameter_id}` " - "differs between the AMICI model " - f"({model_parameter_scale}) and the PEtab problem " - f"({petab_parameter_scale})." - ) - - # Accumulate - accumulated_sllh[petab_parameter_id] += condition_parameter_sllh - - return accumulated_sllh - - -def rescale_sensitivity( - sensitivity: float, - parameter_value: float, - old_scale: str, - new_scale: str, -) -> float: - """Rescale a sensitivity between parameter scales. - - :param sensitivity: - The sensitivity corresponding to the parameter value. - :param parameter_value: - The parameter vector element, on ``old_scale``. - :param old_scale: - The scale of the parameter value. - :param new_scale: - The parameter scale on which to rescale the sensitivity. - - :return: - The rescaled sensitivity. - """ - LOG_E_10 = np.log(10) - - if old_scale == new_scale: - return sensitivity - - unscaled_parameter_value = petab.parameters.unscale( - parameter=parameter_value, - scale_str=old_scale, - ) - - scale = { - (LIN, LOG): lambda s: s * unscaled_parameter_value, - (LOG, LIN): lambda s: s / unscaled_parameter_value, - (LIN, LOG10): lambda s: s * (unscaled_parameter_value * LOG_E_10), - (LOG10, LIN): lambda s: s / (unscaled_parameter_value * LOG_E_10), - } - - scale[(LOG, LOG10)] = lambda s: scale[(LIN, LOG10)](scale[(LOG, LIN)](s)) - scale[(LOG10, LOG)] = lambda s: scale[(LIN, LOG)](scale[(LOG10, LIN)](s)) - - if (old_scale, new_scale) not in scale: - raise NotImplementedError( - f"Old scale: {old_scale}. New scale: {new_scale}." - ) - - return scale[(old_scale, new_scale)](sensitivity) - - -def create_parameterized_edatas( - amici_model: AmiciModel, - petab_problem: petab.Problem, - problem_parameters: Dict[str, numbers.Number], - scaled_parameters: bool = False, - parameter_mapping: ParameterMapping = None, - simulation_conditions: Union[pd.DataFrame, Dict] = None, -) -> List[amici.ExpData]: - """Create list of :class:amici.ExpData objects with parameters filled in. - - :param amici_model: - AMICI Model assumed to be compatible with ``petab_problem``. - :param petab_problem: - PEtab problem to work on. - :param problem_parameters: - Run simulation with these parameters. If ``None``, PEtab - ``nominalValues`` will be used. To be provided as dict, mapping PEtab - problem parameters to SBML IDs. - :param scaled_parameters: - If ``True``, ``problem_parameters`` are assumed to be on the scale - provided in the PEtab parameter table and will be unscaled. - If ``False``, they are assumed to be in linear scale. - :param parameter_mapping: - Optional precomputed PEtab parameter mapping for efficiency, as - generated by :func:`create_parameter_mapping`. - :param simulation_conditions: - Result of :func:`petab.get_simulation_conditions`. Can be provided to - save time if this has been obtained before. - - :return: - List with one :class:`amici.amici.ExpData` per simulation condition, - with filled in timepoints, data and parameters. - """ - # number of amici simulations will be number of unique - # (preequilibrationConditionId, simulationConditionId) pairs. - # Can be optimized by checking for identical condition vectors. - if simulation_conditions is None: - simulation_conditions = ( - petab_problem.get_simulation_conditions_from_measurement_df() - ) - - # Get parameter mapping - if parameter_mapping is None: - parameter_mapping = create_parameter_mapping( - petab_problem=petab_problem, - simulation_conditions=simulation_conditions, - scaled_parameters=scaled_parameters, - amici_model=amici_model, - ) - - # Generate ExpData with all condition-specific information - edatas = create_edatas( - amici_model=amici_model, - petab_problem=petab_problem, - simulation_conditions=simulation_conditions, - ) - - # Fill parameters in ExpDatas (in-place) - fill_in_parameters( - edatas=edatas, - problem_parameters=problem_parameters, - scaled_parameters=scaled_parameters, - parameter_mapping=parameter_mapping, - amici_model=amici_model, - ) - - return edatas - - -def create_parameter_mapping( - petab_problem: petab.Problem, - simulation_conditions: Union[pd.DataFrame, List[Dict]], - scaled_parameters: bool, - amici_model: AmiciModel, - **parameter_mapping_kwargs, -) -> ParameterMapping: - """Generate AMICI specific parameter mapping. - - :param petab_problem: - PEtab problem - :param simulation_conditions: - Result of :func:`petab.get_simulation_conditions`. Can be provided to - save time if this has been obtained before. - :param scaled_parameters: - If ``True``, problem_parameters are assumed to be on the scale provided - in the PEtab parameter table and will be unscaled. If ``False``, they - are assumed to be in linear scale. - :param amici_model: - AMICI model. - :param parameter_mapping_kwargs: - Optional keyword arguments passed to - :func:`petab.get_optimization_to_simulation_parameter_mapping`. - To allow changing fixed PEtab problem parameters (``estimate=0``), - use ``fill_fixed_parameters=False``. - :return: - List of the parameter mappings. - """ - if simulation_conditions is None: - simulation_conditions = ( - petab_problem.get_simulation_conditions_from_measurement_df() - ) - if isinstance(simulation_conditions, list): - simulation_conditions = pd.DataFrame(data=simulation_conditions) - - # Because AMICI globalizes all local parameters during model import, - # we need to do that here as well to prevent parameter mapping errors - # (PEtab does currently not care about SBML LocalParameters) - if petab_problem.model.type_id == MODEL_TYPE_SBML: - if petab_problem.sbml_document: - converter_config = ( - libsbml.SBMLLocalParameterConverter().getDefaultProperties() - ) - petab_problem.sbml_document.convert(converter_config) - else: - logger.debug( - "No petab_problem.sbml_document is set. Cannot " - "convert SBML LocalParameters. If the model contains " - "LocalParameters, parameter mapping will fail." - ) - - default_parameter_mapping_kwargs = { - "warn_unmapped": False, - "scaled_parameters": scaled_parameters, - "allow_timepoint_specific_numeric_noise_parameters": not petab.lint.observable_table_has_nontrivial_noise_formula( - petab_problem.observable_df - ), - } - if parameter_mapping_kwargs is None: - parameter_mapping_kwargs = {} - - prelim_parameter_mapping = ( - petab.get_optimization_to_simulation_parameter_mapping( - condition_df=petab_problem.condition_df, - measurement_df=petab_problem.measurement_df, - parameter_df=petab_problem.parameter_df, - observable_df=petab_problem.observable_df, - mapping_df=petab_problem.mapping_df, - model=petab_problem.model, - simulation_conditions=simulation_conditions, - **dict( - default_parameter_mapping_kwargs, **parameter_mapping_kwargs - ), - ) - ) - - parameter_mapping = ParameterMapping() - for (_, condition), prelim_mapping_for_condition in zip( - simulation_conditions.iterrows(), prelim_parameter_mapping - ): - mapping_for_condition = create_parameter_mapping_for_condition( - prelim_mapping_for_condition, condition, petab_problem, amici_model - ) - parameter_mapping.append(mapping_for_condition) - - return parameter_mapping - - -def _get_initial_state_sbml( - petab_problem: petab.Problem, element_id: str -) -> Union[float, sp.Basic]: - element = petab_problem.sbml_model.getElementBySId(element_id) - type_code = element.getTypeCode() - initial_assignment = petab_problem.sbml_model.getInitialAssignmentBySymbol( - element_id - ) - if initial_assignment: - initial_assignment = sp.sympify( - libsbml.formulaToL3String(initial_assignment.getMath()), - locals=_clash, - ) - if type_code == libsbml.SBML_SPECIES: - value = ( - get_species_initial(element) - if initial_assignment is None - else initial_assignment - ) - elif type_code == libsbml.SBML_PARAMETER: - value = ( - element.getValue() - if initial_assignment is None - else initial_assignment - ) - elif type_code == libsbml.SBML_COMPARTMENT: - value = ( - element.getSize() - if initial_assignment is None - else initial_assignment - ) - else: - raise NotImplementedError( - f"Don't know what how to handle {element_id} in " - "condition table." - ) - return value - - -def _get_initial_state_pysb( - petab_problem: petab.Problem, element_id: str -) -> Union[float, sp.Symbol]: - species_idx = int(re.match(r"__s(\d+)$", element_id)[1]) - species_pattern = petab_problem.model.model.species[species_idx] - from pysb.pattern import match_complex_pattern - - value = next( - ( - initial.value - for initial in petab_problem.model.model.initials - if match_complex_pattern( - initial.pattern, species_pattern, exact=True - ) - ), - 0.0, - ) - if isinstance(value, pysb.Parameter): - if value.name in petab_problem.parameter_df.index: - value = value.name - else: - value = value.value - - return value - - -def _set_initial_state( - petab_problem, - condition_id, - element_id, - init_par_id, - par_map, - scale_map, - value, -): - value = petab.to_float_if_float(value) - if pd.isna(value): - if petab_problem.model.type_id == MODEL_TYPE_SBML: - value = _get_initial_state_sbml(petab_problem, element_id) - elif petab_problem.model.type_id == MODEL_TYPE_PYSB: - value = _get_initial_state_pysb(petab_problem, element_id) - - try: - value = float(value) - except (ValueError, TypeError): - if sp.nsimplify(value).is_Atom and ( - pysb is None or not isinstance(value, pysb.Component) - ): - # Get rid of multiplication with one - value = sp.nsimplify(value) - else: - raise NotImplementedError( - "Cannot handle non-trivial initial state " - f"expression for {element_id}: {value}" - ) - # this should be a parameter ID - value = str(value) - logger.debug( - f"The species {element_id} has no initial value " - f"defined for the condition {condition_id} in " - "the PEtab conditions table. The initial value is " - f"now set to {value}, which is the initial value " - "defined in the SBML model." - ) - par_map[init_par_id] = value - if isinstance(value, float): - # numeric initial state - scale_map[init_par_id] = petab.LIN - else: - # parametric initial state - scale_map[init_par_id] = petab_problem.parameter_df[ - PARAMETER_SCALE - ].get(value, petab.LIN) - - -def create_parameter_mapping_for_condition( - parameter_mapping_for_condition: petab.ParMappingDictQuadruple, - condition: Union[pd.Series, Dict], - petab_problem: petab.Problem, - amici_model: AmiciModel, -) -> ParameterMappingForCondition: - """Generate AMICI specific parameter mapping for condition. - - :param parameter_mapping_for_condition: - Preliminary parameter mapping for condition. - :param condition: - :class:`pandas.DataFrame` row with ``preequilibrationConditionId`` and - ``simulationConditionId``. - :param petab_problem: - Underlying PEtab problem. - :param amici_model: - AMICI model. - - :return: - The parameter and parameter scale mappings, for fixed - preequilibration, fixed simulation, and variable simulation - parameters, and then the respective scalings. - """ - ( - condition_map_preeq, - condition_map_sim, - condition_scale_map_preeq, - condition_scale_map_sim, - ) = parameter_mapping_for_condition - logger.debug(f"PEtab mapping: {parameter_mapping_for_condition}") - - if len(condition_map_preeq) != len(condition_scale_map_preeq) or len( - condition_map_sim - ) != len(condition_scale_map_sim): - raise AssertionError( - "Number of parameters and number of parameter " - "scales do not match." - ) - if len(condition_map_preeq) and len(condition_map_preeq) != len( - condition_map_sim - ): - logger.debug(f"Preequilibration parameter map: {condition_map_preeq}") - logger.debug(f"Simulation parameter map: {condition_map_sim}") - raise AssertionError( - "Number of parameters for preequilbration " - "and simulation do not match." - ) - - ########################################################################## - # initial states - # Initial states have been set during model import based on the SBML model. - # If initial states were overwritten in the PEtab condition table, they are - # applied here. - # During model generation, parameters for initial concentrations and - # respective initial assignments have been created for the - # relevant species, here we add these parameters to the parameter mapping. - # In absence of preequilibration this could also be handled via - # ExpData.x0, but in the case of preequilibration this would not allow for - # resetting initial states. - - if states_in_condition_table := get_states_in_condition_table( - petab_problem, condition - ): - # set indicator fixed parameter for preeq - # (we expect here, that this parameter was added during import and - # that it was not added by the user with a different meaning...) - if condition_map_preeq: - condition_map_preeq[PREEQ_INDICATOR_ID] = 1.0 - condition_scale_map_preeq[PREEQ_INDICATOR_ID] = LIN - - condition_map_sim[PREEQ_INDICATOR_ID] = 0.0 - condition_scale_map_sim[PREEQ_INDICATOR_ID] = LIN - for element_id, ( - value, - preeq_value, - ) in states_in_condition_table.items(): - # for preequilibration - init_par_id = f"initial_{element_id}_preeq" - if ( - condition_id := condition.get(PREEQUILIBRATION_CONDITION_ID) - ) is not None: - _set_initial_state( - petab_problem, - condition_id, - element_id, - init_par_id, - condition_map_preeq, - 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 - - # for simulation - condition_id = condition[SIMULATION_CONDITION_ID] - init_par_id = f"initial_{element_id}_sim" - _set_initial_state( - petab_problem, - condition_id, - element_id, - init_par_id, - condition_map_sim, - condition_scale_map_sim, - value, - ) - - ########################################################################## - # separate fixed and variable AMICI parameters, because we may have - # different fixed parameters for preeq and sim condition, but we cannot - # have different variable parameters. without splitting, - # merge_preeq_and_sim_pars_condition below may fail. - # TODO: This can be done already in parameter mapping creation. - variable_par_ids = amici_model.getParameterIds() - fixed_par_ids = amici_model.getFixedParameterIds() - - condition_map_preeq_var, condition_map_preeq_fix = _subset_dict( - condition_map_preeq, variable_par_ids, fixed_par_ids - ) - - ( - condition_scale_map_preeq_var, - condition_scale_map_preeq_fix, - ) = _subset_dict( - condition_scale_map_preeq, variable_par_ids, fixed_par_ids - ) - - condition_map_sim_var, condition_map_sim_fix = _subset_dict( - condition_map_sim, variable_par_ids, fixed_par_ids - ) - - condition_scale_map_sim_var, condition_scale_map_sim_fix = _subset_dict( - condition_scale_map_sim, variable_par_ids, fixed_par_ids - ) - - logger.debug( - "Fixed parameters preequilibration: " f"{condition_map_preeq_fix}" - ) - logger.debug("Fixed parameters simulation: " f"{condition_map_sim_fix}") - logger.debug( - "Variable parameters preequilibration: " f"{condition_map_preeq_var}" - ) - logger.debug("Variable parameters simulation: " f"{condition_map_sim_var}") - - petab.merge_preeq_and_sim_pars_condition( - condition_map_preeq_var, - condition_map_sim_var, - condition_scale_map_preeq_var, - condition_scale_map_sim_var, - condition, - ) - logger.debug(f"Merged: {condition_map_sim_var}") - - parameter_mapping_for_condition = ParameterMappingForCondition( - map_preeq_fix=condition_map_preeq_fix, - map_sim_fix=condition_map_sim_fix, - map_sim_var=condition_map_sim_var, - scale_map_preeq_fix=condition_scale_map_preeq_fix, - scale_map_sim_fix=condition_scale_map_sim_fix, - scale_map_sim_var=condition_scale_map_sim_var, - ) - - return parameter_mapping_for_condition - - -def create_edatas( - amici_model: AmiciModel, - petab_problem: petab.Problem, - simulation_conditions: Union[pd.DataFrame, Dict] = None, -) -> List[amici.ExpData]: - """Create list of :class:`amici.amici.ExpData` objects for PEtab problem. - - :param amici_model: - AMICI model. - :param petab_problem: - Underlying PEtab problem. - :param simulation_conditions: - Result of :func:`petab.get_simulation_conditions`. Can be provided to - save time if this has be obtained before. - - :return: - List with one :class:`amici.amici.ExpData` per simulation condition, - with filled in timepoints and data. - """ - if simulation_conditions is None: - simulation_conditions = ( - petab_problem.get_simulation_conditions_from_measurement_df() - ) - - observable_ids = amici_model.getObservableIds() - - measurement_groupvar = [SIMULATION_CONDITION_ID] - if PREEQUILIBRATION_CONDITION_ID in simulation_conditions: - measurement_groupvar.append(petab.PREEQUILIBRATION_CONDITION_ID) - measurement_dfs = dict( - list(petab_problem.measurement_df.groupby(measurement_groupvar)) - ) - - edatas = [] - for _, condition in simulation_conditions.iterrows(): - # Create amici.ExpData for each simulation - if PREEQUILIBRATION_CONDITION_ID in condition: - measurement_index = ( - condition.get(SIMULATION_CONDITION_ID), - condition.get(PREEQUILIBRATION_CONDITION_ID), - ) - else: - measurement_index = (condition.get(SIMULATION_CONDITION_ID),) - edata = create_edata_for_condition( - condition=condition, - amici_model=amici_model, - measurement_df=measurement_dfs[measurement_index], - petab_problem=petab_problem, - observable_ids=observable_ids, - ) - edatas.append(edata) - - return edatas - - -def create_edata_for_condition( - condition: Union[Dict, pd.Series], - measurement_df: pd.DataFrame, - amici_model: AmiciModel, - petab_problem: petab.Problem, - observable_ids: List[str], -) -> amici.ExpData: - """Get :class:`amici.amici.ExpData` for the given PEtab condition. - - Sets timepoints, observed data and sigmas. - - :param condition: - :class:`pandas.DataFrame` row with ``preequilibrationConditionId`` and - ``simulationConditionId``. - :param measurement_df: - :class:`pandas.DataFrame` with measurements for the given condition. - :param amici_model: - AMICI model - :param petab_problem: - Underlying PEtab problem - :param observable_ids: - List of observable IDs - - :return: - ExpData instance. - """ - if amici_model.nytrue != len(observable_ids): - raise AssertionError( - "Number of AMICI model observables does not " - "match number of PEtab observables." - ) - - # create an ExpData object - edata = amici.ExpData(amici_model) - edata.id = condition[SIMULATION_CONDITION_ID] - if condition.get(PREEQUILIBRATION_CONDITION_ID): - edata.id += "+" + condition.get(PREEQUILIBRATION_CONDITION_ID) - ########################################################################## - # enable initial parameters reinitialization - - states_in_condition_table = get_states_in_condition_table( - petab_problem, condition=condition - ) - if ( - condition.get(PREEQUILIBRATION_CONDITION_ID) - and states_in_condition_table - ): - state_ids = amici_model.getStateIds() - state_idx_reinitalization = [ - state_ids.index(s) - for s, (v, v_preeq) in states_in_condition_table.items() - if not np.isnan(v) - ] - edata.reinitialization_state_idxs_sim = state_idx_reinitalization - logger.debug( - "Enabling state reinitialization for condition " - f"{condition.get(PREEQUILIBRATION_CONDITION_ID, '')} - " - f"{condition.get(SIMULATION_CONDITION_ID)} " - f"{states_in_condition_table}" - ) - - ########################################################################## - # timepoints - - # find replicate numbers of time points - timepoints_w_reps = _get_timepoints_with_replicates( - df_for_condition=measurement_df - ) - edata.setTimepoints(timepoints_w_reps) - - ########################################################################## - # measurements and sigmas - y, sigma_y = _get_measurements_and_sigmas( - df_for_condition=measurement_df, - timepoints_w_reps=timepoints_w_reps, - observable_ids=observable_ids, - ) - edata.setObservedData(y.flatten()) - edata.setObservedDataStdDev(sigma_y.flatten()) - - return edata - - -def _subset_dict( - full: Dict[Any, Any], *args: Collection[Any] -) -> Iterator[Dict[Any, Any]]: - """Get subset of dictionary based on provided keys - - :param full: - Dictionary to subset - :param args: - Collections of keys to be contained in the different subsets - - :return: - subsetted dictionary - """ - for keys in args: - yield {key: val for (key, val) in full.items() if key in keys} - - -def _get_timepoints_with_replicates( - df_for_condition: pd.DataFrame, -) -> List[numbers.Number]: - """ - Get list of timepoints including replicate measurements - - :param df_for_condition: - PEtab measurement table subset for a single condition. - - :return: - Sorted list of timepoints, including multiple timepoints accounting - for replicate measurements. - """ - # create sorted list of all timepoints for which measurements exist - timepoints = sorted(df_for_condition[TIME].unique().astype(float)) - - # find replicate numbers of time points - timepoints_w_reps = [] - for time in timepoints: - # subselect for time - df_for_time = df_for_condition[ - df_for_condition.time.astype(float) == time - ] - # rep number is maximum over rep numbers for observables - n_reps = max(df_for_time.groupby([OBSERVABLE_ID, TIME]).size()) - # append time point n_rep times - timepoints_w_reps.extend([time] * n_reps) - - return timepoints_w_reps - - -def _get_measurements_and_sigmas( - df_for_condition: pd.DataFrame, - timepoints_w_reps: Sequence[numbers.Number], - observable_ids: Sequence[str], -) -> Tuple[np.array, np.array]: - """ - Get measurements and sigmas - - Generate arrays with measurements and sigmas in AMICI format from a - PEtab measurement table subset for a single condition. - - :param df_for_condition: - Subset of PEtab measurement table for one condition - - :param timepoints_w_reps: - Timepoints for which there exist measurements, including replicates - - :param observable_ids: - List of observable IDs for mapping IDs to indices. - - :return: - arrays for measurement and sigmas - """ - # prepare measurement matrix - y = np.full( - shape=(len(timepoints_w_reps), len(observable_ids)), fill_value=np.nan - ) - # prepare sigma matrix - sigma_y = y.copy() - - timepoints = sorted(df_for_condition[TIME].unique().astype(float)) - - for time in timepoints: - # subselect for time - df_for_time = df_for_condition[df_for_condition[TIME] == time] - time_ix_0 = timepoints_w_reps.index(time) - - # remember used time indices for each observable - time_ix_for_obs_ix = {} - - # iterate over measurements - for _, measurement in df_for_time.iterrows(): - # extract observable index - observable_ix = observable_ids.index(measurement[OBSERVABLE_ID]) - - # update time index for observable - if observable_ix in time_ix_for_obs_ix: - time_ix_for_obs_ix[observable_ix] += 1 - else: - time_ix_for_obs_ix[observable_ix] = time_ix_0 - - # fill observable and possibly noise parameter - y[time_ix_for_obs_ix[observable_ix], observable_ix] = measurement[ - MEASUREMENT - ] - if isinstance( - measurement.get(NOISE_PARAMETERS, None), numbers.Number - ): - sigma_y[ - time_ix_for_obs_ix[observable_ix], observable_ix - ] = measurement[NOISE_PARAMETERS] - return y, sigma_y - - -def rdatas_to_measurement_df( - rdatas: Sequence[amici.ReturnData], - model: AmiciModel, - measurement_df: pd.DataFrame, -) -> pd.DataFrame: - """ - Create a measurement dataframe in the PEtab format from the passed - ``rdatas`` and own information. - - :param rdatas: - A sequence of rdatas with the ordering of - :func:`petab.get_simulation_conditions`. - - :param model: - AMICI model used to generate ``rdatas``. - - :param measurement_df: - PEtab measurement table used to generate ``rdatas``. - - :return: - A dataframe built from the rdatas in the format of ``measurement_df``. - """ - simulation_conditions = petab.get_simulation_conditions(measurement_df) - - observable_ids = model.getObservableIds() - rows = [] - # iterate over conditions - for (_, condition), rdata in zip(simulation_conditions.iterrows(), rdatas): - # current simulation matrix - y = rdata.y - # time array used in rdata - t = list(rdata.ts) - - # extract rows for condition - cur_measurement_df = petab.get_rows_for_condition( - measurement_df, condition - ) - - # iterate over entries for the given condition - # note: this way we only generate a dataframe entry for every - # row that existed in the original dataframe. if we want to - # e.g. have also timepoints non-existent in the original file, - # we need to instead iterate over the rdata['y'] entries - for _, row in cur_measurement_df.iterrows(): - # copy row - row_sim = copy.deepcopy(row) - - # extract simulated measurement value - timepoint_idx = t.index(row[TIME]) - observable_idx = observable_ids.index(row[OBSERVABLE_ID]) - measurement_sim = y[timepoint_idx, observable_idx] - - # change measurement entry - row_sim[MEASUREMENT] = measurement_sim - - rows.append(row_sim) - - return pd.DataFrame(rows) - - -def rdatas_to_simulation_df( - rdatas: Sequence[amici.ReturnData], - model: AmiciModel, - measurement_df: pd.DataFrame, -) -> pd.DataFrame: - """Create a PEtab simulation dataframe from - :class:`amici.amici.ReturnData` s. - - See :func:`rdatas_to_measurement_df` for details, only that model outputs - will appear in column ``simulation`` instead of ``measurement``.""" - - df = rdatas_to_measurement_df( - rdatas=rdatas, model=model, measurement_df=measurement_df - ) - - return df.rename(columns={MEASUREMENT: SIMULATION}) - - -def _default_scaled_parameters( - petab_problem: petab.Problem, - problem_parameters: Optional[Dict[str, float]] = None, - scaled_parameters: bool = False, -) -> Optional[Dict[str, float]]: - """ - Helper method to handle an unscaled or unspecified parameter vector. - - The parameter vector defaults to the nominal values in the PEtab - parameter table. - - Unscaled parameter values are scaled. - - :param petab_problem: - The PEtab problem. - :param problem_parameters: - Keys are PEtab parameter IDs, values are parameter values on the scale - defined in the PEtab parameter table. Defaults to the nominal values in - the PEtab parameter table. - :param scaled_parameters: - Whether `problem_parameters` are on the scale defined in the PEtab - parameter table. +from .petab.conditions import fill_in_parameters # noqa: F401 +from .petab.parameter_mapping import create_parameter_mapping # noqa: F401 +from .petab.simulations import ( # noqa: F401 + EDATAS, + FIM, + LLH, + RDATAS, + RES, + S2LLH, + SLLH, + SRES, + aggregate_sllh, + create_edatas, + rdatas_to_measurement_df, + rdatas_to_simulation_df, + rescale_sensitivity, + simulate_petab, +) - :return: - The scaled parameter vector. - """ - if problem_parameters is None: - problem_parameters = dict( - zip( - petab_problem.x_ids, - petab_problem.x_nominal_scaled, - ) - ) - elif not scaled_parameters: - problem_parameters = petab_problem.scale_parameters(problem_parameters) - return problem_parameters +__all__ = [ + "EDATAS", + "FIM", + "LLH", + "RDATAS", + "RES", + "S2LLH", + "SLLH", + "SRES", + "aggregate_sllh", + "create_edatas", + "fill_in_parameters", + "create_parameter_mapping", + "rdatas_to_measurement_df", + "rdatas_to_simulation_df", + "rescale_sensitivity", + "simulate_petab", +] diff --git a/deps/AMICI/python/sdist/amici/petab_simulate.py b/deps/AMICI/python/sdist/amici/petab_simulate.py index 32c1ef895..5f81d02a9 100644 --- a/deps/AMICI/python/sdist/amici/petab_simulate.py +++ b/deps/AMICI/python/sdist/amici/petab_simulate.py @@ -1,113 +1,21 @@ """ -PEtab Simulate --------------- -Functionality related to the use of AMICI for simulation with PEtab's -Simulator class. +Simulate a PEtab problem -Use cases: - -- generate data for use with PEtab's plotting methods -- generate synthetic data +.. deprecated:: 0.21.0 + Use :mod:`amici.petab.simulator` instead. """ +# THIS FILE IS TO BE REMOVED - DON'T ADD ANYTHING HERE! -import inspect -import sys -from typing import Callable +import warnings -import pandas as pd -import petab -from amici import AmiciModel, SensitivityMethod_none -from amici.petab_import import import_petab_problem -from amici.petab_objective import ( - RDATAS, - rdatas_to_measurement_df, - simulate_petab, +warnings.warn( + f"Importing {__name__} is deprecated. Use `amici.petab.simulator` instead.", + DeprecationWarning, + stacklevel=2, ) -AMICI_MODEL = "amici_model" -AMICI_SOLVER = "solver" -MODEL_NAME = "model_name" -MODEL_OUTPUT_DIR = "model_output_dir" - -PETAB_PROBLEM = "petab_problem" - - -class PetabSimulator(petab.simulate.Simulator): - """Implementation of the PEtab `Simulator` class that uses AMICI.""" - - def __init__(self, *args, amici_model: AmiciModel = None, **kwargs): - super().__init__(*args, **kwargs) - self.amici_model = amici_model - - def simulate_without_noise(self, **kwargs) -> pd.DataFrame: - """ - See :py:func:`petab.simulate.Simulator.simulate()` docstring. - - Additional keyword arguments can be supplied to specify arguments for - the AMICI PEtab import, simulate, and export methods. See the - docstrings for the respective methods for argument options: - - :py:func:`amici.petab_import.import_petab_problem`, and - - :py:func:`amici.petab_objective.simulate_petab`. - - Note that some arguments are expected to have already been specified - in the Simulator constructor (including the PEtab problem). - """ - if AMICI_MODEL in {*kwargs, *dir(self)} and ( - any( - k in kwargs - for k in inspect.signature(import_petab_problem).parameters - ) - ): - print( - "Arguments related to the PEtab import are unused if " - f"`{AMICI_MODEL}` is specified, or the " - "`PetabSimulator.simulate()` method was previously called." - ) - - kwargs[PETAB_PROBLEM] = self.petab_problem - - # The AMICI model instance for the PEtab problem is saved in the state, - # such that it need not be supplied with each request for simulated - # data. Any user-supplied AMICI model will overwrite the model saved - # in the state. - if AMICI_MODEL not in kwargs: - if self.amici_model is None: - if MODEL_NAME not in kwargs: - kwargs[MODEL_NAME] = AMICI_MODEL - # If the model name is the name of a module that is already - # cached, it can cause issues during import. - while kwargs[MODEL_NAME] in sys.modules: - kwargs[MODEL_NAME] += str(self.rng.integers(10)) - if MODEL_OUTPUT_DIR not in kwargs: - kwargs[MODEL_OUTPUT_DIR] = self.working_dir - self.amici_model = _subset_call(import_petab_problem, kwargs) - kwargs[AMICI_MODEL] = self.amici_model - self.amici_model = kwargs[AMICI_MODEL] - - if AMICI_SOLVER not in kwargs: - kwargs[AMICI_SOLVER] = self.amici_model.getSolver() - kwargs[AMICI_SOLVER].setSensitivityMethod(SensitivityMethod_none) - - result = _subset_call(simulate_petab, kwargs) - return rdatas_to_measurement_df( - result[RDATAS], self.amici_model, self.petab_problem.measurement_df - ) - - -def _subset_call(method: Callable, kwargs: dict): - """ - Helper function to call a method with the intersection of arguments in the - method signature and the supplied arguments. +from .petab.simulator import PetabSimulator # noqa: F401 - :param method: - The method to be called. - :param kwargs: - The argument superset as a dictionary, similar to ``**kwargs`` in - method signatures. - :return: - The output of ``method``, called with the applicable arguments in - ``kwargs``. - """ - method_args = inspect.signature(method).parameters - subset_kwargs = {k: v for k, v in kwargs.items() if k in method_args} - return method(**subset_kwargs) +__all__ = [ + "PetabSimulator", +] diff --git a/deps/AMICI/python/sdist/amici/petab_util.py b/deps/AMICI/python/sdist/amici/petab_util.py index 9108b108b..cf12a7411 100644 --- a/deps/AMICI/python/sdist/amici/petab_util.py +++ b/deps/AMICI/python/sdist/amici/petab_util.py @@ -1,107 +1,24 @@ -"""Various helper functions for working with PEtab problems.""" -import re -from typing import Dict, Tuple, Union +""" +Various helper functions for working with PEtab problems. -import libsbml -import pandas as pd -import petab -from petab.C import PREEQUILIBRATION_CONDITION_ID, SIMULATION_CONDITION_ID -from petab.mapping import resolve_mapping -from petab.models import MODEL_TYPE_PYSB, MODEL_TYPE_SBML +.. deprecated:: 0.21.0 + Use :mod:`amici.petab.util` instead. +""" -# ID of model parameter that is to be added to SBML model to indicate -# preequilibration -PREEQ_INDICATOR_ID = "preequilibration_indicator" +# THIS FILE IS TO BE REMOVED - DON'T ADD ANYTHING HERE! +import warnings -def get_states_in_condition_table( - petab_problem: petab.Problem, - condition: Union[Dict, pd.Series] = None, - return_patterns: bool = False, -) -> Dict[str, Tuple[Union[float, str, None], Union[float, str, None]]]: - """Get states and their initial condition as specified in the condition table. +from .petab import PREEQ_INDICATOR_ID # noqa: F401 +from .petab.util import get_states_in_condition_table # noqa: F401 - Returns: Dictionary: ``stateId -> (initial condition simulation, initial condition preequilibration)`` - """ - if petab_problem.model.type_id not in (MODEL_TYPE_SBML, MODEL_TYPE_PYSB): - raise NotImplementedError() +warnings.warn( + f"Importing {__name__} is deprecated. Use `amici.petab.util` instead.", + DeprecationWarning, + stacklevel=2, +) - species_check_funs = { - MODEL_TYPE_SBML: lambda x: _element_is_sbml_state( - petab_problem.sbml_model, x - ), - MODEL_TYPE_PYSB: lambda x: _element_is_pysb_pattern( - petab_problem.model.model, x - ), - } - states = { - resolve_mapping(petab_problem.mapping_df, col): (None, None) - if condition is None - else ( - petab_problem.condition_df.loc[ - condition[SIMULATION_CONDITION_ID], col - ], - petab_problem.condition_df.loc[ - condition[PREEQUILIBRATION_CONDITION_ID], col - ] - if PREEQUILIBRATION_CONDITION_ID in condition - else None, - ) - for col in petab_problem.condition_df.columns - if species_check_funs[petab_problem.model.type_id]( - resolve_mapping(petab_problem.mapping_df, col) - ) - } - - if petab_problem.model.type_id == MODEL_TYPE_PYSB: - if return_patterns: - return states - import pysb.pattern - - if not petab_problem.model.model.species: - import pysb.bng - - pysb.bng.generate_equations(petab_problem.model.model) - - try: - spm = pysb.pattern.SpeciesPatternMatcher( - model=petab_problem.model.model - ) - except NotImplementedError as e: - raise NotImplementedError( - "Requires https://github.com/pysb/pysb/pull/570. " - "To use this functionality, update pysb via " - "`pip install git+https://github.com/FFroehlich/pysb@fix_pattern_matching`" - ) - - # expose model components as variables so we can evaluate patterns - for c in petab_problem.model.model.components: - globals()[c.name] = c - - states = { - f"__s{ix}": value - for pattern, value in states.items() - for ix in spm.match(eval(pattern), index=True, exact=True) - } - return states - - -def _element_is_pysb_pattern(model: "pysb.Model", element: str) -> bool: - """Check if element is a pysb pattern""" - if match := re.match(r"[a-zA-Z_][\w_]*\(", element): - return match[0][:-1] in [m.name for m in model.monomers] - return False - - -def _element_is_sbml_state(sbml_model: libsbml.Model, sbml_id: str) -> bool: - """Does the element with ID `sbml_id` correspond to a state variable?""" - if sbml_model.getCompartment(sbml_id) is not None: - return True - if sbml_model.getSpecies(sbml_id) is not None: - return True - if ( - rule := sbml_model.getRuleByVariable(sbml_id) - ) is not None and rule.getTypeCode() == libsbml.SBML_RATE_RULE: - return True - - return False +__all__ = [ + "get_states_in_condition_table", + "PREEQ_INDICATOR_ID", +] diff --git a/deps/AMICI/python/sdist/amici/plotting.py b/deps/AMICI/python/sdist/amici/plotting.py index bd1f3a8ba..3067d26bc 100644 --- a/deps/AMICI/python/sdist/amici/plotting.py +++ b/deps/AMICI/python/sdist/amici/plotting.py @@ -3,111 +3,167 @@ -------- Plotting related functions """ -from typing import Iterable, Optional, Sequence, Union + +from collections.abc import Iterable, Sequence import matplotlib.pyplot as plt +import numpy as np import pandas as pd import seaborn as sns from matplotlib.axes import Axes +import amici from . import Model, ReturnDataView from .numpy import StrOrExpr, evaluate def plot_state_trajectories( rdata: ReturnDataView, - state_indices: Optional[Iterable[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, ) -> None: """ - Plot state trajectories + Plot state trajectories. :param rdata: AMICI simulation results as returned by - :func:`amici.amici.runAmiciSimulation` - + :func:`amici.amici.runAmiciSimulation`. :param state_indices: - Indices of states for which trajectories are to be plotted - + Indices of state variables for which trajectories are to be plotted. :param ax: - matplotlib Axes instance to plot into - + :class:`matplotlib.pyplot.Axes` instance to plot into. :param model: - amici model instance - + The model *rdata* was generated from. :param prefer_names: Whether state names should be preferred over IDs, if available. + :param marker: + Point marker for plotting (see + `matplotlib documentation `_). """ if not ax: fig, ax = plt.subplots() if not state_indices: state_indices = range(rdata["x"].shape[1]) - for ix in state_indices: - if model is None: - label = f"$x_{{{ix}}}$" - elif prefer_names and model.getStateNames()[ix]: - label = model.getStateNames()[ix] - else: - label = model.getStateIds()[ix] - ax.plot(rdata["t"], rdata["x"][:, ix], label=label) - ax.set_xlabel("$t$") - ax.set_ylabel("$x(t)$") - ax.legend() - ax.set_title("State trajectories") + + if marker is None: + # Show marker if only one time point is available, + # otherwise nothing will be shown + marker = "o" if len(rdata.t) == 1 else None + + if model is None and rdata.ptr.state_ids is None: + labels = [f"$x_{{{ix}}}$" for ix in state_indices] + elif model is not None and prefer_names: + labels = np.asarray(model.getStateNames())[list(state_indices)] + labels = [ + l if l else model.getStateIds()[ix] for ix, l in enumerate(labels) + ] + elif model is not None: + labels = np.asarray(model.getStateIds())[list(state_indices)] + else: + labels = np.asarray(rdata.ptr.state_ids)[list(state_indices)] + + 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$") + ax.set_ylabel("$x(t)$") + ax.legend() + ax.set_title("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: amici.ExpData | amici.ExpDataView = None, ) -> None: """ - Plot observable trajectories + Plot observable trajectories. :param rdata: AMICI simulation results as returned by - :func:`amici.amici.runAmiciSimulation` - + :func:`amici.amici.runAmiciSimulation`. :param observable_indices: - Indices of observables for which trajectories are to be plotted - + Indices of observables for which trajectories are to be plotted. :param ax: - matplotlib Axes instance to plot into - + :class:`matplotlib.pyplot.Axes` instance to plot into. :param model: - amici model instance - + The model *rdata* was generated from. :param prefer_names: - Whether observables names should be preferred over IDs, if available. + Whether observable names should be preferred over IDs, if available. + :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: - observable_indices = range(rdata["y"].shape[1]) - for iy in observable_indices: - if model is None: - label = f"$y_{{{iy}}}$" - elif prefer_names and model.getObservableNames()[iy]: - label = model.getObservableNames()[iy] - else: - label = model.getObservableIds()[iy] - ax.plot(rdata["t"], rdata["y"][:, iy], label=label) - ax.set_xlabel("$t$") - ax.set_ylabel("$y(t)$") - ax.legend() - ax.set_title("Observable trajectories") + observable_indices = range(rdata.ny) + + if marker is None: + # Show marker if only one time point is available, + # otherwise nothing will be shown + marker = "o" if len(rdata.t) == 1 else None + + if model is None and rdata.ptr.observable_ids is None: + labels = [f"$y_{{{iy}}}$" for iy in observable_indices] + elif model is not None and prefer_names: + labels = np.asarray(model.getObservableNames())[ + list(observable_indices) + ] + labels = [ + l if l else model.getObservableIds()[ix] + for ix, l in enumerate(labels) + ] + elif model is not None: + labels = np.asarray(model.getObservableIds())[list(observable_indices)] + else: + labels = np.asarray(rdata.ptr.observable_ids)[list(observable_indices)] + + for iy, label in zip(observable_indices, labels, strict=True): + (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): """Plot Jacobian as heatmap.""" df = pd.DataFrame( data=rdata.J, - index=rdata._swigptr.state_ids_solver, - columns=rdata._swigptr.state_ids_solver, + index=rdata.ptr.state_ids_solver, + columns=rdata.ptr.state_ids_solver, ) sns.heatmap(df, center=0.0) plt.title("Jacobian") @@ -119,15 +175,15 @@ 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. :param exprs: - A symbolic expression, e.g. a sympy expression or a string that can be sympified. - Can include state variable, expression, and observable IDs, depending on whether - the respective data is available in the simulation results. - Parameters are not yet supported. + A symbolic expression, e.g., a sympy expression or a string that can be + sympified. It Can include state variable, expression, and + observable IDs, depending on whether the respective data is available + in the simulation results. Parameters are not yet supported. :param rdata: The simulation results. """ diff --git a/deps/AMICI/python/sdist/amici/pysb_import.py b/deps/AMICI/python/sdist/amici/pysb_import.py index aa1dc7cd9..1a21fef1c 100644 --- a/deps/AMICI/python/sdist/amici/pysb_import.py +++ b/deps/AMICI/python/sdist/amici/pysb_import.py @@ -12,15 +12,10 @@ from pathlib import Path from typing import ( Any, - Callable, - Dict, - Iterable, - List, - Optional, - Set, - Tuple, Union, ) +from collections.abc import Callable +from collections.abc import Iterable import numpy as np import pysb @@ -31,38 +26,38 @@ from .de_export import ( Constant, DEExporter, - DEModel, DifferentialState, Expression, LogLikelihoodY, Observable, Parameter, SigmaY, - _default_simplify, ) +from .de_model import DEModel from .import_utils import ( _get_str_symbol_identifiers, _parse_special_functions, 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 -CL_Prototype = Dict[str, Dict[str, Any]] -ConservationLaw = Dict[str, Union[Dict, str, sp.Basic]] +CL_Prototype = dict[str, dict[str, Any]] +ConservationLaw = dict[str, Union[dict, str, sp.Basic]] logger = get_logger(__name__, logging.ERROR) def pysb2amici( model: pysb.Model, - output_dir: Optional[Union[str, Path]] = 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, + output_dir: str | Path | None = None, + observables: list[str] = None, + constant_parameters: list[str] = None, + sigmas: dict[str, str] = None, + 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, @@ -72,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. @@ -182,6 +177,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: @@ -191,16 +190,16 @@ def pysb2amici( @log_execution_time("creating ODE model", logger) def ode_model_from_pysb_importer( model: pysb.Model, - constant_parameters: List[str] = None, - observables: List[str] = None, - sigmas: Dict[str, str] = None, - noise_distributions: Optional[Dict[str, Union[str, Callable]]] = None, + constant_parameters: list[str] = None, + observables: list[str] = None, + sigmas: dict[str, str] = 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` @@ -245,10 +244,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 = [] @@ -285,7 +280,7 @@ def ode_model_from_pysb_importer( @log_execution_time("processing PySB stoich. matrix", logger) def _process_stoichiometric_matrix( - pysb_model: pysb.Model, ode_model: DEModel, constant_parameters: List[str] + pysb_model: pysb.Model, ode_model: DEModel, constant_parameters: list[str] ) -> None: """ Exploits the PySB stoichiometric matrix to generate xdot derivatives @@ -410,12 +405,12 @@ def _process_pysb_species(pysb_model: pysb.Model, ode_model: DEModel) -> None: sp.Symbol(f"__s{ix}"), f"{specie}", init, xdot[ix] ) ) - logger.debug(f"Finished Processing PySB species ") + logger.debug("Finished Processing PySB species ") @log_execution_time("processing PySB parameters", logger) def _process_pysb_parameters( - pysb_model: pysb.Model, ode_model: DEModel, constant_parameters: List[str] + pysb_model: pysb.Model, ode_model: DEModel, constant_parameters: list[str] ) -> None: """ Converts pysb parameters into Parameters or Constants and adds them to @@ -443,9 +438,9 @@ def _process_pysb_parameters( def _process_pysb_expressions( pysb_model: pysb.Model, ode_model: DEModel, - observables: List[str], - sigmas: Dict[str, str], - noise_distributions: Optional[Dict[str, Union[str, Callable]]] = None, + observables: list[str], + sigmas: dict[str, str], + noise_distributions: dict[str, str | Callable] | None = None, ) -> None: r""" Converts pysb expressions/observables into Observables (with @@ -508,9 +503,9 @@ def _add_expression( expr: sp.Basic, pysb_model: pysb.Model, ode_model: DEModel, - observables: List[str], - sigmas: Dict[str, str], - noise_distributions: Optional[Dict[str, Union[str, Callable]]] = None, + observables: list[str], + sigmas: dict[str, str], + noise_distributions: dict[str, str | Callable] | None = None, ): """ Adds expressions to the ODE model given and adds observables/sigmas if @@ -568,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( @@ -579,8 +578,8 @@ def _add_expression( def _get_sigma_name_and_value( - pysb_model: pysb.Model, obs_name: str, sigmas: Dict[str, str] -) -> Tuple[str, sp.Basic]: + pysb_model: pysb.Model, obs_name: str, sigmas: dict[str, str] +) -> tuple[str, sp.Basic]: """ Tries to extract standard deviation symbolic identifier and formula for a given observable name from the pysb model and if no specification is @@ -623,9 +622,9 @@ def _get_sigma_name_and_value( def _process_pysb_observables( pysb_model: pysb.Model, ode_model: DEModel, - observables: List[str], - sigmas: Dict[str, str], - noise_distributions: Optional[Dict[str, Union[str, Callable]]] = None, + observables: list[str], + sigmas: dict[str, str], + noise_distributions: dict[str, str | Callable] | None = None, ) -> None: """ Converts :class:`pysb.core.Observable` into @@ -704,7 +703,7 @@ def _process_pysb_conservation_laws( def _compute_monomers_with_fixed_initial_conditions( pysb_model: pysb.Model, -) -> Set[str]: +) -> set[str]: """ Computes the set of monomers in a model with species that have fixed initial conditions @@ -988,7 +987,7 @@ def _cl_has_cycle(monomer: str, cl_prototypes: CL_Prototype) -> bool: def _is_in_cycle( - monomer: str, cl_prototypes: CL_Prototype, visited: List[str], root: str + monomer: str, cl_prototypes: CL_Prototype, visited: list[str], root: str ) -> bool: """ Recursively checks for cycles in conservation law dependencies via @@ -1131,7 +1130,7 @@ def _greedy_target_index_update(cl_prototypes: CL_Prototype) -> None: del prototype["appearance_counts"][prototype["local_index"]] -def _get_target_indices(cl_prototypes: CL_Prototype) -> List[List[int]]: +def _get_target_indices(cl_prototypes: CL_Prototype) -> list[list[int]]: """ Computes the list target indices for the current conservation law prototype @@ -1147,7 +1146,7 @@ def _get_target_indices(cl_prototypes: CL_Prototype) -> List[List[int]]: def _construct_conservation_from_prototypes( cl_prototypes: CL_Prototype, pysb_model: pysb.Model -) -> List[ConservationLaw]: +) -> list[ConservationLaw]: """ Computes the algebraic expression for the total amount of a given monomer @@ -1183,7 +1182,7 @@ def _construct_conservation_from_prototypes( def _add_conservation_for_constant_species( - ode_model: DEModel, conservation_laws: List[ConservationLaw] + ode_model: DEModel, conservation_laws: list[ConservationLaw] ) -> None: """ Computes the algebraic expression for the total amount of a given @@ -1209,7 +1208,7 @@ def _add_conservation_for_constant_species( def _flatten_conservation_laws( - conservation_laws: List[ConservationLaw], + conservation_laws: list[ConservationLaw], ) -> None: """ Flatten the conservation laws such that the state_expr not longer @@ -1233,7 +1232,7 @@ def _flatten_conservation_laws( def _apply_conseration_law_sub( - cl: ConservationLaw, sub: Tuple[sp.Symbol, ConservationLaw] + cl: ConservationLaw, sub: tuple[sp.Symbol, ConservationLaw] ) -> bool: """ Applies a substitution to a conservation law by replacing the @@ -1288,8 +1287,8 @@ def _state_in_cl_formula(state: sp.Symbol, cl: ConservationLaw) -> bool: def _get_conservation_law_subs( - conservation_laws: List[ConservationLaw], -) -> List[Tuple[sp.Symbol, Dict[sp.Symbol, sp.Expr]]]: + conservation_laws: list[ConservationLaw], +) -> list[tuple[sp.Symbol, dict[sp.Symbol, sp.Expr]]]: """ Computes a list of (state, coeffs) tuples for conservation laws that still appear in other conservation laws @@ -1353,8 +1352,8 @@ def has_fixed_parameter_ic( def extract_monomers( - complex_patterns: Union[pysb.ComplexPattern, List[pysb.ComplexPattern]] -) -> List[str]: + complex_patterns: pysb.ComplexPattern | list[pysb.ComplexPattern], +) -> list[str]: """ Constructs a list of monomer names contained in complex patterns. Multiplicity of names corresponds to the stoichiometry in the complex. @@ -1377,7 +1376,7 @@ def extract_monomers( def _get_unconserved_monomers( rule: pysb.Rule, pysb_model: pysb.Model -) -> Set[str]: +) -> set[str]: """ Constructs the set of monomer names for which the specified rule changes the stoichiometry of the monomer in the specified model. @@ -1419,9 +1418,9 @@ def _get_unconserved_monomers( def _get_changed_stoichiometries( - reactants: Union[pysb.ComplexPattern, List[pysb.ComplexPattern]], - products: Union[pysb.ComplexPattern, List[pysb.ComplexPattern]], -) -> Set[str]: + reactants: pysb.ComplexPattern | list[pysb.ComplexPattern], + products: pysb.ComplexPattern | list[pysb.ComplexPattern], +) -> set[str]: """ Constructs the set of monomer names which have different stoichiometries in reactants and products. @@ -1448,7 +1447,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/deps/AMICI/python/sdist/amici/sbml_import.py b/deps/AMICI/python/sdist/amici/sbml_import.py index dd24b98cf..61ce9a0ee 100644 --- a/deps/AMICI/python/sdist/amici/sbml_import.py +++ b/deps/AMICI/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,16 +16,10 @@ from pathlib import Path from typing import ( Any, - Callable, - Dict, - Iterable, - List, - Optional, - Sequence, - Set, - Tuple, Union, ) +from collections.abc import Callable +from collections.abc import Iterable, Sequence import libsbml as sbml import numpy as np @@ -32,14 +27,13 @@ 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, - _default_simplify, - smart_is_zero_matrix, - symbol_with_assumptions, ) +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, _check_unsupported_functions, @@ -54,18 +48,22 @@ sbml_time_symbol, smart_subs, smart_subs_dict, + 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] +SymbolicFormula = dict[sp.Symbol, sp.Expr] default_symbols = {symbol: {} for symbol in SymbolId} -ConservationLaw = Dict[str, Union[str, sp.Expr]] +ConservationLaw = dict[str, Union[str, sp.Expr]] logger = get_logger(__name__, logging.ERROR) @@ -136,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,30 +175,23 @@ def __init__( self.sbml: sbml.Model = self.sbml_doc.getModel() # Long and short names for model components - self.symbols: Dict[SymbolId, Dict[sp.Symbol, Dict[str, Any]]] = {} + 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 = {} self.parameter_assignment_rules: SymbolicFormula = {} self.initial_assignments: SymbolicFormula = {} - self.splines = [] + self.splines: list[AbstractSpline] = [] 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 @@ -278,21 +269,21 @@ def _reset_symbols(self) -> None: def sbml2amici( self, model_name: str, - output_dir: Union[str, Path] = None, - observables: Dict[str, Dict[str, str]] = None, - event_observables: Dict[str, Dict[str, str]] = 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,22 +442,23 @@ 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() def _build_ode_model( self, - observables: Dict[str, Dict[str, str]] = None, - event_observables: Dict[str, Dict[str, str]] = 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, 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, @@ -539,15 +531,111 @@ 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) ) + # 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.get(SymbolId.SPECIES), + dxdt, + self.symbols.get(SymbolId.ALGEBRAIC_STATE), + ) + + # correct time derivatives for compartment changes + for ix, ((species_id, species), formula) in enumerate( + zip(self.symbols[SymbolId.SPECIES].items(), dxdt, strict=False) + ): + # 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, strict=True): + # 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) def _process_sbml( self, - constant_parameters: List[str] = None, + constant_parameters: list[str] = None, hardcode_symbols: Sequence[str] = None, ) -> None: """ @@ -555,7 +643,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: @@ -727,7 +815,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), @@ -866,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() @@ -903,7 +992,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"), @@ -965,7 +1056,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: """ @@ -1035,7 +1126,7 @@ def _process_annotations(self) -> None: @log_execution_time("processing SBML parameters", logger) def _process_parameters( self, - constant_parameters: List[str] = None, + constant_parameters: list[str] = None, hardcode_symbols: Sequence[str] = None, ) -> None: """ @@ -1056,15 +1147,28 @@ def _process_parameters( "Parameter does not exist." % parameter ) + # parameter ID => initial assignment sympy expression + par_id_to_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 + } + 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) ): @@ -1079,7 +1183,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 ] @@ -1096,17 +1203,19 @@ 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) + # 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 := self._get_element_initial_assignment(par.getId()) - ) is not None and ia.find( - sp.core.function.UndefinedFunction("rateOf") + (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) @@ -1165,7 +1274,7 @@ def _process_reactions(self): # accounts for possibly variable compartments. self.stoichiometric_matrix[ species["index"], reaction_index - ] += (sign * stoichiometry * species["conversion_factor"]) + ] += sign * stoichiometry * species["conversion_factor"] if reaction.isSetId(): sym_math = self._local_symbols[reaction.getId()] else: @@ -1456,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.""" @@ -1486,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() @@ -1498,14 +1641,12 @@ 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() - 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() @@ -1523,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 @@ -1536,7 +1677,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, @@ -1544,15 +1685,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)] @@ -1595,19 +1736,94 @@ 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, - observables: Union[Dict[str, Dict[str, str]], None], - sigmas: Dict[str, Union[str, float]], - noise_distributions: Dict[str, str], + observables: dict[str, dict[str, str]] | None, + sigmas: dict[str, str | float], + noise_distributions: dict[str, str], ) -> None: """ Perform symbolic computations required for observable and objective @@ -1671,9 +1887,9 @@ def _process_observables( @log_execution_time("processing SBML event observables", logger) def _process_event_observables( self, - event_observables: Dict[str, Dict[str, str]], - event_sigmas: Dict[str, Union[str, float]], - event_noise_distributions: Dict[str, str], + event_observables: dict[str, dict[str, str]], + event_sigmas: dict[str, str | float], + event_noise_distributions: dict[str, str], ) -> None: """ Perform symbolic computations required for observable and objective @@ -1731,7 +1947,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) @@ -1788,8 +2005,8 @@ def _generate_default_observables(self): def _process_log_likelihood( self, - sigmas: Dict[str, Union[str, float]], - noise_distributions: Dict[str, str], + sigmas: dict[str, str | float], + noise_distributions: dict[str, str], events: bool = False, event_reg: bool = False, ): @@ -1849,6 +2066,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") @@ -1859,6 +2077,7 @@ def _process_log_likelihood( zip( _get_str_symbol_identifiers(obs_id), (obs_id, obs["measurement_symbol"], sigma_id), + strict=True, ) ), ) @@ -1882,7 +2101,11 @@ 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.EXPRESSION], + self.symbols[SymbolId.PARAMETER], + self.symbols[SymbolId.FIXED_PARAMETER], ): continue @@ -1936,8 +2159,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. @@ -1956,20 +2179,18 @@ 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) 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 = [] @@ -2017,7 +2238,7 @@ def process_conservation_laws(self, ode_model) -> None: def _get_conservation_laws_demartino( self, ode_model: DEModel, - ) -> List[Tuple[int, List[int], List[float]]]: + ) -> list[tuple[int, list[int], list[float]]]: """Identify conservation laws based on algorithm by DeMartino et al. (see conserved_moieties.py). @@ -2046,7 +2267,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 [] @@ -2075,9 +2297,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() @@ -2093,7 +2315,7 @@ def _get_conservation_laws_demartino( def _get_conservation_laws_rref( self, - ) -> List[Tuple[int, List[int], List[float]]]: + ) -> list[tuple[int, list[int], list[float]]]: """Identify conservation laws based on left nullspace of the stoichiometric matrix, computed through (numeric) Gaussian elimination @@ -2123,7 +2345,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 [] @@ -2156,8 +2379,8 @@ def _get_conservation_laws_rref( return raw_cls def _add_conservation_for_non_constant_species( - self, model: DEModel, conservation_laws: List[ConservationLaw] - ) -> List[int]: + self, model: DEModel, conservation_laws: list[ConservationLaw] + ) -> list[int]: """Add non-constant species to conservation laws :param model: @@ -2221,7 +2444,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, ) }, } @@ -2360,9 +2586,9 @@ def _replace_in_all_expressions( # rule (at the end of the _process_species method), hence needs to be # processed here too. self.compartments = { - smart_subs(c, old, new) - if replace_identifiers - else c: smart_subs(v, old, self._make_initial(new)) + smart_subs(c, old, new) if replace_identifiers else c: smart_subs( + v, old, self._make_initial(new) + ) for c, v in self.compartments.items() } @@ -2390,7 +2616,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 @@ -2443,7 +2669,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 @@ -2516,6 +2742,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 @@ -2640,8 +2934,8 @@ def assignmentRules2observables( def _add_conservation_for_constant_species( - ode_model: DEModel, conservation_laws: List[ConservationLaw] -) -> List[int]: + ode_model: DEModel, conservation_laws: list[ConservationLaw] +) -> list[int]: """ Adds constant species to conservations laws @@ -2738,7 +3032,7 @@ def get_species_initial(species: sbml.Species) -> sp.Expr: def _get_list_of_species_references( sbml_model: sbml.Model, -) -> List[sbml.SpeciesReference]: +) -> list[sbml.SpeciesReference]: """ Extracts list of species references as SBML doesn't provide a native function for this. @@ -2760,7 +3054,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 @@ -2776,15 +3070,17 @@ def replace_logx(math_str: Union[str, float, None]) -> Union[str, float, None]: return re.sub(r"(^|\W)log(\d+)\(", r"\g<1>1/ln(\2)*ln(", math_str) -def _collect_event_assignment_parameter_targets(sbml_model: sbml.Model): - targets = set() +def _collect_event_assignment_parameter_targets( + sbml_model: sbml.Model, +) -> list[sp.Symbol]: + targets = [] sbml_parameters = sbml_model.getListOfParameters() sbml_parameter_ids = [p.getId() for p in sbml_parameters] for event in sbml_model.getListOfEvents(): for event_assignment in event.getListOfEventAssignments(): target_id = event_assignment.getVariable() if target_id in sbml_parameter_ids: - targets.add( + targets.append( _get_identifier_symbol( sbml_parameters[sbml_parameter_ids.index(target_id)] ) @@ -2793,7 +3089,7 @@ def _collect_event_assignment_parameter_targets(sbml_model: sbml.Model): 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) @@ -2811,9 +3107,9 @@ def _parse_special_functions_sbml( def _validate_observables( - observables: Union[Dict[str, Dict[str, str]], None], - sigmas: Dict[str, Union[str, float]], - noise_distributions: Dict[str, str], + observables: dict[str, dict[str, str]] | None, + sigmas: dict[str, str | float], + noise_distributions: dict[str, str], events: bool = False, ) -> None: if observables is None or not observables: @@ -2841,7 +3137,7 @@ def _validate_observables( def _check_symbol_nesting( - symbols: Dict[sp.Symbol, Dict[str, sp.Expr]], symbol_type: str + symbols: dict[sp.Symbol, dict[str, sp.Expr]], symbol_type: str ): observable_syms = set(symbols.keys()) for obs in symbols.values(): @@ -2864,7 +3160,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 @@ -2876,7 +3173,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/deps/AMICI/python/sdist/amici/sbml_utils.py b/deps/AMICI/python/sdist/amici/sbml_utils.py index 66c9d01bb..d40610f4a 100644 --- a/deps/AMICI/python/sdist/amici/sbml_utils.py +++ b/deps/AMICI/python/sdist/amici/sbml_utils.py @@ -11,7 +11,7 @@ import sympy as sp if TYPE_CHECKING: - from typing import Any, Dict, Optional, Tuple, Union + from typing import Any, Union SbmlID = Union[str, sp.Symbol] @@ -52,7 +52,7 @@ class SbmlAnnotationError(SBMLException): def create_sbml_model( model_id: str, level: int = 2, version: int = 5 -) -> Tuple[libsbml.SBMLDocument, libsbml.Model]: +) -> tuple[libsbml.SBMLDocument, libsbml.Model]: """Helper for creating an empty SBML model. :param model_id: @@ -116,10 +116,10 @@ def add_species( model: libsbml.Model, species_id: SbmlID, *, - compartment_id: Optional[str] = None, - name: Union[bool, str] = False, + compartment_id: str | None = None, + name: bool | str = False, initial_amount: float = 0.0, - units: Optional[str] = None, + units: str | None = None, ) -> libsbml.Species: """Helper for adding a species to a SBML model. @@ -182,10 +182,10 @@ def add_parameter( model: libsbml.Model, parameter_id: SbmlID, *, - name: Union[bool, str] = False, - value: Optional[float] = None, - units: Optional[str] = None, - constant: Optional[bool] = None, + name: bool | str = False, + value: float | None = None, + units: str | None = None, + constant: bool | None = None, ) -> libsbml.Parameter: """Helper for adding a parameter to a SBML model. @@ -239,7 +239,7 @@ def add_assignment_rule( model: libsbml.Model, variable_id: SbmlID, formula, - rule_id: Optional[str] = None, + rule_id: str | None = None, ) -> libsbml.AssignmentRule: """Helper for adding an assignment rule to a SBML model. @@ -287,7 +287,7 @@ def add_rate_rule( model: libsbml.Model, variable_id: SbmlID, formula, - rule_id: Optional[str] = None, + rule_id: str | None = None, ) -> libsbml.RateRule: """ Helper for adding a rate rule to a SBML model. @@ -337,7 +337,7 @@ def add_inflow( species_id: SbmlID, rate, *, - reaction_id: Optional[str] = None, + reaction_id: str | None = None, reversible: bool = False, ) -> libsbml.Reaction: species_id = str(species_id) @@ -364,9 +364,7 @@ def add_inflow( return reaction -def get_sbml_units( - model: libsbml.Model, x: Union[SbmlID, sp.Basic] -) -> Union[None, str]: +def get_sbml_units(model: libsbml.Model, x: SbmlID | sp.Basic) -> None | str: """Try to get the units for expression `x`. :param model: @@ -493,7 +491,7 @@ def mathml2sympy( mathml: str, *, evaluate: bool = False, - locals: Optional[Dict[str, Any]] = None, + locals: dict[str, Any] | None = None, expression_type: str = "mathml2sympy", ) -> sp.Basic: ast = libsbml.readMathMLFromString(mathml) @@ -519,8 +517,8 @@ def mathml2sympy( def _parse_logical_operators( - math_str: Union[str, float, None] -) -> Union[str, float, None]: + math_str: str | float | None, +) -> str | float | None: """ Parses a math string in order to replace logical operators by a form parsable for sympy diff --git a/deps/AMICI/python/sdist/amici/setup.template.py b/deps/AMICI/python/sdist/amici/setup.template.py index e7995e2c5..14edbfd2d 100644 --- a/deps/AMICI/python/sdist/amici/setup.template.py +++ b/deps/AMICI/python/sdist/amici/setup.template.py @@ -1,5 +1,7 @@ """AMICI model package setup""" + import os +import sys from pathlib import Path from amici import _get_amici_path @@ -22,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(), @@ -33,7 +40,9 @@ def get_extension() -> CMakeExtension: f"{prefix_path.as_posix()}/lib64/cmake/SuiteSparse", f"-DKLU_ROOT={prefix_path.as_posix()}", "-DAMICI_PYTHON_BUILD_EXT_ONLY=ON", + f"-DPython3_EXECUTABLE={Path(sys.executable).as_posix()}", ], + cmake_build_type="Debug" if debug_build else "Release", ) @@ -69,8 +78,7 @@ 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.9", + python_requires=">=3.10", package_data={}, zip_safe=False, classifiers=CLASSIFIERS, diff --git a/deps/AMICI/python/sdist/amici/splines.py b/deps/AMICI/python/sdist/amici/splines.py index fdb091204..5d423a19e 100644 --- a/deps/AMICI/python/sdist/amici/splines.py +++ b/deps/AMICI/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,21 +14,16 @@ from numbers import Real from typing import ( Any, - Callable, - Dict, - List, - Optional, - Sequence, - Set, - Tuple, Union, ) + from collections.abc import Callable + from collections.abc import Sequence from . import sbml_import - BClike = Union[None, str, Tuple[Union[None, str], Union[None, str]]] + BClike = Union[None, str, tuple[Union[None, str], Union[None, str]]] - NormalizedBC = Tuple[Union[None, str], Union[None, str]] + NormalizedBC = tuple[Union[None, str], Union[None, str]] import collections.abc import logging @@ -57,6 +53,7 @@ pretty_xml, sbml_mathml, ) +from .constants import SymbolId logger = get_logger(__name__, logging.WARNING) @@ -88,11 +85,11 @@ class UniformGrid(collections.abc.Sequence): def __init__( self, - start: Union[Real, sp.Basic], - stop: Union[Real, sp.Basic], - step: Optional[Union[Real, sp.Basic]] = None, + start: Real | sp.Basic, + stop: Real | sp.Basic, + step: Real | sp.Basic | None = None, *, - number_of_nodes: Optional[Integral] = None, + number_of_nodes: Integral | None = None, always_include_stop: bool = True, ): """Create a new ``UniformGrid``. @@ -219,11 +216,11 @@ class AbstractSpline(ABC): def __init__( self, - sbml_id: Union[str, sp.Symbol], + sbml_id: str | sp.Symbol, nodes: Sequence, values_at_nodes: Sequence, *, - evaluate_at: Optional[Union[str, sp.Basic]] = None, + evaluate_at: str | sp.Basic | None = None, bc: BClike = None, extrapolate: BClike = None, logarithmic_parametrization: bool = False, @@ -418,7 +415,7 @@ def _normalize_bc(bc: BClike) -> NormalizedBC: def _normalize_extrapolate( self, bc: NormalizedBC, extrapolate: BClike - ) -> Tuple[NormalizedBC, NormalizedBC]: + ) -> tuple[NormalizedBC, NormalizedBC]: """ Preprocess `extrapolate` to a standard form and perform consistency checks @@ -574,12 +571,10 @@ 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( + fixed_parameters: list[sp.Symbol] = list( importer.symbols[SymbolId.FIXED_PARAMETER].keys() ) - species: List[sp.Symbol] = list( + species: list[sp.Symbol] = list( importer.symbols[SymbolId.SPECIES].keys() ) @@ -599,16 +594,16 @@ 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 if not np.all(np.diff(nodes_values) >= 0): raise ValueError("nodes should be strictly increasing!") - def poly( - self, i: Integral, *, x: Union[Real, sp.Basic] = None - ) -> sp.Basic: + def poly(self, i: Integral, *, x: Real | sp.Basic = None) -> sp.Basic: """ Get the polynomial interpolant on the ``(nodes[i], nodes[i+1])`` interval. The polynomial is written in Horner form with respect to the scaled @@ -646,7 +641,7 @@ def poly( with evaluate(False): return poly.subs(t, t_value) - def poly_variable(self, x: Union[Real, sp.Basic], i: Integral) -> sp.Basic: + def poly_variable(self, x: Real | sp.Basic, i: Integral) -> sp.Basic: """ Given an evaluation point, return the value of the variable in which the polynomial on the ``i``-th interval is expressed. @@ -656,15 +651,13 @@ def poly_variable(self, x: Union[Real, sp.Basic], i: Integral) -> sp.Basic: return self._poly_variable(x, i) @abstractmethod - def _poly_variable( - self, x: Union[Real, sp.Basic], i: Integral - ) -> sp.Basic: + def _poly_variable(self, x: Real | sp.Basic, i: Integral) -> sp.Basic: """This function (and not poly_variable) should be implemented by the subclasses""" raise NotImplementedError() @abstractmethod - def _poly(self, t: Union[Real, sp.Basic], i: Integral) -> sp.Basic: + def _poly(self, t: Real | sp.Basic, i: Integral) -> sp.Basic: """ Return the symbolic expression for the spline restricted to the `i`-th interval as a polynomial in the scaled variable `t`. @@ -672,7 +665,7 @@ def _poly(self, t: Union[Real, sp.Basic], i: Integral) -> sp.Basic: raise NotImplementedError() def segment_formula( - self, i: Integral, *, x: Union[Real, sp.Basic] = None + self, i: Integral, *, x: Real | sp.Basic = None ) -> sp.Basic: """ Return the formula for the actual value of the spline expression @@ -700,7 +693,7 @@ def y_scaled(self, i: Integral): @property def extrapolation_formulas( self, - ) -> Tuple[Union[None, sp.Basic], Union[None, sp.Basic]]: + ) -> tuple[None | sp.Basic, None | sp.Basic]: """ Returns the extrapolation formulas on the left and right side of the interval ``(nodes[0], nodes[-1])``. @@ -710,9 +703,9 @@ def extrapolation_formulas( def _extrapolation_formulas( self, - x: Union[Real, sp.Basic], - extrapolate: Optional[NormalizedBC] = None, - ) -> Tuple[Union[None, sp.Expr], Union[None, sp.Expr]]: + x: Real | sp.Basic, + extrapolate: NormalizedBC | None = None, + ) -> tuple[None | sp.Expr, None | sp.Expr]: if extrapolate is None: extr_left, extr_right = self.extrapolate else: @@ -770,7 +763,7 @@ def mathml_formula(self) -> sp.Piecewise: def _formula( self, *, - x: Union[Real, sp.Basic] = None, + x: Real | sp.Basic = None, sbml_syms: bool = False, sbml_ops: bool = False, cache: bool = True, @@ -845,15 +838,15 @@ def _formula( return formula @property - def period(self) -> Union[sp.Basic, None]: + def period(self) -> sp.Basic | None: """Period of a periodic spline. `None` if the spline is not periodic.""" if self.bc == ("periodic", "periodic"): return self.nodes[-1] - self.nodes[0] return None def _to_base_interval( - self, x: Union[Real, sp.Basic], *, with_interval_number: bool = False - ) -> Union[sp.Basic, Tuple[sp.core.numbers.Integer, sp.Basic]]: + self, x: Real | sp.Basic, *, with_interval_number: bool = False + ) -> sp.Basic | tuple[sp.core.numbers.Integer, sp.Basic]: """For periodic splines, maps the real point `x` to the reference period.""" if self.bc != ("periodic", "periodic"): @@ -874,19 +867,19 @@ def _to_base_interval( return k, z return z - def evaluate(self, x: Union[Real, sp.Basic]) -> sp.Basic: + def evaluate(self, x: Real | sp.Basic) -> sp.Basic: """Evaluate the spline at the point `x`.""" _x = sp.Dummy("x") return self._formula(x=_x, cache=False).subs(_x, x) - def derivative(self, x: Union[Real, sp.Basic], **kwargs) -> sp.Expr: + def derivative(self, x: Real | sp.Basic, **kwargs) -> sp.Expr: """Evaluate the spline derivative at the point `x`.""" # NB kwargs are used to pass on extrapolate=None # when called from .extrapolation_formulas() _x = sp.Dummy("x") return self._formula(x=_x, cache=False, **kwargs).diff(_x).subs(_x, x) - def second_derivative(self, x: Union[Real, sp.Basic]) -> sp.Basic: + def second_derivative(self, x: Real | sp.Basic) -> sp.Basic: """Evaluate the spline second derivative at the point `x`.""" _x = sp.Dummy("x") return self._formula(x=_x, cache=False).diff(_x).diff(_x).subs(_x, x) @@ -907,9 +900,7 @@ def squared_L2_norm_of_curvature(self) -> sp.Basic: ) return sp.simplify(integral) - def integrate( - self, x0: Union[Real, sp.Basic], x1: Union[Real, sp.Basic] - ) -> sp.Basic: + def integrate(self, x0: Real | sp.Basic, x1: Real | sp.Basic) -> sp.Basic: """Integrate the spline between the points `x0` and `x1`.""" x = sp.Dummy("x") x0, x1 = sp.sympify((x0, x1)) @@ -969,7 +960,7 @@ def amici_annotation(self) -> str: # Check XML and prettify return pretty_xml(annotation) - def _annotation_attributes(self) -> Dict[str, Any]: + def _annotation_attributes(self) -> dict[str, Any]: attributes = {"spline_method": self.method} if self.bc[0] == self.bc[1]: @@ -996,7 +987,7 @@ def _annotation_attributes(self) -> Dict[str, Any]: return attributes - def _annotation_children(self) -> Dict[str, Union[str, List[str]]]: + def _annotation_children(self) -> dict[str, str | list[str]]: children = {} with evaluate(False): @@ -1024,12 +1015,12 @@ def add_to_sbml_model( self, model: libsbml.Model, *, - auto_add: Union[bool, str] = False, - x_nominal: Optional[Sequence[float]] = None, - y_nominal: Optional[Union[Sequence[float], float]] = None, - x_units: Optional[str] = None, - y_units: Optional[str] = None, - y_constant: Optional[Union[Sequence[bool], bool]] = None, + auto_add: bool | str = False, + x_nominal: Sequence[float] | None = None, + y_nominal: Sequence[float] | float | None = None, + x_units: str | None = None, + y_units: str | None = None, + y_constant: Sequence[bool] | bool | None = None, ) -> None: """ Function to add the spline to an SBML model using an assignment rule @@ -1104,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 @@ -1127,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( @@ -1196,7 +1187,7 @@ def is_spline(rule: libsbml.AssignmentRule) -> bool: @staticmethod def get_annotation( rule: libsbml.AssignmentRule, - ) -> Union[ET.Element, None]: + ) -> ET.Element | None: """ Extract AMICI spline annotation from an SBML assignment rule (given as a :py:class:`libsbml.AssignmentRule` object). @@ -1216,7 +1207,7 @@ def from_annotation( sbml_id: sp.Symbol, annotation: ET.Element, *, - locals_: Dict[str, Any], + locals_: dict[str, Any], ) -> AbstractSpline: """Create a spline object from a SBML annotation. @@ -1289,9 +1280,9 @@ def from_annotation( @classmethod def _from_annotation( cls, - attributes: Dict[str, Any], - children: Dict[str, List[sp.Basic]], - ) -> Dict[str, Any]: + attributes: dict[str, Any], + children: dict[str, list[sp.Basic]], + ) -> dict[str, Any]: """ Given the attributes and children of a AMICI spline annotation, returns the keyword arguments to be passed @@ -1354,15 +1345,13 @@ def _from_annotation( return kwargs - def parameters(self, importer: sbml_import.SbmlImporter) -> Set[sp.Symbol]: + 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()) ) - def _parameters(self) -> Set[sp.Symbol]: + def _parameters(self) -> set[sp.Symbol]: parameters = set() for y in self.values_at_nodes: parameters.update(y.free_symbols) @@ -1451,12 +1440,12 @@ def _eval_is_real(self): def plot( self, - parameters: Optional[Dict] = None, + parameters: dict | None = None, *, - xlim: Optional[Tuple[float, float]] = None, + xlim: tuple[float, float] | None = None, npoints: int = 100, - xlabel: Optional[str] = None, - ylabel: Union[str, None] = "spline value", + xlabel: str | None = None, + ylabel: str | None = "spline value", ax=None, ): "Plots the spline, highlighting the nodes positions." @@ -1486,9 +1475,9 @@ def plot( def spline_user_functions( - splines: List[AbstractSpline], - p_index: Dict[sp.Symbol, int], -) -> Dict[str, List[Tuple[Callable[..., bool], Callable[..., str]]]]: + splines: list[AbstractSpline], + p_index: dict[sp.Symbol, int], +) -> dict[str, list[tuple[Callable[..., bool], Callable[..., str]]]]: """ Custom user functions to be used in `ODEExporter` for linking spline expressions to C++ code. @@ -1510,7 +1499,10 @@ def spline_user_functions( "AmiciSplineSensitivity": [ ( lambda *args: True, - lambda spline_id, x, param_id, *p: f"sspl_{spline_ids.index(spline_id)}_{p_index[param_id]}", + lambda spline_id, + x, + param_id, + *p: f"sspl_{spline_ids.index(spline_id)}_{p_index[param_id]}", ) ], } @@ -1519,12 +1511,12 @@ def spline_user_functions( class CubicHermiteSpline(AbstractSpline): def __init__( self, - sbml_id: Union[str, sp.Symbol], + sbml_id: str | sp.Symbol, nodes: Sequence, values_at_nodes: Sequence, derivatives_at_nodes: Sequence = None, *, - evaluate_at: Optional[Union[str, sp.Basic]] = None, + evaluate_at: str | sp.Basic | None = None, bc: BClike = "auto", extrapolate: BClike = None, logarithmic_parametrization: bool = False, @@ -1667,9 +1659,8 @@ 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]) + species: list[sp.Symbol] = list(importer.symbols[SymbolId.SPECIES]) for d in self.derivatives_at_nodes: if len(d.free_symbols.intersection(species)) != 0: raise ValueError( @@ -1688,15 +1679,13 @@ def d_scaled(self, i: Integral) -> sp.Expr: return self.derivatives_at_nodes[i] / self.values_at_nodes[i] return self.derivatives_at_nodes[i] - def _poly_variable( - self, x: Union[Real, sp.Basic], i: Integral - ) -> sp.Basic: + def _poly_variable(self, x: Real | sp.Basic, i: Integral) -> sp.Basic: assert 0 <= i < len(self.nodes) - 1 dx = self.nodes[i + 1] - self.nodes[i] with evaluate(False): return (x - self.nodes[i]) / dx - def _poly(self, t: Union[Real, sp.Basic], i: Integral) -> sp.Basic: + def _poly(self, t: Real | sp.Basic, i: Integral) -> sp.Basic: """ Return the symbolic expression for the spline restricted to the `i`-th interval as polynomial in the scaled variable `t`. @@ -1718,7 +1707,7 @@ def _poly(self, t: Union[Real, sp.Basic], i: Integral) -> sp.Basic: with evaluate(False): return h00 * y0 + h10 * dx * dy0 + h01 * y1 + h11 * dx * dy1 - def _annotation_children(self) -> Dict[str, Union[str, List[str]]]: + def _annotation_children(self) -> dict[str, str | list[str]]: children = super()._annotation_children() if not self._derivatives_by_fd: children["spline_derivatives"] = [ @@ -1726,7 +1715,7 @@ def _annotation_children(self) -> Dict[str, Union[str, List[str]]]: ] return children - def _parameters(self) -> Set[sp.Symbol]: + def _parameters(self) -> set[sp.Symbol]: parameters = super()._parameters() for d in self.derivatives_at_nodes: parameters.update(d.free_symbols) @@ -1741,7 +1730,7 @@ def _replace_in_all_expressions( ] @classmethod - def _from_annotation(cls, attributes, children) -> Dict[str, Any]: + def _from_annotation(cls, attributes, children) -> dict[str, Any]: kwargs = super()._from_annotation(attributes, children) if "spline_derivatives" in children.keys(): diff --git a/deps/AMICI/python/sdist/amici/swig.py b/deps/AMICI/python/sdist/amici/swig.py index ef7564638..e0c518957 100644 --- a/deps/AMICI/python/sdist/amici/swig.py +++ b/deps/AMICI/python/sdist/amici/swig.py @@ -1,11 +1,13 @@ """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, @@ -15,6 +17,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 +43,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"), @@ -50,11 +55,17 @@ 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? - if node.returns: + if node.returns and isinstance(node.returns, ast.Constant): node.returns = self._new_annot(node.returns.value) # Has arguments? @@ -62,14 +73,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] @@ -97,21 +111,95 @@ def _new_annot(self, old_annot: str): ) in self.mapping: value_type_annot = self.mapping[value_type] if isinstance(value_type_annot, ast.Constant): - return ast.Name(f"Tuple['{value_type_annot.value}']") + return ast.Name(f"tuple['{value_type_annot.value}']") if isinstance(value_type_annot, ast.Name): - return ast.Name(f"Tuple[{value_type_annot.id}]") + return ast.Name(f"tuple[{value_type_annot.id}]") 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 (``: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. + + Overloaded functions are skipped. + """ + 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 type_str := self.extract_rtype(line): + # handle `:rtype:` + node.returns = ast.Constant(type_str) + lines_to_remove.add(line_no) + continue + + 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 == arg_name: + arg.annotation = ast.Constant(type_str) + 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) + + @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, "r") as f: + with open(infilename) as f: source = f.read() parsed_source = ast.parse(source) diff --git a/deps/AMICI/python/sdist/amici/swig_wrappers.py b/deps/AMICI/python/sdist/amici/swig_wrappers.py index f56f3bd5d..2e90891df 100644 --- a/deps/AMICI/python/sdist/amici/swig_wrappers.py +++ b/deps/AMICI/python/sdist/amici/swig_wrappers.py @@ -1,13 +1,18 @@ """Convenience wrappers for the swig interface""" + import logging -import sys import warnings -from contextlib import contextmanager, suppress -from typing import Any, Dict, List, Optional, Sequence, Union +from typing import Any 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 @@ -17,76 +22,17 @@ __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: - 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 _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, - edata: Optional[AmiciExpData] = None, + edata: AmiciExpData | None = None, ) -> "numpy.ReturnDataView": """ Convenience wrapper around :py:func:`amici.amici.runAmiciSimulation` @@ -114,47 +60,26 @@ 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(): - 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) 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 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)): - # 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)): - return amici_swig.ExpData(_get_ptr(args[0])) - else: - return amici_swig.ExpData(*args) - - def runAmiciSimulations( model: AmiciModel, solver: AmiciSolver, edata_list: AmiciExpDataVector, failfast: bool = True, num_threads: int = 1, -) -> List["numpy.ReturnDataView"]: +) -> list["numpy.ReturnDataView"]: """ Convenience wrapper for loops of amici.runAmiciSimulation @@ -176,18 +101,18 @@ 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(): - 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: @@ -197,7 +122,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` @@ -211,8 +136,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` @@ -249,12 +174,13 @@ def writeSolverSettingsToHDF5( "SteadyStateSensitivityMode", ("t0", "setT0"), "Timepoints", + "_steadystate_mask", ] def get_model_settings( model: AmiciModel, -) -> Dict[str, Any]: +) -> dict[str, Any]: """Get model settings that are set independently of the compiled model. :param model: The AMICI model instance. @@ -285,7 +211,7 @@ def get_model_settings( def set_model_settings( model: AmiciModel, - settings: Dict[str, Any], + settings: dict[str, Any], ) -> None: """Set model settings. diff --git a/deps/AMICI/python/sdist/amici/sympy_utils.py b/deps/AMICI/python/sdist/amici/sympy_utils.py new file mode 100644 index 000000000..9794fadef --- /dev/null +++ b/deps/AMICI/python/sdist/amici/sympy_utils.py @@ -0,0 +1,201 @@ +"""Functionality for working with sympy objects.""" + +import os +from itertools import starmap +from typing import Any +from collections.abc import Callable +import contextlib +import sympy as sp +import logging +from .logging import log_execution_time, get_logger + + +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: sp.MutableDenseMatrix | sp.MutableSparseMatrix, + y: sp.MutableDenseMatrix, +) -> 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: 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, strict=True) + 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/deps/AMICI/python/sdist/amici/testing.py b/deps/AMICI/python/sdist/amici/testing.py index cdee80b1f..40b791d0d 100644 --- a/deps/AMICI/python/sdist/amici/testing.py +++ b/deps/AMICI/python/sdist/amici/testing.py @@ -1,4 +1,5 @@ """Test support functions""" + import os import sys from tempfile import TemporaryDirectory @@ -32,7 +33,19 @@ class TemporaryDirectoryWinSafe(TemporaryDirectory): such failures. """ + def __init__(self, *args, delete=True, **kwargs): + super().__init__(*args, **kwargs) + # TODO Python3.12 TemporaryDirectory already has a delete argument + # remove this once we drop support for Python3.11 + self.delete = delete + + if not self.delete: + self._finalizer.detach() + def cleanup(self): + if not self.delete: + return + try: super().cleanup() except PermissionError as e: diff --git a/deps/AMICI/python/sdist/bin b/deps/AMICI/python/sdist/bin deleted file mode 120000 index 353d1e9f9..000000000 --- a/deps/AMICI/python/sdist/bin +++ /dev/null @@ -1 +0,0 @@ -../bin/ \ No newline at end of file diff --git a/deps/AMICI/python/sdist/pyproject.toml b/deps/AMICI/python/sdist/pyproject.toml index 64a5023d4..621fd4395 100644 --- a/deps/AMICI/python/sdist/pyproject.toml +++ b/deps/AMICI/python/sdist/pyproject.toml @@ -1,11 +1,127 @@ +# 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", "numpy>=2.0", - "cmake-build-extension==0.5.1", + "cmake-build-extension==0.6.0", ] build-backend = "setuptools.build_meta" +[project] +name = "amici" +dynamic = ["version"] +description = "Advanced multi-language Interface to CVODES and IDAS" +requires-python = ">=3.10" +dependencies = [ + "cmake-build-extension==0.6.0", + "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'", + "numpy>=1.26.2; python_version=='3.12'", + "numpy; python_version>='3.13'", + "python-libsbml", + "pandas>=2.0.2", + "pyarrow", + "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.4.0"] +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 + +[tool.ruff] +line-length = 79 +extend-include = ["*.ipynb"] + +[tool.ruff.lint] +extend-select = ["B028"] +ignore = ["E402", "F403", "F405", "E741"] diff --git a/deps/AMICI/python/sdist/setup.cfg b/deps/AMICI/python/sdist/setup.cfg deleted file mode 100644 index f41957875..000000000 --- a/deps/AMICI/python/sdist/setup.cfg +++ /dev/null @@ -1,78 +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 - h5py - pandas>=2.0.2 - wurlitzer - toposort - setuptools>=48 - mpmath -include_package_data = True -zip_safe = False - -[options.extras_require] -petab = petab>=0.2.1 -pysb = pysb>=1.13.1 -test = - pytest - pytest-cov - pytest-rerunfailures - coverage - shyaml - antimony -vis = - matplotlib - seaborn - -[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_import:_main - amici_import_petab.py = amici.petab_import:_main diff --git a/deps/AMICI/python/sdist/setup.py b/deps/AMICI/python/sdist/setup.py index 616abf751..d01a0ec75 100755 --- a/deps/AMICI/python/sdist/setup.py +++ b/deps/AMICI/python/sdist/setup.py @@ -9,6 +9,7 @@ - swig>=3.0 - Optional: hdf5 libraries and headers """ + import os import sys from pathlib import Path @@ -41,6 +42,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 @@ -50,9 +52,16 @@ 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", + "-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 + # "-DBLA_VENDOR=All", + "-DBLAS_LIBRARIES=dummy", + "-DSUITESPARSE_USE_64BIT_BLAS=ON", + "-DSUITESPARSE_USE_CUDA=OFF", + "-DSUITESPARSE_USE_FORTRAN=OFF", ], ) # SuiteSparse AMD @@ -62,7 +71,9 @@ def get_extensions(): source_dir="amici/ThirdParty/SuiteSparse/AMD", cmake_configure_options=[ *global_cmake_configure_options, - "-DNFORTRAN=TRUE", + "-DBUILD_SHARED_LIBS=OFF", + "-DCMAKE_POSITION_INDEPENDENT_CODE=ON", + "-DSUITESPARSE_USE_FORTRAN=OFF", ], ) # SuiteSparse BTF @@ -72,7 +83,9 @@ def get_extensions(): source_dir="amici/ThirdParty/SuiteSparse/BTF", cmake_configure_options=[ *global_cmake_configure_options, - "-DNFORTRAN=TRUE", + "-DSUITESPARSE_USE_FORTRAN=OFF", + "-DBUILD_SHARED_LIBS=OFF", + "-DCMAKE_POSITION_INDEPENDENT_CODE=ON", ], ) # SuiteSparse COLAMD @@ -82,7 +95,9 @@ def get_extensions(): source_dir="amici/ThirdParty/SuiteSparse/COLAMD", cmake_configure_options=[ *global_cmake_configure_options, - "-DNFORTRAN=TRUE", + "-DSUITESPARSE_USE_FORTRAN=OFF", + "-DBUILD_SHARED_LIBS=OFF", + "-DCMAKE_POSITION_INDEPENDENT_CODE=ON", ], ) # SuiteSparse KLU @@ -92,9 +107,11 @@ 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", + "-DBUILD_SHARED_LIBS=OFF", + "-DCMAKE_POSITION_INDEPENDENT_CODE=ON", ], ) # SUNDIALS @@ -120,10 +137,14 @@ 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 + 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", @@ -138,21 +159,13 @@ 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] 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"), - "r", - encoding="utf-8", - ) as fh: - long_description = fh.read() - ext_modules = get_extensions() # handle parallel building @@ -172,8 +185,6 @@ def main(): "develop": AmiciDevelop, "build_py": AmiciBuildPy, }, - long_description=long_description, - long_description_content_type="text/markdown", ext_modules=ext_modules, ) diff --git a/deps/AMICI/python/tests/conftest.py b/deps/AMICI/python/tests/conftest.py index 9ab64b91d..d8d882fcf 100644 --- a/deps/AMICI/python/tests/conftest.py +++ b/deps/AMICI/python/tests/conftest.py @@ -1,8 +1,8 @@ """pytest configuration file""" + import copy import importlib import os -import shutil import sys import amici diff --git a/deps/AMICI/python/tests/petab_/test_petab_problem.py b/deps/AMICI/python/tests/petab_/test_petab_problem.py new file mode 100644 index 000000000..736d895f8 --- /dev/null +++ b/deps/AMICI/python/tests/petab_/test_petab_problem.py @@ -0,0 +1,87 @@ +from amici.petab.petab_problem import PetabProblem +from benchmark_models_petab import get_problem +from amici.testing import skip_on_valgrind + + +@skip_on_valgrind +def test_amici_petab_problem_pregenerate(): + """PetabProblem with pre-generated ExpDatas""" + # any example is fine - the only assumption is that we don't have + # preequilibration + petab_problem = get_problem("Boehm_JProteomeRes2014") + app = PetabProblem(petab_problem, store_edatas=True) + + # ensure all edatas are generated upon construction + assert len(app._edatas) == len( + petab_problem.get_simulation_conditions_from_measurement_df() + ) + + # ensure the cached edatas are returned + for i, (_, condition) in enumerate( + petab_problem.get_simulation_conditions_from_measurement_df().iterrows() + ): + assert app.get_edata(condition.simulationConditionId) is app._edatas[i] + + # ensure parameter are updated + edatas = app.get_edatas() + app.set_parameters( + {app.model.getParameterIds()[0]: 0.12345}, scaled_parameters=True + ) + for edata in edatas: + assert edata.parameters[0] == 0.12345 + + +@skip_on_valgrind +def test_amici_petab_problem_on_demand(): + """PetabProblem with on-demand ExpDatas""" + # any example is fine - the only assumption is that we don't have + # preequilibration + petab_problem = get_problem("Boehm_JProteomeRes2014") + app = PetabProblem(petab_problem, store_edatas=False) + + # ensure no edatas are generated upon construction + assert not app._edatas + + edatas = app.get_edatas() + assert len(edatas) == len( + petab_problem.get_simulation_conditions_from_measurement_df() + ) + + # ensure parameter are updated + app.set_parameters( + {app.model.getParameterIds()[0]: 0.12345}, scaled_parameters=True + ) + # previously generated ExpDatas are not updated + for edata in edatas: + assert edata.parameters[0] != 0.12345 + # but newly generated ExpDatas are + for edata in app.get_edatas(): + assert edata.parameters[0] == 0.12345 + + some_sim_condition = ( + petab_problem.measurement_df.simulationConditionId.iloc[0] + ) + # different objects for subsequent calls + assert app.get_edata(some_sim_condition) is not app.get_edata( + some_sim_condition + ) + + +@skip_on_valgrind +def test_amici_petab_problem_pregenerate_equals_on_demand(): + """Check that PetabProblem produces the same ExpDatas + independent of the `store_edatas` parameter.""" + # any example is fine + petab_problem = get_problem("Boehm_JProteomeRes2014") + app_store_true = PetabProblem(petab_problem, store_edatas=True) + app_store_false = PetabProblem(petab_problem, store_edatas=False) + + parameter_update = {app_store_true.model.getParameterIds()[0]: 0.12345} + app_store_true.set_parameters(parameter_update, scaled_parameters=True) + 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(), strict=True + ): + assert edata_store_true is not edata_store_false + assert edata_store_true == edata_store_false diff --git a/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/model/writer.py b/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/model/writer.py index 76b98c3de..c6abebaeb 100644 --- a/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/model/writer.py +++ b/deps/AMICI/python/tests/petab_test_problems/lotka_volterra/model/writer.py @@ -1,6 +1,5 @@ from pathlib import Path -import petab import yaml2sbml yaml2sbml_yaml = "lotka_volterra.yaml" diff --git a/deps/AMICI/python/tests/pysb_test_models/bngwiki_egfr_simple_deletemolecules.py b/deps/AMICI/python/tests/pysb_test_models/bngwiki_egfr_simple_deletemolecules.py index 767c239c5..4c40f7e81 100644 --- a/deps/AMICI/python/tests/pysb_test_models/bngwiki_egfr_simple_deletemolecules.py +++ b/deps/AMICI/python/tests/pysb_test_models/bngwiki_egfr_simple_deletemolecules.py @@ -3,8 +3,6 @@ http://bionetgen.org/index.php/Egfr_simple """ -from __future__ import print_function - from pysb import * Model() diff --git a/deps/AMICI/python/tests/splines_utils.py b/deps/AMICI/python/tests/splines_utils.py index 0746207dd..8aac6edfe 100644 --- a/deps/AMICI/python/tests/splines_utils.py +++ b/deps/AMICI/python/tests/splines_utils.py @@ -6,18 +6,20 @@ import math import os +import platform import uuid from tempfile import mkdtemp -from typing import Any, Dict, List, Optional, Sequence, Union +from typing import Any +from collections.abc import Sequence import amici import numpy as np import pandas as pd -import petab +import petab.v1 as petab import sympy as sp from amici.gradient_check import _check_results -from amici.petab_import import import_petab_problem -from amici.petab_objective import EDATAS, LLH, RDATAS, SLLH, simulate_petab +from amici.petab.petab_import import import_petab_problem +from amici.petab.simulations import EDATAS, LLH, RDATAS, SLLH, simulate_petab from amici.sbml_utils import ( add_compartment, add_inflow, @@ -29,7 +31,7 @@ ) from amici.splines import AbstractSpline, CubicHermiteSpline, UniformGrid from amici.testing import TemporaryDirectoryWinSafe as TemporaryDirectory -from petab.models.sbml_model import SbmlModel +from petab.v1.models.sbml_model import SbmlModel def evaluate_spline( @@ -44,7 +46,7 @@ def evaluate_spline( def integrate_spline( spline: AbstractSpline, - params: Union[Dict, None], + params: dict | None, tt: Sequence[float], initial_value: float = 0, ): @@ -116,14 +118,14 @@ def species_to_index(name) -> int: def create_petab_problem( - splines: List[AbstractSpline], - params_true: Dict, - initial_values: Optional[np.ndarray] = None, + splines: list[AbstractSpline], + params_true: dict, + 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", ): """ @@ -215,7 +217,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, ) @@ -282,9 +284,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, @@ -479,7 +481,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 = { @@ -504,8 +506,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, @@ -514,7 +516,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, ): """ @@ -603,7 +605,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) @@ -715,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 @@ -770,8 +766,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, ): @@ -895,7 +891,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)): @@ -916,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 = ( @@ -938,7 +934,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, @@ -959,7 +955,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/deps/AMICI/python/tests/test_antimony_import.py b/deps/AMICI/python/tests/test_antimony_import.py index 41af014aa..44f8296c2 100644 --- a/deps/AMICI/python/tests/test_antimony_import.py +++ b/deps/AMICI/python/tests/test_antimony_import.py @@ -2,8 +2,10 @@ import numpy as np from amici.antimony_import import antimony2amici from amici.testing import TemporaryDirectoryWinSafe as TemporaryDirectory +from amici.testing import skip_on_valgrind +@skip_on_valgrind def test_antimony_example(): """If this example requires changes, please also update documentation/python_interface.rst.""" ant_model = """ diff --git a/deps/AMICI/python/tests/test_compare_conservation_laws_sbml.py b/deps/AMICI/python/tests/test_compare_conservation_laws_sbml.py index 4d6a453b5..640d2dd98 100644 --- a/deps/AMICI/python/tests/test_compare_conservation_laws_sbml.py +++ b/deps/AMICI/python/tests/test_compare_conservation_laws_sbml.py @@ -5,6 +5,7 @@ import numpy as np import pytest from numpy.testing import assert_allclose, assert_array_equal +from amici.testing import skip_on_valgrind @pytest.fixture @@ -123,6 +124,7 @@ def get_results( return amici.runAmiciSimulation(model, solver, edata) +@skip_on_valgrind def test_compare_conservation_laws_sbml(models, edata_fixture): # first, create the model model_with_cl, model_without_cl = models @@ -288,6 +290,7 @@ def test_adjoint_pre_and_post_equilibration(models, edata_fixture): assert_allclose(raa_cl["sllh"], raa["sllh"], 1e-5, 1e-5) +@skip_on_valgrind def test_get_set_model_settings(models): """test amici.(get|set)_model_settings cycles for models with and without conservation laws""" diff --git a/deps/AMICI/python/tests/test_conserved_quantities_demartino.py b/deps/AMICI/python/tests/test_conserved_quantities_demartino.py index 339743cc4..94d51a606 100644 --- a/deps/AMICI/python/tests/test_conserved_quantities_demartino.py +++ b/deps/AMICI/python/tests/test_conserved_quantities_demartino.py @@ -1,4 +1,5 @@ """Tests for conservation laws / conserved moieties""" + import os from time import perf_counter @@ -155,15 +156,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 +175,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 @@ -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/deps/AMICI/python/tests/test_conserved_quantities_rref.py b/deps/AMICI/python/tests/test_conserved_quantities_rref.py index ada4b4672..7a131ebee 100644 --- a/deps/AMICI/python/tests/test_conserved_quantities_rref.py +++ b/deps/AMICI/python/tests/test_conserved_quantities_rref.py @@ -1,5 +1,3 @@ -import os - import numpy as np import pytest import sympy as sp diff --git a/deps/AMICI/python/tests/test_cxxcodeprinter.py b/deps/AMICI/python/tests/test_cxxcodeprinter.py index 384b8ad9a..3f92a5495 100644 --- a/deps/AMICI/python/tests/test_cxxcodeprinter.py +++ b/deps/AMICI/python/tests/test_cxxcodeprinter.py @@ -1,8 +1,10 @@ import sympy as sp from amici.cxxcodeprinter import AmiciCxxCodePrinter from sympy.codegen.rewriting import optims_c99 +from amici.testing import skip_on_valgrind +@skip_on_valgrind def test_optimizations(): """Check that AmiciCxxCodePrinter handles optimizations correctly.""" try: diff --git a/deps/AMICI/python/tests/test_de_model.py b/deps/AMICI/python/tests/test_de_model.py new file mode 100644 index 000000000..07c8328ed --- /dev/null +++ b/deps/AMICI/python/tests/test_de_model.py @@ -0,0 +1,37 @@ +import sympy as sp +from amici.de_model_components import Event +from amici.import_utils import amici_time_symbol +from amici.testing import skip_on_valgrind + + +@skip_on_valgrind +def test_event_trigger_time(): + e = Event( + sp.Symbol("event1"), "event name", amici_time_symbol - 10, sp.Float(0) + ) + assert e.triggers_at_fixed_timepoint() is True + assert e.get_trigger_time() == 10 + + # fixed, but multiple timepoints - not (yet) supported + e = Event( + sp.Symbol("event1"), + "event name", + sp.sin(amici_time_symbol), + sp.Float(0), + ) + assert e.triggers_at_fixed_timepoint() is False + + e = Event( + sp.Symbol("event1"), "event name", amici_time_symbol / 2, sp.Float(0) + ) + assert e.triggers_at_fixed_timepoint() is True + assert e.get_trigger_time() == 0 + + # parameter-dependent triggers - not (yet) supported + e = Event( + sp.Symbol("event1"), + "event name", + amici_time_symbol - sp.Symbol("delay"), + sp.Float(0), + ) + assert e.triggers_at_fixed_timepoint() is False diff --git a/deps/AMICI/python/tests/test_edata.py b/deps/AMICI/python/tests/test_edata.py index 9c4d9b9ed..fab49c160 100644 --- a/deps/AMICI/python/tests/test_edata.py +++ b/deps/AMICI/python/tests/test_edata.py @@ -1,12 +1,13 @@ """Tests related to amici.ExpData via Python""" + import amici import numpy as np from amici.testing import skip_on_valgrind -from test_sbml_import import model_units_module +from test_sbml_import import model_units_module # noqa: F401 @skip_on_valgrind -def test_edata_sensi_unscaling(model_units_module): +def test_edata_sensi_unscaling(model_units_module): # noqa: F811 """ ExpData parameters should be used for unscaling initial state sensitivities. diff --git a/deps/AMICI/python/tests/test_events.py b/deps/AMICI/python/tests/test_events.py index d2a177bde..d16877fd2 100644 --- a/deps/AMICI/python/tests/test_events.py +++ b/deps/AMICI/python/tests/test_events.py @@ -1,8 +1,13 @@ """Tests for SBML events, including piecewise expressions.""" + from copy import deepcopy +import amici import numpy as np import pytest +from amici.antimony_import import antimony2amici +from amici.gradient_check import check_derivatives +from amici.testing import TemporaryDirectoryWinSafe as TemporaryDirectory from amici.testing import skip_on_valgrind from util import ( check_trajectories_with_forward_sensitivities, @@ -10,6 +15,7 @@ create_amici_model, create_sbml_model, ) +from numpy.testing import assert_allclose @pytest.fixture( @@ -704,3 +710,122 @@ def expm(x): from mpmath import expm return np.array(expm(x).tolist()).astype(float) + + +def test_handling_of_fixed_time_point_event_triggers(): + """Test handling of events without solver-tracked root functions.""" + ant_model = """ + model test_events_time_based + event_target = 0 + bolus = 1 + at (time > 1): event_target = 1 + at (time > 2): event_target = event_target + bolus + at (time > 3): event_target = 3 + end + """ + module_name = "test_events_time_based" + 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 == 3 + assert amici_model.ne_solver == 0 + amici_model.setTimepoints(np.linspace(0, 4, 200)) + amici_solver = amici_model.getSolver() + rdata = amici.runAmiciSimulation(amici_model, amici_solver) + assert rdata.status == amici.AMICI_SUCCESS + assert (rdata.x[rdata.ts < 1] == 0).all() + assert (rdata.x[(rdata.ts >= 1) & (rdata.ts < 2)] == 1).all() + assert (rdata.x[(rdata.ts >= 2) & (rdata.ts < 3)] == 2).all() + 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, + ) diff --git a/deps/AMICI/python/tests/test_hdf5.py b/deps/AMICI/python/tests/test_hdf5.py index 232f22be8..ee9d5a3e3 100644 --- a/deps/AMICI/python/tests/test_hdf5.py +++ b/deps/AMICI/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/deps/AMICI/python/tests/test_heavisides.py b/deps/AMICI/python/tests/test_heavisides.py index c3bea26a0..26de4f575 100644 --- a/deps/AMICI/python/tests/test_heavisides.py +++ b/deps/AMICI/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/deps/AMICI/python/tests/test_misc.py b/deps/AMICI/python/tests/test_misc.py index 5a88fda6f..80e1afaa0 100644 --- a/deps/AMICI/python/tests/test_misc.py +++ b/deps/AMICI/python/tests/test_misc.py @@ -7,11 +7,7 @@ import amici import pytest import sympy as sp -from amici.de_export import ( - _custom_pow_eval_derivative, - _monkeypatched, - smart_subs_dict, -) +from amici.import_utils import smart_subs_dict from amici.testing import skip_on_valgrind @@ -69,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: @@ -78,8 +75,7 @@ def test_cmake_compilation(sbml_example_presimulation_module): cmd, shell=True, check=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, + capture_output=True, ) except subprocess.CalledProcessError as e: print(e.stdout.decode()) @@ -109,25 +105,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/deps/AMICI/python/tests/test_ode_export.py b/deps/AMICI/python/tests/test_ode_export.py index b30d451a4..6dc4d85f0 100644 --- a/deps/AMICI/python/tests/test_ode_export.py +++ b/deps/AMICI/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")], @@ -93,8 +90,9 @@ def test_csc_matrix_vector(): assert str(sparse_matrix) == "Matrix([[0], [da2_db_1]])" +@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) diff --git a/deps/AMICI/python/tests/test_pandas.py b/deps/AMICI/python/tests/test_pandas.py index 21c58bcaf..40799120a 100644 --- a/deps/AMICI/python/tests/test_pandas.py +++ b/deps/AMICI/python/tests/test_pandas.py @@ -5,6 +5,8 @@ import amici import numpy as np import pytest +from amici.testing import skip_on_valgrind + # test parameters for test_pandas_import_export combos = itertools.product([(10, 5), (5, 10), ()], repeat=3) @@ -18,6 +20,7 @@ ] +@skip_on_valgrind @pytest.mark.parametrize("case", cases) def test_pandas_import_export(sbml_example_presimulation_module, case): """TestCase class for testing csv import using pandas""" diff --git a/deps/AMICI/python/tests/test_parameter_mapping.py b/deps/AMICI/python/tests/test_parameter_mapping.py index ae66e23f5..0138d8093 100644 --- a/deps/AMICI/python/tests/test_parameter_mapping.py +++ b/deps/AMICI/python/tests/test_parameter_mapping.py @@ -1,8 +1,6 @@ """Test for ``amici.parameter_mapping``""" -import os -import pytest -from amici.parameter_mapping import ( +from amici.petab.parameter_mapping import ( ParameterMapping, ParameterMappingForCondition, ) diff --git a/deps/AMICI/python/tests/test_petab_import.py b/deps/AMICI/python/tests/test_petab_import.py index fc978b76e..7979e6fbf 100644 --- a/deps/AMICI/python/tests/test_petab_import.py +++ b/deps/AMICI/python/tests/test_petab_import.py @@ -5,8 +5,7 @@ import pytest from amici.testing import TemporaryDirectoryWinSafe, skip_on_valgrind -petab = pytest.importorskip("petab", reason="Missing petab") -amici_petab_import = pytest.importorskip("amici.petab_import") +petab = pytest.importorskip("petab.v1", reason="Missing petab") @pytest.fixture @@ -37,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) @@ -46,10 +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 petab.models.sbml_model import SbmlModel + from amici.petab.sbml_import import ( + _get_fixed_parameters_sbml as get_fixed_parameters, + ) + from petab.v1.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( { @@ -71,22 +100,24 @@ def test_get_fixed_parameters(simple_sbml_model): parameter_df=parameter_df, condition_df=condition_df, ) - assert set(amici_petab_import.get_fixed_parameters(petab_problem)) == { + assert set(get_fixed_parameters(petab_problem)) == { "p1", "p3", "p5", + "p6", } assert set( - amici_petab_import.get_fixed_parameters( + get_fixed_parameters( petab_problem, non_estimated_parameters_as_constants=False ) - ) == {"p1", "p5"} + ) == {"p1", "p5", "p6"} @skip_on_valgrind def test_default_output_parameters(simple_sbml_model): - from petab.models.sbml_model import SbmlModel + from amici.petab.petab_import import import_model + from petab.v1.models.sbml_model import SbmlModel sbml_doc, sbml_model = simple_sbml_model condition_df = petab.get_condition_df( @@ -116,7 +147,7 @@ def test_default_output_parameters(simple_sbml_model): ) with TemporaryDirectoryWinSafe() as outdir: - sbml_importer = amici_petab_import.import_model( + sbml_importer = import_model( petab_problem=petab_problem, output_parameter_defaults={"observableParameter1_obs1": 1.0}, compile=False, @@ -130,7 +161,7 @@ def test_default_output_parameters(simple_sbml_model): ) with pytest.raises(ValueError): - amici_petab_import.import_model( + import_model( petab_problem=petab_problem, output_parameter_defaults={"nonExistentParameter": 1.0}, compile=False, diff --git a/deps/AMICI/python/tests/test_petab_objective.py b/deps/AMICI/python/tests/test_petab_objective.py index e31e693d1..260036a26 100755 --- a/deps/AMICI/python/tests/test_petab_objective.py +++ b/deps/AMICI/python/tests/test_petab_objective.py @@ -4,13 +4,13 @@ from pathlib import Path import amici -import amici.petab_import -import amici.petab_objective import numpy as np import pandas as pd -import petab +import petab.v1 as petab import pytest -from amici.petab_objective import SLLH +from amici.petab.petab_import import import_petab_problem +from amici.petab.simulations import SLLH, simulate_petab +from amici.testing import skip_on_valgrind # Absolute and relative tolerances for finite difference gradient checks. ATOL: float = 1e-3 @@ -30,19 +30,17 @@ def lotka_volterra() -> petab.Problem: ) +@skip_on_valgrind def test_simulate_petab_sensitivities(lotka_volterra): petab_problem = lotka_volterra - amici_model = amici.petab_import.import_petab_problem(petab_problem) + amici_model = import_petab_problem(petab_problem) amici_solver = amici_model.getSolver() amici_solver.setSensitivityOrder(amici.SensitivityOrder_first) 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 = {} @@ -54,7 +52,7 @@ def test_simulate_petab_sensitivities(lotka_volterra): problem_parameters ) results[(scaled_parameters, scaled_gradients)] = pd.Series( - amici.petab_objective.simulate_petab( + simulate_petab( petab_problem=petab_problem, amici_model=amici_model, solver=amici_solver, diff --git a/deps/AMICI/python/tests/test_petab_simulate.py b/deps/AMICI/python/tests/test_petab_simulate.py index febea5fd5..f0310eaca 100644 --- a/deps/AMICI/python/tests/test_petab_simulate.py +++ b/deps/AMICI/python/tests/test_petab_simulate.py @@ -1,11 +1,12 @@ """Tests for petab_simulate.py.""" + import tempfile from pathlib import Path -import petab +import petab.v1 as petab import petabtests import pytest -from amici.petab_simulate import PetabSimulator +from amici.petab.simulator import PetabSimulator from amici.testing import skip_on_valgrind diff --git a/deps/AMICI/python/tests/test_preequilibration.py b/deps/AMICI/python/tests/test_preequilibration.py index a42bc6354..bb657b415 100644 --- a/deps/AMICI/python/tests/test_preequilibration.py +++ b/deps/AMICI/python/tests/test_preequilibration.py @@ -5,8 +5,13 @@ import amici import numpy as np import pytest -from numpy.testing import assert_allclose +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 @@ -27,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 = () @@ -607,8 +612,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)) @@ -631,5 +634,90 @@ 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): + ( + model, + solver, + edata, + edata_preeq, + edata_presim, + edata_sim, + pscales, + plists, + ) = preeq_fixture + model.setSteadyStateSensitivityMode( + amici.SteadyStateSensitivityMode.integrationOnly + ) + model_preeq = get_model_for_preeq(model, edata) + # the exactly same settings are used, so results should match exactly + rdata1 = amici.runAmiciSimulation(model_preeq, solver) + rdata2 = amici.runAmiciSimulation(model, solver, edata_preeq) + assert_equal( + rdata1.x, + rdata2.x, + ) + assert_equal( + 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/deps/AMICI/python/tests/test_pysb.py b/deps/AMICI/python/tests/test_pysb.py index 52ca3a320..2911b05fc 100644 --- a/deps/AMICI/python/tests/test_pysb.py +++ b/deps/AMICI/python/tests/test_pysb.py @@ -1,4 +1,5 @@ """PYSB model tests""" +# flake8: noqa: F821 import importlib import logging diff --git a/deps/AMICI/python/tests/test_rdata.py b/deps/AMICI/python/tests/test_rdata.py index ac7659f36..80ec3b80e 100644 --- a/deps/AMICI/python/tests/test_rdata.py +++ b/deps/AMICI/python/tests/test_rdata.py @@ -1,9 +1,11 @@ """Test amici.ReturnData(View)-related functionality""" + import amici import numpy as np import pytest from amici.numpy import evaluate from numpy.testing import assert_almost_equal, assert_array_equal +from amici.testing import skip_on_valgrind @pytest.fixture(scope="session") @@ -19,6 +21,7 @@ def rdata_by_id_fixture(sbml_example_presimulation_module): return model, rdata +@skip_on_valgrind def test_rdata_by_id(rdata_by_id_fixture): model, rdata = rdata_by_id_fixture @@ -42,6 +45,7 @@ def test_rdata_by_id(rdata_by_id_fixture): ) +@skip_on_valgrind def test_evaluate(rdata_by_id_fixture): # get IDs of model components model, rdata = rdata_by_id_fixture diff --git a/deps/AMICI/python/tests/test_sbml_import.py b/deps/AMICI/python/tests/test_sbml_import.py index 7c4a67c0a..4936a3c90 100644 --- a/deps/AMICI/python/tests/test_sbml_import.py +++ b/deps/AMICI/python/tests/test_sbml_import.py @@ -1,9 +1,9 @@ """Tests related to amici.sbml_import""" + import os import re from numbers import Number from pathlib import Path -from urllib.request import urlopen import amici import libsbml @@ -369,7 +369,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) @@ -543,13 +543,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: @@ -654,6 +654,7 @@ def _test_set_parameters_by_dict(model_module): assert model.getParameters() == old_parameter_values +@skip_on_valgrind @pytest.mark.parametrize("extract_cse", [True, False]) def test_code_gen_uses_cse(extract_cse): """Check that code generation honors AMICI_EXTRACT_CSE""" @@ -675,6 +676,7 @@ def test_code_gen_uses_cse(extract_cse): os.environ = old_environ +@skip_on_valgrind def test_code_gen_uses_lhs_symbol_ids(): """Check that code generation uses symbol IDs instead of plain array indices""" @@ -691,6 +693,7 @@ def test_code_gen_uses_lhs_symbol_ids(): assert "dobservable_x1_dx1 = " in dwdx +@skip_on_valgrind def test_hardcode_parameters(simple_sbml_model): """Test model generation works for model without observables""" sbml_doc, sbml_model = simple_sbml_model @@ -720,3 +723,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-13) + 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/deps/AMICI/python/tests/test_sbml_import_special_functions.py b/deps/AMICI/python/tests/test_sbml_import_special_functions.py index 9d8f44751..3f8383ce9 100644 --- a/deps/AMICI/python/tests/test_sbml_import_special_functions.py +++ b/deps/AMICI/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 + ) diff --git a/deps/AMICI/python/tests/test_splines.py b/deps/AMICI/python/tests/test_splines.py index a7fe01e84..66104b824 100644 --- a/deps/AMICI/python/tests/test_splines.py +++ b/deps/AMICI/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/deps/AMICI/python/tests/test_splines_python.py b/deps/AMICI/python/tests/test_splines_python.py index 4c4de5ccf..539fb1dd4 100644 --- a/deps/AMICI/python/tests/test_splines_python.py +++ b/deps/AMICI/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/deps/AMICI/python/tests/test_splines_short.py b/deps/AMICI/python/tests/test_splines_short.py index 59e54a327..43b0ccd29 100644 --- a/deps/AMICI/python/tests/test_splines_short.py +++ b/deps/AMICI/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/deps/AMICI/python/tests/test_swig_interface.py b/deps/AMICI/python/tests/test_swig_interface.py index a746552b5..f61b28ea4 100644 --- a/deps/AMICI/python/tests/test_swig_interface.py +++ b/deps/AMICI/python/tests/test_swig_interface.py @@ -5,6 +5,8 @@ import copy import numbers +from math import nan +import pytest import amici import numpy as np @@ -37,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 @@ -66,10 +68,7 @@ def test_copy_constructors(pysb_example_presimulation_module): model_instance_settings0 = { # setting name: [default value, custom value] "AddSigmaResiduals": [False, True], - "AlwaysCheckFinite": [ - False, - True, - ], + "AlwaysCheckFinite": [False, True], # Skipped due to model dependency in `'InitialStates'`. "FixedParameters": None, "InitialStates": [ @@ -80,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, @@ -130,6 +133,13 @@ def test_model_instance_settings(pysb_example_presimulation_module): i_getter = 0 i_setter = 1 + # the default setting for AlwaysCheckFinite depends on whether the amici + # extension has been built in debug mode + model_instance_settings0["AlwaysCheckFinite"] = [ + model0.getAlwaysCheckFinite(), + not model0.getAlwaysCheckFinite(), + ] + # All settings are tested. assert set(model_instance_settings0) == set( amici.swig_wrappers.model_instance_settings @@ -315,6 +325,7 @@ def test_unhandled_settings(pysb_example_presimulation_module): "setParametersByIdRegex", "setParametersByNameRegex", "setInitialStateSensitivities", + "get_trigger_timepoints", ] from amici.swig_wrappers import model_instance_settings @@ -354,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") @@ -420,8 +433,6 @@ def test_solver_repr(): for s in (solver, solver_ptr): assert "maxsteps" in str(s) assert "maxsteps" in repr(s) - # avoid double delete!! - solver_ptr.release() def test_edata_repr(): @@ -441,8 +452,6 @@ def test_edata_repr(): for expected_str in expected_strs: assert expected_str in str(e) assert expected_str in repr(e) - # avoid double delete!! - edata_ptr.release() def test_edata_equality_operator(): @@ -470,3 +479,110 @@ def test_expdata_and_expdataview_are_deepcopyable(): ev2 = copy.deepcopy(ev1) assert ev2._swigptr.this != ev1._swigptr.this assert ev1 == ev2 + + +def test_solvers_are_deepcopyable(): + for solver_type in (amici.CVodeSolver, amici.IDASolver): + for solver1 in (solver_type(), amici.SolverPtr(solver_type())): + solver2 = copy.deepcopy(solver1) + assert solver1.this != solver2.this + assert ( + solver1.getRelativeTolerance() + == solver2.getRelativeTolerance() + ) + solver2.setRelativeTolerance(100 * solver2.getRelativeTolerance()) + assert ( + solver1.getRelativeTolerance() + != solver2.getRelativeTolerance() + ) + + +def test_model_is_deepcopyable(pysb_example_presimulation_module): + model_module = pysb_example_presimulation_module + for model1 in ( + model_module.getModel(), + amici.ModelPtr(model_module.getModel()), + ): + model2 = copy.deepcopy(model1) + assert model1.this != model2.this + assert model1.t0() == model2.t0() + model2.setT0(100 + model2.t0()) + assert model1.t0() != model2.t0() + + +def test_rdataview(sbml_example_presimulation_module): + """Test some SwigPtrView functionality via ReturnDataView.""" + model_module = sbml_example_presimulation_module + model = model_module.getModel() + 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): + _ = rdata.nonexisting_attribute + + with pytest.raises(KeyError): + _ = rdata["nonexisting_attribute"] + + assert not hasattr(rdata, "nonexisting_attribute") + assert "x" in rdata + assert rdata.x == rdata["x"] + + # field names are included by dir() + assert "x" in dir(rdata) + + +def test_python_exceptions(sbml_example_presimulation_module): + """Test that C++ exceptions are correctly caught and re-raised in Python.""" + + # amici-base extension throws and its swig-wrapper catches + solver = amici.CVodeSolver() + with pytest.raises( + RuntimeError, match="maxsteps must be a positive number" + ): + solver.setMaxSteps(-1) + + # model extension throws and its swig-wrapper catches + model = sbml_example_presimulation_module.get_model() + with pytest.raises(RuntimeError, match="Steadystate mask has wrong size"): + model.set_steadystate_mask([1] * model.nx_solver * 2) + + # amici-base extension throws and its swig-wrapper catches + edata = amici.ExpData(1, 1, 1, [1]) + # too short sx0 + edata.sx0 = (1, 2) + with pytest.raises( + RuntimeError, + match=r"Number of initial conditions sensitivities \(36\) " + r"in model does not match ExpData \(2\).", + ): + amici.runAmiciSimulation(model, solver, edata) + + amici.runAmiciSimulations( + model, solver, [edata, edata], failfast=True, num_threads=1 + ) + + # model throws, base catches, swig-exception handling is not involved + model.setParameters([nan] * model.np()) + model.setTimepoints([1]) + rdata = amici.runAmiciSimulation(model, solver) + assert rdata.status == amici.AMICI_FIRST_RHSFUNC_ERR + + edata = amici.ExpData(1, 1, 1, [1]) + rdatas = amici.runAmiciSimulations( + model, solver, [edata, edata], failfast=True, num_threads=1 + ) + assert rdatas[0].status == amici.AMICI_FIRST_RHSFUNC_ERR + + # model throws, base catches, swig-exception handling is involved + from amici._amici import runAmiciSimulation + + with pytest.raises( + RuntimeError, match="AMICI failed to integrate the forward problem" + ): + # rethrow=True + runAmiciSimulation(solver, None, model.get(), True) diff --git a/deps/AMICI/python/tests/test_sympy_utils.py b/deps/AMICI/python/tests/test_sympy_utils.py new file mode 100644 index 000000000..da8974135 --- /dev/null +++ b/deps/AMICI/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 diff --git a/deps/AMICI/python/tests/util.py b/deps/AMICI/python/tests/util.py index dde10eb45..0f368a4f6 100644 --- a/deps/AMICI/python/tests/util.py +++ b/deps/AMICI/python/tests/util.py @@ -1,4 +1,5 @@ """Tests for SBML events, including piecewise expressions.""" + import sys import tempfile from pathlib import Path @@ -95,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) @@ -108,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) diff --git a/deps/AMICI/python/tests/valgrind-python.supp b/deps/AMICI/python/tests/valgrind-python.supp index 26a9f0e7d..16c92e3d1 100644 --- a/deps/AMICI/python/tests/valgrind-python.supp +++ b/deps/AMICI/python/tests/valgrind-python.supp @@ -76,17 +76,28 @@ Memcheck:Leak fun:malloc ... - fun:__pyx_pw_5numpy_6random_13bit_generator_12BitGenerator_1__init__ + fun:__pyx_pw_5numpy_* } { numpy Memcheck:Leak - match-leak-kinds: definite + match-leak-kinds: possible + fun:malloc + fun:PyUFunc_FromFuncAndDataAndSignatureAndIdentity + fun:initumath + fun:PyInit__multiarray_umath + ... +} + +{ + numpy + Memcheck:Leak + match-leak-kinds: possible fun:malloc - obj:/usr/bin/python3.? + fun:default_malloc + fun:PyDataMem_UserNEW ... - fun:gentype_generic_method } # @@ -178,8 +189,8 @@ { other Memcheck:Cond - obj:/usr/bin/python3.? - obj:/usr/bin/python3.? + obj:/usr/bin/python3.* + obj:/usr/bin/python3.* fun:_PyEval_EvalFrameDefault fun:_PyEval_EvalCodeWithName fun:_PyFunction_Vectorcall @@ -187,21 +198,34 @@ fun:_PyEval_EvalCodeWithName fun:_PyFunction_Vectorcall fun:_PyEval_EvalFrameDefault - obj:/usr/bin/python3.? + obj:/usr/bin/python3.* fun:_PyEval_EvalFrameDefault - obj:/usr/bin/python3.? + obj:/usr/bin/python3.* } { other Memcheck:Value8 - obj:/usr/bin/python3.? + obj:/usr/bin/python3.* ... - obj:/usr/bin/python3.? + obj:/usr/bin/python3.* fun:__Pyx_PyObject_Call 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 @@ -308,7 +332,7 @@ ... fun:PyBytes_Repr fun:PyObject_Str - obj:/usr/bin/python3.? + obj:/usr/bin/python3.* ... fun:PyObject_Format ... @@ -382,9 +406,9 @@ { other Memcheck:Cond - obj:/usr/bin/python3.? + obj:/usr/bin/python3.* ... - obj:/usr/bin/python3.? + obj:/usr/bin/python3.* fun:__Pyx_PyObject_Call fun:__Pyx__PyObject_CallOneArg ... @@ -410,9 +434,9 @@ other Memcheck:Cond ... - obj:/usr/bin/python3.? + obj:/usr/bin/python3.* fun:_PyObject_CallMethodIdObjArgs - obj:/usr/bin/python3.? + obj:/usr/bin/python3.* ... } @@ -437,9 +461,9 @@ other Memcheck:Value8 ... - obj:/usr/bin/python3.? + obj:/usr/bin/python3.* fun:_PyObject_CallMethodIdObjArgs - obj:/usr/bin/python3.? + obj:/usr/bin/python3.* ... } @@ -455,25 +479,25 @@ other Memcheck:Value8 ... - obj:/usr/bin/python3.? + obj:/usr/bin/python3.* fun:PyDict_SetItem - obj:/usr/bin/python3.? + obj:/usr/bin/python3.* ... } { other Memcheck:Cond - obj:/usr/bin/python3.? + obj:/usr/bin/python3.* ... - obj:/usr/bin/python3.? + obj:/usr/bin/python3.* } { other Memcheck:Cond fun:realloc - obj:/usr/bin/python3.? + obj:/usr/bin/python3.* ... fun:_PyFunction_Vectorcall } @@ -481,54 +505,40 @@ { other Memcheck:Value8 - obj:/usr/bin/python3.? + obj:/usr/bin/python3.* ... - obj:/usr/bin/python3.? -} - -{ - other - Memcheck:Leak - match-leak-kinds: definite - fun:malloc - obj:/usr/bin/python3.? - fun:_PyObject_MakeTpCall + obj:/usr/bin/python3.* } { other - Memcheck:Leak - match-leak-kinds: definite - fun:malloc - obj:/usr/bin/python3.? - obj:/usr/bin/python3.? + Memcheck:Value8 + obj:/usr/bin/python3.* fun:_PyObject_MakeTpCall + fun:_PyEval_EvalFrameDefault + obj:/usr/bin/python3.11 + obj:/usr/bin/python3.11 + obj:/usr/bin/python3.11 + fun:PyIter_Next + obj:/usr/bin/python3.11 + fun:PyBytes_FromObject + obj:/usr/bin/python3.11 + obj:/usr/bin/python3.11 + fun:PyObject_Vectorcall } { other Memcheck:Leak - match-leak-kinds: definite - fun:malloc - obj:/usr/bin/python3.? - ... - fun:PyTuple_New + fun:realloc ... + fun:_dl_catch_exception } { other Memcheck:Leak - match-leak-kinds: definite fun:malloc - obj:/usr/bin/python3.? - fun:PyList_AsTuple -} - -{ - other - Memcheck:Leak - fun:realloc ... fun:_dl_catch_exception } @@ -536,7 +546,7 @@ { other Memcheck:Leak - fun:malloc + fun:calloc ... fun:_dl_catch_exception } @@ -544,7 +554,6 @@ { Pandas Memcheck:Leak - match-leak-kinds: definite fun:malloc ... obj:*site-packages/pandas/_libs/*.cpython-3*-x86_64-linux-gnu.so @@ -552,36 +561,20 @@ } { - Scipy extensions + Pandas 2.2.0 Memcheck:Leak match-leak-kinds: definite - fun:malloc - ... - obj:*/site-packages/scipy/*.cpython-3*-x86_64-linux-gnu.so - ... -} - - -{ - PyTuple_Pack - Memcheck:Leak - match-leak-kinds: definite - fun:malloc - obj:/usr/bin/python3.* - fun:PyTuple_Pack - obj:/usr/bin/python3.* - ... + fun:realloc + obj:*site-packages/pandas/* } { - PyAST_CompileObject + Scipy extensions Memcheck:Leak match-leak-kinds: definite fun:malloc - obj:/usr/bin/python3.* ... - fun:PyAST_CompileObject - obj:/usr/bin/python3.* + obj:*/site-packages/scipy/*.cpython-3*-x86_64-linux-gnu.so ... } @@ -756,6 +749,16 @@ fun:_PyImport_FindSharedFuncptr } +{ + _dl_catch_exception + Memcheck:Addr8 + fun:strncmp + fun:is_dst + ... + fun:_dl_catch_exception + ... +} + { Python dictkeys_get_index Memcheck:Value8 @@ -775,3 +778,210 @@ fun:os_stat ... } + +{ + Python PyLong_FromUnicodeObject + Memcheck:Cond + fun:PyLong_FromString + fun:PyLong_FromUnicodeObject +} + +{ + Python PyLong_FromUnicodeObject + Memcheck:Value8 + fun:PyLong_FromString + fun:PyLong_FromUnicodeObject +} + +{ + Python + Memcheck:Leak + match-leak-kinds: possible + fun:realloc + obj:/usr/bin/python3.* + fun:_PyEval_EvalFrameDefault + fun:_PyFunction_Vectorcall +} + +{ + Python + Memcheck:Leak + match-leak-kinds: possible + fun:calloc + fun:PyList_New + fun:_PyEval_EvalFrameDefault + fun:_PyFunction_Vectorcall + ... +} + +{ + Python + Memcheck:Leak + match-leak-kinds: possible + fun:malloc + fun:PyModule_ExecDef + obj:/usr/bin/python3.* + ... +} + +{ + Python + Memcheck:Leak + match-leak-kinds: possible + fun:malloc + obj:/usr/bin/python3.* + ... +} + +{ + Python + Memcheck:Addr32 + fun:__wcsncpy_avx2 + fun:_Py_wgetcwd + obj:/usr/bin/python3.* + fun:Py_RunMain + fun:Py_BytesMain + fun:(below main) +} + +{ + Python + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + obj:/usr/bin/python3.* + ... +} + +{ + Python + Memcheck:Leak + match-leak-kinds: definite + fun:*alloc + fun:_PyObject_GC_* + obj:/usr/bin/python3.* +} + + +{ + Antimony with libsbml 5.20.1 + Memcheck:Leak + match-leak-kinds: definite + fun:_Znwm + fun:_ZN7libsbml12SBMLDocument14getAllElementsEPNS_13ElementFilterE + fun:_ZN7libsbml23CompFlatteningConverter21unsetExplicitlyListedEv + fun:_ZN7libsbml23CompFlatteningConverter17performConversionEv + fun:_ZN7libsbml23CompFlatteningConverter7convertEv + fun:_ZN7libsbml22CompSBMLDocumentPlugin16checkConsistencyEv + fun:_ZN7libsbml12SBMLDocument16checkConsistencyEv + ... + 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 + ... +} + +{ + Python + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:gc_alloc + fun:_PyObject_GC_NewVar + ... +} + +{ + Python + Memcheck:Leak + match-leak-kinds: definite + fun:realloc + fun:_PyObject_GC_Resize + fun:_PyTuple_Resize + ... +} diff --git a/deps/AMICI/scripts/buildAmici.sh b/deps/AMICI/scripts/buildAmici.sh index 507a39162..22faece98 100755 --- a/deps/AMICI/scripts/buildAmici.sh +++ b/deps/AMICI/scripts/buildAmici.sh @@ -13,8 +13,9 @@ amici_build_dir="${amici_path}/build" mkdir -p "${amici_build_dir}" cd "${amici_build_dir}" -if [ "${GITHUB_ACTIONS:-}" = true ] || - [ "${ENABLE_AMICI_DEBUGGING:-}" = TRUE ]; then +if [[ "${GITHUB_REPOSITORY:-}" = *"/AMICI" ]] || + [ "${ENABLE_AMICI_DEBUGGING:-}" = TRUE ] || + [ "${ENABLE_GCOV_COVERAGE:-}" = TRUE ]; then # Running on CI server build_type="Debug" # exceptions instead of terminate() @@ -25,7 +26,28 @@ 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" +python3 -m pip install numpy ${cmake} \ -Wdev -DAMICI_CXX_OPTIONS="-Wall;-Wextra${extra_cxx_flags}" \ diff --git a/deps/AMICI/scripts/buildSuiteSparse.sh b/deps/AMICI/scripts/buildSuiteSparse.sh index e916530de..93341810f 100755 --- a/deps/AMICI/scripts/buildSuiteSparse.sh +++ b/deps/AMICI/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/deps/AMICI/scripts/buildSundials.sh b/deps/AMICI/scripts/buildSundials.sh index c898a17d0..f3410ef31 100755 --- a/deps/AMICI/scripts/buildSundials.sh +++ b/deps/AMICI/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} \ .. diff --git a/deps/AMICI/scripts/downloadAndBuildDoxygen.sh b/deps/AMICI/scripts/downloadAndBuildDoxygen.sh index 19d86be5a..a27587d96 100755 --- a/deps/AMICI/scripts/downloadAndBuildDoxygen.sh +++ b/deps/AMICI/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_9_7 \ + --branch Release_1_11_0 \ --depth 1 \ -c advice.detachedHead=false \ https://github.com/doxygen/doxygen.git "${DOXYGEN_DIR}" diff --git a/deps/AMICI/scripts/downloadAndBuildSwig.sh b/deps/AMICI/scripts/downloadAndBuildSwig.sh index 5fa0896f6..29ce32890 100755 --- a/deps/AMICI/scripts/downloadAndBuildSwig.sh +++ b/deps/AMICI/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}" @@ -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/deps/AMICI/scripts/installAmiciArchive.sh b/deps/AMICI/scripts/installAmiciArchive.sh index 25de4f9f9..f427a9125 100755 --- a/deps/AMICI/scripts/installAmiciArchive.sh +++ b/deps/AMICI/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/deps/AMICI/scripts/installAmiciSource.sh b/deps/AMICI/scripts/installAmiciSource.sh index 4e693468b..1c0a97570 100755 --- a/deps/AMICI/scripts/installAmiciSource.sh +++ b/deps/AMICI/scripts/installAmiciSource.sh @@ -1,35 +1,40 @@ #!/bin/bash -# -# Build libamici -# +# Create a virtual environment and perform an editable amici installation set -e SCRIPT_PATH=$(dirname $BASH_SOURCE) -AMICI_PATH=$(cd $SCRIPT_PATH/.. && pwd) +AMICI_PATH=$(cd "$SCRIPT_PATH/.." && pwd) # 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 -python3 -m venv ${AMICI_PATH}/build/venv --clear +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 ${AMICI_PATH}/build/venv --clear --without-pip - source ${AMICI_PATH}/build/venv/bin/activate - curl https://bootstrap.pypa.io/get-pip.py -o ${AMICI_PATH}/build/get-pip.py - python3 ${AMICI_PATH}/build/get-pip.py + 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 ${AMICI_PATH}/build/venv/bin/activate + source "${venv_dir}/bin/activate" fi -pip install --upgrade pip wheel -pip install --upgrade pip scipy matplotlib coverage pytest \ - pytest-cov cmake_build_extension numpy -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" pip install --verbose -e ${AMICI_PATH}/python/sdist[petab,test,vis] --no-build-isolation +# 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==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 deactivate diff --git a/deps/AMICI/scripts/run-SBMLTestsuite.sh b/deps/AMICI/scripts/run-SBMLTestsuite.sh index 97c203375..a3dcee168 100755 --- a/deps/AMICI/scripts/run-SBMLTestsuite.sh +++ b/deps/AMICI/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/deps/AMICI/scripts/run-codecov.sh b/deps/AMICI/scripts/run-codecov.sh index 42d76f7a0..f5f253b5c 100755 --- a/deps/AMICI/scripts/run-codecov.sh +++ b/deps/AMICI/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/deps/AMICI/scripts/run-python-tests.sh b/deps/AMICI/scripts/run-python-tests.sh index 982aa02f0..0fed628fc 100755 --- a/deps/AMICI/scripts/run-python-tests.sh +++ b/deps/AMICI/scripts/run-python-tests.sh @@ -1,7 +1,8 @@ #!/bin/bash -# Test python model wrapping inside virtual environment +# Run Python test suite inside virtual environment +# Usage: ./run-python-tests.sh [additional pytest arguments] -script_path=$(dirname $BASH_SOURCE) +script_path=$(dirname "${BASH_SOURCE[0]}") amici_path=$(cd "$script_path"/.. && pwd) set -e @@ -11,8 +12,11 @@ if [[ -z "${BNGPATH}" ]]; then fi cd "${amici_path}"/python/tests -source "${amici_path}"/build/venv/bin/activate -pip install scipy h5py pytest pytest-cov +source "${amici_path}"/venv/bin/activate # PEtab tests are run separately -pytest --ignore-glob=*petab* --ignore-glob=*test_splines.py +pytest \ + --ignore-glob=*petab* \ + --ignore-glob=*test_splines.py \ + --durations=10 \ + $@ diff --git a/deps/AMICI/scripts/run-sphinx-hasenv.sh b/deps/AMICI/scripts/run-sphinx-hasenv.sh deleted file mode 100755 index e40173702..000000000 --- a/deps/AMICI/scripts/run-sphinx-hasenv.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -# Build the sphinx documentation in an environment prepared -# as in run-sphinx.sh already - -SCRIPT_PATH=$(dirname $BASH_SOURCE) -AMICI_PATH=$(cd $SCRIPT_PATH/.. && pwd) - -source ${AMICI_PATH}/doc-venv/bin/activate - -cd ${AMICI_PATH}/documentation - -rm -rf ${AMICI_PATH}/documentation/generated - -sphinx-build -T -E -W --keep-going -b readthedocs -d _build/doctrees-readthedocs -D language=en . _build/html - -ret=$? -if [[ $ret != 0 ]]; then exit $ret; fi diff --git a/deps/AMICI/scripts/run-sphinx.sh b/deps/AMICI/scripts/run-sphinx.sh deleted file mode 100755 index e7b1ce086..000000000 --- a/deps/AMICI/scripts/run-sphinx.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash -# generate code documentation via sphinx for upload to rtd - -SCRIPT_PATH=$(dirname $BASH_SOURCE) -AMICI_PATH=$(cd $SCRIPT_PATH/.. && pwd) - -python3 -m venv ${AMICI_PATH}/doc-venv --clear -source ${AMICI_PATH}/doc-venv/bin/activate -python -m pip install --upgrade --no-cache-dir pip setuptools wheel -(cd ${AMICI_PATH}/ && python -m pip install --exists-action=w --no-cache-dir -r documentation/rtd_requirements.txt) -(cd ${AMICI_PATH}/ && python -m pip install --exists-action=w --no-cache-dir -r documentation/rtd_requirements2.txt) - -${AMICI_PATH}/scripts/run-sphinx-hasenv.sh - -ret=$? -if [[ $ret != 0 ]]; then exit $ret; fi diff --git a/deps/AMICI/scripts/run-valgrind-py.sh b/deps/AMICI/scripts/run-valgrind-py.sh index 510e27868..9621c24d3 100755 --- a/deps/AMICI/scripts/run-valgrind-py.sh +++ b/deps/AMICI/scripts/run-valgrind-py.sh @@ -1,7 +1,8 @@ #!/bin/bash -# Test python model wrapping inside virtual environment +# Without arguments: run Python test suite under valgrind +# With arguments: run whatever was passed as arguments under valgrind -script_path=$(dirname $BASH_SOURCE) +script_path=$(dirname "${BASH_SOURCE[0]}") amici_path=$(cd "$script_path"/.. && pwd) set -e @@ -9,23 +10,30 @@ set -e if [[ -z "${BNGPATH}" ]]; then export BNGPATH=${amici_path}/ThirdParty/BioNetGen-2.7.0 fi +suppressions="${amici_path}/python/tests/valgrind-python.supp" +if [ $# -eq 0 ] + then + # No arguments supplied, run all tests + cd "${amici_path}"/python/tests + 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"`: + # UserWarning: Signature b'\x00\xd0\xcc\xcc\xcc\xcc\xcc\xcc\xfb\xbf\x00\x00\x00\x00\x00\x00' + # for does not match any known type: falling back to type probe function. +else + # Run whatever was passed as arguments + command=($@) +fi -cd "${amici_path}"/python/tests - -source "${amici_path}"/build/venv/bin/activate - -pip install scipy h5py pytest pytest-rerunfailures +set -x PYTHONMALLOC=malloc valgrind \ - --suppressions=valgrind-python.supp \ + --suppressions="${suppressions}" \ --show-leak-kinds=definite \ --errors-for-leak-kinds=definite \ --error-exitcode=1 \ --leak-check=full \ --gen-suppressions=all \ -v \ - 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"`: -# UserWarning: Signature b'\x00\xd0\xcc\xcc\xcc\xcc\xcc\xcc\xfb\xbf\x00\x00\x00\x00\x00\x00' -# for does not match any known type: falling back to type probe function. + "${command[@]}" diff --git a/deps/AMICI/scripts/runNotebook.sh b/deps/AMICI/scripts/runNotebook.sh index bf1ef8d5e..ee62b59cf 100755 --- a/deps/AMICI/scripts/runNotebook.sh +++ b/deps/AMICI/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)") diff --git a/deps/AMICI/src/CMakeLists.template.cmake b/deps/AMICI/src/CMakeLists.template.cmake index 43df61ff6..dbad3e146 100644 --- a/deps/AMICI/src/CMakeLists.template.cmake +++ b/deps/AMICI/src/CMakeLists.template.cmake @@ -20,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() @@ -38,6 +38,26 @@ 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}") + add_compile_options(-UNDEBUG) + if(MSVC) + add_compile_options(-DEBUG) + else() + add_compile_options(-O0 -g) + endif() +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}) @@ -66,18 +86,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/deps/AMICI/src/abstract_model.cpp b/deps/AMICI/src/abstract_model.cpp index dc2c46917..dfa078053 100644 --- a/deps/AMICI/src/abstract_model.cpp +++ b/deps/AMICI/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( @@ -437,10 +437,11 @@ void AbstractModel::fdJrzdsigma( ); } -void AbstractModel:: - fw(realtype* /*w*/, const realtype /*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!", @@ -449,10 +450,10 @@ 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*/ + 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*/, const realtype /*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*/, 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*/ + 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/deps/AMICI/src/amici.cpp b/deps/AMICI/src/amici.cpp index ee3949b0b..9fbc47277 100644 --- a/deps/AMICI/src/amici.cpp +++ b/deps/AMICI/src/amici.cpp @@ -13,11 +13,6 @@ #include //return codes #include //realtype -#include -#include -#include -#include -#include #include #include #include @@ -64,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"}, @@ -74,6 +70,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( @@ -210,11 +207,27 @@ 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) + throw; + logger.log( + LogSeverity::error, "OTHER", "AMICI simulation failed: %s", + ex.what() + ); + } + 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) @@ -225,10 +238,7 @@ std::unique_ptr runAmiciSimulation( ); } - rdata->processSimulationObjects( - preeq.get(), fwd.get(), bwd_success ? bwd.get() : nullptr, posteq.get(), - model, solver, edata - ); + rdata->t_last = solver.gett(); rdata->cpu_time_total = cpu_timer.elapsed_milliseconds(); @@ -272,17 +282,28 @@ std::vector> runAmiciSimulations( #pragma omp parallel for num_threads(num_threads) #endif for (int i = 0; i < (int)edatas.size(); ++i) { - auto mySolver = std::unique_ptr(solver.clone()); - auto myModel = std::unique_ptr(model.clone()); - - /* if we fail we need to write empty return datas for the python - interface */ - if (skipThrough) { - ConditionContext conditionContext(myModel.get(), edatas[i]); + // must catch exceptions in parallel section to avoid termination + try { + auto mySolver = std::unique_ptr(solver.clone()); + auto myModel = std::unique_ptr(model.clone()); + + /* if we fail we need to write empty return datas for the python + interface */ + if (skipThrough) { + ConditionContext conditionContext(myModel.get(), edatas[i]); + results[i] + = std::unique_ptr(new ReturnData(solver, model) + ); + } else { + results[i] = runAmiciSimulation(*mySolver, edatas[i], *myModel); + } + } catch (std::exception const& ex) { results[i] = std::unique_ptr(new ReturnData(solver, model)); - } else { - results[i] = runAmiciSimulation(*mySolver, edatas[i], *myModel); + results[i]->status = AMICI_ERROR; + results[i]->messages.push_back( + LogItem(LogSeverity::error, "OTHER", ex.what()) + ); } skipThrough |= failfast && results[i]->status < 0; diff --git a/deps/AMICI/src/backwardproblem.cpp b/deps/AMICI/src/backwardproblem.cpp index d1b89967d..c3a859ca8 100644 --- a/deps/AMICI/src/backwardproblem.cpp +++ b/deps/AMICI/src/backwardproblem.cpp @@ -8,9 +8,6 @@ #include "amici/solver.h" #include "amici/steadystateproblem.h" -#include -#include - namespace amici { BackwardProblem::BackwardProblem( diff --git a/deps/AMICI/src/edata.cpp b/deps/AMICI/src/edata.cpp index ddc3b6599..a4c0868ad 100644 --- a/deps/AMICI/src/edata.cpp +++ b/deps/AMICI/src/edata.cpp @@ -5,7 +5,6 @@ #include "amici/symbolic_functions.h" // getNaN #include -#include #include #include @@ -60,9 +59,9 @@ ExpData::ExpData( ExpData::ExpData(Model const& model) : ExpData( - model.nytrue, model.nztrue, model.nMaxEvent(), model.getTimepoints(), - model.getFixedParameters() - ) { + model.nytrue, model.nztrue, model.nMaxEvent(), model.getTimepoints(), + model.getFixedParameters() + ) { reinitializeFixedParameterInitialStates = model.getReinitializeFixedParameterInitialStates() && model.getReinitializationStateIdxs().empty(); @@ -71,9 +70,9 @@ ExpData::ExpData(Model const& model) ExpData::ExpData(ReturnData const& rdata, realtype sigma_y, realtype sigma_z) : ExpData( - rdata, std::vector(rdata.nytrue * rdata.nt, sigma_y), - std::vector(rdata.nztrue * rdata.nmaxevent, sigma_z) - ) {} + rdata, std::vector(rdata.nytrue * rdata.nt, sigma_y), + std::vector(rdata.nztrue * rdata.nmaxevent, sigma_z) + ) {} ExpData::ExpData( ReturnData const& rdata, std::vector sigma_y, @@ -195,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 @@ -217,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; @@ -291,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 @@ -314,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) @@ -339,6 +338,18 @@ realtype const* ExpData::getObservedEventsStdDevPtr(int ie) const { return nullptr; } +void ExpData::clear_observations() { + std::fill(observed_data_.begin(), observed_data_.end(), getNaN()); + std::fill( + observed_data_std_dev_.begin(), observed_data_std_dev_.end(), getNaN() + ); + std::fill(observed_events_.begin(), observed_events_.end(), getNaN()); + std::fill( + observed_events_std_dev_.begin(), observed_events_std_dev_.end(), + getNaN() + ); +} + void ExpData::applyDimensions() { applyDataDimension(); applyEventDimension(); @@ -381,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/deps/AMICI/src/exception.cpp b/deps/AMICI/src/exception.cpp index 3a5811e4f..984be5282 100644 --- a/deps/AMICI/src/exception.cpp +++ b/deps/AMICI/src/exception.cpp @@ -3,7 +3,6 @@ #include #include -#include namespace amici { @@ -34,15 +33,21 @@ void AmiException::storeMessage(char const* fmt, va_list argptr) { vsnprintf(msg_.data(), msg_.size(), fmt, argptr); } -CvodeException::CvodeException(int const error_code, char const* function) +CvodeException::CvodeException( + int const error_code, char const* function, char const* extra +) : AmiException( - "Cvode routine %s failed with error code %i", function, error_code - ) {} + "CVODE routine %s failed with error code %i. %s", function, + error_code, extra ? extra : "" + ) {} -IDAException::IDAException(int const error_code, char const* function) +IDAException::IDAException( + int const error_code, char const* function, char const* extra +) : AmiException( - "IDA routine %s failed with error code %i", function, error_code - ) {} + "IDA routine %s failed with error code %i. %s", function, error_code, + extra ? extra : "" + ) {} IntegrationFailure::IntegrationFailure(int code, realtype t) : AmiException("AMICI failed to integrate the forward problem") @@ -56,8 +61,8 @@ IntegrationFailureB::IntegrationFailureB(int code, realtype t) NewtonFailure::NewtonFailure(int code, char const* function) : AmiException( - "NewtonSolver routine %s failed with error code %i", function, code - ) { + "NewtonSolver routine %s failed with error code %i", function, code + ) { error_code = code; } diff --git a/deps/AMICI/src/forwardproblem.cpp b/deps/AMICI/src/forwardproblem.cpp index c70a9074b..ef4585f9e 100644 --- a/deps/AMICI/src/forwardproblem.cpp +++ b/deps/AMICI/src/forwardproblem.cpp @@ -9,10 +9,31 @@ #include #include -#include namespace amici { +/** + * @brief Check if the next timepoint is too close to the current timepoint. + * + * Based on CVODES' `cvHin`. + * @param cur_t Current time. + * @param t_next Next stop time. + * @return True if too close, false otherwise. + */ +bool is_next_t_too_close(realtype cur_t, realtype t_next) { + auto tdiff = t_next - cur_t; + 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)); + if (tdist < 2.0 * tround) + return true; + + return false; +} + ForwardProblem::ForwardProblem( ExpData const* edata, Model* model, Solver* solver, SteadystateProblem const* preeq @@ -110,30 +131,69 @@ void ForwardProblem::workForwardProblem() { /* store initial state and sensitivity*/ initial_state_ = getSimulationState(); + // store root information at t0 + model->froot(t_, x_, dx_, rootvals_); + + // get list of trigger timepoints for fixed-time triggered events + auto trigger_timepoints = model->get_trigger_timepoints(); + auto it_trigger_timepoints = std::find_if( + trigger_timepoints.begin(), trigger_timepoints.end(), + [this](auto t) { return t > this->t_; } + ); /* loop over timepoints */ for (it_ = 0; it_ < model->nt(); it_++) { - auto nextTimepoint = model->getTimepoint(it_); + // next output time-point + auto next_t_out = model->getTimepoint(it_); - if (std::isinf(nextTimepoint)) + if (std::isinf(next_t_out)) break; - if (nextTimepoint > model->t0()) { - // Solve for nextTimepoint - while (t_ < nextTimepoint) { - int status = solver->run(nextTimepoint); - solver->writeSolution(&t_, x_, dx_, sx_, dx_); + if (next_t_out > model->t0()) { + // Solve for next output timepoint + while (t_ < next_t_out) { + if (is_next_t_too_close(t_, next_t_out)) { + // next timepoint is too close to current timepoint. + // we use the state of the current timepoint. + break; + } + + // next stop time is next output timepoint or next + // time-triggered event + auto next_t_event + = it_trigger_timepoints != trigger_timepoints.end() + ? *it_trigger_timepoints + : std::numeric_limits::infinity(); + auto next_t_stop = std::min(next_t_out, next_t_event); + + int status = solver->run(next_t_stop); /* sx will be copied from solver on demand if sensitivities are computed */ + solver->writeSolution(&t_, x_, dx_, sx_, dx_); + if (status == AMICI_ILL_INPUT) { - /* clustering of roots => turn off rootfinding */ + /* clustering of roots => turn off root-finding */ solver->turnOffRootFinding(); - } else if (status == AMICI_ROOT_RETURN) { + } else if (status == AMICI_ROOT_RETURN || t_ == next_t_event) { + // solver-tracked or time-triggered event + solver->getRootInfo(roots_found_.data()); + + // check if we are at a trigger timepoint. + // if so, set the root-found flag + if (t_ == next_t_event) { + for (auto ie : model->state_independent_events_[t_]) { + // determine direction of root crossing from + // root function value at the previous event + roots_found_[ie] = std::copysign(1, -rootvals_[ie]); + } + ++it_trigger_timepoints; + } + handleEvent(&tlastroot_, false, false); } } } - handleDataPoint(it_); + handleDataPoint(next_t_out); } /* fill events */ @@ -157,13 +217,9 @@ void ForwardProblem::handleEvent( /* store Heaviside information at event occurrence */ model->froot(t_, x_, dx_, rootvals_); - /* store timepoint at which the event occurred*/ + /* store timepoint at which the event occurred */ discs_.push_back(t_); - /* extract and store which events occurred */ - if (!seflag && !initial_event) { - solver->getRootInfo(roots_found_.data()); - } root_idx_.push_back(roots_found_); rval_tmp_ = rootvals_; @@ -183,6 +239,77 @@ void ForwardProblem::handleEvent( if (model->nz > 0) storeEvent(); + store_pre_event_state(seflag, initial_event); + + if (!initial_event) + model->updateHeaviside(roots_found_); + + applyEventBolus(); + + if (solver->computingFSA()) { + /* compute the new xdot */ + model->fxdot(t_, x_, dx_, xdot_); + applyEventSensiBolusFSA(); + } + + handle_secondary_event(tlastroot); + + /* only reinitialise in the first event fired */ + if (!seflag) { + solver->reInit(t_, x_, dx_); + if (solver->computingFSA()) { + solver->sensReInit(sx_, sdx_); + } + } +} + +void ForwardProblem::storeEvent() { + if (t_ == model->getTimepoint(model->nt() - 1)) { + // call from fillEvent at last timepoint + model->froot(t_, x_, dx_, rootvals_); + for (int ie = 0; ie < model->ne; ie++) { + roots_found_.at(ie) = (nroots_.at(ie) < model->nMaxEvent()) ? 1 : 0; + } + root_idx_.push_back(roots_found_); + } + + if (getRootCounter() < getEventCounter()) { + /* update stored state (sensi) */ + event_states_.at(getRootCounter()) = getSimulationState(); + } else { + /* add stored state (sensi) */ + event_states_.push_back(getSimulationState()); + } + + /* EVENT OUTPUT */ + for (int ie = 0; ie < model->ne; ie++) { + /* only look for roots of the rootfunction not discontinuities */ + if (nroots_.at(ie) >= model->nMaxEvent()) + continue; + + /* only consider transitions false -> true or event filling */ + if (roots_found_.at(ie) != 1 + && t_ != model->getTimepoint(model->nt() - 1)) { + continue; + } + + if (edata && solver->computingASA()) + model->getAdjointStateEventUpdate( + slice(dJzdx_, nroots_.at(ie), model->nx_solver * model->nJ), ie, + nroots_.at(ie), t_, x_, *edata + ); + + nroots_.at(ie)++; + } + + if (t_ == model->getTimepoint(model->nt() - 1)) { + // call from fillEvent at last timepoint + // loop until all events are filled + fillEvents(model->nMaxEvent()); + } +} + +void ForwardProblem::store_pre_event_state(bool seflag, bool initial_event) { /* if we need to do forward sensitivities later on we need to store the old * x and the old xdot */ if (solver->getSensitivityOrder() >= SensitivityOrder::first) { @@ -213,18 +340,9 @@ void ForwardProblem::handleEvent( xdot_disc_.push_back(xdot_); xdot_old_disc_.push_back(xdot_old_); } +} - if (!initial_event) - model->updateHeaviside(roots_found_); - - applyEventBolus(); - - if (solver->computingFSA()) { - /* compute the new xdot */ - model->fxdot(t_, x_, dx_, xdot_); - applyEventSensiBolusFSA(); - } - +void ForwardProblem::handle_secondary_event(realtype* tlastroot) { int secondevent = 0; /* check whether we need to fire a secondary event */ @@ -261,67 +379,13 @@ void ForwardProblem::handleEvent( ); handleEvent(tlastroot, true, false); } - - /* only reinitialise in the first event fired */ - if (!seflag) { - solver->reInit(t_, x_, dx_); - if (solver->computingFSA()) { - solver->sensReInit(sx_, sdx_); - } - } -} - -void ForwardProblem::storeEvent() { - if (t_ == model->getTimepoint(model->nt() - 1)) { - // call from fillEvent at last timepoint - model->froot(t_, x_, dx_, rootvals_); - for (int ie = 0; ie < model->ne; ie++) { - roots_found_.at(ie) = (nroots_.at(ie) < model->nMaxEvent()) ? 1 : 0; - } - root_idx_.push_back(roots_found_); - } - - if (getRootCounter() < getEventCounter()) { - /* update stored state (sensi) */ - event_states_.at(getRootCounter()) = getSimulationState(); - } else { - /* add stored state (sensi) */ - event_states_.push_back(getSimulationState()); - } - - /* EVENT OUTPUT */ - for (int ie = 0; ie < model->ne; ie++) { - /* only look for roots of the rootfunction not discontinuities */ - if (nroots_.at(ie) >= model->nMaxEvent()) - continue; - - /* only consider transitions false -> true or event filling */ - if (roots_found_.at(ie) != 1 - && t_ != model->getTimepoint(model->nt() - 1)) { - continue; - } - - if (edata && solver->computingASA()) - model->getAdjointStateEventUpdate( - slice(dJzdx_, nroots_.at(ie), model->nx_solver * model->nJ), ie, - nroots_.at(ie), t_, x_, *edata - ); - - nroots_.at(ie)++; - } - - if (t_ == model->getTimepoint(model->nt() - 1)) { - // call from fillEvent at last timepoint - // loop until all events are filled - fillEvents(model->nMaxEvent()); - } } -void ForwardProblem::handleDataPoint(int /*it*/) { +void ForwardProblem::handleDataPoint(realtype t) { /* We only store the simulation state if it's not the initial state, as the initial state is stored anyway and we want to avoid storing it twice */ - if (t_ != model->t0() && timepoint_states_.count(t_) == 0) - timepoint_states_[t_] = getSimulationState(); + if (t != model->t0() && timepoint_states_.count(t) == 0) + timepoint_states_[t] = getSimulationState(); /* store diagnosis information for debugging */ solver->storeDiagnosis(); } @@ -352,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/deps/AMICI/src/hdf5.cpp b/deps/AMICI/src/hdf5.cpp index e0cdde88c..f9914452e 100644 --- a/deps/AMICI/src/hdf5.cpp +++ b/deps/AMICI/src/hdf5.cpp @@ -16,7 +16,6 @@ #include - namespace amici { namespace hdf5 { @@ -533,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 @@ -656,7 +659,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 @@ -668,7 +671,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 @@ -680,7 +683,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 @@ -803,6 +806,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 @@ -896,6 +904,20 @@ 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 + ); + + createAndWriteDouble1DDataset( + file, hdf5Location + "/constraints", solver.getConstraints() + ); } void readSolverSettingsFromHDF5( @@ -991,6 +1013,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") ); @@ -1107,6 +1135,24 @@ 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") + ); + } + + if (locationExists(file, datasetPath + "/constraints")) { + solver.setConstraints( + getDoubleDataset1D(file, datasetPath + "/constraints") + ); + } } void readSolverSettingsFromHDF5( @@ -1161,8 +1207,8 @@ void readModelDataFromHDF5( model.setSteadyStateComputationMode( static_cast(getIntScalarAttribute( file, datasetPath, "steadyStateComputationMode" - )) - ); + )) + ); } if (attributeExists(file, datasetPath, "steadyStateSensitivityMode")) { @@ -1198,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/deps/AMICI/src/interface_matlab.cpp b/deps/AMICI/src/interface_matlab.cpp index 3caae66a9..93ffb24fd 100644 --- a/deps/AMICI/src/interface_matlab.cpp +++ b/deps/AMICI/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_); } @@ -423,8 +423,10 @@ void setModelData(mxArray const* prhs[], int nrhs, Model& model) { model.setParameterScale( static_cast(dbl2int(mxGetScalar(a))) ); - } else if((mxGetM(a) == 1 && gsl::narrow(mxGetN(a)) == model.np()) - || (mxGetN(a) == 1 && gsl::narrow(mxGetM(a)) == model.np())) { + } else if ((mxGetM(a) == 1 + && gsl::narrow(mxGetN(a)) == model.np()) + || (mxGetN(a) == 1 + && gsl::narrow(mxGetM(a)) == model.np())) { auto pscaleArray = static_cast(mxGetData(a)); std::vector pscale(model.np()); for (int ip = 0; ip < model.np(); ++ip) { @@ -597,7 +599,9 @@ void mexFunction(int nlhs, mxArray* plhs[], int nrhs, mxArray const* prhs[]) { ex.what() ); } - } else if (solver->getSensitivityOrder() >= amici::SensitivityOrder::first && solver->getSensitivityMethod() == amici::SensitivityMethod::adjoint) { + } else if (solver->getSensitivityOrder() >= amici::SensitivityOrder::first + && solver->getSensitivityMethod() + == amici::SensitivityMethod::adjoint) { mexErrMsgIdAndTxt("AMICI:mex:setup", "No data provided!"); } diff --git a/deps/AMICI/src/model.cpp b/deps/AMICI/src/model.cpp index 46c9e9902..c27a5310d 100644 --- a/deps/AMICI/src/model.cpp +++ b/deps/AMICI/src/model.cpp @@ -6,7 +6,7 @@ #include #include -#include +#include #include #include #include @@ -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"}, @@ -178,12 +178,14 @@ Model::Model( SimulationParameters simulation_parameters, SecondOrderMode o2mode, std::vector idlist, std::vector z2event, bool const pythonGenerated, int const ndxdotdp_explicit, - int const ndxdotdx_explicit, int const w_recursion_depth + int const ndxdotdx_explicit, int const w_recursion_depth, + std::map> state_independent_events ) : ModelDimensions(model_dimensions) , pythonGenerated(pythonGenerated) , o2mode(o2mode) , idlist(std::move(idlist)) + , state_independent_events_(std::move(state_independent_events)) , derived_state_(model_dimensions) , z2event_(std::move(z2event)) , state_is_non_negative_(nx_solver, false) @@ -285,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) { @@ -297,6 +301,7 @@ bool operator==(ModelDimensions const& a, ModelDimensions const& b) { && (a.nx_solver_reinit == b.nx_solver_reinit) && (a.np == b.np) && (a.nk == b.nk) && (a.ny == b.ny) && (a.nytrue == b.nytrue) && (a.nz == b.nz) && (a.nztrue == b.nztrue) && (a.ne == b.ne) + && (a.ne_solver == b.ne_solver) && (a.nspl == b.nspl) && (a.nw == b.nw) && (a.ndwdx == b.ndwdx) && (a.ndwdp == b.ndwdp) && (a.ndwdw == b.ndwdw) && (a.ndxdotdw == b.ndxdotdw) && (a.ndJydy == b.ndJydy) && (a.nnz == b.nnz) && (a.nJ == b.nJ) @@ -320,6 +325,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( @@ -344,7 +374,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() { @@ -855,11 +885,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; @@ -993,7 +1023,7 @@ void Model::setUnscaledInitialStateSensitivities( sx0data_ = sx0; } -void Model::setSteadyStateComputationMode(const SteadyStateComputationMode mode +void Model::setSteadyStateComputationMode(SteadyStateComputationMode const mode ) { steadystate_computation_mode_ = mode; } @@ -1002,7 +1032,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; } @@ -1043,14 +1073,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)); + fw(t, computeX_pos(x), false); 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); @@ -1061,7 +1091,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) @@ -1202,14 +1232,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) { @@ -1260,14 +1290,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) { @@ -1310,7 +1340,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); @@ -1318,14 +1348,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); @@ -1345,7 +1375,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); @@ -1367,7 +1397,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 ) { @@ -1402,7 +1432,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); @@ -1411,7 +1441,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; @@ -1422,7 +1452,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 ) { @@ -1438,7 +1468,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 ) { @@ -1454,7 +1484,7 @@ void Model::addStateEventUpdate( ); if (always_check_finite_) { - checkFinite(derived_state_.deltax_, ModelQuantity::deltax); + checkFinite(derived_state_.deltax_, ModelQuantity::deltax, t); } // update @@ -1462,11 +1492,11 @@ 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 ) { - fw(t, x_old.data()); + fw(t, x_old.data(), false); for (int ip = 0; ip < nplist(); ip++) { @@ -1494,7 +1524,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 ) { @@ -1508,7 +1538,7 @@ void Model::addAdjointStateEventUpdate( ); if (always_check_finite_) { - checkFinite(derived_state_.deltaxB_, ModelQuantity::deltaxB); + checkFinite(derived_state_.deltaxB_, ModelQuantity::deltaxB, t); } // apply update @@ -1519,7 +1549,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++) { @@ -1554,7 +1584,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); @@ -1626,23 +1656,26 @@ 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); 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; @@ -1650,7 +1683,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); @@ -1683,7 +1716,7 @@ int Model::checkFinite( if (hasObservableIds()) row_id += " " + getObservableIds()[row]; if (hasParameterIds()) - col_id += " " + getParameterIds()[col]; + col_id += " " + getParameterIds()[plist(gsl::narrow(col))]; break; case ModelQuantity::dydx: if (hasObservableIds()) @@ -1740,19 +1773,24 @@ 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(derived_state_.w_, ModelQuantity::w, t); return AMICI_RECOVERABLE_ERROR; } @@ -1815,7 +1853,7 @@ int Model::checkFinite(SUNMatrix m, ModelQuantity model_quantity, realtype t) if (hasExpressionIds()) row_id += " " + getExpressionIds()[row]; if (hasParameterIds()) - col_id += " " + getParameterIds()[plist(gsl::narrow(col))]; + col_id += " " + getParameterIds()[col]; break; default: break; @@ -1840,10 +1878,9 @@ 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(derived_state_.w_, ModelQuantity::w, t); return AMICI_RECOVERABLE_ERROR; } @@ -1867,7 +1904,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) { @@ -1954,7 +1991,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( @@ -2029,7 +2069,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; @@ -2037,27 +2077,27 @@ void Model::fy(const realtype 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()); 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 ); } } -void Model::fdydp(const realtype t, AmiVector const& x) { +void Model::fdydp(realtype const t, AmiVector const& x) { if (!ny) return; 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++) @@ -2083,7 +2123,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; @@ -2091,8 +2131,8 @@ void Model::fdydx(const realtype 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(), @@ -2246,7 +2286,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( @@ -2417,7 +2457,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); @@ -2426,7 +2466,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; @@ -2445,7 +2485,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; @@ -2462,7 +2502,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); @@ -2471,7 +2511,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; @@ -2490,7 +2530,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; @@ -2508,7 +2548,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; @@ -2544,7 +2584,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; @@ -2580,7 +2620,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) @@ -2612,7 +2652,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) @@ -2712,7 +2752,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 @@ -2760,7 +2800,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) @@ -2791,7 +2831,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) @@ -2822,12 +2862,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++) { @@ -2837,38 +2877,42 @@ void Model::fsspl(const realtype t) { } } -void Model::fw(const realtype 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); + checkFinite(derived_state_.w_, ModelQuantity::w, t); } } -void Model::fdwdp(const realtype 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_; @@ -2894,29 +2938,33 @@ void Model::fdwdp(const realtype t, realtype const* x) { } if (always_check_finite_) { - checkFinite(derived_state_.dwdp_.get(), ModelQuantity::dwdp, t); + checkFinite(derived_state_.dwdp_, ModelQuantity::dwdp, t); } } -void Model::fdwdx(const realtype 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_; @@ -2940,24 +2988,28 @@ void Model::fdwdx(const realtype t, realtype const* x) { } if (always_check_finite_) { - checkFinite(derived_state_.dwdx_.get(), ModelQuantity::dwdx, t); + checkFinite(derived_state_.dwdx_, ModelQuantity::dwdx, t); } } -void Model::fdwdw(const realtype 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_) { - checkFinite(dwdw_.get(), ModelQuantity::dwdw, t); + checkFinite(dwdw_, ModelQuantity::dwdw, t); } } @@ -3071,6 +3123,37 @@ void Model::fstotal_cl( ); } +std::vector Model::get_trigger_timepoints() const { + std::vector trigger_timepoints( + state_independent_events_.size(), 0.0 + ); + // collect keys from state_independent_events_ which are the trigger + // timepoints + auto it = trigger_timepoints.begin(); + for (auto const& kv : state_independent_events_) { + *(it++) = kv.first; + } + std::sort(trigger_timepoints.begin(), trigger_timepoints.end()); + 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/deps/AMICI/src/model_dae.cpp b/deps/AMICI/src/model_dae.cpp index 43a8d8131..22025e118 100644 --- a/deps/AMICI/src/model_dae.cpp +++ b/deps/AMICI/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); @@ -14,14 +14,14 @@ 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); } 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); @@ -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 @@ -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); @@ -88,13 +88,13 @@ 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); } 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()); } @@ -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), @@ -132,18 +132,19 @@ 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()); + 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!"); } 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 +162,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); @@ -171,7 +172,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)); @@ -230,7 +231,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 +243,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 +256,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 +284,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 +312,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 +340,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 ) { @@ -355,14 +356,14 @@ 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); } 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 ) { @@ -376,7 +377,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 +388,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 +398,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); @@ -427,7 +428,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()); @@ -454,18 +455,18 @@ 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(); } 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 */ 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 */ @@ -473,7 +474,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( @@ -491,7 +492,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/deps/AMICI/src/model_header.template.h b/deps/AMICI/src/model_header.template.h index af05c8ccc..932fdeb1a 100644 --- a/deps/AMICI/src/model_header.template.h +++ b/deps/AMICI/src/model_header.template.h @@ -121,6 +121,7 @@ class Model_TPL_MODELNAME : public amici::Model_TPL_MODEL_TYPE_UPPER { TPL_NZ, // nz TPL_NZTRUE, // nztrue TPL_NEVENT, // nevent + TPL_NEVENT_SOLVER, // nevent_solver TPL_NSPL, // nspl TPL_NOBJECTIVE, // nobjective TPL_NW, // nw @@ -146,7 +147,8 @@ class Model_TPL_MODELNAME : public amici::Model_TPL_MODEL_TYPE_UPPER { true, // pythonGenerated TPL_NDXDOTDP_EXPLICIT, // ndxdotdp_explicit TPL_NDXDOTDX_EXPLICIT, // ndxdotdx_explicit - TPL_W_RECURSION_DEPTH // w_recursion_depth + TPL_W_RECURSION_DEPTH, // w_recursion_depth + {TPL_STATE_INDEPENDENT_EVENTS} // state-independent events ) { root_initial_values_ = std::vector( rootInitialValues.begin(), rootInitialValues.end() diff --git a/deps/AMICI/src/model_ode.cpp b/deps/AMICI/src/model_ode.cpp index 24787df8a..787df210e 100644 --- a/deps/AMICI/src/model_ode.cpp +++ b/deps/AMICI/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); @@ -14,16 +14,14 @@ 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()); + fJSparse(t, x, derived_state_.J_); derived_state_.J_.refresh(); auto JDense = SUNMatrixWrapper(J); derived_state_.J_.to_dense(JDense); } 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); @@ -31,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 @@ -69,9 +67,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()); } @@ -79,13 +77,13 @@ 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); } 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 +100,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,15 +118,16 @@ 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()); - if (checkFinite(JDiag.getVector(), ModelQuantity::JDiag) != AMICI_SUCCESS) + if (checkFinite(JDiag.getVector(), ModelQuantity::JDiag, t) + != AMICI_SUCCESS) 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,9 +142,9 @@ 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)); + fdwdp(t, N_VGetArrayPointerConst(x_pos), false); if (pythonGenerated) { // python generated @@ -189,7 +188,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 +197,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 +209,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 +237,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 +249,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 +261,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 +289,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 +317,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 +345,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 ) { @@ -357,14 +356,14 @@ 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); } 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 ) { @@ -375,14 +374,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); } @@ -392,14 +391,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); } @@ -436,7 +435,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()); @@ -458,19 +457,19 @@ 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(); } 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 ) { /* 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 */ @@ -478,7 +477,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()); @@ -494,7 +493,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/deps/AMICI/src/newton_solver.cpp b/deps/AMICI/src/newton_solver.cpp index b8cbe8f34..72bf0a3d6 100644 --- a/deps/AMICI/src/newton_solver.cpp +++ b/deps/AMICI/src/newton_solver.cpp @@ -8,10 +8,6 @@ #include // dense solver #include // sparse solver -#include -#include -#include - namespace amici { NewtonSolver::NewtonSolver(Model const& model) @@ -112,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"); @@ -121,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"); } @@ -131,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 @@ -149,7 +145,7 @@ void NewtonSolverDense::solveLinearSystem(AmiVector& rhs) { throw NewtonFailure(status, "SUNLinSolSolve_Dense"); } -void NewtonSolverDense::reinitialize(){ +void NewtonSolverDense::reinitialize() { /* dense solver does not need reinitialization */ }; @@ -171,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"); @@ -181,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"); } @@ -192,11 +188,9 @@ 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_.get() - ); + model.fJSparseB(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"); } @@ -204,7 +198,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 @@ -215,7 +209,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/deps/AMICI/src/rdata.cpp b/deps/AMICI/src/rdata.cpp index 56fc0023c..649b420cc 100644 --- a/deps/AMICI/src/rdata.cpp +++ b/deps/AMICI/src/rdata.cpp @@ -10,20 +10,19 @@ #include "amici/symbolic_functions.h" #include -#include namespace amici { ReturnData::ReturnData(Solver const& solver, Model const& model) : ReturnData( - model.getTimepoints(), - ModelDimensions(static_cast(model)), - model.nplist(), model.nMaxEvent(), model.nt(), - solver.getNewtonMaxSteps(), model.getParameterScale(), model.o2mode, - solver.getSensitivityOrder(), solver.getSensitivityMethod(), - solver.getReturnDataReportingMode(), model.hasQuadraticLLH(), - model.getAddSigmaResiduals(), model.getMinimumSigmaResiduals() - ) {} + model.getTimepoints(), + ModelDimensions(static_cast(model)), + model.nplist(), model.nMaxEvent(), model.nt(), + solver.getNewtonMaxSteps(), model.getParameterScale(), model.o2mode, + solver.getSensitivityOrder(), solver.getSensitivityMethod(), + solver.getReturnDataReportingMode(), model.hasQuadraticLLH(), + model.getAddSigmaResiduals(), model.getMinimumSigmaResiduals() + ) {} ReturnData::ReturnData( std::vector ts, ModelDimensions const& model_dimensions, @@ -708,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/deps/AMICI/src/solver.cpp b/deps/AMICI/src/solver.cpp index 56bed2a1a..b9e497f30 100644 --- a/deps/AMICI/src/solver.cpp +++ b/deps/AMICI/src/solver.cpp @@ -5,8 +5,6 @@ #include "amici/symbolic_functions.h" #include -#include -#include #include namespace amici { @@ -19,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_) @@ -46,6 +45,9 @@ 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_) + , max_step_size_(other.max_step_size_) , maxstepsB_(other.maxstepsB_) , sensi_(other.sensi_) {} @@ -83,7 +85,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; @@ -102,7 +104,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(); @@ -118,7 +120,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(); @@ -130,7 +132,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() @@ -161,7 +163,7 @@ void Solver::setup( /* activates stability limit detection */ setStabLimDet(stldet_); - rootInit(model->ne); + rootInit(model->ne_solver); if (nx() == 0) return; @@ -193,12 +195,18 @@ void Solver::setup( if (model->nt() > 1) calcIC(model->getTimepoint(1)); + apply_max_nonlin_iters(); + apply_max_conv_fails(); + apply_max_step_size(); + cpu_time_ = 0.0; cpu_timeB_ = 0.0; + + apply_constraints(); } 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_) @@ -230,7 +238,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 */ @@ -253,13 +261,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_); } } @@ -554,7 +563,11 @@ 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_) + && (a.max_step_size_ == b.max_step_size_) + && (a.constraints_.getVector() == b.constraints_.getVector()); } void Solver::applyTolerances() const { @@ -638,26 +651,35 @@ 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 { 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) @@ -667,6 +689,47 @@ 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_; } + +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."); + 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) { @@ -695,7 +758,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; @@ -952,7 +1015,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; @@ -962,7 +1025,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; @@ -970,7 +1033,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; @@ -1022,7 +1085,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; @@ -1152,6 +1215,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); @@ -1183,7 +1248,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_; @@ -1193,7 +1258,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_; @@ -1203,7 +1268,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(); @@ -1215,7 +1280,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_) { @@ -1231,7 +1296,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_) { @@ -1247,7 +1312,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/deps/AMICI/src/solver_cvodes.cpp b/deps/AMICI/src/solver_cvodes.cpp index 7157302c9..e5771ff39 100644 --- a/deps/AMICI/src/solver_cvodes.cpp +++ b/deps/AMICI/src/solver_cvodes.cpp @@ -13,6 +13,8 @@ #include #include +#include + #define ZERO RCONST(0.0) #define ONE RCONST(1.0) #define FOUR RCONST(4.0) @@ -102,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; @@ -120,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, @@ -159,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; @@ -255,6 +257,37 @@ 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"); +} + +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) + throw CvodeException(status, "CVodeSetMaxStep"); +} + Solver* CVodeSolver::clone() const { return new CVodeSolver(*this); } void CVodeSolver::allocateSolver() const { @@ -450,12 +483,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; @@ -475,7 +508,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; @@ -490,13 +523,16 @@ void CVodeSolver::reInitPostProcess( if (status == CV_ROOT_RETURN) throw CvodeException( status, - "CVode returned a root after " - "reinitialization. The initial step-size after the event or " - "heaviside function is too small. To fix this, increase absolute " + "CVode returned a root after reinitialization. " + "The initial step-size after the event or " + "Heaviside function is too small. To fix this, increase absolute " "and relative tolerances!" ); - if (status != CV_SUCCESS) - throw CvodeException(status, "reInitPostProcess"); + if (status != CV_SUCCESS) { + std::stringstream msg; + msg << "tout: " << tout << ", t: " << *t << "."; + throw CvodeException(status, "reInitPostProcess", msg.str().c_str()); + } cv_mem->cv_nst = nst_tmp + 1; if (cv_mem->cv_adjMallocDone == SUNTRUE) { @@ -515,7 +551,7 @@ void CVodeSolver::reInitPostProcess( dt_mem[cv_mem->cv_nst % ca_mem->ca_nsteps]->t = *t; ca_mem->ca_IMstore(cv_mem, dt_mem[cv_mem->cv_nst % ca_mem->ca_nsteps]); - /* Set t1 field of the current ckeck point structure + /* Set t1 field of the current check point structure for the case in which there will be no future check points */ ca_mem->ck_mem->ck_t1 = *t; @@ -526,7 +562,7 @@ void CVodeSolver::reInitPostProcess( } void CVodeSolver:: - reInit(const realtype t0, AmiVector const& yy0, AmiVector const& /*yp0*/) + reInit(realtype const t0, AmiVector const& yy0, AmiVector const& /*yp0*/) const { auto cv_mem = static_cast(solver_memory_.get()); cv_mem->cv_tn = t0; @@ -554,7 +590,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( @@ -611,14 +647,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, @@ -643,7 +679,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() @@ -652,7 +688,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"); @@ -707,7 +743,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); @@ -716,7 +752,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 @@ -726,7 +762,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); @@ -742,7 +778,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); @@ -752,7 +788,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); @@ -765,7 +801,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); @@ -834,12 +870,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"); @@ -909,7 +945,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); } /** @@ -960,7 +996,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); } /** @@ -1023,7 +1059,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); } /** @@ -1049,7 +1085,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); } /** @@ -1066,9 +1102,17 @@ static int froot(realtype t, N_Vector x, realtype* root, void* user_data) { auto model = dynamic_cast(typed_udata->first); Expects(model); - model->froot(t, x, gsl::make_span(root, model->ne)); + if (model->ne != model->ne_solver) { + // temporary buffer to store all root function values, not only the ones + // tracked by the solver + static std::vector root_buffer(model->ne, 0.0); + model->froot(t, x, root_buffer); + std::copy_n(root_buffer.begin(), model->ne_solver, root); + } else { + model->froot(t, x, gsl::make_span(root, model->ne_solver)); + } return model->checkFinite( - gsl::make_span(root, model->ne), ModelQuantity::root + gsl::make_span(root, model->ne_solver), ModelQuantity::root, t ); } @@ -1093,7 +1137,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 @@ -1102,7 +1146,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); } /** @@ -1128,7 +1172,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); } /** @@ -1148,7 +1192,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); } /** @@ -1167,7 +1211,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 + ); } /** @@ -1186,7 +1232,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 + ); } /** @@ -1202,8 +1250,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); @@ -1212,7 +1260,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 ); } @@ -1241,7 +1289,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/deps/AMICI/src/solver_idas.cpp b/deps/AMICI/src/solver_idas.cpp index 63f65b184..8093b336e 100644 --- a/deps/AMICI/src/solver_idas.cpp +++ b/deps/AMICI/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. */ @@ -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; @@ -254,6 +254,37 @@ 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"); +} + +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) + throw IDAException(status, "IDASetMaxStep"); +} + Solver* IDASolver::clone() const { return new IDASolver(*this); } void IDASolver::allocateSolver() const { @@ -263,13 +294,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 +405,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; @@ -406,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 @@ -445,7 +476,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 +523,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 +557,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 +571,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 +588,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 +609,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 +617,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 +670,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 +679,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 +688,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 +708,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 +722,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 +799,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 +807,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"); @@ -822,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 { @@ -852,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 { @@ -860,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"); } /** @@ -1052,7 +1083,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); } /** @@ -1083,7 +1114,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); } /** @@ -1105,7 +1136,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 ); } @@ -1131,7 +1162,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 @@ -1140,7 +1171,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); } /** @@ -1170,7 +1201,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); } /** @@ -1195,7 +1226,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); } /** @@ -1217,7 +1248,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 + ); } /** @@ -1239,7 +1272,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 + ); } /** @@ -1257,7 +1292,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*/ ) { @@ -1268,7 +1303,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 ); } @@ -1301,7 +1336,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; } diff --git a/deps/AMICI/src/spline.cpp b/deps/AMICI/src/spline.cpp index 4e7d74252..719af0a86 100644 --- a/deps/AMICI/src/spline.cpp +++ b/deps/AMICI/src/spline.cpp @@ -217,7 +217,7 @@ double seval( j = k; /* move the upper bound */ if (u >= x[k]) i = k; /* move the lower bound */ - } /* there are no more segments to search */ + } /* there are no more segments to search */ while (j > i + 1); } } @@ -273,7 +273,7 @@ double sinteg( j = k; /* move the upper bound */ if (u >= x[k]) i = k; /* move the lower bound */ - } /* there are no more segments to search */ + } /* there are no more segments to search */ while (j > i + 1); } diff --git a/deps/AMICI/src/splinefunctions.cpp b/deps/AMICI/src/splinefunctions.cpp index ba9865a72..0b07a75aa 100644 --- a/deps/AMICI/src/splinefunctions.cpp +++ b/deps/AMICI/src/splinefunctions.cpp @@ -2,9 +2,9 @@ #include "amici/amici.h" #include "amici/defines.h" #include "amici/exception.h" -#include "amici/vector.h" #include // std::min +#include #include #include @@ -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; @@ -145,9 +145,9 @@ HermiteSpline::HermiteSpline( bool logarithmic_parametrization ) : AbstractSpline( - std::move(nodes), std::move(node_values), equidistant_spacing, - logarithmic_parametrization - ) + std::move(nodes), std::move(node_values), equidistant_spacing, + logarithmic_parametrization + ) , node_values_derivative_(std::move(node_values_derivative)) , first_node_bc_(firstNodeBC) , last_node_bc_(lastNodeBC) @@ -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( @@ -602,21 +602,25 @@ void HermiteSpline::compute_coefficients_extrapolation_sensi( case SplineExtrapolation::linear: if (first_node_bc_ == SplineBoundaryCondition::zeroDerivative) { sm0 = 0; - } else if (get_node_derivative_by_fd() && first_node_bc_ == SplineBoundaryCondition::given) { + } else if (get_node_derivative_by_fd() + && first_node_bc_ == SplineBoundaryCondition::given) { sm0 = (dvaluesdp[spline_offset + ip + nplist] - sp0) / (nodes_[1] - nodes_[0]); - } else if (get_node_derivative_by_fd() && first_node_bc_ == SplineBoundaryCondition::natural) { + } else if (get_node_derivative_by_fd() + && first_node_bc_ == SplineBoundaryCondition::natural) { throw AmiException( "Natural boundary condition for " "Hermite splines with linear extrapolation is " "not yet implemented." ); - } else if (!get_node_derivative_by_fd() && first_node_bc_ == SplineBoundaryCondition::given) { + } else if (!get_node_derivative_by_fd() + && first_node_bc_ == SplineBoundaryCondition::given) { sm0 = dslopesdp[spline_offset + ip]; - } else if (!get_node_derivative_by_fd() && first_node_bc_ == SplineBoundaryCondition::natural) { + } else if (!get_node_derivative_by_fd() + && first_node_bc_ == SplineBoundaryCondition::natural) { throw AmiException( "Natural boundary condition for " "Hermite splines with linear extrapolation is " @@ -662,24 +666,28 @@ void HermiteSpline::compute_coefficients_extrapolation_sensi( case SplineExtrapolation::linear: if (last_node_bc_ == SplineBoundaryCondition::zeroDerivative) { sm_end = 0; - } else if (get_node_derivative_by_fd() && last_node_bc_ == SplineBoundaryCondition::given) { + } else if (get_node_derivative_by_fd() + && last_node_bc_ == SplineBoundaryCondition::given) { sm_end = (sp_end - dvaluesdp [spline_offset + ip + (n_nodes() - 2) * nplist]) / (nodes_[n_nodes() - 1] - nodes_[n_nodes() - 2]); - } else if (get_node_derivative_by_fd() && last_node_bc_ == SplineBoundaryCondition::natural) { + } else if (get_node_derivative_by_fd() + && last_node_bc_ == SplineBoundaryCondition::natural) { throw AmiException( "Natural boundary condition for " "Hermite splines with linear extrapolation is " "not yet implemented." ); - } else if (!get_node_derivative_by_fd() && last_node_bc_ == SplineBoundaryCondition::given) { + } else if (!get_node_derivative_by_fd() + && last_node_bc_ == SplineBoundaryCondition::given) { sm_end = dslopesdp[spline_offset + ip + (n_nodes() - 1) * nplist]; - } else if (!get_node_derivative_by_fd() && last_node_bc_ == SplineBoundaryCondition::natural) { + } else if (!get_node_derivative_by_fd() + && last_node_bc_ == SplineBoundaryCondition::natural) { throw AmiException( "Natural boundary condition for " "Hermite splines with linear extrapolation is " @@ -821,7 +829,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 +930,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/deps/AMICI/src/steadystateproblem.cpp b/deps/AMICI/src/steadystateproblem.cpp index c655b9b38..d78f9a870 100644 --- a/deps/AMICI/src/steadystateproblem.cpp +++ b/deps/AMICI/src/steadystateproblem.cpp @@ -8,7 +8,6 @@ #include "amici/solver.h" #include -#include #include #include #include @@ -510,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 */ @@ -523,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() ); @@ -544,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 */ @@ -553,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; @@ -574,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 @@ -602,8 +614,6 @@ void SteadystateProblem::applyNewtonsMethod(Model& model, bool newton_retry) { int& i_newtonstep = numsteps_.at(newton_retry ? 2 : 0); i_newtonstep = 0; gamma_ = 1.0; - bool update_direction = true; - bool step_successful = false; if (model.nx_solver == 0) return; @@ -614,6 +624,8 @@ void SteadystateProblem::applyNewtonsMethod(Model& model, bool newton_retry) { bool converged = false; wrms_ = getWrms(model, SensitivityMethod::none); converged = newton_retry ? false : wrms_ < conv_thresh; + bool update_direction = true; + while (!converged && i_newtonstep < max_steps_) { /* If Newton steps are necessary, compute the initial search @@ -635,7 +647,7 @@ void SteadystateProblem::applyNewtonsMethod(Model& model, bool newton_retry) { /* Compute new xdot and residuals */ realtype wrms_tmp = getWrms(model, SensitivityMethod::none); - step_successful = wrms_tmp < wrms_; + bool step_successful = wrms_tmp < wrms_; if (step_successful) { /* If new residuals are smaller than old ones, update state */ wrms_ = wrms_tmp; diff --git a/deps/AMICI/src/sundials_linsol_wrapper.cpp b/deps/AMICI/src/sundials_linsol_wrapper.cpp index de5d4f1d6..6836949cb 100644 --- a/deps/AMICI/src/sundials_linsol_wrapper.cpp +++ b/deps/AMICI/src/sundials_linsol_wrapper.cpp @@ -2,7 +2,6 @@ #include -#include // bad_alloc #include namespace amici { @@ -162,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."); } @@ -171,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."); } @@ -188,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."); @@ -198,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); } @@ -278,8 +277,8 @@ N_Vector SUNLinSolSPBCGS::getResid() const { SUNLinSolSPFGMR::SUNLinSolSPFGMR(AmiVector const& x, int pretype, int maxl) : SUNLinSolWrapper( - SUNLinSol_SPFGMR(const_cast(x.getNVector()), pretype, maxl) - ) { + SUNLinSol_SPFGMR(const_cast(x.getNVector()), pretype, maxl) + ) { if (!solver_) throw AmiException("Failed to create solver."); } @@ -312,8 +311,8 @@ N_Vector SUNLinSolSPFGMR::getResid() const { SUNLinSolSPGMR::SUNLinSolSPGMR(AmiVector const& x, int pretype, int maxl) : SUNLinSolWrapper( - SUNLinSol_SPGMR(const_cast(x.getNVector()), pretype, maxl) - ) { + SUNLinSol_SPGMR(const_cast(x.getNVector()), pretype, maxl) + ) { if (!solver_) throw AmiException("Failed to create solver."); } @@ -405,8 +404,8 @@ SUNNonLinSolFixedPoint::SUNNonLinSolFixedPoint( int count, const_N_Vector x, int m ) : SUNNonLinSolWrapper( - SUNNonlinSol_FixedPointSens(count, const_cast(x), m) - ) {} + SUNNonlinSol_FixedPointSens(count, const_cast(x), m) + ) {} int SUNNonLinSolFixedPoint::getSysFn(SUNNonlinSolSysFn* SysFn) const { return SUNNonlinSolGetSysFn_FixedPoint(solver, SysFn); diff --git a/deps/AMICI/src/sundials_matrix_wrapper.cpp b/deps/AMICI/src/sundials_matrix_wrapper.cpp index b5c730062..c415c2694 100644 --- a/deps/AMICI/src/sundials_matrix_wrapper.cpp +++ b/deps/AMICI/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; @@ -793,20 +793,25 @@ unravel_index(sunindextype i, SUNMatrix m) { } if (mat_id == SUNMATRIX_SPARSE) { - gsl_ExpectsDebug(i < SM_NNZ_S(m)); - sunindextype row = SM_INDEXVALS_S(m)[i]; - sunindextype i_colptr = 0; - while (SM_INDEXPTRS_S(m)[i_colptr] < SM_NNZ_S(m)) { - if (SM_INDEXPTRS_S(m)[i_colptr + 1] > i) { - sunindextype col = i_colptr; - gsl_EnsuresDebug(row >= 0); - gsl_EnsuresDebug(row < SM_ROWS_S(m)); - gsl_EnsuresDebug(col >= 0); - gsl_EnsuresDebug(col < SM_COLUMNS_S(m)); - return {row, col}; - } - ++i_colptr; - } + auto nnz = SM_NNZ_S(m); + auto ncols = SM_COLUMNS_S(m); + auto index_vals = SM_INDEXVALS_S(m); + auto index_ptrs = SM_INDEXPTRS_S(m); + gsl_ExpectsDebug(i < nnz); + sunindextype row = index_vals[i]; + sunindextype col = 0; + while (col < ncols && index_ptrs[col + 1] <= i) + ++col; + + // This can happen if indexvals / indexptrs haven't been set. + if (col == ncols) + return {-1, -1}; + + gsl_EnsuresDebug(row >= 0); + gsl_EnsuresDebug(row < SM_ROWS_S(m)); + gsl_EnsuresDebug(col >= 0); + gsl_EnsuresDebug(col < ncols); + return {row, col}; } throw amici::AmiException("Unimplemented SUNMatrix type for unravel_index"); diff --git a/deps/AMICI/src/symbolic_functions.cpp b/deps/AMICI/src/symbolic_functions.cpp index 6c18d851b..37e10fde6 100644 --- a/deps/AMICI/src/symbolic_functions.cpp +++ b/deps/AMICI/src/symbolic_functions.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #if _MSC_VER && !__INTEL_COMPILER #include #define alloca _alloca diff --git a/deps/AMICI/src/vector.cpp b/deps/AMICI/src/vector.cpp index 78b66d795..460b92c71 100644 --- a/deps/AMICI/src/vector.cpp +++ b/deps/AMICI/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() { diff --git a/deps/AMICI/swig/CMakeLists.txt b/deps/AMICI/swig/CMakeLists.txt index 87d607d33..5cc6e6b5a 100644 --- a/deps/AMICI/swig/CMakeLists.txt +++ b/deps/AMICI/swig/CMakeLists.txt @@ -20,6 +20,10 @@ 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 @@ -112,10 +116,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( @@ -123,7 +126,6 @@ if(WIN32) POST_BUILD COMMAND dumpbin "/DEPENDENTS" "$" COMMENT "Dumping extension dependencies.") - message("Dependencies: ${dump}") endif() install(FILES ${CMAKE_CURRENT_BINARY_DIR}/amici.py $ diff --git a/deps/AMICI/swig/CMakeLists_model.cmake b/deps/AMICI/swig/CMakeLists_model.cmake index eb9fe681c..523571c52 100644 --- a/deps/AMICI/swig/CMakeLists_model.cmake +++ b/deps/AMICI/swig/CMakeLists_model.cmake @@ -1,4 +1,15 @@ 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) +# cmake >= 3.30 +if(POLICY CMP0167) + cmake_policy(SET CMP0167 NEW) +endif(POLICY CMP0167) + if(DEFINED ENV{SWIG}) set(SWIG_EXECUTABLE $ENV{SWIG}) diff --git a/deps/AMICI/swig/amici.i b/deps/AMICI/swig/amici.i index 9eac8e504..48f99cd7a 100644 --- a/deps/AMICI/swig/amici.i +++ b/deps/AMICI/swig/amici.i @@ -1,7 +1,7 @@ %define DOCSTRING """ Core C++ bindings ------------------ + This module encompasses the complete public C++ API of AMICI, which was exposed via swig. All functions listed here are directly accessible in the main amici package, i.e., :py:class:`amici.amici.ExpData` is available as @@ -16,6 +16,7 @@ nonstandard type conversions. // typemaps for docstrings %typemap(doctype) std::unique_ptr< amici::ExpData >::pointer "ExpData"; +%typemap(doctype) std::unique_ptr< amici::Model > "ModelPtr"; %typemap(doctype) std::unique_ptr< amici::Solver > "SolverPtr"; %typemap(doctype) std::vector< amici::realtype,std::allocator< amici::realtype > > "DoubleVector"; %typemap(doctype) std::vector< double,std::allocator< double > > "DoubleVector"; @@ -43,8 +44,8 @@ nonstandard type conversions. %typemap(doctype) amici::SteadyStateSensitivityMode "SteadyStateSensitivityMode"; %typemap(doctype) amici::realtype "float"; %typemap(doctype) DoubleVector "numpy.ndarray"; -%typemap(doctype) IntVector "List[int]"; -%typemap(doctype) std::pair< size_t,size_t > "Tuple[int, int]"; +%typemap(doctype) IntVector "list[int]"; +%typemap(doctype) std::pair< size_t,size_t > "tuple[int, int]"; %typemap(doctype) std::string "str"; %typemap(doctype) std::string const & "str"; %typemap(doctype) std::unique_ptr< amici::ExpData > "ExpData"; @@ -234,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 @@ -318,6 +327,7 @@ SteadyStateStatus = enum('SteadyStateStatus') NewtonDampingFactorMode = enum('NewtonDampingFactorMode') FixedParameterContext = enum('FixedParameterContext') RDataReporting = enum('RDataReporting') +Constraint = enum('Constraint') %} %template(SteadyStateStatusVector) std::vector; @@ -331,6 +341,8 @@ def __repr__(self): // Handle AMICI_DLL_DIRS environment variable %pythonbegin %{ +from __future__ import annotations + import sys import os @@ -343,8 +355,52 @@ 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, List, Tuple, Sequence +from typing import TYPE_CHECKING, Iterable, Union +from collections.abc import Sequence import numpy as np if TYPE_CHECKING: import numpy %} + +%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 + for x in dir(sys.modules[__name__]) + if not x.startswith('_') + and x not in {"np", "sys", "os", "numpy", "IntEnum", "enum", "pi", "TYPE_CHECKING", "Iterable", "Sequence"} +] + +%} diff --git a/deps/AMICI/swig/edata.i b/deps/AMICI/swig/edata.i index f2f7d0da8..0a8a01e3c 100644 --- a/deps/AMICI/swig/edata.i +++ b/deps/AMICI/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"): diff --git a/deps/AMICI/swig/misc.i b/deps/AMICI/swig/misc.i index 8015e28bf..af166b48e 100644 --- a/deps/AMICI/swig/misc.i +++ b/deps/AMICI/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 %{ diff --git a/deps/AMICI/swig/model.i b/deps/AMICI/swig/model.i index 3063590c2..93a21662c 100644 --- a/deps/AMICI/swig/model.i +++ b/deps/AMICI/swig/model.i @@ -37,6 +37,7 @@ using namespace amici; %ignore initializeB; %ignore initializeStateSensitivities; %ignore initializeStates; +%ignore reinitialize; %ignore ModelState; %ignore getModelState; %ignore setModelState; @@ -93,11 +94,23 @@ using namespace amici; %ignore fdx_rdatadtcl; %ignore fdx_rdatadx_solver; %ignore fdsigmaydy; +%ignore get_steadystate_mask_av; +%newobject amici::Model::clone; +%extend amici::Model { +%pythoncode %{ +def __deepcopy__(self, memo): + return self.clone() +%} +}; - -%newobject amici::Model::clone; +%extend std::unique_ptr { +%pythoncode %{ +def __deepcopy__(self, memo): + return self.clone() +%} +}; // Process symbols in header %include "amici/model.h" diff --git a/deps/AMICI/swig/solver.i b/deps/AMICI/swig/solver.i index 992842c40..20641ba31 100644 --- a/deps/AMICI/swig/solver.i +++ b/deps/AMICI/swig/solver.i @@ -113,9 +113,18 @@ def __repr__(self): %pythoncode %{ def __repr__(self): return _solver_repr(self) + +def __deepcopy__(self, memo): + return self.clone() %} }; +%extend amici::Solver { +%pythoncode %{ +def __deepcopy__(self, memo): + return self.clone() +%} +}; %newobject amici::Solver::clone; // Process symbols in header diff --git a/deps/AMICI/swig/solver_cvodes.i b/deps/AMICI/swig/solver_cvodes.i index 52feb1f4d..7e2c28a17 100644 --- a/deps/AMICI/swig/solver_cvodes.i +++ b/deps/AMICI/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/deps/AMICI/swig/solver_idas.i b/deps/AMICI/swig/solver_idas.i index b4a49a836..7ec9f663b 100644 --- a/deps/AMICI/swig/solver_idas.i +++ b/deps/AMICI/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/deps/AMICI/swig/std_unique_ptr.i b/deps/AMICI/swig/std_unique_ptr.i index 1063bd75b..f73babfd9 100644 --- a/deps/AMICI/swig/std_unique_ptr.i +++ b/deps/AMICI/swig/std_unique_ptr.i @@ -6,15 +6,18 @@ namespace std { struct unique_ptr { typedef Type* pointer; + %apply SWIGTYPE *DISOWN { pointer Ptr }; explicit unique_ptr( pointer Ptr ); + %clear pointer Ptr; unique_ptr (unique_ptr&& Right); + template unique_ptr( unique_ptr&& Right ); unique_ptr( const unique_ptr& Right) = delete; 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; diff --git a/deps/AMICI/tests/benchmark-models/evaluate_benchmark.py b/deps/AMICI/tests/benchmark-models/evaluate_benchmark.py index 0c6e2e412..0fe80f662 100644 --- a/deps/AMICI/tests/benchmark-models/evaluate_benchmark.py +++ b/deps/AMICI/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/deps/AMICI/tests/benchmark-models/test_benchmark_collection.sh b/deps/AMICI/tests/benchmark-models/test_benchmark_collection.sh index 6b9284f4e..f8ccd0c1c 100755 --- a/deps/AMICI/tests/benchmark-models/test_benchmark_collection.sh +++ b/deps/AMICI/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 diff --git a/deps/AMICI/tests/benchmark-models/test_petab_benchmark.py b/deps/AMICI/tests/benchmark-models/test_petab_benchmark.py old mode 100755 new mode 100644 index 0b3c6d80e..d0e2a6d46 --- a/deps/AMICI/tests/benchmark-models/test_petab_benchmark.py +++ b/deps/AMICI/tests/benchmark-models/test_petab_benchmark.py @@ -1,30 +1,26 @@ """Tests for simulate_petab on PEtab benchmark problems.""" +import os from pathlib import Path import amici -import amici.petab_import -import amici.petab_objective import numpy as np import pandas as pd -import petab +import petab.v1 as petab import pytest -from fiddy import MethodId, get_derivative -from fiddy.derivative_check import NumpyIsCloseDerivativeCheck -from fiddy.extensions.amici import simulate_petab_to_cached_functions -from fiddy.success import Consistency +from amici.petab.petab_import import import_petab_problem # Absolute and relative tolerances for finite difference gradient checks. ATOL: float = 1e-3 RTOL: float = 1e-2 -benchmark_path = ( - Path(__file__).parent.parent.parent - / "Benchmark-Models-PEtab" - / "Benchmark-Models" -) +repo_root = Path(__file__).parent.parent.parent +benchmark_path = repo_root / "Benchmark-Models-PEtab" / "Benchmark-Models" +if not benchmark_path.exists(): + benchmark_path = Path(os.environ["BENCHMARK_COLLECTION"]) + # reuse compiled models from test_benchmark_collection.sh -benchmark_outdir = Path(__file__).parent.parent.parent / "test_bmc" +benchmark_outdir = repo_root / "test_bmc" models = [ str(petab_path.stem) for petab_path in benchmark_path.glob("*") @@ -40,6 +36,7 @@ "Isensee_JCB2018", "Beer_MolBioSystems2014", "Alkan_SciSignal2018", + "Lang_PLOSComputBiol2024", # excluded due to excessive numerical failures "Crauste_CellSystems2017", "Fujita_SciSignal2010", @@ -52,10 +49,19 @@ debug_path.mkdir(exist_ok=True, parents=True) +# until fiddy is updated +@pytest.mark.filterwarnings( + "ignore:Importing amici.petab_objective is deprecated.:DeprecationWarning" +) @pytest.mark.filterwarnings("ignore:divide by zero encountered in log10") @pytest.mark.parametrize("scale", (True, False)) @pytest.mark.parametrize("model", models) def test_benchmark_gradient(model, scale): + from fiddy import MethodId, get_derivative + from fiddy.derivative_check import NumpyIsCloseDerivativeCheck + from fiddy.extensions.amici import simulate_petab_to_cached_functions + from fiddy.success import Consistency + if not scale and model in ( "Smith_BMCSystBiol2013", "Brannmark_JBC2010", @@ -81,7 +87,7 @@ def test_benchmark_gradient(model, scale): parameter_ids = list(parameter_df_free.index) # Setup AMICI objects. - amici_model = amici.petab_import.import_petab_problem( + amici_model = import_petab_problem( petab_problem, model_output_dir=benchmark_outdir / model, ) diff --git a/deps/AMICI/tests/benchmark-models/test_petab_model.py b/deps/AMICI/tests/benchmark-models/test_petab_model.py index cf8514753..c4ec2f5dd 100755 --- a/deps/AMICI/tests/benchmark-models/test_petab_model.py +++ b/deps/AMICI/tests/benchmark-models/test_petab_model.py @@ -3,26 +3,28 @@ """ Simulate a PEtab problem and compare results to reference values """ + import argparse import contextlib import importlib import logging import os import sys +from pathlib import Path import amici import numpy as np import pandas as pd -import petab +import petab.v1 as petab import yaml from amici.logging import get_logger -from amici.petab_objective import ( +from amici.petab.simulations import ( LLH, RDATAS, rdatas_to_measurement_df, simulate_petab, ) -from petab.visualize import plot_problem +from petab.v1.visualize import plot_problem logger = get_logger(f"amici.{__name__}", logging.WARNING) @@ -100,7 +102,7 @@ def parse_cli_args(): def main(): """Simulate the model specified on the command line""" - + script_dir = Path(__file__).parent.absolute() args = parse_cli_args() loglevel = logging.DEBUG if args.verbose else logging.INFO logger.setLevel(loglevel) @@ -168,10 +170,7 @@ def main(): times["np"] = sum(problem.parameter_df[petab.ESTIMATE]) - pd.Series(times).to_csv( - f"./tests/benchmark-models/{args.model_name}_benchmark.csv" - ) - + pd.Series(times).to_csv(script_dir / f"{args.model_name}_benchmark.csv") for rdata in rdatas: assert ( rdata.status == amici.AMICI_SUCCESS @@ -201,9 +200,7 @@ def main(): ax.get_figure().savefig(fig_path, dpi=150) if args.check: - references_yaml = os.path.join( - os.path.dirname(__file__), "benchmark_models.yaml" - ) + references_yaml = script_dir / "benchmark_models.yaml" with open(references_yaml) as f: refs = yaml.full_load(f) diff --git a/deps/AMICI/tests/conftest.py b/deps/AMICI/tests/conftest.py index 9e9040051..9b7dd7fb0 100644 --- a/deps/AMICI/tests/conftest.py +++ b/deps/AMICI/tests/conftest.py @@ -3,10 +3,13 @@ import re import sys from pathlib import Path -from typing import List, Set, Tuple +from typing import TYPE_CHECKING import pytest +if TYPE_CHECKING: + from _pytest.reports import TestReport + # stores passed SBML semantic test suite IDs passed_ids = [] @@ -21,7 +24,7 @@ def sbml_semantic_cases_dir() -> Path: return SBML_SEMANTIC_CASES_DIR -def parse_selection(selection_str: str, last: int) -> List[int]: +def parse_selection(selection_str: str, last: int) -> list[int]: """ Parse comma-separated list of integer ranges, return selected indices as integer list @@ -128,7 +131,7 @@ def pytest_runtest_logreport(report: "TestReport") -> None: passed_ids.append(test_case_id) -def get_tags_for_test(test_id: str) -> Tuple[Set[str], Set[str]]: +def get_tags_for_test(test_id: str) -> tuple[set[str], set[str]]: """Get sbml test suite tags for the given test ID Returns: diff --git a/deps/AMICI/tests/cpp/steadystate/tests1.cpp b/deps/AMICI/tests/cpp/steadystate/tests1.cpp index e939a84ff..109cc9eec 100644 --- a/deps/AMICI/tests/cpp/steadystate/tests1.cpp +++ b/deps/AMICI/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) diff --git a/deps/AMICI/tests/cpp/testfunctions.cpp b/deps/AMICI/tests/cpp/testfunctions.cpp index 3e03eedfc..8a8054f14 100644 --- a/deps/AMICI/tests/cpp/testfunctions.cpp +++ b/deps/AMICI/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 { diff --git a/deps/AMICI/tests/cpp/unittests/testExpData.cpp b/deps/AMICI/tests/cpp/unittests/testExpData.cpp index 416a41227..d6e1a6fff 100644 --- a/deps/AMICI/tests/cpp/unittests/testExpData.cpp +++ b/deps/AMICI/tests/cpp/unittests/testExpData.cpp @@ -4,8 +4,6 @@ #include #include -#include -#include #include #include @@ -49,6 +47,7 @@ class ExpDataTest : public ::testing::Test { nz, // nz nz, // nztrue nmaxevent, // ne + 0, // ne_solver 0, // nspl 0, // nJ 0, // nw diff --git a/deps/AMICI/tests/cpp/unittests/testMisc.cpp b/deps/AMICI/tests/cpp/unittests/testMisc.cpp index 80d2c3bc3..a722b567a 100644 --- a/deps/AMICI/tests/cpp/unittests/testMisc.cpp +++ b/deps/AMICI/tests/cpp/unittests/testMisc.cpp @@ -65,6 +65,7 @@ class ModelTest : public ::testing::Test { nz, // nz nz, // nztrue nmaxevent, // ne + 0, // ne_solver 0, // nspl 0, // nJ 0, // nw @@ -260,6 +261,13 @@ TEST(SolverIdasTest, DefaultConstructableAndNotLeaky) IDASolver solver; } +TEST(SolverIdasTest, CopyCtor) +{ + IDASolver solver1; + IDASolver solver2(solver1); +} + + class SolverTest : public ::testing::Test { protected: @@ -303,6 +311,7 @@ class SolverTest : public ::testing::Test { nz, // nz nz, // nztrue ne, // ne + 0, // ne_solver 0, // nspl 0, // nJ 0, // nw @@ -672,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); } } @@ -687,7 +696,7 @@ TEST(UnravelIndex, UnravelIndexSunMatSparse) // [2, 0] // data [1, 2, 3] // colptrs [0, 2, 3] - // rowidxs [2, 3, 1] + // rowidxs [2, 3, 0] D.set_data(0, 0, 0); D.set_data(1, 0, 0); D.set_data(2, 0, 1); @@ -697,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)); @@ -706,6 +715,16 @@ TEST(UnravelIndex, UnravelIndexSunMatSparse) SUNMatDestroy(S); } + +TEST(UnravelIndex, UnravelIndexSunMatSparseMissingIndices) +{ + // Sparse matrix without any indices set + SUNMatrixWrapper mat = SUNMatrixWrapper(2, 3, 2, CSC_MAT); + 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)); +} + + TEST(ReturnCodeToStr, ReturnCodeToStr) { EXPECT_EQ("AMICI_SUCCESS", simulation_status_to_str(AMICI_SUCCESS)); diff --git a/deps/AMICI/tests/cpp/unittests/testSerialization.cpp b/deps/AMICI/tests/cpp/unittests/testSerialization.cpp index 5b4fb1ed2..f59f04d9c 100644 --- a/deps/AMICI/tests/cpp/unittests/testSerialization.cpp +++ b/deps/AMICI/tests/cpp/unittests/testSerialization.cpp @@ -5,7 +5,7 @@ #include "testfunctions.h" #include - +#include #include void @@ -142,6 +142,7 @@ TEST(ModelSerializationTest, ToFile) nz, // nz nz, // nztrue ne, // ne + 0, // ne_solver 0, // nspl 0, // nJ 9, // nw @@ -207,6 +208,7 @@ TEST(ReturnDataSerializationTest, ToString) nz, // nz nz, // nztrue ne, // ne + 0, // ne_solver 0, // nspl 0, // nJ 9, // nw diff --git a/deps/AMICI/tests/generateTestConfig/example_steadystate.py b/deps/AMICI/tests/generateTestConfig/example_steadystate.py index 07fab49df..80e8a776d 100755 --- a/deps/AMICI/tests/generateTestConfig/example_steadystate.py +++ b/deps/AMICI/tests/generateTestConfig/example_steadystate.py @@ -2,7 +2,7 @@ import sys import numpy as np -from example import AmiciExample, dict2attrs +from example import AmiciExample class ExampleSteadystate(AmiciExample): diff --git a/deps/AMICI/tests/performance/test.py b/deps/AMICI/tests/performance/test.py index c497577ab..f11737191 100755 --- a/deps/AMICI/tests/performance/test.py +++ b/deps/AMICI/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, ) diff --git a/deps/AMICI/tests/petab_test_suite/conftest.py b/deps/AMICI/tests/petab_test_suite/conftest.py index df0b00ee8..2e1c6d3ce 100644 --- a/deps/AMICI/tests/petab_test_suite/conftest.py +++ b/deps/AMICI/tests/petab_test_suite/conftest.py @@ -2,12 +2,11 @@ import re import sys -from typing import List from petabtests.core import get_cases -def parse_selection(selection_str: str) -> List[int]: +def parse_selection(selection_str: str) -> list[int]: """ Parse comma-separated list of integer ranges, return selected indices as integer list diff --git a/deps/AMICI/tests/petab_test_suite/test_petab_suite.py b/deps/AMICI/tests/petab_test_suite/test_petab_suite.py index 35ee3adcf..cf1c7d426 100755 --- a/deps/AMICI/tests/petab_test_suite/test_petab_suite.py +++ b/deps/AMICI/tests/petab_test_suite/test_petab_suite.py @@ -6,19 +6,16 @@ import amici import pandas as pd -import petab +import petab.v1 as petab import petabtests import pytest from _pytest.outcomes import Skipped from amici import SteadyStateSensitivityMode from amici.gradient_check import check_derivatives as amici_check_derivatives from amici.logging import get_logger, set_log_level -from amici.petab_import import import_petab_problem -from amici.petab_objective import ( - create_parameterized_edatas, - rdatas_to_measurement_df, - simulate_petab, -) +from amici.petab.conditions import create_parameterized_edatas +from amici.petab.petab_import import import_petab_problem +from amici.petab.simulations import rdatas_to_measurement_df, simulate_petab logger = get_logger(__name__, logging.DEBUG) set_log_level(get_logger("amici.petab_import"), logging.DEBUG) @@ -65,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) @@ -181,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) diff --git a/deps/AMICI/tests/testSBMLSuite.py b/deps/AMICI/tests/testSBMLSuite.py index cfad477ac..2feb3ea7e 100755 --- a/deps/AMICI/tests/testSBMLSuite.py +++ b/deps/AMICI/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}, diff --git a/deps/AMICI/tox.ini b/deps/AMICI/tox.ini new file mode 100644 index 000000000..9f060a7cf --- /dev/null +++ b/deps/AMICI/tox.ini @@ -0,0 +1,23 @@ +[tox] +env_list = + py311 +minversion = 4.11.3 +envlist = + doc + +[testenv] +passenv = AMICI_PARALLEL_COMPILE,CC,CXX + +[testenv:doc] +description = + Build documentation +deps = + -r documentation/rtd_requirements.txt +# don't install the package, this is already handled by `deps` above +skip_install = true +change_dir = documentation/ +allowlist_externals = + rm +commands = + rm -rf amici_models/ _doxyoutput_amici_cpp/ _exhale_cpp_api/ _exhale_matlab_api/ + sphinx-build -T -E -W --keep-going -b readthedocs -d _build/doctrees-readthedocs -D language=en . _build/html diff --git a/deps/AMICI/version.txt b/deps/AMICI/version.txt index 5a03fb737..30f6cf8d9 100644 --- a/deps/AMICI/version.txt +++ b/deps/AMICI/version.txt @@ -1 +1 @@ -0.20.0 +0.26.1